xody 0.2.4

Utilities Library
Documentation
use serde::{Deserialize, Serialize};

use crate::traits::ICallRet;
pub type DataContentType = serde_json::Value;

const SUCCESS_CODE: &str = "0";
const FAIL_CODE: &str = "1";

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

impl<T> CallReq<T>
where
    T: Serialize,
{
    pub fn new(method: impl Into<String>, args: T) -> Self {
        Self {
            method: method.into(),
            args,
        }
    }
    pub fn method(&self) -> &str {
        &self.method
    }
    pub fn args(&self) -> &T {
        &self.args
    }
    pub fn into_args(self) -> T {
        self.args
    }
}
#[derive(Deserialize, Serialize, PartialEq, Eq, Debug)]
pub struct CallRet<T>
where
    T: Serialize,
{
    code: String,
    msg: String,
    data: T,
}

impl<T> CallRet<T>
where
    T: Serialize,
{
    pub fn new(code: impl Into<String>, msg: impl Into<String>, data: T) -> Self {
        Self {
            code: code.into(),
            msg: msg.into(),
            data,
        }
    }
    pub fn suc(data: T) -> Self {
        Self::new(SUCCESS_CODE, "", data)
    }

    pub fn suc_default() -> Self
    where
        T: Default,
    {
        Self::new(SUCCESS_CODE, "", T::default())
    }

    pub fn fail(msg: impl Into<String>, data: T) -> Self {
        Self::new(FAIL_CODE, msg, data)
    }

    pub fn fail_default(msg: impl Into<String>) -> Self
    where
        T: Default,
    {
        Self::new(FAIL_CODE, msg, T::default())
    }

    pub fn code(&self) -> &str {
        &self.code
    }
    pub fn msg(&self) -> &str {
        &self.msg
    }
    pub fn data(&self) -> &T {
        &self.data
    }

    pub fn ok_or_else<E, F>(self, err: F) -> std::result::Result<T, E>
    where
        F: FnOnce(String) -> E,
    {
        if self.code == SUCCESS_CODE {
            Ok(self.data)
        } else {
            Err(err(self.msg))
        }
    }
}

impl<T> From<T> for CallRet<serde_json::Value>
where
    T: ICallRet + Serialize,
{
    fn from(value: T) -> Self {
        let (code, msg, data) = value.split();
        CallRet { code, msg, data }
    }
}

