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 miette::Diagnostic for JsonError {
33    fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
34        Some(Box::new(self.kind.code()))
35    }
36
37    fn source_code(&self) -> Option<&dyn miette::SourceCode> {
38        self.source_code
39            .as_ref()
40            .map(|s| s as &dyn miette::SourceCode)
41    }
42
43    fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
44        // Handle MissingField with multiple spans
45        if let JsonErrorKind::MissingField {
46            field,
47            object_start,
48            object_end,
49        } = &self.kind
50        {
51            let mut labels = Vec::new();
52            if let Some(start) = object_start {
53                labels.push(miette::LabeledSpan::new(
54                    Some("object started here".into()),
55                    start.offset,
56                    start.len,
57                ));
58            }
59            if let Some(end) = object_end {
60                labels.push(miette::LabeledSpan::new(
61                    Some(format!("object ended without field `{field}`")),
62                    end.offset,
63                    end.len,
64                ));
65            }
66            if labels.is_empty() {
67                return None;
68            }
69            return Some(Box::new(labels.into_iter()));
70        }
71
72        // Default: single span with label
73        let span = self.span?;
74        Some(Box::new(core::iter::once(miette::LabeledSpan::new(
75            Some(self.kind.label()),
76            span.offset,
77            span.len,
78        ))))
79    }
80}
81
82impl JsonError {
83    /// Create a new error with span information
84    pub fn new(kind: JsonErrorKind, span: Span) -> Self {
85        JsonError {
86            kind,
87            span: Some(span),
88            source_code: None,
89        }
90    }
91
92    /// Create an error without span information
93    pub fn without_span(kind: JsonErrorKind) -> Self {
94        JsonError {
95            kind,
96            span: None,
97            source_code: None,
98        }
99    }
100
101    /// Attach source code for rich diagnostics
102    pub fn with_source(mut self, source: &str) -> Self {
103        self.source_code = Some(source.to_string());
104        self
105    }
106}
107
108/// Specific error kinds for JSON deserialization
109#[derive(Debug)]
110pub enum JsonErrorKind {
111    /// Scanner/adapter error
112    Scan(ScanErrorKind),
113    /// Scanner error with type context (what type was being parsed)
114    ScanWithContext {
115        /// The underlying scan error
116        error: ScanErrorKind,
117        /// The type that was being parsed
118        expected_type: &'static str,
119    },
120    /// Unexpected token
121    UnexpectedToken {
122        /// The token that was found
123        got: String,
124        /// What was expected instead
125        expected: &'static str,
126    },
127    /// Unexpected end of input
128    UnexpectedEof {
129        /// What was expected before EOF
130        expected: &'static str,
131    },
132    /// Type mismatch
133    TypeMismatch {
134        /// The expected type
135        expected: &'static str,
136        /// The actual type found
137        got: &'static str,
138    },
139    /// Unknown field in struct
140    UnknownField {
141        /// The unknown field name
142        field: String,
143        /// List of valid field names
144        expected: Vec<&'static str>,
145        /// Suggested field name (if similar to an expected field)
146        suggestion: Option<&'static str>,
147    },
148    /// Missing required field
149    MissingField {
150        /// The name of the missing field
151        field: &'static str,
152        /// Span of the object start (opening brace)
153        object_start: Option<Span>,
154        /// Span of the object end (closing brace)
155        object_end: Option<Span>,
156    },
157    /// Invalid value for type
158    InvalidValue {
159        /// Description of why the value is invalid
160        message: String,
161    },
162    /// Reflection error from facet-reflect
163    Reflect(ReflectError),
164    /// Number out of range
165    NumberOutOfRange {
166        /// The numeric value that was out of range
167        value: String,
168        /// The target type that couldn't hold the value
169        target_type: &'static str,
170    },
171    /// Duplicate key in object
172    DuplicateKey {
173        /// The key that appeared more than once
174        key: String,
175    },
176    /// Invalid UTF-8 in string
177    InvalidUtf8,
178    /// Solver error (for flattened types)
179    Solver(String),
180    /// I/O error (for streaming deserialization)
181    Io(String),
182}
183
184impl Display for JsonErrorKind {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        match self {
187            JsonErrorKind::Scan(e) => write!(f, "{e:?}"),
188            JsonErrorKind::ScanWithContext {
189                error,
190                expected_type,
191            } => {
192                write!(f, "{error:?} (while parsing {expected_type})")
193            }
194            JsonErrorKind::UnexpectedToken { got, expected } => {
195                write!(f, "unexpected token: got {got}, expected {expected}")
196            }
197            JsonErrorKind::UnexpectedEof { expected } => {
198                write!(f, "unexpected end of input, expected {expected}")
199            }
200            JsonErrorKind::TypeMismatch { expected, got } => {
201                write!(f, "type mismatch: expected {expected}, got {got}")
202            }
203            JsonErrorKind::UnknownField {
204                field,
205                expected,
206                suggestion,
207            } => {
208                write!(f, "unknown field `{field}`, expected one of: {expected:?}")?;
209                if let Some(suggested) = suggestion {
210                    write!(f, " (did you mean `{suggested}`?)")?;
211                }
212                Ok(())
213            }
214            JsonErrorKind::MissingField { field, .. } => {
215                write!(f, "missing required field `{field}`")
216            }
217            JsonErrorKind::InvalidValue { message } => {
218                write!(f, "invalid value: {message}")
219            }
220            JsonErrorKind::Reflect(e) => write!(f, "reflection error: {e}"),
221            JsonErrorKind::NumberOutOfRange { value, target_type } => {
222                write!(f, "number `{value}` out of range for {target_type}")
223            }
224            JsonErrorKind::DuplicateKey { key } => {
225                write!(f, "duplicate key `{key}`")
226            }
227            JsonErrorKind::InvalidUtf8 => write!(f, "invalid UTF-8 sequence"),
228            JsonErrorKind::Solver(msg) => write!(f, "solver error: {msg}"),
229            JsonErrorKind::Io(msg) => write!(f, "I/O error: {msg}"),
230        }
231    }
232}
233
234impl JsonErrorKind {
235    /// Get an error code for this kind of error.
236    pub fn code(&self) -> &'static str {
237        match self {
238            JsonErrorKind::Scan(_) => "json::scan",
239            JsonErrorKind::ScanWithContext { .. } => "json::scan",
240            JsonErrorKind::UnexpectedToken { .. } => "json::unexpected_token",
241            JsonErrorKind::UnexpectedEof { .. } => "json::unexpected_eof",
242            JsonErrorKind::TypeMismatch { .. } => "json::type_mismatch",
243            JsonErrorKind::UnknownField { .. } => "json::unknown_field",
244            JsonErrorKind::MissingField { .. } => "json::missing_field",
245            JsonErrorKind::InvalidValue { .. } => "json::invalid_value",
246            JsonErrorKind::Reflect(_) => "json::reflect",
247            JsonErrorKind::NumberOutOfRange { .. } => "json::number_out_of_range",
248            JsonErrorKind::DuplicateKey { .. } => "json::duplicate_key",
249            JsonErrorKind::InvalidUtf8 => "json::invalid_utf8",
250            JsonErrorKind::Solver(_) => "json::solver",
251            JsonErrorKind::Io(_) => "json::io",
252        }
253    }
254
255    /// Get a label describing where/what the error points to.
256    pub fn label(&self) -> String {
257        match self {
258            JsonErrorKind::Scan(e) => match e {
259                ScanErrorKind::UnexpectedChar(c) => format!("unexpected '{c}'"),
260                ScanErrorKind::UnexpectedEof(ctx) => format!("unexpected end of input {ctx}"),
261                ScanErrorKind::InvalidUtf8 => "invalid UTF-8 here".into(),
262            },
263            JsonErrorKind::ScanWithContext {
264                error,
265                expected_type,
266            } => match error {
267                ScanErrorKind::UnexpectedChar(c) => {
268                    format!("unexpected '{c}', expected {expected_type}")
269                }
270                ScanErrorKind::UnexpectedEof(_) => {
271                    format!("unexpected end of input, expected {expected_type}")
272                }
273                ScanErrorKind::InvalidUtf8 => "invalid UTF-8 here".into(),
274            },
275            JsonErrorKind::UnexpectedToken { got, expected } => {
276                format!("expected {expected}, got '{got}'")
277            }
278            JsonErrorKind::UnexpectedEof { expected } => format!("expected {expected}"),
279            JsonErrorKind::TypeMismatch { expected, got } => {
280                format!("expected {expected}, got {got}")
281            }
282            JsonErrorKind::UnknownField {
283                field, suggestion, ..
284            } => {
285                if let Some(suggested) = suggestion {
286                    format!("unknown field '{field}' - did you mean '{suggested}'?")
287                } else {
288                    format!("unknown field '{field}'")
289                }
290            }
291            JsonErrorKind::MissingField { field, .. } => format!("missing field '{field}'"),
292            JsonErrorKind::InvalidValue { .. } => "invalid value".into(),
293            JsonErrorKind::Reflect(_) => "reflection error".into(),
294            JsonErrorKind::NumberOutOfRange { target_type, .. } => {
295                format!("out of range for {target_type}")
296            }
297            JsonErrorKind::DuplicateKey { key } => format!("duplicate key '{key}'"),
298            JsonErrorKind::InvalidUtf8 => "invalid UTF-8".into(),
299            JsonErrorKind::Solver(_) => "solver error".into(),
300            JsonErrorKind::Io(_) => "I/O error".into(),
301        }
302    }
303}
304
305impl From<AdapterError> for JsonError {
306    fn from(err: AdapterError) -> Self {
307        let kind = match err.kind {
308            AdapterErrorKind::Scan(scan_err) => JsonErrorKind::Scan(scan_err),
309            AdapterErrorKind::NeedMore => JsonErrorKind::UnexpectedEof {
310                expected: "more data",
311            },
312        };
313        JsonError {
314            kind,
315            span: Some(err.span),
316            source_code: None,
317        }
318    }
319}
320
321impl From<ReflectError> for JsonError {
322    fn from(err: ReflectError) -> Self {
323        JsonError {
324            kind: JsonErrorKind::Reflect(err),
325            span: None,
326            source_code: None,
327        }
328    }
329}
330
331/// Result type for JSON deserialization
332#[allow(dead_code)]
333pub type Result<T> = core::result::Result<T, JsonError>;