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