qubit_metadata/metadata_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//! Errors from bounded JSON metadata wire encoding.
9
10use std::fmt;
11
12use qubit_budget::BudgetError;
13use qubit_budget::MeasuredBudgetError;
14use qubit_budget::QuantityConversionError;
15use qubit_budget::json::JsonResource;
16use qubit_json::decode::JsonSyntaxError;
17use qubit_json::encode::JsonEncodeError;
18use qubit_json::encode::JsonEncodeErrorSource;
19use qubit_json::encode::JsonSerializationError;
20
21use crate::MetadataError;
22
23/// Failure returned by bounded metadata JSON encoding APIs.
24///
25/// # Examples
26///
27/// ```
28/// use qubit_metadata::MetadataWireEncodeError;
29///
30/// let error = MetadataWireEncodeError::Io(std::io::Error::other("closed"));
31/// assert!(error.to_string().contains("closed"));
32/// ```
33#[derive(Debug)]
34#[non_exhaustive]
35#[must_use]
36pub enum MetadataWireEncodeError {
37 /// The JSON document exceeded one configured resource budget.
38 Budget(
39 /// Shared JSON resource budget that was exceeded.
40 BudgetError<JsonResource, usize>,
41 ),
42 /// A native JSON measurement could not be represented by the budget
43 /// quantity type.
44 Quantity {
45 /// Resource whose measurement failed to convert.
46 resource: JsonResource,
47 /// Native measurement conversion failure.
48 source: QuantityConversionError,
49 },
50 /// The metadata value exceeds the strict V1 metadata wire contract.
51 ///
52 /// The original domain error is preserved as the source.
53 Domain(
54 /// Structured metadata-domain failure.
55 MetadataError,
56 ),
57 /// The encoded value contains invalid JSON syntax.
58 Syntax(
59 /// Lexical JSON syntax failure.
60 JsonSyntaxError,
61 ),
62 /// Strict JSON serialization rejected the value during bounded encoding.
63 Json(
64 /// JSON serialization failure.
65 JsonSerializationError,
66 ),
67 /// The destination writer rejected bounded JSON output.
68 Io(
69 /// Destination writer error.
70 std::io::Error,
71 ),
72}
73
74impl fmt::Display for MetadataWireEncodeError {
75 /// Formats the encoding failure with its underlying error context.
76 ///
77 /// # Parameters
78 ///
79 /// * `formatter` - Formatter receiving the error message.
80 ///
81 /// # Returns
82 ///
83 /// A formatting result indicating whether the message was written.
84 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85 match self {
86 Self::Budget(error) => {
87 write!(formatter, "JSON resource budget exceeded: {error}")
88 }
89 Self::Quantity { resource, source } => write!(
90 formatter,
91 "JSON resource quantity conversion failed for {resource:?}: {source}",
92 ),
93 Self::Domain(error) => {
94 write!(formatter, "metadata wire contract rejected: {error}")
95 }
96 Self::Syntax(error) => {
97 write!(formatter, "invalid JSON wire syntax: {error}")
98 }
99 Self::Json(error) => {
100 write!(formatter, "failed to encode JSON wire value: {error}")
101 }
102 Self::Io(error) => {
103 write!(formatter, "failed to write JSON wire value: {error}")
104 }
105 }
106 }
107}
108
109impl std::error::Error for MetadataWireEncodeError {
110 /// Returns the underlying budget, domain, syntax, JSON, or I/O error.
111 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
112 match self {
113 Self::Budget(error) => Some(error),
114 Self::Quantity { source, .. } => Some(source),
115 Self::Domain(error) => Some(error),
116 Self::Syntax(error) => Some(error),
117 Self::Json(error) => Some(error),
118 Self::Io(error) => Some(error),
119 }
120 }
121}
122
123impl From<JsonEncodeError<JsonResource>> for MetadataWireEncodeError {
124 /// Converts a shared budget adapter error into the metadata encoding
125 /// error.
126 ///
127 /// # Parameters
128 ///
129 /// * `error` - Shared JSON encoding failure to convert.
130 ///
131 /// # Returns
132 ///
133 /// The corresponding metadata wire encoding error.
134 fn from(error: JsonEncodeError<JsonResource>) -> Self {
135 match error.into_source() {
136 JsonEncodeErrorSource::Budget(source) => match source {
137 MeasuredBudgetError::Budget(error) => Self::Budget(error),
138 MeasuredBudgetError::Quantity { resource, source } => Self::Quantity { resource, source },
139 },
140 JsonEncodeErrorSource::InvalidRawJson(source) => Self::Syntax(source),
141 JsonEncodeErrorSource::Serialize(source) => Self::Json(source),
142 JsonEncodeErrorSource::Write(source) => Self::Io(source),
143 }
144 }
145}
146
147impl From<MeasuredBudgetError<JsonResource, usize>> for MetadataWireEncodeError {
148 /// Converts a measured JSON budget failure into the metadata encoding
149 /// error.
150 ///
151 /// # Parameters
152 ///
153 /// * `error` - Measured budget failure to convert.
154 ///
155 /// # Returns
156 ///
157 /// The corresponding metadata wire encoding error.
158 fn from(error: MeasuredBudgetError<JsonResource, usize>) -> Self {
159 match error {
160 MeasuredBudgetError::Budget(error) => Self::Budget(error),
161 MeasuredBudgetError::Quantity { resource, source } => Self::Quantity { resource, source },
162 }
163 }
164}