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