use serde::{Deserialize, Serialize};
use serde_json::Value;
pub type Param = std::collections::HashMap<String, Value>;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Response {
pub result: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extra: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub paging: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub job: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub access: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exception: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub redirect_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub redirect_code: Option<i32>,
#[serde(skip)]
pub request_id: Option<String>,
}
impl Response {
pub fn raw(&self) -> Option<&Value> {
self.data.as_ref()
}
pub fn full_raw(&self) -> serde_json::Map<String, Value> {
match serde_json::to_value(self) {
Ok(Value::Object(map)) => map,
_ => serde_json::Map::new(),
}
}
pub fn apply<T>(&self) -> Result<T, crate::error::RestError>
where
T: serde::de::DeserializeOwned,
{
match &self.data {
Some(data) => serde_json::from_value(data.clone()).map_err(|e| e.into()),
None => serde_json::from_value(Value::Null).map_err(|e| e.into()),
}
}
pub fn get(&self, path: &str) -> Option<&Value> {
let parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
let mut current = self.data.as_ref()?;
for part in parts {
current = match current {
Value::Object(map) => map.get(part)?,
Value::Array(arr) => {
let index: usize = part.parse().ok()?;
arr.get(index)?
}
_ => return None,
};
}
Some(current)
}
pub fn get_string(&self, path: &str) -> Option<String> {
self.get(path)
.and_then(|v| v.as_str().map(|s| s.to_string()))
}
pub fn offset_get(&self, key: &str) -> Option<Value> {
if let Some(stripped) = key.strip_prefix('@') {
match stripped {
"error" => self.error.as_ref().map(|s| Value::String(s.clone())),
"code" => self.code.map(|c| Value::Number(c.into())),
"extra" => self.extra.as_ref().map(|s| Value::String(s.clone())),
"token" => self.token.as_ref().map(|s| Value::String(s.clone())),
"paging" => self.paging.clone(),
"job" => self.job.clone(),
"time" => self.time.clone(),
"access" => self.access.clone(),
"exception" => self.exception.as_ref().map(|s| Value::String(s.clone())),
_ => None,
}
} else {
self.get(key).cloned()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_response_deserialization() {
let json = r#"{
"result": "success",
"data": {"user": {"name": "test"}}
}"#;
let response: Response = serde_json::from_str(json).unwrap();
assert_eq!(response.result, "success");
assert!(response.data.is_some());
}
#[test]
fn test_response_get() {
let json = r#"{
"result": "success",
"data": {"user": {"name": "test"}}
}"#;
let response: Response = serde_json::from_str(json).unwrap();
let name = response.get_string("user/name");
assert_eq!(name, Some("test".to_string()));
}
#[test]
fn test_response_apply() {
#[derive(Deserialize)]
struct User {
name: String,
}
let json = r#"{
"result": "success",
"data": {"name": "test"}
}"#;
let response: Response = serde_json::from_str(json).unwrap();
let user: User = response.apply().unwrap();
assert_eq!(user.name, "test");
}
}