1use std::error::Error;
11use std::fmt;
12use std::sync::Arc;
13
14use qubit_budget::MeasuredBudgetError;
15use qubit_budget::ResourceQuantity;
16use qubit_budget::json::JsonResource;
17use serde_json::Error as JsonError;
18
19use super::DiagnosticPolicy;
20use super::JsonDecodeErrorKind;
21use super::JsonDecodeErrorSource;
22use super::JsonDecodeStage;
23use super::JsonRootKind;
24use super::JsonSyntaxError;
25use crate::lexical::JsonLexicalFailure;
26
27#[must_use]
50#[derive(Clone)]
51pub struct JsonDecodeError<R = JsonResource, Q = usize>
52where
53 Q: Copy + fmt::Debug,
54{
55 diagnostic_policy: DiagnosticPolicy,
57 failure: JsonDecodeErrorSource<R, Q>,
59}
60
61impl<R, Q> JsonDecodeError<R, Q>
62where
63 Q: Copy + fmt::Debug,
64{
65 #[must_use = "return or inspect the constructed decoding error"]
67 pub(crate) const fn budget(
68 source: MeasuredBudgetError<R, Q>,
69 stage: JsonDecodeStage,
70 raw_input_bytes: usize,
71 normalized_input_bytes: Option<usize>,
72 diagnostic_policy: DiagnosticPolicy,
73 ) -> Self {
74 Self {
75 diagnostic_policy,
76 failure: JsonDecodeErrorSource::Budget {
77 stage,
78 raw_input_bytes,
79 normalized_input_bytes,
80 source,
81 },
82 }
83 }
84
85 #[must_use = "return or inspect the constructed decoding error"]
87 pub(crate) const fn empty_input(
88 stage: JsonDecodeStage,
89 raw_input_bytes: usize,
90 normalized_input_bytes: Option<usize>,
91 diagnostic_policy: DiagnosticPolicy,
92 ) -> Self {
93 Self {
94 diagnostic_policy,
95 failure: JsonDecodeErrorSource::EmptyInput {
96 stage,
97 raw_input_bytes,
98 normalized_input_bytes,
99 },
100 }
101 }
102
103 #[must_use = "return or inspect the constructed decoding error"]
105 pub(crate) fn invalid_utf8(
106 source: std::str::Utf8Error,
107 raw_input_bytes: usize,
108 diagnostic_policy: DiagnosticPolicy,
109 ) -> Self {
110 let valid_up_to = source.valid_up_to();
111 let error_len = source.error_len();
112 let source = (diagnostic_policy == DiagnosticPolicy::Detailed).then_some(source);
113 Self {
114 diagnostic_policy,
115 failure: JsonDecodeErrorSource::InvalidUtf8 {
116 raw_input_bytes,
117 valid_up_to,
118 error_len,
119 source,
120 },
121 }
122 }
123
124 #[must_use = "return or inspect the constructed decoding error"]
126 pub(crate) fn invalid_json(
127 syntax_source: JsonLexicalFailure,
128 detailed_source: Option<Arc<dyn Error + Send + Sync>>,
129 raw_input_bytes: usize,
130 normalized_input_bytes: Option<usize>,
131 diagnostic_policy: DiagnosticPolicy,
132 ) -> Self {
133 Self {
134 diagnostic_policy,
135 failure: JsonDecodeErrorSource::InvalidJson {
136 raw_input_bytes,
137 normalized_input_bytes,
138 syntax: JsonSyntaxError::from_lexical(syntax_source),
139 source: detailed_source,
140 },
141 }
142 }
143
144 #[must_use = "return or inspect the constructed decoding error"]
146 pub(crate) const fn unexpected_top_level(
147 expected: JsonRootKind,
148 actual: JsonRootKind,
149 raw_input_bytes: usize,
150 normalized_input_bytes: Option<usize>,
151 diagnostic_policy: DiagnosticPolicy,
152 ) -> Self {
153 Self {
154 diagnostic_policy,
155 failure: JsonDecodeErrorSource::UnexpectedTopLevel {
156 raw_input_bytes,
157 normalized_input_bytes,
158 expected,
159 actual,
160 },
161 }
162 }
163
164 #[must_use = "return or inspect the constructed decoding error"]
167 pub(crate) fn deserialize(
168 source: JsonError,
169 raw_input_bytes: usize,
170 normalized_input_bytes: Option<usize>,
171 diagnostic_policy: DiagnosticPolicy,
172 ) -> Self {
173 let line = source.line();
174 let column = source.column();
175 let source =
176 (diagnostic_policy == DiagnosticPolicy::Detailed).then(|| Arc::new(source) as Arc<dyn Error + Send + Sync>);
177 Self {
178 diagnostic_policy,
179 failure: JsonDecodeErrorSource::Deserialize {
180 raw_input_bytes,
181 normalized_input_bytes,
182 line,
183 column,
184 source,
185 },
186 }
187 }
188
189 #[must_use]
195 #[inline(always)]
196 pub const fn kind(&self) -> JsonDecodeErrorKind {
197 match self.failure {
198 JsonDecodeErrorSource::Budget { .. } => JsonDecodeErrorKind::Budget,
199 JsonDecodeErrorSource::EmptyInput { .. } => JsonDecodeErrorKind::EmptyInput,
200 JsonDecodeErrorSource::InvalidUtf8 { .. } => JsonDecodeErrorKind::InvalidUtf8,
201 JsonDecodeErrorSource::InvalidJson { .. } => JsonDecodeErrorKind::InvalidJson,
202 JsonDecodeErrorSource::UnexpectedTopLevel { .. } => JsonDecodeErrorKind::UnexpectedTopLevel,
203 JsonDecodeErrorSource::Deserialize { .. } => JsonDecodeErrorKind::Deserialize,
204 }
205 }
206
207 #[must_use]
213 #[inline(always)]
214 pub const fn stage(&self) -> JsonDecodeStage {
215 match self.failure {
216 JsonDecodeErrorSource::Budget { stage, .. } | JsonDecodeErrorSource::EmptyInput { stage, .. } => stage,
217 JsonDecodeErrorSource::InvalidUtf8 { .. } => JsonDecodeStage::DecodeText,
218 JsonDecodeErrorSource::InvalidJson { .. } => JsonDecodeStage::Parse,
219 JsonDecodeErrorSource::UnexpectedTopLevel { .. } => JsonDecodeStage::TopLevelCheck,
220 JsonDecodeErrorSource::Deserialize { .. } => JsonDecodeStage::Deserialize,
221 }
222 }
223
224 #[must_use]
230 #[inline(always)]
231 pub const fn diagnostic_policy(&self) -> DiagnosticPolicy {
232 self.diagnostic_policy
233 }
234
235 #[must_use]
241 #[inline(always)]
242 pub const fn raw_input_bytes(&self) -> usize {
243 match self.failure {
244 JsonDecodeErrorSource::Budget { raw_input_bytes, .. }
245 | JsonDecodeErrorSource::EmptyInput { raw_input_bytes, .. }
246 | JsonDecodeErrorSource::InvalidUtf8 { raw_input_bytes, .. }
247 | JsonDecodeErrorSource::InvalidJson { raw_input_bytes, .. }
248 | JsonDecodeErrorSource::UnexpectedTopLevel { raw_input_bytes, .. }
249 | JsonDecodeErrorSource::Deserialize { raw_input_bytes, .. } => raw_input_bytes,
250 }
251 }
252
253 #[must_use]
260 #[inline(always)]
261 pub const fn normalized_input_bytes(&self) -> Option<usize> {
262 match self.failure {
263 JsonDecodeErrorSource::Budget {
264 normalized_input_bytes, ..
265 }
266 | JsonDecodeErrorSource::EmptyInput {
267 normalized_input_bytes, ..
268 }
269 | JsonDecodeErrorSource::InvalidJson {
270 normalized_input_bytes, ..
271 }
272 | JsonDecodeErrorSource::UnexpectedTopLevel {
273 normalized_input_bytes, ..
274 }
275 | JsonDecodeErrorSource::Deserialize {
276 normalized_input_bytes, ..
277 } => normalized_input_bytes,
278 JsonDecodeErrorSource::InvalidUtf8 { .. } => None,
279 }
280 }
281
282 #[must_use]
288 #[inline(always)]
289 pub const fn line(&self) -> Option<usize> {
290 match &self.failure {
291 JsonDecodeErrorSource::InvalidJson { syntax, .. } => Some(syntax.line()),
292 JsonDecodeErrorSource::Deserialize { line, .. } if *line > 0 => Some(*line),
293 _ => None,
294 }
295 }
296
297 #[must_use]
303 #[inline(always)]
304 pub const fn column(&self) -> Option<usize> {
305 match &self.failure {
306 JsonDecodeErrorSource::InvalidJson { syntax, .. } => Some(syntax.column()),
307 JsonDecodeErrorSource::Deserialize { column, .. } if *column > 0 => Some(*column),
308 _ => None,
309 }
310 }
311
312 #[must_use]
318 #[inline(always)]
319 pub const fn syntax_error(&self) -> Option<&JsonSyntaxError> {
320 match &self.failure {
321 JsonDecodeErrorSource::InvalidJson { syntax, .. } => Some(syntax),
322 _ => None,
323 }
324 }
325
326 #[must_use]
333 #[inline(always)]
334 pub const fn budget_error(&self) -> Option<&MeasuredBudgetError<R, Q>> {
335 match &self.failure {
336 JsonDecodeErrorSource::Budget { source, .. } => Some(source),
337 _ => None,
338 }
339 }
340
341 #[must_use]
348 #[inline(always)]
349 pub const fn utf8_valid_up_to(&self) -> Option<usize> {
350 match self.failure {
351 JsonDecodeErrorSource::InvalidUtf8 { valid_up_to, .. } => Some(valid_up_to),
352 _ => None,
353 }
354 }
355
356 #[must_use]
363 #[inline(always)]
364 pub const fn utf8_error_len(&self) -> Option<usize> {
365 match self.failure {
366 JsonDecodeErrorSource::InvalidUtf8 { error_len, .. } => error_len,
367 _ => None,
368 }
369 }
370
371 #[must_use]
378 #[inline(always)]
379 pub const fn expected_top_level(&self) -> Option<JsonRootKind> {
380 match self.failure {
381 JsonDecodeErrorSource::UnexpectedTopLevel { expected, .. } => Some(expected),
382 _ => None,
383 }
384 }
385
386 #[must_use]
392 #[inline(always)]
393 pub const fn actual_top_level(&self) -> Option<JsonRootKind> {
394 match self.failure {
395 JsonDecodeErrorSource::UnexpectedTopLevel { actual, .. } => Some(actual),
396 _ => None,
397 }
398 }
399
400 #[inline(always)]
412 pub fn into_source(self) -> JsonDecodeErrorSource<R, Q> {
413 self.failure
414 }
415}
416
417impl<R, Q> fmt::Debug for JsonDecodeError<R, Q>
418where
419 R: fmt::Debug,
420 Q: Copy + fmt::Debug,
421{
422 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
424 formatter
425 .debug_struct("JsonDecodeError")
426 .field("diagnostic_policy", &self.diagnostic_policy)
427 .field("failure", &self.failure)
428 .finish()
429 }
430}
431
432impl<R, Q> fmt::Display for JsonDecodeError<R, Q>
433where
434 R: fmt::Debug,
435 Q: ResourceQuantity,
436{
437 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
439 match &self.failure {
440 JsonDecodeErrorSource::Budget { source, .. } => {
441 write!(formatter, "JSON resource budget rejected input: {source}")
442 }
443 JsonDecodeErrorSource::EmptyInput { .. } => formatter.write_str("JSON input is empty after normalization"),
444 JsonDecodeErrorSource::InvalidUtf8 { source, .. } => match source {
445 Some(source) => write!(formatter, "Failed to decode JSON input as UTF-8: {source}"),
446 None => formatter.write_str("Failed to decode JSON input as UTF-8"),
447 },
448 JsonDecodeErrorSource::InvalidJson { syntax, source, .. } => match source {
449 Some(source) => {
450 write!(formatter, "Failed to parse JSON: {source}")
451 }
452 None => write!(formatter, "Failed to parse JSON: {syntax}"),
453 },
454 JsonDecodeErrorSource::UnexpectedTopLevel { expected, actual, .. } => {
455 write!(
456 formatter,
457 "Unexpected JSON top-level type: expected {expected}, got {actual}"
458 )
459 }
460 JsonDecodeErrorSource::Deserialize {
461 normalized_input_bytes,
462 line,
463 column,
464 source,
465 ..
466 } => match source {
467 Some(source) => write!(formatter, "Failed to deserialize JSON value: {source}"),
468 None if normalized_input_bytes.is_some() => write!(
469 formatter,
470 "Failed to deserialize JSON value at normalized line {line} column {column}"
471 ),
472 None => write!(
473 formatter,
474 "Failed to deserialize JSON value at line {line} column {column}"
475 ),
476 },
477 }
478 }
479}
480
481impl<R, Q> Error for JsonDecodeError<R, Q>
482where
483 R: fmt::Debug + 'static,
484 Q: ResourceQuantity + 'static,
485{
486 fn source(&self) -> Option<&(dyn Error + 'static)> {
489 match &self.failure {
490 JsonDecodeErrorSource::Budget { source, .. } => Some(source),
491 JsonDecodeErrorSource::InvalidUtf8 {
492 source: Some(source), ..
493 } => Some(source),
494 JsonDecodeErrorSource::InvalidJson {
495 source: Some(source), ..
496 } => Some(source.as_ref()),
497 JsonDecodeErrorSource::Deserialize {
498 source: Some(source), ..
499 } => Some(source.as_ref()),
500 _ => None,
501 }
502 }
503}