json5/
error.rs

1use std::fmt::{Display, Formatter};
2
3pub type Result<T> = std::result::Result<T, Error>;
4
5/// An error serializing or deserializing JSON5.
6#[derive(Debug, PartialEq, Clone)]
7pub struct Error {
8    inner: Box<ErrorInner>,
9}
10
11impl Error {
12    #[must_use]
13    pub fn code(&self) -> Option<ErrorCode> {
14        match self.inner.content {
15            ErrorContent::Code(code) => Some(code),
16            ErrorContent::Custom(_) => None,
17        }
18    }
19
20    #[must_use]
21    pub fn position(&self) -> Option<Position> {
22        self.inner.position
23    }
24}
25
26#[derive(Debug, PartialEq, Clone)]
27struct ErrorInner {
28    content: ErrorContent,
29    position: Option<Position>,
30}
31
32#[derive(Debug, PartialEq, Clone)]
33enum ErrorContent {
34    Code(ErrorCode),
35    Custom(String),
36}
37
38/// A code identifying an error originating within this crate.
39#[derive(Debug, PartialEq, Clone, Copy)]
40pub enum ErrorCode {
41    EofParsingArray,
42    EofParsingBool,
43    EofParsingComment,
44    EofParsingEscapeSequence,
45    EofParsingIdentifier,
46    EofParsingNull,
47    EofParsingNumber,
48    EofParsingObject,
49    EofParsingString,
50    EofParsingValue,
51
52    ExpectedBool,
53    ExpectedClosingBrace,
54    ExpectedClosingBracket,
55    ExpectedColon,
56    ExpectedComma,
57    ExpectedComment,
58    ExpectedIdentifier,
59    ExpectedNull,
60    ExpectedNumber,
61    ExpectedOpeningBrace,
62    ExpectedOpeningBracket,
63    ExpectedString,
64    ExpectedStringOrObject,
65    ExpectedValue,
66
67    InvalidBytes,
68    InvalidEscapeSequence,
69    InvalidKey,
70    LeadingZero,
71    LineTerminatorInString,
72    OverflowParsingNumber,
73    TrailingCharacters,
74}
75
76impl Display for ErrorCode {
77    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
78        match self {
79            ErrorCode::EofParsingArray => write!(f, "EOF parsing array"),
80            ErrorCode::EofParsingBool => write!(f, "EOF parsing bool"),
81            ErrorCode::EofParsingComment => write!(f, "EOF parsing comment"),
82            ErrorCode::EofParsingEscapeSequence => write!(f, "EOF parsing escape sequence"),
83            ErrorCode::EofParsingIdentifier => write!(f, "EOF parsing identifier"),
84            ErrorCode::EofParsingNull => write!(f, "EOF parsing null"),
85            ErrorCode::EofParsingNumber => write!(f, "EOF parsing number"),
86            ErrorCode::EofParsingObject => write!(f, "EOF parsing object"),
87            ErrorCode::EofParsingString => write!(f, "EOF parsing string"),
88            ErrorCode::EofParsingValue => write!(f, "EOF parsing value"),
89
90            ErrorCode::ExpectedBool => write!(f, "expected bool"),
91            ErrorCode::ExpectedClosingBrace => write!(f, "expected closing brace"),
92            ErrorCode::ExpectedClosingBracket => write!(f, "expected closing bracket"),
93            ErrorCode::ExpectedColon => write!(f, "expected colon"),
94            ErrorCode::ExpectedComma => write!(f, "expected comma"),
95            ErrorCode::ExpectedComment => write!(f, "expected comment"),
96            ErrorCode::ExpectedIdentifier => write!(f, "expected identifier"),
97            ErrorCode::ExpectedNull => write!(f, "expected null"),
98            ErrorCode::ExpectedNumber => write!(f, "expected number"),
99            ErrorCode::ExpectedOpeningBrace => write!(f, "expected opening brace"),
100            ErrorCode::ExpectedOpeningBracket => write!(f, "expected opening bracket"),
101            ErrorCode::ExpectedString => write!(f, "expected string"),
102            ErrorCode::ExpectedStringOrObject => write!(f, "expected string or object"),
103            ErrorCode::ExpectedValue => write!(f, "expected value"),
104
105            ErrorCode::InvalidBytes => write!(f, "invalid bytes"),
106            ErrorCode::InvalidEscapeSequence => write!(f, "invalid escape sequence"),
107            ErrorCode::InvalidKey => write!(f, "invalid key"),
108            ErrorCode::LeadingZero => write!(f, "leading zero"),
109            ErrorCode::LineTerminatorInString => write!(f, "line terminator in string"),
110            ErrorCode::OverflowParsingNumber => write!(f, "overflow parsing number"),
111            ErrorCode::TrailingCharacters => write!(f, "trailing characters"),
112        }
113    }
114}
115
116impl Display for ErrorContent {
117    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
118        match self {
119            ErrorContent::Code(code) => write!(f, "{code}"),
120            ErrorContent::Custom(msg) => write!(f, "{msg}"),
121        }
122    }
123}
124
125impl Display for Error {
126    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
127        if let Some(position) = self.inner.position {
128            write!(f, "{} at {}", self.inner.content, position)
129        } else {
130            write!(f, "{}", self.inner.content)
131        }
132    }
133}
134
135impl Error {
136    #[must_use]
137    pub fn new(code: ErrorCode) -> Self {
138        Self {
139            inner: Box::new(ErrorInner {
140                content: ErrorContent::Code(code),
141                position: None,
142            }),
143        }
144    }
145
146    #[must_use]
147    pub fn new_at(position: Position, code: ErrorCode) -> Self {
148        Self {
149            inner: Box::new(ErrorInner {
150                content: ErrorContent::Code(code),
151                position: Some(position),
152            }),
153        }
154    }
155
156    #[must_use]
157    pub fn custom<T: Display>(msg: T) -> Self {
158        Self {
159            inner: Box::new(ErrorInner {
160                content: ErrorContent::Custom(msg.to_string()),
161                position: None,
162            }),
163        }
164    }
165
166    #[must_use]
167    pub fn custom_at<T: Display>(position: Position, msg: T) -> Self {
168        Self {
169            inner: Box::new(ErrorInner {
170                content: ErrorContent::Custom(msg.to_string()),
171                position: Some(position),
172            }),
173        }
174    }
175
176    #[must_use]
177    pub fn with_position(mut self, position: Position) -> Self {
178        if self.inner.position.is_none() {
179            self.inner.position = Some(position);
180        }
181        self
182    }
183}
184
185impl serde::de::Error for Error {
186    fn custom<T: Display>(msg: T) -> Self {
187        Self::custom(msg)
188    }
189}
190
191impl serde::ser::Error for Error {
192    fn custom<T: Display>(msg: T) -> Self {
193        Self::custom(msg)
194    }
195}
196
197impl From<std::io::Error> for Error {
198    fn from(err: std::io::Error) -> Self {
199        Self::custom(err)
200    }
201}
202
203impl std::error::Error for Error {}
204
205/// The line and column that an error occured.
206#[derive(Debug, PartialEq, Clone, Copy)]
207pub struct Position {
208    /// The first line is line 0.
209    pub line: usize,
210    /// The first column is column 0.
211    pub column: usize,
212}
213
214impl Position {
215    #[must_use]
216    pub fn from_offset(offset: usize, input: &str) -> Self {
217        let mut res = Self { line: 0, column: 0 };
218        let mut chars = input[..offset].chars().peekable();
219        while let Some(c) = chars.next() {
220            if crate::char::is_json5_line_terminator(c) {
221                // "The character sequence <CR><LF> is commonly used as a line terminator. It
222                // should be considered a single character for the purpose of reporting line
223                // numbers." – https://262.ecma-international.org/5.1/#sec-7.3
224                if c == '\u{000D}' && chars.peek() == Some(&'\u{000A}') {
225                    chars.next();
226                }
227                res.line += 1;
228                res.column = 0;
229            } else {
230                res.column += 1;
231            }
232        }
233        res
234    }
235}
236
237impl Display for Position {
238    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
239        write!(f, "line {} column {}", self.line + 1, self.column + 1)
240    }
241}