Skip to main content

facet_json/
error.rs

1//! Error types for JSON deserialization.
2
3use alloc::format;
4use alloc::string::{String, ToString};
5use alloc::vec::Vec;
6use core::fmt::{self, Display};
7
8use facet_reflect::{ReflectError, Span};
9
10use crate::adapter::{AdapterError, AdapterErrorKind};
11use crate::scanner::ScanErrorKind;
12
13/// Error type for JSON deserialization.
14#[derive(Debug)]
15pub struct JsonError {
16    /// The specific kind of error
17    pub kind: JsonErrorKind,
18    /// Source span where the error occurred
19    pub span: Option<Span>,
20    /// The source input (for diagnostics)
21    pub source_code: Option<String>,
22}
23
24impl Display for JsonError {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        write!(f, "{}", self.kind)
27    }
28}
29
30impl std::error::Error for JsonError {}
31
32impl JsonError {
33    /// Create a new error with span information
34    pub const fn new(kind: JsonErrorKind, span: Span) -> Self {
35        JsonError {
36            kind,
37            span: Some(span),
38            source_code: None,
39        }
40    }
41
42    /// Create an error without span information
43    pub const fn without_span(kind: JsonErrorKind) -> Self {
44        JsonError {
45            kind,
46            span: None,
47            source_code: None,
48        }
49    }
50
51    /// Attach source code for rich diagnostics
52    pub fn with_source(mut self, source: &str) -> Self {
53        self.source_code = Some(source.to_string());
54        self
55    }
56}
57
58/// Specific error kinds for JSON deserialization
59#[derive(Debug)]
60pub enum JsonErrorKind {
61    /// Scanner/adapter error
62    Scan(ScanErrorKind),
63    /// Scanner error with type context (what type was being parsed)
64    ScanWithContext {
65        /// The underlying scan error
66        error: ScanErrorKind,
67        /// The type that was being parsed
68        expected_type: &'static str,
69    },
70    /// Unexpected token
71    UnexpectedToken {
72        /// The token that was found
73        got: String,
74        /// What was expected instead
75        expected: &'static str,
76    },
77    /// Unexpected end of input
78    UnexpectedEof {
79        /// What was expected before EOF
80        expected: &'static str,
81    },
82    /// Type mismatch
83    TypeMismatch {
84        /// The expected type
85        expected: &'static str,
86        /// The actual type found
87        got: &'static str,
88    },
89    /// Unknown field in struct
90    UnknownField {
91        /// The unknown field name
92        field: String,
93        /// List of valid field names
94        expected: Vec<&'static str>,
95        /// Suggested field name (if similar to an expected field)
96        suggestion: Option<&'static str>,
97    },
98    /// Missing required field
99    MissingField {
100        /// The name of the missing field
101        field: &'static str,
102        /// Span of the object start (opening brace)
103        object_start: Option<Span>,
104        /// Span of the object end (closing brace)
105        object_end: Option<Span>,
106    },
107    /// Invalid value for type
108    InvalidValue {
109        /// Description of why the value is invalid
110        message: String,
111    },
112    /// Reflection error from facet-reflect
113    Reflect(ReflectError),
114    /// Number out of range
115    NumberOutOfRange {
116        /// The numeric value that was out of range
117        value: String,
118        /// The target type that couldn't hold the value
119        target_type: &'static str,
120    },
121    /// Duplicate key in object
122    DuplicateKey {
123        /// The key that appeared more than once
124        key: String,
125    },
126    /// Invalid UTF-8 in string
127    InvalidUtf8,
128    /// Solver error (for flattened types)
129    Solver(String),
130    /// I/O error (for streaming deserialization)
131    Io(String),
132}
133
134impl Display for JsonErrorKind {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        match self {
137            JsonErrorKind::Scan(e) => write!(f, "{e:?}"),
138            JsonErrorKind::ScanWithContext {
139                error,
140                expected_type,
141            } => {
142                write!(f, "{error:?} (while parsing {expected_type})")
143            }
144            JsonErrorKind::UnexpectedToken { got, expected } => {
145                write!(f, "unexpected token: got {got}, expected {expected}")
146            }
147            JsonErrorKind::UnexpectedEof { expected } => {
148                write!(f, "unexpected end of input, expected {expected}")
149            }
150            JsonErrorKind::TypeMismatch { expected, got } => {
151                write!(f, "type mismatch: expected {expected}, got {got}")
152            }
153            JsonErrorKind::UnknownField {
154                field,
155                expected,
156                suggestion,
157            } => {
158                write!(f, "unknown field `{field}`, expected one of: {expected:?}")?;
159                if let Some(suggested) = suggestion {
160                    write!(f, " (did you mean `{suggested}`?)")?;
161                }
162                Ok(())
163            }
164            JsonErrorKind::MissingField { field, .. } => {
165                write!(f, "missing required field `{field}`")
166            }
167            JsonErrorKind::InvalidValue { message } => {
168                write!(f, "invalid value: {message}")
169            }
170            JsonErrorKind::Reflect(e) => write!(f, "reflection error: {e}"),
171            JsonErrorKind::NumberOutOfRange { value, target_type } => {
172                write!(f, "number `{value}` out of range for {target_type}")
173            }
174            JsonErrorKind::DuplicateKey { key } => {
175                write!(f, "duplicate key `{key}`")
176            }
177            JsonErrorKind::InvalidUtf8 => write!(f, "invalid UTF-8 sequence"),
178            JsonErrorKind::Solver(msg) => write!(f, "solver error: {msg}"),
179            JsonErrorKind::Io(msg) => write!(f, "I/O error: {msg}"),
180        }
181    }
182}
183
184impl JsonErrorKind {
185    /// Get an error code for this kind of error.
186    pub const fn code(&self) -> &'static str {
187        match self {
188            JsonErrorKind::Scan(_) => "json::scan",
189            JsonErrorKind::ScanWithContext { .. } => "json::scan",
190            JsonErrorKind::UnexpectedToken { .. } => "json::unexpected_token",
191            JsonErrorKind::UnexpectedEof { .. } => "json::unexpected_eof",
192            JsonErrorKind::TypeMismatch { .. } => "json::type_mismatch",
193            JsonErrorKind::UnknownField { .. } => "json::unknown_field",
194            JsonErrorKind::MissingField { .. } => "json::missing_field",
195            JsonErrorKind::InvalidValue { .. } => "json::invalid_value",
196            JsonErrorKind::Reflect(_) => "json::reflect",
197            JsonErrorKind::NumberOutOfRange { .. } => "json::number_out_of_range",
198            JsonErrorKind::DuplicateKey { .. } => "json::duplicate_key",
199            JsonErrorKind::InvalidUtf8 => "json::invalid_utf8",
200            JsonErrorKind::Solver(_) => "json::solver",
201            JsonErrorKind::Io(_) => "json::io",
202        }
203    }
204
205    /// Get a label describing where/what the error points to.
206    pub fn label(&self) -> String {
207        match self {
208            JsonErrorKind::Scan(e) => match e {
209                ScanErrorKind::UnexpectedChar(c) => format!("unexpected '{c}'"),
210                ScanErrorKind::UnexpectedEof(ctx) => format!("unexpected end of input {ctx}"),
211                ScanErrorKind::InvalidUtf8 => "invalid UTF-8 here".into(),
212            },
213            JsonErrorKind::ScanWithContext {
214                error,
215                expected_type,
216            } => match error {
217                ScanErrorKind::UnexpectedChar(c) => {
218                    format!("unexpected '{c}', expected {expected_type}")
219                }
220                ScanErrorKind::UnexpectedEof(_) => {
221                    format!("unexpected end of input, expected {expected_type}")
222                }
223                ScanErrorKind::InvalidUtf8 => "invalid UTF-8 here".into(),
224            },
225            JsonErrorKind::UnexpectedToken { got, expected } => {
226                format!("expected {expected}, got '{got}'")
227            }
228            JsonErrorKind::UnexpectedEof { expected } => format!("expected {expected}"),
229            JsonErrorKind::TypeMismatch { expected, got } => {
230                format!("expected {expected}, got {got}")
231            }
232            JsonErrorKind::UnknownField {
233                field, suggestion, ..
234            } => {
235                if let Some(suggested) = suggestion {
236                    format!("unknown field '{field}' - did you mean '{suggested}'?")
237                } else {
238                    format!("unknown field '{field}'")
239                }
240            }
241            JsonErrorKind::MissingField { field, .. } => format!("missing field '{field}'"),
242            JsonErrorKind::InvalidValue { .. } => "invalid value".into(),
243            JsonErrorKind::Reflect(_) => "reflection error".into(),
244            JsonErrorKind::NumberOutOfRange { target_type, .. } => {
245                format!("out of range for {target_type}")
246            }
247            JsonErrorKind::DuplicateKey { key } => format!("duplicate key '{key}'"),
248            JsonErrorKind::InvalidUtf8 => "invalid UTF-8".into(),
249            JsonErrorKind::Solver(_) => "solver error".into(),
250            JsonErrorKind::Io(_) => "I/O error".into(),
251        }
252    }
253}
254
255impl From<AdapterError> for JsonError {
256    fn from(err: AdapterError) -> Self {
257        let kind = match err.kind {
258            AdapterErrorKind::Scan(scan_err) => JsonErrorKind::Scan(scan_err),
259            AdapterErrorKind::NeedMore => JsonErrorKind::UnexpectedEof {
260                expected: "more data",
261            },
262        };
263        JsonError {
264            kind,
265            span: Some(err.span),
266            source_code: None,
267        }
268    }
269}
270
271impl From<ReflectError> for JsonError {
272    fn from(err: ReflectError) -> Self {
273        JsonError {
274            kind: JsonErrorKind::Reflect(err),
275            span: None,
276            source_code: None,
277        }
278    }
279}
280
281/// Result type for JSON deserialization
282#[allow(dead_code)]
283pub type Result<T> = core::result::Result<T, JsonError>;