Skip to main content

blazingly_json/
error.rs

1use std::fmt;
2use std::io;
3
4/// Error returned while parsing or encoding JSON.
5#[derive(Debug)]
6pub struct Error {
7    kind: ErrorKind,
8    line: usize,
9    column: usize,
10}
11
12#[derive(Debug)]
13enum ErrorKind {
14    Message(String),
15    Io(io::Error),
16}
17
18impl Error {
19    pub(crate) fn syntax(message: impl Into<String>, input: &[u8], index: usize) -> Self {
20        let end = index.min(input.len());
21        let line = 1 + memchr::memchr_iter(b'\n', &input[..end]).count();
22        let column = input[..end]
23            .iter()
24            .rposition(|&byte| byte == b'\n')
25            .map_or(end + 1, |position| end - position);
26        Self {
27            kind: ErrorKind::Message(message.into()),
28            line,
29            column,
30        }
31    }
32
33    pub(crate) fn message(message: impl Into<String>) -> Self {
34        Self {
35            kind: ErrorKind::Message(message.into()),
36            line: 0,
37            column: 0,
38        }
39    }
40
41    /// One-based line of a syntax error, or zero for a non-syntax error.
42    #[must_use]
43    pub const fn line(&self) -> usize {
44        self.line
45    }
46
47    /// One-based column of a syntax error, or zero for a non-syntax error.
48    #[must_use]
49    pub const fn column(&self) -> usize {
50        self.column
51    }
52
53    /// Returns true when the error originated from an output writer.
54    #[must_use]
55    pub const fn is_io(&self) -> bool {
56        matches!(self.kind, ErrorKind::Io(_))
57    }
58}
59
60impl fmt::Display for Error {
61    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
62        match &self.kind {
63            ErrorKind::Message(message) if self.line != 0 => {
64                write!(
65                    formatter,
66                    "{message} at line {} column {}",
67                    self.line, self.column
68                )
69            }
70            ErrorKind::Message(message) => formatter.write_str(message),
71            ErrorKind::Io(error) => error.fmt(formatter),
72        }
73    }
74}
75
76impl std::error::Error for Error {
77    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
78        match &self.kind {
79            ErrorKind::Io(error) => Some(error),
80            ErrorKind::Message(_) => None,
81        }
82    }
83}
84
85impl serde::de::Error for Error {
86    fn custom<T: fmt::Display>(message: T) -> Self {
87        Self::message(message.to_string())
88    }
89}
90
91impl serde::ser::Error for Error {
92    fn custom<T: fmt::Display>(message: T) -> Self {
93        Self::message(message.to_string())
94    }
95}
96
97impl From<io::Error> for Error {
98    fn from(error: io::Error) -> Self {
99        Self {
100            kind: ErrorKind::Io(error),
101            line: 0,
102            column: 0,
103        }
104    }
105}
106
107impl From<Error> for io::Error {
108    fn from(error: Error) -> Self {
109        match error.kind {
110            ErrorKind::Io(error) => error,
111            ErrorKind::Message(message) => Self::new(io::ErrorKind::InvalidData, message),
112        }
113    }
114}
115
116/// Result alias used by this crate.
117pub type Result<T> = std::result::Result<T, Error>;