substrate_data/forms/text/
writer.rs1use crate::model::value::{Map, Value};
4
5pub fn to_string(value: &Value) -> String {
7 let mut out = String::with_capacity(256);
8 write(value, None, 0, &mut out);
9 out
10}
11
12pub fn to_string_pretty(value: &Value) -> String {
14 let mut out = String::new();
15 write(value, Some(" "), 0, &mut out);
16 out.push('\n');
17 out
18}
19
20fn write(value: &Value, indent: Option<&str>, level: usize, out: &mut String) {
21 Layout { indent, out }.value(value, level);
22}
23
24struct Layout<'a, 'o> {
26 indent: Option<&'a str>,
27 out: &'o mut String,
28}
29
30impl Layout<'_, '_> {
31 fn value(&mut self, value: &Value, level: usize) {
32 match value {
33 Value::Null => self.out.push_str("null"),
34 Value::Bool(b) => self.out.push_str(if *b { "true" } else { "false" }),
35 Value::Int(n) => push_fmt(self.out, format_args!("{n}")),
36 Value::Float(f) => write_float(*f, self.out),
37 Value::List(items) => self.list(items, level),
38 Value::Map(map) => self.map(map, level),
39 Value::Str(s) => write_string(s, self.out),
42 typed => write_string(&crate::typed::spelling(typed).unwrap_or_default(), self.out),
43 }
44 }
45
46 fn newline(&mut self, level: usize) {
47 if let Some(unit) = self.indent {
48 self.out.push('\n');
49 for _ in 0..level {
50 self.out.push_str(unit);
51 }
52 }
53 }
54
55 fn list(&mut self, items: &[Value], level: usize) {
56 if items.is_empty() {
57 self.out.push_str("[]");
58 return;
59 }
60 self.out.push('[');
61 for (i, v) in items.iter().enumerate() {
62 if i > 0 {
63 self.out.push(',');
64 }
65 self.newline(level + 1);
66 self.value(v, level + 1);
67 }
68 self.newline(level);
69 self.out.push(']');
70 }
71
72 fn map(&mut self, map: &Map, level: usize) {
73 if map.is_empty() {
74 self.out.push_str("{}");
75 return;
76 }
77 self.out.push('{');
78 for (i, (k, v)) in map.iter().enumerate() {
79 if i > 0 {
80 self.out.push(',');
81 }
82 self.newline(level + 1);
83 write_string(k, self.out);
84 self.out
85 .push_str(if self.indent.is_some() { ": " } else { ":" });
86 self.value(v, level + 1);
87 }
88 self.newline(level);
89 self.out.push('}');
90 }
91}
92
93fn push_fmt(out: &mut String, args: std::fmt::Arguments<'_>) {
95 use std::fmt::Write as _;
96 out.write_fmt(args).unwrap_or_default();
97}
98
99fn write_float(f: f64, out: &mut String) {
100 if f.is_finite() {
101 out.push_str(&float_string(f));
102 } else {
103 out.push_str("null");
104 }
105}
106
107pub(crate) fn float_string(f: f64) -> String {
111 format!("{f:?}")
112}
113
114pub fn write_string(s: &str, out: &mut String) {
116 out.push('"');
117 write_string_body(s, out);
118 out.push('"');
119}
120
121fn write_string_body(s: &str, out: &mut String) {
122 let bytes = s.as_bytes();
123 let mut run = 0;
124 while let Some(n) = bytes[run..]
125 .iter()
126 .position(|&b| b == b'"' || b == b'\\' || b < 0x20)
127 {
128 let i = run + n;
129 out.push_str(&s[run..i]);
130 match bytes[i] {
131 b'"' => out.push_str("\\\""),
132 b'\\' => out.push_str("\\\\"),
133 b'\n' => out.push_str("\\n"),
134 b'\r' => out.push_str("\\r"),
135 b'\t' => out.push_str("\\t"),
136 b => push_fmt(out, format_args!("\\u{b:04x}")),
137 }
138 run = i + 1;
139 }
140 out.push_str(&s[run..]);
141}