xody 0.1.3

Utilities Library
Documentation
use std::ops::Deref;

use serde::{Deserialize, Serialize};
pub type DataContentType = serde_json::Value;
use anyhow::Result;

pub type DataTypeOpt = Option<DataType>;

#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
pub struct CallReq {
    method: String,
    args: DataTypeOpt,
}

#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct DataType(DataContentType);
impl Deref for DataType {
    type Target = DataContentType;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl DataType {
    pub fn new<T>(t: T) -> Result<Self>
    where
        T: serde::Serialize,
    {
        let v = serde_json::to_value(t)?;
        Ok(DataType(v))
    }

    pub fn decode_as<T>(&self) -> Result<T>
    where
        T: serde::de::DeserializeOwned,
    {
        let t = serde_json::from_value(self.0.clone())?;
        Ok(t)
    }
}

impl CallReq {
    pub fn try_new<T>(method: &str, t: T) -> Result<Self>
    where
        T: serde::Serialize,
    {
        let args = Some(DataType::new(t)?);
        Ok(Self {
            method: method.to_string(),
            args,
        })
    }
    pub fn new(method: &str, args: DataTypeOpt) -> Self {
        Self {
            method: method.to_string(),
            args,
        }
    }
    pub fn new_from_json(method: &str, args: serde_json::Value) -> Result<Self> {
        Ok(Self {
            method: method.to_string(),
            args: Some(DataType::new(args)?),
        })
    }

    pub fn args(&self) -> &DataTypeOpt {
        &self.args
    }
    pub fn method(&self) -> &str {
        self.method.as_str()
    }
    pub fn args_as<T>(&self) -> Result<T>
    where
        T: serde::de::DeserializeOwned,
    {
        let v = self
            .args
            .clone()
            .ok_or_else(|| anyhow::anyhow!("args in None"))?
            .decode_as::<T>()?;
        Ok(v)
    }
}
#[derive(Deserialize, Serialize)]
pub struct CallRet {
    code: String,
    msg: String,
    data: DataTypeOpt,
}
impl CallRet {
    pub fn suc(data: DataTypeOpt) -> Self {
        Self {
            code: "0".to_string(),
            msg: "".to_string(),
            data,
        }
    }

    pub fn suc_as<T>(data: T) -> Result<Self>
    where
        T: serde::Serialize,
    {
        Ok(Self {
            code: "0".to_string(),
            msg: "".to_string(),
            data: Some(DataType::new(data)?),
        })
    }
    pub fn fail(msg: &str, data: DataTypeOpt) -> Self {
        Self {
            code: "1".to_string(),
            msg: msg.to_owned(),
            data,
        }
    }
    pub fn fail_as<T>(msg: &str, data: T) -> Result<Self>
    where
        T: serde::Serialize,
    {
        Ok(Self {
            code: "1".to_string(),
            msg: msg.to_owned(),
            data: Some(DataType::new(data)?),
        })
    }

    pub fn data_as<T>(&self) -> Result<T>
    where
        T: serde::de::DeserializeOwned,
    {
        let v = self
            .data
            .clone()
            .ok_or_else(|| anyhow::anyhow!("data in None"))?
            .decode_as::<T>()?;
        Ok(v)
    }

    pub fn to_result(self) -> anyhow::Result<DataTypeOpt> {
        if self.is_suc() {
            return Ok(self.data);
        }
        anyhow::bail!("{}", self.msg)
    }
    pub fn is_suc(&self) -> bool {
        self.code == "0"
    }

    pub fn unwrap_err(self) -> String {
        if self.is_suc() {
            panic!("callret is success, not fail")
        }
        self.msg
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use super::*;
    use serde_json;

    #[test]
    fn test_callreq_new_and_accessors() {
        let data = DataType::new::<u32>(12).unwrap();

        assert_eq!(data.to_string(), "12");
        let args = Some(data);
        let req = CallReq::new("test_method", args.clone());
        assert_eq!(req.method(), "test_method");
        assert_eq!(req.args(), &args);
        assert_eq!(req, CallReq::try_new("test_method", 12).unwrap());
    }

    #[test]
    fn test_callret_suc_and_fail() {
        let args = Some(DataType::new::<bool>(true).unwrap());

        let suc = CallRet::suc_as::<bool>(true).unwrap();
        assert_eq!(suc.code, "0");
        assert_eq!(suc.msg, "");
        assert_eq!(suc.data, args);

        let fail = CallRet::fail_as::<bool>("error", true).unwrap();
        assert_eq!(fail.code, "1");
        assert_eq!(fail.msg, "error");
        assert!(fail.data_as::<bool>().unwrap());
    }

    #[test]
    fn test_callreq_deserialize() {
        let json = r#"{
            "method": "do_something",
            "args": 123
        }"#;
        let req: CallReq = serde_json::from_str(json).unwrap();
        assert_eq!(req.method(), "do_something");

        assert_eq!(req.args_as::<u32>().unwrap(), 123);
        assert_eq!(
            serde_json::to_string(&req).unwrap(),
            r#"{"method":"do_something","args":123}"#
        );
    }
    #[test]
    fn test_callret_deserialize() {
        let json = r#"{
            "code": "0",
            "msg": "",
            "data": "hello"
        }"#;
        let ret: CallRet = serde_json::from_str(json).unwrap();
        assert_eq!(ret.code, "0");
        assert_eq!(ret.msg, "");
        assert_eq!(ret.data_as::<String>().unwrap(), "hello");
    }

    #[test]
    fn test_callret_deserialize_data_struct() {
        let json = r#"{
            "code": "0",
            "msg": "",
            "data": {
                "pic": "path_for"
            }
        }"#;
        let ret: CallRet = serde_json::from_str(json).unwrap();
        assert_eq!(ret.code, "0");
        assert_eq!(ret.msg, "");

        #[derive(serde::Deserialize, PartialEq, Eq, Debug)]
        struct DataDetail {
            pic: String,
        }
        assert_eq!(
            ret.data_as::<DataDetail>().unwrap(),
            DataDetail {
                pic: "path_for".to_string()
            }
        );
        assert_eq!(
            ret.data_as::<HashMap<String, String>>().unwrap(),
            [("pic".to_string(), "path_for".to_string())].into()
        );
    }
}