Skip to main content

ron/
error.rs

1use alloc::string::{String, ToString};
2use core::{
3    fmt,
4    str::{self, Utf8Error},
5};
6
7use serde::{
8    de,
9    ser::{self, StdError},
10};
11use unicode_ident::is_xid_continue;
12
13use crate::parse::{is_ident_first_char, is_ident_raw_char};
14
15#[cfg(feature = "std")]
16use std::io;
17
18/// This type represents all possible errors that can occur when
19/// serializing or deserializing RON data.
20#[allow(clippy::module_name_repetitions)]
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct SpannedError {
23    pub code: Error,
24    pub span: Span,
25}
26
27pub type Result<T, E = Error> = core::result::Result<T, E>;
28pub type SpannedResult<T> = core::result::Result<T, SpannedError>;
29
30#[derive(Clone, Debug, PartialEq, Eq)]
31#[non_exhaustive]
32pub enum Error {
33    Fmt,
34    Io(String),
35    Message(String),
36    Eof,
37    ExpectedArray,
38    ExpectedArrayEnd,
39    ExpectedAttribute,
40    ExpectedAttributeEnd,
41    ExpectedBoolean,
42    ExpectedComma,
43    ExpectedChar,
44    ExpectedByteLiteral,
45    ExpectedFloat,
46    FloatUnderscore,
47    ExpectedInteger,
48    ExpectedOption,
49    ExpectedOptionEnd,
50    ExpectedMap,
51    ExpectedMapColon,
52    ExpectedMapEnd,
53    ExpectedDifferentStructName {
54        expected: &'static str,
55        found: String,
56    },
57    ExpectedStructLike,
58    ExpectedNamedStructLike(&'static str),
59    ExpectedStructLikeEnd,
60    ExpectedUnit,
61    ExpectedString,
62    ExpectedByteString,
63    ExpectedStringEnd,
64    ExpectedIdentifier,
65
66    InvalidEscape(&'static str),
67
68    IntegerOutOfBounds,
69    InvalidIntegerDigit {
70        digit: char,
71        base: u8,
72    },
73
74    NoSuchExtension(String),
75
76    UnclosedBlockComment,
77    UnclosedLineComment,
78    UnderscoreAtBeginning,
79    UnexpectedChar(char),
80
81    Utf8Error(Utf8Error),
82    TrailingCharacters,
83
84    InvalidValueForType {
85        expected: String,
86        found: String,
87    },
88    ExpectedDifferentLength {
89        expected: String,
90        found: usize,
91    },
92    NoSuchEnumVariant {
93        expected: &'static [&'static str],
94        found: String,
95        outer: Option<String>,
96    },
97    NoSuchStructField {
98        expected: &'static [&'static str],
99        found: String,
100        outer: Option<String>,
101    },
102    MissingStructField {
103        field: &'static str,
104        outer: Option<String>,
105    },
106    DuplicateStructField {
107        field: &'static str,
108        outer: Option<String>,
109    },
110    InvalidIdentifier(String),
111    SuggestRawIdentifier(String),
112    ExpectedRawValue,
113    ExceededRecursionLimit,
114    ExpectedStructName(String),
115    ExpectedRangeSyntax,
116}
117
118impl fmt::Display for SpannedError {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        write!(f, "{}: {}", self.span, self.code)
121    }
122}
123
124impl fmt::Display for Error {
125    #[allow(clippy::too_many_lines)]
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        match *self {
128            Error::Fmt => f.write_str("Formatting RON failed"),
129            Error::Io(ref s) | Error::Message(ref s) => f.write_str(s),
130            Error::Eof => f.write_str("Unexpected end of RON"),
131            Error::ExpectedArray => f.write_str("Expected opening `[`"),
132            Error::ExpectedArrayEnd => f.write_str("Expected closing `]`"),
133            Error::ExpectedAttribute => f.write_str("Expected an `#![enable(...)]` attribute"),
134            Error::ExpectedAttributeEnd => {
135                f.write_str("Expected closing `)]` after the enable attribute")
136            }
137            Error::ExpectedBoolean => f.write_str("Expected boolean"),
138            Error::ExpectedComma => f.write_str("Expected comma"),
139            Error::ExpectedChar => f.write_str("Expected char"),
140            Error::ExpectedByteLiteral => f.write_str("Expected byte literal"),
141            Error::ExpectedFloat => f.write_str("Expected float"),
142            Error::FloatUnderscore => f.write_str("Unexpected underscore in float"),
143            Error::ExpectedInteger => f.write_str("Expected integer"),
144            Error::ExpectedOption => f.write_str("Expected option"),
145            Error::ExpectedOptionEnd | Error::ExpectedStructLikeEnd => {
146                f.write_str("Expected closing `)`")
147            }
148            Error::ExpectedMap => f.write_str("Expected opening `{`"),
149            Error::ExpectedMapColon => f.write_str("Expected colon"),
150            Error::ExpectedMapEnd => f.write_str("Expected closing `}`"),
151            Error::ExpectedDifferentStructName {
152                expected,
153                ref found,
154            } => write!(
155                f,
156                "Expected struct {} but found {}",
157                Identifier(expected),
158                Identifier(found)
159            ),
160            Error::ExpectedStructLike => f.write_str("Expected opening `(`"),
161            Error::ExpectedNamedStructLike(name) => {
162                if name.is_empty() {
163                    f.write_str("Expected only opening `(`, no name, for un-nameable struct")
164                } else {
165                    write!(f, "Expected opening `(` for struct {}", Identifier(name))
166                }
167            }
168            Error::ExpectedUnit => f.write_str("Expected unit"),
169            Error::ExpectedString => f.write_str("Expected string"),
170            Error::ExpectedByteString => f.write_str("Expected byte string"),
171            Error::ExpectedStringEnd => f.write_str("Expected end of string"),
172            Error::ExpectedIdentifier => f.write_str("Expected identifier"),
173            Error::InvalidEscape(s) => f.write_str(s),
174            Error::IntegerOutOfBounds => f.write_str("Integer is out of bounds"),
175            Error::InvalidIntegerDigit { digit, base } => {
176                write!(f, "Invalid digit {:?} for base {} integers", digit, base)
177            }
178            Error::NoSuchExtension(ref name) => {
179                write!(f, "No RON extension named {}", Identifier(name))
180            }
181            Error::Utf8Error(ref e) => fmt::Display::fmt(e, f),
182            Error::UnclosedBlockComment => f.write_str("Unclosed block comment"),
183            Error::UnclosedLineComment => f.write_str(
184                "`ron::value::RawValue` cannot end in unclosed line comment, \
185                try using a block comment or adding a newline",
186            ),
187            Error::UnderscoreAtBeginning => {
188                f.write_str("Unexpected leading underscore in a number")
189            }
190            Error::UnexpectedChar(c) => write!(f, "Unexpected char {:?}", c),
191            Error::TrailingCharacters => f.write_str("Non-whitespace trailing characters"),
192            Error::InvalidValueForType {
193                ref expected,
194                ref found,
195            } => {
196                write!(f, "Expected {} but found {} instead", expected, found)
197            }
198            Error::ExpectedDifferentLength {
199                ref expected,
200                found,
201            } => {
202                write!(f, "Expected {} but found ", expected)?;
203
204                match found {
205                    0 => f.write_str("zero elements")?,
206                    1 => f.write_str("one element")?,
207                    n => write!(f, "{} elements", n)?,
208                }
209
210                f.write_str(" instead")
211            }
212            Error::NoSuchEnumVariant {
213                expected,
214                ref found,
215                ref outer,
216            } => {
217                f.write_str("Unexpected ")?;
218
219                if outer.is_none() {
220                    f.write_str("enum ")?;
221                }
222
223                write!(f, "variant named {}", Identifier(found))?;
224
225                if let Some(outer) = outer {
226                    write!(f, " in enum {}", Identifier(outer))?;
227                }
228
229                write!(
230                    f,
231                    ", {}",
232                    OneOf {
233                        alts: expected,
234                        none: "variants"
235                    }
236                )
237            }
238            Error::NoSuchStructField {
239                expected,
240                ref found,
241                ref outer,
242            } => {
243                write!(f, "Unexpected field named {}", Identifier(found))?;
244
245                if let Some(outer) = outer {
246                    write!(f, " in {}", Identifier(outer))?;
247                }
248
249                write!(
250                    f,
251                    ", {}",
252                    OneOf {
253                        alts: expected,
254                        none: "fields"
255                    }
256                )
257            }
258            Error::MissingStructField { field, ref outer } => {
259                write!(f, "Unexpected missing field named {}", Identifier(field))?;
260
261                match outer {
262                    Some(outer) => write!(f, " in {}", Identifier(outer)),
263                    None => Ok(()),
264                }
265            }
266            Error::DuplicateStructField { field, ref outer } => {
267                write!(f, "Unexpected duplicate field named {}", Identifier(field))?;
268
269                match outer {
270                    Some(outer) => write!(f, " in {}", Identifier(outer)),
271                    None => Ok(()),
272                }
273            }
274            Error::InvalidIdentifier(ref invalid) => write!(f, "Invalid identifier {:?}", invalid),
275            Error::SuggestRawIdentifier(ref identifier) => write!(
276                f,
277                "Found invalid std identifier {:?}, try the raw identifier `r#{}` instead",
278                identifier, identifier
279            ),
280            Error::ExpectedRawValue => f.write_str("Expected a `ron::value::RawValue`"),
281            Error::ExceededRecursionLimit => f.write_str(
282                "Exceeded recursion limit, try increasing `ron::Options::recursion_limit` \
283                and using `serde_stacker` to protect against a stack overflow",
284            ),
285            Error::ExpectedStructName(ref name) => write!(
286                f,
287                "Expected the explicit struct name {}, but none was found",
288                Identifier(name)
289            ),
290            Error::ExpectedRangeSyntax => f.write_str("Expected `..` or `..=` for range syntax"),
291        }
292    }
293}
294
295#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
296pub struct Position {
297    pub line: usize,
298    pub col: usize,
299}
300
301impl Position {
302    pub(crate) fn from_src_end(src: &str) -> Position {
303        let line = 1 + src.chars().filter(|&c| c == '\n').count();
304        let col = 1 + src.chars().rev().take_while(|&c| c != '\n').count();
305
306        Self { line, col }
307    }
308}
309
310impl fmt::Display for Position {
311    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312        write!(f, "{}:{}", self.line, self.col)
313    }
314}
315
316#[derive(Clone, Debug, PartialEq, Eq)]
317/// Spans select a range of text between two positions.
318/// Spans are used in [`SpannedError`] to indicate the start and end positions
319/// of the parser cursor before and after it encountered an error in parsing.
320pub struct Span {
321    pub start: Position,
322    pub end: Position,
323}
324
325impl fmt::Display for Span {
326    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327        if self.start == self.end {
328            write!(f, "{}", self.start)
329        } else {
330            write!(f, "{}-{}", self.start, self.end)
331        }
332    }
333}
334
335impl ser::Error for Error {
336    #[cold]
337    fn custom<T: fmt::Display>(msg: T) -> Self {
338        Error::Message(msg.to_string())
339    }
340}
341
342impl de::Error for Error {
343    #[cold]
344    fn custom<T: fmt::Display>(msg: T) -> Self {
345        Error::Message(msg.to_string())
346    }
347
348    #[cold]
349    fn invalid_type(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
350        // Invalid type and invalid value are merged given their similarity in ron
351        Self::invalid_value(unexp, exp)
352    }
353
354    #[cold]
355    fn invalid_value(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
356        struct UnexpectedSerdeTypeValue<'a>(de::Unexpected<'a>);
357
358        impl<'a> fmt::Display for UnexpectedSerdeTypeValue<'a> {
359            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360                match self.0 {
361                    de::Unexpected::Bool(b) => write!(f, "the boolean `{}`", b),
362                    de::Unexpected::Unsigned(i) => write!(f, "the unsigned integer `{}`", i),
363                    de::Unexpected::Signed(i) => write!(f, "the signed integer `{}`", i),
364                    de::Unexpected::Float(n) => write!(f, "the floating point number `{}`", n),
365                    de::Unexpected::Char(c) => write!(f, "the UTF-8 character `{}`", c),
366                    de::Unexpected::Str(s) => write!(f, "the string {:?}", s),
367                    de::Unexpected::Bytes(b) => write!(f, "the byte string b\"{}\"", {
368                        b.iter()
369                            .flat_map(|c| core::ascii::escape_default(*c))
370                            .map(char::from)
371                            .collect::<String>()
372                    }),
373                    de::Unexpected::Unit => write!(f, "a unit value"),
374                    de::Unexpected::Option => write!(f, "an optional value"),
375                    de::Unexpected::NewtypeStruct => write!(f, "a newtype struct"),
376                    de::Unexpected::Seq => write!(f, "a sequence"),
377                    de::Unexpected::Map => write!(f, "a map"),
378                    de::Unexpected::Enum => write!(f, "an enum"),
379                    de::Unexpected::UnitVariant => write!(f, "a unit variant"),
380                    de::Unexpected::NewtypeVariant => write!(f, "a newtype variant"),
381                    de::Unexpected::TupleVariant => write!(f, "a tuple variant"),
382                    de::Unexpected::StructVariant => write!(f, "a struct variant"),
383                    de::Unexpected::Other(other) => f.write_str(other),
384                }
385            }
386        }
387
388        Error::InvalidValueForType {
389            expected: exp.to_string(),
390            found: UnexpectedSerdeTypeValue(unexp).to_string(),
391        }
392    }
393
394    #[cold]
395    fn invalid_length(len: usize, exp: &dyn de::Expected) -> Self {
396        Error::ExpectedDifferentLength {
397            expected: exp.to_string(),
398            found: len,
399        }
400    }
401
402    #[cold]
403    fn unknown_variant(variant: &str, expected: &'static [&'static str]) -> Self {
404        Error::NoSuchEnumVariant {
405            expected,
406            found: variant.to_string(),
407            outer: None,
408        }
409    }
410
411    #[cold]
412    fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self {
413        Error::NoSuchStructField {
414            expected,
415            found: field.to_string(),
416            outer: None,
417        }
418    }
419
420    #[cold]
421    fn missing_field(field: &'static str) -> Self {
422        Error::MissingStructField { field, outer: None }
423    }
424
425    #[cold]
426    fn duplicate_field(field: &'static str) -> Self {
427        Error::DuplicateStructField { field, outer: None }
428    }
429}
430
431impl StdError for SpannedError {}
432
433impl StdError for Error {}
434
435impl From<Utf8Error> for Error {
436    fn from(e: Utf8Error) -> Self {
437        Error::Utf8Error(e)
438    }
439}
440
441impl From<fmt::Error> for Error {
442    fn from(_: fmt::Error) -> Self {
443        Error::Fmt
444    }
445}
446
447#[cfg(feature = "std")]
448impl From<io::Error> for Error {
449    fn from(e: io::Error) -> Self {
450        Error::Io(e.to_string())
451    }
452}
453
454impl From<SpannedError> for Error {
455    fn from(e: SpannedError) -> Self {
456        e.code
457    }
458}
459
460struct OneOf {
461    alts: &'static [&'static str],
462    none: &'static str,
463}
464
465impl fmt::Display for OneOf {
466    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467        match self.alts {
468            [] => write!(f, "there are no {}", self.none),
469            [a1] => write!(f, "expected {} instead", Identifier(a1)),
470            [a1, a2] => write!(
471                f,
472                "expected either {} or {} instead",
473                Identifier(a1),
474                Identifier(a2)
475            ),
476            [a1, ref alts @ .., an] => {
477                write!(f, "expected one of {}", Identifier(a1))?;
478
479                for alt in alts {
480                    write!(f, ", {}", Identifier(alt))?;
481                }
482
483                write!(f, ", or {} instead", Identifier(an))
484            }
485        }
486    }
487}
488
489struct Identifier<'a>(&'a str);
490
491impl<'a> fmt::Display for Identifier<'a> {
492    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493        if self.0.is_empty() || !self.0.chars().all(is_ident_raw_char) {
494            return write!(f, "{:?}_[invalid identifier]", self.0);
495        }
496
497        let mut chars = self.0.chars();
498
499        if !chars.next().map_or(false, is_ident_first_char) || !chars.all(is_xid_continue) {
500            write!(f, "`r#{}`", self.0)
501        } else {
502            write!(f, "`{}`", self.0)
503        }
504    }
505}
506
507#[cfg(test)]
508mod tests {
509    use alloc::{format, string::String};
510
511    use serde::{de::Error as DeError, de::Unexpected, ser::Error as SerError};
512
513    use super::{Error, Position, Span, SpannedError};
514
515    #[test]
516    fn error_messages() {
517        check_error_message(&Error::from(core::fmt::Error), "Formatting RON failed");
518        #[cfg(feature = "std")]
519        check_error_message(
520            &Error::from(std::io::Error::new(
521                std::io::ErrorKind::InvalidData,
522                "my-error",
523            )),
524            "my-error",
525        );
526        check_error_message(&<Error as SerError>::custom("my-ser-error"), "my-ser-error");
527        check_error_message(&<Error as DeError>::custom("my-de-error"), "my-de-error");
528        check_error_message(&Error::Eof, "Unexpected end of RON");
529        check_error_message(&Error::ExpectedArray, "Expected opening `[`");
530        check_error_message(&Error::ExpectedArrayEnd, "Expected closing `]`");
531        check_error_message(
532            &Error::ExpectedAttribute,
533            "Expected an `#![enable(...)]` attribute",
534        );
535        check_error_message(
536            &Error::ExpectedAttributeEnd,
537            "Expected closing `)]` after the enable attribute",
538        );
539        check_error_message(&Error::ExpectedBoolean, "Expected boolean");
540        check_error_message(&Error::ExpectedComma, "Expected comma");
541        check_error_message(&Error::ExpectedChar, "Expected char");
542        check_error_message(&Error::ExpectedByteLiteral, "Expected byte literal");
543        check_error_message(&Error::ExpectedFloat, "Expected float");
544        check_error_message(&Error::FloatUnderscore, "Unexpected underscore in float");
545        check_error_message(&Error::ExpectedInteger, "Expected integer");
546        check_error_message(&Error::ExpectedOption, "Expected option");
547        check_error_message(&Error::ExpectedOptionEnd, "Expected closing `)`");
548        check_error_message(&Error::ExpectedStructLikeEnd, "Expected closing `)`");
549        check_error_message(&Error::ExpectedMap, "Expected opening `{`");
550        check_error_message(&Error::ExpectedMapColon, "Expected colon");
551        check_error_message(&Error::ExpectedMapEnd, "Expected closing `}`");
552        check_error_message(
553            &Error::ExpectedDifferentStructName {
554                expected: "raw+identifier",
555                found: String::from("identifier"),
556            },
557            "Expected struct `r#raw+identifier` but found `identifier`",
558        );
559        check_error_message(&Error::ExpectedStructLike, "Expected opening `(`");
560        check_error_message(
561            &Error::ExpectedNamedStructLike(""),
562            "Expected only opening `(`, no name, for un-nameable struct",
563        );
564        check_error_message(
565            &Error::ExpectedNamedStructLike("_ident"),
566            "Expected opening `(` for struct `_ident`",
567        );
568        check_error_message(&Error::ExpectedUnit, "Expected unit");
569        check_error_message(&Error::ExpectedString, "Expected string");
570        check_error_message(&Error::ExpectedByteString, "Expected byte string");
571        check_error_message(&Error::ExpectedStringEnd, "Expected end of string");
572        check_error_message(&Error::ExpectedIdentifier, "Expected identifier");
573        check_error_message(&Error::InvalidEscape("Invalid escape"), "Invalid escape");
574        check_error_message(&Error::IntegerOutOfBounds, "Integer is out of bounds");
575        check_error_message(
576            &Error::InvalidIntegerDigit {
577                digit: 'q',
578                base: 16,
579            },
580            "Invalid digit 'q' for base 16 integers",
581        );
582        check_error_message(
583            &Error::NoSuchExtension(String::from("unknown")),
584            "No RON extension named `unknown`",
585        );
586        check_error_message(&Error::UnclosedBlockComment, "Unclosed block comment");
587        check_error_message(
588            &Error::UnclosedLineComment,
589            "`ron::value::RawValue` cannot end in unclosed line comment, \
590        try using a block comment or adding a newline",
591        );
592        check_error_message(
593            &Error::UnderscoreAtBeginning,
594            "Unexpected leading underscore in a number",
595        );
596        check_error_message(&Error::UnexpectedChar('🦀'), "Unexpected char \'🦀\'");
597        #[allow(invalid_from_utf8)]
598        check_error_message(
599            &Error::Utf8Error(core::str::from_utf8(b"error: \xff\xff\xff\xff").unwrap_err()),
600            "invalid utf-8 sequence of 1 bytes from index 7",
601        );
602        check_error_message(
603            &Error::TrailingCharacters,
604            "Non-whitespace trailing characters",
605        );
606        check_error_message(
607            &Error::invalid_value(Unexpected::Enum, &"struct `Hi`"),
608            "Expected struct `Hi` but found an enum instead",
609        );
610        check_error_message(
611            &Error::invalid_length(0, &"two bees"),
612            "Expected two bees but found zero elements instead",
613        );
614        check_error_message(
615            &Error::invalid_length(1, &"two bees"),
616            "Expected two bees but found one element instead",
617        );
618        check_error_message(
619            &Error::invalid_length(3, &"two bees"),
620            "Expected two bees but found 3 elements instead",
621        );
622        check_error_message(
623            &Error::unknown_variant("unknown", &[]),
624            "Unexpected enum variant named `unknown`, there are no variants",
625        );
626        check_error_message(
627            &Error::NoSuchEnumVariant {
628                expected: &["A", "B+C"],
629                found: String::from("D"),
630                outer: Some(String::from("E")),
631            },
632            "Unexpected variant named `D` in enum `E`, \
633            expected either `A` or `r#B+C` instead",
634        );
635        check_error_message(
636            &Error::unknown_field("unknown", &[]),
637            "Unexpected field named `unknown`, there are no fields",
638        );
639        check_error_message(
640            &Error::NoSuchStructField {
641                expected: &["a"],
642                found: String::from("b"),
643                outer: Some(String::from("S")),
644            },
645            "Unexpected field named `b` in `S`, expected `a` instead",
646        );
647        check_error_message(
648            &Error::NoSuchStructField {
649                expected: &["a", "b+c", "d"],
650                found: String::from("e"),
651                outer: Some(String::from("S")),
652            },
653            "Unexpected field named `e` in `S`, \
654            expected one of `a`, `r#b+c`, or `d` instead",
655        );
656        check_error_message(
657            &Error::missing_field("a"),
658            "Unexpected missing field named `a`",
659        );
660        check_error_message(
661            &Error::MissingStructField {
662                field: "",
663                outer: Some(String::from("S+T")),
664            },
665            "Unexpected missing field named \"\"_[invalid identifier] in `r#S+T`",
666        );
667        check_error_message(
668            &Error::duplicate_field("a"),
669            "Unexpected duplicate field named `a`",
670        );
671        check_error_message(
672            &Error::DuplicateStructField {
673                field: "b+c",
674                outer: Some(String::from("S+T")),
675            },
676            "Unexpected duplicate field named `r#b+c` in `r#S+T`",
677        );
678        check_error_message(
679            &Error::InvalidIdentifier(String::from("why+🦀+not")),
680            "Invalid identifier \"why+🦀+not\"",
681        );
682        check_error_message(
683            &Error::SuggestRawIdentifier(String::from("raw+ident")),
684            "Found invalid std identifier \"raw+ident\", \
685            try the raw identifier `r#raw+ident` instead",
686        );
687        check_error_message(
688            &Error::ExpectedRawValue,
689            "Expected a `ron::value::RawValue`",
690        );
691        check_error_message(
692            &Error::ExceededRecursionLimit,
693            "Exceeded recursion limit, try increasing `ron::Options::recursion_limit` \
694            and using `serde_stacker` to protect against a stack overflow",
695        );
696        check_error_message(
697            &Error::ExpectedStructName(String::from("Struct")),
698            "Expected the explicit struct name `Struct`, but none was found",
699        );
700    }
701
702    fn check_error_message<T: core::fmt::Display>(err: &T, msg: &str) {
703        assert_eq!(format!("{}", err), msg);
704    }
705
706    #[test]
707    fn spanned_error_into_code() {
708        assert_eq!(
709            Error::from(SpannedError {
710                code: Error::Eof,
711                span: Span {
712                    start: Position { line: 1, col: 1 },
713                    end: Position { line: 1, col: 5 },
714                }
715            }),
716            Error::Eof
717        );
718        assert_eq!(
719            Error::from(SpannedError {
720                code: Error::ExpectedRawValue,
721                span: Span {
722                    start: Position { line: 1, col: 1 },
723                    end: Position { line: 1, col: 5 },
724                }
725            }),
726            Error::ExpectedRawValue
727        );
728    }
729}