substrate_data/forms/text/writer/
writer.rs1use super::float::push_float;
4use super::integer::push_int;
5use super::open::Open;
6use super::string::{push_string, write_body};
7use crate::model::value::Value;
8
9const INDENT: &[u8] = b" ";
11
12#[derive(Debug)]
24pub struct Writer {
25 out: Vec<u8>,
26 pretty: bool,
27 open: Vec<Open>,
28}
29
30impl Default for Writer {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl Writer {
37 #[must_use]
39 pub fn new() -> Self {
40 Self::with_pretty(false)
41 }
42
43 #[must_use]
46 pub fn pretty() -> Self {
47 Self::with_pretty(true)
48 }
49
50 fn with_pretty(pretty: bool) -> Self {
51 Self {
52 out: Vec::with_capacity(256),
53 pretty,
54 open: Vec::new(),
55 }
56 }
57
58 pub fn null(&mut self) -> &mut Self {
60 self.begin();
61 self.out.extend_from_slice(b"null");
62 self.end()
63 }
64
65 pub fn bool(&mut self, b: bool) -> &mut Self {
67 self.begin();
68 self.out
69 .extend_from_slice(if b { b"true" } else { b"false" });
70 self.end()
71 }
72
73 pub fn int(&mut self, n: i64) -> &mut Self {
75 self.begin();
76 push_int(n, &mut self.out);
77 self.end()
78 }
79
80 pub fn float(&mut self, f: f64) -> &mut Self {
83 self.begin();
84 push_float(f, &mut self.out);
85 self.end()
86 }
87
88 pub fn str(&mut self, s: &str) -> &mut Self {
90 self.begin();
91 push_string(s, &mut self.out);
92 self.end()
93 }
94
95 pub fn reference(&mut self, kind: &str, id: &str) -> &mut Self {
97 self.begin();
98 self.out.extend_from_slice(b"\"@");
99 write_body(kind, &mut self.out);
100 self.out.push(b':');
101 write_body(id, &mut self.out);
102 self.out.push(b'"');
103 self.end()
104 }
105
106 pub fn bytes(&mut self, bytes: &[u8]) -> &mut Self {
108 use base64::Engine as _;
109 self.begin();
110 self.out.extend_from_slice(b"\"@");
111 self.out.extend_from_slice(crate::typed::BYTES.as_bytes());
112 self.out.push(b':');
113 let at = self.out.len();
114 self.out
115 .resize(at + base64::encoded_len(bytes.len(), true).unwrap_or(0), 0);
116 let n = base64::engine::general_purpose::STANDARD
117 .encode_slice(bytes, &mut self.out[at..])
118 .unwrap_or(0);
119 self.out.truncate(at + n);
120 self.out.push(b'"');
121 self.end()
122 }
123
124 pub fn list(&mut self, len: usize) -> &mut Self {
126 self.container(len, false)
127 }
128
129 pub fn map(&mut self, len: usize) -> &mut Self {
131 self.container(len, true)
132 }
133
134 pub fn key(&mut self, key: &str) -> &mut Self {
136 self.separate();
137 push_string(key, &mut self.out);
138 self.out.push(b':');
139 if self.pretty {
140 self.out.push(b' ');
141 }
142 self
143 }
144
145 pub fn value(&mut self, value: &Value) -> &mut Self {
147 match value {
148 Value::Null => self.null(),
149 Value::Bool(b) => self.bool(*b),
150 Value::Int(n) => self.int(*n),
151 Value::Float(f) => self.float(*f),
152 Value::Str(s) => self.str(s),
155 Value::List(items) => {
156 self.list(items.len());
157 for item in items {
158 self.value(item);
159 }
160 self
161 }
162 Value::Map(map) => {
163 self.map(map.len());
164 for (k, v) in map {
165 self.key(k).value(v);
166 }
167 self
168 }
169 Value::Ref(r) => self.reference(r.kind.as_str(), r.id.as_str()),
170 Value::Bytes(b) => self.bytes(b),
171 }
172 }
173
174 #[must_use]
176 pub fn finish(mut self) -> String {
177 if self.pretty {
178 self.out.push(b'\n');
179 }
180 String::from_utf8(self.out)
182 .unwrap_or_else(|e| String::from_utf8_lossy(e.as_bytes()).into_owned())
183 }
184
185 fn separate(&mut self) {
187 let Some(top) = self.open.last() else {
188 return;
189 };
190 if top.done > 0 {
191 self.out.push(b',');
192 }
193 self.line_break(self.open.len());
194 }
195
196 fn line_break(&mut self, level: usize) {
197 if self.pretty {
198 self.out.push(b'\n');
199 for _ in 0..level {
200 self.out.extend_from_slice(INDENT);
201 }
202 }
203 }
204
205 fn begin(&mut self) {
207 if matches!(self.open.last(), Some(top) if !top.map) {
208 self.separate();
209 }
210 }
211
212 fn end(&mut self) -> &mut Self {
215 while let Some(top) = self.open.last_mut() {
216 top.done += 1;
217 top.remaining -= 1;
218 if top.remaining > 0 {
219 break;
220 }
221 let closer = if top.map { b'}' } else { b']' };
222 self.open.pop();
223 self.line_break(self.open.len());
224 self.out.push(closer);
225 }
226 self
227 }
228
229 fn container(&mut self, len: usize, map: bool) -> &mut Self {
230 self.begin();
231 if len == 0 {
232 self.out.extend_from_slice(if map { b"{}" } else { b"[]" });
233 return self.end();
234 }
235 self.out.push(if map { b'{' } else { b'[' });
236 self.open.push(Open {
237 map,
238 remaining: len,
239 done: 0,
240 });
241 self
242 }
243}