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::Shape;
13
14pub struct JsonError<'input> {
16 pub input: alloc::borrow::Cow<'input, [u8]>,
18
19 pub span: Span,
21
22 pub path: String,
24
25 pub kind: JsonErrorKind,
27}
28
29impl<'input> JsonError<'input> {
30 pub fn new(kind: JsonErrorKind, input: &'input [u8], span: Span, path: String) -> Self {
38 Self {
39 input: alloc::borrow::Cow::Borrowed(input),
40 span,
41 kind,
42 path,
43 }
44 }
45
46 pub fn message(&self) -> String {
48 match &self.kind {
49 JsonErrorKind::UnexpectedEof(msg) => format!("Unexpected end of file: {}", msg.red()),
50 JsonErrorKind::MissingField(fld) => format!("Missing required field: {}", fld.red()),
51 JsonErrorKind::UnexpectedToken { got, wanted } => {
52 format!(
53 "Unexpected token: got {}, wanted {}",
54 got.red(),
55 wanted.green()
56 )
57 }
58 JsonErrorKind::NumberOutOfRange(n) => {
59 format!("Number out of range: {}", n.red())
60 }
61 JsonErrorKind::StringAsNumber(s) => {
62 format!("Expected a string but got number: {}", s.red())
63 }
64 JsonErrorKind::UnknownField { field_name, shape } => {
65 format!(
66 "Unknown field: {} for shape {}",
67 field_name.red(),
68 shape.yellow()
69 )
70 }
71 JsonErrorKind::InvalidUtf8(e) => format!("Invalid UTF-8 encoding: {}", e.red()),
72 JsonErrorKind::ReflectError(e) => format!("{e}"),
73 JsonErrorKind::SyntaxError(e) => format!("{e}"),
74 JsonErrorKind::Unimplemented(s) => {
75 format!("Feature not yet implemented: {}", s.yellow())
76 }
77 JsonErrorKind::UnsupportedType { got, wanted } => {
78 format!(
79 "Unsupported type: got {}, wanted {}",
80 got.red(),
81 wanted.green()
82 )
83 }
84 JsonErrorKind::NoSuchVariant { name, enum_shape } => {
85 format!(
86 "Enum variant not found: {} in enum {}",
87 name.red(),
88 enum_shape.yellow()
89 )
90 }
91 }
92 }
93}
94
95#[derive(Debug, PartialEq, Clone)]
97pub enum JsonErrorKind {
98 UnexpectedEof(&'static str),
100 MissingField(&'static str),
102 UnexpectedToken {
104 got: Token,
106
107 wanted: &'static str,
109 },
110 NumberOutOfRange(f64),
112 StringAsNumber(String),
114 UnknownField {
116 field_name: String,
118 shape: &'static Shape,
120 },
121 InvalidUtf8(String),
123 ReflectError(ReflectError),
125 SyntaxError(TokenErrorKind),
127 Unimplemented(&'static str),
129 UnsupportedType {
131 got: &'static Shape,
133
134 wanted: &'static str,
136 },
137 NoSuchVariant {
139 name: String,
141
142 enum_shape: &'static Shape,
144 },
145}
146
147impl From<ReflectError> for JsonErrorKind {
148 fn from(err: ReflectError) -> Self {
149 JsonErrorKind::ReflectError(err)
150 }
151}
152
153#[cfg(not(feature = "rich-diagnostics"))]
154impl core::fmt::Display for JsonError<'_> {
155 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
156 write!(
157 f,
158 "{} at byte {} in path {}",
159 self.message(),
160 self.span.start,
161 self.path
162 )
163 }
164}
165
166#[cfg(feature = "rich-diagnostics")]
167impl core::fmt::Display for JsonError<'_> {
168 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
169 let Ok(input_str) = core::str::from_utf8(&self.input[..]) else {
170 return write!(f, "(JSON input was invalid UTF-8)");
171 };
172
173 let source_id = "json";
174 let span_start = self.span.start();
175 let span_end = self.span.end();
176
177 let mut report = Report::build(ReportKind::Error, (source_id, span_start..span_end))
178 .with_message(format!("Error at {}", self.path.yellow()))
179 .with_config(Config::new().with_index_type(IndexType::Byte));
180
181 let label = Label::new((source_id, span_start..span_end))
182 .with_message(self.message())
183 .with_color(Color::Red);
184
185 report = report.with_label(label);
186
187 let source = Source::from(input_str);
188
189 let mut writer = Vec::new();
190 let cache = (source_id, &source);
191
192 if report.finish().write(cache, &mut writer).is_err() {
193 return write!(f, "Error formatting with ariadne");
194 }
195
196 if let Ok(output) = String::from_utf8(writer) {
197 write!(f, "{}", output)
198 } else {
199 write!(f, "Error converting ariadne output to string")
200 }
201 }
202}
203
204impl core::fmt::Debug for JsonError<'_> {
205 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
206 core::fmt::Display::fmt(self, f)
207 }
208}
209
210impl core::error::Error for JsonError<'_> {}