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