1#[cfg(feature = "rich-diagnostics")]
2use ariadne::{Color, Config, IndexType, Label, Report, ReportKind, Source};
3
4use alloc::{borrow::Cow, string::String};
5
6use facet_core::{Shape, Type, UserType};
7use facet_reflect::{ReflectError, VariantError};
8use owo_colors::OwoColorize;
9
10use crate::{Outcome, Span};
11
12pub struct DeserError<'input> {
14 pub input: Cow<'input, [u8]>,
16
17 pub span: Span,
19
20 pub kind: DeserErrorKind,
22}
23
24impl<'input> DeserError<'input> {
25 pub fn into_owned(self) -> DeserError<'static> {
27 DeserError {
28 input: self.input.into_owned().into(),
29 span: self.span,
30 kind: self.kind,
31 }
32 }
33
34 pub fn with_span(self, span: Span) -> DeserError<'input> {
36 DeserError {
37 input: self.input,
38 span,
39 kind: self.kind,
40 }
41 }
42}
43
44#[derive(Debug, Clone)]
46pub enum DeserErrorKind {
47 UnexpectedByte {
49 got: u8,
51 wanted: &'static str,
53 },
54
55 UnexpectedChar {
57 got: char,
59 wanted: &'static str,
61 },
62
63 UnexpectedOutcome {
65 got: Outcome<'static>,
67 wanted: &'static str,
69 },
70
71 UnexpectedEof {
73 wanted: &'static str,
75 },
76
77 MissingValue {
79 expected: &'static str,
81 field: String,
83 },
84
85 MissingField(&'static str),
87
88 NumberOutOfRange(f64),
90
91 StringAsNumber(String),
93
94 UnknownField {
96 field_name: String,
98
99 shape: &'static Shape,
101 },
102
103 InvalidUtf8(String),
105
106 ReflectError(ReflectError),
108
109 Unimplemented(&'static str),
111
112 UnsupportedType {
114 got: &'static Shape,
116
117 wanted: &'static str,
119 },
120
121 NoSuchVariant {
123 name: String,
125
126 enum_shape: &'static Shape,
128 },
129
130 VariantError(VariantError),
132
133 ArrayOverflow {
135 shape: &'static Shape,
137
138 max_len: usize,
140 },
141
142 NumericConversion {
144 from: &'static str,
146
147 to: &'static str,
149 },
150}
151
152impl<'input> DeserError<'input> {
153 pub fn new(kind: DeserErrorKind, input: &'input [u8], span: Span) -> Self {
155 Self {
156 input: input.into(),
157 span,
158 kind,
159 }
160 }
161
162 pub(crate) fn new_reflect(e: ReflectError, input: &'input [u8], span: Span) -> Self {
164 DeserError::new(DeserErrorKind::ReflectError(e), input, span)
165 }
166
167 pub fn message(&self) -> DeserErrorMessage<'_> {
169 DeserErrorMessage(self)
170 }
171}
172
173pub struct DeserErrorMessage<'input>(&'input DeserError<'input>);
175
176impl core::fmt::Display for DeserErrorMessage<'_> {
177 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
178 match &self.0.kind {
179 DeserErrorKind::UnexpectedByte { got, wanted } => write!(
180 f,
181 "Unexpected byte: got 0x{:02X}, wanted {}",
182 got.red(),
183 wanted.yellow()
184 ),
185 DeserErrorKind::UnexpectedChar { got, wanted } => write!(
186 f,
187 "Unexpected character: got '{}', wanted {}",
188 got.red(),
189 wanted.yellow()
190 ),
191 DeserErrorKind::UnexpectedOutcome { got, wanted } => {
192 write!(f, "Unexpected {}, wanted {}", got.red(), wanted.yellow())
193 }
194 DeserErrorKind::UnexpectedEof { wanted } => {
195 write!(f, "Unexpected end of file: wanted {}", wanted.red())
196 }
197 DeserErrorKind::MissingValue { expected, field } => {
198 write!(f, "Missing {} for {}", expected.red(), field.yellow())
199 }
200 DeserErrorKind::MissingField(fld) => write!(f, "Missing required field: {}", fld.red()),
201 DeserErrorKind::NumberOutOfRange(n) => {
202 write!(f, "Number out of range: {}", n.red())
203 }
204 DeserErrorKind::StringAsNumber(s) => {
205 write!(f, "Expected a string but got number: {}", s.red())
206 }
207 DeserErrorKind::UnknownField { field_name, shape } => {
208 write!(
209 f,
210 "Unknown field: {} for shape {}",
211 field_name.red(),
212 shape.yellow()
213 )
214 }
215 DeserErrorKind::InvalidUtf8(e) => write!(f, "Invalid UTF-8 encoding: {}", e.red()),
216 DeserErrorKind::ReflectError(e) => write!(f, "{e}"),
217 DeserErrorKind::Unimplemented(s) => {
218 write!(f, "Feature not yet implemented: {}", s.yellow())
219 }
220 DeserErrorKind::UnsupportedType { got, wanted } => {
221 write!(
222 f,
223 "Unsupported type: got {}, wanted {}",
224 got.red(),
225 wanted.green()
226 )
227 }
228 DeserErrorKind::NoSuchVariant { name, enum_shape } => {
229 if let Type::User(UserType::Enum(ed)) = enum_shape.ty {
230 write!(
231 f,
232 "Enum variant not found: {} in enum {}. Available variants: [",
233 name.red(),
234 enum_shape.yellow()
235 )?;
236
237 let mut first = true;
238 for variant in ed.variants.iter() {
239 if !first {
240 write!(f, ", ")?;
241 }
242 write!(f, "{}", variant.name.green())?;
243 first = false;
244 }
245
246 write!(f, "]")?;
247 Ok(())
248 } else {
249 write!(
250 f,
251 "Enum variant not found: {} in non-enum type {}",
252 name.red(),
253 enum_shape.yellow()
254 )?;
255 Ok(())
256 }
257 }
258 DeserErrorKind::VariantError(e) => {
259 write!(f, "Variant error: {e}")
260 }
261 DeserErrorKind::ArrayOverflow { shape, max_len } => {
262 write!(
263 f,
264 "Too many elements for array {}: maximum {} elements allowed",
265 shape.blue(),
266 max_len.yellow()
267 )
268 }
269 DeserErrorKind::NumericConversion { from, to } => {
270 write!(
271 f,
272 "Cannot convert {} to {}: value out of range or precision loss",
273 from.red(),
274 to.green()
275 )
276 }
277 }
278 }
279}
280
281#[cfg(not(feature = "rich-diagnostics"))]
282impl core::fmt::Display for DeserError<'_> {
283 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
284 write!(f, "{} at byte {}", self.message(), self.span.start(),)
285 }
286}
287
288#[cfg(feature = "rich-diagnostics")]
289impl core::fmt::Display for DeserError<'_> {
290 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
291 let Ok(orig_input_str) = core::str::from_utf8(&self.input[..]) else {
293 return write!(f, "(JSON input was invalid UTF-8)");
294 };
295
296 let mut span_start = self.span.start();
297 let mut span_end = self.span.end();
298 use alloc::borrow::Cow;
299 let mut input_str: Cow<'_, str> = Cow::Borrowed(orig_input_str);
300
301 let mut did_truncate = false;
318
319 {
320 let bytes = self.input.as_ref();
322 let line_start = bytes[..span_start]
323 .iter()
324 .rposition(|&b| b == b'\n')
325 .map(|pos| pos + 1)
326 .unwrap_or(0);
327 let line_end = bytes[span_start..]
328 .iter()
329 .position(|&b| b == b'\n')
330 .map(|pos| span_start + pos)
331 .unwrap_or(bytes.len());
332
333 if span_end <= line_end {
335 let before_chars = span_start - line_start;
337 let after_chars = line_end.saturating_sub(span_end);
338
339 if before_chars > 180 || after_chars > 180 {
341 let trim_left = if before_chars > 180 {
342 before_chars - 80
343 } else {
344 0
345 };
346 let trim_right = if after_chars > 180 {
347 after_chars - 80
348 } else {
349 0
350 };
351
352 let new_start = line_start + trim_left;
353 let new_end = line_end - trim_right;
354
355 let truncated = &orig_input_str[new_start..new_end];
356
357 let left_ellipsis = if trim_left > 0 { "…" } else { "" };
358 let right_ellipsis = if trim_right > 0 { "…" } else { "" };
359
360 let mut buf = String::with_capacity(
361 left_ellipsis.len() + truncated.len() + right_ellipsis.len(),
362 );
363 buf.push_str(left_ellipsis);
364 buf.push_str(truncated);
365 buf.push_str(right_ellipsis);
366
367 span_start = span_start - new_start + left_ellipsis.len();
369 span_end = span_end - new_start + left_ellipsis.len();
370
371 input_str = Cow::Owned(buf);
372
373 did_truncate = true; }
376 }
377 }
379
380 if did_truncate {
381 writeln!(
382 f,
383 "{}",
384 "WARNING: Input was truncated for display. Byte indexes in the error below do not match original input.".yellow().bold()
385 )?;
386 }
387
388 let mut report = Report::build(ReportKind::Error, span_start..span_end)
389 .with_config(Config::new().with_index_type(IndexType::Byte));
390
391 let label = Label::new(span_start..span_end)
392 .with_message(self.message())
393 .with_color(Color::Red);
394
395 report = report.with_label(label);
396
397 let source = Source::from(input_str);
398
399 struct FmtWriter<'a, 'b: 'a> {
400 f: &'a mut core::fmt::Formatter<'b>,
401 error: Option<core::fmt::Error>,
402 }
403
404 impl core::fmt::Write for FmtWriter<'_, '_> {
405 fn write_str(&mut self, s: &str) -> core::fmt::Result {
406 if self.error.is_some() {
407 return Err(core::fmt::Error);
409 }
410 if let Err(e) = self.f.write_str(s) {
411 self.error = Some(e);
412 Err(core::fmt::Error)
413 } else {
414 Ok(())
415 }
416 }
417 }
418
419 struct IoWriter<'a, 'b: 'a> {
420 inner: FmtWriter<'a, 'b>,
421 }
422
423 impl std::io::Write for IoWriter<'_, '_> {
424 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
425 match core::str::from_utf8(buf) {
426 Ok(s) => match core::fmt::Write::write_str(&mut self.inner, s) {
427 Ok(()) => Ok(buf.len()),
428 Err(_) => Err(std::io::ErrorKind::Other.into()),
429 },
430 Err(_) => Err(std::io::ErrorKind::InvalidData.into()),
431 }
432 }
433 fn flush(&mut self) -> std::io::Result<()> {
434 Ok(())
435 }
436 }
437
438 let cache = &source;
439
440 let fmt_writer = FmtWriter { f, error: None };
441 let mut io_writer = IoWriter { inner: fmt_writer };
442
443 if report.finish().write(cache, &mut io_writer).is_err() {
444 return write!(f, "Error formatting with ariadne");
445 }
446
447 if io_writer.inner.error.is_some() {
449 return write!(f, "Error writing ariadne output to fmt::Formatter");
450 }
451
452 Ok(())
453 }
454}
455
456impl core::fmt::Debug for DeserError<'_> {
457 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
458 core::fmt::Display::fmt(self, f)
459 }
460}
461
462impl core::error::Error for DeserError<'_> {}