substrate_data/forms/patch/
format.rs1use crate::model::value::Value;
4use crate::text::write_string;
5
6const ONE_LINE: usize = 72;
8pub(super) const INDENT: &str = " ";
10
11pub fn format(value: &Value, level: usize) -> String {
15 if let Some(one) = inline(value) {
16 return one;
17 }
18 let pad = INDENT.repeat(level + 1);
19 let end = INDENT.repeat(level);
20 match value {
21 Value::List(items) => {
22 let body: Vec<String> = items
23 .iter()
24 .map(|v| format!("{pad}{}", format(v, level + 1)))
25 .collect();
26 format!("[\n{}\n{end}]", body.join(",\n"))
27 }
28 Value::Map(map) => {
29 let body: Vec<String> = map
30 .iter()
31 .map(|(k, v)| format!("{pad}{}: {}", quoted(k), format(v, level + 1)))
32 .collect();
33 format!("{{\n{}\n{end}}}", body.join(",\n"))
34 }
35 other => scalar(other).unwrap_or_default(),
36 }
37}
38
39pub(super) fn quoted(s: &str) -> String {
40 let mut out = String::with_capacity(s.len() + 2);
41 write_string(s, &mut out);
42 out
43}
44
45pub(super) fn number(n: f64) -> String {
50 crate::text::float_string(n)
51}
52
53fn scalar(v: &Value) -> Option<String> {
54 match v {
55 Value::Bool(b) => Some(b.to_string()),
56 Value::Int(n) => Some(n.to_string()),
57 Value::Float(f) if f.is_finite() => Some(number(*f)),
58 Value::Null | Value::Float(_) => Some("null".into()),
59 Value::List(_) | Value::Map(_) => None,
60 Value::Str(s) => Some(quoted(s)),
62 other => Some(crate::text::to_string(other)),
63 }
64}
65
66fn inline(v: &Value) -> Option<String> {
67 if let Some(s) = scalar(v) {
68 return Some(s);
69 }
70 let out = match v {
71 Value::List(items) if items.is_empty() => return Some("[]".into()),
72 Value::List(items) => {
73 if !items
74 .iter()
75 .all(|x| matches!(x, Value::Int(_) | Value::Float(_) | Value::Bool(_)))
76 {
77 return None;
78 }
79 format!(
80 "[{}]",
81 items
82 .iter()
83 .filter_map(scalar)
84 .collect::<Vec<_>>()
85 .join(", ")
86 )
87 }
88 Value::Map(map) if map.is_empty() => return Some("{}".into()),
89 Value::Map(map) => {
90 let mut parts = Vec::with_capacity(map.len());
91 for (k, x) in map.iter() {
92 if matches!(x, Value::Map(_)) {
93 return None;
94 }
95 parts.push(format!("{}: {}", quoted(k), inline(x)?));
96 }
97 format!("{{ {} }}", parts.join(", "))
98 }
99 _ => return None,
100 };
101 (out.len() <= ONE_LINE).then_some(out)
102}
103
104pub fn to_json(value: &Value) -> String {
107 let mut out = String::new();
108 json_into(value, &mut out);
109 out
110}
111
112fn json_into(value: &Value, out: &mut String) {
113 match value {
114 Value::List(items) => {
115 out.push('[');
116 for (i, v) in items.iter().enumerate() {
117 if i > 0 {
118 out.push(',');
119 }
120 json_into(v, out);
121 }
122 out.push(']');
123 }
124 Value::Map(map) => {
125 out.push('{');
126 for (i, (k, v)) in map.iter().enumerate() {
127 if i > 0 {
128 out.push(',');
129 }
130 write_string(k, out);
131 out.push(':');
132 json_into(v, out);
133 }
134 out.push('}');
135 }
136 Value::Str(s) => write_string(s, out),
137 Value::Float(f) if f.is_finite() => out.push_str(&f.to_string()),
138 Value::Float(_) => out.push_str("null"),
139 other => out.push_str(&crate::text::to_string(other)),
140 }
141}
142
143pub fn same(a: &Value, b: &Value) -> bool {
145 match (a, b) {
146 (Value::Int(_) | Value::Float(_), Value::Int(_) | Value::Float(_)) => {
147 match (a.as_f64(), b.as_f64()) {
149 (Some(x), Some(y)) => x.to_bits() == y.to_bits(),
150 _ => false,
151 }
152 }
153 (Value::List(x), Value::List(y)) => {
154 x.len() == y.len() && x.iter().zip(y).all(|(p, q)| same(p, q))
155 }
156 (Value::Map(x), Value::Map(y)) => {
157 x.len() == y.len()
158 && x.iter()
159 .zip(y.iter())
160 .all(|((ka, p), (kb, q))| ka == kb && same(p, q))
161 }
162 _ => a == b,
163 }
164}