insta/content/yaml/vendored/
yaml.rs

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/// A YAML node is stored as this `Yaml` enumeration, which provides an easy way to
14/// access your YAML document.
15#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)]
16pub enum Yaml {
17    /// Float types are stored as [`String`] and parsed on demand.
18    /// Note that [`f64'] does NOT implement [`Eq'] trait and can NOT be stored in [`BTreeMap`].
19    Real(string::String),
20    /// YAML int is stored as i64.
21    Integer(i64),
22    /// YAML scalar.
23    String(string::String),
24    /// YAML bool, e.g. `true` or `false`.
25    Boolean(bool),
26    /// YAML array, can be accessed as a `Vec`.
27    Array(self::Array),
28    /// YAML hash, can be accessed as a `LinkedHashMap`.
29    ///
30    /// Insertion order will match the order of insertion into the map.
31    Hash(self::Hash),
32    /// YAML null, e.g. `null` or `~`.
33    Null,
34    /// Accessing a nonexistent node via the Index trait returns `BadValue`. This
35    /// simplifies error handling in the calling code. Invalid type conversion also
36    /// returns `BadValue`.
37    BadValue,
38}
39
40pub type Array = Vec<Yaml>;
41pub type Hash = LinkedHashMap<Yaml, Yaml>;
42
43// parse f64 as Core schema
44// See: https://github.com/chyh1990/yaml-rust/issues/51
45fn 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    // states
57    // (current node, anchor_id) tuple
58    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        // println!("EV {:?}", ev);
66        match ev {
67            Event::DocumentStart => {
68                // do nothing
69            }
70            Event::DocumentEnd => {
71                match self.doc_stack.len() {
72                    // empty document
73                    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                    // XXX tag:yaml.org,2002:
99                    if handle == "!!" {
100                        match suffix.as_ref() {
101                            "bool" => {
102                                // "true" or "false"
103                                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                    // Datatype is not specified, or unrecognized
127                    Yaml::from_str(&v)
128                };
129
130                self.insert_new_node((node, aid));
131            }
132            _ => { /* ignore */ }
133        }
134        // println!("DOC {:?}", self.doc_stack);
135    }
136}
137
138impl YamlLoader {
139    fn insert_new_node(&mut self, node: (Yaml, usize)) {
140        // valid anchor id starts from 1
141        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                    // current node is a key
153                    if cur_key.is_badvalue() {
154                        *cur_key = node.0;
155                    // current node is a value
156                    } 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    // Not implementing FromStr because there is no possibility of Error.
256    // This function falls back to Yaml::String if nothing else matches.
257    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            // try parsing as f64
279            _ 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        // https://github.com/chyh1990/yaml-rust/issues/27
415        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        // See: https://github.com/chyh1990/yaml-rust/issues/23
488        let s = "{-";
489        assert!(YamlLoader::load_from_str(s).is_err());
490    }
491
492    #[test]
493    fn test_issue_65() {
494        // See: https://github.com/chyh1990/yaml-rust/issues/65
495        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        // https://github.com/kbknapp/clap-rs/issues/965
661
662        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}