Skip to main content

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}