Skip to main content

openlark_core/api/
mod.rs

1//! API处理模块 - 独立版本
2//!
3//! 现代化、类型安全的API请求和响应处理系统。
4//! 完全独立,不依赖已弃用的api_req/api_resp模块。
5
6// 核心类型定义
7
8pub use responses::RawResponse;
9use std::{collections::HashMap, time::Duration};
10
11/// HTTP方法枚举
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum HttpMethod {
14    /// HTTP GET 方法
15    Get,
16    /// HTTP POST 方法
17    Post,
18    /// HTTP PUT 方法
19    Put,
20    /// HTTP DELETE 方法
21    Delete,
22    /// HTTP PATCH 方法
23    Patch,
24    /// HTTP HEAD 方法
25    Head,
26    /// HTTP OPTIONS 方法
27    Options,
28}
29
30impl HttpMethod {
31    /// 获取 HTTP 方法的字符串表示
32    pub fn as_str(self) -> &'static str {
33        match self {
34            HttpMethod::Get => "GET",
35            HttpMethod::Post => "POST",
36            HttpMethod::Put => "PUT",
37            HttpMethod::Delete => "DELETE",
38            HttpMethod::Patch => "PATCH",
39            HttpMethod::Head => "HEAD",
40            HttpMethod::Options => "OPTIONS",
41        }
42    }
43}
44
45impl std::fmt::Display for HttpMethod {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        write!(f, "{}", self.as_str())
48    }
49}
50
51/// 请求数据枚举
52#[derive(Debug, Clone)]
53pub enum RequestData {
54    /// JSON 格式数据
55    Json(serde_json::Value),
56    /// 纯文本数据
57    Text(String),
58    /// 二进制数据
59    Binary(Vec<u8>),
60    /// 表单数据
61    Form(std::collections::HashMap<String, String>),
62}
63
64// 处理字符串类型 - 优先使用Text处理
65impl From<String> for RequestData {
66    fn from(value: String) -> Self {
67        RequestData::Text(value)
68    }
69}
70
71impl From<&str> for RequestData {
72    fn from(value: &str) -> Self {
73        RequestData::Text(value.to_string())
74    }
75}
76
77// 为JSON值类型提供直接转换
78impl From<serde_json::Value> for RequestData {
79    fn from(value: serde_json::Value) -> Self {
80        RequestData::Json(value)
81    }
82}
83
84// 为Vec<u8>提供二进制数据支持
85impl From<Vec<u8>> for RequestData {
86    fn from(value: Vec<u8>) -> Self {
87        RequestData::Binary(value)
88    }
89}
90
91// 为HashMap<String, String>提供表单数据支持
92impl From<std::collections::HashMap<String, String>> for RequestData {
93    fn from(value: std::collections::HashMap<String, String>) -> Self {
94        RequestData::Form(value)
95    }
96}
97
98// 重新导出响应类型
99pub use responses::{ApiResponseTrait, BaseResponse, ErrorInfo, Response, ResponseFormat};
100
101/// 简化的API请求结构
102#[derive(Debug, Clone)]
103pub struct ApiRequest<R> {
104    pub(crate) method: HttpMethod,
105    pub(crate) url: String,
106    pub(crate) headers: HashMap<String, String>,
107    pub(crate) query: HashMap<String, String>,
108    pub(crate) body: Option<RequestData>,
109    pub(crate) file: Option<Vec<u8>>,
110    pub(crate) timeout: Option<Duration>,
111    pub(crate) _phantom: std::marker::PhantomData<R>,
112}
113
114impl<R> ApiRequest<R> {
115    /// 创建 GET 请求
116    pub fn get(url: impl Into<String>) -> Self {
117        Self {
118            method: HttpMethod::Get,
119            url: url.into(),
120            headers: HashMap::new(),
121            query: HashMap::new(),
122            body: None,
123            file: None,
124            timeout: None,
125            _phantom: std::marker::PhantomData,
126        }
127    }
128
129    /// 创建 POST 请求
130    pub fn post(url: impl Into<String>) -> Self {
131        Self {
132            method: HttpMethod::Post,
133            url: url.into(),
134            headers: HashMap::new(),
135            query: HashMap::new(),
136            body: None,
137            file: None,
138            timeout: None,
139            _phantom: std::marker::PhantomData,
140        }
141    }
142
143    /// 创建 PUT 请求
144    pub fn put(url: impl Into<String>) -> Self {
145        Self {
146            method: HttpMethod::Put,
147            url: url.into(),
148            headers: HashMap::new(),
149            query: HashMap::new(),
150            body: None,
151            file: None,
152            timeout: None,
153            _phantom: std::marker::PhantomData,
154        }
155    }
156
157    /// 创建 PATCH 请求
158    pub fn patch(url: impl Into<String>) -> Self {
159        Self {
160            method: HttpMethod::Patch,
161            url: url.into(),
162            headers: HashMap::new(),
163            query: HashMap::new(),
164            body: None,
165            file: None,
166            timeout: None,
167            _phantom: std::marker::PhantomData,
168        }
169    }
170
171    /// 创建 DELETE 请求
172    pub fn delete(url: impl Into<String>) -> Self {
173        Self {
174            method: HttpMethod::Delete,
175            url: url.into(),
176            headers: HashMap::new(),
177            query: HashMap::new(),
178            body: None,
179            file: None,
180            timeout: None,
181            _phantom: std::marker::PhantomData,
182        }
183    }
184
185    /// 添加单个请求头
186    pub fn header<K, V>(mut self, key: K, value: V) -> Self
187    where
188        K: Into<String>,
189        V: Into<String>,
190    {
191        self.headers.insert(key.into(), value.into());
192        self
193    }
194
195    /// 添加单个查询参数
196    pub fn query<K, V>(mut self, key: K, value: V) -> Self
197    where
198        K: Into<String>,
199        V: Into<String>,
200    {
201        self.query.insert(key.into(), value.into());
202        self
203    }
204
205    /// 添加可选查询参数,如果值为None则跳过
206    pub fn query_opt<K, V>(mut self, key: K, value: Option<V>) -> Self
207    where
208        K: Into<String>,
209        V: Into<String>,
210    {
211        if let Some(v) = value {
212            self.query.insert(key.into(), v.into());
213        }
214        self
215    }
216
217    /// 设置请求体
218    pub fn body(mut self, body: impl Into<RequestData>) -> Self {
219        self.body = Some(body.into());
220        self
221    }
222
223    /// 设置文件内容 (用于 multipart 上传)
224    pub fn file_content(mut self, file: Vec<u8>) -> Self {
225        self.file = Some(file);
226        self
227    }
228
229    /// 为任何可序列化的类型设置请求体
230    pub fn json_body<T>(mut self, body: &T) -> Self
231    where
232        T: serde::Serialize,
233    {
234        match serde_json::to_value(body) {
235            Ok(json_value) => self.body = Some(RequestData::Json(json_value)),
236            Err(e) => {
237                tracing::warn!(error = %e, "json_body 序列化失败");
238                self.body = Some(RequestData::Json(serde_json::Value::Null));
239            }
240        }
241        self
242    }
243
244    /// 设置请求超时时间
245    pub fn timeout(mut self, duration: Duration) -> Self {
246        self.timeout = Some(duration);
247        self
248    }
249
250    /// 构建完整 URL(包含查询参数)
251    pub fn build_url(&self) -> String {
252        if self.query.is_empty() {
253            self.url.clone()
254        } else {
255            let query_string = self
256                .query
257                .iter()
258                .map(|(k, v)| format!("{}={}", urlencoding::encode(k), urlencoding::encode(v)))
259                .collect::<Vec<_>>()
260                .join("&");
261            format!("{}?{}", self.url, query_string)
262        }
263    }
264
265    // 兼容性字段和方法,用于与现有http.rs代码兼容
266    /// 获取 HTTP 方法
267    pub fn method(&self) -> &HttpMethod {
268        &self.method
269    }
270
271    /// 获取 API 路径(从 URL 中提取)
272    pub fn api_path(&self) -> &str {
273        // 从URL中提取路径部分
274        if let Some(start) = self.url.find(crate::constants::API_PATH_PREFIX) {
275            &self.url[start..]
276        } else {
277            &self.url
278        }
279    }
280
281    /// 获取支持的访问令牌类型
282    pub fn supported_access_token_types(&self) -> Vec<crate::constants::AccessTokenType> {
283        // 默认返回用户和租户令牌类型
284        vec![
285            crate::constants::AccessTokenType::User,
286            crate::constants::AccessTokenType::Tenant,
287        ]
288    }
289
290    /// 将请求体转换为字节
291    pub fn to_bytes(&self) -> Vec<u8> {
292        match &self.body {
293            Some(RequestData::Json(data)) => serde_json::to_vec(data).unwrap_or_default(),
294            Some(RequestData::Binary(data)) => data.clone(),
295            Some(RequestData::Form(data)) => data
296                .iter()
297                .map(|(k, v)| format!("{}={}", urlencoding::encode(k), urlencoding::encode(v)))
298                .collect::<Vec<_>>()
299                .join("&")
300                .into_bytes(),
301            Some(RequestData::Text(text)) => text.clone().into_bytes(),
302            None => vec![],
303        }
304    }
305
306    /// 获取 headers 的可变引用,用于直接插入多个 header
307    pub fn headers_mut(&mut self) -> &mut HashMap<String, String> {
308        &mut self.headers
309    }
310
311    /// 获取 query 的可变引用,用于直接插入多个查询参数
312    pub fn query_mut(&mut self) -> &mut HashMap<String, String> {
313        &mut self.query
314    }
315
316    /// 获取文件内容
317    pub fn file(&self) -> Vec<u8> {
318        self.file.clone().unwrap_or_default()
319    }
320
321    /// 应用请求选项(兼容方法)
322    pub fn request_option(mut self, option: crate::req_option::RequestOption) -> Self {
323        // 将 RequestOption 的头部信息添加到请求中
324        for (key, value) in option.header {
325            self = self.header(key, value);
326        }
327        self
328    }
329
330    /// 设置查询参数(兼容方法)
331    pub fn query_param<K, V>(mut self, key: K, value: V) -> Self
332    where
333        K: Into<String>,
334        V: Into<String>,
335    {
336        self.query.insert(key.into(), value.into());
337        self
338    }
339
340    /// 设置多个查询参数(兼容方法)
341    pub fn query_params<I, K, V>(mut self, params: I) -> Self
342    where
343        I: IntoIterator<Item = (K, V)>,
344        K: Into<String>,
345        V: Into<String>,
346    {
347        for (key, value) in params {
348            self.query.insert(key.into(), value.into());
349        }
350        self
351    }
352}
353
354impl<R> Default for ApiRequest<R> {
355    fn default() -> Self {
356        Self {
357            method: HttpMethod::Get,
358            url: String::default(),
359            headers: HashMap::new(),
360            query: HashMap::new(),
361            body: None,
362            file: None,
363            timeout: None,
364            _phantom: std::marker::PhantomData,
365        }
366    }
367}
368
369// 类型别名,保持兼容性
370/// API 响应类型别名
371pub type ApiResponse<R> = Response<R>;
372
373// 子模块
374
375pub mod prelude;
376pub mod responses;
377pub mod traits;
378
379// 重新导出
380
381pub use traits::{AsyncApiClient, SyncApiClient};
382
383// 测试
384
385#[cfg(test)]
386mod tests {
387    use super::*;
388
389    #[test]
390    fn test_patch_method() {
391        // 测试patch方法是否正确创建ApiRequest
392        let request: ApiRequest<()> = ApiRequest::patch("https://example.com/api/resource");
393
394        // 验证HTTP方法
395        assert_eq!(request.method, HttpMethod::Patch);
396
397        // 验证URL
398        assert_eq!(request.url, "https://example.com/api/resource");
399
400        // 验证HTTP方法字符串
401        assert_eq!(request.method.as_str(), "PATCH");
402
403        println!("✅ Patch method test passed!");
404    }
405
406    #[test]
407    fn test_all_http_methods() {
408        // 测试所有HTTP方法
409        let get_req: ApiRequest<()> = ApiRequest::get("https://example.com/api");
410        let post_req: ApiRequest<()> = ApiRequest::post("https://example.com/api");
411        let put_req: ApiRequest<()> = ApiRequest::put("https://example.com/api");
412        let patch_req: ApiRequest<()> = ApiRequest::patch("https://example.com/api");
413        let delete_req: ApiRequest<()> = ApiRequest::delete("https://example.com/api");
414
415        // 验证HTTP方法
416        assert_eq!(get_req.method, HttpMethod::Get);
417        assert_eq!(post_req.method, HttpMethod::Post);
418        assert_eq!(put_req.method, HttpMethod::Put);
419        assert_eq!(patch_req.method, HttpMethod::Patch);
420        assert_eq!(delete_req.method, HttpMethod::Delete);
421
422        // 验证HTTP方法字符串
423        assert_eq!(get_req.method.as_str(), "GET");
424        assert_eq!(post_req.method.as_str(), "POST");
425        assert_eq!(put_req.method.as_str(), "PUT");
426        assert_eq!(patch_req.method.as_str(), "PATCH");
427        assert_eq!(delete_req.method.as_str(), "DELETE");
428
429        println!("✅ All HTTP methods test passed!");
430    }
431}