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