mirror of
https://git.proxmox.com/git/rustc
synced 2026-01-23 00:57:19 +00:00
232 lines
5.4 KiB
Rust
232 lines
5.4 KiB
Rust
//! 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
|
|
}
|
|
}
|