Skip to main content

qubit_value/value_wire/
value_wire_encode_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//! Errors produced while constructing a V1 wire DTO.
10
11#[cfg(feature = "json")]
12use std::io::Error as IoError;
13
14#[cfg(feature = "json")]
15use qubit_budget::BudgetError;
16#[cfg(feature = "json")]
17use qubit_budget::MeasuredBudgetError;
18#[cfg(feature = "json")]
19use qubit_budget::QuantityConversionError;
20#[cfg(feature = "json")]
21use qubit_budget::json::JsonResource;
22use qubit_datatype::DataType;
23#[cfg(feature = "json")]
24use qubit_json::decode::JsonSyntaxError;
25#[cfg(feature = "json")]
26use qubit_json::encode::JsonEncodeError;
27#[cfg(feature = "json")]
28use qubit_json::encode::JsonEncodeErrorSource;
29#[cfg(feature = "json")]
30use qubit_json::encode::JsonSerializationError;
31use thiserror::Error;
32
33/// A runtime value cannot be represented by the JSON V1 wire contract.
34///
35/// # Examples
36///
37/// ```
38/// use qubit_value::Value;
39/// use qubit_value::ValueWireEncodeError;
40/// use qubit_value::ValueWireV1;
41///
42/// let error = ValueWireV1::try_from(Value::Float64(f64::NAN)).unwrap_err();
43/// assert!(matches!(error, ValueWireEncodeError::NonFiniteFloat { .. }));
44/// ```
45#[non_exhaustive]
46#[must_use]
47#[derive(Debug, Error)]
48pub enum ValueWireEncodeError {
49    /// A JSON number cannot be restored with the V1 number model without
50    /// changing its `serde_json::Number` identity.
51    #[cfg(feature = "json")]
52    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
53    #[error("V1 JSON wire cannot preserve this JSON number representation")]
54    JsonNumberNotRoundTrippable,
55    /// A JSON V1 float must be finite.
56    #[error("V1 JSON wire cannot represent a non-finite {data_type} value")]
57    NonFiniteFloat {
58        /// Runtime data type of the rejected value.
59        data_type: DataType,
60    },
61    /// A V1 decimal exponent must stay within the bounded wire range.
62    #[error("V1 JSON wire cannot represent decimal scale {scale}; maximum absolute scale is {maximum_absolute_scale}")]
63    BigDecimalScaleTooLarge {
64        /// Rejected decimal exponent.
65        scale: i64,
66        /// Inclusive exponent magnitude limit for V1.
67        maximum_absolute_scale: i64,
68    },
69    /// A UTC instant year must be within the four-digit V1 wire range.
70    #[error("V1 JSON wire cannot represent UTC instant year {year}; supported years are 0 through 9999")]
71    InstantYearOutOfRange {
72        /// UTC calendar year rejected by the V1 wire contract.
73        year: i32,
74    },
75    /// The JSON output exceeded one configured resource budget.
76    #[cfg(feature = "json")]
77    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
78    #[error("V1 JSON wire resource budget exceeded: {0}")]
79    Budget(
80        /// Budget violation reported by the bounded JSON encoder.
81        #[source]
82        BudgetError<JsonResource, usize>,
83    ),
84    /// A native JSON measurement could not be represented by the budget
85    /// quantity type.
86    #[cfg(feature = "json")]
87    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
88    #[error("V1 JSON wire resource quantity conversion failed for {resource:?}: {source}")]
89    Quantity {
90        /// Resource whose measurement failed.
91        resource: JsonResource,
92        /// Native measurement conversion failure.
93        #[source]
94        source: QuantityConversionError,
95    },
96    /// The encoded value contains invalid JSON syntax.
97    #[cfg(feature = "json")]
98    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
99    #[error("invalid V1 JSON wire syntax: {0}")]
100    Syntax(
101        /// Syntax error found in an embedded raw JSON payload.
102        #[source]
103        JsonSyntaxError,
104    ),
105    /// Strict JSON serialization rejected the value during bounded encoding.
106    #[cfg(feature = "json")]
107    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
108    #[error("failed to encode V1 JSON wire value: {0}")]
109    Json(
110        /// Stable, privacy-safe JSON serialization failure.
111        #[source]
112        JsonSerializationError,
113    ),
114    /// The destination writer rejected bounded JSON output.
115    #[cfg(feature = "json")]
116    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
117    #[error("failed to write V1 JSON wire value: {0}")]
118    Io(
119        /// Destination-writer failure.
120        #[source]
121        IoError,
122    ),
123}
124
125#[cfg(feature = "json")]
126impl From<JsonEncodeError<JsonResource, usize>> for ValueWireEncodeError {
127    /// Converts bounded JSON encoder failures into wire encoding errors.
128    ///
129    /// Resource-budget failures retain their budget details, invalid raw JSON
130    /// retains its syntax error, and serialization or writer failures retain
131    /// their corresponding source errors.
132    ///
133    /// # Parameters
134    ///
135    /// * `error` - Failure reported by the bounded JSON encoder.
136    ///
137    /// # Returns
138    ///
139    /// The wire encoding error corresponding to the encoder failure.
140    #[inline]
141    fn from(error: JsonEncodeError<JsonResource>) -> Self {
142        match error.into_source() {
143            JsonEncodeErrorSource::Budget(source) => Self::from(source),
144            JsonEncodeErrorSource::InvalidRawJson(source) => Self::Syntax(source),
145            JsonEncodeErrorSource::Serialize(source) => Self::Json(source),
146            JsonEncodeErrorSource::Write(source) => Self::Io(source),
147        }
148    }
149}
150
151#[cfg(feature = "json")]
152impl From<MeasuredBudgetError<JsonResource, usize>> for ValueWireEncodeError {
153    /// Converts a measured budget failure into its corresponding wire error.
154    ///
155    /// Budget violations retain their budget details, while quantity
156    /// conversion failures retain the affected resource and source error.
157    ///
158    /// # Parameters
159    ///
160    /// * `error` - Measured failure reported by the budget layer.
161    ///
162    /// # Returns
163    ///
164    /// The wire encoding error corresponding to the measured failure.
165    #[inline]
166    fn from(error: MeasuredBudgetError<JsonResource, usize>) -> Self {
167        match error {
168            MeasuredBudgetError::Budget(error) => Self::Budget(error),
169            MeasuredBudgetError::Quantity { resource, source } => Self::Quantity { resource, source },
170        }
171    }
172}