use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RawResponse {
pub code: i32,
pub msg: String,
pub request_id: Option<String>,
pub data: Option<serde_json::Value>,
pub error: Option<ErrorInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorInfo {
pub code: i32,
pub message: String,
pub details: Option<HashMap<String, serde_json::Value>>,
}
impl Default for RawResponse {
fn default() -> Self {
Self {
code: 0,
msg: "success".to_string(),
request_id: None,
data: None,
error: None,
}
}
}
impl RawResponse {
pub fn success() -> Self {
Self::default()
}
pub fn success_with_data(data: serde_json::Value) -> Self {
Self {
data: Some(data),
..Default::default()
}
}
pub fn error(code: i32, msg: impl Into<String> + Clone) -> Self {
let msg_str = msg.into();
Self {
code,
msg: msg_str.clone(),
error: Some(ErrorInfo {
code,
message: msg_str,
details: None,
}),
..Default::default()
}
}
pub fn is_success(&self) -> bool {
self.code == 0
}
pub fn get_error(&self) -> Option<&ErrorInfo> {
self.error.as_ref()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ResponseFormat {
#[serde(rename = "data")]
Data,
#[serde(rename = "flatten")]
Flatten,
#[serde(rename = "binary")]
Binary,
#[serde(rename = "text")]
Text,
#[serde(rename = "custom")]
Custom,
}
pub trait ApiResponseTrait: Send + Sync + 'static {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Response<T> {
pub data: Option<T>,
pub raw_response: RawResponse,
}
impl<T> Response<T> {
pub fn new(data: Option<T>, raw_response: RawResponse) -> Self {
Self { data, raw_response }
}
pub fn success(data: T) -> Self {
Self {
data: Some(data),
raw_response: RawResponse::success(),
}
}
pub fn success_empty() -> Self {
Self {
data: None,
raw_response: RawResponse::success(),
}
}
pub fn error(code: i32, msg: impl Into<String> + Clone) -> Self {
Self {
data: None,
raw_response: RawResponse::error(code, msg),
}
}
pub fn is_success(&self) -> bool {
self.raw_response.is_success()
}
pub fn code(&self) -> i32 {
self.raw_response.code
}
pub fn message(&self) -> &str {
&self.raw_response.msg
}
pub fn msg(&self) -> &str {
&self.raw_response.msg
}
pub fn data(&self) -> Option<&T> {
self.data.as_ref()
}
pub fn raw(&self) -> &RawResponse {
&self.raw_response
}
pub fn into_result(self) -> Result<T, crate::error::CoreError> {
let is_success = self.is_success();
let code = self.raw_response.code;
let request_id = self.raw_response.request_id.clone();
if is_success {
match self.data {
Some(data) => Ok(data),
None => Err(crate::error::api_error(
code as u16,
"response",
"响应数据为空",
request_id,
)),
}
} else {
Err(crate::error::api_error(
code as u16,
"response",
self.raw_response.msg.clone(),
request_id,
))
}
}
}
impl ApiResponseTrait for serde_json::Value {}
impl ApiResponseTrait for String {}
impl ApiResponseTrait for Vec<u8> {
fn data_format() -> ResponseFormat {
ResponseFormat::Binary
}
}
impl ApiResponseTrait for () {}
pub type BaseResponse<T> = Response<T>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_raw_response_default() {
let response = RawResponse::default();
assert_eq!(response.code, 0);
assert_eq!(response.msg, "success");
assert!(response.request_id.is_none());
assert!(response.data.is_none());
assert!(response.error.is_none());
}
#[test]
fn test_raw_response_success() {
let response = RawResponse::success();
assert!(response.is_success());
}
#[test]
fn test_raw_response_success_with_data() {
let data = serde_json::json!({"key": "value"});
let response = RawResponse::success_with_data(data.clone());
assert!(response.is_success());
assert_eq!(response.data, Some(data));
}
#[test]
fn test_raw_response_error() {
let response = RawResponse::error(400, "Bad Request");
assert!(!response.is_success());
assert_eq!(response.code, 400);
assert!(response.error.is_some());
}
#[test]
fn test_raw_response_get_error() {
let response = RawResponse::error(404, "Not Found");
let error = response.get_error();
assert!(error.is_some());
assert_eq!(error.unwrap().code, 404);
}
#[test]
fn test_raw_response_serialization() {
let response = RawResponse::success_with_data(serde_json::json!({"test": 123}));
let json = serde_json::to_string(&response).unwrap();
let parsed: RawResponse = serde_json::from_str(&json).unwrap();
assert!(parsed.is_success());
}
#[test]
fn test_error_info_creation() {
let error = ErrorInfo {
code: 500,
message: "Internal Error".to_string(),
details: None,
};
assert_eq!(error.code, 500);
assert_eq!(error.message, "Internal Error");
}
#[test]
fn test_response_format() {
assert_eq!(ResponseFormat::Data, ResponseFormat::Data);
assert_ne!(ResponseFormat::Data, ResponseFormat::Flatten);
}
#[test]
fn test_response_format_binary() {
assert_eq!(<Vec<u8>>::data_format(), ResponseFormat::Binary);
}
#[test]
fn test_response_format_default() {
assert_eq!(<()>::data_format(), ResponseFormat::Data);
}
#[test]
fn test_response_deserialize_requires_raw_response() {
let payload = r#"{"code":400,"msg":"Bad Request"}"#;
let parsed = serde_json::from_str::<Response<serde_json::Value>>(payload);
assert!(parsed.is_err());
}
#[test]
fn test_response_deserialize_with_raw_response_error_keeps_code_and_msg() {
let payload = r#"{"raw_response":{"code":400,"msg":"Bad Request","request_id":null,"data":null,"error":null},"data":null}"#;
let parsed = serde_json::from_str::<Response<serde_json::Value>>(payload).unwrap();
assert_eq!(parsed.raw_response.code, 400);
assert_eq!(parsed.raw_response.msg, "Bad Request");
assert!(!parsed.is_success());
}
}