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_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_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
129impl 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_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"#; #[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 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 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 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 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 let second_document = documents.next().unwrap();
448 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 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 = "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 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 #[test]
696 fn test_infinite_recursion_objects() -> eyre::Result<()> {
697 crate::tests::init();
698
699 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 Ok(())
711 }
712
713 #[test]
716 fn test_infinite_recursion_arrays() -> eyre::Result<()> {
717 crate::tests::init();
718
719 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 Ok(())
731 }
732
733 #[test]
736 fn test_infinite_recursion_newtype() -> eyre::Result<()> {
737 crate::tests::init();
738
739 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 Ok(())
748 }
749
750 #[test]
753 fn test_finite_recursion_objects() -> eyre::Result<()> {
754 crate::tests::init();
755
756 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 Ok(())
768 }
769
770 #[test]
773 fn test_finite_recursion_arrays() -> eyre::Result<()> {
774 crate::tests::init();
775
776 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 Ok(())
788 }
789
790 #[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 }
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}