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