1use crate::json_node::JsonNode;
22use crate::types::LineEnding;
23
24pub fn format(node: &JsonNode) -> String {
36 let opts = crate::settings::get_settings().format;
37 format_impl(node, 0, &opts)
38}
39
40pub fn minify(node: &JsonNode) -> String {
50 minify_impl(node)
51}
52
53fn get_indent(level: usize, opts: &crate::types::FormatOptions) -> String {
65 if level == 0 {
66 return String::new();
67 }
68 if opts.use_tabs {
69 "\t".repeat(level)
70 } else {
71 " ".repeat(level * opts.indent as usize)
72 }
73}
74
75fn get_line_ending(opts: &crate::types::FormatOptions) -> &'static str {
82 match opts.line_ending {
83 LineEnding::LF => "\n",
84 LineEnding::CRLF => "\r\n",
85 }
86}
87
88fn format_inline(node: &JsonNode, opts: &crate::types::FormatOptions) -> Option<String> {
100 match node {
101 JsonNode::Null(_) => Some("null".to_string()),
102 JsonNode::Bool(b, _) => Some(if *b { "true" } else { "false" }.to_string()),
103 JsonNode::Number(_, raw, _) => Some(raw.clone()),
104 JsonNode::String(s, _) => Some(serde_json::to_string(s).unwrap()),
105 JsonNode::Array(elements, _) => {
106 if elements.is_empty() {
107 return Some("[]".to_string());
108 }
109 let mut parts = Vec::with_capacity(elements.len());
110 for elem in elements {
111 let part = format_inline(elem, opts)?;
112 parts.push(part);
113 }
114 let comma_space = if opts.space_after_comma { ", " } else { "," };
115 let joined = parts.join(comma_space);
116 let result = format!("[{}]", joined);
117 if result.len() <= 80 {
119 Some(result)
120 } else {
121 None
122 }
123 }
124 JsonNode::Object(pairs, _) => {
125 if pairs.is_empty() {
126 return Some("{}".to_string());
127 }
128 let mut pairs = pairs.clone();
129 if opts.sort_keys {
130 pairs.sort_by(|a, b| a.key.cmp(&b.key));
131 }
132 let mut parts = Vec::with_capacity(pairs.len());
133 for pair in &pairs {
134 let val_str = format_inline(&pair.value, opts)?;
135 let key_str = serde_json::to_string(&pair.key).unwrap();
136 let colon_space = if opts.space_after_colon { ": " } else { ":" };
137 parts.push(format!("{}{}{}", key_str, colon_space, val_str));
138 }
139 let comma_space = if opts.space_after_comma { ", " } else { "," };
140 let joined = parts.join(comma_space);
141 let result = format!("{{{}}}", joined);
142 if result.len() <= 80 {
144 Some(result)
145 } else {
146 None
147 }
148 }
149 }
150}
151
152fn format_impl(node: &JsonNode, level: usize, opts: &crate::types::FormatOptions) -> String {
164 match node {
165 JsonNode::Null(_) => "null".to_string(),
166 JsonNode::Bool(b, _) => if *b { "true" } else { "false" }.to_string(),
167 JsonNode::Number(_, raw, _) => raw.clone(),
168 JsonNode::String(s, _) => serde_json::to_string(s).unwrap(),
169 JsonNode::Array(elements, _) => {
170 if elements.is_empty() {
171 return "[]".to_string();
172 }
173 if let (crate::types::FoldingStyle::Compact, Some(inline)) =
174 (&opts.folding_style, format_inline(node, opts))
175 {
176 return inline;
177 }
178
179 let line_ending = get_line_ending(opts);
180 let next_indent = get_indent(level + 1, opts);
181 let current_indent = get_indent(level, opts);
182
183 let mut result = String::new();
184 result.push('[');
185 result.push_str(line_ending);
186
187 for (i, elem) in elements.iter().enumerate() {
188 result.push_str(&next_indent);
189 result.push_str(&format_impl(elem, level + 1, opts));
190 if i + 1 < elements.len() {
191 result.push(',');
192 }
193 result.push_str(line_ending);
194 }
195
196 result.push_str(¤t_indent);
197 result.push(']');
198 result
199 }
200 JsonNode::Object(pairs, _) => {
201 if pairs.is_empty() {
202 return "{}".to_string();
203 }
204 if let (crate::types::FoldingStyle::Compact, Some(inline)) =
205 (&opts.folding_style, format_inline(node, opts))
206 {
207 return inline;
208 }
209
210 let mut pairs = pairs.clone();
211 if opts.sort_keys {
212 pairs.sort_by(|a, b| a.key.cmp(&b.key));
213 }
214
215 let line_ending = get_line_ending(opts);
216 let next_indent = get_indent(level + 1, opts);
217 let current_indent = get_indent(level, opts);
218 let colon_str = if opts.space_after_colon { ": " } else { ":" };
219
220 let mut result = String::new();
221 result.push('{');
222 result.push_str(line_ending);
223
224 for (i, pair) in pairs.iter().enumerate() {
225 result.push_str(&next_indent);
226 result.push_str(&serde_json::to_string(&pair.key).unwrap());
227 result.push_str(colon_str);
228 result.push_str(&format_impl(&pair.value, level + 1, opts));
229 if i + 1 < pairs.len() {
230 result.push(',');
231 }
232 result.push_str(line_ending);
233 }
234
235 result.push_str(¤t_indent);
236 result.push('}');
237 result
238 }
239 }
240}
241
242fn minify_impl(node: &JsonNode) -> String {
252 match node {
253 JsonNode::Null(_) => "null".to_string(),
254 JsonNode::Bool(b, _) => if *b { "true" } else { "false" }.to_string(),
255 JsonNode::Number(_, raw, _) => raw.clone(),
256 JsonNode::String(s, _) => serde_json::to_string(s).unwrap(),
257 JsonNode::Array(elements, _) => {
258 let mut parts = Vec::with_capacity(elements.len());
259 for elem in elements {
260 parts.push(minify_impl(elem));
261 }
262 format!("[{}]", parts.join(","))
263 }
264 JsonNode::Object(pairs, _) => {
265 let mut parts = Vec::with_capacity(pairs.len());
266 for pair in pairs {
267 let val_str = minify_impl(&pair.value);
268 let key_str = serde_json::to_string(&pair.key).unwrap();
269 parts.push(format!("{}:{}", key_str, val_str));
270 }
271 format!("{{{}}}", parts.join(","))
272 }
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279 use crate::parser::parse;
280 use crate::types::{FoldingStyle, FormatOptions};
281 use std::sync::Mutex;
282
283 static TEST_LOCK: Mutex<()> = Mutex::new(());
284
285 fn with_temporary_settings<F, R>(opts: FormatOptions, f: F) -> R
286 where
287 F: FnOnce() -> R,
288 {
289 let mut app_settings = crate::settings::get_settings();
290 let original_format = app_settings.format;
291 app_settings.format = opts;
292 crate::settings::update_settings(app_settings).unwrap();
293 let result = f();
294 app_settings.format = original_format;
295 crate::settings::update_settings(app_settings).unwrap();
296 result
297 }
298
299 #[test]
300 fn test_format_primitives() {
301 let _guard = TEST_LOCK.lock().unwrap();
302 let null_node = parse("null").unwrap();
303 assert_eq!(format(&null_node), "null");
304 assert_eq!(minify(&null_node), "null");
305
306 let true_node = parse("true").unwrap();
307 assert_eq!(format(&true_node), "true");
308 assert_eq!(minify(&true_node), "true");
309
310 let false_node = parse("false").unwrap();
311 assert_eq!(format(&false_node), "false");
312 assert_eq!(minify(&false_node), "false");
313
314 let num_node = parse("123.45e-2").unwrap();
315 assert_eq!(format(&num_node), "123.45e-2");
316 assert_eq!(minify(&num_node), "123.45e-2");
317
318 let str_node = parse("\"hello \\u263a world\"").unwrap();
319 assert_eq!(format(&str_node), "\"hello ☺ world\"");
320 assert_eq!(minify(&str_node), "\"hello ☺ world\"");
321 }
322
323 #[test]
324 fn test_format_empty() {
325 let _guard = TEST_LOCK.lock().unwrap();
326 let empty_arr = parse("[]").unwrap();
327 assert_eq!(format(&empty_arr), "[]");
328 assert_eq!(minify(&empty_arr), "[]");
329
330 let empty_obj = parse("{}").unwrap();
331 assert_eq!(format(&empty_obj), "{}");
332 assert_eq!(minify(&empty_obj), "{}");
333 }
334
335 #[test]
336 fn test_format_expanded_array() {
337 let _guard = TEST_LOCK.lock().unwrap();
338 let input = "[1,true,null]";
339 let node = parse(input).unwrap();
340 let expected = "[\n 1,\n true,\n null\n]";
341 assert_eq!(format(&node), expected);
342 assert_eq!(minify(&node), "[1,true,null]");
343 }
344
345 #[test]
346 fn test_format_expanded_object() {
347 let _guard = TEST_LOCK.lock().unwrap();
348 let input = "{\"a\":1,\"b\":true}";
349 let node = parse(input).unwrap();
350 let expected = "{\n \"a\": 1,\n \"b\": true\n}";
351 assert_eq!(format(&node), expected);
352 assert_eq!(minify(&node), "{\"a\":1,\"b\":true}");
353 }
354
355 #[test]
356 fn test_format_compact_folding() {
357 let _guard = TEST_LOCK.lock().unwrap();
358 let opts = FormatOptions {
359 folding_style: FoldingStyle::Compact,
360 ..Default::default()
361 };
362 let input = "[1,true,null]";
363 let node = parse(input).unwrap();
364 let res = with_temporary_settings(opts, || format(&node));
365 assert_eq!(res, "[1, true, null]");
366
367 let obj_input = "{\"a\":1,\"b\":true}";
368 let obj_node = parse(obj_input).unwrap();
369 let res_obj = with_temporary_settings(opts, || format(&obj_node));
370 assert_eq!(res_obj, "{\"a\": 1, \"b\": true}");
371 }
372
373 #[test]
374 fn test_format_sort_keys() {
375 let _guard = TEST_LOCK.lock().unwrap();
376 let opts = FormatOptions {
377 sort_keys: true,
378 ..Default::default()
379 };
380 let input = "{\"z\":1,\"a\":true}";
381 let node = parse(input).unwrap();
382 let expected = "{\n \"a\": true,\n \"z\": 1\n}";
383 let res = with_temporary_settings(opts, || format(&node));
384 assert_eq!(res, expected);
385 }
386}