Skip to main content

serde_rison/
error.rs

1use std::fmt::{self, Debug, Display};
2
3/// An error that occurred while serializing or deserializing RISON.
4pub struct Error {
5    inner: Box<ErrorImpl>,
6}
7
8struct ErrorImpl {
9    code: ErrorCode,
10    offset: Option<usize>,
11}
12
13/// Broad classification of an [`Error`].
14#[derive(Copy, Clone, Debug, PartialEq, Eq)]
15#[non_exhaustive]
16pub enum Category {
17    /// The input is not syntactically valid RISON.
18    Syntax,
19    /// The input is valid RISON but does not match the target type, or the
20    /// value cannot be represented in RISON.
21    Data,
22    /// The input ended before a complete value was read.
23    Eof,
24}
25
26pub(crate) enum ErrorCode {
27    Message(Box<str>),
28    EofWhileParsing,
29    ExpectedChar(char),
30    UnexpectedChar(char),
31    InvalidEscape(char),
32    InvalidNumber,
33    NumberOutOfRange,
34    TrailingCharacters,
35    RecursionLimitExceeded,
36    KeyMustBeAString,
37}
38
39impl Error {
40    pub(crate) fn syntax(code: ErrorCode, offset: usize) -> Self {
41        Error {
42            inner: Box::new(ErrorImpl {
43                code,
44                offset: Some(offset),
45            }),
46        }
47    }
48
49    pub(crate) fn data(code: ErrorCode) -> Self {
50        Error {
51            inner: Box::new(ErrorImpl { code, offset: None }),
52        }
53    }
54
55    pub(crate) fn fix_offset(mut self, offset: usize) -> Self {
56        self.inner.offset.get_or_insert(offset);
57        self
58    }
59
60    /// Byte offset into the input where the error was detected.
61    ///
62    /// `None` for errors that are not tied to a position, such as
63    /// serialization errors or errors from [`from_value`](crate::from_value).
64    #[must_use]
65    pub fn offset(&self) -> Option<usize> {
66        self.inner.offset
67    }
68
69    #[must_use]
70    pub fn classify(&self) -> Category {
71        match self.inner.code {
72            ErrorCode::Message(_) | ErrorCode::KeyMustBeAString | ErrorCode::NumberOutOfRange => {
73                Category::Data
74            }
75            ErrorCode::EofWhileParsing => Category::Eof,
76            ErrorCode::ExpectedChar(_)
77            | ErrorCode::UnexpectedChar(_)
78            | ErrorCode::InvalidEscape(_)
79            | ErrorCode::InvalidNumber
80            | ErrorCode::TrailingCharacters
81            | ErrorCode::RecursionLimitExceeded => Category::Syntax,
82        }
83    }
84
85    #[must_use]
86    pub fn is_syntax(&self) -> bool {
87        self.classify() == Category::Syntax
88    }
89
90    #[must_use]
91    pub fn is_data(&self) -> bool {
92        self.classify() == Category::Data
93    }
94
95    #[must_use]
96    pub fn is_eof(&self) -> bool {
97        self.classify() == Category::Eof
98    }
99}
100
101impl Display for ErrorCode {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        match self {
104            ErrorCode::Message(msg) => f.write_str(msg),
105            ErrorCode::EofWhileParsing => f.write_str("unexpected end of input"),
106            ErrorCode::ExpectedChar(c) => write!(f, "expected `{c}`"),
107            ErrorCode::UnexpectedChar(c) => write!(f, "unexpected character `{c}`"),
108            ErrorCode::InvalidEscape(c) => write!(f, "invalid escape `!{c}`"),
109            ErrorCode::InvalidNumber => f.write_str("invalid number"),
110            ErrorCode::NumberOutOfRange => f.write_str("number out of range"),
111            ErrorCode::TrailingCharacters => f.write_str("trailing characters"),
112            ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
113            ErrorCode::KeyMustBeAString => {
114                f.write_str("map key must be a string, integer, char or bool")
115            }
116        }
117    }
118}
119
120impl Display for Error {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        match self.inner.offset {
123            Some(offset) => write!(f, "{} at offset {offset}", self.inner.code),
124            None => Display::fmt(&self.inner.code, f),
125        }
126    }
127}
128
129impl Debug for Error {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        match self.inner.offset {
132            Some(offset) => write!(
133                f,
134                "Error({:?}, offset: {offset})",
135                self.inner.code.to_string()
136            ),
137            None => write!(f, "Error({:?})", self.inner.code.to_string()),
138        }
139    }
140}
141
142impl std::error::Error for Error {}
143
144impl serde::ser::Error for Error {
145    fn custom<T: Display>(msg: T) -> Self {
146        Error::data(ErrorCode::Message(msg.to_string().into_boxed_str()))
147    }
148}
149
150impl serde::de::Error for Error {
151    fn custom<T: Display>(msg: T) -> Self {
152        Error::data(ErrorCode::Message(msg.to_string().into_boxed_str()))
153    }
154}