Skip to main content

openlark_core/api/
mod.rs

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