use std::collections::{BTreeMap, HashMap};
use crate::driver_module::js_value::MapItem;
use crate::driver_module::js_value::js_json_struct::JsJsonNumber;
use super::JsJsonContext;
use super::js_json_struct::JsJson;
pub trait JsJsonSerialize {
fn to_json(self) -> JsJson;
}
pub trait JsJsonDeserialize
where
Self: Sized,
{
fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext>;
}
impl JsJsonSerialize for String {
fn to_json(self) -> JsJson {
JsJson::String(self)
}
}
impl JsJsonDeserialize for String {
fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext> {
match json {
JsJson::String(value) => Ok(value),
other => {
let message = ["string expected, received ", other.typename()].concat();
Err(context.add(message))
}
}
}
}
macro_rules! impl_js_json_for_number {
($name:ty) => {
impl JsJsonSerialize for $name {
fn to_json(self) -> JsJson {
JsJson::Number(JsJsonNumber(self as f64))
}
}
impl JsJsonDeserialize for $name {
fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext> {
match json {
JsJson::Number(JsJsonNumber(value)) => Ok(value as Self),
other => Err(context
.add(["number($name) expected, received ", other.typename()].concat())),
}
}
}
};
}
impl_js_json_for_number!(i8);
impl_js_json_for_number!(i16);
impl_js_json_for_number!(i32);
impl_js_json_for_number!(i64);
impl_js_json_for_number!(isize);
impl_js_json_for_number!(u8);
impl_js_json_for_number!(u16);
impl_js_json_for_number!(u32);
impl_js_json_for_number!(u64);
impl_js_json_for_number!(usize);
impl_js_json_for_number!(f32);
impl_js_json_for_number!(f64);
impl JsJsonSerialize for bool {
fn to_json(self) -> JsJson {
match self {
false => JsJson::False,
true => JsJson::True,
}
}
}
impl JsJsonDeserialize for bool {
fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext> {
match json {
JsJson::False => Ok(false),
JsJson::True => Ok(true),
other => {
let message = ["bool expected, received ", other.typename()].concat();
Err(context.add(message))
}
}
}
}
impl JsJsonSerialize for () {
fn to_json(self) -> JsJson {
JsJson::Object(BTreeMap::default())
}
}
impl JsJsonDeserialize for () {
fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext> {
let map = json.get_hashmap(&context)?;
if !map.is_empty() {
let message = "Empty {} expected, inner content received".to_string();
return Err(context.add(message));
}
Ok(())
}
}
impl JsJsonSerialize for &str {
fn to_json(self) -> JsJson {
JsJson::String(self.into())
}
}
impl<T: JsJsonSerialize> JsJsonSerialize for Vec<T> {
fn to_json(self) -> JsJson {
let mut list = Vec::new();
for item in self {
list.push(item.to_json());
}
JsJson::List(list)
}
}
impl<T: JsJsonDeserialize> JsJsonDeserialize for Vec<T> {
fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext> {
let mut list = Vec::new();
let JsJson::List(inner) = json else {
let message = ["List expected, received ", json.typename()].concat();
return Err(context.add(message));
};
for (index, item) in inner.into_iter().enumerate() {
list.push(T::from_json(context.add(index), item)?);
}
Ok(list)
}
}
impl<T: JsJsonSerialize> JsJsonSerialize for Option<T> {
fn to_json(self) -> JsJson {
match self {
Some(value) => value.to_json(),
None => JsJson::Null,
}
}
}
impl<T: JsJsonDeserialize> JsJsonDeserialize for Option<T> {
fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext> {
if let JsJson::Null = json {
return Ok(None);
}
Ok(Some(T::from_json(context, json)?))
}
}
impl<T: JsJsonSerialize> JsJsonSerialize for HashMap<String, T> {
fn to_json(self) -> JsJson {
let mut result = BTreeMap::new();
for (key, item) in self {
result.insert(key, item.to_json());
}
JsJson::Object(result)
}
}
impl<T: JsJsonDeserialize> JsJsonDeserialize for HashMap<String, T> {
fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext> {
let map = json.get_hashmap(&context)?;
let mut result = HashMap::new();
for (key, item) in map {
let context = context.add(["field: '", key.as_str(), "'"].concat());
let value = T::from_json(context, item)?;
result.insert(key, value);
}
Ok(result)
}
}
impl<K: JsJsonSerialize + JsJsonDeserialize, T: JsJsonSerialize + JsJsonDeserialize> JsJsonSerialize
for BTreeMap<K, T>
{
fn to_json(self) -> JsJson {
let mut result = Vec::<JsJson>::new();
for (key, item) in self {
result.push(MapItem { key, value: item }.to_json());
}
JsJson::List(result)
}
}
impl<K: JsJsonSerialize + JsJsonDeserialize + Ord, T: JsJsonSerialize + JsJsonDeserialize>
JsJsonDeserialize for BTreeMap<K, T>
{
fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext> {
let JsJson::List(list) = json else {
let message = ["list expected, received ", json.typename()].concat();
return Err(context.add(message));
};
let mut result = BTreeMap::new();
for (index, item) in list.into_iter().enumerate() {
let item = from_json::<MapItem<K, T>>(item)
.map_err(|err| context.add(format!("index={index} error={err}")))?;
let exist = result.insert(item.key, item.value);
if exist.is_some() {
return Err(context.add("Duplicate key"));
}
}
Ok(result)
}
}
pub fn from_json<T: JsJsonDeserialize>(json: JsJson) -> Result<T, String> {
let context = JsJsonContext::new("root");
let result = T::from_json(context, json);
result.map_err(|context| context.convert_to_string())
}
pub fn to_json<T: JsJsonSerialize>(value: T) -> JsJson {
value.to_json()
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq, Clone)]
struct Post {
name: String,
age: u64,
}
impl JsJsonSerialize for Post {
fn to_json(self) -> JsJson {
JsJson::Object(BTreeMap::from([
("name".to_string(), self.name.to_json()),
("age".to_string(), self.age.to_json()),
]))
}
}
impl JsJsonDeserialize for Post {
fn from_json(context: JsJsonContext, mut json: JsJson) -> Result<Self, JsJsonContext> {
Ok(Self {
name: json.get_property(&context, "name")?,
age: json.get_property(&context, "age")?,
})
}
}
#[test]
fn aaaa() {
let aaa = JsJson::String("aaa".into());
let aaa_post = from_json::<Post>(aaa);
assert_eq!(
aaa_post,
Err(String::from("root -> object expected, received string"))
);
let bbb = Post {
name: "dsada".into(),
age: 33,
};
let ccc = bbb.clone().to_json();
let Ok(ddd) = from_json::<Post>(ccc) else {
unreachable!();
};
assert_eq!(bbb, ddd);
}
#[test]
fn test_vec() {
let aaa = Post {
name: "aaa".into(),
age: 11,
};
let bbb = Post {
name: "bbb".into(),
age: 22,
};
let ccc = vec![aaa, bbb];
let ddd = ccc.clone().to_json();
let Ok(eee) = from_json::<Vec<Post>>(ddd) else {
unreachable!();
};
assert_eq!(ccc, eee);
}
#[test]
fn test_unit() {
let unit = JsJson::Object(BTreeMap::default());
let Ok(()) = from_json::<()>(unit.clone()) else {
unreachable!();
};
let unit2 = to_json(());
assert_eq!(unit2, unit)
}
#[test]
fn test_serialize_to_string() {
let data: Vec<u8> = b"Hello, world!".to_vec();
let encoded = data
.iter()
.map(|b| format!("{:02x}", b))
.collect::<String>();
let decoded = (0..encoded.len())
.step_by(2)
.filter_map(|i| {
let decoded_i = u8::from_str_radix(&encoded[i..i + 2], 16);
assert!(decoded_i.is_ok());
decoded_i.ok()
})
.collect::<Vec<u8>>();
println!("Original: {}", String::from_utf8_lossy(&decoded));
}
}