tinyjson/
generator.rs

1use crate::JsonValue;
2use std::collections::HashMap;
3use std::fmt;
4use std::io::{self, Write};
5
6/// Serialization error. This error only happens when some write error happens on writing the serialized byte sequence
7/// to the given `io::Write` object.
8#[derive(Debug)]
9pub struct JsonGenerateError {
10    msg: String,
11}
12
13impl JsonGenerateError {
14    pub fn message(&self) -> &str {
15        self.msg.as_str()
16    }
17}
18
19impl fmt::Display for JsonGenerateError {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        write!(f, "Generate error: {}", &self.msg)
22    }
23}
24
25impl std::error::Error for JsonGenerateError {}
26
27/// Convenient type alias for serialization results.
28pub type JsonGenerateResult = Result<String, JsonGenerateError>;
29
30/// JSON serializer for `JsonValue`.
31///
32/// Basically you don't need to use this struct directly since `JsonValue::stringify` or `JsonValue::format` methods are
33/// using this internally.
34///
35/// ```
36/// use tinyjson::{JsonGenerator, JsonValue};
37///
38/// let v = JsonValue::from("hello, world".to_string());
39/// let mut buf = vec![];
40/// let mut gen = JsonGenerator::new(&mut buf);
41/// gen.generate(&v).unwrap();
42///
43/// assert_eq!(String::from_utf8(buf).unwrap(), "\"hello, world\"");
44/// ```
45pub struct JsonGenerator<'indent, W: Write> {
46    out: W,
47    indent: Option<&'indent str>,
48}
49
50impl<'indent, W: Write> JsonGenerator<'indent, W> {
51    /// Create a new `JsonGenerator` object. The serialized byte sequence will be written to the given `io::Write`
52    /// object.
53    pub fn new(out: W) -> Self {
54        Self { out, indent: None }
55    }
56
57    /// Set indent string. This will be used by [`JsonGenerator::generate`].
58    /// ```
59    /// use tinyjson::{JsonGenerator, JsonValue};
60    ///
61    /// let v = JsonValue::from(vec![1.0.into(), 2.0.into(), 3.0.into()]);
62    /// let mut buf = vec![];
63    /// let mut gen = JsonGenerator::new(&mut buf).indent("        ");
64    /// gen.generate(&v).unwrap();
65    ///
66    /// assert_eq!(String::from_utf8(buf).unwrap(),
67    /// "[
68    ///         1,
69    ///         2,
70    ///         3
71    /// ]");
72    /// ```
73    pub fn indent(mut self, indent: &'indent str) -> Self {
74        self.indent = Some(indent);
75        self
76    }
77
78    fn quote(&mut self, s: &str) -> io::Result<()> {
79        const B: u8 = b'b'; // \x08
80        const T: u8 = b't'; // \x09
81        const N: u8 = b'n'; // \x0a
82        const F: u8 = b'f'; // \x0c
83        const R: u8 = b'r'; // \x0d
84        const Q: u8 = b'"'; // \x22
85        const S: u8 = b'\\'; // \x5c
86        const U: u8 = 1; // non-printable
87
88        #[rustfmt::skip]
89        const ESCAPE_TABLE: [u8; 256] = [
90         // 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
91            U, U, U, U, U, U, U, U, B, T, N, U, F, R, U, U, // 0
92            U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, // 1
93            0, 0, Q, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2
94            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3
95            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4
96            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, S, 0, 0, 0, // 5
97            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6
98            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7
99            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8
100            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9
101            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
102            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
103            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C
104            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D
105            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E
106            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F
107        ];
108
109        self.out.write_all(b"\"")?;
110        let mut start = 0;
111        for (i, c) in s.char_indices() {
112            let u = c as usize;
113            if u < 256 {
114                let esc = ESCAPE_TABLE[u];
115                if esc == 0 {
116                    continue;
117                }
118                if start != i {
119                    self.out.write_all(s[start..i].as_bytes())?;
120                }
121                if esc == U {
122                    write!(self.out, "\\u{:04x}", u)?;
123                } else {
124                    self.out.write_all(&[b'\\', esc])?;
125                }
126                start = i + 1;
127            }
128        }
129        if start != s.len() {
130            self.out.write_all(s[start..].as_bytes())?;
131        }
132        self.out.write_all(b"\"")
133    }
134
135    fn number(&mut self, f: f64) -> io::Result<()> {
136        if f.is_infinite() {
137            Err(io::Error::new(
138                io::ErrorKind::Other,
139                "JSON cannot represent inf",
140            ))
141        } else if f.is_nan() {
142            Err(io::Error::new(
143                io::ErrorKind::Other,
144                "JSON cannot represent NaN",
145            ))
146        } else {
147            write!(self.out, "{}", f)
148        }
149    }
150
151    fn encode_array(&mut self, array: &[JsonValue]) -> io::Result<()> {
152        self.out.write_all(b"[")?;
153        let mut first = true;
154        for elem in array.iter() {
155            if first {
156                first = false;
157            } else {
158                self.out.write_all(b",")?;
159            }
160            self.encode(elem)?;
161        }
162        self.out.write_all(b"]")
163    }
164
165    fn encode_object(&mut self, m: &HashMap<String, JsonValue>) -> io::Result<()> {
166        self.out.write_all(b"{")?;
167        let mut first = true;
168        for (k, v) in m {
169            if first {
170                first = false;
171            } else {
172                self.out.write_all(b",")?;
173            }
174            self.quote(k)?;
175            self.out.write_all(b":")?;
176            self.encode(v)?;
177        }
178        self.out.write_all(b"}")
179    }
180
181    fn encode(&mut self, value: &JsonValue) -> io::Result<()> {
182        match value {
183            JsonValue::Number(n) => self.number(*n),
184            JsonValue::Boolean(b) => write!(self.out, "{}", *b),
185            JsonValue::String(s) => self.quote(s),
186            JsonValue::Null => self.out.write_all(b"null"),
187            JsonValue::Array(a) => self.encode_array(a),
188            JsonValue::Object(o) => self.encode_object(o),
189        }
190    }
191
192    fn write_indent(&mut self, indent: &str, level: usize) -> io::Result<()> {
193        for _ in 0..level {
194            self.out.write_all(indent.as_bytes())?;
195        }
196        Ok(())
197    }
198
199    fn format_array(&mut self, array: &[JsonValue], indent: &str, level: usize) -> io::Result<()> {
200        if array.is_empty() {
201            return self.out.write_all(b"[]");
202        }
203
204        self.out.write_all(b"[\n")?;
205        let mut first = true;
206        for elem in array.iter() {
207            if first {
208                first = false;
209            } else {
210                self.out.write_all(b",\n")?;
211            }
212            self.write_indent(indent, level + 1)?;
213            self.format(elem, indent, level + 1)?;
214        }
215        self.out.write_all(b"\n")?;
216        self.write_indent(indent, level)?;
217        self.out.write_all(b"]")
218    }
219
220    fn format_object(
221        &mut self,
222        m: &HashMap<String, JsonValue>,
223        indent: &str,
224        level: usize,
225    ) -> io::Result<()> {
226        if m.is_empty() {
227            return self.out.write_all(b"{}");
228        }
229
230        self.out.write_all(b"{\n")?;
231        let mut first = true;
232        for (k, v) in m {
233            if first {
234                first = false;
235            } else {
236                self.out.write_all(b",\n")?;
237            }
238            self.write_indent(indent, level + 1)?;
239            self.quote(k)?;
240            self.out.write_all(b": ")?;
241            self.format(v, indent, level + 1)?;
242        }
243        self.out.write_all(b"\n")?;
244        self.write_indent(indent, level)?;
245        self.out.write_all(b"}")
246    }
247
248    fn format(&mut self, value: &JsonValue, indent: &str, level: usize) -> io::Result<()> {
249        match value {
250            JsonValue::Number(n) => self.number(*n),
251            JsonValue::Boolean(b) => write!(self.out, "{}", *b),
252            JsonValue::String(s) => self.quote(s),
253            JsonValue::Null => self.out.write_all(b"null"),
254            JsonValue::Array(a) => self.format_array(a, indent, level),
255            JsonValue::Object(o) => self.format_object(o, indent, level),
256        }
257    }
258
259    /// Serialize the `JsonValue` into UTF-8 byte sequence. The result will be written to the `io::Write` object passed
260    /// at [`JsonGenerator::new`].
261    /// This method serializes the value without indentation by default. But after setting an indent string via
262    /// [`JsonGenerator::indent`], this method will use the indent for elements of array and object.
263    ///
264    /// ```
265    /// use tinyjson::{JsonGenerator, JsonValue};
266    ///
267    /// let v = JsonValue::from(vec![1.0.into(), 2.0.into(), 3.0.into()]);
268    ///
269    /// let mut buf = vec![];
270    /// let mut gen = JsonGenerator::new(&mut buf);
271    /// gen.generate(&v).unwrap();
272    /// assert_eq!(String::from_utf8(buf).unwrap(), "[1,2,3]");
273    ///
274    /// let mut buf = vec![];
275    /// let mut gen = JsonGenerator::new(&mut buf).indent("  "); // with 2-spaces indent
276    /// gen.generate(&v).unwrap();
277    ///
278    /// assert_eq!(String::from_utf8(buf).unwrap(),
279    /// "[
280    ///   1,
281    ///   2,
282    ///   3
283    /// ]");
284    /// ```
285    pub fn generate(&mut self, value: &JsonValue) -> io::Result<()> {
286        if let Some(indent) = &self.indent {
287            self.format(value, indent, 0)
288        } else {
289            self.encode(value)
290        }
291    }
292}
293
294/// Serialize the given `JsonValue` value to `String` without indentation. This method is almost identical to
295/// `JsonValue::stringify` but exists for a historical reason.
296///
297/// ```
298/// use tinyjson::JsonValue;
299///
300/// let v = JsonValue::from(vec![1.0.into(), 2.0.into(), 3.0.into()]);
301/// let s = tinyjson::stringify(&v).unwrap();
302/// assert_eq!(s, "[1,2,3]");
303/// ```
304pub fn stringify(value: &JsonValue) -> JsonGenerateResult {
305    let mut to = Vec::new();
306    let mut gen = JsonGenerator::new(&mut to);
307    gen.generate(value).map_err(|err| JsonGenerateError {
308        msg: format!("{}", err),
309    })?;
310    Ok(String::from_utf8(to).unwrap())
311}
312
313/// Serialize the given `JsonValue` value to `String` with 2-spaces indentation. This method is almost identical to
314/// `JsonValue::format` but exists for a historical reason.
315///
316/// ```
317/// use tinyjson::JsonValue;
318///
319/// let v = JsonValue::from(vec![1.0.into(), 2.0.into(), 3.0.into()]);
320/// let s = tinyjson::format(&v).unwrap();
321/// assert_eq!(s, "[\n  1,\n  2,\n  3\n]");
322/// ```
323pub fn format(value: &JsonValue) -> JsonGenerateResult {
324    let mut to = Vec::new();
325    let mut gen = JsonGenerator::new(&mut to).indent("  ");
326    gen.generate(value).map_err(|err| JsonGenerateError {
327        msg: format!("{}", err),
328    })?;
329    Ok(String::from_utf8(to).unwrap())
330}