write_json/
lib.rs

1//! Simple dependency-less macro-less trait-less JSON serialization.
2//!
3//! # Example
4//!
5//! ```
6//! let mut buf = String::new();
7//!
8//! {
9//!     let mut obj = write_json::object(&mut buf);
10//!     obj.string("name", "Peter").number("favorite number", 92.0);
11//!     obj.array("films")
12//!         .string("Drowning By Numbers")
13//!         .string("A Zed & Two Noughts");
14//!     obj.null("suitcase");
15//! }
16//!
17//! assert_eq!(
18//!     buf,
19//!     r#"{"name":"Peter","favorite number":92,"films":["Drowning By Numbers","A Zed & Two Noughts"],"suitcase":null}"#
20//! )
21//! ```
22
23#[inline]
24pub fn null(buf: &mut String) {
25    encode_null(buf, ());
26}
27#[inline]
28pub fn bool(buf: &mut String, value: bool) {
29    encode_bool(buf, value);
30}
31#[inline]
32pub fn number(buf: &mut String, number: f64) {
33    encode_number(buf, number);
34}
35#[inline]
36pub fn string(buf: &mut String, string: &str) {
37    encode_str(buf, string);
38}
39#[inline]
40pub fn object(buf: &mut String) -> Object<'_> {
41    Object::new(buf)
42}
43#[inline]
44pub fn array(buf: &mut String) -> Array<'_> {
45    Array::new(buf)
46}
47
48pub struct Object<'a> {
49    buf: &'a mut String,
50    first: bool,
51}
52
53impl<'a> Object<'a> {
54    #[inline]
55    fn new(buf: &'a mut String) -> Self {
56        buf.push('{');
57        Object { buf, first: true }
58    }
59    #[inline]
60    fn key(&mut self, key: &str) {
61        if !self.first {
62            self.buf.push(',');
63        }
64        self.first = false;
65        encode_str(&mut self.buf, key);
66        self.buf.push(':');
67    }
68    #[inline]
69    fn field<T, F: FnOnce(&mut String, T)>(&mut self, key: &str, enc: F, value: T) -> &mut Self {
70        self.key(key);
71        enc(&mut self.buf, value);
72        self
73    }
74
75    #[inline]
76    pub fn null(&mut self, key: &str) -> &mut Self {
77        self.field(key, encode_null, ())
78    }
79    #[inline]
80    pub fn bool(&mut self, key: &str, value: bool) -> &mut Self {
81        self.field(key, encode_bool, value)
82    }
83    #[inline]
84    pub fn number(&mut self, key: &str, value: f64) -> &mut Self {
85        self.field(key, encode_number, value)
86    }
87    #[inline]
88    pub fn string(&mut self, key: &str, value: &str) -> &mut Self {
89        self.field(key, encode_str, value)
90    }
91    #[inline]
92    pub fn object(&mut self, key: &str) -> Object<'_> {
93        self.key(key);
94        Object::new(self.buf)
95    }
96    #[inline]
97    pub fn array(&mut self, key: &str) -> Array<'_> {
98        self.key(key);
99        Array::new(self.buf)
100    }
101}
102
103impl Drop for Object<'_> {
104    #[inline]
105    fn drop(&mut self) {
106        self.buf.push('}')
107    }
108}
109
110pub struct Array<'a> {
111    buf: &'a mut String,
112    first: bool,
113}
114
115impl<'a> Array<'a> {
116    #[inline]
117    fn new(buf: &'a mut String) -> Self {
118        buf.push('[');
119        Array { buf, first: true }
120    }
121    #[inline]
122    fn comma(&mut self) {
123        if !self.first {
124            self.buf.push(',');
125        }
126        self.first = false;
127    }
128    #[inline]
129    fn element<T, F: FnOnce(&mut String, T)>(&mut self, enc: F, value: T) -> &mut Self {
130        self.comma();
131        enc(&mut self.buf, value);
132        self
133    }
134
135    #[inline]
136    pub fn null(&mut self) -> &mut Self {
137        self.element(encode_null, ())
138    }
139    #[inline]
140    pub fn bool(&mut self, value: bool) -> &mut Self {
141        self.element(encode_bool, value)
142    }
143    #[inline]
144    pub fn number(&mut self, value: f64) -> &mut Self {
145        self.element(encode_number, value)
146    }
147    #[inline]
148    pub fn string(&mut self, value: &str) -> &mut Self {
149        self.element(encode_str, value)
150    }
151    #[inline]
152    pub fn object(&mut self) -> Object<'_> {
153        self.comma();
154        Object::new(self.buf)
155    }
156    #[inline]
157    pub fn array(&mut self) -> Array<'_> {
158        self.comma();
159        Array::new(self.buf)
160    }
161}
162
163impl Drop for Array<'_> {
164    #[inline]
165    fn drop(&mut self) {
166        self.buf.push(']')
167    }
168}
169
170#[inline]
171fn encode_null(buf: &mut String, (): ()) {
172    buf.push_str("null")
173}
174#[inline]
175fn encode_bool(buf: &mut String, value: bool) {
176    buf.push_str(if value { "true" } else { "false" })
177}
178#[inline]
179fn encode_number(buf: &mut String, number: f64) {
180    use std::fmt::Write;
181    let _ = write!(buf, "{}", number);
182}
183
184#[inline]
185fn encode_str(buf: &mut String, s: &str) {
186    buf.reserve(s.len() + 2);
187    buf.push('\"');
188    if s.bytes()
189        .all(|b| 0x1F < b && b != b'"' && b != b'\\' && b < 0x7F)
190    {
191        buf.push_str(s)
192    } else {
193        slow_path(buf, s)
194    }
195    buf.push('\"');
196
197    #[inline(never)]
198    fn slow_path(buf: &mut String, s: &str) {
199        for c in s.chars() {
200            if (c as u32) < 256 {
201                let b = c as u8;
202                match b {
203                    b'\\' | b'"' => push_escape(buf, c),
204                    b'\n' => push_escape(buf, 'n'),
205                    b'\r' => push_escape(buf, 'r'),
206                    b'\t' => push_escape(buf, 't'),
207                    0..=0x1F | 0x7F..=0x9F => {
208                        push_escape(buf, 'u');
209                        buf.push_str("00");
210                        buf.push(hex(b >> 4));
211                        buf.push(hex(b & 0xF));
212                    }
213                    _ => buf.push(c),
214                }
215            } else {
216                buf.push(c)
217            }
218        }
219    }
220
221    #[inline]
222    fn push_escape(buf: &mut String, c: char) {
223        buf.push('\\');
224        buf.push(c);
225    }
226
227    #[inline]
228    fn hex(b: u8) -> char {
229        (b"0123456789ABCDEF"[(b & 0xF) as usize]) as char
230    }
231}