substrate_data/forms/patch/
format.rs1use std::fmt;
4
5use crate::model::value::{Map, Value};
6use crate::text::write_string;
7
8const ONE_LINE: usize = 72;
10pub(super) const INDENT: &str = " ";
12
13pub fn format(value: &Value, level: usize) -> String {
17 let mut out = String::new();
18 write_value(value, level, &mut out);
19 out
20}
21
22fn write_value(value: &Value, level: usize, out: &mut String) {
24 let start = out.len();
25 if write_inline(value, out) {
26 return;
27 }
28 out.truncate(start);
29 match value {
30 Value::List(items) => {
31 out.push_str("[\n");
32 for (i, v) in items.iter().enumerate() {
33 if i > 0 {
34 out.push_str(",\n");
35 }
36 push_indent(out, level + 1);
37 write_value(v, level + 1, out);
38 }
39 out.push('\n');
40 push_indent(out, level);
41 out.push(']');
42 }
43 Value::Map(map) => {
44 out.push_str("{\n");
45 for (i, (k, v)) in map.iter().enumerate() {
46 if i > 0 {
47 out.push_str(",\n");
48 }
49 push_indent(out, level + 1);
50 write_string(k, out);
51 out.push_str(": ");
52 write_value(v, level + 1, out);
53 }
54 out.push('\n');
55 push_indent(out, level);
56 out.push('}');
57 }
58 other => {
59 write_scalar(other, out);
60 }
61 }
62}
63
64fn push_indent(out: &mut String, level: usize) {
65 for _ in 0..level {
66 out.push_str(INDENT);
67 }
68}
69
70pub(super) fn quoted(s: &str) -> String {
71 let mut out = String::with_capacity(s.len() + 2);
72 write_string(s, &mut out);
73 out
74}
75
76pub(super) fn number(n: f64) -> String {
81 crate::text::float_string(n)
82}
83
84fn write_scalar(v: &Value, out: &mut String) -> bool {
86 match v {
87 Value::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
88 Value::Int(n) => {
89 fmt::Write::write_fmt(out, format_args!("{n}")).ok();
90 }
91 Value::Float(f) if f.is_finite() => out.push_str(&number(*f)),
92 Value::Null | Value::Float(_) => out.push_str("null"),
93 Value::List(_) | Value::Map(_) => return false,
94 Value::Str(s) => write_string(s, out),
96 other => out.push_str(&crate::text::to_string(other)),
97 }
98 true
99}
100
101fn write_inline(v: &Value, out: &mut String) -> bool {
104 if write_scalar(v, out) {
105 return true;
106 }
107 let start = out.len();
108 let written = match v {
109 Value::List(items) if items.is_empty() => {
110 out.push_str("[]");
111 return true;
112 }
113 Value::List(items) => write_numbers(items, out),
114 Value::Map(map) if map.is_empty() => {
115 out.push_str("{}");
116 return true;
117 }
118 Value::Map(map) => write_scalars(map, out),
119 _ => false,
120 };
121 written && out.len() - start <= ONE_LINE
122}
123
124fn write_numbers(items: &[Value], out: &mut String) -> bool {
126 if !items
127 .iter()
128 .all(|x| matches!(x, Value::Int(_) | Value::Float(_) | Value::Bool(_)))
129 {
130 return false;
131 }
132 out.push('[');
133 for (i, x) in items.iter().enumerate() {
134 if i > 0 {
135 out.push_str(", ");
136 }
137 write_scalar(x, out);
138 }
139 out.push(']');
140 true
141}
142
143fn write_scalars(map: &Map, out: &mut String) -> bool {
145 out.push_str("{ ");
146 for (i, (k, x)) in map.iter().enumerate() {
147 if matches!(x, Value::Map(_)) {
148 return false;
149 }
150 if i > 0 {
151 out.push_str(", ");
152 }
153 write_string(k, out);
154 out.push_str(": ");
155 if !write_inline(x, out) {
156 return false;
157 }
158 }
159 out.push_str(" }");
160 true
161}
162
163pub fn to_json(value: &Value) -> String {
166 let mut out = String::new();
167 json_into(value, &mut out);
168 out
169}
170
171fn json_into(value: &Value, out: &mut String) {
172 match value {
173 Value::List(items) => {
174 out.push('[');
175 for (i, v) in items.iter().enumerate() {
176 if i > 0 {
177 out.push(',');
178 }
179 json_into(v, out);
180 }
181 out.push(']');
182 }
183 Value::Map(map) => {
184 out.push('{');
185 for (i, (k, v)) in map.iter().enumerate() {
186 if i > 0 {
187 out.push(',');
188 }
189 write_string(k, out);
190 out.push(':');
191 json_into(v, out);
192 }
193 out.push('}');
194 }
195 Value::Str(s) => write_string(s, out),
196 Value::Float(f) if f.is_finite() => out.push_str(&f.to_string()),
197 Value::Float(_) => out.push_str("null"),
198 other => out.push_str(&crate::text::to_string(other)),
199 }
200}
201
202pub fn same(a: &Value, b: &Value) -> bool {
204 match (a, b) {
205 (Value::Int(_) | Value::Float(_), Value::Int(_) | Value::Float(_)) => {
206 match (a.as_f64(), b.as_f64()) {
208 (Some(x), Some(y)) => x.to_bits() == y.to_bits(),
209 _ => false,
210 }
211 }
212 (Value::List(x), Value::List(y)) => {
213 x.len() == y.len() && x.iter().zip(y).all(|(p, q)| same(p, q))
214 }
215 (Value::Map(x), Value::Map(y)) => {
216 x.len() == y.len()
217 && x.iter()
218 .zip(y.iter())
219 .all(|((ka, p), (kb, q))| ka == kb && same(p, q))
220 }
221 _ => a == b,
222 }
223}