ocpi_tariffs/json/
write.rs1#[cfg(test)]
4mod test_tree_writer;
5
6use std::fmt::{self, Write as _};
7
8use super::{Document, 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 from_doc(doc: &'a Document<'buf>) -> Self {
29 Pretty { elem: doc.root() }
30 }
31}
32
33impl fmt::Display for Pretty<'_, '_> {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 let mut stack = vec![State::Root(self.elem)];
36 let mut write_comma = WriteComma::Skip;
37
38 loop {
40 let elem = loop {
42 let depth = stack.len();
43
44 let Some(mut state) = stack.pop() else {
45 return Ok(());
47 };
48
49 let elem = match &mut state {
50 State::Root(elem) => {
51 write_elem(elem, f)?;
55 elem
56 }
57 State::Array(iter) => {
58 let Some(elem) = iter.next() else {
59 let Some(depth) = depth.checked_sub(1) else {
62 return Ok(());
63 };
64 write_nl_and_indent(depth, f)?;
65 f.write_char(ARRAY_CLOSE)?;
66 continue;
67 };
68
69 if let WriteComma::Write = write_comma {
70 f.write_char(COMMA)?;
71 }
72 write_nl_and_indent(depth, f)?;
73 write_comma = write_elem(elem, f)?;
74 elem
75 }
76 State::Object(iter) => {
77 let Some(field) = iter.next() else {
78 let Some(depth) = depth.checked_sub(1) else {
81 return Ok(());
82 };
83 write_nl_and_indent(depth, f)?;
84 f.write_char(OBJECT_CLOSE)?;
85 continue;
86 };
87
88 if let WriteComma::Write = write_comma {
89 f.write_char(COMMA)?;
90 }
91 write_nl_and_indent(depth, f)?;
92 write_comma = write_field(field, f)?;
93 field.element()
94 }
95 };
96
97 match &state {
98 State::Array(_) | State::Object(_) => stack.push(state),
99 State::Root(_) => (),
100 }
101
102 break elem;
103 };
104
105 if let Value::Array(elements) = elem.value() {
106 stack.push(State::Array(elements.iter()));
107 } else if let Value::Object(fields) = elem.value() {
108 stack.push(State::Object(fields.iter()));
109 }
110 }
111 }
112}
113
114#[derive(Debug)]
116enum State<'a, 'buf> {
117 Root(&'a Element<'buf>),
119
120 Array(std::slice::Iter<'a, Element<'buf>>),
122
123 Object(std::slice::Iter<'a, Field<'buf>>),
125}
126
127fn write_nl_and_indent(depth: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 f.write_char(NEWLINE)?;
130
131 for _ in 0..depth {
132 f.write_str(TAB)?;
133 }
134
135 Ok(())
136}
137
138#[derive(Copy, Clone)]
140enum WriteComma {
141 Write,
143
144 Skip,
146}
147
148fn write_elem(elem: &Element<'_>, f: &mut fmt::Formatter<'_>) -> Result<WriteComma, fmt::Error> {
153 match elem.value() {
154 Value::Null => {
155 f.write_str("null")?;
156 Ok(WriteComma::Write)
157 }
158 Value::True => {
159 f.write_str("true")?;
160 Ok(WriteComma::Write)
161 }
162 Value::False => {
163 f.write_str("false")?;
164 Ok(WriteComma::Write)
165 }
166 Value::String(s) => {
167 write!(f, "\"{}\"", s.as_unescaped_str())?;
168 Ok(WriteComma::Write)
169 }
170 Value::Number(n) => {
171 write!(f, "{n}")?;
172 Ok(WriteComma::Write)
173 }
174 Value::Array(_) => {
175 f.write_char(ARRAY_OPEN)?;
176 Ok(WriteComma::Skip)
177 }
178 Value::Object(_) => {
179 f.write_char(OBJECT_OPEN)?;
180 Ok(WriteComma::Skip)
181 }
182 }
183}
184
185fn write_field(field: &Field<'_>, f: &mut fmt::Formatter<'_>) -> Result<WriteComma, fmt::Error> {
187 write!(f, "\"{}\": ", field.key().as_unescaped_str())?;
188 write_elem(field.element(), f)
189}