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