1#[cfg(feature = "alloc")]
2use alloc::format;
3#[cfg(feature = "alloc")]
4use alloc::string::String;
5
6#[cfg(feature = "rich-diagnostics")]
7use ariadne::{Color, Config, IndexType, Label, Report, ReportKind, Source};
8use facet_reflect::ReflectError;
9use owo_colors::OwoColorize;
10
11use super::{Token, TokenErrorKind, tokenizer::Span};
12use facet_core::Def;
13use facet_core::Shape;
14
15pub struct JsonError<'input> {
17 pub input: alloc::borrow::Cow<'input, [u8]>,
19
20 pub span: Span,
22
23 pub path: String,
25
26 pub kind: JsonErrorKind,
28}
29
30impl<'input> JsonError<'input> {
31 pub fn new(kind: JsonErrorKind, input: &'input [u8], span: Span, path: String) -> Self {
39 Self {
40 input: alloc::borrow::Cow::Borrowed(input),
41 span,
42 kind,
43 path,
44 }
45 }
46
47 pub fn message(&self) -> JsonErrorMessage<'_> {
49 JsonErrorMessage(self)
50 }
51}
52
53pub struct JsonErrorMessage<'a>(&'a JsonError<'a>);
55
56impl core::fmt::Display for JsonErrorMessage<'_> {
57 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58 match &self.0.kind {
59 JsonErrorKind::UnexpectedEof(msg) => write!(f, "Unexpected end of file: {}", msg.red()),
60 JsonErrorKind::MissingField(fld) => write!(f, "Missing required field: {}", fld.red()),
61 JsonErrorKind::UnexpectedToken { got, wanted } => {
62 write!(
63 f,
64 "Unexpected token: got {}, wanted {}",
65 got.red(),
66 wanted.green()
67 )
68 }
69 JsonErrorKind::NumberOutOfRange(n) => {
70 write!(f, "Number out of range: {}", n.red())
71 }
72 JsonErrorKind::StringAsNumber(s) => {
73 write!(f, "Expected a string but got number: {}", s.red())
74 }
75 JsonErrorKind::UnknownField { field_name, shape } => {
76 write!(
77 f,
78 "Unknown field: {} for shape {}",
79 field_name.red(),
80 shape.yellow()
81 )
82 }
83 JsonErrorKind::InvalidUtf8(e) => write!(f, "Invalid UTF-8 encoding: {}", e.red()),
84 JsonErrorKind::ReflectError(e) => write!(f, "{e}"),
85 JsonErrorKind::SyntaxError(e) => write!(f, "{e}"),
86 JsonErrorKind::Unimplemented(s) => {
87 write!(f, "Feature not yet implemented: {}", s.yellow())
88 }
89 JsonErrorKind::UnsupportedType { got, wanted } => {
90 write!(
91 f,
92 "Unsupported type: got {}, wanted {}",
93 got.red(),
94 wanted.green()
95 )
96 }
97 JsonErrorKind::NoSuchVariant { name, enum_shape } => match enum_shape.def {
98 Def::Enum(ed) => {
99 write!(
100 f,
101 "Enum variant not found: {} in enum {}. Available variants: [",
102 name.red(),
103 enum_shape.yellow()
104 )?;
105
106 let mut first = true;
107 for variant in ed.variants.iter() {
108 if !first {
109 write!(f, ", ")?;
110 }
111 write!(f, "{}", variant.name.green())?;
112 first = false;
113 }
114
115 write!(f, "]")
116 }
117 _ => {
118 write!(
119 f,
120 "Enum variant not found: {} in enum {}. No variants available (not an enum)",
121 name.red(),
122 enum_shape.yellow()
123 )
124 }
125 },
126 }
127 }
128}
129
130#[derive(Debug, PartialEq, Clone)]
132pub enum JsonErrorKind {
133 UnexpectedEof(&'static str),
135 MissingField(&'static str),
137 UnexpectedToken {
139 got: Token,
141
142 wanted: &'static str,
144 },
145 NumberOutOfRange(f64),
147 StringAsNumber(String),
149 UnknownField {
151 field_name: String,
153 shape: &'static Shape,
155 },
156 InvalidUtf8(String),
158 ReflectError(ReflectError),
160 SyntaxError(TokenErrorKind),
162 Unimplemented(&'static str),
164 UnsupportedType {
166 got: &'static Shape,
168
169 wanted: &'static str,
171 },
172 NoSuchVariant {
174 name: String,
176
177 enum_shape: &'static Shape,
179 },
180}
181
182impl From<ReflectError> for JsonErrorKind {
183 fn from(err: ReflectError) -> Self {
184 JsonErrorKind::ReflectError(err)
185 }
186}
187
188#[cfg(not(feature = "rich-diagnostics"))]
189impl core::fmt::Display for JsonError<'_> {
190 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
191 write!(
192 f,
193 "{} at byte {} in path {}",
194 self.message(),
195 self.span.start,
196 self.path
197 )
198 }
199}
200
201#[cfg(feature = "rich-diagnostics")]
202impl core::fmt::Display for JsonError<'_> {
203 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
204 let Ok(input_str) = core::str::from_utf8(&self.input[..]) else {
205 return write!(f, "(JSON input was invalid UTF-8)");
206 };
207
208 let source_id = "json";
209 let span_start = self.span.start();
210 let span_end = self.span.end();
211
212 let mut report = Report::build(ReportKind::Error, (source_id, span_start..span_end))
213 .with_message(format!("Error at {}", self.path.yellow()))
214 .with_config(Config::new().with_index_type(IndexType::Byte));
215
216 let label = Label::new((source_id, span_start..span_end))
217 .with_message(self.message())
218 .with_color(Color::Red);
219
220 report = report.with_label(label);
221
222 let source = Source::from(input_str);
223
224 let mut writer = Vec::new();
225 let cache = (source_id, &source);
226
227 if report.finish().write(cache, &mut writer).is_err() {
228 return write!(f, "Error formatting with ariadne");
229 }
230
231 if let Ok(output) = String::from_utf8(writer) {
232 write!(f, "{}", output)
233 } else {
234 write!(f, "Error converting ariadne output to string")
235 }
236 }
237}
238
239impl core::fmt::Debug for JsonError<'_> {
240 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
241 core::fmt::Display::fmt(self, f)
242 }
243}
244
245impl core::error::Error for JsonError<'_> {}