| //! Simple dependency-less macro-less trait-less JSON serialization. |
| //! |
| //! # Example |
| //! |
| //! ``` |
| //! let mut buf = String::new(); |
| //! |
| //! { |
| //! let mut obj = write_json::object(&mut buf); |
| //! obj.string("name", "Peter").number("favorite number", 92.0); |
| //! obj.array("films") |
| //! .string("Drowning By Numbers") |
| //! .string("A Zed & Two Noughts"); |
| //! obj.null("suitcase"); |
| //! } |
| //! |
| //! assert_eq!( |
| //! buf, |
| //! r#"{"name":"Peter","favorite number":92,"films":["Drowning By Numbers","A Zed & Two Noughts"],"suitcase":null}"# |
| //! ) |
| //! ``` |
| |
| #[inline] |
| pub fn null(buf: &mut String) { |
| encode_null(buf, ()); |
| } |
| #[inline] |
| pub fn bool(buf: &mut String, value: bool) { |
| encode_bool(buf, value); |
| } |
| #[inline] |
| pub fn number(buf: &mut String, number: f64) { |
| encode_number(buf, number); |
| } |
| #[inline] |
| pub fn string(buf: &mut String, string: &str) { |
| encode_str(buf, string); |
| } |
| #[inline] |
| pub fn object(buf: &mut String) -> Object<'_> { |
| Object::new(buf) |
| } |
| #[inline] |
| pub fn array(buf: &mut String) -> Array<'_> { |
| Array::new(buf) |
| } |
| |
| pub struct Object<'a> { |
| buf: &'a mut String, |
| first: bool, |
| } |
| |
| impl<'a> Object<'a> { |
| #[inline] |
| fn new(buf: &'a mut String) -> Self { |
| buf.push('{'); |
| Object { buf, first: true } |
| } |
| #[inline] |
| fn key(&mut self, key: &str) { |
| if !self.first { |
| self.buf.push(','); |
| } |
| self.first = false; |
| encode_str(&mut self.buf, key); |
| self.buf.push(':'); |
| } |
| #[inline] |
| fn field<T, F: FnOnce(&mut String, T)>(&mut self, key: &str, enc: F, value: T) -> &mut Self { |
| self.key(key); |
| enc(&mut self.buf, value); |
| self |
| } |
| |
| #[inline] |
| pub fn null(&mut self, key: &str) -> &mut Self { |
| self.field(key, encode_null, ()) |
| } |
| #[inline] |
| pub fn bool(&mut self, key: &str, value: bool) -> &mut Self { |
| self.field(key, encode_bool, value) |
| } |
| #[inline] |
| pub fn number(&mut self, key: &str, value: f64) -> &mut Self { |
| self.field(key, encode_number, value) |
| } |
| #[inline] |
| pub fn string(&mut self, key: &str, value: &str) -> &mut Self { |
| self.field(key, encode_str, value) |
| } |
| #[inline] |
| pub fn object(&mut self, key: &str) -> Object<'_> { |
| self.key(key); |
| Object::new(self.buf) |
| } |
| #[inline] |
| pub fn array(&mut self, key: &str) -> Array<'_> { |
| self.key(key); |
| Array::new(self.buf) |
| } |
| } |
| |
| impl Drop for Object<'_> { |
| #[inline] |
| fn drop(&mut self) { |
| self.buf.push('}') |
| } |
| } |
| |
| pub struct Array<'a> { |
| buf: &'a mut String, |
| first: bool, |
| } |
| |
| impl<'a> Array<'a> { |
| #[inline] |
| fn new(buf: &'a mut String) -> Self { |
| buf.push('['); |
| Array { buf, first: true } |
| } |
| #[inline] |
| fn comma(&mut self) { |
| if !self.first { |
| self.buf.push(','); |
| } |
| self.first = false; |
| } |
| #[inline] |
| fn element<T, F: FnOnce(&mut String, T)>(&mut self, enc: F, value: T) -> &mut Self { |
| self.comma(); |
| enc(&mut self.buf, value); |
| self |
| } |
| |
| #[inline] |
| pub fn null(&mut self) -> &mut Self { |
| self.element(encode_null, ()) |
| } |
| #[inline] |
| pub fn bool(&mut self, value: bool) -> &mut Self { |
| self.element(encode_bool, value) |
| } |
| #[inline] |
| pub fn number(&mut self, value: f64) -> &mut Self { |
| self.element(encode_number, value) |
| } |
| #[inline] |
| pub fn string(&mut self, value: &str) -> &mut Self { |
| self.element(encode_str, value) |
| } |
| #[inline] |
| pub fn object(&mut self) -> Object<'_> { |
| self.comma(); |
| Object::new(self.buf) |
| } |
| #[inline] |
| pub fn array(&mut self) -> Array<'_> { |
| self.comma(); |
| Array::new(self.buf) |
| } |
| } |
| |
| impl Drop for Array<'_> { |
| #[inline] |
| fn drop(&mut self) { |
| self.buf.push(']') |
| } |
| } |
| |
| #[inline] |
| fn encode_null(buf: &mut String, (): ()) { |
| buf.push_str("null") |
| } |
| #[inline] |
| fn encode_bool(buf: &mut String, value: bool) { |
| buf.push_str(if value { "true" } else { "false" }) |
| } |
| #[inline] |
| fn encode_number(buf: &mut String, number: f64) { |
| use std::fmt::Write; |
| let _ = write!(buf, "{}", number); |
| } |
| |
| #[inline] |
| fn encode_str(buf: &mut String, s: &str) { |
| buf.reserve(s.len() + 2); |
| buf.push('\"'); |
| if s.bytes() |
| .all(|b| 0x1F < b && b != b'"' && b != b'\\' && b < 0x7F) |
| { |
| buf.push_str(s) |
| } else { |
| slow_path(buf, s) |
| } |
| buf.push('\"'); |
| |
| #[inline(never)] |
| fn slow_path(buf: &mut String, s: &str) { |
| for c in s.chars() { |
| if (c as u32) < 256 { |
| let b = c as u8; |
| match b { |
| b'\\' | b'"' => push_escape(buf, c), |
| b'\n' => push_escape(buf, 'n'), |
| b'\r' => push_escape(buf, 'r'), |
| b'\t' => push_escape(buf, 't'), |
| 0..=0x1F | 0x7F..=0x9F => { |
| push_escape(buf, 'u'); |
| buf.push_str("00"); |
| buf.push(hex(b >> 4)); |
| buf.push(hex(b & 0xF)); |
| } |
| _ => buf.push(c), |
| } |
| } else { |
| buf.push(c) |
| } |
| } |
| } |
| |
| #[inline] |
| fn push_escape(buf: &mut String, c: char) { |
| buf.push('\\'); |
| buf.push(c); |
| } |
| |
| #[inline] |
| fn hex(b: u8) -> char { |
| (b"0123456789ABCDEF"[(b & 0xF) as usize]) as char |
| } |
| } |