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