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