1use core::{iter, range::Range};
2
3use crate::{
4 Result,
5 ast::{Document, Value},
6 format::LineEnding,
7 tokens::{FALSE, NULL, TRUE},
8};
9
10const QUOTE_LEN: usize = 1;
11const QUOTE_PAIR_LEN: usize = QUOTE_LEN * 2;
12const COMMA_LEN: usize = 1;
13const COLON_LEN: usize = 1;
14const EXPONENT_MARKER_LEN: usize = 1;
15const BRACKET_PAIR_LEN: usize = 2;
16const BRACE_PAIR_LEN: usize = 2;
17
18type ObjectEntry = (Range<usize>, Value);
19
20#[derive(Debug, PartialEq, Eq, Clone, Copy)]
21pub(crate) struct FormatOptions {
22 key_val_delimiter: Option<(char, usize)>,
23 indent: Option<(char, usize)>,
24 line_ending: LineEnding,
25}
26
27impl FormatOptions {
28 pub(crate) fn prettify(line_ending: LineEnding) -> Self {
29 Self {
30 key_val_delimiter: Some((' ', 1)),
31 indent: Some((' ', 2)),
32 line_ending,
33 }
34 }
35}
36
37struct FormatBuf {
38 opts: FormatOptions,
39 buf: String,
40 line_start: usize,
41 preferred_width: usize,
42}
43
44impl FormatBuf {
45 fn new(buf: String, opts: FormatOptions, preferred_width: usize) -> Self {
46 Self {
47 opts,
48 buf,
49 line_start: 0,
50 preferred_width,
51 }
52 }
53
54 fn push(&mut self, value: char) {
55 self.buf.push(value);
56 }
57 fn push_str(&mut self, value: &str) {
58 self.buf.push_str(value);
59 }
60
61 #[inline]
62 fn push_quoted(&mut self, value: &str) {
63 self.push('"');
64 self.push_str(value);
65 self.push('"');
66 }
67
68 #[inline]
69 fn push_repeat(&mut self, c: char, count: usize) {
70 self.buf.extend(iter::repeat_n(c, count));
71 }
72
73 #[inline]
74 fn write_spec(&mut self, spec: Option<(char, usize)>) {
75 if let Some((c, size)) = spec {
76 self.push_repeat(c, size);
77 }
78 }
79
80 pub fn write_key_val_delimiter(&mut self) {
81 self.write_spec(self.opts.key_val_delimiter);
82 }
83
84 fn delimiter_len(&self) -> usize {
85 self.opts.key_val_delimiter.map_or(0, |(_, size)| size)
86 }
87
88 pub fn write_eol(&mut self) {
89 self.push_str(self.opts.line_ending.as_str());
90 self.line_start = self.buf.len();
91 }
92
93 pub fn write_indent(&mut self, level: usize) {
94 self.write_spec(self.opts.indent.map(|(c, size)| (c, size * level)));
95 }
96
97 fn into_inner(self) -> String {
98 self.buf
99 }
100
101 pub fn column(&self) -> usize {
102 self.buf.len() - self.line_start
103 }
104
105 fn available_bytes(&self) -> usize {
106 self.preferred_width.saturating_sub(self.column())
107 }
108}
109
110pub(crate) fn format_str(
111 json: &str,
112 options: FormatOptions,
113 preferred_width: usize,
114) -> Result<String> {
115 let mut buf = FormatBuf::new(String::with_capacity(json.len()), options, preferred_width);
116 let doc = Document::parse(json)?;
117 format_document_value_into(&mut buf, &doc, doc.root(), 0);
118 Ok(buf.into_inner())
119}
120
121use super::join_into;
122
123fn range_len(range: Range<usize>) -> usize {
124 range.end - range.start
125}
126
127fn number_len(mantissa: Range<usize>, exponent: Option<Range<usize>>) -> usize {
128 range_len(mantissa)
129 + if let Some(exponent) = exponent {
130 EXPONENT_MARKER_LEN + range_len(exponent)
131 } else {
132 0
133 }
134}
135
136fn format_document_value_into<S: AsRef<str>>(
137 buf: &mut FormatBuf,
138 doc: &Document<S>,
139 val: &Value,
140 depth: usize,
141) {
142 match val {
143 Value::Null(_) => buf.push_str(NULL),
144 Value::String(r) => buf.push_quoted(doc.slice(*r)),
145 Value::Number { mantissa, exponent } => {
146 buf.push_str(doc.slice(*mantissa));
147 if let Some(exponent) = exponent {
148 buf.push('e');
149 buf.push_str(doc.slice(*exponent));
150 }
151 }
152 Value::Object(_, entries) if entries.0.is_empty() => buf.push_str("{}"),
153 Value::Object(_, entries) => {
154 if !len::should_expand(doc, val, buf.available_bytes(), buf.delimiter_len()) {
155 compact_format_obj_into(buf, doc, entries.0.as_slice(), depth);
156 } else {
157 expanded_format_obj_into(buf, doc, entries.0.as_slice(), depth);
158 }
159 }
160 Value::Array(_, items) if items.is_empty() => buf.push_str("[]"),
161 Value::Array(_, items) => {
162 if !len::should_expand(doc, val, buf.available_bytes(), buf.delimiter_len()) {
163 compact_format_arr_into(buf, doc, items, depth);
164 } else if items.iter().all(|v| matches!(v, Value::Number { .. })) {
165 fill_format_arr_into(buf, doc, items, depth);
166 } else {
167 expanded_format_arr_into(buf, doc, items, depth);
168 }
169 }
170 Value::Boolean(_, b) => buf.push_str(if *b { TRUE } else { FALSE }),
171 }
172}
173
174fn expanded_format_arr_into<S: AsRef<str>>(
175 buf: &mut FormatBuf,
176 doc: &Document<S>,
177 items: &[Value],
178 depth: usize,
179) {
180 buf.push('[');
181 buf.write_eol();
182 join_into(
183 buf,
184 items,
185 |buf, val| {
186 buf.write_indent(depth + 1);
187 format_document_value_into(buf, doc, val, depth + 1);
188 },
189 |buf, _| {
190 buf.push(',');
191 buf.write_eol();
192 },
193 );
194 buf.write_eol();
195 buf.write_indent(depth);
196 buf.push(']');
197}
198
199fn expanded_format_obj_into<S: AsRef<str>>(
200 buf: &mut FormatBuf,
201 doc: &Document<S>,
202 entries: &[ObjectEntry],
203 depth: usize,
204) {
205 buf.push('{');
206 buf.write_eol();
207 join_into(
208 buf,
209 entries.iter(),
210 |buf, (key_range, val)| {
211 buf.write_indent(depth + 1);
212 write_object_entry_into(buf, doc, key_range, val, depth + 1);
213 },
214 |buf, _| {
215 buf.push(',');
216 buf.write_eol();
217 },
218 );
219 buf.write_eol();
220 buf.write_indent(depth);
221 buf.push('}');
222}
223
224fn fill_format_arr_into<S: AsRef<str>>(
225 buf: &mut FormatBuf,
226 doc: &Document<S>,
227 items: &[Value],
228 depth: usize,
229) {
230 buf.push('[');
231 buf.write_eol();
232 buf.write_indent(depth + 1);
233 for (i, item) in items.iter().enumerate() {
234 let Value::Number { mantissa, exponent } = item else {
235 unreachable!("fill_format_arr_into called with non-Number item");
236 };
237 let item_len = number_len(*mantissa, *exponent);
238 if i > 0 {
239 buf.push(',');
240 let trailing_comma_len = usize::from(i + 1 < items.len());
241 if item_len + COMMA_LEN + trailing_comma_len > buf.available_bytes() {
242 buf.write_eol();
243 buf.write_indent(depth + 1);
244 } else {
245 buf.write_key_val_delimiter();
246 }
247 }
248 format_document_value_into(buf, doc, item, depth + 1);
249 }
250 buf.write_eol();
251 buf.write_indent(depth);
252 buf.push(']');
253}
254
255fn compact_format_arr_into<S: AsRef<str>>(
256 buf: &mut FormatBuf,
257 doc: &Document<S>,
258 items: &[Value],
259 depth: usize,
260) {
261 buf.push('[');
262 join_into(
263 buf,
264 items,
265 |buf, val| format_document_value_into(buf, doc, val, depth + 1),
266 |buf, _| {
267 buf.push(',');
268 buf.write_key_val_delimiter();
269 },
270 );
271 buf.push(']');
272}
273
274fn write_object_entry_into<S: AsRef<str>>(
275 buf: &mut FormatBuf,
276 doc: &Document<S>,
277 key_range: &Range<usize>,
278 val: &Value,
279 depth: usize,
280) {
281 buf.push_quoted(doc.slice(*key_range));
282 buf.push(':');
283 buf.write_key_val_delimiter();
284 format_document_value_into(buf, doc, val, depth);
285}
286
287fn compact_format_obj_into<S: AsRef<str>>(
288 buf: &mut FormatBuf,
289 doc: &Document<S>,
290 entries: &[ObjectEntry],
291 depth: usize,
292) {
293 buf.push('{');
294 buf.write_key_val_delimiter();
295 join_into(
296 buf,
297 entries.iter(),
298 |buf, (key_range, val)| write_object_entry_into(buf, doc, key_range, val, depth + 1),
299 |buf, _| {
300 buf.push(',');
301 buf.write_key_val_delimiter();
302 },
303 );
304 buf.write_key_val_delimiter();
305 buf.push('}');
306}
307
308fn format_document<S: AsRef<str>>(
309 doc: &Document<S>,
310 options: &FormatOptions,
311 preferred_width: usize,
312) -> String {
313 let mut buf = FormatBuf::new(String::new(), *options, preferred_width);
314 format_document_value_into(&mut buf, doc, doc.root(), 0);
315 buf.into_inner()
316}
317
318pub fn prettify_str(json: &str, preferred_width: usize, line_ending: LineEnding) -> Result<String> {
319 format_str(json, FormatOptions::prettify(line_ending), preferred_width)
320}
321
322pub fn prettify_document<S: AsRef<str>>(
323 doc: &Document<S>,
324 preferred_width: usize,
325 line_ending: LineEnding,
326) -> String {
327 format_document(doc, &FormatOptions::prettify(line_ending), preferred_width)
328}
329
330pub fn prettify_document_into<S: AsRef<str>>(
331 buf: &mut String,
332 doc: &Document<S>,
333 preferred_width: usize,
334 line_ending: LineEnding,
335) {
336 let mut fmt_buf = FormatBuf::new(
337 std::mem::take(buf),
338 FormatOptions::prettify(line_ending),
339 preferred_width,
340 );
341 format_document_value_into(&mut fmt_buf, doc, doc.root(), 0);
342 *buf = fmt_buf.into_inner();
343}
344
345mod len {
346 use super::{
347 BRACE_PAIR_LEN, BRACKET_PAIR_LEN, COLON_LEN, COMMA_LEN, QUOTE_PAIR_LEN, number_len,
348 };
349 use crate::{
350 ast::Value,
351 tokens::{FALSE, NULL, TRUE},
352 };
353
354 pub fn should_expand<S: AsRef<str>>(
356 doc: &crate::ast::Document<S>,
357 val: &Value,
358 limit: usize,
359 delimiter_len: usize,
360 ) -> bool {
361 try_get_value_len(doc, val, limit, delimiter_len).is_none()
362 }
363
364 fn quoted_len(value: &str) -> usize {
365 value.chars().count() + QUOTE_PAIR_LEN
366 }
367
368 fn separated_item_prefix_len(delimiter_len: usize) -> usize {
369 COMMA_LEN + delimiter_len
370 }
371
372 fn try_get_value_len<S: AsRef<str>>(
373 doc: &crate::ast::Document<S>,
374 val: &Value,
375 limit: usize,
376 delimiter_len: usize,
377 ) -> Option<usize> {
378 fn within_limit(len: usize, limit: usize) -> bool {
379 len <= limit
380 }
381
382 let len = match val {
383 Value::Null(_) => NULL.len(),
384 Value::String(r) => quoted_len(doc.slice(*r)),
385 Value::Number { mantissa, exponent } => number_len(*mantissa, *exponent),
386 Value::Object(_, entries) => {
387 if entries.is_empty() {
388 BRACE_PAIR_LEN
389 } else {
390 let braces_len = BRACE_PAIR_LEN + (delimiter_len * 2);
391 let mut sum = braces_len;
392 for (i, (key_range, value)) in entries.0.iter().enumerate() {
393 if !within_limit(sum, limit) {
394 break;
395 }
396 if i > 0 {
397 sum += separated_item_prefix_len(delimiter_len);
398 }
399 sum += quoted_len(doc.slice(*key_range));
400 sum += COLON_LEN + delimiter_len;
401 let remaining = limit.saturating_sub(sum);
402 let len = try_get_value_len(doc, value, remaining, delimiter_len)?;
403 sum += len;
404 }
405 sum
406 }
407 }
408 Value::Array(_, values) => {
409 let brackets_len = BRACKET_PAIR_LEN;
410 let mut sum = brackets_len;
411 for (i, value) in values.iter().enumerate() {
412 if !within_limit(sum, limit) {
413 break;
414 }
415 if i > 0 {
416 sum += separated_item_prefix_len(delimiter_len);
417 }
418 let remaining = limit.saturating_sub(sum);
419 let len = try_get_value_len(doc, value, remaining, delimiter_len)?;
420 sum += len;
421 }
422 sum
423 }
424 Value::Boolean(_, b) => {
425 if *b {
426 TRUE.len()
427 } else {
428 FALSE.len()
429 }
430 }
431 };
432
433 within_limit(len, limit).then_some(len)
434 }
435}