Skip to main content

openlark_core/api/
responses.rs

1//! API响应类型定义
2//!
3//! 独立的响应处理系统,替代api_resp模块
4
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8/// 原始响应数据
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct RawResponse {
11    /// 响应代码
12    pub code: i32,
13    /// 响应消息
14    pub msg: String,
15    /// 请求数据ID
16    pub request_id: Option<String>,
17    /// 额外数据
18    pub data: Option<serde_json::Value>,
19    /// 错误信息
20    pub error: Option<ErrorInfo>,
21}
22
23/// 错误信息
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ErrorInfo {
26    /// 错误代码
27    pub code: i32,
28    /// 错误消息
29    pub message: String,
30    /// 错误详情
31    pub details: Option<HashMap<String, serde_json::Value>>,
32}
33
34impl Default for RawResponse {
35    fn default() -> Self {
36        Self {
37            code: 0,
38            msg: "success".to_string(),
39            request_id: None,
40            data: None,
41            error: None,
42        }
43    }
44}
45
46impl RawResponse {
47    /// 创建成功响应
48    pub fn success() -> Self {
49        Self::default()
50    }
51
52    /// 创建带数据的成功响应
53    pub fn success_with_data(data: serde_json::Value) -> Self {
54        Self {
55            data: Some(data),
56            ..Default::default()
57        }
58    }
59
60    /// 创建错误响应
61    pub fn error(code: i32, msg: impl Into<String> + Clone) -> Self {
62        let msg_str = msg.into();
63        Self {
64            code,
65            msg: msg_str.clone(),
66            error: Some(ErrorInfo {
67                code,
68                message: msg_str,
69                details: None,
70            }),
71            ..Default::default()
72        }
73    }
74
75    /// 检查是否成功
76    pub fn is_success(&self) -> bool {
77        self.code == 0
78    }
79
80    /// 获取错误信息
81    pub fn get_error(&self) -> Option<&ErrorInfo> {
82        self.error.as_ref()
83    }
84}
85
86/// 响应格式枚举
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
88pub enum ResponseFormat {
89    /// 标准数据格式
90    #[serde(rename = "data")]
91    Data,
92    /// 扁平格式
93    #[serde(rename = "flatten")]
94    Flatten,
95    /// 二进制数据
96    #[serde(rename = "binary")]
97    Binary,
98    /// 文本数据
99    #[serde(rename = "text")]
100    Text,
101    /// 自定义格式
102    #[serde(rename = "custom")]
103    Custom,
104}
105
106impl ResponseFormat {
107    /// 观测/日志用短标签(crate 内部;与解码分派共用,避免双 match)
108    pub(crate) fn as_label(self) -> &'static str {
109        match self {
110            ResponseFormat::Data => "data",
111            ResponseFormat::Flatten => "flatten",
112            ResponseFormat::Binary => "binary",
113            ResponseFormat::Text => "text",
114            ResponseFormat::Custom => "custom",
115        }
116    }
117}
118
119/// API 响应特征:声明解码策略,由 Transport 请求执行层按策略解码。
120///
121/// - [`Self::data_format`] 选择解码路径(不得静默降级到 Data)
122/// - [`Self::requires_payload`]:成功时是否必须解出 `data`(默认 `true`)
123/// - [`Self::empty_success`]:成功且**无** `data` 字段时的显式空载荷(删除类 API);
124///   **禁止**用「能否反序列化 `{}`」探测代替本方法
125/// - Binary / Text / Custom 通过 [`Self::from_binary`] / [`Self::from_text`] /
126///   [`Self::from_custom`] 参与解码,避免运行时 `TypeId` 猜测
127pub trait ApiResponseTrait: Sized + Send + Sync + 'static {
128    /// 获取响应数据格式
129    fn data_format() -> ResponseFormat {
130        ResponseFormat::Data
131    }
132
133    /// 成功响应是否必须携带可解码 payload。
134    /// `()` 等无体响应返回 `false`;默认 `true`。
135    fn requires_payload() -> bool {
136        true
137    }
138
139    /// 成功且响应体无 `data` 字段时的显式空成功值。
140    ///
141    /// 默认 `None`:若同时 [`Self::requires_payload`] 为 true,则解码失败。
142    /// 删除类空 struct 应返回 `Some(Self { .. })`。
143    fn empty_success() -> Option<Self> {
144        None
145    }
146
147    /// Binary 解码:保留文件名 metadata,由类型自行映射。
148    fn from_binary(_file_name: String, _body: Vec<u8>) -> Option<Self> {
149        None
150    }
151
152    /// Text 解码:原始响应体按 UTF-8 文本处理。
153    fn from_text(_text: String) -> Option<Self> {
154        None
155    }
156
157    /// Custom 解码:原始字节 + Content-Type,未实现则解码失败。
158    fn from_custom(_body: Vec<u8>, _content_type: Option<&str>) -> Option<Self> {
159        None
160    }
161}
162
163/// 通用响应结构
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct Response<T> {
166    /// 响应数据
167    pub data: Option<T>,
168    /// 原始响应
169    pub raw_response: RawResponse,
170}
171
172impl<T> Response<T> {
173    /// 创建新响应
174    pub fn new(data: Option<T>, raw_response: RawResponse) -> Self {
175        Self { data, raw_response }
176    }
177
178    /// 创建成功响应
179    pub fn success(data: T) -> Self {
180        Self {
181            data: Some(data),
182            raw_response: RawResponse::success(),
183        }
184    }
185
186    /// 创建空成功响应
187    pub fn success_empty() -> Self {
188        Self {
189            data: None,
190            raw_response: RawResponse::success(),
191        }
192    }
193
194    /// 创建错误响应
195    pub fn error(code: i32, msg: impl Into<String> + Clone) -> Self {
196        Self {
197            data: None,
198            raw_response: RawResponse::error(code, msg),
199        }
200    }
201
202    /// 检查是否成功
203    pub fn is_success(&self) -> bool {
204        self.raw_response.is_success()
205    }
206
207    /// 获取响应代码
208    pub fn code(&self) -> i32 {
209        self.raw_response.code
210    }
211
212    /// 获取响应消息
213    pub fn message(&self) -> &str {
214        &self.raw_response.msg
215    }
216
217    /// 获取响应消息(兼容方法)
218    pub fn msg(&self) -> &str {
219        &self.raw_response.msg
220    }
221
222    /// 获取数据
223    pub fn data(&self) -> Option<&T> {
224        self.data.as_ref()
225    }
226
227    /// 获取原始响应
228    pub fn raw(&self) -> &RawResponse {
229        &self.raw_response
230    }
231
232    /// 转换为结果类型
233    pub fn into_result(self) -> Result<T, crate::error::CoreError> {
234        let is_success = self.is_success();
235        let code = self.raw_response.code;
236        let request_id = self.raw_response.request_id.clone();
237
238        if is_success {
239            match self.data {
240                Some(data) => Ok(data),
241                None => Err(crate::error::api_error(
242                    code as u16,
243                    "response",
244                    "响应数据为空",
245                    request_id,
246                )),
247            }
248        } else {
249            Err(crate::error::api_error(
250                code as u16,
251                "response",
252                self.raw_response.msg.clone(),
253                request_id,
254            ))
255        }
256    }
257}
258
259// 为常见类型实现 ApiResponseTrait
260impl ApiResponseTrait for serde_json::Value {}
261// String 默认 Data 格式(JSON envelope);Text 请用自定义类型并覆写 data_format + from_text
262impl ApiResponseTrait for String {}
263impl ApiResponseTrait for Vec<u8> {
264    fn data_format() -> ResponseFormat {
265        ResponseFormat::Binary
266    }
267
268    fn from_binary(_file_name: String, body: Vec<u8>) -> Option<Self> {
269        Some(body)
270    }
271}
272impl ApiResponseTrait for () {
273    fn requires_payload() -> bool {
274        false
275    }
276}
277
278// 类型别名,用于向后兼容
279/// 基础响应类型别名
280pub type BaseResponse<T> = Response<T>;
281
282#[cfg(test)]
283mod tests {
284    use super::*;
285
286    #[test]
287    fn test_raw_response_default() {
288        let response = RawResponse::default();
289        assert_eq!(response.code, 0);
290        assert_eq!(response.msg, "success");
291        assert!(response.request_id.is_none());
292        assert!(response.data.is_none());
293        assert!(response.error.is_none());
294    }
295
296    #[test]
297    fn test_raw_response_success() {
298        let response = RawResponse::success();
299        assert!(response.is_success());
300    }
301
302    #[test]
303    fn test_raw_response_success_with_data() {
304        let data = serde_json::json!({"key": "value"});
305        let response = RawResponse::success_with_data(data.clone());
306        assert!(response.is_success());
307        assert_eq!(response.data, Some(data));
308    }
309
310    #[test]
311    fn test_raw_response_error() {
312        let response = RawResponse::error(400, "Bad Request");
313        assert!(!response.is_success());
314        assert_eq!(response.code, 400);
315        assert!(response.error.is_some());
316    }
317
318    #[test]
319    fn test_raw_response_get_error() {
320        let response = RawResponse::error(404, "Not Found");
321        let error = response.get_error();
322        assert!(error.is_some());
323        assert_eq!(error.unwrap().code, 404);
324    }
325
326    #[test]
327    fn test_raw_response_serialization() {
328        let response = RawResponse::success_with_data(serde_json::json!({"test": 123}));
329        let json = serde_json::to_string(&response).unwrap();
330        let parsed: RawResponse = serde_json::from_str(&json).expect("JSON 反序列化失败");
331        assert!(parsed.is_success());
332    }
333
334    #[test]
335    fn test_error_info_creation() {
336        let error = ErrorInfo {
337            code: 500,
338            message: "Internal Error".to_string(),
339            details: None,
340        };
341        assert_eq!(error.code, 500);
342        assert_eq!(error.message, "Internal Error");
343    }
344
345    #[test]
346    fn test_response_format() {
347        assert_eq!(ResponseFormat::Data, ResponseFormat::Data);
348        assert_ne!(ResponseFormat::Data, ResponseFormat::Flatten);
349    }
350
351    #[test]
352    fn test_response_format_binary() {
353        assert_eq!(<Vec<u8>>::data_format(), ResponseFormat::Binary);
354    }
355
356    #[test]
357    fn test_response_format_default() {
358        assert_eq!(<()>::data_format(), ResponseFormat::Data);
359    }
360
361    #[test]
362    fn test_response_deserialize_requires_raw_response() {
363        let payload = r#"{"code":400,"msg":"Bad Request"}"#;
364        let parsed = serde_json::from_str::<Response<serde_json::Value>>(payload);
365        assert!(parsed.is_err());
366    }
367
368    #[test]
369    fn test_response_deserialize_with_raw_response_error_keeps_code_and_msg() {
370        let payload = r#"{"raw_response":{"code":400,"msg":"Bad Request","request_id":null,"data":null,"error":null},"data":null}"#;
371        let parsed = serde_json::from_str::<Response<serde_json::Value>>(payload)
372            .expect("JSON 反序列化失败");
373        assert_eq!(parsed.raw_response.code, 400);
374        assert_eq!(parsed.raw_response.msg, "Bad Request");
375        assert!(!parsed.is_success());
376    }
377}