Skip to main content

qubit_json/value/
accounting_json_value_seed.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//! Incremental accounting deserialization into a JSON value tree.
9
10use std::fmt::Debug;
11
12use qubit_budget::ResourceQuantity;
13use qubit_budget::json::JsonValueTransaction;
14use serde::Deserializer;
15use serde::de::DeserializeSeed;
16use serde_json::Value;
17
18use super::internal::JsonValueVisitor;
19
20/// Serde seed that constructs a [`Value`] while accounting decoded resources.
21///
22/// Unlike lexical JSON admission, this seed observes values after a Serde
23/// deserializer has decoded them. It cannot inspect original number lexemes or
24/// enforce text-level integer and floating-point range rules. Use
25/// `JsonDecoder` when decoding JSON text requires those guarantees. This seed
26/// remains suitable for decoded-value budget enforcement inside a type's
27/// ordinary [`serde::Deserialize`] implementation, where the original input
28/// bytes are unavailable.
29///
30/// Do not pass this seed to [`crate::decode::JsonDecoder::decode_seed_str`] or
31/// [`crate::decode::JsonDecoder::decode_seed_utf8`] when its transaction and
32/// the decoder represent the same logical decoded-value budget. `JsonDecoder`
33/// already accounts the complete value during lexical admission, so the seed
34/// would charge that value a second time. In that pipeline, use a seed that
35/// performs only domain deserialization and domain-specific checks.
36///
37/// # Type Parameters
38///
39/// * `R` - Resource identity tracked by the value transaction.
40/// * `Q` - Quantity representation used for resource accounting.
41///
42/// # Examples
43///
44/// ```
45/// use qubit_budget::json::{JsonResource, JsonValueBudget, JsonValueLimits};
46/// use qubit_json::value::AccountingJsonValueSeed;
47/// use serde::de::DeserializeSeed;
48///
49/// let mut budget = JsonValueBudget::new(JsonValueLimits::<JsonResource, usize>::default());
50/// let mut transaction = budget.transaction();
51/// let mut deserializer = serde_json::Deserializer::from_str(r#"{"ok":true}"#);
52/// let value = AccountingJsonValueSeed::new(&mut transaction).deserialize(&mut deserializer)?;
53/// assert_eq!(value["ok"], true);
54/// transaction.commit()?;
55/// # Ok::<(), Box<dyn std::error::Error>>(())
56/// ```
57pub struct AccountingJsonValueSeed<'transaction, 'budget, R, Q = usize>
58where
59    Q: ResourceQuantity,
60{
61    /// Transaction receiving the decoded value's staged resource charges.
62    transaction: &'transaction mut JsonValueTransaction<'budget, R, Q>,
63}
64
65impl<'transaction, 'budget, R, Q> AccountingJsonValueSeed<'transaction, 'budget, R, Q>
66where
67    Q: ResourceQuantity,
68{
69    /// Creates a root seed using the supplied decoded-value transaction.
70    ///
71    /// # Parameters
72    ///
73    /// * `transaction` - Transaction receiving decoded JSON resource charges.
74    ///   It must not duplicate decoded-value accounting already performed by an
75    ///   outer [`crate::decode::JsonDecoder`].
76    ///
77    /// # Returns
78    ///
79    /// A seed that constructs one accounted [`Value`] tree.
80    #[inline(always)]
81    #[must_use]
82    pub fn new(transaction: &'transaction mut JsonValueTransaction<'budget, R, Q>) -> Self {
83        Self { transaction }
84    }
85}
86
87impl<'de, R, Q> DeserializeSeed<'de> for AccountingJsonValueSeed<'_, '_, R, Q>
88where
89    R: Clone + Debug,
90    Q: ResourceQuantity,
91{
92    type Value = Value;
93
94    /// Builds one value through the accounting visitor.
95    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
96    where
97        D: Deserializer<'de>,
98    {
99        deserializer.deserialize_any(JsonValueVisitor::new(self.transaction, 1))
100    }
101}