Skip to main content

qubit_value/value_wire/
value_wire_decode_error.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8
9//! Error reported while decoding bounded JSON wire input.
10
11use std::io;
12use std::sync::Arc;
13
14use qubit_budget::BudgetError;
15use qubit_budget::MeasuredBudgetError;
16use qubit_budget::QuantityConversionError;
17use qubit_budget::json::JsonResource;
18use qubit_json::decode::JsonDecodeError;
19use qubit_json::decode::JsonDecodeErrorSource;
20use qubit_json::decode::JsonSyntaxError;
21use serde_json::Error as JsonError;
22use serde_json::error::Category;
23use thiserror::Error;
24
25use super::value_wire_json_error::ValueWireJsonError;
26
27/// Error produced by a bounded [`crate::ValueWireV1`] JSON decoder.
28///
29/// # Examples
30///
31/// ```
32/// use qubit_value::{ValueWireDecodeError, ValueWireV1};
33///
34/// let error = ValueWireV1::decode_json_slice(b"{").unwrap_err();
35/// assert!(matches!(
36///     error,
37///     ValueWireDecodeError::Syntax(_) | ValueWireDecodeError::InvalidJson(_)
38/// ));
39/// ```
40#[non_exhaustive]
41#[must_use]
42#[derive(Debug, Error)]
43pub enum ValueWireDecodeError {
44    /// Reading the JSON wire input failed.
45    #[error("failed to read V1 JSON wire input: {kind:?}")]
46    Read {
47        /// Stable standard I/O failure category.
48        kind: io::ErrorKind,
49        /// Reference-counted original error retained under detailed diagnostics
50        /// (`Some`), or absent under privacy-safe diagnostics (`None`).
51        #[source]
52        source: Option<Arc<io::Error>>,
53    },
54
55    /// The JSON document exceeded one configured resource budget.
56    #[error("V1 JSON wire resource budget exceeded: {0}")]
57    Budget(
58        /// Budget violation reported by the bounded JSON decoder.
59        #[source]
60        BudgetError<JsonResource, usize>,
61    ),
62
63    /// A native JSON measurement could not be represented by the budget
64    /// quantity type.
65    #[error("V1 JSON wire resource quantity conversion failed for {resource:?}: {source}")]
66    Quantity {
67        /// Resource whose measurement failed.
68        resource: JsonResource,
69        /// Native measurement conversion failure.
70        #[source]
71        source: QuantityConversionError,
72    },
73
74    /// The bounded input contains JSON syntax errors with source location.
75    #[error("invalid V1 JSON wire syntax: {0}")]
76    Syntax(
77        /// Syntax error with its source location preserved.
78        #[source]
79        JsonSyntaxError,
80    ),
81
82    /// The envelope declares a wire version that this decoder does not support.
83    #[error("unsupported qubit-value wire version {actual}; expected {expected}")]
84    UnsupportedVersion {
85        /// Wire version accepted by this decoder.
86        expected: u8,
87
88        /// Wire version declared by the input envelope.
89        actual: u8,
90    },
91
92    /// The bounded input is not a valid V1 JSON wire value.
93    #[error("failed to decode V1 JSON wire input: {0}")]
94    InvalidJson(
95        /// Safe Serde JSON metadata without the original detailed error.
96        #[source]
97        ValueWireJsonError,
98    ),
99}
100
101impl ValueWireDecodeError {
102    /// Constructs privacy-safe Serde metadata from structured decode facts.
103    ///
104    /// # Parameters
105    ///
106    /// * `category` - Serde JSON failure category.
107    /// * `line` - One-based input line, or zero when unavailable.
108    /// * `column` - One-based input column, or zero when unavailable.
109    ///
110    /// # Returns
111    ///
112    /// An invalid-JSON error that retains diagnostics without input contents.
113    #[inline]
114    fn deserialize(category: Category, line: usize, column: usize) -> Self {
115        Self::InvalidJson(ValueWireJsonError::new(category, line, column))
116    }
117}
118
119impl From<JsonDecodeError<JsonResource, usize>> for ValueWireDecodeError {
120    /// Converts bounded decoder failures into the corresponding wire error.
121    ///
122    /// Decoder resource violations retain their structured details, syntax
123    /// failures retain their source location, and other malformed inputs are
124    /// represented by a privacy-safe deserialization error.
125    ///
126    /// # Parameters
127    ///
128    /// * `error` - Failure returned by the bounded JSON decoder.
129    ///
130    /// # Returns
131    ///
132    /// The corresponding value-wire decoding error.
133    #[inline]
134    fn from(error: JsonDecodeError<JsonResource>) -> Self {
135        let line = error.line().unwrap_or(0);
136        let column = error.column().unwrap_or(0);
137        match error.into_source() {
138            JsonDecodeErrorSource::Read { kind, source, .. } => Self::Read { kind, source },
139            JsonDecodeErrorSource::Budget { source, .. } => match source {
140                MeasuredBudgetError::Budget(error) => Self::Budget(error),
141                MeasuredBudgetError::Quantity { resource, source } => {
142                    Self::Quantity { resource, source }
143                }
144            },
145            JsonDecodeErrorSource::InvalidJson { syntax, .. } => Self::Syntax(syntax),
146            JsonDecodeErrorSource::EmptyInput { .. }
147            | JsonDecodeErrorSource::InvalidUtf8 { .. }
148            | JsonDecodeErrorSource::UnexpectedTopLevel { .. }
149            | JsonDecodeErrorSource::Deserialize { .. } => {
150                Self::deserialize(Category::Data, line, column)
151            }
152        }
153    }
154}
155
156impl From<JsonError> for ValueWireDecodeError {
157    /// Converts a Serde JSON error while preserving its category and location.
158    ///
159    /// The formatted diagnostic does not retain the input contents.
160    ///
161    /// # Parameters
162    ///
163    /// * `error` - Serde JSON decoding failure.
164    ///
165    /// # Returns
166    ///
167    /// An invalid-JSON wire error with a privacy-safe diagnostic.
168    #[inline]
169    fn from(error: JsonError) -> Self {
170        let category = error.classify();
171        let line = error.line();
172        let column = error.column();
173        Self::deserialize(category, line, column)
174    }
175}