1use linked_hash_map::LinkedHashMap;
2use crate::parser::*;
3use crate::scanner::{Marker, ScanError, TScalarStyle, TokenType};
4use std::collections::BTreeMap;
5use std::f64;
6use std::i64;
7use std::mem;
8use std::ops::{Index, IndexMut};
9use std::string;
10use std::vec;
11
12#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)]
29pub enum Yaml {
30 Real(string::String),
33 Integer(i64),
35 String(string::String),
37 Boolean(bool),
39 Array(self::Array),
41 Hash(self::Hash),
45 Alias(usize),
47 Null,
49 BadValue,
53
54 Original(string::String),
56}
57
58pub type Array = Vec<Yaml>;
59#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)]
62pub struct Hash {
63 pub map: LinkedHashMap<Yaml, Yaml>,
64 pub block: bool
65}
66
67impl Hash {
68 pub fn new(block: bool) -> Self {
69 Hash { map: LinkedHashMap::new(), block }
70 }
71
72 pub fn into_iter(self) -> linked_hash_map::IntoIter<Yaml, Yaml> {
73 self.map.into_iter()
74 }
75
76 pub fn insert(&mut self, k: Yaml, v: Yaml) -> Option<Yaml> {
77 self.map.insert(k, v)
78 }
79
80 pub fn get(&self, k: &Yaml) -> Option<&Yaml> {
81 self.map.get(k)
82 }
83
84 pub fn get_mut(&mut self, k: &Yaml) -> Option<&mut Yaml> {
85 self.map.get_mut(k)
86 }
87
88 pub fn remove(&mut self, k: &Yaml) -> Option<Yaml> {
89 self.map.remove(k)
90 }
91
92 pub fn is_empty(&self) -> bool {
93 self.map.is_empty()
94 }
95
96 pub fn iter(&self) -> linked_hash_map::Iter<Yaml, Yaml> {
97 self.map.iter()
98 }
99
100 pub fn iter_mut(&mut self) -> linked_hash_map::IterMut<Yaml, Yaml> {
101 self.map.iter_mut()
102 }
103
104
105}
106
107
108
109fn parse_f64(v: &str) -> Option<f64> {
112 match v {
113 ".inf" | ".Inf" | ".INF" | "+.inf" | "+.Inf" | "+.INF" => Some(f64::INFINITY),
114 "-.inf" | "-.Inf" | "-.INF" => Some(f64::NEG_INFINITY),
115 ".nan" | "NaN" | ".NAN" => Some(f64::NAN),
116 _ => v.parse::<f64>().ok(),
117 }
118}
119
120pub struct YamlLoader {
121 docs: Vec<Yaml>,
122 doc_stack: Vec<(Yaml, usize)>,
125 key_stack: Vec<Yaml>,
126 anchor_map: BTreeMap<usize, Yaml>,
127}
128
129impl MarkedEventReceiver for YamlLoader {
130 fn on_event(&mut self, ev: Event, _: Marker) {
131 match ev {
133 Event::Line(content) => {
134 self.docs.push(Yaml::Original(content))
135 }
136 Event::DocumentStart(cid, oid) => {
137 if cid > 0 && oid > 0 {
139 self.docs.push(Yaml::Original(format!("--- !u!{} &{}", cid, oid)))
140 }
141 }
142 Event::DocumentEnd => {
143 match self.doc_stack.len() {
144 0 => self.docs.push(Yaml::BadValue),
146 1 => self.docs.push(self.doc_stack.pop().unwrap().0),
147 _ => unreachable!(),
148 }
149 }
150 Event::SequenceStart(aid) => {
151 self.doc_stack.push((Yaml::Array(Vec::new()), aid));
152 }
153 Event::SequenceEnd => {
154 let node = self.doc_stack.pop().unwrap();
155 self.insert_new_node(node);
156 }
157 Event::MappingStart(aid, block) => {
158 self.doc_stack.push((Yaml::Hash(Hash::new(block)), aid));
159 self.key_stack.push(Yaml::BadValue);
160 }
161 Event::MappingEnd => {
162 self.key_stack.pop().unwrap();
163 let node = self.doc_stack.pop().unwrap();
164 self.insert_new_node(node);
165 }
166 Event::Scalar(v, style, aid, tag) => {
167 let node = if style != TScalarStyle::Plain {
168 Yaml::String(v)
169 } else if let Some(TokenType::Tag(ref handle, ref suffix)) = tag {
170 if handle == "!!" {
172 match suffix.as_ref() {
173 "bool" => {
174 match v.parse::<bool>() {
176 Err(_) => Yaml::BadValue,
177 Ok(v) => Yaml::Boolean(v),
178 }
179 }
180 "int" => match v.parse::<i64>() {
181 Err(_) => Yaml::BadValue,
182 Ok(v) => Yaml::Integer(v),
183 },
184 "float" => match parse_f64(&v) {
185 Some(_) => Yaml::Real(v),
186 None => Yaml::BadValue,
187 },
188 "null" => match v.as_ref() {
189 "~" | "null" => Yaml::Null,
190 _ => Yaml::BadValue,
191 },
192 _ => Yaml::String(v),
193 }
194 } else {
195 Yaml::String(v)
196 }
197 } else {
198 Yaml::from_str(&v)
200 };
201
202 self.insert_new_node((node, aid));
203 }
204 Event::Alias(id) => {
205 let n = match self.anchor_map.get(&id) {
206 Some(v) => v.clone(),
207 None => Yaml::BadValue,
208 };
209 self.insert_new_node((n, 0));
210 }
211 _ => { }
212 }
213 }
215}
216
217impl YamlLoader {
218 fn insert_new_node(&mut self, node: (Yaml, usize)) {
219 if node.1 > 0 {
221 self.anchor_map.insert(node.1, node.0.clone());
222 }
223 if self.doc_stack.is_empty() {
224 self.doc_stack.push(node);
225 } else {
226 let parent = self.doc_stack.last_mut().unwrap();
227 match *parent {
228 (Yaml::Array(ref mut v), _) => v.push(node.0),
229 (Yaml::Hash(ref mut h), _) => {
230 let cur_key = self.key_stack.last_mut().unwrap();
231 if cur_key.is_badvalue() {
233 *cur_key = node.0;
234 } else {
236 let mut newkey = Yaml::BadValue;
237 mem::swap(&mut newkey, cur_key);
238 h.insert(newkey, node.0);
239 }
240 }
241 _ => unreachable!(),
242 }
243 }
244 }
245
246 pub fn load_from_str(source: &str) -> Result<Vec<Yaml>, ScanError> {
247 let mut loader = YamlLoader {
248 docs: Vec::new(),
249 doc_stack: Vec::new(),
250 key_stack: Vec::new(),
251 anchor_map: BTreeMap::new(),
252 };
253 let mut parser = Parser::new(source.chars());
254 parser.load(&mut loader, true)?;
255 Ok(loader.docs)
256 }
257}
258
259macro_rules! define_as (
260 ($name:ident, $t:ident, $yt:ident) => (
261pub fn $name(&self) -> Option<$t> {
262 match *self {
263 Yaml::$yt(v) => Some(v),
264 _ => None
265 }
266}
267 );
268);
269
270macro_rules! define_as_ref (
271 ($name:ident, $t:ty, $yt:ident) => (
272pub fn $name(&self) -> Option<$t> {
273 match *self {
274 Yaml::$yt(ref v) => Some(v),
275 _ => None
276 }
277}
278 );
279);
280
281macro_rules! define_as_mut_ref (
282 ($name:ident, $t:ty, $yt:ident) => (
283pub fn $name(&mut self) -> Option<$t> {
284 match *self {
285 Yaml::$yt(ref mut v) => Some(v),
286 _ => None
287 }
288}
289 );
290);
291
292macro_rules! define_replace (
293 ($name:ident, $t:ty, $yt:ident) => (
294pub fn $name(&mut self, value: $t) -> bool {
295 match *self {
296 Yaml::$yt(ref mut v) => {
297 *v = value;
298 true
299 },
300 _ => false
301 }
302}
303 );
304);
305
306macro_rules! define_into (
307 ($name:ident, $t:ty, $yt:ident) => (
308pub fn $name(self) -> Option<$t> {
309 match self {
310 Yaml::$yt(v) => Some(v),
311 _ => None
312 }
313}
314 );
315);
316
317impl Yaml {
318 define_as!(as_bool, bool, Boolean);
319 define_as!(as_i64, i64, Integer);
320
321 define_as_ref!(as_str, &str, String);
322 define_as_ref!(as_hash, &Hash, Hash);
323 define_as_ref!(as_vec, &Array, Array);
324
325 define_as_mut_ref!(as_mut_hash, &mut Hash, Hash);
326 define_as_mut_ref!(as_mut_vec, &mut Array, Array);
327
328 define_replace!(replace_bool, bool, Boolean);
329 define_replace!(replace_i64, i64, Integer);
330 define_replace!(replace_string, String, String);
331
332 define_into!(into_bool, bool, Boolean);
333 define_into!(into_i64, i64, Integer);
334 define_into!(into_string, String, String);
335 define_into!(into_hash, Hash, Hash);
336 define_into!(into_vec, Array, Array);
337
338 pub fn is_null(&self) -> bool {
339 matches!(*self, Yaml::Null)
340 }
341
342 pub fn is_badvalue(&self) -> bool {
343 matches!(*self, Yaml::BadValue)
344 }
345
346 pub fn is_array(&self) -> bool {
347 matches!(*self, Yaml::Array(_))
348 }
349
350 pub fn as_f64(&self) -> Option<f64> {
351 match *self {
352 Yaml::Real(ref v) => parse_f64(v),
353 _ => None,
354 }
355 }
356
357 pub fn into_f64(self) -> Option<f64> {
358 match self {
359 Yaml::Real(ref v) => parse_f64(v),
360 _ => None,
361 }
362 }
363
364 pub fn push(&mut self, value: Yaml) -> bool {
366 match *self {
367 Yaml::Array(ref mut arr) => {
368 arr.push(value);
369 true
370 }
371 _ => false
372 }
373 }
374
375 pub fn insert(&mut self, key: &str, value: Yaml) -> bool {
377 match *self {
378 Yaml::Hash(ref mut h) => {
379 h.insert(Yaml::String(key.to_owned()), value);
380 true
381 }
382 _ => false
383 }
384 }
385
386 pub fn remove(&mut self, key: &str) -> Option<Yaml> {
388 match *self {
389 Yaml::Hash(ref mut h) => {
390 h.remove(&Yaml::String(key.to_owned()))
391 }
392 _ => None
393 }
394 }
395
396 pub fn remove_at(&mut self, idx: usize) -> Option<Yaml> {
398 match *self {
399 Yaml::Array(ref mut arr) => {
400 if idx < arr.len() {
401 Some(arr.remove(idx))
402 } else {
403 None
404 }
405 }
406 _ => None
407 }
408 }
409
410}
411
412#[cfg_attr(feature = "cargo-clippy", allow(should_implement_trait))]
413impl Yaml {
414 pub fn from_str(v: &str) -> Yaml {
417 if let Some(hex) = v.strip_prefix("0x") {
418 if let Ok(i) = i64::from_str_radix(hex, 16) {
419 return Yaml::Integer(i)
420 }
421 }
422 if let Some(octal) = v.strip_prefix("0o") {
423 if let Ok(i) = i64::from_str_radix(octal, 8) {
424 return Yaml::Integer(i);
425 }
426 }
427 if let Some(num) = v.strip_prefix('+') {
428 if let Ok(i) = num.parse::<i64>() {
429 return Yaml::Integer(i);
430 }
431 }
432 match v {
433 "~" | "null" => Yaml::Null,
434 "true" => Yaml::Boolean(true),
435 "false" => Yaml::Boolean(false),
436 _ if v.parse::<i64>().is_ok() => Yaml::Integer(v.parse::<i64>().unwrap()),
437 _ if parse_f64(v).is_some() => Yaml::Real(v.to_owned()),
439 _ => Yaml::String(v.to_owned()),
440 }
441 }
442}
443
444static BAD_VALUE: Yaml = Yaml::BadValue;
445static mut MUT_BAD_VALUE: Yaml = Yaml::BadValue;
446impl<'a> Index<&'a str> for Yaml {
447 type Output = Yaml;
448
449 fn index(&self, idx: &'a str) -> &Yaml {
450 let key = Yaml::String(idx.to_owned());
451 self.as_hash().and_then(|h| h.get(&key)).unwrap_or(&BAD_VALUE)
452 }
453}
454
455impl<'a> IndexMut<&'a str> for Yaml {
456
457 fn index_mut(&mut self, idx: &'a str) -> &mut Yaml {
458 let key = Yaml::String(idx.to_owned());
459 self.as_mut_hash().and_then(|h| h.get_mut(&key)).unwrap_or(unsafe { &mut MUT_BAD_VALUE })
460 }
461}
462
463impl Index<usize> for Yaml {
464 type Output = Yaml;
465
466 fn index(&self, idx: usize) -> &Self::Output {
467 if let Some(v) = self.as_vec() {
468 v.get(idx).unwrap_or(&BAD_VALUE)
469 } else if let Some(v) = self.as_hash() {
470 let key = Yaml::Integer(idx as i64);
471 v.get(&key).unwrap_or(&BAD_VALUE)
472 } else {
473 &BAD_VALUE
474 }
475 }
476}
477
478impl IndexMut<usize> for Yaml {
479
480 fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
481 match self.is_array() {
482 true => {
483 self.as_mut_vec().and_then(|v|
484 v.get_mut(idx)).unwrap_or(unsafe {
485 &mut MUT_BAD_VALUE
486 })
487 },
488 false => {
489 self.as_mut_hash().and_then(|v| {
490 let key = Yaml::Integer(idx as i64);
491 v.get_mut(&key)
492 }).unwrap_or(unsafe {
493 &mut MUT_BAD_VALUE
494 })
495 },
496 }
497 }
512}
513
514impl IntoIterator for Yaml {
515 type Item = Yaml;
516 type IntoIter = YamlIter;
517
518 fn into_iter(self) -> Self::IntoIter {
519 YamlIter {
520 yaml: self.into_vec().unwrap_or_default().into_iter(),
521 }
522 }
523}
524
525pub struct YamlIter {
526 yaml: vec::IntoIter<Yaml>,
527}
528
529impl Iterator for YamlIter {
530 type Item = Yaml;
531
532 fn next(&mut self) -> Option<Yaml> {
533 self.yaml.next()
534 }
535}
536
537#[cfg(test)]
538mod test {
539 use std::f64;
540 use crate::yaml::*;
541 #[test]
542 fn test_coerce() {
543 let s = "---
544a: 1
545b: 2.2
546c: [1, 2]
547";
548 let out = YamlLoader::load_from_str(s).unwrap();
549 let doc = &out[0];
550 assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
551 assert_eq!(doc["b"].as_f64().unwrap(), 2.2f64);
552 assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
553 assert!(doc["d"][0].is_badvalue());
554 }
555
556 #[test]
557 fn test_empty_doc() {
558 let s: String = "".to_owned();
559 YamlLoader::load_from_str(&s).unwrap();
560 let s: String = "---".to_owned();
561 assert_eq!(YamlLoader::load_from_str(&s).unwrap()[0], Yaml::Null);
562 }
563
564 #[test]
565 fn test_parser() {
566 let s: String = "
567# comment
568a0 bb: val
569a1:
570 b1: 4
571 b2: d
572a2: 4 # i'm comment
573a3: [1, 2, 3]
574a4:
575 - - a1
576 - a2
577 - 2
578a5: 'single_quoted'
579a6: \"double_quoted\"
580a7: 你好
581"
582 .to_owned();
583 let out = YamlLoader::load_from_str(&s).unwrap();
584 let doc = &out[0];
585 assert_eq!(doc["a7"].as_str().unwrap(), "你好");
586 }
587
588 #[test]
589 fn test_multi_doc() {
590 let s = "
591'a scalar'
592---
593'a scalar'
594---
595'a scalar'
596";
597 let out = YamlLoader::load_from_str(s).unwrap();
598 assert_eq!(out.len(), 3);
599 }
600
601 #[test]
602 fn test_anchor() {
603 let s = "
604a1: &DEFAULT
605 b1: 4
606 b2: d
607a2: *DEFAULT
608";
609 let out = YamlLoader::load_from_str(s).unwrap();
610 let doc = &out[0];
611 assert_eq!(doc["a2"]["b1"].as_i64().unwrap(), 4);
612 }
613
614 #[test]
615 fn test_bad_anchor() {
616 let s = "
617a1: &DEFAULT
618 b1: 4
619 b2: *DEFAULT
620";
621 let out = YamlLoader::load_from_str(s).unwrap();
622 let doc = &out[0];
623 assert_eq!(doc["a1"]["b2"], Yaml::BadValue);
624 }
625
626 #[test]
627 fn test_github_27() {
628 let s = "&a";
630 let out = YamlLoader::load_from_str(s).unwrap();
631 let doc = &out[0];
632 assert_eq!(doc.as_str().unwrap(), "");
633 }
634
635 #[test]
636 fn test_plain_datatype() {
637 let s = "
638- 'string'
639- \"string\"
640- string
641- 123
642- -321
643- 1.23
644- -1e4
645- ~
646- null
647- true
648- false
649- !!str 0
650- !!int 100
651- !!float 2
652- !!null ~
653- !!bool true
654- !!bool false
655- 0xFF
656# bad values
657- !!int string
658- !!float string
659- !!bool null
660- !!null val
661- 0o77
662- [ 0xF, 0xF ]
663- +12345
664- [ true, false ]
665";
666 let out = YamlLoader::load_from_str(s).unwrap();
667 let doc = &out[0];
668
669 assert_eq!(doc[0].as_str().unwrap(), "string");
670 assert_eq!(doc[1].as_str().unwrap(), "string");
671 assert_eq!(doc[2].as_str().unwrap(), "string");
672 assert_eq!(doc[3].as_i64().unwrap(), 123);
673 assert_eq!(doc[4].as_i64().unwrap(), -321);
674 assert_eq!(doc[5].as_f64().unwrap(), 1.23);
675 assert_eq!(doc[6].as_f64().unwrap(), -1e4);
676 assert!(doc[7].is_null());
677 assert!(doc[8].is_null());
678 assert!(doc[9].as_bool().unwrap());
679 assert!(!doc[10].as_bool().unwrap());
680 assert_eq!(doc[11].as_str().unwrap(), "0");
681 assert_eq!(doc[12].as_i64().unwrap(), 100);
682 assert_eq!(doc[13].as_f64().unwrap(), 2.0);
683 assert!(doc[14].is_null());
684 assert!(doc[15].as_bool().unwrap());
685 assert!(!doc[16].as_bool().unwrap());
686 assert_eq!(doc[17].as_i64().unwrap(), 255);
687 assert!(doc[18].is_badvalue());
688 assert!(doc[19].is_badvalue());
689 assert!(doc[20].is_badvalue());
690 assert!(doc[21].is_badvalue());
691 assert_eq!(doc[22].as_i64().unwrap(), 63);
692 assert_eq!(doc[23][0].as_i64().unwrap(), 15);
693 assert_eq!(doc[23][1].as_i64().unwrap(), 15);
694 assert_eq!(doc[24].as_i64().unwrap(), 12345);
695 assert!(doc[25][0].as_bool().unwrap());
696 assert!(!doc[25][1].as_bool().unwrap());
697 }
698
699 #[test]
700 fn test_bad_hyphen() {
701 let s = "{-";
703 assert!(YamlLoader::load_from_str(s).is_err());
704 }
705
706 #[test]
707 fn test_issue_65() {
708 let b = "\n\"ll\\\"ll\\\r\n\"ll\\\"ll\\\r\r\r\rU\r\r\rU";
710 assert!(YamlLoader::load_from_str(b).is_err());
711 }
712
713 #[test]
714 fn test_bad_docstart() {
715 assert!(YamlLoader::load_from_str("---This used to cause an infinite loop").is_ok());
716 assert_eq!(
717 YamlLoader::load_from_str("----"),
718 Ok(vec![Yaml::String(String::from("----"))])
719 );
720 assert_eq!(
721 YamlLoader::load_from_str("--- #here goes a comment"),
722 Ok(vec![Yaml::Null])
723 );
724 assert_eq!(
725 YamlLoader::load_from_str("---- #here goes a comment"),
726 Ok(vec![Yaml::String(String::from("----"))])
727 );
728 }
729
730 #[test]
731 fn test_plain_datatype_with_into_methods() {
732 let s = "
733- 'string'
734- \"string\"
735- string
736- 123
737- -321
738- 1.23
739- -1e4
740- true
741- false
742- !!str 0
743- !!int 100
744- !!float 2
745- !!bool true
746- !!bool false
747- 0xFF
748- 0o77
749- +12345
750- -.INF
751- .NAN
752- !!float .INF
753";
754 let mut out = YamlLoader::load_from_str(s).unwrap().into_iter();
755 let mut doc = out.next().unwrap().into_iter();
756
757 assert_eq!(doc.next().unwrap().into_string().unwrap(), "string");
758 assert_eq!(doc.next().unwrap().into_string().unwrap(), "string");
759 assert_eq!(doc.next().unwrap().into_string().unwrap(), "string");
760 assert_eq!(doc.next().unwrap().into_i64().unwrap(), 123);
761 assert_eq!(doc.next().unwrap().into_i64().unwrap(), -321);
762 assert_eq!(doc.next().unwrap().into_f64().unwrap(), 1.23);
763 assert_eq!(doc.next().unwrap().into_f64().unwrap(), -1e4);
764 assert!(doc.next().unwrap().into_bool().unwrap());
765 assert!(!doc.next().unwrap().into_bool().unwrap());
766 assert_eq!(doc.next().unwrap().into_string().unwrap(), "0");
767 assert_eq!(doc.next().unwrap().into_i64().unwrap(), 100);
768 assert_eq!(doc.next().unwrap().into_f64().unwrap(), 2.0);
769 assert!(doc.next().unwrap().into_bool().unwrap());
770 assert!(!doc.next().unwrap().into_bool().unwrap());
771 assert_eq!(doc.next().unwrap().into_i64().unwrap(), 255);
772 assert_eq!(doc.next().unwrap().into_i64().unwrap(), 63);
773 assert_eq!(doc.next().unwrap().into_i64().unwrap(), 12345);
774 assert_eq!(doc.next().unwrap().into_f64().unwrap(), f64::NEG_INFINITY);
775 assert!(doc.next().unwrap().into_f64().is_some());
776 assert_eq!(doc.next().unwrap().into_f64().unwrap(), f64::INFINITY);
777 }
778
779 #[test]
780 fn test_hash_order() {
781 let s = "---
782b: ~
783a: ~
784c: ~
785";
786 let out = YamlLoader::load_from_str(s).unwrap();
787 let first = out.into_iter().next().unwrap();
788 let mut iter = first.into_hash().unwrap().into_iter();
789 assert_eq!(
790 Some((Yaml::String("b".to_owned()), Yaml::Null)),
791 iter.next()
792 );
793 assert_eq!(
794 Some((Yaml::String("a".to_owned()), Yaml::Null)),
795 iter.next()
796 );
797 assert_eq!(
798 Some((Yaml::String("c".to_owned()), Yaml::Null)),
799 iter.next()
800 );
801 assert_eq!(None, iter.next());
802 }
803
804 #[test]
805 fn test_integer_key() {
806 let s = "
8070:
808 important: true
8091:
810 important: false
811";
812 let out = YamlLoader::load_from_str(s).unwrap();
813 let first = out.into_iter().next().unwrap();
814 assert!(first[0]["important"].as_bool().unwrap());
815 }
816
817 #[test]
818 fn test_indentation_equality() {
819 let four_spaces = YamlLoader::load_from_str(
820 r#"
821hash:
822 with:
823 indentations
824"#,
825 )
826 .unwrap()
827 .into_iter()
828 .next()
829 .unwrap();
830
831 let two_spaces = YamlLoader::load_from_str(
832 r#"
833hash:
834 with:
835 indentations
836"#,
837 )
838 .unwrap()
839 .into_iter()
840 .next()
841 .unwrap();
842
843 let one_space = YamlLoader::load_from_str(
844 r#"
845hash:
846 with:
847 indentations
848"#,
849 )
850 .unwrap()
851 .into_iter()
852 .next()
853 .unwrap();
854
855 let mixed_spaces = YamlLoader::load_from_str(
856 r#"
857hash:
858 with:
859 indentations
860"#,
861 )
862 .unwrap()
863 .into_iter()
864 .next()
865 .unwrap();
866
867 assert_eq!(four_spaces, two_spaces);
868 assert_eq!(two_spaces, one_space);
869 assert_eq!(four_spaces, mixed_spaces);
870 }
871
872 #[test]
873 fn test_two_space_indentations() {
874 let s = r#"
877subcommands:
878 - server:
879 about: server related commands
880subcommands2:
881 - server:
882 about: server related commands
883subcommands3:
884 - server:
885 about: server related commands
886 "#;
887
888 let out = YamlLoader::load_from_str(s).unwrap();
889 let doc = &out.into_iter().next().unwrap();
890
891 println!("{:#?}", doc);
892 assert_eq!(doc["subcommands"][0]["server"], Yaml::Null);
893 assert!(doc["subcommands2"][0]["server"].as_hash().is_some());
894 assert!(doc["subcommands3"][0]["server"].as_hash().is_some());
895 }
896
897 #[test]
898 fn test_recursion_depth_check_objects() {
899 let s = "{a:".repeat(10_000) + &"}".repeat(10_000);
900 assert!(YamlLoader::load_from_str(&s).is_err());
901 }
902
903 #[test]
904 fn test_recursion_depth_check_arrays() {
905 let s = "[".repeat(10_000) + &"]".repeat(10_000);
906 assert!(YamlLoader::load_from_str(&s).is_err());
907 }
908}