blob: 21c8b85fa2e2a5095ac1077bba15c3a63c87f7ed [file] [log] [blame]
//! 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
}
}