Skip to main content

qubit_value/value_wire/
value_wire_ref_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//! Borrowed V1 envelope serialization.
10
11#[cfg(feature = "json")]
12use std::io::Write;
13
14#[cfg(feature = "json")]
15use qubit_budget::json::JsonEncodeLimits;
16#[cfg(feature = "json")]
17use qubit_budget::json::JsonEncodeSession;
18#[cfg(feature = "json")]
19use qubit_json::encode::JsonEncoder;
20use serde::Serialize;
21use serde::Serializer;
22
23use super::ValueWireEncodeError;
24use super::ValueWirePayloadRefV1;
25use super::serialize_wire;
26use crate::MultiValues;
27use crate::Value;
28use crate::ValueContainer;
29
30/// Borrowed standalone V1 envelope for serialization without cloning.
31///
32/// Runtime JSON numbers must round trip to the same `serde_json::Number` under
33/// the strict `qubit-json` number model. A number that would change identity
34/// returns the JSON-gated `JsonNumberNotRoundTrippable` error; the error does
35/// not expose the number text.
36///
37/// # Type Parameters
38///
39/// * `'a` - Lifetime of the runtime payload borrowed for serialization.
40///
41/// # Examples
42///
43/// ```
44/// use qubit_value::{Value, ValueWireRefV1};
45///
46/// let value = Value::from(42_i32);
47/// let wire = ValueWireRefV1::from_value(&value).unwrap();
48/// let encoded = serde_json::to_string(&wire).unwrap();
49/// assert_eq!(encoded, r#"{"version":1,"value":{"scalar":{"int32":42}}}"#);
50/// let owned = qubit_value::ValueWireV1::try_from(Value::from(42_i32)).unwrap();
51/// assert_eq!(encoded, serde_json::to_string(&owned).unwrap());
52/// ```
53#[must_use]
54pub struct ValueWireRefV1<'a> {
55    /// Borrowed V1 payload carried by this versioned envelope.
56    value: ValueWirePayloadRefV1<'a>,
57}
58
59impl<'a> ValueWireRefV1<'a> {
60    /// Borrows a scalar after validating V1's representational constraints.
61    ///
62    /// # Parameters
63    ///
64    /// * `value` - Scalar runtime value to validate and borrow.
65    ///
66    /// # Returns
67    ///
68    /// A standalone borrowed V1 envelope containing the scalar.
69    ///
70    /// # Errors
71    ///
72    /// Returns [`ValueWireEncodeError`] when the scalar violates V1 numeric
73    /// representation constraints, including the supported UTC year range
74    /// for instants.
75    #[inline(always)]
76    pub fn from_value(value: &'a Value) -> Result<Self, ValueWireEncodeError> {
77        ValueWirePayloadRefV1::from_value(value).map(Self::new)
78    }
79    /// Borrows a collection after validating V1's representational constraints.
80    ///
81    /// # Parameters
82    ///
83    /// * `values` - Homogeneous runtime collection to validate and borrow.
84    ///
85    /// # Returns
86    ///
87    /// A standalone borrowed V1 envelope containing the collection.
88    ///
89    /// # Errors
90    ///
91    /// Returns [`ValueWireEncodeError`] when an element violates V1 numeric
92    /// representation constraints, including the supported UTC year range
93    /// for instants.
94    #[inline(always)]
95    pub fn from_values(values: &'a MultiValues) -> Result<Self, ValueWireEncodeError> {
96        ValueWirePayloadRefV1::from_values(values).map(Self::new)
97    }
98    /// Borrows an explicit shape after validating V1's representational
99    /// constraints.
100    ///
101    /// # Parameters
102    ///
103    /// * `value` - Explicit scalar-or-collection value to validate and borrow.
104    ///
105    /// # Returns
106    ///
107    /// A standalone borrowed V1 envelope preserving the original shape.
108    ///
109    /// # Errors
110    ///
111    /// Returns [`ValueWireEncodeError`] when any contained value violates V1
112    /// numeric representation constraints, including the supported UTC year
113    /// range for instants.
114    #[inline(always)]
115    pub fn from_container(value: &'a ValueContainer) -> Result<Self, ValueWireEncodeError> {
116        ValueWirePayloadRefV1::from_container(value).map(Self::new)
117    }
118    /// Wraps an already validated borrowed payload.
119    ///
120    /// # Parameters
121    ///
122    /// * `value` - Validated borrowed payload to place in the V1 envelope.
123    ///
124    /// # Returns
125    ///
126    /// A standalone borrowed V1 envelope.
127    #[inline(always)]
128    pub const fn new(value: ValueWirePayloadRefV1<'a>) -> Self {
129        Self { value }
130    }
131
132    /// Encodes the borrowed V1 envelope into a compact JSON vector.
133    ///
134    /// # Returns
135    ///
136    /// Compact UTF-8 JSON bytes for the complete V1 envelope.
137    ///
138    /// # Errors
139    ///
140    /// Returns [`ValueWireEncodeError`] for resource or serialization failures.
141    #[cfg(feature = "json")]
142    #[inline(always)]
143    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
144    pub fn to_json_vec(&self) -> Result<Vec<u8>, ValueWireEncodeError> {
145        self.to_json_vec_with_limits(super::default_json_encode_limits())
146    }
147
148    /// Encodes the borrowed V1 envelope with explicit JSON resource limits.
149    ///
150    /// # Parameters
151    ///
152    /// * `limits` - Resource limits enforced during JSON encoding.
153    ///
154    /// # Returns
155    ///
156    /// Compact UTF-8 JSON bytes for the complete V1 envelope.
157    ///
158    /// Runs a conservative payload preflight before encoding, then enforces
159    /// `limits` authoritatively while encoding the full envelope. The preflight
160    /// may report a lower-bound budget error and does not bound every
161    /// preparation allocation.
162    ///
163    /// # Errors
164    ///
165    /// Returns [`ValueWireEncodeError`] when preflight or final encoding
166    /// exceeds `limits`, or Serde rejects the envelope.
167    #[cfg(feature = "json")]
168    #[inline]
169    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
170    pub fn to_json_vec_with_limits(
171        &self,
172        limits: JsonEncodeLimits,
173    ) -> Result<Vec<u8>, ValueWireEncodeError> {
174        self.value.check_encode_preflight(limits)?;
175        let session = JsonEncodeSession::from_limits(limits);
176        JsonEncoder::new(session)
177            .to_vec(self)
178            .map_err(ValueWireEncodeError::from)
179    }
180
181    /// Encodes the borrowed V1 envelope to a writer with default limits.
182    ///
183    /// # Type Parameters
184    ///
185    /// * `W` - Destination writer type.
186    ///
187    /// # Parameters
188    ///
189    /// * `writer` - Destination receiving the complete V1 JSON envelope.
190    ///
191    /// # Returns
192    ///
193    /// `Ok(())` after the complete envelope is written.
194    ///
195    /// The complete document is buffered and admitted before this method writes
196    /// its bytes to the destination. Preflight, serialization, or budget
197    /// rejection does not write this document. An I/O error may leave an
198    /// accepted prefix in the writer; do not blindly retry a complete
199    /// document on the same destination.
200    ///
201    /// This method does not explicitly flush the writer. Success means the
202    /// writer accepted all document bytes, not that they were flushed,
203    /// persisted, or received by a remote peer. Pass `&mut writer` to
204    /// retain ownership of a buffered writer, then call `flush()` and
205    /// handle its error separately. Passing an owned writer may invoke that
206    /// writer's drop behavior; errors from an implicit drop-time flush are
207    /// not returned by this method.
208    ///
209    /// # Errors
210    ///
211    /// Returns [`ValueWireEncodeError`] for resource, serialization, or writer
212    /// failures.
213    #[cfg(feature = "json")]
214    #[inline(always)]
215    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
216    pub fn to_json_writer<W>(&self, writer: W) -> Result<(), ValueWireEncodeError>
217    where
218        W: Write,
219    {
220        self.to_json_writer_with_limits(writer, super::default_json_encode_limits())
221    }
222
223    /// Encodes the borrowed V1 envelope to a writer with explicit limits.
224    ///
225    /// # Type Parameters
226    ///
227    /// * `W` - Destination writer type.
228    ///
229    /// # Parameters
230    ///
231    /// * `writer` - Destination receiving the complete V1 JSON envelope.
232    /// * `limits` - Resource limits enforced during JSON encoding.
233    ///
234    /// # Returns
235    ///
236    /// `Ok(())` after the complete envelope is written.
237    ///
238    /// Runs a conservative payload preflight before encoding, then enforces
239    /// `limits` authoritatively while encoding the full envelope. The preflight
240    /// may report a lower-bound budget error and does not bound every
241    /// preparation allocation.
242    ///
243    /// The complete document is buffered and admitted before this method writes
244    /// its bytes to the destination. Preflight, serialization, or budget
245    /// rejection does not write this document. An I/O error may leave an
246    /// accepted prefix in the writer; do not blindly retry a complete
247    /// document on the same destination.
248    ///
249    /// This method does not explicitly flush the writer. Success means the
250    /// writer accepted all document bytes, not that they were flushed,
251    /// persisted, or received by a remote peer. Pass `&mut writer` to
252    /// retain ownership of a buffered writer, then call `flush()` and
253    /// handle its error separately. Passing an owned writer may invoke that
254    /// writer's drop behavior; errors from an implicit drop-time flush are
255    /// not returned by this method.
256    ///
257    /// # Errors
258    ///
259    /// Returns [`ValueWireEncodeError`] when preflight or final encoding
260    /// exceeds `limits`, Serde rejects the envelope, or `writer` rejects
261    /// output.
262    #[cfg(feature = "json")]
263    #[inline]
264    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
265    pub fn to_json_writer_with_limits<W>(
266        &self,
267        mut writer: W,
268        limits: JsonEncodeLimits,
269    ) -> Result<(), ValueWireEncodeError>
270    where
271        W: Write,
272    {
273        self.value.check_encode_preflight(limits)?;
274        let session = JsonEncodeSession::from_limits(limits);
275        JsonEncoder::new(session)
276            .write_buffered(&mut writer, self)
277            .map_err(ValueWireEncodeError::from)
278    }
279}
280
281impl<'a> TryFrom<&'a Value> for ValueWireRefV1<'a> {
282    /// Validation failure when the borrowed input cannot be represented in V1.
283    type Error = ValueWireEncodeError;
284
285    /// Validates and borrows the input without cloning its payload.
286    ///
287    /// # Parameters
288    ///
289    /// * `value` - Scalar runtime value to validate and borrow for `'a`.
290    ///
291    /// # Returns
292    ///
293    /// A borrowed V1 envelope preserving the input shape.
294    ///
295    /// # Errors
296    ///
297    /// Returns the same representational validation errors as
298    /// [`Self::from_value`].
299    #[inline]
300    fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
301        Self::from_value(value)
302    }
303}
304
305impl<'a> TryFrom<&'a MultiValues> for ValueWireRefV1<'a> {
306    /// Validation failure when the borrowed input cannot be represented in V1.
307    type Error = ValueWireEncodeError;
308
309    /// Validates and borrows the input without cloning its payload.
310    ///
311    /// # Parameters
312    ///
313    /// * `values` - Homogeneous runtime collection to validate and borrow for
314    ///   `'a`.
315    ///
316    /// # Returns
317    ///
318    /// A borrowed V1 envelope preserving the input shape.
319    ///
320    /// # Errors
321    ///
322    /// Returns the same representational validation errors as
323    /// [`Self::from_values`].
324    #[inline]
325    fn try_from(values: &'a MultiValues) -> Result<Self, Self::Error> {
326        Self::from_values(values)
327    }
328}
329
330impl<'a> TryFrom<&'a ValueContainer> for ValueWireRefV1<'a> {
331    /// Validation failure when the borrowed input cannot be represented in V1.
332    type Error = ValueWireEncodeError;
333
334    /// Validates and borrows the input without cloning its payload.
335    ///
336    /// # Parameters
337    ///
338    /// * `value` - Explicit scalar-or-collection value to validate and borrow
339    ///   for `'a`.
340    ///
341    /// # Returns
342    ///
343    /// A borrowed V1 envelope preserving the input shape.
344    ///
345    /// # Errors
346    ///
347    /// Returns the same representational validation errors as
348    /// [`Self::from_container`].
349    #[inline]
350    fn try_from(value: &'a ValueContainer) -> Result<Self, Self::Error> {
351        Self::from_container(value)
352    }
353}
354
355impl Serialize for ValueWireRefV1<'_> {
356    /// Serializes the borrowed runtime shape through the V1 envelope.
357    ///
358    /// # Type Parameters
359    ///
360    /// * `S` - Serde serializer defining the output format and error type.
361    ///
362    /// # Parameters
363    ///
364    /// * `serializer` - Consumed serializer receiving the version and payload.
365    ///
366    /// # Returns
367    ///
368    /// The serializer's success value after encoding the complete V1 envelope.
369    ///
370    /// # Errors
371    ///
372    /// Returns the serializer's error if encoding the envelope or payload
373    /// fails.
374    #[inline]
375    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
376    where
377        S: Serializer,
378    {
379        serialize_wire(self.value.shape(), serializer)
380    }
381}