ocpi_tariffs/json/
write.rs1use std::fmt::{self, Write};
2
3use super::{Element, Field, Value};
4
5const TAB: &str = " ";
6const ARRAY_OPEN: char = '[';
7const ARRAY_CLOSE: char = ']';
8const OBJECT_OPEN: char = '{';
9const OBJECT_CLOSE: char = '}';
10const COMMA: char = ',';
11const NEWLINE: char = '\n';
12
13pub struct Pretty<'a, 'bin> {
18 elem: &'a Element<'bin>,
19}
20
21impl<'a, 'bin> Pretty<'a, 'bin> {
22 pub fn new(elem: &'a Element<'bin>) -> Self {
23 Pretty { elem }
24 }
25}
26
27impl fmt::Display for Pretty<'_, '_> {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 let mut stack = vec![State::Root(self.elem)];
30 let mut write_comma = WriteComma::Skip;
31
32 loop {
34 let elem = loop {
36 let depth = stack.len();
37
38 let Some(mut state) = stack.pop() else {
39 return Ok(());
41 };
42
43 let elem = match &mut state {
44 State::Root(elem) => {
45 write_elem(elem, f)?;
49 elem
50 }
51 State::Array(iter) => {
52 let Some(elem) = iter.next() else {
53 write_nl_and_indent(depth - 1, f)?;
56 f.write_char(ARRAY_CLOSE)?;
57 continue;
58 };
59
60 if let WriteComma::Write = write_comma {
61 f.write_char(COMMA)?;
62 }
63 write_nl_and_indent(depth, f)?;
64 write_comma = write_elem(elem, f)?;
65 elem
66 }
67 State::Object(iter) => {
68 let Some(field) = iter.next() else {
69 write_nl_and_indent(depth - 1, f)?;
72 f.write_char(OBJECT_CLOSE)?;
73 continue;
74 };
75
76 if let WriteComma::Write = write_comma {
77 f.write_char(COMMA)?;
78 }
79 write_nl_and_indent(depth, f)?;
80 write_comma = write_field(field, f)?;
81 field.element()
82 }
83 };
84
85 match &state {
86 State::Array(_) | State::Object(_) => stack.push(state),
87 State::Root(_) => (),
88 }
89
90 break elem;
91 };
92
93 match elem.value() {
94 Value::Array(elements) => stack.push(State::Array(elements.iter())),
95 Value::Object(fields) => stack.push(State::Object(fields.iter())),
96 _ => (),
97 }
98 }
99 }
100}
101
102#[derive(Debug)]
104enum State<'a, 'bin> {
105 Root(&'a Element<'bin>),
107
108 Array(std::slice::Iter<'a, Element<'bin>>),
110
111 Object(std::slice::Iter<'a, Field<'bin>>),
113}
114
115fn write_nl_and_indent(depth: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 f.write_char(NEWLINE)?;
118
119 for _ in 0..depth {
120 f.write_str(TAB)?;
121 }
122
123 Ok(())
124}
125
126#[derive(Copy, Clone)]
128enum WriteComma {
129 Write,
131
132 Skip,
134}
135
136fn write_elem(elem: &Element<'_>, f: &mut fmt::Formatter<'_>) -> Result<WriteComma, fmt::Error> {
141 match elem.value() {
142 Value::Null => {
143 f.write_str("null")?;
144 Ok(WriteComma::Write)
145 }
146 Value::True => {
147 f.write_str("true")?;
148 Ok(WriteComma::Write)
149 }
150 Value::False => {
151 f.write_str("false")?;
152 Ok(WriteComma::Write)
153 }
154 Value::String(s) => {
155 write!(f, "\"{s}\"")?;
156 Ok(WriteComma::Write)
157 }
158 Value::Number(n) => {
159 write!(f, "{n}")?;
160 Ok(WriteComma::Write)
161 }
162 Value::Array(_) => {
163 f.write_char(ARRAY_OPEN)?;
164 Ok(WriteComma::Skip)
165 }
166 Value::Object(_) => {
167 f.write_char(OBJECT_OPEN)?;
168 Ok(WriteComma::Skip)
169 }
170 }
171}
172
173fn write_field(field: &Field<'_>, f: &mut fmt::Formatter<'_>) -> Result<WriteComma, fmt::Error> {
175 write!(f, "\"{}\": ", field.key())?;
176 write_elem(field.element(), f)
177}
178
179#[cfg(test)]
180mod test_tree_writer {
181 use crate::{json, test};
182
183 use super::Pretty;
184
185 #[test]
186 fn should_pretty_print_root_null() {
187 const JSON_IN: &str = "null";
188
189 test::setup();
190 let elem = json::parse(JSON_IN).unwrap();
191 let pretty = Pretty::new(&elem);
192
193 let s = format!("{pretty}");
194 assert_eq!(s, JSON_IN);
195 let _ignored = serde_json::from_str::<serde_json::Value>(&s).unwrap();
196 }
197
198 #[test]
199 fn should_pretty_print_root_bool() {
200 const JSON_IN: &str = "true";
201
202 test::setup();
203 let elem = json::parse(JSON_IN).unwrap();
204 let pretty = Pretty::new(&elem);
205
206 let s = format!("{pretty}");
207 assert_eq!(s, JSON_IN);
208 let _ignored = serde_json::from_str::<serde_json::Value>(&s).unwrap();
209 }
210
211 #[test]
212 fn should_pretty_print_root_string() {
213 const JSON_IN: &str = r#""one""#;
214
215 test::setup();
216 let elem = json::parse(JSON_IN).unwrap();
217 let pretty = Pretty::new(&elem);
218
219 let s = format!("{pretty}");
220 assert_eq!(s, JSON_IN);
221 let _ignored = serde_json::from_str::<serde_json::Value>(&s).unwrap();
222 }
223
224 #[test]
225 fn should_pretty_print_escaped_str() {
226 const JSON_IN: &str = r#""one\ntwo\u0021three\"four\"five""#;
227
228 test::setup();
229 let elem = json::parse(JSON_IN).unwrap();
230 let pretty = Pretty::new(&elem);
231
232 let s = format!("{pretty}");
233 assert_eq!(s, JSON_IN);
234 let _ignored = serde_json::from_str::<serde_json::Value>(&s).unwrap();
235 }
236
237 #[test]
238 fn should_pretty_print_empty_array() {
239 const JSON_IN: &str = "[]";
240 const JSON_OUT: &str = "[
241]";
242
243 test::setup();
244 let elem = json::parse(JSON_IN).unwrap();
245 let pretty = Pretty::new(&elem);
246
247 let s = format!("{pretty}");
248 assert_eq!(s, JSON_OUT);
249 let _ignored = serde_json::from_str::<serde_json::Value>(&s).unwrap();
250 }
251
252 #[test]
253 fn should_pretty_print_string_array() {
254 const JSON_IN: &str = r#"["a", "b", "c"]"#;
255 const JSON_OUT: &str = r#"[
256 "a",
257 "b",
258 "c"
259]"#;
260
261 test::setup();
262 let elem = json::parse(JSON_IN).unwrap();
263 let pretty = Pretty::new(&elem);
264
265 let s = format!("{pretty}");
266 assert_eq!(s, JSON_OUT);
267 let _ignored = serde_json::from_str::<serde_json::Value>(&s).unwrap();
268 }
269
270 #[test]
271 fn should_pretty_print_float_array() {
272 const JSON_IN: &str = "[0.000564, 1.0, 3.14159, 2.71828182845904523536028747135266249775724709369995957496696]";
273 const JSON_OUT: &str = "[
274 0.000564,
275 1.0,
276 3.14159,
277 2.71828182845904523536028747135266249775724709369995957496696
278]";
279
280 test::setup();
281 let elem = json::parse(JSON_IN).unwrap();
282 let pretty = Pretty::new(&elem);
283
284 let s = format!("{pretty}");
285 assert_eq!(s, JSON_OUT);
286 let _ignored = serde_json::from_str::<serde_json::Value>(&s).unwrap();
287 }
288
289 #[test]
290 fn should_pretty_print_empty_object() {
291 const JSON_IN: &str = "{}";
292 const JSON_OUT: &str = "{
293}";
294
295 test::setup();
296 let elem = json::parse(JSON_IN).unwrap();
297 let pretty = Pretty::new(&elem);
298
299 let s = format!("{pretty}");
300 assert_eq!(s, JSON_OUT);
301 let _ignored = serde_json::from_str::<serde_json::Value>(&s).unwrap();
302 }
303
304 #[test]
305 fn should_pretty_print_object_with_bools() {
306 const JSON_IN: &str = r#"{"one":true,"two":false}"#;
307
308 const JSON_OUT: &str = r#"{
309 "one": true,
310 "two": false
311}"#;
312
313 test::setup();
314 let elem = json::parse(JSON_IN).unwrap();
315 let pretty = Pretty::new(&elem);
316
317 let s = format!("{pretty}");
318 assert_eq!(s, JSON_OUT);
319 let _ignored = serde_json::from_str::<serde_json::Value>(&s).unwrap();
320 }
321
322 #[test]
323 fn should_pretty_print_object_with_nulls() {
324 const JSON_IN: &str = r#"{"one":null,"two":null}"#;
325
326 const JSON_OUT: &str = r#"{
327 "one": null,
328 "two": null
329}"#;
330
331 test::setup();
332 let elem = json::parse(JSON_IN).unwrap();
333 let pretty = Pretty::new(&elem);
334
335 let s = format!("{pretty}");
336 assert_eq!(s, JSON_OUT);
337 let _ignored = serde_json::from_str::<serde_json::Value>(&s).unwrap();
338 }
339
340 #[test]
341 fn should_pretty_print_root_object() {
342 const JSON_IN: &str =
343 r#"{"one": 1,"two": ["a", "b", "c"],"three": {"d": 4,"e": 5,"f": 6},"four":null}"#;
344
345 const JSON_OUT: &str = r#"{
346 "one": 1,
347 "two": [
348 "a",
349 "b",
350 "c"
351 ],
352 "three": {
353 "d": 4,
354 "e": 5,
355 "f": 6
356 },
357 "four": null
358}"#;
359
360 test::setup();
361 let elem = json::parse(JSON_IN).unwrap();
362 let pretty = Pretty::new(&elem);
363
364 let s = format!("{pretty}");
365 assert_eq!(s, JSON_OUT);
366 let _ignored = serde_json::from_str::<serde_json::Value>(&s).unwrap();
367 }
368}