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