impl<T, E> From<Result<T, E>> for CallRet<T>
where
    T: Serialize + Default,
    E: ToString,
    E: std::fmt::Display,
{
    fn from(value: Result<T, E>) -> Self {
        match value {
            Ok(data) => CallRet::suc(data),
            Err(err) => CallRet::fail_default(format!("{:#}", err)),
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use anyhow::Error;
    use serde_json;
    use serde_json::json;

    type CallReqAny = CallReq<serde_json::Value>;
    type CallRetAny = CallRet<serde_json::Value>;

    #[test]
    fn test_callreq() {
        {
            let req = CallReq::new("test_method", 12);
            assert_eq!(req.method(), "test_method");
            assert_eq!(req.args(), &12);
            let json_str = serde_json::to_string(&req).unwrap();
            assert_eq!(json_str, r#"{"method":"test_method","args":12}"#);
            let callreq_any: CallReqAny = serde_json::from_str(&json_str).unwrap();
            assert_eq!(callreq_any.args().to_owned(), json!(12));
            assert_eq!(callreq_any.into_args(), json!(12));
        }

        {
            let req = CallReqAny::new("test_method", serde_json::Value::Null);
            let json_str = serde_json::to_string(&req).unwrap();
            assert_eq!(json_str, r#"{"method":"test_method","args":null}"#);
        }
        {
            let req = CallReqAny::new("test_method", json!({}));
            let json_str = serde_json::to_string(&req).unwrap();
            assert_eq!(json_str, r#"{"method":"test_method","args":{}}"#);
        }
    }

    #[test]
    fn test_case_callret() {
        assert_eq!(
            serde_json::to_string(&CallRet::<()>::suc_default()).unwrap(),
            r#"{"code":"0","msg":"","data":null}"#
        );
        assert_eq!(
            serde_json::to_string(&CallRet::<()>::fail_default("unkown")).unwrap(),
            r#"{"code":"1","msg":"unkown","data":null}"#
        );
        assert_eq!(
            serde_json::to_string(&CallRet::<serde_json::Value>::suc(json!({}))).unwrap(),
            r#"{"code":"0","msg":"","data":{}}"#
        );
    }

    #[test]
    fn test_callret_from_icallret() {
        #[derive(Serialize)]
        struct DummyRet {
            code: String,
            msg: String,
            data: serde_json::Value,
        }

        impl ICallRet for DummyRet {
            fn split(self) -> (String, String, serde_json::Value) {
                (self.code, self.msg, self.data)
            }
        }

        let dummy = DummyRet {
            code: "0".to_string(),
            msg: "ok".to_string(),
            data: serde_json::json!({"foo": 42}),
        };
        let callret: CallRetAny = CallRet::from(dummy);
        assert_eq!(callret.code(), "0");
        assert_eq!(callret.msg(), "ok");
        assert_eq!(callret.data().clone(), serde_json::json!({"foo": 42}));
    }

    #[test]
    fn test_callret_deserialize() {
        let json = r#"{
            "code": "0",
            "msg": "",
            "data": "hello"
        }"#;
        let callret: CallRetAny = serde_json::from_str(json).unwrap();
        assert_eq!(callret.code(), "0");
        assert_eq!(callret.msg(), "");
        assert_eq!(callret.data().clone(), serde_json::json!("hello"));
    }

    #[test]
    fn test_callret_ok_or_else_anyhow() {
        {
            let ok_ret = CallRet::suc(json!(123));
            let val: serde_json::Value = ok_ret.ok_or_else(Error::msg).unwrap();
            assert_eq!(val, json!(123));
        }
        {
            let fail_ret = CallRet::fail("fail reason", json!(null));
            let err = fail_ret.ok_or_else(Error::msg).unwrap_err();
            assert_eq!(err.to_string(), "fail reason");
        }
    }

    #[test]
    fn test_callret_from_result() {
        let ok: Result<i32, &str> = Ok(42);
        let ok_ret: CallRet<i32> = CallRet::from(ok);
        assert_eq!(ok_ret.code(), "0");
        assert_eq!(ok_ret.msg(), "");
        assert_eq!(ok_ret.data(), &42);

        let err: Result<i32, &str> = Err("bad request");
        let err_ret: CallRet<i32> = CallRet::from(err);
        assert_eq!(err_ret.code(), "1");
        assert_eq!(err_ret.msg(), "bad request");
        assert_eq!(err_ret.data(), &0);
    }

    #[test]
    fn test_callret_from_anyhow_result() {
        let ok: anyhow::Result<serde_json::Value> = Ok(json!({"n": 1}));
        let ok_ret: CallRet<serde_json::Value> = CallRet::from(ok);
        assert_eq!(ok_ret.code(), "0");
        assert_eq!(ok_ret.msg(), "");
        assert_eq!(ok_ret.data().clone(), json!({"n": 1}));

        let err: anyhow::Result<serde_json::Value> = Err(Error::msg("boom"));
        let err_ret: CallRet<serde_json::Value> = CallRet::from(err);
        assert_eq!(err_ret.code(), "1");
        assert_eq!(err_ret.msg(), "boom");
        assert_eq!(err_ret.data().clone(), json!(null));
    }

    #[test]
    fn test_partial_eq_for_callret_and_callreq() {
        // CallReq PartialEq, Eq
        let req1 = CallReq::new("foo", json!(1));
        let req2 = CallReq::new("foo", json!(1));
        let req3 = CallReq::new("bar", json!(2));
        assert_eq!(req1, req2);
        assert_ne!(req1, req3);

        // CallRet PartialEq, Eq
        let ret1 = CallRet::suc(json!({"a": 1}));
        let ret2 = CallRet::suc(json!({"a": 1}));
        let ret3 = CallRet::fail("fail", json!(null));
        assert_eq!(ret1, ret2);
        assert_ne!(ret1, ret3);
    }
}