Skip to main content

qubit_value/value_wire/
value_wire_payload_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//! Unversioned V1 payload for use inside an already-versioned protocol.
10
11use serde::{
12    Deserialize,
13    Deserializer,
14    Serialize,
15    Serializer,
16};
17
18use crate::{
19    MultiValues,
20    Value,
21    ValueContainer,
22};
23#[cfg(feature = "json")]
24use crate::{
25    ValueWireDecodeError,
26    WireLimits,
27};
28
29use super::value_wire_payload_ref_v1::{
30    validate_value,
31    validate_values,
32};
33use super::{
34    ValueWireEncodeError,
35    WireShapeOwned,
36    WireShapeRef,
37};
38
39/// Typed V1 scalar-or-collection payload without an enclosing version field.
40///
41/// # Resource limits
42///
43/// The generic [`Deserialize`](serde::Deserialize) implementation is intended
44/// for already-bounded embedded documents and does not enforce message-size or
45/// structural limits. Use `ValueWirePayloadV1::decode_json_slice` or
46/// `ValueWirePayloadV1::decode_json_slice_with_limits` for untrusted complete
47/// JSON input.
48#[must_use]
49#[derive(Debug, Clone, PartialEq, Eq, Hash)]
50pub struct ValueWirePayloadV1 {
51    /// Preserved runtime shape and payload.
52    value: ValueContainer,
53}
54
55impl ValueWirePayloadV1 {
56    /// Decodes a complete V1 JSON payload using default resource limits.
57    ///
58    /// Prefer this entry point when the payload itself is the complete
59    /// untrusted document. Embedded protocols should share one budget across
60    /// all payloads in their complete document.
61    ///
62    /// # Errors
63    ///
64    /// Returns a limit error when the input or decoded structure is too large,
65    /// or [`ValueWireDecodeError::InvalidJson`] for malformed input.
66    #[cfg(feature = "json")]
67    #[inline]
68    pub fn decode_json_slice(
69        input: &[u8],
70    ) -> Result<Self, ValueWireDecodeError> {
71        Self::decode_json_slice_with_limits(input, WireLimits::default())
72    }
73
74    /// Decodes a complete V1 JSON payload using explicit resource limits.
75    ///
76    /// # Errors
77    ///
78    /// Returns a limit error when `input` or its decoded structure exceeds
79    /// `limits`, or [`ValueWireDecodeError::InvalidJson`] for malformed input.
80    #[cfg(feature = "json")]
81    #[inline]
82    pub fn decode_json_slice_with_limits(
83        input: &[u8],
84        limits: WireLimits,
85    ) -> Result<Self, ValueWireDecodeError> {
86        let mut budget = limits.begin(input.len())?;
87        let value: Self = serde_json::from_slice(input)
88            .map_err(ValueWireDecodeError::from)?;
89        budget.check_container(value.container())?;
90        Ok(value)
91    }
92
93    /// Borrows the preserved runtime value.
94    #[inline(always)]
95    pub const fn container(&self) -> &ValueContainer {
96        &self.value
97    }
98
99    /// Consumes this payload and returns its runtime value.
100    #[inline(always)]
101    pub fn into_container(self) -> ValueContainer {
102        self.value
103    }
104
105    /// Builds a payload after enforcing V1's finite-float invariant.
106    fn try_new(value: ValueContainer) -> Result<Self, ValueWireEncodeError> {
107        match &value {
108            ValueContainer::Scalar(value) => validate_value(value)?,
109            ValueContainer::Collection(values) => validate_values(values)?,
110        }
111        Ok(Self { value })
112    }
113
114    /// Wraps a payload decoded through V1's finite-number Serde adapters.
115    pub(in crate::value_wire) const fn from_decoded(
116        value: ValueContainer,
117    ) -> Self {
118        Self { value }
119    }
120}
121
122impl TryFrom<Value> for ValueWirePayloadV1 {
123    type Error = ValueWireEncodeError;
124
125    /// Validates a scalar for use in a V1 payload.
126    #[inline(always)]
127    fn try_from(value: Value) -> Result<Self, Self::Error> {
128        Self::try_new(value.into())
129    }
130}
131
132impl TryFrom<MultiValues> for ValueWirePayloadV1 {
133    type Error = ValueWireEncodeError;
134
135    /// Validates a collection for use in a V1 payload.
136    #[inline(always)]
137    fn try_from(value: MultiValues) -> Result<Self, Self::Error> {
138        Self::try_new(value.into())
139    }
140}
141
142impl TryFrom<ValueContainer> for ValueWirePayloadV1 {
143    type Error = ValueWireEncodeError;
144
145    /// Validates an explicitly shaped value for use in a V1 payload.
146    #[inline(always)]
147    fn try_from(value: ValueContainer) -> Result<Self, Self::Error> {
148        Self::try_new(value)
149    }
150}
151
152impl From<ValueWirePayloadV1> for ValueContainer {
153    /// Extracts the shaped runtime value from a V1 payload.
154    #[inline(always)]
155    fn from(value: ValueWirePayloadV1) -> Self {
156        value.into_container()
157    }
158}
159
160impl Serialize for ValueWirePayloadV1 {
161    /// Serializes the unversioned V1 shape.
162    #[inline(always)]
163    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164    where
165        S: Serializer,
166    {
167        WireShapeRef::from(&self.value).serialize(serializer)
168    }
169}
170
171impl<'de> Deserialize<'de> for ValueWirePayloadV1 {
172    /// Deserializes an unversioned V1 shape.
173    #[inline(always)]
174    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
175    where
176        D: Deserializer<'de>,
177    {
178        Ok(Self::from_decoded(
179            WireShapeOwned::deserialize(deserializer)?.into(),
180        ))
181    }
182}