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}