qubit_value/value_wire/value_wire_v1.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//! Public DTO for the stable version-one JSON wire contract.
10
11use serde::{
12 Deserialize,
13 Deserializer,
14 Serialize,
15 Serializer,
16};
17
18use crate::{
19 MultiValues,
20 Value,
21 ValueContainer,
22};
23
24use super::{
25 VALUE_WIRE_V1_VERSION,
26 ValueWireEncodeError,
27 ValueWirePayloadV1,
28 deserialize_wire,
29 serialize_wire,
30};
31#[cfg(feature = "json")]
32use super::{
33 ValueWireDecodeError,
34 WireLimits,
35};
36
37/// Stable version-one wire DTO for a scalar or homogeneous collection.
38///
39/// For a given serializer and value, output is byte-stable with canonical field
40/// and object-key order, including recursively nested JSON objects. V1 is
41/// closed: existing tags, shapes, and payload representations cannot change,
42/// and future runtime data types require a new wire version instead of
43/// extending V1.
44///
45/// # Resource limits
46///
47/// The generic [`Deserialize`](serde::Deserialize) implementation is intended
48/// for already-bounded embedded documents and does not enforce message-size or
49/// structural limits. Use `ValueWireV1::decode_json_slice` or
50/// `ValueWireV1::decode_json_slice_with_limits` for untrusted complete JSON
51/// input.
52#[must_use]
53#[derive(Debug, Clone, PartialEq, Eq, Hash)]
54pub struct ValueWireV1 {
55 /// Explicit runtime shape and typed payload represented by this DTO.
56 value: ValueWirePayloadV1,
57}
58
59impl ValueWireV1 {
60 /// Numeric version emitted and accepted by this DTO.
61 pub const VERSION: u8 = VALUE_WIRE_V1_VERSION;
62
63 /// Creates a V1 DTO from an explicit scalar-or-collection container.
64 ///
65 /// # Parameters
66 ///
67 /// * `value` - Runtime container whose exact type and shape are preserved.
68 ///
69 /// # Returns
70 ///
71 /// A V1 DTO containing `value`.
72 #[inline(always)]
73 pub const fn new(value: ValueWirePayloadV1) -> Self {
74 Self { value }
75 }
76
77 /// Decodes a V1 JSON wire value using the default structural limits.
78 ///
79 /// The complete input length and decoded structure are checked before the
80 /// value is returned. Embedded protocols should share one
81 /// [`crate::WireBudget`] across their complete document.
82 ///
83 /// # Parameters
84 ///
85 /// * `input` - Complete UTF-8 JSON document to decode.
86 ///
87 /// # Returns
88 ///
89 /// The decoded V1 wire DTO.
90 ///
91 /// # Errors
92 ///
93 /// Returns a limit error when the input or decoded structure is too large,
94 /// or [`ValueWireDecodeError::InvalidJson`] for malformed input.
95 #[cfg(feature = "json")]
96 #[inline]
97 pub fn decode_json_slice(
98 input: &[u8],
99 ) -> Result<Self, ValueWireDecodeError> {
100 Self::decode_json_slice_with_limits(input, WireLimits::default())
101 }
102
103 /// Decodes a V1 JSON wire value using explicit structural limits.
104 ///
105 /// The complete input length and decoded structure are checked before the
106 /// value is returned. Embedded values should be checked through the outer
107 /// protocol's shared [`crate::WireBudget`].
108 ///
109 /// # Parameters
110 ///
111 /// * `input` - Complete UTF-8 JSON document to decode.
112 /// * `limits` - Shared encoded-input and structural limits.
113 ///
114 /// # Returns
115 ///
116 /// The decoded V1 wire DTO.
117 ///
118 /// # Errors
119 ///
120 /// Returns a limit error when `input` or its decoded structure exceeds
121 /// `limits`, or [`ValueWireDecodeError::InvalidJson`] for malformed input.
122 #[cfg(feature = "json")]
123 #[inline]
124 pub fn decode_json_slice_with_limits(
125 input: &[u8],
126 limits: WireLimits,
127 ) -> Result<Self, ValueWireDecodeError> {
128 let mut budget = limits.begin(input.len())?;
129 let value: Self = serde_json::from_slice(input)
130 .map_err(ValueWireDecodeError::from)?;
131 budget.check_container(value.container())?;
132 Ok(value)
133 }
134
135 /// Returns the runtime container represented by this DTO.
136 ///
137 /// # Returns
138 ///
139 /// A shared reference to the preserved runtime container.
140 #[inline(always)]
141 pub const fn container(&self) -> &ValueContainer {
142 self.value.container()
143 }
144
145 /// Consumes the DTO and returns its runtime container.
146 ///
147 /// # Returns
148 ///
149 /// The preserved runtime container.
150 #[inline(always)]
151 pub fn into_container(self) -> ValueContainer {
152 self.value.into_container()
153 }
154}
155
156impl TryFrom<Value> for ValueWireV1 {
157 type Error = ValueWireEncodeError;
158 /// Wraps a runtime scalar in a V1 DTO.
159 #[inline(always)]
160 fn try_from(value: Value) -> Result<Self, Self::Error> {
161 ValueWirePayloadV1::try_from(value).map(Self::new)
162 }
163}
164
165impl TryFrom<MultiValues> for ValueWireV1 {
166 type Error = ValueWireEncodeError;
167 /// Wraps a runtime collection in a V1 DTO.
168 #[inline(always)]
169 fn try_from(values: MultiValues) -> Result<Self, Self::Error> {
170 ValueWirePayloadV1::try_from(values).map(Self::new)
171 }
172}
173
174impl TryFrom<ValueContainer> for ValueWireV1 {
175 type Error = ValueWireEncodeError;
176 /// Wraps an explicit runtime shape in a V1 DTO.
177 #[inline(always)]
178 fn try_from(value: ValueContainer) -> Result<Self, Self::Error> {
179 ValueWirePayloadV1::try_from(value).map(Self::new)
180 }
181}
182
183impl From<ValueWireV1> for ValueContainer {
184 /// Unwraps the runtime container from a V1 DTO.
185 #[inline(always)]
186 fn from(value: ValueWireV1) -> Self {
187 value.into_container()
188 }
189}
190
191impl Serialize for ValueWireV1 {
192 /// Serializes the contained runtime shape through the V1 contract.
193 #[inline(always)]
194 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
195 where
196 S: Serializer,
197 {
198 serialize_wire(self.value.container().into(), serializer)
199 }
200}
201
202impl<'de> Deserialize<'de> for ValueWireV1 {
203 /// Deserializes a validated V1 runtime container into the DTO.
204 #[inline(always)]
205 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
206 where
207 D: Deserializer<'de>,
208 {
209 deserialize_wire(deserializer)
210 .map(ValueWirePayloadV1::from_decoded)
211 .map(Self::new)
212 }
213}