qubit_json/decode/json_decode_error_source.rs
1// =============================================================================
2// Copyright (c) 2026 Haixing Hu.
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Defines the owned sources retained by JSON decoding errors.
9
10use std::error::Error;
11use std::fmt;
12use std::sync::Arc;
13
14use qubit_budget::MeasuredBudgetError;
15
16use super::JsonDecodeStage;
17use super::JsonRootKind;
18use super::JsonSyntaxError;
19
20/// An owned semantic source extracted from a JSON decoding failure.
21///
22/// This enum preserves the complete structured state of exactly one failure.
23/// It lets downstream adapters move error data into their own models with one
24/// exhaustive `match`, without pairing [`super::JsonDecodeError::kind`] with
25/// fallible source accessors. Parser and Serde sources remain present only
26/// when decoding used [`super::DiagnosticPolicy::Detailed`].
27///
28/// # Type Parameters
29///
30/// * `R` - Resource identity attached to budget failures.
31/// * `Q` - Quantity representation attached to budget failures.
32///
33/// # Examples
34///
35/// ```
36/// use qubit_json::decode::{JsonDecodeErrorSource, JsonDecoder};
37///
38/// let mut decoder = JsonDecoder::unlimited();
39/// let error = decoder.validate_str("{").expect_err("invalid JSON must fail");
40/// match error.into_source() {
41/// JsonDecodeErrorSource::InvalidJson { syntax, .. } => {
42/// assert_eq!(syntax.offset(), 1);
43/// }
44/// source => panic!("unexpected decoding source: {source:?}"),
45/// }
46/// ```
47#[must_use]
48#[derive(Debug, Clone)]
49pub enum JsonDecodeErrorSource<R, Q = usize>
50where
51 Q: Copy + fmt::Debug,
52{
53 /// A resource measurement was rejected.
54 Budget {
55 /// Semantic stage where the measurement was rejected.
56 stage: JsonDecodeStage,
57 /// Original input length.
58 raw_input_bytes: usize,
59 /// Normalized length when normalization completed.
60 normalized_input_bytes: Option<usize>,
61 /// Complete measured-budget failure.
62 source: MeasuredBudgetError<R, Q>,
63 },
64 /// Input was empty at a public boundary.
65 EmptyInput {
66 /// Semantic stage where emptiness was detected.
67 stage: JsonDecodeStage,
68 /// Original input length.
69 raw_input_bytes: usize,
70 /// Normalized length when available.
71 normalized_input_bytes: Option<usize>,
72 },
73 /// Raw bytes were not valid UTF-8.
74 InvalidUtf8 {
75 /// Original input length.
76 raw_input_bytes: usize,
77 /// Valid prefix length reported by UTF-8 validation.
78 valid_up_to: usize,
79 /// Invalid sequence length when known.
80 error_len: Option<usize>,
81 /// Detailed source retained only under detailed diagnostics.
82 source: Option<std::str::Utf8Error>,
83 },
84 /// Text was not one valid JSON document.
85 InvalidJson {
86 /// Original input length.
87 raw_input_bytes: usize,
88 /// Normalized length when normalization completed.
89 normalized_input_bytes: Option<usize>,
90 /// Stable scanner reason and source coordinates.
91 syntax: JsonSyntaxError,
92 /// Detailed parser source retained only under detailed diagnostics.
93 source: Option<Arc<dyn Error + Send + Sync>>,
94 },
95 /// A valid document had an unexpected top-level kind.
96 UnexpectedTopLevel {
97 /// Original input length.
98 raw_input_bytes: usize,
99 /// Normalized length when normalization completed.
100 normalized_input_bytes: Option<usize>,
101 /// Required top-level kind.
102 expected: JsonRootKind,
103 /// Observed top-level kind.
104 actual: JsonRootKind,
105 },
106 /// A valid admitted document could not materialize the target type.
107 Deserialize {
108 /// Original input length.
109 raw_input_bytes: usize,
110 /// Normalized length when normalization completed.
111 normalized_input_bytes: Option<usize>,
112 /// One-based Serde line, or zero when unavailable.
113 line: usize,
114 /// One-based Serde column, or zero when unavailable.
115 column: usize,
116 /// Detailed source retained only under detailed diagnostics.
117 source: Option<Arc<dyn Error + Send + Sync>>,
118 },
119}