char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somrearrange libs into lib and extensions into extsf4a0f5b

main
3.3 KiB110 linesraw
1use serde_json::Value;
2
3use crate::{Error, syntax};
4
5const WIDTH: usize = 80;
6
7pub(crate) fn key(text: &str) -> String {
8    let mut chars = text.chars();
9    if chars
10        .next()
11        .is_some_and(|ch| ch.is_ascii_alphabetic() || ch == '_' || ch == '$')
12        && chars.all(|ch| ch.is_ascii_alphanumeric() || ch == '_' || ch == '$')
13    {
14        text.to_owned()
15    } else {
16        serde_json::to_string(text).unwrap()
17    }
18}
19
20pub(crate) fn print(
21    value: &Value,
22    implicit: bool,
23    options: &crate::Options,
24) -> Result<String, Error> {
25    check_depth(value, 0, options.max_depth)?;
26    let mut output = String::new();
27    write(value, implicit, 0, &mut output);
28    Ok(output)
29}
30
31fn check_depth(value: &Value, depth: usize, limit: usize) -> Result<(), Error> {
32    if matches!(value, Value::Array(_) | Value::Object(_)) && depth > limit {
33        return Err(syntax::nesting_limit(0..0, limit));
34    }
35    match value {
36        Value::Array(items) => {
37            for value in items {
38                check_depth(value, depth + 1, limit)?;
39            }
40        }
41        Value::Object(map) => {
42            for value in map.values() {
43                check_depth(value, depth + 1, limit)?;
44            }
45        }
46        _ => {}
47    }
48    Ok(())
49}
50
51fn flat(value: &Value) -> Option<String> {
52    match value {
53        Value::Object(map) if map.is_empty() => Some("{}".into()),
54        Value::Array(items) if items.is_empty() => Some("[]".into()),
55        Value::Object(_) | Value::Array(_) => None,
56        _ => Some(serde_json::to_string(value).unwrap()),
57    }
58}
59
60fn write(value: &Value, implicit: bool, indent: usize, output: &mut String) {
61    if let Some(text) = flat(value) {
62        output.push_str(&text);
63        return;
64    }
65    match value {
66        Value::Object(map) => {
67            let child = if implicit { indent } else { indent + 2 };
68            if !implicit {
69                output.push_str("{\n");
70            }
71            for (index, (name, value)) in map.iter().enumerate() {
72                if index > 0 {
73                    output.push('\n');
74                }
75                output.push_str(&" ".repeat(child));
76                output.push_str(&key(name));
77                output.push_str(": ");
78                write(value, false, child, output);
79            }
80            if !implicit {
81                output.push('\n');
82                output.push_str(&" ".repeat(indent));
83                output.push('}');
84            }
85        }
86        Value::Array(items) => {
87            let inline = items.iter().map(flat).collect::<Option<Vec<_>>>();
88            if let Some(inline) = inline {
89                let text = format!("[{}]", inline.join(", "));
90                let column = output.rsplit('\n').next().unwrap().chars().count();
91                if column + text.chars().count() <= WIDTH {
92                    output.push_str(&text);
93                    return;
94                }
95            }
96            output.push_str("[\n");
97            for (index, value) in items.iter().enumerate() {
98                if index > 0 {
99                    output.push('\n');
100                }
101                output.push_str(&" ".repeat(indent + 2));
102                write(value, false, indent + 2, output);
103            }
104            output.push('\n');
105            output.push_str(&" ".repeat(indent));
106            output.push(']');
107        }
108        _ => unreachable!(),
109    }
110}