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#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct NormalizingJsonDecodePolicy {
34    /// Whether leading and trailing whitespace is removed.
35    trim_whitespace: bool,
36    /// Whether a leading UTF-8 byte order mark is removed.
37    strip_utf8_bom: bool,
38    /// How one outer Markdown code fence is handled.
39    markdown_fence_policy: MarkdownFencePolicy,
40    /// Whether raw ASCII control characters in strings are escaped.
41    escape_control_chars_in_strings: bool,
42    /// Whether input-derived decoding details are retained.
43    diagnostic_policy: DiagnosticPolicy,
44}
45
46impl NormalizingJsonDecodePolicy {
47    /// Creates the default permissive normalization policy.
48    ///
49    /// The defaults trim surrounding whitespace, strip a leading UTF-8 BOM,
50    /// accept JSON-only Markdown fences with an optional closing fence, escape
51    /// raw string control characters, and redact input-derived diagnostics.
52    ///
53    /// # Returns
54    ///
55    /// A policy with the documented permissive defaults.
56    #[inline]
57    #[must_use]
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    #[must_use]
77    pub const fn builder() -> NormalizingJsonDecodePolicyBuilder {
78        NormalizingJsonDecodePolicyBuilder::new()
79    }
80
81    /// Returns whether surrounding whitespace is removed.
82    ///
83    /// # Returns
84    ///
85    /// `true` when leading and trailing whitespace is discarded before JSON
86    /// parsing.
87    #[inline(always)]
88    #[must_use]
89    pub const fn trim_whitespace(&self) -> bool {
90        self.trim_whitespace
91    }
92
93    /// Returns whether a leading UTF-8 byte order mark is removed.
94    ///
95    /// # Returns
96    ///
97    /// `true` when a leading UTF-8 BOM is discarded before JSON parsing.
98    #[inline(always)]
99    #[must_use]
100    pub const fn strip_utf8_bom(&self) -> bool {
101        self.strip_utf8_bom
102    }
103
104    /// Returns the outer Markdown fence policy.
105    ///
106    /// # Returns
107    ///
108    /// A shared reference to the policy governing Markdown fence stripping.
109    #[inline(always)]
110    #[must_use]
111    pub const fn markdown_fence_policy(&self) -> &MarkdownFencePolicy {
112        &self.markdown_fence_policy
113    }
114
115    /// Returns whether raw control characters in strings are escaped.
116    ///
117    /// # Returns
118    ///
119    /// `true` when raw JSON string control characters are rewritten to escaped
120    /// forms during normalization.
121    #[inline(always)]
122    #[must_use]
123    pub const fn escape_control_chars_in_strings(&self) -> bool {
124        self.escape_control_chars_in_strings
125    }
126
127    /// Returns the error diagnostic policy.
128    ///
129    /// # Returns
130    ///
131    /// The policy controlling retention of input-derived error details.
132    #[inline(always)]
133    #[must_use]
134    pub const fn diagnostic_policy(&self) -> DiagnosticPolicy {
135        self.diagnostic_policy
136    }
137
138    /// Updates whitespace trimming during builder composition.
139    #[inline(always)]
140    pub(super) const fn set_trim_whitespace(&mut self, enabled: bool) {
141        self.trim_whitespace = enabled;
142    }
143
144    /// Updates BOM stripping during builder composition.
145    #[inline(always)]
146    pub(super) const fn set_strip_utf8_bom(&mut self, enabled: bool) {
147        self.strip_utf8_bom = enabled;
148    }
149
150    /// Updates Markdown fence handling during builder composition.
151    #[inline(always)]
152    pub(super) const fn set_markdown_fence_policy(&mut self, policy: MarkdownFencePolicy) {
153        self.markdown_fence_policy = policy;
154    }
155
156    /// Updates control-character escaping during builder composition.
157    #[inline(always)]
158    pub(super) const fn set_escape_control_chars_in_strings(&mut self, enabled: bool) {
159        self.escape_control_chars_in_strings = enabled;
160    }
161
162    /// Updates diagnostic handling during builder composition.
163    #[inline(always)]
164    pub(super) const fn set_diagnostic_policy(&mut self, policy: DiagnosticPolicy) {
165        self.diagnostic_policy = policy;
166    }
167}
168
169impl Default for NormalizingJsonDecodePolicy {
170    /// Creates the default lenient policy.
171    #[inline(always)]
172    fn default() -> Self {
173        Self::lenient()
174    }
175}