use crate::assert_err;
use super::Dynamic;
use anyhow::{Result, anyhow};
macro_rules! vec_to_json {
($buf:expr, $vec:expr, $variant:ident) => {{
$buf.push('[');
let mut once = ZOnce::new("", ",");
for value in $vec.iter() {
$buf.push_str(once.take());
Dynamic::$variant(value.clone()).to_json($buf);
}
$buf.push(']');
}};
}
pub(crate) fn skip_white(buf: &[u8]) -> Result<usize> {
let mut pos = 0usize;
while pos < buf.len() && (buf[pos] == b' ' || buf[pos] == b'\r' || buf[pos] == b'\t' || buf[pos] == b'\n') {
pos += 1;
}
if pos < buf.len() { Ok(pos) } else { Err(anyhow!("no more data")) }
}
const TOKEN: &[u8] = b"01234567890.-+truefalsenull"; pub trait FromJson: Sized {
fn from_json(buf: &[u8]) -> Result<(Self, usize)>;
fn get_token(buf: &[u8]) -> Result<(&str, usize)> {
let mut pos = 0usize;
while pos < buf.len() && TOKEN.contains(&buf[pos]) {
pos += 1;
}
Ok((std::str::from_utf8(&buf[..pos])?, pos))
}
fn get_string(buf: &[u8]) -> Result<(String, usize)> {
let mut pos = 1usize;
let mut vec = Vec::new();
while pos < buf.len() && buf[pos] != b'"' {
if buf[pos] == b'\\' {
pos += 1;
if pos == buf.len() {
return Err(anyhow!("uncomplete string"));
}
match buf[pos] {
b'\\' => vec.push(b'\\'),
b'"' => vec.push(b'\"'),
b'r' => vec.push(b'\r'),
b'n' => vec.push(b'\n'),
b't' => vec.push(b'\t'),
b'u' => {
let unicode_str = unsafe { std::str::from_utf8_unchecked(&buf[(pos + 1)..(pos + 5)]) };
if let Ok(unicode) = u32::from_str_radix(unicode_str, 16) {
if unicode < 0x80 {
vec.push(unicode as u8);
} else {
if let Some(unicode_char) = char::from_u32(unicode) {
unicode_char.encode_utf8(&mut vec);
}
}
}
pos += 4;
}
_ => {
return Err(anyhow!("unknow escape {}", buf[pos]));
}
}
} else {
vec.push(buf[pos]);
}
pos += 1;
}
if pos < buf.len() {
pos += 1;
}
Ok(String::from_utf8(vec).map(|s| (s, pos))?)
}
}
pub trait ToJson {
fn to_json(&self, buf: &mut String);
}
use smol_str::SmolStr;
use std::{
collections::BTreeMap,
sync::{Arc, RwLock},
};
impl FromJson for Dynamic {
fn from_json(buf: &[u8]) -> Result<(Self, usize)> {
let mut pos = skip_white(buf)?;
if buf[pos] == b'[' {
pos += 1;
pos += skip_white(&buf[pos..])?;
let mut vec = Vec::<Self>::new();
while buf[pos] != b']' {
let (item, size) = Self::from_json(&buf[pos..])?;
vec.push(item);
pos += size;
pos += skip_white(&buf[pos..])?;
if buf[pos] == b',' {
pos += 1;
pos += skip_white(&buf[pos..])?;
}
}
Ok((Dynamic::List(Arc::new(RwLock::new(vec))), pos + 1))
} else if buf[pos] == b'{' {
pos += 1;
pos += skip_white(&buf[pos..])?;
let mut map = BTreeMap::new();
while buf[pos] != b'}' {
assert_err!(buf[pos] != b'"', anyhow!("need a string key {:?}", String::from_utf8_lossy(&buf[pos..])));
let (key, size) = Self::get_string(&buf[pos..])?;
pos += size;
pos += skip_white(&buf[pos..])?;
assert_err!(buf[pos] != b':', anyhow!("need a :"));
pos += 1;
pos += skip_white(&buf[pos..])?;
let (item, size) = Self::from_json(&buf[pos..])?;
map.insert(SmolStr::from(key), item);
pos += size;
pos += skip_white(&buf[pos..])?;
if buf[pos] == b',' {
pos += 1;
pos += skip_white(&buf[pos..])?;
}
}
Ok((Dynamic::Map(Arc::new(RwLock::new(map))), pos + 1))
} else if buf[pos] == b'"' {
let (s, size) = Self::get_string(&buf[pos..])?;
Ok((SmolStr::from(s).into(), size))
} else {
let (token, size) = Self::get_token(&buf[pos..])?;
if token == "true" {
Ok((Dynamic::from(true), size))
} else if token == "false" {
Ok((Dynamic::from(false), size))
} else if token == "null" {
Ok((Dynamic::Null, size))
} else if token.contains('.') {
let v = token.parse::<f64>()?;
Ok((Dynamic::from(v), size))
} else {
let v = token.parse::<i64>()?;
Ok((Dynamic::from(v), size))
}
}
}
}
impl ToJson for &str {
fn to_json(&self, buf: &mut String) {
let mut formatted = self.as_bytes().iter().fold(vec![b'\"'], |mut vec, ch| match ch {
b'\"' => {
vec.extend_from_slice(&[0x5c, 0x22]);
vec
}
b'\\' => {
vec.extend_from_slice(&[0x5c, 0x5c]);
vec
}
b'\n' => {
vec.extend_from_slice(&[0x5c, 0x6e]);
vec
}
b'\r' => {
vec.extend_from_slice(&[0x5c, 0x72]);
vec
}
b'\t' => {
vec.extend_from_slice(&[0x5c, 0x74]);
vec
}
_ => {
vec.push(*ch);
vec
}
});
formatted.push(b'\"');
buf.push_str(unsafe { std::str::from_utf8_unchecked(&formatted) });
}
}
impl ToJson for i64 {
fn to_json(&self, buf: &mut String) {
buf.push_str(&self.to_string());
}
}
use super::ZOnce;
impl ToJson for Dynamic {
fn to_json(&self, buf: &mut String) {
match self {
Self::Iter { idx: _, keys: _, value: _ } => {}
Self::Bool(b) => {
if *b {
buf.push_str("true")
} else {
buf.push_str("false")
}
}
Self::F32(f) => buf.push_str(&f.to_string()),
Self::F64(f) => buf.push_str(&f.to_string()),
Self::I8(i) => buf.push_str(&i.to_string()),
Self::I16(i) => buf.push_str(&i.to_string()),
Self::I32(i) => buf.push_str(&i.to_string()),
Self::I64(i) => buf.push_str(&i.to_string()),
Self::U8(i) => buf.push_str(&i.to_string()),
Self::U16(i) => buf.push_str(&i.to_string()),
Self::U32(i) => buf.push_str(&i.to_string()),
Self::U64(i) => buf.push_str(&i.to_string()),
Self::Null => buf.push_str("null"),
Self::String(s) => s.as_str().to_json(buf),
Self::Bytes(vec) => vec_to_json!(buf, vec, U8),
Self::VecI8(vec) => vec_to_json!(buf, vec, I8),
Self::VecU16(vec) => vec_to_json!(buf, vec, U16),
Self::VecI16(vec) => vec_to_json!(buf, vec, I16),
Self::VecU32(vec) => vec_to_json!(buf, vec, U32),
Self::VecI32(vec) => vec_to_json!(buf, vec, I32),
Self::VecF32(vec) => vec_to_json!(buf, vec, F32),
Self::VecU64(vec) => vec_to_json!(buf, vec, U64),
Self::VecI64(vec) => vec_to_json!(buf, vec, I64),
Self::VecF64(vec) => vec_to_json!(buf, vec, F64),
Self::List(a) => {
buf.push('[');
let mut once = ZOnce::new("", ",\n");
a.read().unwrap().iter().for_each(|item| {
buf.push_str(once.take());
item.to_json(buf);
});
buf.push(']');
}
Self::Map(map) => {
buf.push('{');
let mut once = ZOnce::new("", ",\n");
map.read().unwrap().iter().for_each(|(k, v)| {
buf.push_str(once.take());
k.as_str().to_json(buf);
buf.push_str(": ");
v.to_json(buf);
});
buf.push_str("}\n");
}
Self::Struct { .. } => {
buf.push('{');
let mut once = ZOnce::new("", ",\n");
self.keys().iter().for_each(|k| {
buf.push_str(once.take());
k.as_str().to_json(buf);
buf.push_str(": ");
self.get_dynamic(k).unwrap_or(Dynamic::Null).to_json(buf);
});
buf.push_str("}\n");
}
}
}
}