json_write/
value.rs

1#[cfg(feature = "alloc")]
2use alloc::borrow::Cow;
3#[cfg(feature = "alloc")]
4use alloc::string::String;
5#[cfg(feature = "alloc")]
6use alloc::vec::Vec;
7
8use crate::JsonWrite;
9use crate::WriteJsonKey;
10
11#[cfg(feature = "alloc")]
12pub trait ToJsonValue {
13    fn to_json_value(&self) -> String;
14}
15
16#[cfg(feature = "alloc")]
17impl<T> ToJsonValue for T
18where
19    T: WriteJsonValue + ?Sized,
20{
21    fn to_json_value(&self) -> String {
22        let mut result = String::new();
23        let _ = self.write_json_value(&mut result);
24        result
25    }
26}
27
28pub trait WriteJsonValue {
29    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result;
30}
31
32impl WriteJsonValue for bool {
33    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
34        write!(writer, "{self}")
35    }
36}
37
38impl WriteJsonValue for u8 {
39    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
40        write!(writer, "{self}")
41    }
42}
43
44impl WriteJsonValue for i8 {
45    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
46        write!(writer, "{self}")
47    }
48}
49
50impl WriteJsonValue for u16 {
51    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
52        write!(writer, "{self}")
53    }
54}
55
56impl WriteJsonValue for i16 {
57    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
58        write!(writer, "{self}")
59    }
60}
61
62impl WriteJsonValue for u32 {
63    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
64        write!(writer, "{self}")
65    }
66}
67
68impl WriteJsonValue for i32 {
69    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
70        write!(writer, "{self}")
71    }
72}
73
74impl WriteJsonValue for u64 {
75    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
76        write!(writer, "{self}")
77    }
78}
79
80impl WriteJsonValue for i64 {
81    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
82        write!(writer, "{self}")
83    }
84}
85
86impl WriteJsonValue for u128 {
87    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
88        write!(writer, "{self}")
89    }
90}
91
92impl WriteJsonValue for i128 {
93    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
94        write!(writer, "{self}")
95    }
96}
97
98impl WriteJsonValue for f32 {
99    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
100        if self.is_nan() || self.is_infinite() {
101            None::<Self>.write_json_value(writer)
102        } else {
103            if self % 1.0 == 0.0 {
104                write!(writer, "{self}.0")
105            } else {
106                write!(writer, "{self}")
107            }
108        }
109    }
110}
111
112impl WriteJsonValue for f64 {
113    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
114        if self.is_nan() || self.is_infinite() {
115            None::<Self>.write_json_value(writer)
116        } else {
117            if self % 1.0 == 0.0 {
118                write!(writer, "{self}.0")
119            } else {
120                write!(writer, "{self}")
121            }
122        }
123    }
124}
125
126impl WriteJsonValue for char {
127    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
128        let mut buf = [0; 4];
129        let v = self.encode_utf8(&mut buf);
130        v.write_json_value(writer)
131    }
132}
133
134impl WriteJsonValue for str {
135    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
136        write_json_str(self, writer)
137    }
138}
139
140#[cfg(feature = "alloc")]
141impl WriteJsonValue for String {
142    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
143        self.as_str().write_json_value(writer)
144    }
145}
146
147#[cfg(feature = "alloc")]
148impl WriteJsonValue for Cow<'_, str> {
149    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
150        self.as_ref().write_json_value(writer)
151    }
152}
153
154impl<T: WriteJsonValue> WriteJsonValue for Option<T> {
155    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
156        match self {
157            Some(v) => v.write_json_value(writer),
158            None => write_json_null(writer),
159        }
160    }
161}
162
163impl<V: WriteJsonValue> WriteJsonValue for [V] {
164    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
165        writer.open_array()?;
166        let mut iter = self.iter();
167        if let Some(v) = iter.next() {
168            writer.value(v)?;
169        }
170        for v in iter {
171            writer.val_sep()?;
172            writer.space()?;
173            writer.value(v)?;
174        }
175        writer.close_array()?;
176        Ok(())
177    }
178}
179
180impl<V: WriteJsonValue, const N: usize> WriteJsonValue for [V; N] {
181    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
182        self.as_slice().write_json_value(writer)
183    }
184}
185
186#[cfg(feature = "alloc")]
187impl<V: WriteJsonValue> WriteJsonValue for Vec<V> {
188    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
189        self.as_slice().write_json_value(writer)
190    }
191}
192
193#[cfg(feature = "alloc")]
194impl<K: WriteJsonKey, V: WriteJsonValue> WriteJsonValue for alloc::collections::BTreeMap<K, V> {
195    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
196        write_json_object(self.iter(), writer)
197    }
198}
199
200#[cfg(feature = "std")]
201impl<K: WriteJsonKey, V: WriteJsonValue> WriteJsonValue for std::collections::HashMap<K, V> {
202    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
203        write_json_object(self.iter(), writer)
204    }
205}
206
207impl<V: WriteJsonValue + ?Sized> WriteJsonValue for &V {
208    fn write_json_value<W: JsonWrite + ?Sized>(&self, writer: &mut W) -> core::fmt::Result {
209        (*self).write_json_value(writer)
210    }
211}
212
213pub(crate) fn write_json_null<W: JsonWrite + ?Sized>(writer: &mut W) -> core::fmt::Result {
214    write!(writer, "null")
215}
216
217pub(crate) fn write_json_str<W: JsonWrite + ?Sized>(
218    value: &str,
219    writer: &mut W,
220) -> core::fmt::Result {
221    write!(writer, "\"")?;
222    format_escaped_str_contents(writer, value)?;
223    write!(writer, "\"")?;
224    Ok(())
225}
226
227fn format_escaped_str_contents<W>(writer: &mut W, value: &str) -> core::fmt::Result
228where
229    W: ?Sized + JsonWrite,
230{
231    let mut bytes = value.as_bytes();
232
233    let mut i = 0;
234    while i < bytes.len() {
235        let (string_run, rest) = bytes.split_at(i);
236        let (&byte, rest) = rest.split_first().unwrap();
237
238        let escape = ESCAPE[byte as usize];
239
240        i += 1;
241        if escape == 0 {
242            continue;
243        }
244
245        bytes = rest;
246        i = 0;
247
248        // Safety: string_run is a valid utf8 string, since we only split on ascii sequences
249        let string_run = unsafe { core::str::from_utf8_unchecked(string_run) };
250        if !string_run.is_empty() {
251            write!(writer, "{string_run}")?;
252        }
253
254        let char_escape = match escape {
255            BB => CharEscape::Backspace,
256            TT => CharEscape::Tab,
257            NN => CharEscape::LineFeed,
258            FF => CharEscape::FormFeed,
259            RR => CharEscape::CarriageReturn,
260            QU => CharEscape::Quote,
261            BS => CharEscape::ReverseSolidus,
262            UU => CharEscape::AsciiControl(byte),
263            // Safety: the escape table does not contain any other type of character.
264            _ => unsafe { core::hint::unreachable_unchecked() },
265        };
266        write_char_escape(writer, char_escape)?;
267    }
268
269    // Safety: bytes is a valid utf8 string, since we only split on ascii sequences
270    let string_run = unsafe { core::str::from_utf8_unchecked(bytes) };
271    if string_run.is_empty() {
272        return Ok(());
273    }
274
275    write!(writer, "{string_run}")?;
276    Ok(())
277}
278
279const BB: u8 = b'b'; // \x08
280const TT: u8 = b't'; // \x09
281const NN: u8 = b'n'; // \x0A
282const FF: u8 = b'f'; // \x0C
283const RR: u8 = b'r'; // \x0D
284const QU: u8 = b'"'; // \x22
285const BS: u8 = b'\\'; // \x5C
286const UU: u8 = b'u'; // \x00...\x1F except the ones above
287const __: u8 = 0;
288
289// Lookup table of escape sequences. A value of b'x' at index i means that byte
290// i is escaped as "\x" in JSON. A value of 0 means that byte i is not escaped.
291static ESCAPE: [u8; 256] = [
292    //   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
293    UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, // 0
294    UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, // 1
295    __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2
296    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3
297    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4
298    __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5
299    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6
300    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7
301    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8
302    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9
303    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A
304    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B
305    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C
306    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D
307    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E
308    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F
309];
310
311/// Represents a character escape code in a type-safe manner.
312enum CharEscape {
313    /// An escaped quote `"`
314    Quote,
315    /// An escaped reverse solidus `\`
316    ReverseSolidus,
317    /// An escaped backspace character (usually escaped as `\b`)
318    Backspace,
319    /// An escaped form feed character (usually escaped as `\f`)
320    FormFeed,
321    /// An escaped line feed character (usually escaped as `\n`)
322    LineFeed,
323    /// An escaped carriage return character (usually escaped as `\r`)
324    CarriageReturn,
325    /// An escaped tab character (usually escaped as `\t`)
326    Tab,
327    /// An escaped ASCII plane control character (usually escaped as
328    /// `\u00XX` where `XX` are two hex characters)
329    AsciiControl(u8),
330}
331
332fn write_char_escape<W>(writer: &mut W, char_escape: CharEscape) -> core::fmt::Result
333where
334    W: ?Sized + JsonWrite,
335{
336    let escape_char = match char_escape {
337        CharEscape::Quote => '"',
338        CharEscape::ReverseSolidus => '\\',
339        CharEscape::Backspace => 'b',
340        CharEscape::FormFeed => 'f',
341        CharEscape::LineFeed => 'n',
342        CharEscape::CarriageReturn => 'r',
343        CharEscape::Tab => 't',
344        CharEscape::AsciiControl(_) => 'u',
345    };
346
347    match char_escape {
348        CharEscape::AsciiControl(byte) => {
349            static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";
350            let first = HEX_DIGITS[(byte >> 4) as usize] as char;
351            let second = HEX_DIGITS[(byte & 0xF) as usize] as char;
352            write!(writer, "\\{escape_char}00{first}{second}")
353        }
354        _ => {
355            write!(writer, "\\{escape_char}")
356        }
357    }
358}
359
360fn write_json_object<
361    'i,
362    I: Iterator<Item = (&'i K, &'i V)>,
363    K: WriteJsonKey + 'i,
364    V: WriteJsonValue + 'i,
365    W: JsonWrite + ?Sized,
366>(
367    mut iter: I,
368    writer: &mut W,
369) -> core::fmt::Result {
370    writer.open_object()?;
371    let mut trailing_space = false;
372    if let Some((key, value)) = iter.next() {
373        writer.space()?;
374        writer.key(key)?;
375        writer.keyval_sep()?;
376        writer.space()?;
377        writer.value(value)?;
378        trailing_space = true;
379    }
380    for (key, value) in iter {
381        writer.val_sep()?;
382        writer.space()?;
383        writer.key(key)?;
384        writer.keyval_sep()?;
385        writer.space()?;
386        writer.value(value)?;
387    }
388    if trailing_space {
389        writer.space()?;
390    }
391    writer.close_object()?;
392    Ok(())
393}