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, 'shape> {
16 pub input: alloc::borrow::Cow<'input, [u8]>,
18
19 pub span: Span,
21
22 pub kind: DeserErrorKind<'shape>,
24
25 pub source_id: &'static str,
27}
28
29impl<'shape> DeserError<'_, 'shape> {
30 pub fn into_owned(self) -> DeserError<'static, 'shape> {
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<'shape> {
50 UnexpectedByte {
52 got: u8,
54 wanted: &'static str,
56 },
57
58 UnexpectedChar {
60 got: char,
62 wanted: &'static str,
64 },
65
66 UnexpectedOutcome {
68 got: Outcome<'static>,
70 wanted: &'static str,
72 },
73
74 UnexpectedEof {
76 wanted: &'static str,
78 },
79
80 MissingValue {
82 expected: &'static str,
84 field: String,
86 },
87
88 MissingField(&'static str),
90
91 NumberOutOfRange(f64),
93
94 StringAsNumber(String),
96
97 UnknownField {
99 field_name: String,
101
102 shape: &'shape Shape<'shape>,
104 },
105
106 InvalidUtf8(String),
108
109 ReflectError(ReflectError<'shape>),
111
112 Unimplemented(&'static str),
114
115 UnsupportedType {
117 got: &'shape Shape<'shape>,
119
120 wanted: &'static str,
122 },
123
124 NoSuchVariant {
126 name: String,
128
129 enum_shape: &'shape Shape<'shape>,
131 },
132
133 VariantError(VariantError),
135}
136
137impl<'input, 'shape> DeserError<'input, 'shape> {
138 pub fn new<I>(
140 kind: DeserErrorKind<'shape>,
141 input: &'input I,
142 span: Span,
143 source_id: &'static str,
144 ) -> Self
145 where
146 I: ?Sized + 'input + InputDebug,
147 {
148 Self {
149 input: input.as_cow(),
150 span,
151 kind,
152 source_id,
153 }
154 }
155
156 pub(crate) fn new_reflect<I>(
165 e: ReflectError<'shape>,
166 input: &'input I,
167 span: Span,
168 source_id: &'static str,
169 ) -> Self
170 where
171 I: ?Sized + 'input + InputDebug,
172 {
173 DeserError::new(DeserErrorKind::ReflectError(e), input, span, source_id)
174 }
175
176 pub fn with_source_id(mut self, source_id: &'static str) -> Self {
196 self.source_id = source_id;
197 self
198 }
199
200 pub fn message(&self) -> DeserErrorMessage<'_, '_> {
202 DeserErrorMessage(self)
203 }
204}
205
206pub struct DeserErrorMessage<'input, 'shape>(&'input DeserError<'input, 'shape>);
208
209impl core::fmt::Display for DeserErrorMessage<'_, '_> {
210 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
211 match &self.0.kind {
212 DeserErrorKind::UnexpectedByte { got, wanted } => write!(
213 f,
214 "Unexpected byte: got 0x{:02X}, wanted {}",
215 got.red(),
216 wanted.yellow()
217 ),
218 DeserErrorKind::UnexpectedChar { got, wanted } => write!(
219 f,
220 "Unexpected character: got '{}', wanted {}",
221 got.red(),
222 wanted.yellow()
223 ),
224 DeserErrorKind::UnexpectedOutcome { got, wanted } => {
225 write!(f, "Unexpected {}, wanted {}", got.red(), wanted.yellow())
226 }
227 DeserErrorKind::UnexpectedEof { wanted } => {
228 write!(f, "Unexpected end of file: wanted {}", wanted.red())
229 }
230 DeserErrorKind::MissingValue { expected, field } => {
231 write!(f, "Missing {} for {}", expected.red(), field.yellow())
232 }
233 DeserErrorKind::MissingField(fld) => write!(f, "Missing required field: {}", fld.red()),
234 DeserErrorKind::NumberOutOfRange(n) => {
235 write!(f, "Number out of range: {}", n.red())
236 }
237 DeserErrorKind::StringAsNumber(s) => {
238 write!(f, "Expected a string but got number: {}", s.red())
239 }
240 DeserErrorKind::UnknownField { field_name, shape } => {
241 write!(
242 f,
243 "Unknown field: {} for shape {}",
244 field_name.red(),
245 shape.yellow()
246 )
247 }
248 DeserErrorKind::InvalidUtf8(e) => write!(f, "Invalid UTF-8 encoding: {}", e.red()),
249 DeserErrorKind::ReflectError(e) => write!(f, "{e}"),
250 DeserErrorKind::Unimplemented(s) => {
251 write!(f, "Feature not yet implemented: {}", s.yellow())
252 }
253 DeserErrorKind::UnsupportedType { got, wanted } => {
254 write!(
255 f,
256 "Unsupported type: got {}, wanted {}",
257 got.red(),
258 wanted.green()
259 )
260 }
261 DeserErrorKind::NoSuchVariant { name, enum_shape } => {
262 if let Type::User(UserType::Enum(ed)) = enum_shape.ty {
263 write!(
264 f,
265 "Enum variant not found: {} in enum {}. Available variants: [",
266 name.red(),
267 enum_shape.yellow()
268 )?;
269
270 let mut first = true;
271 for variant in ed.variants.iter() {
272 if !first {
273 write!(f, ", ")?;
274 }
275 write!(f, "{}", variant.name.green())?;
276 first = false;
277 }
278
279 write!(f, "]")?;
280 Ok(())
281 } else {
282 write!(
283 f,
284 "Enum variant not found: {} in non-enum type {}",
285 name.red(),
286 enum_shape.yellow()
287 )?;
288 Ok(())
289 }
290 }
291 DeserErrorKind::VariantError(e) => {
292 write!(f, "Variant error: {e}")
293 }
294 }
295 }
296}
297
298#[cfg(not(feature = "rich-diagnostics"))]
299impl core::fmt::Display for DeserError<'_, '_> {
300 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
301 write!(f, "{} at byte {}", self.message(), self.span.start(),)
302 }
303}
304
305#[cfg(feature = "rich-diagnostics")]
306impl core::fmt::Display for DeserError<'_, '_> {
307 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
308 let Ok(input_str) = core::str::from_utf8(&self.input[..]) else {
309 return write!(f, "(JSON input was invalid UTF-8)");
310 };
311
312 let source_id = self.source_id;
313 let span_start = self.span.start();
314 let span_end = self.span.end();
315
316 let mut report = Report::build(ReportKind::Error, (source_id, span_start..span_end))
317 .with_config(Config::new().with_index_type(IndexType::Byte));
318
319 let label = Label::new((source_id, span_start..span_end))
320 .with_message(self.message())
321 .with_color(Color::Red);
322
323 report = report.with_label(label);
324
325 let source = Source::from(input_str);
326
327 struct FmtWriter<'a, 'b: 'a> {
328 f: &'a mut core::fmt::Formatter<'b>,
329 error: Option<core::fmt::Error>,
330 }
331
332 impl core::fmt::Write for FmtWriter<'_, '_> {
333 fn write_str(&mut self, s: &str) -> core::fmt::Result {
334 if self.error.is_some() {
335 return Err(core::fmt::Error);
337 }
338 if let Err(e) = self.f.write_str(s) {
339 self.error = Some(e);
340 Err(core::fmt::Error)
341 } else {
342 Ok(())
343 }
344 }
345 }
346
347 struct IoWriter<'a, 'b: 'a> {
348 inner: FmtWriter<'a, 'b>,
349 }
350
351 impl std::io::Write for IoWriter<'_, '_> {
352 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
353 match core::str::from_utf8(buf) {
354 Ok(s) => match core::fmt::Write::write_str(&mut self.inner, s) {
355 Ok(()) => Ok(buf.len()),
356 Err(_) => Err(std::io::ErrorKind::Other.into()),
357 },
358 Err(_) => Err(std::io::ErrorKind::InvalidData.into()),
359 }
360 }
361 fn flush(&mut self) -> std::io::Result<()> {
362 Ok(())
363 }
364 }
365
366 let cache = (source_id, &source);
367
368 let fmt_writer = FmtWriter { f, error: None };
369 let mut io_writer = IoWriter { inner: fmt_writer };
370
371 if report.finish().write(cache, &mut io_writer).is_err() {
372 return write!(f, "Error formatting with ariadne");
373 }
374
375 if io_writer.inner.error.is_some() {
377 return write!(f, "Error writing ariadne output to fmt::Formatter");
378 }
379
380 Ok(())
381 }
382}
383
384impl core::error::Error for DeserError<'_, '_> {}