Skip to main content

qubit_json/decode/
normalized_json_document.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//! Defines normalized JSON text retained for borrowing deserialization.
9
10use std::borrow::Cow;
11
12/// A normalized JSON document whose text outlives borrowed decode results.
13///
14/// Preparing a document charges raw and normalized input budgets. Decoding it
15/// later charges only decoded-value budgets, and the same document may be
16/// decoded repeatedly. Documents are detached from the decoder that prepared
17/// them; value charges belong to the decoder performing each decode.
18///
19/// Borrowing follows Serde's JSON representation rules: strings without JSON
20/// escapes can borrow from this document, while strings containing escapes
21/// require an owned target because deserialization must materialize their
22/// unescaped contents.
23///
24/// # Examples
25///
26/// ```
27/// use qubit_budget::json::JsonDecodeLimits;
28/// use qubit_json::decode::NormalizingJsonDecodePolicy;
29/// use qubit_json::decode::NormalizingJsonDecoder;
30/// use serde_json::Value;
31///
32/// let input = "  {\"ok\":true}  ";
33/// let mut decoder = NormalizingJsonDecoder::with_limits(
34///     NormalizingJsonDecodePolicy::builder().build(),
35///     JsonDecodeLimits::new(),
36/// );
37/// let document = decoder.prepare_str(input)?;
38/// assert_eq!(document.as_str(), r#"{"ok":true}"#);
39/// assert_eq!(document.raw_input_bytes(), input.len());
40/// assert_eq!(document.normalized_input_bytes(), document.as_str().len());
41/// let value = decoder.decode_precharged_document::<Value>(&document)?;
42/// assert_eq!(value["ok"], true);
43/// # Ok::<(), qubit_json::decode::JsonDecodeError>(())
44/// ```
45#[derive(Debug, Clone)]
46pub struct NormalizedJsonDocument<'input> {
47    /// Normalized text, borrowed when rewriting did not require allocation.
48    text: Cow<'input, str>,
49    /// Original input length in bytes.
50    raw_input_bytes: usize,
51    /// Normalized text length in bytes.
52    normalized_input_bytes: usize,
53}
54
55impl<'input> NormalizedJsonDocument<'input> {
56    /// Creates a document from normalized text and its original byte length.
57    #[inline]
58    #[must_use]
59    pub(in crate::decode) fn new(text: Cow<'input, str>, raw_input_bytes: usize) -> Self {
60        let normalized_input_bytes = text.len();
61        Self {
62            text,
63            raw_input_bytes,
64            normalized_input_bytes,
65        }
66    }
67
68    /// Returns the normalized JSON text retained by this document.
69    ///
70    /// The returned slice borrows the document. It is the exact text consumed
71    /// by later document-based decoding and does not allocate.
72    ///
73    /// # Returns
74    ///
75    /// The normalized JSON text.
76    #[inline(always)]
77    #[must_use]
78    pub fn as_str(&self) -> &str {
79        self.text.as_ref()
80    }
81
82    /// Returns the original input length in bytes.
83    ///
84    /// This value includes whitespace, a UTF-8 byte-order mark, and any other
85    /// input bytes removed or rewritten during normalization.
86    ///
87    /// # Returns
88    ///
89    /// The byte length charged for the original input.
90    #[inline(always)]
91    #[must_use]
92    pub const fn raw_input_bytes(&self) -> usize {
93        self.raw_input_bytes
94    }
95
96    /// Returns the normalized text length in bytes.
97    ///
98    /// This is the byte length of [`Self::as_str`], after all enabled
99    /// normalization has completed.
100    ///
101    /// # Returns
102    ///
103    /// The byte length charged for normalized text.
104    #[inline(always)]
105    #[must_use]
106    pub const fn normalized_input_bytes(&self) -> usize {
107        self.normalized_input_bytes
108    }
109}