1use crate::content::yaml::vendored::parser::*;
2use crate::content::yaml::vendored::scanner::{Marker, ScanError, TScalarStyle, TokenType};
3
4use linked_hash_map::LinkedHashMap;
5
6use std::collections::BTreeMap;
7use std::f64;
8use std::mem;
9use std::ops::Index;
10use std::string;
11use std::vec;
12
13#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)]
16pub enum Yaml {
17 Real(string::String),
20 Integer(i64),
22 String(string::String),
24 Boolean(bool),
26 Array(self::Array),
28 Hash(self::Hash),
32 Null,
34 BadValue,
38}
39
40pub type Array = Vec<Yaml>;
41pub type Hash = LinkedHashMap<Yaml, Yaml>;
42
43fn parse_f64(v: &str) -> Option<f64> {
46 match v {
47 ".inf" | ".Inf" | ".INF" | "+.inf" | "+.Inf" | "+.INF" => Some(f64::INFINITY),
48 "-.inf" | "-.Inf" | "-.INF" => Some(f64::NEG_INFINITY),
49 ".nan" | "NaN" | ".NAN" => Some(f64::NAN),
50 _ => v.parse::<f64>().ok(),
51 }
52}
53
54pub struct YamlLoader {
55 docs: Vec<Yaml>,
56 doc_stack: Vec<(Yaml, usize)>,
59 key_stack: Vec<Yaml>,
60 anchor_map: BTreeMap<usize, Yaml>,
61}
62
63impl MarkedEventReceiver for YamlLoader {
64 fn on_event(&mut self, ev: Event, _: Marker) {
65 match ev {
67 Event::DocumentStart => {
68 }
70 Event::DocumentEnd => {
71 match self.doc_stack.len() {
72 0 => self.docs.push(Yaml::BadValue),
74 1 => self.docs.push(self.doc_stack.pop().unwrap().0),
75 _ => unreachable!(),
76 }
77 }
78 Event::SequenceStart(aid) => {
79 self.doc_stack.push((Yaml::Array(Vec::new()), aid));
80 }
81 Event::SequenceEnd => {
82 let node = self.doc_stack.pop().unwrap();
83 self.insert_new_node(node);
84 }
85 Event::MappingStart(aid) => {
86 self.doc_stack.push((Yaml::Hash(Hash::new()), aid));
87 self.key_stack.push(Yaml::BadValue);
88 }
89 Event::MappingEnd => {
90 self.key_stack.pop().unwrap();
91 let node = self.doc_stack.pop().unwrap();
92 self.insert_new_node(node);
93 }
94 Event::Scalar(v, style, aid, tag) => {
95 let node = if style != TScalarStyle::Plain {
96 Yaml::String(v)
97 } else if let Some(TokenType::Tag(ref handle, ref suffix)) = tag {
98 if handle == "!!" {
100 match suffix.as_ref() {
101 "bool" => {
102 match v.parse::<bool>() {
104 Err(_) => Yaml::BadValue,
105 Ok(v) => Yaml::Boolean(v),
106 }
107 }
108 "int" => match v.parse::<i64>() {
109 Err(_) => Yaml::BadValue,
110 Ok(v) => Yaml::Integer(v),
111 },
112 "float" => match parse_f64(&v) {
113 Some(_) => Yaml::Real(v),
114 None => Yaml::BadValue,
115 },
116 "null" => match v.as_ref() {
117 "~" | "null" => Yaml::Null,
118 _ => Yaml::BadValue,
119 },
120 _ => Yaml::String(v),
121 }
122 } else {
123 Yaml::String(v)
124 }
125 } else {
126 Yaml::from_str(&v)
128 };
129
130 self.insert_new_node((node, aid));
131 }
132 _ => { }
133 }
134 }
136}
137
138impl YamlLoader {
139 fn insert_new_node(&mut self, node: (Yaml, usize)) {
140 if node.1 > 0 {
142 self.anchor_map.insert(node.1, node.0.clone());
143 }
144 if self.doc_stack.is_empty() {
145 self.doc_stack.push(node);
146 } else {
147 let parent = self.doc_stack.last_mut().unwrap();
148 match *parent {
149 (Yaml::Array(ref mut v), _) => v.push(node.0),
150 (Yaml::Hash(ref mut h), _) => {
151 let cur_key = self.key_stack.last_mut().unwrap();
152 if cur_key.is_badvalue() {
154 *cur_key = node.0;
155 } else {
157 let mut newkey = Yaml::BadValue;
158 mem::swap(&mut newkey, cur_key);
159 h.insert(newkey, node.0);
160 }
161 }
162 _ => unreachable!(),
163 }
164 }
165 }
166
167 pub fn load_from_str(source: &str) -> Result<Vec<Yaml>, ScanError> {
168 let mut loader = YamlLoader {
169 docs: Vec::new(),
170 doc_stack: Vec::new(),
171 key_stack: Vec::new(),
172 anchor_map: BTreeMap::new(),
173 };
174 let mut parser = Parser::new(source.chars());
175 parser.load(&mut loader, true)?;
176 Ok(loader.docs)
177 }
178}
179
180macro_rules! define_as (
181 ($name:ident, $t:ident, $yt:ident) => (
182pub fn $name(&self) -> Option<$t> {
183 match *self {
184 Yaml::$yt(v) => Some(v),
185 _ => None
186 }
187}
188 );
189);
190
191macro_rules! define_as_ref (
192 ($name:ident, $t:ty, $yt:ident) => (
193pub fn $name(&self) -> Option<$t> {
194 match *self {
195 Yaml::$yt(ref v) => Some(v),
196 _ => None
197 }
198}
199 );
200);
201
202macro_rules! define_into (
203 ($name:ident, $t:ty, $yt:ident) => (
204pub fn $name(self) -> Option<$t> {
205 match self {
206 Yaml::$yt(v) => Some(v),
207 _ => None
208 }
209}
210 );
211);
212
213impl Yaml {
214 define_as!(as_bool, bool, Boolean);
215 define_as!(as_i64, i64, Integer);
216
217 define_as_ref!(as_str, &str, String);
218 define_as_ref!(as_hash, &Hash, Hash);
219 define_as_ref!(as_vec, &Array, Array);
220
221 define_into!(into_bool, bool, Boolean);
222 define_into!(into_i64, i64, Integer);
223 define_into!(into_string, String, String);
224 define_into!(into_hash, Hash, Hash);
225 define_into!(into_vec, Array, Array);
226
227 pub fn is_null(&self) -> bool {
228 matches!(*self, Yaml::Null)
229 }
230
231 pub fn is_badvalue(&self) -> bool {
232 matches!(*self, Yaml::BadValue)
233 }
234
235 pub fn is_array(&self) -> bool {
236 matches!(*self, Yaml::Array(_))
237 }
238
239 pub fn as_f64(&self) -> Option<f64> {
240 match *self {
241 Yaml::Real(ref v) => parse_f64(v),
242 _ => None,
243 }
244 }
245
246 pub fn into_f64(self) -> Option<f64> {
247 match self {
248 Yaml::Real(ref v) => parse_f64(v),
249 _ => None,
250 }
251 }
252}
253
254impl Yaml {
255 pub fn from_str(v: &str) -> Yaml {
258 if let Some(rest) = v.strip_prefix("0x") {
259 if let Ok(i) = i64::from_str_radix(rest, 16) {
260 return Yaml::Integer(i);
261 }
262 }
263 if let Some(rest) = v.strip_prefix("0o") {
264 if let Ok(i) = i64::from_str_radix(rest, 8) {
265 return Yaml::Integer(i);
266 }
267 }
268 if let Some(rest) = v.strip_prefix('+') {
269 if let Ok(i) = rest.parse::<i64>() {
270 return Yaml::Integer(i);
271 }
272 }
273 match v {
274 "~" | "null" => Yaml::Null,
275 "true" => Yaml::Boolean(true),
276 "false" => Yaml::Boolean(false),
277 _ if v.parse::<i64>().is_ok() => Yaml::Integer(v.parse::<i64>().unwrap()),
278 _ if parse_f64(v).is_some() => Yaml::Real(v.to_owned()),
280 _ => Yaml::String(v.to_owned()),
281 }
282 }
283}
284
285static BAD_VALUE: Yaml = Yaml::BadValue;
286impl<'a> Index<&'a str> for Yaml {
287 type Output = Yaml;
288
289 fn index(&self, idx: &'a str) -> &Yaml {
290 let key = Yaml::String(idx.to_owned());
291 match self.as_hash() {
292 Some(h) => h.get(&key).unwrap_or(&BAD_VALUE),
293 None => &BAD_VALUE,
294 }
295 }
296}
297
298impl Index<usize> for Yaml {
299 type Output = Yaml;
300
301 fn index(&self, idx: usize) -> &Yaml {
302 if let Some(v) = self.as_vec() {
303 v.get(idx).unwrap_or(&BAD_VALUE)
304 } else if let Some(v) = self.as_hash() {
305 let key = Yaml::Integer(idx as i64);
306 v.get(&key).unwrap_or(&BAD_VALUE)
307 } else {
308 &BAD_VALUE
309 }
310 }
311}
312
313impl IntoIterator for Yaml {
314 type Item = Yaml;
315 type IntoIter = YamlIter;
316
317 fn into_iter(self) -> Self::IntoIter {
318 YamlIter {
319 yaml: self.into_vec().unwrap_or_default().into_iter(),
320 }
321 }
322}
323
324pub struct YamlIter {
325 yaml: vec::IntoIter<Yaml>,
326}
327
328impl Iterator for YamlIter {
329 type Item = Yaml;
330
331 fn next(&mut self) -> Option<Yaml> {
332 self.yaml.next()
333 }
334}
335
336#[cfg(test)]
337mod test {
338 use crate::content::yaml::vendored::yaml::*;
339 use std::f64;
340 #[test]
341 fn test_coerce() {
342 let s = "---
343a: 1
344b: 2.2
345c: [1, 2]
346";
347 let out = YamlLoader::load_from_str(s).unwrap();
348 let doc = &out[0];
349 assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
350 assert_eq!(doc["b"].as_f64().unwrap(), 2.2f64);
351 assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
352 assert!(doc["d"][0].is_badvalue());
353 }
354
355 #[test]
356 fn test_empty_doc() {
357 let s: String = "".to_owned();
358 YamlLoader::load_from_str(&s).unwrap();
359 let s: String = "---".to_owned();
360 assert_eq!(YamlLoader::load_from_str(&s).unwrap()[0], Yaml::Null);
361 }
362
363 #[test]
364 fn test_parser() {
365 let s: String = "
366# comment
367a0 bb: val
368a1:
369 b1: 4
370 b2: d
371a2: 4 # i'm comment
372a3: [1, 2, 3]
373a4:
374 - - a1
375 - a2
376 - 2
377a5: 'single_quoted'
378a6: \"double_quoted\"
379a7: 你好
380"
381 .to_owned();
382 let out = YamlLoader::load_from_str(&s).unwrap();
383 let doc = &out[0];
384 assert_eq!(doc["a7"].as_str().unwrap(), "你好");
385 }
386
387 #[test]
388 fn test_multi_doc() {
389 let s = "
390'a scalar'
391---
392'a scalar'
393---
394'a scalar'
395";
396 let out = YamlLoader::load_from_str(s).unwrap();
397 assert_eq!(out.len(), 3);
398 }
399
400 #[test]
401 fn test_bad_anchor() {
402 let s = "
403a1: &DEFAULT
404 b1: 4
405 b2: *DEFAULT
406";
407 let out = YamlLoader::load_from_str(s).unwrap();
408 let doc = &out[0];
409 assert_eq!(doc["a1"]["b2"], Yaml::BadValue);
410 }
411
412 #[test]
413 fn test_github_27() {
414 let s = "&a";
416 let out = YamlLoader::load_from_str(s).unwrap();
417 let doc = &out[0];
418 assert_eq!(doc.as_str().unwrap(), "");
419 }
420
421 #[test]
422 fn test_plain_datatype() {
423 let s = "
424- 'string'
425- \"string\"
426- string
427- 123
428- -321
429- 1.23
430- -1e4
431- ~
432- null
433- true
434- false
435- !!str 0
436- !!int 100
437- !!float 2
438- !!null ~
439- !!bool true
440- !!bool false
441- 0xFF
442# bad values
443- !!int string
444- !!float string
445- !!bool null
446- !!null val
447- 0o77
448- [ 0xF, 0xF ]
449- +12345
450- [ true, false ]
451";
452 let out = YamlLoader::load_from_str(s).unwrap();
453 let doc = &out[0];
454
455 assert_eq!(doc[0].as_str().unwrap(), "string");
456 assert_eq!(doc[1].as_str().unwrap(), "string");
457 assert_eq!(doc[2].as_str().unwrap(), "string");
458 assert_eq!(doc[3].as_i64().unwrap(), 123);
459 assert_eq!(doc[4].as_i64().unwrap(), -321);
460 assert_eq!(doc[5].as_f64().unwrap(), 1.23);
461 assert_eq!(doc[6].as_f64().unwrap(), -1e4);
462 assert!(doc[7].is_null());
463 assert!(doc[8].is_null());
464 assert!(doc[9].as_bool().unwrap());
465 assert!(!doc[10].as_bool().unwrap());
466 assert_eq!(doc[11].as_str().unwrap(), "0");
467 assert_eq!(doc[12].as_i64().unwrap(), 100);
468 assert_eq!(doc[13].as_f64().unwrap(), 2.0);
469 assert!(doc[14].is_null());
470 assert!(doc[15].as_bool().unwrap());
471 assert!(!doc[16].as_bool().unwrap());
472 assert_eq!(doc[17].as_i64().unwrap(), 255);
473 assert!(doc[18].is_badvalue());
474 assert!(doc[19].is_badvalue());
475 assert!(doc[20].is_badvalue());
476 assert!(doc[21].is_badvalue());
477 assert_eq!(doc[22].as_i64().unwrap(), 63);
478 assert_eq!(doc[23][0].as_i64().unwrap(), 15);
479 assert_eq!(doc[23][1].as_i64().unwrap(), 15);
480 assert_eq!(doc[24].as_i64().unwrap(), 12345);
481 assert!(doc[25][0].as_bool().unwrap());
482 assert!(!doc[25][1].as_bool().unwrap());
483 }
484
485 #[test]
486 fn test_bad_hyphen() {
487 let s = "{-";
489 assert!(YamlLoader::load_from_str(s).is_err());
490 }
491
492 #[test]
493 fn test_issue_65() {
494 let b = "\n\"ll\\\"ll\\\r\n\"ll\\\"ll\\\r\r\r\rU\r\r\rU";
496 assert!(YamlLoader::load_from_str(b).is_err());
497 }
498
499 #[test]
500 fn test_bad_docstart() {
501 assert!(YamlLoader::load_from_str("---This used to cause an infinite loop").is_ok());
502 assert_eq!(
503 YamlLoader::load_from_str("----"),
504 Ok(vec![Yaml::String(String::from("----"))])
505 );
506 assert_eq!(
507 YamlLoader::load_from_str("--- #here goes a comment"),
508 Ok(vec![Yaml::Null])
509 );
510 assert_eq!(
511 YamlLoader::load_from_str("---- #here goes a comment"),
512 Ok(vec![Yaml::String(String::from("----"))])
513 );
514 }
515
516 #[test]
517 fn test_plain_datatype_with_into_methods() {
518 let s = "
519- 'string'
520- \"string\"
521- string
522- 123
523- -321
524- 1.23
525- -1e4
526- true
527- false
528- !!str 0
529- !!int 100
530- !!float 2
531- !!bool true
532- !!bool false
533- 0xFF
534- 0o77
535- +12345
536- -.INF
537- .NAN
538- !!float .INF
539";
540 let mut out = YamlLoader::load_from_str(s).unwrap().into_iter();
541 let mut doc = out.next().unwrap().into_iter();
542
543 assert_eq!(doc.next().unwrap().into_string().unwrap(), "string");
544 assert_eq!(doc.next().unwrap().into_string().unwrap(), "string");
545 assert_eq!(doc.next().unwrap().into_string().unwrap(), "string");
546 assert_eq!(doc.next().unwrap().into_i64().unwrap(), 123);
547 assert_eq!(doc.next().unwrap().into_i64().unwrap(), -321);
548 assert_eq!(doc.next().unwrap().into_f64().unwrap(), 1.23);
549 assert_eq!(doc.next().unwrap().into_f64().unwrap(), -1e4);
550 assert!(doc.next().unwrap().into_bool().unwrap());
551 assert!(!doc.next().unwrap().into_bool().unwrap());
552 assert_eq!(doc.next().unwrap().into_string().unwrap(), "0");
553 assert_eq!(doc.next().unwrap().into_i64().unwrap(), 100);
554 assert_eq!(doc.next().unwrap().into_f64().unwrap(), 2.0);
555 assert!(doc.next().unwrap().into_bool().unwrap());
556 assert!(!doc.next().unwrap().into_bool().unwrap());
557 assert_eq!(doc.next().unwrap().into_i64().unwrap(), 255);
558 assert_eq!(doc.next().unwrap().into_i64().unwrap(), 63);
559 assert_eq!(doc.next().unwrap().into_i64().unwrap(), 12345);
560 assert_eq!(doc.next().unwrap().into_f64().unwrap(), f64::NEG_INFINITY);
561 assert!(doc.next().unwrap().into_f64().is_some());
562 assert_eq!(doc.next().unwrap().into_f64().unwrap(), f64::INFINITY);
563 }
564
565 #[test]
566 fn test_hash_order() {
567 let s = "---
568b: ~
569a: ~
570c: ~
571";
572 let out = YamlLoader::load_from_str(s).unwrap();
573 let first = out.into_iter().next().unwrap();
574 let mut iter = first.into_hash().unwrap().into_iter();
575 assert_eq!(
576 Some((Yaml::String("b".to_owned()), Yaml::Null)),
577 iter.next()
578 );
579 assert_eq!(
580 Some((Yaml::String("a".to_owned()), Yaml::Null)),
581 iter.next()
582 );
583 assert_eq!(
584 Some((Yaml::String("c".to_owned()), Yaml::Null)),
585 iter.next()
586 );
587 assert_eq!(None, iter.next());
588 }
589
590 #[test]
591 fn test_integer_key() {
592 let s = "
5930:
594 important: true
5951:
596 important: false
597";
598 let out = YamlLoader::load_from_str(s).unwrap();
599 let first = out.into_iter().next().unwrap();
600 assert!(first[0]["important"].as_bool().unwrap());
601 }
602
603 #[test]
604 fn test_indentation_equality() {
605 let four_spaces = YamlLoader::load_from_str(
606 r#"
607hash:
608 with:
609 indentations
610"#,
611 )
612 .unwrap()
613 .into_iter()
614 .next()
615 .unwrap();
616
617 let two_spaces = YamlLoader::load_from_str(
618 r#"
619hash:
620 with:
621 indentations
622"#,
623 )
624 .unwrap()
625 .into_iter()
626 .next()
627 .unwrap();
628
629 let one_space = YamlLoader::load_from_str(
630 r#"
631hash:
632 with:
633 indentations
634"#,
635 )
636 .unwrap()
637 .into_iter()
638 .next()
639 .unwrap();
640
641 let mixed_spaces = YamlLoader::load_from_str(
642 r#"
643hash:
644 with:
645 indentations
646"#,
647 )
648 .unwrap()
649 .into_iter()
650 .next()
651 .unwrap();
652
653 assert_eq!(four_spaces, two_spaces);
654 assert_eq!(two_spaces, one_space);
655 assert_eq!(four_spaces, mixed_spaces);
656 }
657
658 #[test]
659 fn test_two_space_indentations() {
660 let s = r#"
663subcommands:
664 - server:
665 about: server related commands
666subcommands2:
667 - server:
668 about: server related commands
669subcommands3:
670 - server:
671 about: server related commands
672 "#;
673
674 let out = YamlLoader::load_from_str(s).unwrap();
675 let doc = &out.into_iter().next().unwrap();
676
677 println!("{:#?}", doc);
678 assert_eq!(doc["subcommands"][0]["server"], Yaml::Null);
679 assert!(doc["subcommands2"][0]["server"].as_hash().is_some());
680 assert!(doc["subcommands3"][0]["server"].as_hash().is_some());
681 }
682
683 #[test]
684 fn test_recursion_depth_check_objects() {
685 let s = "{a:".repeat(10_000) + &"}".repeat(10_000);
686 assert!(YamlLoader::load_from_str(&s).is_err());
687 }
688
689 #[test]
690 fn test_recursion_depth_check_arrays() {
691 let s = "[".repeat(10_000) + &"]".repeat(10_000);
692 assert!(YamlLoader::load_from_str(&s).is_err());
693 }
694}