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}