qubit_json/value/json_value_encode_error.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//! Errors from budgeted JSON value projection.
9
10use std::fmt;
11
12use qubit_budget::MeasuredBudgetError;
13use qubit_budget::ResourceQuantity;
14
15use crate::encode::JsonCollectionKind;
16use crate::encode::JsonIntegerSignedness;
17use crate::encode::JsonMapKeyKind;
18use crate::encode::JsonSerializationError;
19use crate::encode::JsonSerializationErrorCategory;
20use crate::encode::JsonSerializationErrorKind;
21use crate::encode::JsonSerializerStateError;
22
23/// Failure while projecting a Serde value into a budgeted JSON tree.
24///
25/// The error distinguishes resource budget rejection, strict JSON
26/// serialization failure, and private text payloads that exceed their
27/// configured limit. Because this type is non-exhaustive, matches outside this
28/// crate must include a fallback arm. After any failure, discard the entire
29/// caller-owned resource transaction; do not treat its partial measurements as
30/// a successful projection.
31///
32/// `None` from a serialization query does not indicate success. It means that
33/// the error is not a serialization failure.
34///
35/// # Type Parameters
36///
37/// * `R` is the resource identifier type used by the budget.
38/// * `Q` is the resource quantity type and must implement [`ResourceQuantity`].
39///
40/// # Examples
41///
42/// ```
43/// use qubit_budget::json::JsonResource;
44/// use qubit_budget::json::JsonValueBudget;
45/// use qubit_budget::json::JsonValueLimits;
46/// use qubit_json::value::JsonValueEncodeError;
47/// use qubit_json::value::JsonValueEncoder;
48/// use serde_json::value::RawValue;
49///
50/// let limits = JsonValueLimits::<JsonResource, usize>::new();
51/// let mut budget = JsonValueBudget::new(limits);
52/// let mut transaction = budget.transaction();
53/// let raw = RawValue::from_string("null".to_owned()).unwrap();
54/// let error = JsonValueEncoder::new()
55/// .encode_in_transaction_with_private_text_limit(&raw, &mut transaction, 0)
56/// .unwrap_err();
57/// let classification = match &error {
58/// JsonValueEncodeError::Budget(_) => "budget",
59/// JsonValueEncodeError::Serialization(_) => "serialization",
60/// JsonValueEncodeError::PrivateTextLimitExceeded { maximum } => {
61/// assert_eq!(*maximum, 0);
62/// "private text limit"
63/// }
64/// _ => "other",
65/// };
66/// assert_eq!(classification, "private text limit");
67/// assert!(error.serialization_error().is_none());
68/// assert!(error.kind().is_none());
69/// assert!(error.category().is_none());
70/// drop(transaction);
71/// ```
72#[non_exhaustive]
73#[derive(Debug)]
74pub enum JsonValueEncodeError<R, Q>
75where
76 Q: ResourceQuantity,
77{
78 /// The caller's resource transaction rejected a value.
79 Budget(MeasuredBudgetError<R, Q>),
80 /// The Serde value could not be represented as strict JSON.
81 Serialization(JsonSerializationError),
82 /// A private RawValue or Number payload exceeded its configured UTF-8 text
83 /// limit.
84 PrivateTextLimitExceeded {
85 /// Maximum UTF-8 byte length allowed for one private text payload.
86 maximum: usize,
87 },
88}
89
90impl<R, Q> JsonValueEncodeError<R, Q>
91where
92 Q: ResourceQuantity,
93{
94 /// Returns the underlying serialization error when Serde projection failed.
95 ///
96 /// # Returns
97 ///
98 /// Returns `Some` for [`Self::Serialization`]. Returns `None` for
99 /// [`Self::Budget`] and [`Self::PrivateTextLimitExceeded`]; `None` means
100 /// the failure is not a serialization error, not that projection
101 /// succeeded.
102 pub const fn serialization_error(&self) -> Option<&JsonSerializationError> {
103 match self {
104 Self::Serialization(error) => Some(error),
105 Self::Budget(_) => None,
106 Self::PrivateTextLimitExceeded { .. } => None,
107 }
108 }
109
110 /// Returns the stable serialization failure kind, if applicable.
111 ///
112 /// # Returns
113 ///
114 /// Returns `Some` for [`Self::Serialization`]. Returns `None` for
115 /// [`Self::Budget`] and [`Self::PrivateTextLimitExceeded`]; `None` means
116 /// the failure has no serialization kind, not that projection succeeded.
117 pub const fn kind(&self) -> Option<JsonSerializationErrorKind> {
118 match self {
119 Self::Serialization(error) => Some(error.kind()),
120 Self::Budget(_) => None,
121 Self::PrivateTextLimitExceeded { .. } => None,
122 }
123 }
124
125 /// Returns the broad serialization category, if applicable.
126 ///
127 /// # Returns
128 ///
129 /// Returns `Some` for [`Self::Serialization`]. Returns `None` for
130 /// [`Self::Budget`] and [`Self::PrivateTextLimitExceeded`]; `None` means
131 /// the failure has no serialization category, not that projection
132 /// succeeded.
133 pub const fn category(&self) -> Option<JsonSerializationErrorCategory> {
134 match self {
135 Self::Serialization(error) => Some(error.category()),
136 Self::Budget(_) => None,
137 Self::PrivateTextLimitExceeded { .. } => None,
138 }
139 }
140
141 /// Reports whether the serialization failure concerns a JSON number.
142 pub const fn is_number_error(&self) -> bool {
143 matches!(self, Self::Serialization(error) if error.is_number_error())
144 }
145
146 /// Reports whether the serialization failure concerns an object key.
147 pub const fn is_map_key_error(&self) -> bool {
148 matches!(self, Self::Serialization(error) if error.is_map_key_error())
149 }
150
151 /// Reports whether the serialization failure concerns a raw JSON value.
152 pub const fn is_raw_value_error(&self) -> bool {
153 matches!(self, Self::Serialization(error) if error.is_raw_value_error())
154 }
155
156 /// Reports whether Serde violated a serializer protocol contract.
157 pub const fn is_serializer_contract_error(&self) -> bool {
158 matches!(self, Self::Serialization(error) if error.is_serializer_contract_error())
159 }
160
161 /// Returns the rejected integer signedness, if applicable.
162 pub const fn integer_signedness(&self) -> Option<JsonIntegerSignedness> {
163 match self {
164 Self::Serialization(error) => error.integer_signedness(),
165 Self::Budget(_) => None,
166 Self::PrivateTextLimitExceeded { .. } => None,
167 }
168 }
169
170 /// Returns the unsupported map-key category, if applicable.
171 pub const fn map_key_kind(&self) -> Option<JsonMapKeyKind> {
172 match self {
173 Self::Serialization(error) => error.map_key_kind(),
174 Self::Budget(_) => None,
175 Self::PrivateTextLimitExceeded { .. } => None,
176 }
177 }
178
179 /// Returns the collection whose length overflowed, if applicable.
180 pub const fn collection_kind(&self) -> Option<JsonCollectionKind> {
181 match self {
182 Self::Serialization(error) => error.collection_kind(),
183 Self::Budget(_) => None,
184 Self::PrivateTextLimitExceeded { .. } => None,
185 }
186 }
187
188 /// Returns the serializer protocol state failure, if applicable.
189 pub const fn serializer_state_error(&self) -> Option<JsonSerializerStateError> {
190 match self {
191 Self::Serialization(error) => error.serializer_state_error(),
192 Self::Budget(_) => None,
193 Self::PrivateTextLimitExceeded { .. } => None,
194 }
195 }
196}
197
198impl<R, Q> fmt::Display for JsonValueEncodeError<R, Q>
199where
200 Q: ResourceQuantity,
201{
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 match self {
204 Self::Budget(_) => f.write_str("JSON value budget rejected serialization"),
205 Self::Serialization(error) => write!(f, "{error}"),
206 Self::PrivateTextLimitExceeded { maximum } => {
207 write!(f, "private JSON text exceeded the {maximum}-byte limit")
208 }
209 }
210 }
211}
212
213impl<R, Q> std::error::Error for JsonValueEncodeError<R, Q>
214where
215 R: fmt::Debug + 'static,
216 Q: ResourceQuantity + 'static,
217{
218 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
219 match self {
220 Self::Budget(error) => Some(error),
221 Self::Serialization(error) => Some(error),
222 Self::PrivateTextLimitExceeded { .. } => None,
223 }
224 }
225}