open_lark/service/cloud_docs/drive/v2/
explorer.rs

1use std::fmt::Debug;
2
3use log::error;
4use reqwest::Method;
5use serde::{Deserialize, Serialize};
6
7use crate::{
8    core::{
9        api_req::ApiRequest,
10        api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
11        config::Config,
12        constants::AccessTokenType,
13        http::Transport,
14        req_option::RequestOption,
15        SDKResult,
16    },
17    impl_executable_builder_owned,
18};
19
20pub struct ExplorerService {
21    config: Config,
22}
23
24impl ExplorerService {
25    pub fn new(config: Config) -> Self {
26        Self { config }
27    }
28
29    /// GET /open-apis/drive/explorer/v2/root_folder/meta
30    ///
31    /// 获取云空间的根目录
32    pub async fn root_folder_meta(
33        &self,
34        option: Option<RequestOption>,
35    ) -> SDKResult<BaseResponse<ExplorerRootMeta>> {
36        let api_req = ApiRequest {
37            http_method: Method::GET,
38            api_path: "/open-apis/drive/explorer/v2/root_folder/meta".to_string(),
39            supported_access_token_types: vec![AccessTokenType::Tenant, AccessTokenType::User],
40            ..Default::default()
41        };
42
43        let api_resp = Transport::request(api_req, &self.config, option).await?;
44
45        Ok(api_resp)
46    }
47
48    /// GET /open-apis/drive/explorer/v2/folder/:folderToken/meta
49    ///
50    /// 获取文件夹的元信息
51    pub async fn folder_meta(
52        &self,
53        folder_token: &str,
54        option: Option<RequestOption>,
55    ) -> SDKResult<BaseResponse<ExplorerFolderMeta>> {
56        let api_req = ApiRequest {
57            http_method: Method::GET,
58            api_path: format!("/open-apis/drive/explorer/v2/folder/{folder_token}/meta"),
59            supported_access_token_types: vec![AccessTokenType::Tenant, AccessTokenType::User],
60            ..Default::default()
61        };
62
63        let api_resp = Transport::request(api_req, &self.config, option).await?;
64
65        Ok(api_resp)
66    }
67
68    /// POST /open-apis/drive/v1/files/create_folder
69    /// 新建文件夹
70    pub async fn create_folder(
71        &self,
72        create_folder_request: CreateFolderRequest,
73        option: Option<RequestOption>,
74    ) -> SDKResult<BaseResponse<CreateFolderResponse>> {
75        let mut api_req = create_folder_request.api_req;
76        api_req.http_method = Method::POST;
77        api_req.api_path = "/open-apis/drive/v1/files/create_folder".to_string();
78        api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
79
80        let api_resp = Transport::request(api_req, &self.config, option).await?;
81
82        Ok(api_resp)
83    }
84
85    /// 获取文件夹下的清单
86    ///
87    /// <https://open.feishu.cn/open-apis/drive/v1/files>
88    pub async fn list_folder(
89        &self,
90        list_folder_request: ListFolderRequest,
91        option: Option<RequestOption>,
92    ) -> SDKResult<BaseResponse<ListFolderResponse>> {
93        let mut api_req = list_folder_request.api_req;
94        api_req.http_method = Method::GET;
95        api_req.api_path = "/open-apis/drive/v1/files".to_string();
96        api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
97
98        let api_resp = Transport::request(api_req, &self.config, option).await?;
99
100        Ok(api_resp)
101    }
102
103    pub fn list_folder_iter(
104        &self,
105        req: ListFolderRequest,
106        option: Option<RequestOption>,
107    ) -> ListFolderIterator<'_> {
108        ListFolderIterator {
109            explorer_service: self,
110            req,
111            option,
112            has_more: true,
113        }
114    }
115}
116
117pub struct ListFolderIterator<'a> {
118    explorer_service: &'a ExplorerService,
119    req: ListFolderRequest,
120    option: Option<RequestOption>,
121    has_more: bool,
122}
123
124impl ListFolderIterator<'_> {
125    pub async fn next(&mut self) -> Option<Vec<FileInFolder>> {
126        if !self.has_more {
127            return None;
128        }
129
130        match self
131            .explorer_service
132            .list_folder(self.req.clone(), self.option.clone())
133            .await
134        {
135            Ok(resp) => match resp.data {
136                Some(data) => {
137                    self.has_more = data.has_more;
138                    if data.has_more {
139                        self.req
140                            .api_req
141                            .query_params
142                            .insert("page_token".to_string(), data.next_page_token.unwrap());
143                        Some(data.files)
144                    } else if data.files.is_empty() {
145                        None
146                    } else {
147                        Some(data.files)
148                    }
149                }
150                None => None,
151            },
152            Err(e) => {
153                error!("Error: {e:?}");
154                None
155            }
156        }
157    }
158}
159
160/// 我的空间(root folder)元信息
161#[derive(Debug, Serialize, Deserialize)]
162pub struct ExplorerRootMeta {
163    /// 文件夹的 token
164    pub token: String,
165    /// 文件夹的 id
166    pub id: String,
167    /// 文件夹的所有者 id
168    pub user_id: String,
169}
170
171impl ApiResponseTrait for ExplorerRootMeta {
172    fn data_format() -> ResponseFormat {
173        ResponseFormat::Data
174    }
175}
176
177/// 文件夹元信息
178#[derive(Debug, Serialize, Deserialize)]
179pub struct ExplorerFolderMeta {
180    /// 文件夹的 id
181    pub id: String,
182    /// 文件夹的标题
183    pub name: String,
184    /// 文件夹的 token
185    pub token: String,
186    /// 文件夹的创建者 id
187    #[serde(rename = "createUid")]
188    pub create_uid: String,
189    /// 文件夹的最后编辑者 id
190    #[serde(rename = "editUid")]
191    pub edit_uid: String,
192    /// 文件夹的上级目录 id
193    #[serde(rename = "parentId")]
194    pub parent_id: String,
195    /// 文件夹为个人文件夹时,为文件夹的所有者 id;文件夹为共享文件夹时,为文件夹树id
196    #[serde(rename = "ownUid")]
197    pub own_uid: String,
198}
199
200impl ApiResponseTrait for ExplorerFolderMeta {
201    fn data_format() -> ResponseFormat {
202        ResponseFormat::Data
203    }
204}
205
206#[derive(Default, Serialize, Deserialize)]
207pub struct CreateFolderRequest {
208    /// 请求体
209    #[serde(skip)]
210    api_req: ApiRequest,
211    /// 文件夹名称
212    ///
213    /// 示例值:"New Folder"
214    name: String,
215    /// 父文件夹token。如果需要创建到「我的空间」作为顶级文件夹,请传入我的空间token
216    folder_token: String,
217}
218
219impl CreateFolderRequest {
220    pub fn builder() -> CreateFolderRequestBuilder {
221        CreateFolderRequestBuilder::default()
222    }
223}
224
225#[derive(Default)]
226/// 创建文件夹请求体
227pub struct CreateFolderRequestBuilder {
228    request: CreateFolderRequest,
229}
230
231impl CreateFolderRequestBuilder {
232    /// 文件夹名称
233    pub fn name(mut self, name: impl ToString) -> Self {
234        self.request.name = name.to_string();
235        self
236    }
237
238    /// 父文件夹token
239    pub fn folder_token(mut self, folder_token: impl ToString) -> Self {
240        self.request.folder_token = folder_token.to_string();
241        self
242    }
243
244    pub fn build(mut self) -> CreateFolderRequest {
245        self.request.api_req.body = serde_json::to_vec(&self.request).unwrap();
246
247        self.request
248    }
249}
250
251/// 创建文件夹响应体
252#[derive(Debug, Serialize, Deserialize)]
253pub struct CreateFolderResponse {
254    /// 创建文件夹的token
255    pub token: String,
256    /// 创建文件夹的访问url
257    pub url: String,
258}
259
260impl ApiResponseTrait for CreateFolderResponse {
261    fn data_format() -> ResponseFormat {
262        ResponseFormat::Data
263    }
264}
265
266/// 列出文件夹请求体
267#[derive(Default)]
268pub struct ListFolderRequestBuilder {
269    request: ListFolderRequest,
270}
271
272impl ListFolderRequestBuilder {
273    /// 页大小
274    ///
275    /// 示例值:50
276    ///
277    /// 数据校验规则:
278    ///
279    /// 最大值:200
280    pub fn page_size(mut self, page_size: i32) -> Self {
281        self.request
282            .api_req
283            .query_params
284            .insert("page_size".to_string(), page_size.to_string());
285        self
286    }
287
288    /// 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的
289    /// page_token,下次遍历可采用该 page_token 获取查询结果
290    ///
291    /// 示例值:"MTY1NTA3MTA1OXw3MTA4NDc2MDc1NzkyOTI0Nabcef"
292    pub fn page_token(mut self, page_token: impl ToString) -> Self {
293        self.request
294            .api_req
295            .query_params
296            .insert("page_token".to_string(), page_token.to_string());
297        self
298    }
299
300    /// 文件夹的token(若不填写该参数或填写空字符串,则默认获取用户云空间下的清单,且不支持分页)
301    ///
302    /// 示例值:"fldbcO1UuPz8VwnpPx5a9abcef"
303    pub fn folder_token(mut self, fold_token: impl ToString) -> Self {
304        self.request
305            .api_req
306            .query_params
307            .insert("folder_token".to_string(), fold_token.to_string());
308        self
309    }
310
311    /// 排序规则
312    ///
313    /// 示例值:"EditedTime"
314    ///
315    /// 可选值有:
316    ///
317    /// - EditedTime:编辑时间排序
318    /// - CreatedTime:创建时间排序
319    ///
320    /// 默认值:EditedTime
321    pub fn order_by(mut self, order_by: impl ToString) -> Self {
322        self.request
323            .api_req
324            .query_params
325            .insert("order_by".to_string(), order_by.to_string());
326        self
327    }
328
329    /// 升序降序
330    ///
331    /// 默认值:DESC
332    ///
333    /// 可选值有:
334    ///
335    /// - ASC:升序
336    /// - DESC:降序
337    pub fn direction(mut self, direction: impl ToString) -> Self {
338        self.request
339            .api_req
340            .query_params
341            .insert("direction".to_string(), direction.to_string());
342        self
343    }
344
345    /// 用户 ID 类型
346    ///
347    /// 默认值:open_id
348    ///
349    /// 可选值有:
350    ///
351    /// - open_id:标识一个用户在某个应用中的身份。同一个用户在不同应用中的 Open ID
352    ///   不同。了解更多:如何获取 Open ID
353    /// - union_id:标识一个用户在某个应用开发商下的身份。同一用户在同一开发商下的应用中的 Union ID
354    ///   是相同的,在不同开发商下的应用中的 Union ID 是不同的。通过 Union
355    ///   ID,应用开发商可以把同个用户在多个应用中的身份关联起来。了解更多:如何获取 Union ID?
356    /// - user_id:标识一个用户在某个租户内的身份。同一个用户在租户 A 和租户 B 内的 User ID
357    ///   是不同的。在同一个租户内,一个用户的 User ID 在所有应用(包括商店应用)中都保持一致。User
358    ///   ID 主要用于在不同的应用间打通用户数据。了解更多:如何获取 User ID?
359    pub fn user_id_type(mut self, user_id_type: impl ToString) -> Self {
360        self.request
361            .api_req
362            .query_params
363            .insert("user_id_type".to_string(), user_id_type.to_string());
364        self
365    }
366
367    pub fn build(self) -> ListFolderRequest {
368        self.request
369    }
370}
371
372/// 列出文件夹查询参数
373#[derive(Default, Clone, Serialize, Deserialize)]
374pub struct ListFolderRequest {
375    /// 请求体
376    #[serde(skip)]
377    api_req: ApiRequest,
378}
379
380impl ListFolderRequest {
381    pub fn builder() -> ListFolderRequestBuilder {
382        ListFolderRequestBuilder::default()
383    }
384}
385
386#[derive(Debug, Serialize, Deserialize)]
387pub struct ListFolderResponse {
388    /// 文件夹列表
389    pub files: Vec<FileInFolder>,
390    /// 分页标记,当 has_more 为 true 时,会同时返回下一次遍历的page_token,否则则不返回
391    #[serde(skip_serializing_if = "Option::is_none")]
392    pub next_page_token: Option<String>,
393    /// 是否还有更多项
394    pub has_more: bool,
395}
396
397/// 文件夹清单列表
398#[derive(Debug, Serialize, Deserialize)]
399pub struct FileInFolder {
400    /// 文件标识
401    pub token: String,
402    /// 文件名
403    pub name: String,
404    /// 文件类型
405    ///
406    /// 可选值有:
407    ///
408    /// - doc:旧版文档
409    ///
410    /// - sheet:表格
411    ///
412    /// - mindnote:思维导图
413    //
414    /// - bitable:多维表格
415    ///
416    /// - file:文件
417    ///
418    /// - docx:新版文档
419    ///
420    /// - folder:文件夹
421    ///
422    /// - shortcut: 快捷方式
423    pub r#type: String,
424    /// 父文件夹标识
425    pub parent_token: String,
426    /// 在浏览器中查看的链接
427    pub url: String,
428    /// 快捷方式文件信息
429    #[serde(skip_serializing_if = "Option::is_none")]
430    pub shortcut_info: Option<ShortcutInfo>,
431    /// 文件创建时间
432    pub created_time: String,
433    /// 文件最近修改时间
434    pub modified_time: String,
435    /// 文件所有者
436    pub owner_id: String,
437}
438
439#[derive(Debug, Serialize, Deserialize)]
440pub struct ShortcutInfo {
441    /// 快捷方式指向的原文件类型
442    ///
443    /// 可选值有:
444    ///
445    /// - doc:旧版文档
446    ///
447    /// - sheet:表格
448    ///
449    /// - mindnote:思维导图
450    ///
451    /// - bitable:多维表格
452    ///
453    /// - file:文件
454    ///
455    /// - docx:新版文档
456    pub target_type: String,
457    /// 快捷方式指向的原文件token
458    pub target_token: String,
459}
460
461impl ApiResponseTrait for ListFolderResponse {
462    fn data_format() -> ResponseFormat {
463        ResponseFormat::Data
464    }
465}
466
467impl_executable_builder_owned!(
468    CreateFolderRequestBuilder,
469    ExplorerService,
470    CreateFolderRequest,
471    BaseResponse<CreateFolderResponse>,
472    create_folder
473);
474
475impl_executable_builder_owned!(
476    ListFolderRequestBuilder,
477    ExplorerService,
478    ListFolderRequest,
479    BaseResponse<ListFolderResponse>,
480    list_folder
481);