unity_yaml_rust/
yaml.rs

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/// A YAML node is stored as this `Yaml` enumeration, which provides an easy way to
13/// access your YAML document.
14///
15/// # Examples
16///
17/// ```
18/// use yaml_rust::Yaml;
19/// let foo = Yaml::from_str("-123"); // convert the string to the appropriate YAML type
20/// assert_eq!(foo.as_i64().unwrap(), -123);
21///
22/// // iterate over an Array
23/// let vec = Yaml::Array(vec![Yaml::Integer(1), Yaml::Integer(2)]);
24/// for v in vec.as_vec().unwrap() {
25///     assert!(v.as_i64().is_some());
26/// }
27/// ```
28#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)]
29pub enum Yaml {
30    /// Float types are stored as String and parsed on demand.
31    /// Note that f64 does NOT implement Eq trait and can NOT be stored in BTreeMap.
32    Real(string::String),
33    /// YAML int is stored as i64.
34    Integer(i64),
35    /// YAML scalar.
36    String(string::String),
37    /// YAML bool, e.g. `true` or `false`.
38    Boolean(bool),
39    /// YAML array, can be accessed as a `Vec`.
40    Array(self::Array),
41    /// YAML hash, can be accessed as a `LinkedHashMap`.
42    ///
43    /// Insertion order will match the order of insertion into the map.
44    Hash(self::Hash),
45    /// Alias, not fully supported yet.
46    Alias(usize),
47    /// YAML null, e.g. `null` or `~`.
48    Null,
49    /// Accessing a nonexistent node via the Index trait returns `BadValue`. This
50    /// simplifies error handling in the calling code. Invalid type conversion also
51    /// returns `BadValue`.
52    BadValue,
53
54    /// Original content.
55    Original(string::String),
56}
57
58pub type Array = Vec<Yaml>;
59// pub type Hash = LinkedHashMap<Yaml, Yaml>;
60
61#[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
109// parse f64 as Core schema
110// See: https://github.com/chyh1990/yaml-rust/issues/51
111fn 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    // states
123    // (current node, anchor_id) tuple
124    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        // println!("EV {:?}", ev);
132        match ev {
133            Event::Line(content) => {
134                self.docs.push(Yaml::Original(content))
135            }
136            Event::DocumentStart(cid, oid) => {
137                // do nothing
138                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                    // empty document
145                    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                    // XXX tag:yaml.org,2002:
171                    if handle == "!!" {
172                        match suffix.as_ref() {
173                            "bool" => {
174                                // "true" or "false"
175                                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                    // Datatype is not specified, or unrecognized
199                    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            _ => { /* ignore */ }
212        }
213        // println!("DOC {:?}", self.doc_stack);
214    }
215}
216
217impl YamlLoader {
218    fn insert_new_node(&mut self, node: (Yaml, usize)) {
219        // valid anchor id starts from 1
220        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                    // current node is a key
232                    if cur_key.is_badvalue() {
233                        *cur_key = node.0;
234                    // current node is a value
235                    } 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    /// try push yaml into Yaml::Array
365    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    /// try insert yaml into Yaml::Hash
376    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    /// try remove from Yaml::Hash
387    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    /// try remove from Yaml::Array
397    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    // Not implementing FromStr because there is no possibility of Error.
415    // This function falls back to Yaml::String if nothing else matches.
416    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            // try parsing as f64
438            _ 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        // if let Some(v) = self.as_mut_vec() {
498        //     v.get_mut(idx).unwrap_or(unsafe {
499        //         &mut MUT_BAD_VALUE   
500        //     })
501        // } else if let Some(v) = self.as_mut_hash() {
502        //     let key = Yaml::Integer(idx as i64);
503        //     v.get_mut(&key).unwrap_or(unsafe {
504        //         &mut MUT_BAD_VALUE
505        //     })
506        // } else {
507        //     unsafe {
508        //         &mut MUT_BAD_VALUE
509        //     }
510        // }
511    }
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        // https://github.com/chyh1990/yaml-rust/issues/27
629        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        // See: https://github.com/chyh1990/yaml-rust/issues/23
702        let s = "{-";
703        assert!(YamlLoader::load_from_str(s).is_err());
704    }
705
706    #[test]
707    fn test_issue_65() {
708        // See: https://github.com/chyh1990/yaml-rust/issues/65
709        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        // https://github.com/kbknapp/clap-rs/issues/965
875
876        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}