Skip to main content

substrate_data/forms/text/writer/
writer.rs

1//! [`Writer`]: text written piece by piece, from data that isn't a value.
2
3use 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
9/// How far a nested level is indented in the pretty form.
10const INDENT: &[u8] = b"  ";
11
12/// Writes the text form piece by piece, without a [`Value`] in between. A
13/// list or map is given its length, then that many values (a map's each
14/// after its `key`); it closes by itself after the last, so there are no
15/// end calls. The pretty form puts one entry per line.
16///
17/// ```
18/// let mut w = substrate_data::text::Writer::new();
19/// w.map(2).key("id").str("docs/intro").key("pages").list(2);
20/// w.int(1).int(2);
21/// assert_eq!(w.finish(), r#"{"id":"docs/intro","pages":[1,2]}"#);
22/// ```
23#[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    /// A writer of compact text.
38    #[must_use]
39    pub fn new() -> Self {
40        Self::with_pretty(false)
41    }
42
43    /// A writer of text indented by two spaces, one entry per line, ending
44    /// with a newline.
45    #[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    /// Null.
59    pub fn null(&mut self) -> &mut Self {
60        self.begin();
61        self.out.extend_from_slice(b"null");
62        self.end()
63    }
64
65    /// A bool.
66    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    /// An integer.
74    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    /// A float, in the shortest spelling that reads back as it and as a
81    /// float; `null` for a NaN or an infinity.
82    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    /// A string, as spelled (the text form has no other kind of text).
89    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    /// A reference, as its string form `@kind:id`.
96    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    /// Bytes, as their string form `@bytes:<base64>`.
107    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    /// A list of `len` values, which follow.
125    pub fn list(&mut self, len: usize) -> &mut Self {
126        self.container(len, false)
127    }
128
129    /// A map of `len` entries (`key`, then its value), which follow.
130    pub fn map(&mut self, len: usize) -> &mut Self {
131        self.container(len, true)
132    }
133
134    /// The key of the map's next entry; its value follows.
135    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    /// A whole [`Value`] (typed values in their string forms).
146    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            // As spelled: a plain string's leading `@` was doubled when it
153            // was put in the value (`Schema::write`), a typed one's wasn't.
154            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    /// The text written.
175    #[must_use]
176    pub fn finish(mut self) -> String {
177        if self.pretty {
178            self.out.push(b'\n');
179        }
180        // Only what was written from `str`s and ASCII: always valid.
181        String::from_utf8(self.out)
182            .unwrap_or_else(|e| String::from_utf8_lossy(e.as_bytes()).into_owned())
183    }
184
185    /// The comma and the line break before an entry of a list or map.
186    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    /// Before a value: its separator, unless a key just wrote it.
206    fn begin(&mut self) {
207        if matches!(self.open.last(), Some(top) if !top.map) {
208            self.separate();
209        }
210    }
211
212    /// After a value: counts it in its container, and closes the containers
213    /// that it was the last of.
214    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}