json_fixer/jsonfixer/
jsonformatter.rs

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                //self.escaped_string(output, &s.replace('"', "\\\""))?;
54                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            //self.escaped_string(output, &entry.clone().key.unwrap())?;
244            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            //println!("Entry {i}: {:?}", entry);
287            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                //self.escaped_string(output, &entry.clone().key.unwrap())?;
298                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        // Keep first and last whitespaces
342        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        // Remove all wihtespaces
349        let mut cleaned_obj = obj.to_vec();
350        cleaned_obj.retain(|entry| entry.value.is_some());
351
352        // Sort the cleaned obj entries
353        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}