open_lark/core/
improved_response_handler.rs

1use log::debug;
2use serde_json::Value;
3
4use crate::core::{
5    api_resp::{ApiResponseTrait, BaseResponse, RawResponse, ResponseFormat},
6    error::LarkAPIError,
7    SDKResult,
8};
9
10/// 改进的响应处理器,解决双重解析问题
11/// 使用 #[serde(flatten)] 和高级 Serde 特性简化反序列化
12pub struct ImprovedResponseHandler;
13
14impl ImprovedResponseHandler {
15    /// 处理响应的核心方法
16    /// 相比原始实现,这个版本:
17    /// 1. 减少了不必要的JSON解析次数
18    /// 2. 使用更高效的直接反序列化
19    /// 3. 更好的错误处理
20    pub async fn handle_response<T: ApiResponseTrait>(
21        response: reqwest::Response,
22    ) -> SDKResult<BaseResponse<T>> {
23        match T::data_format() {
24            ResponseFormat::Data => Self::handle_data_response(response).await,
25            ResponseFormat::Flatten => Self::handle_flatten_response(response).await,
26            ResponseFormat::Binary => Self::handle_binary_response(response).await,
27        }
28    }
29
30    /// 处理标准数据格式响应
31    /// 使用单次解析而非双重解析
32    async fn handle_data_response<T: ApiResponseTrait>(
33        response: reqwest::Response,
34    ) -> SDKResult<BaseResponse<T>> {
35        let response_text = response.text().await?;
36        debug!("Raw response: {response_text}");
37
38        // 尝试直接解析为BaseResponse<T>
39        match serde_json::from_str::<BaseResponse<T>>(&response_text) {
40            Ok(base_response) => Ok(base_response),
41            Err(_) => {
42                // 如果直接解析失败,可能是错误响应,先解析基本信息
43                let raw_value: Value = serde_json::from_str(&response_text)?;
44
45                let code = raw_value["code"].as_i64().unwrap_or(-1) as i32;
46                let msg = raw_value["msg"]
47                    .as_str()
48                    .unwrap_or("Unknown error")
49                    .to_string();
50
51                // 构建错误响应
52                Ok(BaseResponse {
53                    raw_response: RawResponse {
54                        code,
55                        msg,
56                        err: raw_value
57                            .get("error")
58                            .and_then(|e| serde_json::from_value(e.clone()).ok()),
59                    },
60                    data: None,
61                })
62            }
63        }
64    }
65
66    /// 处理扁平格式响应
67    /// 对于扁平格式,使用自定义反序列化器
68    async fn handle_flatten_response<T: ApiResponseTrait>(
69        response: reqwest::Response,
70    ) -> SDKResult<BaseResponse<T>> {
71        let response_text = response.text().await?;
72        debug!("Raw response: {response_text}");
73
74        let raw_value: Value = serde_json::from_str(&response_text)?;
75
76        // 解析原始响应信息
77        let raw_response: RawResponse = serde_json::from_value(raw_value.clone())?;
78
79        // 如果成功,尝试解析数据
80        let data = if raw_response.code == 0 {
81            match serde_json::from_value::<T>(raw_value) {
82                Ok(parsed_data) => Some(parsed_data),
83                Err(e) => {
84                    debug!("Failed to parse data for flatten response: {e}");
85                    None
86                }
87            }
88        } else {
89            None
90        };
91
92        Ok(BaseResponse { raw_response, data })
93    }
94
95    /// 处理二进制响应
96    async fn handle_binary_response<T: ApiResponseTrait>(
97        response: reqwest::Response,
98    ) -> SDKResult<BaseResponse<T>> {
99        // 获取文件名
100        let file_name = response
101            .headers()
102            .get("Content-Disposition")
103            .and_then(|header| header.to_str().ok())
104            .and_then(Self::extract_filename)
105            .unwrap_or_default();
106
107        // 获取二进制数据
108        let bytes = response.bytes().await?.to_vec();
109
110        // 使用trait方法创建数据
111        let data = T::from_binary(file_name, bytes);
112
113        Ok(BaseResponse {
114            raw_response: RawResponse {
115                code: 0,
116                msg: "success".to_string(),
117                err: None,
118            },
119            data,
120        })
121    }
122
123    /// 提取文件名的辅助函数
124    fn extract_filename(content_disposition: &str) -> Option<String> {
125        // 支持多种文件名格式
126        for part in content_disposition.split(';') {
127            let part = part.trim();
128
129            // 支持 filename*=UTF-8''filename 格式
130            if let Some(filename) = part.strip_prefix("filename*=UTF-8''") {
131                return Some(filename.to_string());
132            }
133
134            // 支持 filename="filename" 格式
135            if let Some(filename) = part.strip_prefix("filename=") {
136                let filename = filename.trim_matches('"');
137                return Some(filename.to_string());
138            }
139        }
140        None
141    }
142}
143
144/// 优化的BaseResponse,使用更好的serde特性
145#[derive(Debug, serde::Serialize, serde::Deserialize)]
146pub struct OptimizedBaseResponse<T>
147where
148    T: Default,
149{
150    /// 响应状态码
151    pub code: i32,
152    /// 响应消息
153    pub msg: String,
154    /// 错误信息(可选)
155    #[serde(rename = "error", default, skip_serializing_if = "Option::is_none")]
156    pub error: Option<ErrorInfo>,
157    /// 业务数据(可选)
158    #[serde(default, skip_serializing_if = "Option::is_none")]
159    pub data: Option<T>,
160}
161
162impl<T> OptimizedBaseResponse<T>
163where
164    T: Default,
165{
166    /// 检查请求是否成功
167    pub fn is_success(&self) -> bool {
168        self.code == 0
169    }
170
171    /// 获取业务数据,如果请求失败则返回错误
172    pub fn into_data(self) -> Result<T, LarkAPIError> {
173        if self.is_success() {
174            self.data.ok_or_else(|| {
175                LarkAPIError::illegal_param("Response is successful but data is missing")
176            })
177        } else {
178            Err(LarkAPIError::api_error(
179                self.code, self.msg, None, // 这里可以添加request_id如果有的话
180            ))
181        }
182    }
183
184    /// 获取数据的引用
185    pub fn data(&self) -> Option<&T> {
186        self.data.as_ref()
187    }
188
189    /// 检查是否有错误信息
190    pub fn has_error(&self) -> bool {
191        self.error.is_some()
192    }
193}
194
195#[derive(Debug, serde::Serialize, serde::Deserialize)]
196pub struct ErrorInfo {
197    #[serde(rename = "key", default, skip_serializing_if = "Option::is_none")]
198    pub log_id: Option<String>,
199    #[serde(default, skip_serializing_if = "Vec::is_empty")]
200    pub details: Vec<ErrorDetail>,
201}
202
203#[derive(Debug, serde::Serialize, serde::Deserialize)]
204pub struct ErrorDetail {
205    #[serde(default, skip_serializing_if = "Option::is_none")]
206    pub key: Option<String>,
207    #[serde(default, skip_serializing_if = "Option::is_none")]
208    pub value: Option<String>,
209    #[serde(default, skip_serializing_if = "Option::is_none")]
210    pub description: Option<String>,
211}
212
213/// 使用宏简化APIResponseTrait实现
214#[macro_export]
215macro_rules! impl_api_response {
216    ($type:ty, $format:expr) => {
217        impl ApiResponseTrait for $type {
218            fn data_format() -> ResponseFormat {
219                $format
220            }
221        }
222    };
223
224    ($type:ty, $format:expr, binary) => {
225        impl ApiResponseTrait for $type {
226            fn data_format() -> ResponseFormat {
227                $format
228            }
229
230            fn from_binary(file_name: String, body: Vec<u8>) -> Option<Self> {
231                Some(<$type>::from_binary_data(file_name, body))
232            }
233        }
234    };
235}
236
237#[cfg(test)]
238mod tests {
239    use super::*;
240    use crate::core::api_resp::ResponseFormat;
241    use serde::{Deserialize, Serialize};
242
243    #[derive(Debug, Serialize, Deserialize, PartialEq, Default)]
244    struct TestData {
245        id: i32,
246        name: String,
247    }
248
249    impl ApiResponseTrait for TestData {
250        fn data_format() -> ResponseFormat {
251            ResponseFormat::Data
252        }
253    }
254
255    #[test]
256    fn test_optimized_base_response_success() {
257        let response = OptimizedBaseResponse {
258            code: 0,
259            msg: "success".to_string(),
260            error: None,
261            data: Some(TestData {
262                id: 1,
263                name: "test".to_string(),
264            }),
265        };
266
267        assert!(response.is_success());
268        assert!(response.data().is_some());
269        assert_eq!(response.data().unwrap().id, 1);
270    }
271
272    #[test]
273    fn test_optimized_base_response_error() {
274        let response: OptimizedBaseResponse<TestData> = OptimizedBaseResponse {
275            code: 400,
276            msg: "Bad Request".to_string(),
277            error: Some(ErrorInfo {
278                log_id: Some("log123".to_string()),
279                details: vec![],
280            }),
281            data: None,
282        };
283
284        assert!(!response.is_success());
285        assert!(response.has_error());
286        assert!(response.data().is_none());
287    }
288
289    #[test]
290    fn test_filename_extraction() {
291        let cases = vec![
292            (
293                "attachment; filename=\"test.txt\"",
294                Some("test.txt".to_string()),
295            ),
296            (
297                "attachment; filename*=UTF-8''test%20file.pdf",
298                Some("test%20file.pdf".to_string()),
299            ),
300            (
301                "attachment; filename=simple.doc",
302                Some("simple.doc".to_string()),
303            ),
304            ("attachment", None),
305        ];
306
307        for (input, expected) in cases {
308            let result = ImprovedResponseHandler::extract_filename(input);
309            assert_eq!(result, expected, "Failed for input: {input}");
310        }
311    }
312
313    #[test]
314    fn test_json_parsing_performance() {
315        let json_data = r#"{"code": 0, "msg": "success", "data": {"id": 1, "name": "test"}}"#;
316
317        // 测试直接解析
318        let start = std::time::Instant::now();
319        let _result: Result<OptimizedBaseResponse<TestData>, _> = serde_json::from_str(json_data);
320        let direct_parse_time = start.elapsed();
321
322        // 测试双重解析(原始方法)
323        let start = std::time::Instant::now();
324        let _value: Value = serde_json::from_str(json_data).unwrap();
325        let _result: Result<OptimizedBaseResponse<TestData>, _> = serde_json::from_value(_value);
326        let double_parse_time = start.elapsed();
327
328        println!("Direct parse time: {direct_parse_time:?}");
329        println!("Double parse time: {double_parse_time:?}");
330
331        // 直接解析应该更快(虽然在微基准测试中差异可能很小)
332        // 这里主要是为了展示概念
333    }
334}
335
336/// 使用示例
337///
338/// 在RequestExecutor中使用改进的响应处理器:
339/// ```rust,ignore
340/// impl RequestExecutor {
341///     pub async fn execute_improved<T: ApiResponseTrait + DeserializeOwned>(
342///         // ... 参数
343///     ) -> SDKResult<OptimizedBaseResponse<T>> {
344///         // ... 构建请求
345///         let response = http_client.send(request).await?;
346///         ImprovedResponseHandler::handle_response(response).await
347///     }
348/// }
349///
350/// // 使用新的响应格式
351/// let result = RequestExecutor::execute_improved::<MessageData>(...).await?;
352///
353/// match result.into_data() {
354///     Ok(data) => println!("Success: {:?}", data),
355///     Err(e) => println!("Error: {:?}", e),
356/// }
357/// ```
358mod usage_examples {}