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}