1use std::error::Error;
11use std::fmt;
12use std::io;
13use std::sync::Arc;
14
15use qubit_budget::MeasuredBudgetError;
16use qubit_budget::ResourceQuantity;
17use qubit_budget::json::JsonResource;
18use serde_json::Error as JsonError;
19
20use super::DiagnosticPolicy;
21use super::JsonDecodeErrorKind;
22use super::JsonDecodeErrorSource;
23use super::JsonDecodeStage;
24use super::JsonRootKind;
25use super::JsonSyntaxError;
26use crate::lexical::JsonLexicalFailure;
27
28#[must_use]
51#[derive(Clone)]
52pub struct JsonDecodeError<R = JsonResource, Q = usize>
53where
54 Q: Copy + fmt::Debug,
55{
56 diagnostic_policy: DiagnosticPolicy,
58 failure: JsonDecodeErrorSource<R, Q>,
60}
61
62impl<R, Q> JsonDecodeError<R, Q>
63where
64 Q: Copy + fmt::Debug,
65{
66 #[must_use = "return or inspect the constructed decoding error"]
74 pub(crate) fn read(source: io::Error, raw_input_bytes: usize, diagnostic_policy: DiagnosticPolicy) -> Self {
75 let kind = source.kind();
76 let source = (diagnostic_policy == DiagnosticPolicy::Detailed).then(|| Arc::new(source));
77 Self {
78 diagnostic_policy,
79 failure: JsonDecodeErrorSource::Read {
80 raw_input_bytes,
81 kind,
82 source,
83 },
84 }
85 }
86
87 #[must_use = "return or inspect the constructed decoding error"]
97 pub(crate) const fn budget(
98 source: MeasuredBudgetError<R, Q>,
99 stage: JsonDecodeStage,
100 raw_input_bytes: usize,
101 normalized_input_bytes: Option<usize>,
102 diagnostic_policy: DiagnosticPolicy,
103 ) -> Self {
104 Self {
105 diagnostic_policy,
106 failure: JsonDecodeErrorSource::Budget {
107 stage,
108 raw_input_bytes,
109 normalized_input_bytes,
110 source,
111 },
112 }
113 }
114
115 #[must_use = "return or inspect the constructed decoding error"]
124 pub(crate) const fn empty_input(
125 stage: JsonDecodeStage,
126 raw_input_bytes: usize,
127 normalized_input_bytes: Option<usize>,
128 diagnostic_policy: DiagnosticPolicy,
129 ) -> Self {
130 Self {
131 diagnostic_policy,
132 failure: JsonDecodeErrorSource::EmptyInput {
133 stage,
134 raw_input_bytes,
135 normalized_input_bytes,
136 },
137 }
138 }
139
140 #[must_use = "return or inspect the constructed decoding error"]
148 pub(crate) fn invalid_utf8(
149 source: std::str::Utf8Error,
150 raw_input_bytes: usize,
151 diagnostic_policy: DiagnosticPolicy,
152 ) -> Self {
153 let valid_up_to = source.valid_up_to();
154 let error_len = source.error_len();
155 let source = (diagnostic_policy == DiagnosticPolicy::Detailed).then_some(source);
156 Self {
157 diagnostic_policy,
158 failure: JsonDecodeErrorSource::InvalidUtf8 {
159 raw_input_bytes,
160 valid_up_to,
161 error_len,
162 source,
163 },
164 }
165 }
166
167 #[must_use = "return or inspect the constructed decoding error"]
178 pub(crate) fn invalid_json(
179 syntax_source: JsonLexicalFailure,
180 detailed_source: Option<Arc<dyn Error + Send + Sync>>,
181 raw_input_bytes: usize,
182 normalized_input_bytes: Option<usize>,
183 diagnostic_policy: DiagnosticPolicy,
184 ) -> Self {
185 Self {
186 diagnostic_policy,
187 failure: JsonDecodeErrorSource::InvalidJson {
188 raw_input_bytes,
189 normalized_input_bytes,
190 syntax: JsonSyntaxError::from_lexical(syntax_source),
191 source: detailed_source,
192 },
193 }
194 }
195
196 #[must_use = "return or inspect the constructed decoding error"]
206 pub(crate) const fn unexpected_top_level(
207 expected: JsonRootKind,
208 actual: JsonRootKind,
209 raw_input_bytes: usize,
210 normalized_input_bytes: Option<usize>,
211 diagnostic_policy: DiagnosticPolicy,
212 ) -> Self {
213 Self {
214 diagnostic_policy,
215 failure: JsonDecodeErrorSource::UnexpectedTopLevel {
216 raw_input_bytes,
217 normalized_input_bytes,
218 expected,
219 actual,
220 },
221 }
222 }
223
224 #[must_use = "return or inspect the constructed decoding error"]
234 pub(crate) fn deserialize(
235 source: JsonError,
236 raw_input_bytes: usize,
237 normalized_input_bytes: Option<usize>,
238 diagnostic_policy: DiagnosticPolicy,
239 ) -> Self {
240 let line = source.line();
241 let column = source.column();
242 let source =
243 (diagnostic_policy == DiagnosticPolicy::Detailed).then(|| Arc::new(source) as Arc<dyn Error + Send + Sync>);
244 Self {
245 diagnostic_policy,
246 failure: JsonDecodeErrorSource::Deserialize {
247 raw_input_bytes,
248 normalized_input_bytes,
249 line,
250 column,
251 source,
252 },
253 }
254 }
255
256 #[must_use]
262 #[inline(always)]
263 pub const fn kind(&self) -> JsonDecodeErrorKind {
264 match self.failure {
265 JsonDecodeErrorSource::Read { .. } => JsonDecodeErrorKind::Read,
266 JsonDecodeErrorSource::Budget { .. } => JsonDecodeErrorKind::Budget,
267 JsonDecodeErrorSource::EmptyInput { .. } => JsonDecodeErrorKind::EmptyInput,
268 JsonDecodeErrorSource::InvalidUtf8 { .. } => JsonDecodeErrorKind::InvalidUtf8,
269 JsonDecodeErrorSource::InvalidJson { .. } => JsonDecodeErrorKind::InvalidJson,
270 JsonDecodeErrorSource::UnexpectedTopLevel { .. } => JsonDecodeErrorKind::UnexpectedTopLevel,
271 JsonDecodeErrorSource::Deserialize { .. } => JsonDecodeErrorKind::Deserialize,
272 }
273 }
274
275 #[must_use]
281 #[inline(always)]
282 pub const fn stage(&self) -> JsonDecodeStage {
283 match self.failure {
284 JsonDecodeErrorSource::Budget { stage, .. } | JsonDecodeErrorSource::EmptyInput { stage, .. } => stage,
285 JsonDecodeErrorSource::Read { .. } => JsonDecodeStage::Input,
286 JsonDecodeErrorSource::InvalidUtf8 { .. } => JsonDecodeStage::DecodeText,
287 JsonDecodeErrorSource::InvalidJson { .. } => JsonDecodeStage::Parse,
288 JsonDecodeErrorSource::UnexpectedTopLevel { .. } => JsonDecodeStage::TopLevelCheck,
289 JsonDecodeErrorSource::Deserialize { .. } => JsonDecodeStage::Deserialize,
290 }
291 }
292
293 #[must_use]
299 #[inline(always)]
300 pub const fn diagnostic_policy(&self) -> DiagnosticPolicy {
301 self.diagnostic_policy
302 }
303
304 #[must_use]
310 #[inline(always)]
311 pub const fn raw_input_bytes(&self) -> usize {
312 match self.failure {
313 JsonDecodeErrorSource::Read { raw_input_bytes, .. }
314 | JsonDecodeErrorSource::Budget { raw_input_bytes, .. }
315 | JsonDecodeErrorSource::EmptyInput { raw_input_bytes, .. }
316 | JsonDecodeErrorSource::InvalidUtf8 { raw_input_bytes, .. }
317 | JsonDecodeErrorSource::InvalidJson { raw_input_bytes, .. }
318 | JsonDecodeErrorSource::UnexpectedTopLevel { raw_input_bytes, .. }
319 | JsonDecodeErrorSource::Deserialize { raw_input_bytes, .. } => raw_input_bytes,
320 }
321 }
322
323 #[must_use]
330 #[inline(always)]
331 pub const fn normalized_input_bytes(&self) -> Option<usize> {
332 match self.failure {
333 JsonDecodeErrorSource::Budget {
334 normalized_input_bytes, ..
335 }
336 | JsonDecodeErrorSource::EmptyInput {
337 normalized_input_bytes, ..
338 }
339 | JsonDecodeErrorSource::InvalidJson {
340 normalized_input_bytes, ..
341 }
342 | JsonDecodeErrorSource::UnexpectedTopLevel {
343 normalized_input_bytes, ..
344 }
345 | JsonDecodeErrorSource::Deserialize {
346 normalized_input_bytes, ..
347 } => normalized_input_bytes,
348 JsonDecodeErrorSource::InvalidUtf8 { .. } | JsonDecodeErrorSource::Read { .. } => None,
349 }
350 }
351
352 #[must_use]
354 #[inline(always)]
355 pub const fn read_error_kind(&self) -> Option<io::ErrorKind> {
356 match self.failure {
357 JsonDecodeErrorSource::Read { kind, .. } => Some(kind),
358 _ => None,
359 }
360 }
361
362 #[must_use]
369 #[inline(always)]
370 pub fn read_error(&self) -> Option<&io::Error> {
371 match &self.failure {
372 JsonDecodeErrorSource::Read {
373 source: Some(source), ..
374 } => Some(source.as_ref()),
375 _ => None,
376 }
377 }
378
379 #[must_use]
385 #[inline(always)]
386 pub const fn line(&self) -> Option<usize> {
387 match &self.failure {
388 JsonDecodeErrorSource::InvalidJson { syntax, .. } => Some(syntax.line()),
389 JsonDecodeErrorSource::Deserialize { line, .. } if *line > 0 => Some(*line),
390 _ => None,
391 }
392 }
393
394 #[must_use]
400 #[inline(always)]
401 pub const fn column(&self) -> Option<usize> {
402 match &self.failure {
403 JsonDecodeErrorSource::InvalidJson { syntax, .. } => Some(syntax.column()),
404 JsonDecodeErrorSource::Deserialize { column, .. } if *column > 0 => Some(*column),
405 _ => None,
406 }
407 }
408
409 #[must_use]
415 #[inline(always)]
416 pub const fn syntax_error(&self) -> Option<&JsonSyntaxError> {
417 match &self.failure {
418 JsonDecodeErrorSource::InvalidJson { syntax, .. } => Some(syntax),
419 _ => None,
420 }
421 }
422
423 #[must_use]
430 #[inline(always)]
431 pub const fn budget_error(&self) -> Option<&MeasuredBudgetError<R, Q>> {
432 match &self.failure {
433 JsonDecodeErrorSource::Budget { source, .. } => Some(source),
434 _ => None,
435 }
436 }
437
438 #[must_use]
445 #[inline(always)]
446 pub const fn utf8_valid_up_to(&self) -> Option<usize> {
447 match self.failure {
448 JsonDecodeErrorSource::InvalidUtf8 { valid_up_to, .. } => Some(valid_up_to),
449 _ => None,
450 }
451 }
452
453 #[must_use]
460 #[inline(always)]
461 pub const fn utf8_error_len(&self) -> Option<usize> {
462 match self.failure {
463 JsonDecodeErrorSource::InvalidUtf8 { error_len, .. } => error_len,
464 _ => None,
465 }
466 }
467
468 #[must_use]
475 #[inline(always)]
476 pub const fn expected_top_level(&self) -> Option<JsonRootKind> {
477 match self.failure {
478 JsonDecodeErrorSource::UnexpectedTopLevel { expected, .. } => Some(expected),
479 _ => None,
480 }
481 }
482
483 #[must_use]
489 #[inline(always)]
490 pub const fn actual_top_level(&self) -> Option<JsonRootKind> {
491 match self.failure {
492 JsonDecodeErrorSource::UnexpectedTopLevel { actual, .. } => Some(actual),
493 _ => None,
494 }
495 }
496
497 #[inline(always)]
513 pub fn into_source(self) -> JsonDecodeErrorSource<R, Q> {
514 self.failure
515 }
516}
517
518impl<R, Q> fmt::Debug for JsonDecodeError<R, Q>
519where
520 R: fmt::Debug,
521 Q: Copy + fmt::Debug,
522{
523 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
525 formatter
526 .debug_struct("JsonDecodeError")
527 .field("diagnostic_policy", &self.diagnostic_policy)
528 .field("failure", &self.failure)
529 .finish()
530 }
531}
532
533impl<R, Q> fmt::Display for JsonDecodeError<R, Q>
534where
535 R: fmt::Debug,
536 Q: ResourceQuantity,
537{
538 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
540 match &self.failure {
541 JsonDecodeErrorSource::Read { kind, .. } => write!(formatter, "JSON input read failed ({kind:?})"),
542 JsonDecodeErrorSource::Budget { source, .. } => {
543 write!(formatter, "JSON resource budget rejected input: {source}")
544 }
545 JsonDecodeErrorSource::EmptyInput { .. } => formatter.write_str("JSON input is empty after normalization"),
546 JsonDecodeErrorSource::InvalidUtf8 { source, .. } => match source {
547 Some(source) => write!(formatter, "Failed to decode JSON input as UTF-8: {source}"),
548 None => formatter.write_str("Failed to decode JSON input as UTF-8"),
549 },
550 JsonDecodeErrorSource::InvalidJson { syntax, source, .. } => match source {
551 Some(source) => {
552 write!(formatter, "Failed to parse JSON: {source}")
553 }
554 None => write!(formatter, "Failed to parse JSON: {syntax}"),
555 },
556 JsonDecodeErrorSource::UnexpectedTopLevel { expected, actual, .. } => {
557 write!(
558 formatter,
559 "Unexpected JSON top-level type: expected {expected}, got {actual}"
560 )
561 }
562 JsonDecodeErrorSource::Deserialize {
563 normalized_input_bytes,
564 line,
565 column,
566 source,
567 ..
568 } => match source {
569 Some(source) => write!(formatter, "Failed to deserialize JSON value: {source}"),
570 None if normalized_input_bytes.is_some() => write!(
571 formatter,
572 "Failed to deserialize JSON value at normalized line {line} column {column}"
573 ),
574 None => write!(
575 formatter,
576 "Failed to deserialize JSON value at line {line} column {column}"
577 ),
578 },
579 }
580 }
581}
582
583impl<R, Q> Error for JsonDecodeError<R, Q>
584where
585 R: fmt::Debug + 'static,
586 Q: ResourceQuantity + 'static,
587{
588 fn source(&self) -> Option<&(dyn Error + 'static)> {
591 match &self.failure {
592 JsonDecodeErrorSource::Read {
593 source: Some(source), ..
594 } => Some(source.as_ref()),
595 JsonDecodeErrorSource::Budget { source, .. } => Some(source),
596 JsonDecodeErrorSource::InvalidUtf8 {
597 source: Some(source), ..
598 } => Some(source),
599 JsonDecodeErrorSource::InvalidJson {
600 source: Some(source), ..
601 } => Some(source.as_ref()),
602 JsonDecodeErrorSource::Deserialize {
603 source: Some(source), ..
604 } => Some(source.as_ref()),
605 _ => None,
606 }
607 }
608}