1use std::sync::Arc;
2
3use crate::JsonValue;
4use crate::shared::constants::DEFAULT_DELIMITER;
5
6pub type EncodeReplacer =
7 Arc<dyn Fn(&str, &JsonValue, &[PathSegment]) -> Option<JsonValue> + Send + Sync>;
8
9#[derive(Clone)]
10pub struct EncodeOptions {
11 pub indent: Option<usize>,
12 pub delimiter: Option<char>,
13 pub key_folding: Option<KeyFoldingMode>,
14 pub flatten_depth: Option<usize>,
15 pub replacer: Option<EncodeReplacer>,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum KeyFoldingMode {
20 Off,
21 Safe,
22}
23
24#[derive(Debug, Clone)]
25pub struct DecodeOptions {
26 pub indent: Option<usize>,
27 pub strict: Option<bool>,
28 pub expand_paths: Option<ExpandPathsMode>,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum ExpandPathsMode {
33 Off,
34 Safe,
35}
36
37#[derive(Debug, Clone, Default)]
38pub struct DecodeStreamOptions {
39 pub indent: Option<usize>,
40 pub strict: Option<bool>,
41}
42
43#[derive(Clone)]
44pub struct ResolvedEncodeOptions {
45 pub indent: usize,
46 pub delimiter: char,
47 pub key_folding: KeyFoldingMode,
48 pub flatten_depth: usize,
49 pub replacer: Option<EncodeReplacer>,
50}
51
52#[derive(Debug, Clone)]
53pub struct ResolvedDecodeOptions {
54 pub indent: usize,
55 pub strict: bool,
56 pub expand_paths: ExpandPathsMode,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
60pub enum PathSegment {
61 Key(String),
62 Index(usize),
63}
64
65#[must_use]
66pub fn resolve_encode_options(options: Option<EncodeOptions>) -> ResolvedEncodeOptions {
67 let options = options.unwrap_or(EncodeOptions {
68 indent: None,
69 delimiter: None,
70 key_folding: None,
71 flatten_depth: None,
72 replacer: None,
73 });
74
75 ResolvedEncodeOptions {
76 indent: options.indent.unwrap_or(2),
77 delimiter: options.delimiter.unwrap_or(DEFAULT_DELIMITER),
78 key_folding: options.key_folding.unwrap_or(KeyFoldingMode::Off),
79 flatten_depth: options.flatten_depth.unwrap_or(usize::MAX),
80 replacer: options.replacer,
81 }
82}
83
84#[must_use]
85pub fn resolve_decode_options(options: Option<DecodeOptions>) -> ResolvedDecodeOptions {
86 let options = options.unwrap_or(DecodeOptions {
87 indent: None,
88 strict: None,
89 expand_paths: None,
90 });
91
92 ResolvedDecodeOptions {
93 indent: options.indent.unwrap_or(2),
94 strict: options.strict.unwrap_or(true),
95 expand_paths: options.expand_paths.unwrap_or(ExpandPathsMode::Off),
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn resolve_encode_defaults_when_none() {
105 let r = resolve_encode_options(None);
106 assert_eq!(r.indent, 2);
107 assert_eq!(r.delimiter, DEFAULT_DELIMITER);
108 assert_eq!(r.key_folding, KeyFoldingMode::Off);
109 assert_eq!(r.flatten_depth, usize::MAX);
110 assert!(r.replacer.is_none());
111 }
112
113 #[test]
114 fn resolve_encode_uses_overrides() {
115 let r = resolve_encode_options(Some(EncodeOptions {
116 indent: Some(4),
117 delimiter: Some('|'),
118 key_folding: Some(KeyFoldingMode::Safe),
119 flatten_depth: Some(3),
120 replacer: None,
121 }));
122 assert_eq!(r.indent, 4);
123 assert_eq!(r.delimiter, '|');
124 assert_eq!(r.key_folding, KeyFoldingMode::Safe);
125 assert_eq!(r.flatten_depth, 3);
126 }
127
128 #[test]
129 fn resolve_encode_partial_overrides() {
130 let r = resolve_encode_options(Some(EncodeOptions {
131 indent: Some(0),
132 delimiter: None,
133 key_folding: None,
134 flatten_depth: None,
135 replacer: None,
136 }));
137 assert_eq!(r.indent, 0);
138 assert_eq!(r.delimiter, DEFAULT_DELIMITER);
139 assert_eq!(r.flatten_depth, usize::MAX);
140 }
141
142 #[test]
143 fn resolve_decode_defaults_when_none() {
144 let r = resolve_decode_options(None);
145 assert_eq!(r.indent, 2);
146 assert!(r.strict);
147 assert_eq!(r.expand_paths, ExpandPathsMode::Off);
148 }
149
150 #[test]
151 fn resolve_decode_uses_overrides() {
152 let r = resolve_decode_options(Some(DecodeOptions {
153 indent: Some(4),
154 strict: Some(false),
155 expand_paths: Some(ExpandPathsMode::Safe),
156 }));
157 assert_eq!(r.indent, 4);
158 assert!(!r.strict);
159 assert_eq!(r.expand_paths, ExpandPathsMode::Safe);
160 }
161
162 #[test]
163 fn resolve_decode_explicit_true_strict() {
164 let r = resolve_decode_options(Some(DecodeOptions {
165 indent: None,
166 strict: Some(true),
167 expand_paths: None,
168 }));
169 assert!(r.strict);
170 }
171
172 #[test]
173 fn key_folding_equality_and_copy() {
174 let a = KeyFoldingMode::Off;
175 let b = a;
176 assert_eq!(a, b);
177 assert_ne!(KeyFoldingMode::Off, KeyFoldingMode::Safe);
178 }
179
180 #[test]
181 fn expand_paths_equality_and_copy() {
182 let a = ExpandPathsMode::Safe;
183 let b = a;
184 assert_eq!(a, b);
185 assert_ne!(ExpandPathsMode::Off, ExpandPathsMode::Safe);
186 }
187
188 #[test]
189 fn path_segment_equality() {
190 assert_eq!(
191 PathSegment::Key("a".to_string()),
192 PathSegment::Key("a".to_string())
193 );
194 assert_eq!(PathSegment::Index(3), PathSegment::Index(3));
195 assert_ne!(PathSegment::Index(1), PathSegment::Index(2));
196 assert_ne!(PathSegment::Key("a".into()), PathSegment::Key("b".into()));
197 }
198
199 #[test]
200 fn decode_stream_options_default() {
201 let d = DecodeStreamOptions::default();
202 assert!(d.indent.is_none());
203 assert!(d.strict.is_none());
204 }
205
206 #[test]
207 fn resolve_encode_replacer_threads_through() {
208 use crate::JsonValue;
209 use std::sync::Arc;
210 let replacer: EncodeReplacer = Arc::new(|_key, value, _path| Some(value.clone()));
211 let r = resolve_encode_options(Some(EncodeOptions {
212 indent: None,
213 delimiter: None,
214 key_folding: None,
215 flatten_depth: None,
216 replacer: Some(replacer.clone()),
217 }));
218 assert!(r.replacer.is_some());
219 let out = (r.replacer.as_ref().unwrap())("k", &JsonValue::from(1i64), &[]);
221 assert!(out.is_some());
222 }
223}