Skip to main content

qubit_json/
json_decode_options.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Defines the option type used to configure the lenient JSON decoder.
11//!
12
13/// Configuration switches for [`crate::LenientJsonDecoder`].
14///
15/// Each flag controls one normalization rule applied before parsing JSON.
16/// Defaults are intentionally conservative and cover the most common
17/// non-fully-trusted text inputs without attempting aggressive repair.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct JsonDecodeOptions {
20    /// Controls whether leading and trailing whitespace is removed before any
21    /// other normalization step is applied.
22    pub trim_whitespace: bool,
23    /// Controls whether a leading UTF-8 byte order mark (`U+FEFF`) is removed
24    /// before parsing.
25    pub strip_utf8_bom: bool,
26    /// Controls whether one outer Markdown code fence is removed.
27    ///
28    /// Typical examples include `````json ... ````` and bare fenced blocks
29    /// starting with ````` followed by a newline.
30    pub strip_markdown_code_fence: bool,
31    /// Controls whether Markdown fence stripping requires a matching closing
32    /// fence.
33    ///
34    /// When enabled, an opening fence without a valid closing fence keeps the
35    /// input unchanged.
36    pub strip_markdown_code_fence_requires_closing: bool,
37    /// Controls whether Markdown fence stripping only accepts JSON-like info
38    /// strings whose first token is empty, `json`, or `jsonc`.
39    ///
40    /// When enabled, fenced blocks labeled with other languages are not
41    /// stripped.
42    pub strip_markdown_code_fence_json_only: bool,
43    /// Controls whether raw ASCII control characters inside JSON string
44    /// literals are converted into valid JSON escape sequences.
45    pub escape_control_chars_in_strings: bool,
46    /// Caps the accepted raw input size in bytes before normalization.
47    ///
48    /// When set to `Some(limit)`, any input whose byte length is greater than
49    /// `limit` is rejected before further processing. When set to `None`,
50    /// no size limit is enforced.
51    pub max_input_bytes: Option<usize>,
52}
53
54impl JsonDecodeOptions {
55    /// Creates the default lenient option set.
56    ///
57    /// This preset enables the small, predictable normalization rules intended
58    /// for non-fully-trusted text inputs while keeping aggressive JSON repair out
59    /// of scope.
60    #[must_use]
61    pub const fn lenient() -> Self {
62        Self {
63            trim_whitespace: true,
64            strip_utf8_bom: true,
65            strip_markdown_code_fence: true,
66            strip_markdown_code_fence_requires_closing: false,
67            strip_markdown_code_fence_json_only: false,
68            escape_control_chars_in_strings: true,
69            max_input_bytes: None,
70        }
71    }
72
73    /// Creates an option set that disables all normalization rules.
74    ///
75    /// This preset still allows `serde_json` to accept JSON syntax that is valid
76    /// on its own, but the decoder will not trim, strip BOMs, remove Markdown
77    /// fences, or escape raw control characters before parsing.
78    #[must_use]
79    pub const fn strict() -> Self {
80        Self {
81            trim_whitespace: false,
82            strip_utf8_bom: false,
83            strip_markdown_code_fence: false,
84            strip_markdown_code_fence_requires_closing: false,
85            strip_markdown_code_fence_json_only: false,
86            escape_control_chars_in_strings: false,
87            max_input_bytes: None,
88        }
89    }
90
91    /// Creates lenient options that only strip JSON-like Markdown code fences.
92    ///
93    /// The resulting preset keeps the other default normalization rules, but
94    /// restricts Markdown fence stripping to empty, `json`, or `jsonc` as the
95    /// first info-string token.
96    #[must_use]
97    pub const fn json_code_fences_only() -> Self {
98        let mut options = Self::lenient();
99        options.strip_markdown_code_fence_json_only = true;
100        options
101    }
102
103    /// Returns a copy of these options with a raw input byte-size limit.
104    ///
105    /// Inputs whose byte length is greater than `max_input_bytes` are rejected
106    /// before normalization.
107    #[must_use]
108    pub const fn with_max_input_bytes(mut self, max_input_bytes: usize) -> Self {
109        self.max_input_bytes = Some(max_input_bytes);
110        self
111    }
112}
113
114impl Default for JsonDecodeOptions {
115    fn default() -> Self {
116        Self::lenient()
117    }
118}