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::{Cooked, Outcome, Span};
12
13pub struct DeserError<'input, 'shape, C = Cooked> {
15 pub input: alloc::borrow::Cow<'input, [u8]>,
17
18 pub span: Span<C>,
20
21 pub kind: DeserErrorKind<'shape>,
23
24 pub source_id: &'static str,
26}
27
28impl<'input, 'shape, C> DeserError<'input, 'shape, C> {
29 pub fn into_owned(self) -> DeserError<'static, 'shape, C> {
31 DeserError {
32 input: self.input.into_owned().into(),
33 span: self.span,
34 kind: self.kind,
35 source_id: self.source_id,
36 }
37 }
38
39 pub fn with_span<D>(self, span: Span<D>) -> DeserError<'input, 'shape, D> {
41 DeserError {
42 input: self.input,
43 span,
44 kind: self.kind,
45 source_id: self.source_id,
46 }
47 }
48}
49
50#[derive(Debug, PartialEq, Clone)]
52pub enum DeserErrorKind<'shape> {
53 UnexpectedByte {
55 got: u8,
57 wanted: &'static str,
59 },
60
61 UnexpectedChar {
63 got: char,
65 wanted: &'static str,
67 },
68
69 UnexpectedOutcome {
71 got: Outcome<'static>,
73 wanted: &'static str,
75 },
76
77 UnexpectedEof {
79 wanted: &'static str,
81 },
82
83 MissingValue {
85 expected: &'static str,
87 field: String,
89 },
90
91 MissingField(&'static str),
93
94 NumberOutOfRange(f64),
96
97 StringAsNumber(String),
99
100 UnknownField {
102 field_name: String,
104
105 shape: &'shape Shape<'shape>,
107 },
108
109 InvalidUtf8(String),
111
112 ReflectError(ReflectError<'shape>),
114
115 Unimplemented(&'static str),
117
118 UnsupportedType {
120 got: &'shape Shape<'shape>,
122
123 wanted: &'static str,
125 },
126
127 NoSuchVariant {
129 name: String,
131
132 enum_shape: &'shape Shape<'shape>,
134 },
135
136 VariantError(VariantError),
138
139 ArrayOverflow {
141 shape: &'shape Shape<'shape>,
143
144 max_len: usize,
146 },
147
148 NumericConversion {
150 from: &'static str,
152
153 to: &'static str,
155 },
156}
157
158impl<'input, 'shape, C> DeserError<'input, 'shape, C> {
159 pub fn new<I>(
161 kind: DeserErrorKind<'shape>,
162 input: &'input I,
163 span: Span<C>,
164 source_id: &'static str,
165 ) -> Self
166 where
167 I: ?Sized + 'input + InputDebug,
168 {
169 Self {
170 input: input.as_cow(),
171 span,
172 kind,
173 source_id,
174 }
175 }
176
177 pub(crate) fn new_reflect<I>(
179 e: ReflectError<'shape>,
180 input: &'input I,
181 span: Span<C>,
182 source_id: &'static str,
183 ) -> Self
184 where
185 I: ?Sized + 'input + InputDebug,
186 {
187 DeserError::new(DeserErrorKind::ReflectError(e), input, span, source_id)
188 }
189
190 pub fn with_source_id(mut self, source_id: &'static str) -> Self {
192 self.source_id = source_id;
193 self
194 }
195
196 pub fn message(&self) -> DeserErrorMessage<'_, '_, C> {
198 DeserErrorMessage(self)
199 }
200}
201
202pub struct DeserErrorMessage<'input, 'shape, C = Cooked>(&'input DeserError<'input, 'shape, C>);
204
205impl core::fmt::Display for DeserErrorMessage<'_, '_> {
206 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
207 match &self.0.kind {
208 DeserErrorKind::UnexpectedByte { got, wanted } => write!(
209 f,
210 "Unexpected byte: got 0x{:02X}, wanted {}",
211 got.red(),
212 wanted.yellow()
213 ),
214 DeserErrorKind::UnexpectedChar { got, wanted } => write!(
215 f,
216 "Unexpected character: got '{}', wanted {}",
217 got.red(),
218 wanted.yellow()
219 ),
220 DeserErrorKind::UnexpectedOutcome { got, wanted } => {
221 write!(f, "Unexpected {}, wanted {}", got.red(), wanted.yellow())
222 }
223 DeserErrorKind::UnexpectedEof { wanted } => {
224 write!(f, "Unexpected end of file: wanted {}", wanted.red())
225 }
226 DeserErrorKind::MissingValue { expected, field } => {
227 write!(f, "Missing {} for {}", expected.red(), field.yellow())
228 }
229 DeserErrorKind::MissingField(fld) => write!(f, "Missing required field: {}", fld.red()),
230 DeserErrorKind::NumberOutOfRange(n) => {
231 write!(f, "Number out of range: {}", n.red())
232 }
233 DeserErrorKind::StringAsNumber(s) => {
234 write!(f, "Expected a string but got number: {}", s.red())
235 }
236 DeserErrorKind::UnknownField { field_name, shape } => {
237 write!(
238 f,
239 "Unknown field: {} for shape {}",
240 field_name.red(),
241 shape.yellow()
242 )
243 }
244 DeserErrorKind::InvalidUtf8(e) => write!(f, "Invalid UTF-8 encoding: {}", e.red()),
245 DeserErrorKind::ReflectError(e) => write!(f, "{e}"),
246 DeserErrorKind::Unimplemented(s) => {
247 write!(f, "Feature not yet implemented: {}", s.yellow())
248 }
249 DeserErrorKind::UnsupportedType { got, wanted } => {
250 write!(
251 f,
252 "Unsupported type: got {}, wanted {}",
253 got.red(),
254 wanted.green()
255 )
256 }
257 DeserErrorKind::NoSuchVariant { name, enum_shape } => {
258 if let Type::User(UserType::Enum(ed)) = enum_shape.ty {
259 write!(
260 f,
261 "Enum variant not found: {} in enum {}. Available variants: [",
262 name.red(),
263 enum_shape.yellow()
264 )?;
265
266 let mut first = true;
267 for variant in ed.variants.iter() {
268 if !first {
269 write!(f, ", ")?;
270 }
271 write!(f, "{}", variant.name.green())?;
272 first = false;
273 }
274
275 write!(f, "]")?;
276 Ok(())
277 } else {
278 write!(
279 f,
280 "Enum variant not found: {} in non-enum type {}",
281 name.red(),
282 enum_shape.yellow()
283 )?;
284 Ok(())
285 }
286 }
287 DeserErrorKind::VariantError(e) => {
288 write!(f, "Variant error: {e}")
289 }
290 DeserErrorKind::ArrayOverflow { shape, max_len } => {
291 write!(
292 f,
293 "Too many elements for array {}: maximum {} elements allowed",
294 shape.blue(),
295 max_len.yellow()
296 )
297 }
298 DeserErrorKind::NumericConversion { from, to } => {
299 write!(
300 f,
301 "Cannot convert {} to {}: value out of range or precision loss",
302 from.red(),
303 to.green()
304 )
305 }
306 }
307 }
308}
309
310#[cfg(not(feature = "rich-diagnostics"))]
311impl core::fmt::Display for DeserError<'_, '_> {
312 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
313 write!(f, "{} at byte {}", self.message(), self.span.start(),)
314 }
315}
316
317#[cfg(feature = "rich-diagnostics")]
318impl core::fmt::Display for DeserError<'_, '_> {
319 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
320 let Ok(orig_input_str) = core::str::from_utf8(&self.input[..]) else {
322 return write!(f, "(JSON input was invalid UTF-8)");
323 };
324
325 let source_id = self.source_id;
326 let mut span_start = self.span.start();
327 let mut span_end = self.span.end();
328 use alloc::borrow::Cow;
329 let mut input_str: Cow<'_, str> = Cow::Borrowed(orig_input_str);
330
331 let mut did_truncate = false;
348
349 {
350 let bytes = self.input.as_ref();
352 let line_start = bytes[..span_start]
353 .iter()
354 .rposition(|&b| b == b'\n')
355 .map(|pos| pos + 1)
356 .unwrap_or(0);
357 let line_end = bytes[span_start..]
358 .iter()
359 .position(|&b| b == b'\n')
360 .map(|pos| span_start + pos)
361 .unwrap_or(bytes.len());
362
363 if span_end <= line_end {
365 let before_chars = span_start - line_start;
367 let after_chars = line_end.saturating_sub(span_end);
368
369 if before_chars > 180 || after_chars > 180 {
371 let trim_left = if before_chars > 180 {
372 before_chars - 80
373 } else {
374 0
375 };
376 let trim_right = if after_chars > 180 {
377 after_chars - 80
378 } else {
379 0
380 };
381
382 let new_start = line_start + trim_left;
383 let new_end = line_end - trim_right;
384
385 let truncated = &orig_input_str[new_start..new_end];
386
387 let left_ellipsis = if trim_left > 0 { "…" } else { "" };
388 let right_ellipsis = if trim_right > 0 { "…" } else { "" };
389
390 let mut buf = String::with_capacity(
391 left_ellipsis.len() + truncated.len() + right_ellipsis.len(),
392 );
393 buf.push_str(left_ellipsis);
394 buf.push_str(truncated);
395 buf.push_str(right_ellipsis);
396
397 span_start = span_start - new_start + left_ellipsis.len();
399 span_end = span_end - new_start + left_ellipsis.len();
400
401 input_str = Cow::Owned(buf);
402
403 did_truncate = true; }
406 }
407 }
409
410 if did_truncate {
411 writeln!(
412 f,
413 "{}",
414 "WARNING: Input was truncated for display. Byte indexes in the error below do not match original input.".yellow().bold()
415 )?;
416 }
417
418 let mut report = Report::build(ReportKind::Error, (source_id, span_start..span_end))
419 .with_config(Config::new().with_index_type(IndexType::Byte));
420
421 let label = Label::new((source_id, span_start..span_end))
422 .with_message(self.message())
423 .with_color(Color::Red);
424
425 report = report.with_label(label);
426
427 let source = Source::from(input_str);
428
429 struct FmtWriter<'a, 'b: 'a> {
430 f: &'a mut core::fmt::Formatter<'b>,
431 error: Option<core::fmt::Error>,
432 }
433
434 impl core::fmt::Write for FmtWriter<'_, '_> {
435 fn write_str(&mut self, s: &str) -> core::fmt::Result {
436 if self.error.is_some() {
437 return Err(core::fmt::Error);
439 }
440 if let Err(e) = self.f.write_str(s) {
441 self.error = Some(e);
442 Err(core::fmt::Error)
443 } else {
444 Ok(())
445 }
446 }
447 }
448
449 struct IoWriter<'a, 'b: 'a> {
450 inner: FmtWriter<'a, 'b>,
451 }
452
453 impl std::io::Write for IoWriter<'_, '_> {
454 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
455 match core::str::from_utf8(buf) {
456 Ok(s) => match core::fmt::Write::write_str(&mut self.inner, s) {
457 Ok(()) => Ok(buf.len()),
458 Err(_) => Err(std::io::ErrorKind::Other.into()),
459 },
460 Err(_) => Err(std::io::ErrorKind::InvalidData.into()),
461 }
462 }
463 fn flush(&mut self) -> std::io::Result<()> {
464 Ok(())
465 }
466 }
467
468 let cache = (source_id, &source);
469
470 let fmt_writer = FmtWriter { f, error: None };
471 let mut io_writer = IoWriter { inner: fmt_writer };
472
473 if report.finish().write(cache, &mut io_writer).is_err() {
474 return write!(f, "Error formatting with ariadne");
475 }
476
477 if io_writer.inner.error.is_some() {
479 return write!(f, "Error writing ariadne output to fmt::Formatter");
480 }
481
482 Ok(())
483 }
484}
485
486impl core::fmt::Debug for DeserError<'_, '_> {
487 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
488 core::fmt::Display::fmt(self, f)
489 }
490}
491
492impl core::error::Error for DeserError<'_, '_> {}