yaml_spanned/
error.rs

1use crate::value::Value;
2use codespan_reporting::diagnostic::{Diagnostic, Label};
3
4pub type ErrorSpan = std::ops::Range<usize>;
5
6pub trait ToDiagnostics {
7    fn to_diagnostics<F: Copy + PartialEq>(&self, file_id: F) -> Vec<Diagnostic<F>>;
8}
9
10#[derive(thiserror::Error, Debug)]
11#[error("invalid key {path:?}, expected string but got {value:?}")]
12pub struct InvalidKeyError {
13    pub value: Value,
14    pub span: ErrorSpan,
15    pub path: String,
16}
17
18impl ToDiagnostics for InvalidKeyError {
19    fn to_diagnostics<F: Copy + PartialEq>(&self, file_id: F) -> Vec<Diagnostic<F>> {
20        vec![
21            Diagnostic::error()
22                // .with_code("E0308")
23                .with_message("invalid key")
24                .with_labels(vec![
25                    Label::primary(file_id, self.span.clone())
26                        .with_message(format!("Expected `String`, found: `{:?}`", self.value)),
27                ]),
28        ]
29    }
30}
31
32#[derive(thiserror::Error, Debug)]
33#[error("duplicate key `{path}.{key}`")]
34pub struct DuplicateKeyError {
35    pub key: String,
36    pub path: String,
37    pub occurrences: Vec<ErrorSpan>,
38}
39
40impl ToDiagnostics for DuplicateKeyError {
41    fn to_diagnostics<F: Copy + PartialEq>(&self, file_id: F) -> Vec<Diagnostic<F>> {
42        assert!(
43            self.occurrences.len() >= 2,
44            "duplicated key must have at least two occurrences"
45        );
46
47        let span = &self.occurrences[self.occurrences.len() - 2];
48        let secondary_label = Label::secondary(file_id, span.clone()).with_message(format!(
49            "first use of key {}.{}.{}",
50            self.path,
51            self.key,
52            if self.occurrences.len() > 2 {
53                format!(" (duplicated {} more time)", self.occurrences.len() - 2)
54            } else {
55                String::new()
56            },
57        ));
58
59        let span = &self.occurrences[self.occurrences.len() - 2];
60        let primary_label =
61            Label::primary(file_id, span.clone()).with_message("cannot set the same key twice");
62
63        vec![
64            Diagnostic::error()
65                // .with_code("E0384")
66                .with_message(format!("duplicate key `{}.{}`", self.path, self.key))
67                .with_labels(vec![secondary_label, primary_label]),
68        ]
69    }
70}
71
72#[derive(thiserror::Error, Debug)]
73pub enum LimitExceeded {
74    #[error("recursion limit exceeded")]
75    RecursionLimitExceeded,
76
77    #[error("repetition limit exceeded")]
78    RepetitionLimitExceeded,
79}
80
81#[derive(thiserror::Error, Debug)]
82pub enum ParseError {
83    #[error(transparent)]
84    InvalidKey(#[from] InvalidKeyError),
85    #[error(transparent)]
86    DuplicateKey(#[from] DuplicateKeyError),
87}
88
89impl ToDiagnostics for ParseError {
90    fn to_diagnostics<F: Copy + PartialEq>(&self, file_id: F) -> Vec<Diagnostic<F>> {
91        match self {
92            Self::InvalidKey(err) => err.to_diagnostics(file_id),
93            Self::DuplicateKey(err) => err.to_diagnostics(file_id),
94        }
95    }
96}
97
98#[derive(thiserror::Error, Debug)]
99pub enum Error {
100    #[error(transparent)]
101    YAML(#[from] libyaml_safer::Error),
102
103    #[cfg(feature = "serde")]
104    #[error(transparent)]
105    Serde(#[from] crate::error::SerdeError),
106
107    #[error(transparent)]
108    LimitExceeded(#[from] LimitExceeded),
109
110    #[error("parse error: {0:?}")]
111    Parse(Vec<ParseError>),
112}
113
114impl ToDiagnostics for Error {
115    fn to_diagnostics<F: Copy + PartialEq>(&self, file_id: F) -> Vec<Diagnostic<F>> {
116        match self {
117            Self::YAML(err) => err.to_diagnostics(file_id),
118            Self::LimitExceeded(_) => vec![],
119            #[cfg(feature = "serde")]
120            Self::Serde(_) => vec![],
121            Self::Parse(errs) => errs
122                .iter()
123                .flat_map(|err| err.to_diagnostics(file_id))
124                .collect(),
125        }
126    }
127}
128
129// impl Error {
130//     // pub fn into_iter(self) -> impl Iterator<Item = dyn std::error::Error> {
131//     pub fn iter_boxed(self) -> Box<dyn Iterator<Item = Box<dyn std::error::Error>>> {
132//         match self {
133//             Self::YAML(err) => Box::new([Box::<dyn std::error::Error>::from(err)].into_iter()),
134//             Self::RecursionLimit(err) => {
135//                 Box::new([Box::<dyn std::error::Error>::from(err)].into_iter())
136//             }
137//             Self::Parse(errors) => Box::new(
138//                 errors
139//                     .into_iter()
140//                     .map(|err| Box::<dyn std::error::Error>::from(err)),
141//             ),
142//         }
143//     }
144//
145//     pub fn iter_boxed(self) -> Box<dyn Iterator<Item = Box<dyn std::error::Error>>> {
146//         match self {
147//             Self::YAML(err) => Box::new([Box::<dyn std::error::Error>::from(err)].into_iter()),
148//             Self::RecursionLimit(err) => {
149//                 Box::new([Box::<dyn std::error::Error>::from(err)].into_iter())
150//             }
151//             Self::Parse(errors) => Box::new(
152//                 errors
153//                     .into_iter()
154//                     .map(|err| Box::<dyn std::error::Error>::from(err)),
155//             ),
156//         }
157//     }
158// }
159
160// impl IntoIterator for Error {
161//     type IntoIter = dyn Iterator<Item = Self::Item>;
162//     type Item = dyn std::error::Error;
163//
164//     fn into_iter(self) -> Self::IntoIter {}
165// }
166
167impl ToDiagnostics for libyaml_safer::Error {
168    fn to_diagnostics<F: Copy + PartialEq>(&self, file_id: F) -> Vec<Diagnostic<F>> {
169        let mut labels = vec![];
170
171        if let (Some(context), Some(index)) = (
172            self.context(),
173            self.context_mark()
174                .and_then(|mark| mark.index.try_into().ok()),
175        ) {
176            labels.push(Label::secondary(file_id, index..index).with_message(context));
177        }
178        if let Some(index) = self
179            .problem_mark()
180            .and_then(|mark| mark.index.try_into().ok())
181        {
182            labels.push(Label::primary(file_id, index..index).with_message(self.problem()));
183        }
184
185        vec![
186            Diagnostic::error()
187                .with_message(self.problem())
188                .with_labels(labels),
189        ]
190    }
191}
192
193#[cfg(feature = "serde")]
194#[derive(thiserror::Error, Debug)]
195pub enum SerdeError {
196    #[error("{0}")]
197    Custom(String, Option<()>),
198    #[error("invalid number {0}")]
199    InvalidNumber(InvalidNumberError),
200    #[error(transparent)]
201    FromUtf8(#[from] std::string::FromUtf8Error),
202    #[error("empty tag")]
203    EmptyTag,
204}
205
206#[cfg(feature = "serde")]
207impl serde::ser::Error for SerdeError {
208    fn custom<T: std::fmt::Display>(msg: T) -> Self {
209        Self::Custom(msg.to_string(), None)
210    }
211}
212
213#[cfg(feature = "serde")]
214impl serde::de::Error for SerdeError {
215    fn custom<T: std::fmt::Display>(msg: T) -> Self {
216        Self::Custom(msg.to_string(), None)
217    }
218}
219
220#[derive(thiserror::Error, Debug)]
221pub enum InvalidNumberError {
222    #[error("unknown number format {0:?}")]
223    UnknownFormat(String),
224    #[error(transparent)]
225    ParseInt(#[from] std::num::ParseIntError),
226    #[error(transparent)]
227    ParseFloat(#[from] std::num::ParseFloatError),
228}
229
230#[derive(thiserror::Error, Debug)]
231pub enum MergeError {
232    #[error("cannot merge sequence in element")]
233    SequenceInMergeElement,
234    #[error("cannot merge taggged value")]
235    TaggedInMerge,
236    #[error("cannot merge scalar in element")]
237    ScalarInMergeElement,
238}
239
240#[derive(thiserror::Error, Debug)]
241pub struct InvalidNumberErrorWithSpan {
242    pub path: String,
243    pub span: ErrorSpan,
244    #[source]
245    pub source: InvalidNumberError,
246}
247
248impl std::fmt::Display for InvalidNumberErrorWithSpan {
249    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250        std::fmt::Display::fmt(&self.source, f)
251    }
252}
253
254impl ToDiagnostics for InvalidNumberErrorWithSpan {
255    fn to_diagnostics<F: Copy + PartialEq>(&self, file_id: F) -> Vec<Diagnostic<F>> {
256        let labels = vec![
257            Label::primary(file_id, self.span.clone())
258                .with_message(format!("failed to parse: {:?}", self.source)),
259        ];
260        vec![
261            Diagnostic::error()
262                // .with_code("E0308")
263                .with_message("invalid number")
264                .with_labels(labels),
265        ]
266    }
267}
268
269#[cfg(test)]
270mod tests {
271    use crate::{Mapping, Value};
272    use color_eyre::eyre;
273    use indoc::indoc;
274    use similar_asserts::assert_eq as sim_assert_eq;
275
276    #[test]
277    fn test_incorrect_type() -> eyre::Result<()> {
278        crate::tests::init();
279        let yaml = indoc! {"
280            ---
281            str
282        "};
283        let value = crate::from_str(yaml)?;
284        let expected = r#"invalid type: string "str", expected i16"#; // at line 2 column 1";
285
286        #[cfg(feature = "serde")]
287        {
288            sim_assert_eq!(
289                have: crate::from_value::<i16>(&value).unwrap_err().to_string(),
290                want: expected
291            );
292        }
293        Ok(())
294    }
295
296    #[test]
297    fn test_incorrect_nested_type() -> eyre::Result<()> {
298        crate::tests::init();
299
300        // spellcheck:ignore-block
301        let yaml = indoc! {"
302            b:
303              - !C
304                d: fase
305        "};
306
307        let value = crate::from_str(yaml)?;
308
309        #[cfg(feature = "serde")]
310        {
311            #[derive(serde::Deserialize, Debug)]
312            pub struct A {
313                #[allow(dead_code)]
314                pub b: Vec<B>,
315            }
316            #[derive(serde::Deserialize, Debug)]
317            pub enum B {
318                C(#[allow(dead_code)] C),
319            }
320            #[derive(serde::Deserialize, Debug)]
321            pub struct C {
322                #[allow(dead_code)]
323                pub d: bool,
324            }
325
326            // spellcheck:ignore-next-line
327            let expected = r#"invalid type: string "fase", expected a boolean"#;
328
329            sim_assert_eq!(
330                crate::from_value::<A>(&value).unwrap_err().to_string(),
331                expected
332            );
333            sim_assert_eq!(
334                have: serde_yaml::from_value::<A>(serde_yaml::from_str(yaml)?)
335                    .unwrap_err()
336                    .to_string(),
337                want: expected
338            );
339        }
340        Ok(())
341    }
342
343    #[ignore = "empty files (EOF) are parsed as NULL"]
344    #[test]
345    fn test_empty() -> eyre::Result<()> {
346        crate::tests::init();
347        let expected = "EOF while parsing a value";
348        sim_assert_eq!(have: crate::from_str("").unwrap_err().to_string(), want: expected);
349        Ok(())
350    }
351
352    #[test]
353    fn test_missing_field() -> eyre::Result<()> {
354        crate::tests::init();
355
356        let yaml = indoc! {"
357            ---
358            v: true
359        "};
360
361        let value = crate::from_str(yaml)?;
362
363        #[cfg(feature = "serde")]
364        {
365            #[derive(serde::Deserialize, Debug)]
366            pub struct Basic {
367                #[allow(dead_code)]
368                pub v: bool,
369                #[allow(dead_code)]
370                pub w: bool,
371            }
372
373            let expected = r"missing field `w`";
374            sim_assert_eq!(
375                have: crate::from_value::<Basic>(&value).unwrap_err().to_string(),
376                want: expected
377            );
378        }
379        Ok(())
380    }
381
382    #[test]
383    fn test_unknown_anchor() -> eyre::Result<()> {
384        crate::tests::init();
385
386        let yaml = indoc! {"
387            ---
388            *some
389        "};
390
391        let expected = "Composer error: line 2 column 1: found undefined alias";
392        sim_assert_eq!(have: crate::from_str(yaml).unwrap_err().to_string(), want: expected);
393        Ok(())
394    }
395
396    #[test]
397    fn test_ignored_unknown_anchor() -> eyre::Result<()> {
398        crate::tests::init();
399
400        let yaml = indoc! {"
401            b: [*a]
402            c: ~
403        "};
404        dbg!(&yaml);
405
406        let expected = "Composer error: line 1 column 5: found undefined alias";
407        sim_assert_eq!(have: crate::from_str(yaml).unwrap_err().to_string(), want: expected);
408        Ok(())
409    }
410
411    #[test]
412    fn test_bytes() -> eyre::Result<()> {
413        crate::tests::init();
414
415        let expected = "Parser error: line 1 column 1: did not find expected node content while parsing a block node (line 1 column 1)";
416        sim_assert_eq!(have: crate::from_str("...").unwrap_err().to_string(), want: expected);
417
418        // sim_assert_eq!(
419        //     crate::from_value::<Vec<u8>>(&value)
420        //         .unwrap_err()
421        //         .to_string(),
422        //     expected
423        // );
424        Ok(())
425    }
426
427    #[test]
428    fn test_second_document_syntax_error() -> eyre::Result<()> {
429        crate::tests::init();
430
431        let yaml = indoc! {"
432            ---
433            0
434            ---
435            ]
436        "};
437
438        let mut test = yaml.as_bytes();
439        let mut documents = crate::from_str_lossy_iter(&mut test);
440
441        // first document
442        let (value, _errors) = documents.next().unwrap().unwrap();
443        let expected: Value = 0.into();
444        sim_assert_eq!(value.cleared_spans().into_inner(), expected);
445
446        // second document
447        let second_document = documents.next().unwrap();
448        // let expected = "did not find expected node content at line 4 column 1, while parsing a block node";
449        let expected = r"Parser error: line 4 column 1: did not find expected node content while parsing a block node (line 4 column 1)";
450        sim_assert_eq!(have: second_document.unwrap_err().to_string(), want: expected);
451
452        Ok(())
453    }
454
455    #[test]
456    fn test_missing_enum_tag() -> eyre::Result<()> {
457        crate::tests::init();
458
459        let yaml = indoc! {r#"
460            "V": 16
461            "other": 32
462        "#};
463
464        let value = crate::from_str(yaml)?;
465        sim_assert_eq!(
466            value.clone().cleared_spans().into_inner(),
467            Value::from(Mapping::from_iter([
468                ("V".into(), 16.into()),
469                ("other".into(), 32.into()),
470            ]))
471        );
472
473        #[cfg(feature = "serde")]
474        {
475            #[derive(serde::Deserialize, Debug)]
476            pub enum E {
477                V(#[allow(dead_code)] usize),
478            }
479            // let expected = "invalid type: map, expected a YAML tag starting with '!'";
480            let expected = "invalid type: map, expected a Value::Tagged enum";
481            sim_assert_eq!(
482                crate::from_value::<E>(&value).unwrap_err().to_string(),
483                expected,
484            );
485        }
486        Ok(())
487    }
488
489    #[cfg(feature = "serde")]
490    #[test]
491    fn test_deserialize_nested_enum() -> eyre::Result<()> {
492        crate::tests::init();
493
494        #[derive(serde::Deserialize, Debug)]
495        pub enum Outer {
496            Inner(#[allow(dead_code)] Inner),
497        }
498        #[derive(serde::Deserialize, Debug)]
499        pub enum Inner {
500            Variant(#[allow(dead_code)] Vec<usize>),
501        }
502
503        let yaml = indoc! {"
504            ---
505            !Inner []
506        "};
507        let value = crate::from_str(yaml)?;
508        let error = crate::from_value::<Outer>(&value).unwrap_err();
509        // let expected = "deserializing nested enum in Outer::Inner from YAML is not supported yet at line 2 column 1";
510        let expected = "invalid type: sequence, expected a Value::Tagged enum";
511        sim_assert_eq!(error.to_string(), expected);
512
513        let yaml = indoc! {"
514            ---
515            !Variant []
516        "};
517        let value = crate::from_str(yaml)?;
518        let error = crate::from_value::<Outer>(&value).unwrap_err();
519        let expected = "unknown variant `Variant`, expected `Inner`";
520        sim_assert_eq!(error.to_string(), expected);
521
522        let yaml = indoc! {"
523            ---
524            !Inner !Variant []
525        "};
526        // let expected = "deserializing nested enum in Outer::Inner from YAML is not supported yet at line 2 column 1";
527        let value = crate::from_str(yaml)?;
528        let error = crate::from_value::<Outer>(&value).unwrap_err();
529        let expected = "invalid type: unit value, expected a Value::Tagged enum";
530        sim_assert_eq!(error.to_string(), expected);
531
532        Ok(())
533    }
534
535    #[cfg(feature = "serde")]
536    #[test]
537    fn test_variant_not_a_seq() -> eyre::Result<()> {
538        crate::tests::init();
539
540        #[derive(serde::Deserialize, Debug)]
541        pub enum E {
542            V(#[allow(dead_code)] usize),
543        }
544        let yaml = indoc! {"
545            ---
546            !V
547            value: 0
548        "};
549        let value = crate::from_str(yaml)?;
550        let error = crate::from_value::<E>(&value).unwrap_err();
551        let expected = "invalid type: map, expected usize";
552        sim_assert_eq!(error.to_string(), expected);
553        Ok(())
554    }
555
556    #[cfg(feature = "serde")]
557    #[test]
558    fn test_struct_from_sequence() -> eyre::Result<()> {
559        crate::tests::init();
560
561        #[derive(serde::Deserialize, Debug)]
562        pub struct Struct {
563            #[allow(dead_code)]
564            pub x: usize,
565            #[allow(dead_code)]
566            pub y: usize,
567        }
568        let yaml = indoc! {"
569            [0, 0]
570        "};
571        let value = crate::from_str(yaml)?;
572        let error = crate::from_value::<Struct>(&value).unwrap_err();
573        let expected = "invalid type: sequence, expected struct Struct";
574        sim_assert_eq!(error.to_string(), expected);
575        Ok(())
576    }
577
578    #[cfg(feature = "serde")]
579    #[test]
580    fn test_bad_bool() -> eyre::Result<()> {
581        crate::tests::init();
582
583        let yaml = indoc! {"
584            ---
585            !!bool str
586        "};
587        let value = crate::from_str(yaml)?;
588        let error = crate::from_value::<bool>(&value).unwrap_err();
589        let expected = r#"invalid type: string "str", expected a boolean"#;
590        sim_assert_eq!(error.to_string(), expected);
591        Ok(())
592    }
593
594    #[cfg(feature = "serde")]
595    #[test]
596    fn test_bad_int() -> eyre::Result<()> {
597        crate::tests::init();
598
599        let yaml = indoc! {"
600            ---
601            !!int str
602        "};
603        let value = crate::from_str(yaml)?;
604        let error = crate::from_value::<i64>(&value).unwrap_err();
605        let expected = r#"invalid type: string "str", expected i64"#;
606        sim_assert_eq!(error.to_string(), expected);
607        Ok(())
608    }
609
610    #[cfg(feature = "serde")]
611    #[test]
612    fn test_bad_float() -> eyre::Result<()> {
613        crate::tests::init();
614
615        let yaml = indoc! {"
616            ---
617            !!float str
618        "};
619        let value = crate::from_str(yaml)?;
620        let error = crate::from_value::<f64>(&value).unwrap_err();
621        let expected = r#"invalid type: string "str", expected f64"#;
622        sim_assert_eq!(error.to_string(), expected);
623        Ok(())
624    }
625
626    #[cfg(feature = "serde")]
627    #[test]
628    fn test_bad_null() -> eyre::Result<()> {
629        crate::tests::init();
630
631        let yaml = indoc! {"
632            ---
633            !!null str
634        "};
635        let value = crate::from_str(yaml)?;
636        let error = crate::from_value::<()>(&value).unwrap_err();
637        let expected = r#"invalid type: string "str", expected unit"#;
638        sim_assert_eq!(error.to_string(), expected);
639        Ok(())
640    }
641
642    #[cfg(feature = "serde")]
643    #[test]
644    fn test_short_tuple() -> eyre::Result<()> {
645        crate::tests::init();
646
647        let yaml = indoc! {"
648            ---
649            [0, 0]
650        "};
651        let value = crate::from_str(yaml)?;
652        let error = crate::from_value::<(u8, u8, u8)>(&value).unwrap_err();
653        let expected = "invalid length 2, expected a tuple of size 3";
654        sim_assert_eq!(error.to_string(), expected);
655        Ok(())
656    }
657
658    #[cfg(feature = "serde")]
659    #[test]
660    fn test_long_tuple() -> eyre::Result<()> {
661        crate::tests::init();
662
663        let yaml = indoc! {"
664            ---
665            [0, 0, 0]
666        "};
667        let value = crate::from_str(yaml)?;
668        let error = crate::from_value::<(u8, u8)>(&value).unwrap_err();
669        let expected = "invalid length 3, expected fewer elements in sequence";
670        sim_assert_eq!(error.to_string(), expected);
671        Ok(())
672    }
673
674    #[cfg(feature = "serde")]
675    #[test]
676    fn test_invalid_scalar_type() -> eyre::Result<()> {
677        crate::tests::init();
678
679        #[derive(serde::Deserialize, Debug)]
680        pub struct S {
681            #[allow(dead_code)]
682            pub x: [i32; 1],
683        }
684
685        let yaml = "x: ''\n";
686        let value = crate::from_str(yaml)?;
687        let error = crate::from_value::<S>(&value).unwrap_err();
688        let expected = r#"invalid type: string "", expected an array of length 1"#;
689        sim_assert_eq!(error.to_string(), expected);
690        Ok(())
691    }
692
693    // #[cfg(feature = "serde")]
694    // #[cfg(not(miri))]
695    #[test]
696    fn test_infinite_recursion_objects() -> eyre::Result<()> {
697        crate::tests::init();
698
699        // #[derive(serde::Deserialize, Debug)]
700        // pub struct S {
701        //     #[allow(dead_code)]
702        //     pub x: Option<Box<S>>,
703        // }
704
705        let yaml = "&a {'x': *a}";
706        let error = crate::from_str(yaml).unwrap_err();
707        let expected = "recursion limit exceeded";
708        sim_assert_eq!(error.to_string(), expected);
709        // test_error::<S>(yaml, expected);
710        Ok(())
711    }
712
713    // #[cfg(feature = "serde")]
714    // #[cfg(not(miri))]
715    #[test]
716    fn test_infinite_recursion_arrays() -> eyre::Result<()> {
717        crate::tests::init();
718
719        // #[derive(serde::Deserialize, Debug)]
720        // pub struct S(
721        //     #[allow(dead_code)] pub usize,
722        //     #[allow(dead_code)] pub Option<Box<S>>,
723        // );
724
725        let yaml = "&a [0, *a]";
726        let error = crate::from_str(yaml).unwrap_err();
727        let expected = "recursion limit exceeded";
728        sim_assert_eq!(error.to_string(), expected);
729        // test_error::<S>(yaml, expected);
730        Ok(())
731    }
732
733    // #[cfg(feature = "serde")]
734    // #[cfg(not(miri))]
735    #[test]
736    fn test_infinite_recursion_newtype() -> eyre::Result<()> {
737        crate::tests::init();
738
739        // #[derive(serde::Deserialize, Debug)]
740        // pub struct S(#[allow(dead_code)] pub Option<Box<S>>);
741
742        let yaml = "&a [*a]";
743        let error = crate::from_str(yaml).unwrap_err();
744        let expected = "recursion limit exceeded";
745        sim_assert_eq!(error.to_string(), expected);
746        // test_error::<S>(yaml, expected);
747        Ok(())
748    }
749
750    // #[cfg(feature = "serde")]
751    // #[cfg(not(miri))]
752    #[test]
753    fn test_finite_recursion_objects() -> eyre::Result<()> {
754        crate::tests::init();
755
756        // #[derive(serde::Deserialize, Debug)]
757        // pub struct S {
758        //     #[allow(dead_code)]
759        //     pub x: Option<Box<S>>,
760        // }
761
762        let yaml = "{'x':".repeat(1_000) + &"}".repeat(1_000);
763        let error = crate::from_str(&yaml).unwrap_err();
764        let expected = "recursion limit exceeded";
765        sim_assert_eq!(error.to_string(), expected);
766        // test_error::<S>(&yaml, expected);
767        Ok(())
768    }
769
770    // #[cfg(feature = "serde")]
771    // #[cfg(not(miri))]
772    #[test]
773    fn test_finite_recursion_arrays() -> eyre::Result<()> {
774        crate::tests::init();
775
776        // #[derive(serde::Deserialize, Debug)]
777        // pub struct S(
778        //     #[allow(dead_code)] pub usize,
779        //     #[allow(dead_code)] pub Option<Box<S>>,
780        // );
781
782        let yaml = "[0, ".repeat(1_000) + &"]".repeat(1_000);
783        let error = crate::from_str(&yaml).unwrap_err();
784        let expected = "recursion limit exceeded";
785        sim_assert_eq!(error.to_string(), expected);
786        // test_error::<S>(&yaml, expected);
787        Ok(())
788    }
789
790    // #[cfg(not(miri))]
791    #[test]
792    fn test_billion_laughs() {
793        let yaml = indoc! {"
794            a: &a ~
795            b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]
796            c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]
797            d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]
798            e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d]
799            f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e]
800            g: &g [*f,*f,*f,*f,*f,*f,*f,*f,*f]
801            h: &h [*g,*g,*g,*g,*g,*g,*g,*g,*g]
802            i: &i [*h,*h,*h,*h,*h,*h,*h,*h,*h]
803        "};
804        let error = crate::from_str(yaml).unwrap_err();
805        let expected = "repetition limit exceeded";
806        sim_assert_eq!(error.to_string(), expected);
807
808        // #[cfg(feature = "serde")]
809        // {
810        //     #[derive(Debug)]
811        //     struct X;
812        //
813        //     impl<'de> serde::de::Visitor<'de> for X {
814        //         type Value = X;
815        //
816        //         fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
817        //             formatter.write_str("exponential blowup")
818        //         }
819        //
820        //         fn visit_unit<E>(self) -> Result<X, E> {
821        //             Ok(X)
822        //         }
823        //
824        //         fn visit_seq<S>(self, mut seq: S) -> Result<X, S::Error>
825        //         where
826        //             S: serde::de::SeqAccess<'de>,
827        //         {
828        //             while let Some(X) = seq.next_element()? {}
829        //             Ok(X)
830        //         }
831        //     }
832        //
833        //     impl<'de> serde::de::Deserialize<'de> for X {
834        //         fn deserialize<D>(deserializer: D) -> Result<X, D::Error>
835        //         where
836        //             D: serde::Deserializer<'de>,
837        //         {
838        //             deserializer.deserialize_any(X)
839        //         }
840        //     }
841        //
842        //     test_error::<BTreeMap<String, X>>(yaml, expected);
843        // }
844    }
845
846    impl crate::error::Error {
847        #[must_use]
848        pub fn errors(&self) -> Vec<String> {
849            match self {
850                Self::YAML(err) => vec![err.to_string()],
851                #[cfg(feature = "serde")]
852                Self::Serde(err) => vec![err.to_string()],
853                Self::LimitExceeded(err) => vec![err.to_string()],
854                Self::Parse(errors) => errors
855                    .iter()
856                    .map(std::string::ToString::to_string)
857                    .collect(),
858            }
859        }
860    }
861
862    #[test]
863    fn test_duplicate_keys() -> eyre::Result<()> {
864        crate::tests::init();
865
866        let yaml = indoc! {"
867            ---
868            thing: true
869            thing: false
870        "};
871        let errors = crate::from_str(yaml).unwrap_err().errors();
872        let expected = r"duplicate key `.thing`";
873        sim_assert_eq!(errors, vec![expected]);
874
875        let yaml = indoc! {"
876            ---
877            null: true
878            ~: false
879        "};
880        let errors = crate::from_str(yaml).unwrap_err().errors();
881        let expected = "duplicate key `.NULL`";
882        sim_assert_eq!(errors, vec![expected]);
883
884        let yaml = indoc! {"
885            ---
886            99: true
887            99: false
888        "};
889        let errors = crate::from_str(yaml).unwrap_err().errors();
890        let expected = "duplicate key `.99`";
891        sim_assert_eq!(errors, vec![expected]);
892
893        let yaml = indoc! {"
894            ---
895            {}: true
896            {}: false
897        "};
898        let errors = crate::from_str(yaml).unwrap_err().errors();
899        let expected = "duplicate key `.{}`";
900        sim_assert_eq!(errors, vec![expected]);
901        Ok(())
902    }
903}