ocpi_tariffs/json/
write.rs1#[cfg(test)]
4mod test_tree_writer;
5
6use std::fmt::{self, Write};
7
8use super::{Element, Field, Value};
9
10const TAB: &str = " ";
11const ARRAY_OPEN: char = '[';
12const ARRAY_CLOSE: char = ']';
13const OBJECT_OPEN: char = '{';
14const OBJECT_CLOSE: char = '}';
15const COMMA: char = ',';
16const NEWLINE: char = '\n';
17
18pub struct Pretty<'a, 'buf> {
23 elem: &'a Element<'buf>,
24}
25
26impl<'a, 'buf> Pretty<'a, 'buf> {
27 pub fn new(elem: &'a Element<'buf>) -> Self {
28 Pretty { elem }
29 }
30}
31
32impl fmt::Display for Pretty<'_, '_> {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 let mut stack = vec![State::Root(self.elem)];
35 let mut write_comma = WriteComma::Skip;
36
37 loop {
39 let elem = loop {
41 let depth = stack.len();
42
43 let Some(mut state) = stack.pop() else {
44 return Ok(());
46 };
47
48 let elem = match &mut state {
49 State::Root(elem) => {
50 write_elem(elem, f)?;
54 elem
55 }
56 State::Array(iter) => {
57 let Some(elem) = iter.next() else {
58 let Some(depth) = depth.checked_sub(1) else {
61 return Ok(());
62 };
63 write_nl_and_indent(depth, f)?;
64 f.write_char(ARRAY_CLOSE)?;
65 continue;
66 };
67
68 if let WriteComma::Write = write_comma {
69 f.write_char(COMMA)?;
70 }
71 write_nl_and_indent(depth, f)?;
72 write_comma = write_elem(elem, f)?;
73 elem
74 }
75 State::Object(iter) => {
76 let Some(field) = iter.next() else {
77 let Some(depth) = depth.checked_sub(1) else {
80 return Ok(());
81 };
82 write_nl_and_indent(depth, f)?;
83 f.write_char(OBJECT_CLOSE)?;
84 continue;
85 };
86
87 if let WriteComma::Write = write_comma {
88 f.write_char(COMMA)?;
89 }
90 write_nl_and_indent(depth, f)?;
91 write_comma = write_field(field, f)?;
92 field.element()
93 }
94 };
95
96 match &state {
97 State::Array(_) | State::Object(_) => stack.push(state),
98 State::Root(_) => (),
99 }
100
101 break elem;
102 };
103
104 match elem.value() {
105 Value::Array(elements) => stack.push(State::Array(elements.iter())),
106 Value::Object(fields) => stack.push(State::Object(fields.iter())),
107 _ => (),
108 }
109 }
110 }
111}
112
113#[derive(Debug)]
115enum State<'a, 'buf> {
116 Root(&'a Element<'buf>),
118
119 Array(std::slice::Iter<'a, Element<'buf>>),
121
122 Object(std::slice::Iter<'a, Field<'buf>>),
124}
125
126fn write_nl_and_indent(depth: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 f.write_char(NEWLINE)?;
129
130 for _ in 0..depth {
131 f.write_str(TAB)?;
132 }
133
134 Ok(())
135}
136
137#[derive(Copy, Clone)]
139enum WriteComma {
140 Write,
142
143 Skip,
145}
146
147fn write_elem(elem: &Element<'_>, f: &mut fmt::Formatter<'_>) -> Result<WriteComma, fmt::Error> {
152 match elem.value() {
153 Value::Null => {
154 f.write_str("null")?;
155 Ok(WriteComma::Write)
156 }
157 Value::True => {
158 f.write_str("true")?;
159 Ok(WriteComma::Write)
160 }
161 Value::False => {
162 f.write_str("false")?;
163 Ok(WriteComma::Write)
164 }
165 Value::String(s) => {
166 write!(f, "\"{s}\"")?;
167 Ok(WriteComma::Write)
168 }
169 Value::Number(n) => {
170 write!(f, "{n}")?;
171 Ok(WriteComma::Write)
172 }
173 Value::Array(_) => {
174 f.write_char(ARRAY_OPEN)?;
175 Ok(WriteComma::Skip)
176 }
177 Value::Object(_) => {
178 f.write_char(OBJECT_OPEN)?;
179 Ok(WriteComma::Skip)
180 }
181 }
182}
183
184fn write_field(field: &Field<'_>, f: &mut fmt::Formatter<'_>) -> Result<WriteComma, fmt::Error> {
186 write!(f, "\"{}\": ", field.key())?;
187 write_elem(field.element(), f)
188}