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