1#[cfg(feature = "rich-diagnostics")]
2use ariadne::{Color, Config, IndexType, Label, Report, ReportKind, Source};
3
4use alloc::string::String;
5
6use facet_core::{Shape, Type, UserType};
7use facet_reflect::{ReflectError, VariantError};
8use owo_colors::OwoColorize;
9
10use crate::debug::InputDebug;
11use crate::{Outcome, Span};
12
13#[derive(Debug)]
15pub struct DeserError<'input> {
16 pub input: alloc::borrow::Cow<'input, [u8]>,
18
19 pub span: Span,
21
22 pub kind: DeserErrorKind,
24
25 pub source_id: &'static str,
27}
28
29impl DeserError<'_> {
30 pub fn into_owned(self) -> DeserError<'static> {
32 DeserError {
33 input: self.input.into_owned().into(),
34 span: self.span,
35 kind: self.kind,
36 source_id: self.source_id,
37 }
38 }
39
40 pub fn with_span(mut self, span: Span) -> Self {
42 self.span = span;
43 self
44 }
45}
46
47#[derive(Debug, PartialEq, Clone)]
49pub enum DeserErrorKind {
50 UnexpectedByte {
52 got: u8,
54 wanted: &'static str,
56 },
57 UnexpectedChar {
59 got: char,
61 wanted: &'static str,
63 },
64 UnexpectedOutcome {
66 got: Outcome<'static>,
68 wanted: &'static str,
70 },
71 UnexpectedEof {
73 wanted: &'static str,
75 },
76 MissingValue {
78 expected: &'static str,
80 field: String,
82 },
83 MissingField(&'static str),
85 NumberOutOfRange(f64),
87 StringAsNumber(String),
89 UnknownField {
91 field_name: String,
93 shape: &'static Shape,
95 },
96 InvalidUtf8(String),
98 ReflectError(ReflectError),
100 Unimplemented(&'static str),
102 UnsupportedType {
104 got: &'static Shape,
106
107 wanted: &'static str,
109 },
110 NoSuchVariant {
112 name: String,
114
115 enum_shape: &'static Shape,
117 },
118 VariantError(VariantError),
120}
121
122impl<'input> DeserError<'input> {
123 pub fn new<I>(
125 kind: DeserErrorKind,
126 input: &'input I,
127 span: Span,
128 source_id: &'static str,
129 ) -> Self
130 where
131 I: ?Sized + 'input + InputDebug,
132 {
133 Self {
134 input: input.as_cow(),
135 span,
136 kind,
137 source_id,
138 }
139 }
140
141 pub(crate) fn new_reflect<I>(
150 e: ReflectError,
151 input: &'input I,
152 span: Span,
153 source_id: &'static str,
154 ) -> Self
155 where
156 I: ?Sized + 'input + InputDebug,
157 {
158 DeserError::new(DeserErrorKind::ReflectError(e), input, span, source_id)
159 }
160
161 pub fn with_source_id(mut self, source_id: &'static str) -> Self {
181 self.source_id = source_id;
182 self
183 }
184
185 pub fn message(&self) -> DeserErrorMessage<'_> {
187 DeserErrorMessage(self)
188 }
189}
190
191pub struct DeserErrorMessage<'a>(&'a DeserError<'a>);
193
194impl core::fmt::Display for DeserErrorMessage<'_> {
195 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
196 match &self.0.kind {
197 DeserErrorKind::UnexpectedByte { got, wanted } => write!(
198 f,
199 "Unexpected byte: got 0x{:02X}, wanted {}",
200 got.red(),
201 wanted.yellow()
202 ),
203 DeserErrorKind::UnexpectedChar { got, wanted } => write!(
204 f,
205 "Unexpected character: got '{}', wanted {}",
206 got.red(),
207 wanted.yellow()
208 ),
209 DeserErrorKind::UnexpectedOutcome { got, wanted } => {
210 write!(f, "Unexpected {}, wanted {}", got.red(), wanted.yellow())
211 }
212 DeserErrorKind::UnexpectedEof { wanted } => {
213 write!(f, "Unexpected end of file: wanted {}", wanted.red())
214 }
215 DeserErrorKind::MissingValue { expected, field } => {
216 write!(f, "Missing {} for {}", expected.red(), field.yellow())
217 }
218 DeserErrorKind::MissingField(fld) => write!(f, "Missing required field: {}", fld.red()),
219 DeserErrorKind::NumberOutOfRange(n) => {
220 write!(f, "Number out of range: {}", n.red())
221 }
222 DeserErrorKind::StringAsNumber(s) => {
223 write!(f, "Expected a string but got number: {}", s.red())
224 }
225 DeserErrorKind::UnknownField { field_name, shape } => {
226 write!(
227 f,
228 "Unknown field: {} for shape {}",
229 field_name.red(),
230 shape.yellow()
231 )
232 }
233 DeserErrorKind::InvalidUtf8(e) => write!(f, "Invalid UTF-8 encoding: {}", e.red()),
234 DeserErrorKind::ReflectError(e) => write!(f, "{e}"),
235 DeserErrorKind::Unimplemented(s) => {
236 write!(f, "Feature not yet implemented: {}", s.yellow())
237 }
238 DeserErrorKind::UnsupportedType { got, wanted } => {
239 write!(
240 f,
241 "Unsupported type: got {}, wanted {}",
242 got.red(),
243 wanted.green()
244 )
245 }
246 DeserErrorKind::NoSuchVariant { name, enum_shape } => {
247 if let Type::User(UserType::Enum(ed)) = enum_shape.ty {
248 write!(
249 f,
250 "Enum variant not found: {} in enum {}. Available variants: [",
251 name.red(),
252 enum_shape.yellow()
253 )?;
254
255 let mut first = true;
256 for variant in ed.variants.iter() {
257 if !first {
258 write!(f, ", ")?;
259 }
260 write!(f, "{}", variant.name.green())?;
261 first = false;
262 }
263
264 write!(f, "]")?;
265 Ok(())
266 } else {
267 write!(
268 f,
269 "Enum variant not found: {} in non-enum type {}",
270 name.red(),
271 enum_shape.yellow()
272 )?;
273 Ok(())
274 }
275 }
276 DeserErrorKind::VariantError(e) => {
277 write!(f, "Variant error: {e}")
278 }
279 }
280 }
281}
282
283#[cfg(not(feature = "rich-diagnostics"))]
284impl core::fmt::Display for DeserError<'_> {
285 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
286 write!(f, "{} at byte {}", self.message(), self.span.start(),)
287 }
288}
289
290#[cfg(feature = "rich-diagnostics")]
291impl core::fmt::Display for DeserError<'_> {
292 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
293 let Ok(input_str) = core::str::from_utf8(&self.input[..]) else {
294 return write!(f, "(JSON input was invalid UTF-8)");
295 };
296
297 let source_id = self.source_id;
298 let span_start = self.span.start();
299 let span_end = self.span.end();
300
301 let mut report = Report::build(ReportKind::Error, (source_id, span_start..span_end))
302 .with_config(Config::new().with_index_type(IndexType::Byte));
303
304 let label = Label::new((source_id, span_start..span_end))
305 .with_message(self.message())
306 .with_color(Color::Red);
307
308 report = report.with_label(label);
309
310 let source = Source::from(input_str);
311
312 struct FmtWriter<'a, 'b: 'a> {
313 f: &'a mut core::fmt::Formatter<'b>,
314 error: Option<core::fmt::Error>,
315 }
316
317 impl core::fmt::Write for FmtWriter<'_, '_> {
318 fn write_str(&mut self, s: &str) -> core::fmt::Result {
319 if self.error.is_some() {
320 return Err(core::fmt::Error);
322 }
323 if let Err(e) = self.f.write_str(s) {
324 self.error = Some(e);
325 Err(core::fmt::Error)
326 } else {
327 Ok(())
328 }
329 }
330 }
331
332 struct IoWriter<'a, 'b: 'a> {
333 inner: FmtWriter<'a, 'b>,
334 }
335
336 impl std::io::Write for IoWriter<'_, '_> {
337 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
338 match core::str::from_utf8(buf) {
339 Ok(s) => match core::fmt::Write::write_str(&mut self.inner, s) {
340 Ok(()) => Ok(buf.len()),
341 Err(_) => Err(std::io::ErrorKind::Other.into()),
342 },
343 Err(_) => Err(std::io::ErrorKind::InvalidData.into()),
344 }
345 }
346 fn flush(&mut self) -> std::io::Result<()> {
347 Ok(())
348 }
349 }
350
351 let cache = (source_id, &source);
352
353 let fmt_writer = FmtWriter { f, error: None };
354 let mut io_writer = IoWriter { inner: fmt_writer };
355
356 if report.finish().write(cache, &mut io_writer).is_err() {
357 return write!(f, "Error formatting with ariadne");
358 }
359
360 if io_writer.inner.error.is_some() {
362 return write!(f, "Error writing ariadne output to fmt::Formatter");
363 }
364
365 Ok(())
366 }
367}
368
369impl core::error::Error for DeserError<'_> {}