vertigo 0.2.0-alpha

Reactive Real-DOM library for Rust
Documentation
use std::collections::HashMap;
use std::rc::Rc;

use super::js_json_struct::JsJson;

struct JsJsonContextInner {
    parent: Option<Rc<JsJsonContextInner>>,
    current: String,
}

#[derive(Clone)]
pub struct JsJsonContext {
    inner: Rc<JsJsonContextInner>,
}

impl JsJsonContext {
    pub fn new(current: impl Into<String>) -> JsJsonContext {
        Self {
            inner: Rc::new(JsJsonContextInner {
                parent: None,
                current: current.into()
            })
        }
    }

    pub fn add(&self, child: impl ToString) -> JsJsonContext {
        Self {
            inner: Rc::new(JsJsonContextInner {
                parent: Some(self.inner.clone()),
                current: child.to_string()
            })
        }
    }

    pub fn convert_to_string(&self) -> String {
        let mut path = Vec::new();
        let mut current = self.inner.clone();

        loop {
            path.push(current.current.clone());

            let Some(parent) = current.parent.clone() else {
                return path
                    .into_iter()
                    .rev()
                    .collect::<Vec<_>>()
                    .join(" -> ");
            };

            current = parent;
        }
    }
}

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))
            }
        }
    }
}

impl JsJsonSerialize for u64 {
    fn to_json(self) -> JsJson {
        JsJson::Number(self as f64)
    }
}

impl JsJsonDeserialize for u64 {
    fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext> {
        match json {
            JsJson::Number(value) => Ok(value as u64),
            other => {
                let message = ["number(u64) expected, received", other.typename()].concat();
                Err(context.add(message))
            }
        }
    }
}

impl JsJsonSerialize for i64 {
    fn to_json(self) -> JsJson {
        JsJson::Number(self as f64)
    }
}

impl JsJsonDeserialize for i64 {
    fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext> {
        match json {
            JsJson::Number(value) => Ok(value as i64),
            other => {
                let message = ["number(i64) expected, received", other.typename()].concat();
                Err(context.add(message))
            }
        }
    }
}

impl JsJsonSerialize for u32 {
    fn to_json(self) -> JsJson {
        JsJson::Number(self as f64)
    }
}

impl JsJsonDeserialize for u32 {
    fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext> {
        match json {
            JsJson::Number(value) => Ok(value as u32),
            other => {
                let message = ["number(u32) expected, received", other.typename()].concat();
                Err(context.add(message))
            }
        }
    }
}

impl JsJsonSerialize for i32 {
    fn to_json(self) -> JsJson {
        JsJson::Number(self as f64)
    }
}

impl JsJsonDeserialize for i32 {
    fn from_json(context: JsJsonContext, json: JsJson) -> Result<Self, JsJsonContext> {
        match json {
            JsJson::Number(value) => Ok(value as i32),
            other => {
                let message = ["number(i32) expected, received", other.typename()].concat();
                Err(context.add(message))
            }
        }
    }
}

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 &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 = HashMap::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)
    }
}

#[derive(Debug, PartialEq, Clone)]
struct Post {
    name: String,
    age: u64,
}

impl JsJsonSerialize for Post {
    fn to_json(self) -> JsJson {
        JsJson::Object(HashMap::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")?,
        })
    }
}

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()
}


#[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);
}