serde_json5/
error.rs

1use pest::Span;
2use serde::{de, ser};
3use std::fmt::{self, Display};
4
5use crate::de::Rule;
6
7/// Alias for a `Result` with error type `json5::Error`
8pub type Result<T> = std::result::Result<T, Error>;
9
10/// One-based line and column at which the error was detected.
11#[derive(Clone, Debug, PartialEq)]
12pub struct Location {
13    /// The one-based line number of the error.
14    pub line: usize,
15    /// The one-based column number of the error.
16    pub column: usize,
17}
18
19impl From<&Span<'_>> for Location {
20    fn from(s: &Span<'_>) -> Self {
21        let (line, column) = s.start_pos().line_col();
22        Self { line, column }
23    }
24}
25
26/// A bare bones error type which currently just collapses all the underlying errors in to a single
27/// string... This is fine for displaying to the user, but not very useful otherwise. Work to be
28/// done here.
29#[derive(Clone, Debug, PartialEq)]
30pub enum Error {
31    /// Just shove everything in a single variant for now.
32    Message {
33        /// The error message.
34        msg: String,
35        /// The location of the error, if applicable.
36        location: Option<Location>,
37    },
38}
39
40impl From<pest::error::Error<Rule>> for Error {
41    fn from(err: pest::error::Error<Rule>) -> Self {
42        let (line, column) = match err.line_col {
43            pest::error::LineColLocation::Pos((l, c)) => (l, c),
44            pest::error::LineColLocation::Span((l, c), (_, _)) => (l, c),
45        };
46        Error::Message {
47            msg: err.to_string(),
48            location: Some(Location { line, column }),
49        }
50    }
51}
52
53impl From<std::io::Error> for Error {
54    fn from(err: std::io::Error) -> Self {
55        Error::Message {
56            msg: err.to_string(),
57            location: None,
58        }
59    }
60}
61
62impl From<std::string::FromUtf8Error> for Error {
63    fn from(err: std::string::FromUtf8Error) -> Self {
64        Error::Message {
65            msg: err.to_string(),
66            location: None,
67        }
68    }
69}
70
71impl From<std::str::Utf8Error> for Error {
72    fn from(err: std::str::Utf8Error) -> Self {
73        Error::Message {
74            msg: err.to_string(),
75            location: None,
76        }
77    }
78}
79
80impl ser::Error for Error {
81    fn custom<T: Display>(msg: T) -> Self {
82        Error::Message {
83            msg: msg.to_string(),
84            location: None,
85        }
86    }
87}
88
89impl de::Error for Error {
90    fn custom<T: Display>(msg: T) -> Self {
91        Error::Message {
92            msg: msg.to_string(),
93            location: None,
94        }
95    }
96}
97
98impl Display for Error {
99    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
100        match self {
101            Error::Message { ref msg, .. } => write!(formatter, "{}", msg),
102        }
103    }
104}
105
106impl std::error::Error for Error {}
107
108/// Adds location information from `span`, if `res` is an error.
109pub fn set_location<T>(res: &mut Result<T>, span: &Span<'_>) {
110    if let Err(ref mut e) = res {
111        let Error::Message { location, .. } = e;
112        if location.is_none() {
113            let (line, column) = span.start_pos().line_col();
114            *location = Some(Location { line, column });
115        }
116    }
117}