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
13#[derive(Debug)]
15pub struct DeserError<'input, 'shape, C = Cooked> {
16 pub input: alloc::borrow::Cow<'input, [u8]>,
18
19 pub span: Span<C>,
21
22 pub kind: DeserErrorKind<'shape>,
24
25 pub source_id: &'static str,
27}
28
29impl<'input, 'shape, C> DeserError<'input, 'shape, C> {
30 pub fn into_owned(self) -> DeserError<'static, 'shape, C> {
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<D>(self, span: Span<D>) -> DeserError<'input, 'shape, D> {
42 DeserError {
43 input: self.input,
44 span,
45 kind: self.kind,
46 source_id: self.source_id,
47 }
48 }
49}
50
51#[derive(Debug, PartialEq, Clone)]
53pub enum DeserErrorKind<'shape> {
54 UnexpectedByte {
56 got: u8,
58 wanted: &'static str,
60 },
61
62 UnexpectedChar {
64 got: char,
66 wanted: &'static str,
68 },
69
70 UnexpectedOutcome {
72 got: Outcome<'static>,
74 wanted: &'static str,
76 },
77
78 UnexpectedEof {
80 wanted: &'static str,
82 },
83
84 MissingValue {
86 expected: &'static str,
88 field: String,
90 },
91
92 MissingField(&'static str),
94
95 NumberOutOfRange(f64),
97
98 StringAsNumber(String),
100
101 UnknownField {
103 field_name: String,
105
106 shape: &'shape Shape<'shape>,
108 },
109
110 InvalidUtf8(String),
112
113 ReflectError(ReflectError<'shape>),
115
116 Unimplemented(&'static str),
118
119 UnsupportedType {
121 got: &'shape Shape<'shape>,
123
124 wanted: &'static str,
126 },
127
128 NoSuchVariant {
130 name: String,
132
133 enum_shape: &'shape Shape<'shape>,
135 },
136
137 VariantError(VariantError),
139}
140
141impl<'input, 'shape, C> DeserError<'input, 'shape, C> {
142 pub fn new<I>(
144 kind: DeserErrorKind<'shape>,
145 input: &'input I,
146 span: Span<C>,
147 source_id: &'static str,
148 ) -> Self
149 where
150 I: ?Sized + 'input + InputDebug,
151 {
152 Self {
153 input: input.as_cow(),
154 span,
155 kind,
156 source_id,
157 }
158 }
159
160 pub(crate) fn new_reflect<I>(
162 e: ReflectError<'shape>,
163 input: &'input I,
164 span: Span<C>,
165 source_id: &'static str,
166 ) -> Self
167 where
168 I: ?Sized + 'input + InputDebug,
169 {
170 DeserError::new(DeserErrorKind::ReflectError(e), input, span, source_id)
171 }
172
173 pub fn with_source_id(mut self, source_id: &'static str) -> Self {
175 self.source_id = source_id;
176 self
177 }
178
179 pub fn message(&self) -> DeserErrorMessage<'_, '_, C> {
181 DeserErrorMessage(self)
182 }
183}
184
185pub struct DeserErrorMessage<'input, 'shape, C = Cooked>(&'input DeserError<'input, 'shape, C>);
187
188impl core::fmt::Display for DeserErrorMessage<'_, '_> {
189 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
190 match &self.0.kind {
191 DeserErrorKind::UnexpectedByte { got, wanted } => write!(
192 f,
193 "Unexpected byte: got 0x{:02X}, wanted {}",
194 got.red(),
195 wanted.yellow()
196 ),
197 DeserErrorKind::UnexpectedChar { got, wanted } => write!(
198 f,
199 "Unexpected character: got '{}', wanted {}",
200 got.red(),
201 wanted.yellow()
202 ),
203 DeserErrorKind::UnexpectedOutcome { got, wanted } => {
204 write!(f, "Unexpected {}, wanted {}", got.red(), wanted.yellow())
205 }
206 DeserErrorKind::UnexpectedEof { wanted } => {
207 write!(f, "Unexpected end of file: wanted {}", wanted.red())
208 }
209 DeserErrorKind::MissingValue { expected, field } => {
210 write!(f, "Missing {} for {}", expected.red(), field.yellow())
211 }
212 DeserErrorKind::MissingField(fld) => write!(f, "Missing required field: {}", fld.red()),
213 DeserErrorKind::NumberOutOfRange(n) => {
214 write!(f, "Number out of range: {}", n.red())
215 }
216 DeserErrorKind::StringAsNumber(s) => {
217 write!(f, "Expected a string but got number: {}", s.red())
218 }
219 DeserErrorKind::UnknownField { field_name, shape } => {
220 write!(
221 f,
222 "Unknown field: {} for shape {}",
223 field_name.red(),
224 shape.yellow()
225 )
226 }
227 DeserErrorKind::InvalidUtf8(e) => write!(f, "Invalid UTF-8 encoding: {}", e.red()),
228 DeserErrorKind::ReflectError(e) => write!(f, "{e}"),
229 DeserErrorKind::Unimplemented(s) => {
230 write!(f, "Feature not yet implemented: {}", s.yellow())
231 }
232 DeserErrorKind::UnsupportedType { got, wanted } => {
233 write!(
234 f,
235 "Unsupported type: got {}, wanted {}",
236 got.red(),
237 wanted.green()
238 )
239 }
240 DeserErrorKind::NoSuchVariant { name, enum_shape } => {
241 if let Type::User(UserType::Enum(ed)) = enum_shape.ty {
242 write!(
243 f,
244 "Enum variant not found: {} in enum {}. Available variants: [",
245 name.red(),
246 enum_shape.yellow()
247 )?;
248
249 let mut first = true;
250 for variant in ed.variants.iter() {
251 if !first {
252 write!(f, ", ")?;
253 }
254 write!(f, "{}", variant.name.green())?;
255 first = false;
256 }
257
258 write!(f, "]")?;
259 Ok(())
260 } else {
261 write!(
262 f,
263 "Enum variant not found: {} in non-enum type {}",
264 name.red(),
265 enum_shape.yellow()
266 )?;
267 Ok(())
268 }
269 }
270 DeserErrorKind::VariantError(e) => {
271 write!(f, "Variant error: {e}")
272 }
273 }
274 }
275}
276
277#[cfg(not(feature = "rich-diagnostics"))]
278impl core::fmt::Display for DeserError<'_, '_> {
279 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
280 write!(f, "{} at byte {}", self.message(), self.span.start(),)
281 }
282}
283
284#[cfg(feature = "rich-diagnostics")]
285impl core::fmt::Display for DeserError<'_, '_> {
286 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
287 let Ok(orig_input_str) = core::str::from_utf8(&self.input[..]) else {
289 return write!(f, "(JSON input was invalid UTF-8)");
290 };
291
292 let source_id = self.source_id;
293 let mut span_start = self.span.start();
294 let mut span_end = self.span.end();
295 use alloc::borrow::Cow;
296 let mut input_str: Cow<'_, str> = Cow::Borrowed(orig_input_str);
297
298 let mut did_truncate = false;
315
316 {
317 let bytes = self.input.as_ref();
319 let line_start = bytes[..span_start]
320 .iter()
321 .rposition(|&b| b == b'\n')
322 .map(|pos| pos + 1)
323 .unwrap_or(0);
324 let line_end = bytes[span_start..]
325 .iter()
326 .position(|&b| b == b'\n')
327 .map(|pos| span_start + pos)
328 .unwrap_or(bytes.len());
329
330 if span_end <= line_end {
332 let before_chars = span_start - line_start;
334 let after_chars = line_end.saturating_sub(span_end);
335
336 if before_chars > 180 || after_chars > 180 {
338 let trim_left = if before_chars > 180 {
339 before_chars - 80
340 } else {
341 0
342 };
343 let trim_right = if after_chars > 180 {
344 after_chars - 80
345 } else {
346 0
347 };
348
349 let new_start = line_start + trim_left;
350 let new_end = line_end - trim_right;
351
352 let truncated = &orig_input_str[new_start..new_end];
353
354 let left_ellipsis = if trim_left > 0 { "…" } else { "" };
355 let right_ellipsis = if trim_right > 0 { "…" } else { "" };
356
357 let mut buf = String::with_capacity(
358 left_ellipsis.len() + truncated.len() + right_ellipsis.len(),
359 );
360 buf.push_str(left_ellipsis);
361 buf.push_str(truncated);
362 buf.push_str(right_ellipsis);
363
364 span_start = span_start - new_start + left_ellipsis.len();
366 span_end = span_end - new_start + left_ellipsis.len();
367
368 input_str = Cow::Owned(buf);
369
370 did_truncate = true; }
373 }
374 }
376
377 if did_truncate {
378 writeln!(
379 f,
380 "{}",
381 "WARNING: Input was truncated for display. Byte indexes in the error below do not match original input.".yellow().bold()
382 )?;
383 }
384
385 let mut report = Report::build(ReportKind::Error, (source_id, span_start..span_end))
386 .with_config(Config::new().with_index_type(IndexType::Byte));
387
388 let label = Label::new((source_id, span_start..span_end))
389 .with_message(self.message())
390 .with_color(Color::Red);
391
392 report = report.with_label(label);
393
394 let source = Source::from(input_str);
395
396 struct FmtWriter<'a, 'b: 'a> {
397 f: &'a mut core::fmt::Formatter<'b>,
398 error: Option<core::fmt::Error>,
399 }
400
401 impl core::fmt::Write for FmtWriter<'_, '_> {
402 fn write_str(&mut self, s: &str) -> core::fmt::Result {
403 if self.error.is_some() {
404 return Err(core::fmt::Error);
406 }
407 if let Err(e) = self.f.write_str(s) {
408 self.error = Some(e);
409 Err(core::fmt::Error)
410 } else {
411 Ok(())
412 }
413 }
414 }
415
416 struct IoWriter<'a, 'b: 'a> {
417 inner: FmtWriter<'a, 'b>,
418 }
419
420 impl std::io::Write for IoWriter<'_, '_> {
421 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
422 match core::str::from_utf8(buf) {
423 Ok(s) => match core::fmt::Write::write_str(&mut self.inner, s) {
424 Ok(()) => Ok(buf.len()),
425 Err(_) => Err(std::io::ErrorKind::Other.into()),
426 },
427 Err(_) => Err(std::io::ErrorKind::InvalidData.into()),
428 }
429 }
430 fn flush(&mut self) -> std::io::Result<()> {
431 Ok(())
432 }
433 }
434
435 let cache = (source_id, &source);
436
437 let fmt_writer = FmtWriter { f, error: None };
438 let mut io_writer = IoWriter { inner: fmt_writer };
439
440 if report.finish().write(cache, &mut io_writer).is_err() {
441 return write!(f, "Error formatting with ariadne");
442 }
443
444 if io_writer.inner.error.is_some() {
446 return write!(f, "Error writing ariadne output to fmt::Formatter");
447 }
448
449 Ok(())
450 }
451}
452
453impl core::error::Error for DeserError<'_, '_> {}