Skip to main content

qubit_json/decode/
normalizing_json_decode_policy.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//! Defines the policy used by the normalizing JSON decoder.
9
10use super::DiagnosticPolicy;
11use super::MarkdownFenceClosing;
12use super::MarkdownFencePolicy;
13use super::NormalizingJsonDecodePolicyBuilder;
14
15/// Text-normalization and diagnostic policy for
16/// [`crate::decode::NormalizingJsonDecoder`].
17///
18/// Resource limits deliberately live in
19/// [`qubit_budget::json::JsonDecodeLimits`]
20/// and are supplied separately when constructing a decoder.
21///
22/// # Examples
23///
24/// ```
25/// use qubit_json::decode::NormalizingJsonDecodePolicy;
26///
27/// let policy = NormalizingJsonDecodePolicy::builder()
28///     .trim_whitespace(false)
29///     .build();
30/// assert!(!policy.trim_whitespace());
31/// ```
32#[must_use]
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct NormalizingJsonDecodePolicy {
35    /// Whether leading and trailing whitespace is removed.
36    trim_whitespace: bool,
37    /// Whether a leading UTF-8 byte order mark is removed.
38    strip_utf8_bom: bool,
39    /// How one outer Markdown code fence is handled.
40    markdown_fence_policy: MarkdownFencePolicy,
41    /// Whether raw ASCII control characters in strings are escaped.
42    escape_control_chars_in_strings: bool,
43    /// Whether input-derived decoding details are retained.
44    diagnostic_policy: DiagnosticPolicy,
45}
46
47impl NormalizingJsonDecodePolicy {
48    /// Creates the default permissive normalization policy.
49    ///
50    /// The defaults trim surrounding whitespace, strip a leading UTF-8 BOM,
51    /// accept JSON-only Markdown fences with an optional closing fence, escape
52    /// raw string control characters, and redact input-derived diagnostics.
53    ///
54    /// # Returns
55    ///
56    /// A policy with the documented permissive defaults.
57    #[inline]
58    pub const fn lenient() -> Self {
59        Self {
60            trim_whitespace: true,
61            strip_utf8_bom: true,
62            markdown_fence_policy: MarkdownFencePolicy::JsonOnly {
63                closing: MarkdownFenceClosing::Optional,
64            },
65            escape_control_chars_in_strings: true,
66            diagnostic_policy: DiagnosticPolicy::Redacted,
67        }
68    }
69
70    /// Creates a builder initialized with the lenient policy.
71    ///
72    /// # Returns
73    ///
74    /// A builder whose fields can be selectively changed before `build`.
75    #[inline]
76    pub const fn builder() -> NormalizingJsonDecodePolicyBuilder {
77        NormalizingJsonDecodePolicyBuilder::new()
78    }
79
80    /// Returns whether surrounding whitespace is removed.
81    ///
82    /// # Returns
83    ///
84    /// `true` when leading and trailing whitespace is discarded before JSON
85    /// parsing.
86    #[inline(always)]
87    #[must_use]
88    pub const fn trim_whitespace(&self) -> bool {
89        self.trim_whitespace
90    }
91
92    /// Returns whether a leading UTF-8 byte order mark is removed.
93    ///
94    /// # Returns
95    ///
96    /// `true` when a leading UTF-8 BOM is discarded before JSON parsing.
97    #[inline(always)]
98    #[must_use]
99    pub const fn strip_utf8_bom(&self) -> bool {
100        self.strip_utf8_bom
101    }
102
103    /// Returns the outer Markdown fence policy.
104    ///
105    /// # Returns
106    ///
107    /// A shared reference to the policy governing Markdown fence stripping.
108    #[inline(always)]
109    #[must_use]
110    pub const fn markdown_fence_policy(&self) -> &MarkdownFencePolicy {
111        &self.markdown_fence_policy
112    }
113
114    /// Returns whether raw control characters in strings are escaped.
115    ///
116    /// # Returns
117    ///
118    /// `true` when raw JSON string control characters are rewritten to escaped
119    /// forms during normalization.
120    #[inline(always)]
121    #[must_use]
122    pub const fn escape_control_chars_in_strings(&self) -> bool {
123        self.escape_control_chars_in_strings
124    }
125
126    /// Returns the error diagnostic policy.
127    ///
128    /// # Returns
129    ///
130    /// The policy controlling retention of input-derived error details.
131    #[inline(always)]
132    #[must_use]
133    pub const fn diagnostic_policy(&self) -> DiagnosticPolicy {
134        self.diagnostic_policy
135    }
136
137    /// Updates whitespace trimming during builder composition.
138    ///
139    /// # Parameters
140    ///
141    /// - `enabled`: Whether surrounding whitespace should be removed.
142    #[inline(always)]
143    pub(super) const fn set_trim_whitespace(&mut self, enabled: bool) {
144        self.trim_whitespace = enabled;
145    }
146
147    /// Updates BOM stripping during builder composition.
148    ///
149    /// # Parameters
150    ///
151    /// - `enabled`: Whether a leading UTF-8 BOM should be removed.
152    #[inline(always)]
153    pub(super) const fn set_strip_utf8_bom(&mut self, enabled: bool) {
154        self.strip_utf8_bom = enabled;
155    }
156
157    /// Updates Markdown fence handling during builder composition.
158    ///
159    /// # Parameters
160    ///
161    /// - `policy`: The Markdown fence handling policy to apply.
162    #[inline(always)]
163    pub(super) const fn set_markdown_fence_policy(&mut self, policy: MarkdownFencePolicy) {
164        self.markdown_fence_policy = policy;
165    }
166
167    /// Updates control-character escaping during builder composition.
168    ///
169    /// # Parameters
170    ///
171    /// - `enabled`: Whether raw control characters in JSON strings should be
172    ///   escaped.
173    #[inline(always)]
174    pub(super) const fn set_escape_control_chars_in_strings(&mut self, enabled: bool) {
175        self.escape_control_chars_in_strings = enabled;
176    }
177
178    /// Updates diagnostic handling during builder composition.
179    ///
180    /// # Parameters
181    ///
182    /// - `policy`: The policy for retaining input-derived diagnostic details.
183    #[inline(always)]
184    pub(super) const fn set_diagnostic_policy(&mut self, policy: DiagnosticPolicy) {
185        self.diagnostic_policy = policy;
186    }
187}
188
189impl Default for NormalizingJsonDecodePolicy {
190    /// Creates the default lenient policy.
191    #[inline(always)]
192    fn default() -> Self {
193        Self::lenient()
194    }
195}