1use std::fmt::Write;
2
3use super::{jsonparser::JsonEntryValue, JsonFixerConfig, JsonFixerError, jsonparser::JsonValue};
4
5#[derive(Debug, Clone)]
6pub enum IndentStyle {
7 Spaces,
8 Tabs,
9}
10
11impl IndentStyle {
12 fn with_size(&self, size: Option<usize>) -> String {
13 match self {
14 Self::Spaces => " ".repeat(size.unwrap_or(0)),
15 Self::Tabs => "\t".to_string(),
16 }
17 }
18}
19
20pub trait Formatter {
21 fn format(&self, value: &JsonValue, config: &JsonFixerConfig)
22 -> Result<String, JsonFixerError>;
23}
24
25pub struct JsonFormatter;
26
27impl Formatter for JsonFormatter {
28 fn format(
29 &self,
30 value: &JsonValue,
31 config: &JsonFixerConfig,
32 ) -> Result<String, JsonFixerError> {
33 let mut output = String::new();
34 self.format_value(value, &mut output, 0, config)?;
35 Ok(output)
36 }
37}
38
39impl JsonFormatter {
40 fn format_value(
41 &self,
42 value: &JsonValue,
43 output: &mut String,
44 depth: usize,
45 config: &JsonFixerConfig,
46 ) -> Result<(), JsonFixerError> {
47 match value {
48 JsonValue::Null => output.push_str("null"),
49 JsonValue::Boolean(b) => output.push_str(if *b { "true" } else { "false" }),
50 JsonValue::Number(n) => write!(output, "{}", n).map_err(|e| JsonFixerError::IO(e))?,
51 JsonValue::String(s) => {
52 output.push('"');
53 output.push_str(s);
55 output.push('"');
56 }
57 JsonValue::Array(arr) => {
58 if config.preserve() {
59 self.format_array_preserved(arr, output, depth, config)?;
60 } else {
61 self.format_array(arr, output, depth, config)?;
62 }
63 }
64 JsonValue::Object(obj) => {
65 if config.preserve() {
66 self.format_object_preserved(obj, output, depth, config)?;
67 } else {
68 self.format_object(obj, output, depth, config)?;
69 }
70 }
71 JsonValue::Space(sp) => write!(output, "{}", sp).map_err(|e| JsonFixerError::IO(e))?,
72 }
73 Ok(())
74 }
75 fn _escaped_string(&self, output: &mut String, s: &str) -> Result<(), JsonFixerError> {
76 for c in s.chars() {
77 match c {
78 '"' => output.push_str("\\\""),
79 '\\' => output.push_str("\\\\"),
80 '\n' => output.push_str("\\n"),
81 '\r' => output.push_str("\\r"),
82 '\t' => output.push_str("\\t"),
83 '\u{0008}' => output.push_str("\\b"),
84 '\u{000C}' => output.push_str("\\f"),
85 c if c.is_control() => {
86 write!(output, "\\u{:04x}", c as u32).map_err(|e| JsonFixerError::IO(e))?
87 }
88 c => output.push(c),
89 }
90 }
91 Ok(())
92 }
93
94 fn write_newline(
95 &self,
96 output: &mut String,
97 _depth: usize,
98 _config: &JsonFixerConfig,
99 ) -> Result<(), JsonFixerError> {
100 output.push('\n');
101 Ok(())
102 }
103
104 fn write_indent(
105 &self,
106 output: &mut String,
107 depth: usize,
108 config: &JsonFixerConfig,
109 ) -> Result<(), JsonFixerError> {
110 let indent = config.indent_style.with_size(Some(config.indent_size));
111
112 for _ in 0..depth {
113 output.push_str(&indent);
114 }
115
116 Ok(())
117 }
118
119 fn format_array(
120 &self,
121 arr: &[JsonEntryValue],
122 output: &mut String,
123 depth: usize,
124 config: &JsonFixerConfig,
125 ) -> Result<(), JsonFixerError> {
126 if arr.is_empty() {
127 output.push_str("[]");
128 return Ok(());
129 }
130
131 output.push('[');
132 if config.beautify() {
133 self.write_newline(output, depth + 1, config)?;
134 }
135 if config.space_between() {
136 output.push(' ');
137 }
138
139 for (i, entry) in arr.iter().enumerate() {
140 if entry.value.is_some() {
141 if i > 0 {
142 output.push(',');
143 if config.beautify() {
144 self.write_newline(output, depth + 1, config)?;
145 }
146 if config.space_between() {
147 output.push(' ');
148 }
149 }
150 if config.beautify() {
151 self.write_indent(output, depth + 1, config)?;
152 }
153 self.format_value(&entry.get_value(), output, depth + 1, config)?;
154 }
155 }
156 if config.beautify() {
157 self.write_newline(output, depth, config)?;
158 self.write_indent(output, depth, config)?;
159 }
160 if config.space_between() {
161 output.push(' ');
162 }
163
164 output.push(']');
165 Ok(())
166 }
167
168 fn format_array_preserved(
169 &self,
170 arr: &[JsonEntryValue],
171 output: &mut String,
172 depth: usize,
173 config: &JsonFixerConfig,
174 ) -> Result<(), JsonFixerError> {
175 if arr.is_empty() {
176 output.push_str("[]");
177 return Ok(());
178 }
179
180 output.push('[');
181
182 for (i, entry) in arr.iter().enumerate() {
183 if i > 0 && entry.value.is_some() {
184 output.push(',');
185 }
186
187 output.push_str(&entry.get_sp_bf_val());
188
189 if entry.value.is_some() {
190 self.format_value(&entry.get_value(), output, depth + 1, config)?;
191 }
192 output.push_str(&entry.get_sp_af_val());
193 }
194
195 output.push(']');
196 Ok(())
197 }
198
199 fn format_object(
200 &self,
201 obj: &Vec<JsonEntryValue>,
202 output: &mut String,
203 depth: usize,
204 config: &JsonFixerConfig,
205 ) -> Result<(), JsonFixerError> {
206 let mut entries = obj.to_vec();
207 entries.retain(|val| val.value.is_some());
208
209 if entries.is_empty() {
210 output.push_str("{}");
211 return Ok(());
212 }
213
214 output.push('{');
215 if config.beautify() {
216 self.write_newline(output, depth + 1, config)?;
217 }
218
219 if config.sort_keys {
220 entries.sort_by(|a, b| a.key.cmp(&b.key));
221 }
222
223 if config.space_between() {
224 output.push(' ');
225 }
226
227 for (i, entry) in entries.iter().enumerate() {
228 if i > 0 {
229 output.push(',');
230 if config.beautify() {
231 self.write_newline(output, depth + 1, config)?;
232 }
233 if config.space_between() {
234 output.push(' ');
235 }
236 }
237
238 if config.beautify() {
239 self.write_indent(output, depth + 1, config)?;
240 }
241
242 output.push('"');
243 output.push_str(&entry.get_key());
245 output.push('"');
246
247 output.push(':');
248
249 if config.space_between() || config.beautify() {
250 output.push(' ');
251 }
252
253 self.format_value(&entry.get_value(), output, depth + 1, config)?;
254 }
255
256 if config.beautify() {
257 self.write_newline(output, depth, config)?;
258 self.write_indent(output, depth, config)?;
259 }
260
261 if config.space_between() {
262 output.push(' ');
263 }
264
265 output.push('}');
266
267 Ok(())
268 }
269
270 fn format_object_preserved(
271 &self,
272 obj: &Vec<JsonEntryValue>,
273 output: &mut String,
274 depth: usize,
275 config: &JsonFixerConfig,
276 ) -> Result<(), JsonFixerError> {
277 let entries = self.clean_middle_spaces_and_sort(&obj, config);
278 if entries.is_empty() {
279 output.push_str("{}");
280 return Ok(());
281 }
282
283 output.push('{');
284
285 for (_i, entry) in entries.iter().enumerate() {
286 if entry.value.is_none() {
288 output.push_str(&entry.get_sp_bf_key());
289 output.push_str(&entry.get_sp_af_key());
290
291 continue;
292 } else {
293 output.push_str(&entry.get_sp_bf_key());
294
295 output.push('"');
296 output.push_str(&entry.get_key());
297 output.push('"');
299
300 output.push_str(&entry.get_sp_af_key());
301
302 output.push(':');
303
304 output.push_str(&entry.get_sp_bf_val());
305
306 self.format_value(&entry.get_value(), output, depth + 1, config)?;
307 let last_space = entry.get_sp_af_val();
308
309 if last_space.contains('\n') {
310 output.push(',');
311 output.push_str(&last_space);
312 } else {
313 output.push_str(&last_space);
314 output.push(',');
315 }
316 }
317 }
318
319 let found = output
320 .chars()
321 .rev()
322 .enumerate()
323 .find(|(_i, ch)| !ch.is_whitespace());
324 if found.is_some() {
325 let (i, ch) = found.unwrap();
326 if ch == ',' {
327 output.remove(output.len() - i - 1);
328 }
329 }
330
331 output.push('}');
332
333 Ok(())
334 }
335
336 fn clean_middle_spaces_and_sort(
337 &self,
338 obj: &Vec<JsonEntryValue>,
339 config: &JsonFixerConfig,
340 ) -> Vec<JsonEntryValue> {
341 let first_whitespaces = obj.first();
343 let mut last_whitespaces: Option<&JsonEntryValue> = None;
344 if obj.len() > 1 {
345 last_whitespaces = obj.last();
346 }
347
348 let mut cleaned_obj = obj.to_vec();
350 cleaned_obj.retain(|entry| entry.value.is_some());
351
352 if config.sort_keys {
354 cleaned_obj.sort_by(|a, b| {
355 let key_a = a.get_key();
356 let key_b = b.get_key();
357 key_a.cmp(&key_b)
358 });
359 }
360
361 if let Some(entry) = first_whitespaces {
362 if entry.value.is_none() {
363 cleaned_obj.insert(0, entry.clone());
364 }
365 }
366
367 if let Some(entry) = last_whitespaces {
368 if entry.value.is_none() {
369 cleaned_obj.push(entry.clone());
370 }
371 }
372
373 cleaned_obj
374 }
375}