Skip to main content

baidu_pcs_rs_sdk/
lib.rs

1pub mod baidu_pcs_sdk {
2    use getset::Getters;
3    use serde::{Deserialize, Deserializer, Serialize};
4    use serde_json::Value;
5    use std::error::Error;
6
7    pub mod pcs;
8
9    #[path = "pcs_device_auth_impl.rs"]
10    pub mod pcs_device_auth;
11    pub mod pcs_error;
12
13    /// 百度网盘开放平台-我的应用
14    /// [官方申请地址](https://pan.baidu.com/union/console/applist)
15    #[derive(Debug)]
16    pub struct BaiduPcsApp {
17        /// 密钥信息-AppKey
18        pub app_key: &'static str,
19        /// 密钥信息-SecretKey
20        pub app_secret: &'static str,
21        /// 基本信息-应用名称
22        pub app_name: &'static str,
23        /// 基本信息-应用id 某些情况才会用到
24        pub app_id: Option<&'static str>,
25    }
26
27    /// 认证授权时调用的接口,错误时返回此类型
28    #[derive(Serialize, Deserialize, Debug, Getters)]
29    #[getset(get = "pub")]
30    pub struct PcsError {
31        error: String,
32        error_description: String,
33    }
34
35    /// 网盘接口调用时,返回此类型
36    /// 错误码 https://pan.baidu.com/union/doc/okumlx17r
37    #[derive(Serialize, Deserialize, Debug)]
38    pub struct PcsApiError {
39        /// 表示具体错误码。 0 表示成功
40        // 返回的json为 number, rust反序列化时,会报错,所以改为 i32
41        #[serde(alias = "error_code")]
42        errno: i32,
43        /// 有关该错误的描述。
44        #[serde(skip_serializing_if = "Option::is_none")]
45        #[serde(alias = "show_msg")]
46        #[serde(alias = "error_msg")]
47        #[serde(alias = "errmsg")]
48        err_msg: Option<String>,
49        /// * `request_id`    String    发起请求的请求 Id。
50        // 实际有的接口返回是的 number,有的是 string
51        #[serde(deserialize_with = "from_str_or_int", default)]
52        request_id: Option<String>,
53        #[serde(skip)]
54        pub(crate) raw: String,
55    }
56
57    #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
58    pub enum AppErrorType {
59        /// 未知错误
60        Unknown,
61        /// 网络错误
62        Network,
63        /// 服务端错误
64        Server,
65        /// 客户端错误
66        Client,
67    }
68
69    #[derive(Debug, Serialize, Deserialize)]
70    pub struct AppError {
71        pub error_type: AppErrorType,
72        pub message: String,
73        pub errno: Option<i64>,
74    }
75
76    #[derive(Debug, Deserialize, Getters)]
77    #[getset(get = "pub with_prefix")]
78    pub struct PcsAccessToken {
79        /// 获取到的Access Token,Access Token是调用网盘开放API访问用户授权资源的凭证。
80        access_token: String,
81        /// Access Token的有效期,单位为秒。
82        expires_in: u32,
83        /// 用于刷新Access Token, 有效期为10年。
84        refresh_token: String,
85        /// Access Token 最终的访问权限,即用户的实际授权列表。
86        scope: String,
87        // 未定义的参数(实际有,忽略)
88        #[serde(skip_serializing_if = "Option::is_none")]
89        session_secret: Option<String>,
90        // 未定义的参数(实际有,忽略)
91        #[serde(skip_serializing_if = "Option::is_none")]
92        session_key: Option<String>,
93        /// 自行定义的参数,用于判断是否过期(对象创建时间)
94        #[serde(skip)]
95        born_at: i64,
96    }
97
98    #[derive(Serialize, Deserialize, Getters, Debug, Clone)]
99    #[getset(get = "pub")]
100    pub struct PcsUserInfo {
101        /// `baidu_name`    string    百度账号
102        baidu_name: String,
103        /// `netdisk_name`    string    网盘账号
104        netdisk_name: String,
105        /// `avatar_url`    string    头像地址
106        avatar_url: String,
107        /// `vip_type`    int    会员类型,0普通用户、1普通会员、2超级会员
108        vip_type: i32,
109        /// `uk`    int    用户ID
110        uk: u64,
111    }
112
113    #[derive(Serialize, Deserialize, Debug, Getters)]
114    #[getset(get = "pub")]
115    pub struct PcsDiskQuota {
116        /// `total`    int    总空间大小,单位B
117        total: u64,
118        /// `expire`    bool    7天内是否有容量到期
119        expire: bool,
120        /// `used`    int    已使用大小,单位B
121        used: u64,
122        /// `free`    int    免费容量,单位B
123        free: u64,
124    }
125
126    #[derive(Serialize, Deserialize, Debug, Getters)]
127    #[getset(get = "pub")]
128    pub struct PcsCreateFolderResult {
129        /// `fs_id`    uint64    文件在云端的唯一标识ID
130        fs_id: u64,
131        /// `category`    int    分类类型, 6 文件夹
132        category: i32,
133        /// `path`    string    上传后使用的文件绝对路径
134        path: String,
135        /// `ctime`    int64    文件创建时间
136        ctime: i64,
137        /// `mtime`    int64    文件修改时间
138        mtime: i64,
139        /// `isdir`    int    是否目录,0 文件、1 目录
140        #[serde(alias = "isdir")]
141        is_dir: i32,
142    }
143
144    #[derive(Serialize, Deserialize, Debug)]
145    pub struct PcsFileTask {
146        errno: i32,
147        path: String,
148        task_id: Option<String>,
149    }
150
151    #[derive(Serialize, Deserialize, Debug)]
152    pub struct PcsFileTaskOperationResult {
153        info: Vec<PcsFileTask>,
154        task_id: Option<String>,
155    }
156
157    #[derive(Serialize, Deserialize, Debug, Getters, Clone)]
158    #[getset(get = "pub")]
159    pub struct PcsFileItem {
160        /// `fs_id` uint64 文件在云端的唯一标识ID
161        fs_id: u64,
162        /// `path` string 文件的绝对路径
163        path: String,
164        /// `server_filename` string 文件名称
165        server_filename: String,
166        /// `size` uint 文件大小,单位B
167        size: u64,
168        /// `server_mtime` int 文件在服务器修改时间
169        server_mtime: i64,
170        /// `server_ctime` int 文件在服务器创建时间
171        server_ctime: i64,
172        /// `local_mtime` int 文件在客户端修改时间
173        local_mtime: i64,
174        /// `local_ctime` int 文件在客户端创建时间
175        local_ctime: i64,
176        /// `isdir` uint 是否为目录,0 文件、1 目录
177        #[serde(alias = "isdir")]
178        is_dir: i32,
179        /// `category` uint 文件类型,1 视频、2 音频、3 图片、4 文档、5 应用、6 其他、7 种子
180        category: i32,
181        /// `md5` string 云端哈希(非文件真实MD5),只有是文件类型时,该字段才存在
182        md5: Option<String>,
183        /// `dir_empty` int 该目录是否存在子目录,0为存在,1为不存在
184        dir_empty: Option<i32>,
185        /// `thumbs` array 包含三个尺寸的缩略图URL,仅当只有请求参数web=1且该条目分类为图片时存在
186        thumbs: Option<Vec<String>>,
187    }
188
189    #[derive(Serialize, Deserialize, Debug, Getters)]
190    #[getset(get = "pub")]
191    pub struct PcsFileListResult {
192        list: Vec<PcsFileItem>,
193        guid: i64,
194    }
195
196    #[derive(Serialize, Deserialize, Debug, Getters)]
197    #[getset(get = "pub")]
198    pub struct PcsFileUploadResult {
199        /// `path`    string    文件的绝对路径
200        path: String,
201        /// `size`    uint64    文件大小,单位B
202        size: u64,
203        /// `ctime`    int64    文件创建时间
204        ctime: i64,
205        /// `mtime`    int64    文件修改时间
206        mtime: i64,
207        /// `md5`    string    文件的MD5,只有提交文件时才返回,提交目录时没有该值
208        md5: Option<String>,
209        /// `fs_id`    uint64    文件在云端的唯一标识ID
210        fs_id: u64,
211    }
212
213    #[derive(Serialize, Deserialize, Debug, Getters)]
214    #[getset(get = "pub")]
215    pub struct UploadServerResult {
216        client_ip: String,
217        host: String,
218        #[serde(deserialize_with = "from_str_or_int", default)]
219        request_id: Option<String>,
220        server_time: i64,
221        #[serde(deserialize_with = "from_str_or_int", default)]
222        sl: Option<String>,
223        servers: Vec<Server>,
224        bak_servers: Vec<Server>,
225    }
226
227    #[derive(Serialize, Deserialize, Debug, Getters)]
228    #[getset(get = "pub")]
229    pub(crate) struct Server {
230        server: String,
231    }
232
233    #[derive(Serialize, Deserialize, Debug, Getters)]
234    #[getset(get = "pub")]
235    pub struct PcsFileSlicePrepareResult {
236        /// `path`    string    文件的绝对路径
237        // 有时候返回没有这个字段
238        #[serde(default)]
239        path: String,
240        /// `uploadid`    string    上传唯一ID标识此上传任务
241        #[serde(alias = "uploadid")]
242        upload_id: String,
243        /// `return_type`    int    返回类型,系统内部状态字段
244        return_type: i32,
245        /// `block_list`    string    需要上传的分片序号列表,索引从0开始
246        block_list: Vec<i32>,
247    }
248    #[derive(Serialize, Deserialize, Debug, Getters)]
249    #[getset(get = "pub")]
250    pub struct PcsFileMetaInfo {
251        /// category 文件类型,1 视频、2 音频、3 图片、4 文档、5 应用、6 其他、7 种子
252        category: i32,
253        /// dlink 文件下载地址,参考下载文档进行下载操作。注意unicode解码处理。
254        dlink: Option<String>,
255        /// filename 文件名
256        filename: String,
257        /// isdir 是否是目录,为1表示目录,为0表示非目录
258        #[serde(alias = "isdir", rename = "isdir")]
259        is_dir: i32,
260        /// server_ctime 文件的服务器创建Unix时间戳,单位秒
261        server_ctime: i64,
262        /// server_mtime 文件的服务器修改Unix时间戳,单位秒
263        server_mtime: i64,
264        /// size 文件大小,单位字节
265        size: u64,
266    }
267    #[derive(Serialize, Deserialize, Debug, Getters)]
268    #[getset(get = "pub")]
269    pub struct PcsFileMetaResult {
270        list: Vec<PcsFileMetaInfo>,
271    }
272
273    //has_more	int	是否还有下一页
274    // list	array	文件列表
275    // list[0] ["category"]	int	文件类型
276    // list[0] ["fs_id"]	int	文件在云端的唯一标识
277    // list[0] ["isdir"]	int	是否是目录,0为否,1为是
278    // list[0] ["local_ctime"]	int	文件在客户端创建时间
279    // list[0] ["local_mtime"]	int	文件在客户端修改时间
280    // list[0] ["server_ctime"]	int	文件在服务端创建时间
281    // list[0] ["server_mtime"]	int	文件在服务端修改时间
282    // list[0] ["md5"]	string	云端哈希(非文件真实MD5)
283    // list[0] ["size"]	int	文件大小
284    // list[0] ["thumbs"]	string	缩略图地址
285    #[derive(Serialize, Deserialize, Debug, Getters)]
286    #[getset(get = "pub")]
287    pub struct PcsFileSearchInfo {
288        category: i32,
289        fs_id: u64,
290        is_dir: i32,
291        local_ctime: i64,
292        local_mtime: i64,
293        server_ctime: i64,
294        server_mtime: i64,
295        md5: Option<String>,
296        size: u64,
297        thumbs: Option<Vec<String>>,
298    }
299    #[derive(Serialize, Deserialize, Debug, Getters)]
300    #[getset(get = "pub")]
301    pub struct PcsFileSearchResult {
302        has_more: i32,
303        list: Vec<PcsFileSearchInfo>,
304    }
305
306    impl BaiduPcsApp {
307        pub fn get_app_key(&self) -> String {
308            self.app_key.to_string()
309        }
310        pub fn get_app_secret(&self) -> String {
311            self.app_secret.to_string()
312        }
313        pub fn get_app_name(&self) -> String {
314            self.app_name.to_string()
315        }
316    }
317    impl Error for AppError {}
318
319    impl PcsAccessToken {
320        pub fn new(access_token: &str, expires_in: u32, refresh_token: &str, scope: &str) -> Self {
321            Self {
322                access_token: access_token.to_string(),
323                expires_in,
324                refresh_token: refresh_token.to_string(),
325                scope: scope.to_string(),
326                session_key: None,
327                session_secret: None,
328                born_at: chrono::Utc::now().timestamp(),
329            }
330        }
331        pub fn is_expired(&self) -> bool {
332            (chrono::Utc::now().timestamp() + 600) > (self.born_at + self.expires_in as i64)
333        }
334
335        pub fn is_need_refresh(&self) -> bool {
336            // 一般有效期是30天, 小于 7 天 则刷新
337            (chrono::Utc::now().timestamp() + 7 * 24 * 3600)
338                < (self.born_at + self.expires_in as i64)
339        }
340    }
341
342    /// 反序列化时,支持 string 和 number 或者空,避免服务器返回的数据不规范导致反序列化失败
343    fn from_str_or_int<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
344    where
345        D: Deserializer<'de>,
346    {
347        let value = Value::deserialize(deserializer);
348        if value.is_err() {
349            // 无法解析,返回 None
350            return Ok(None);
351        }
352        match value? {
353            Value::String(s) => Ok(Some(s)),
354            Value::Number(n) => Ok(Some(n.to_string())),
355            _ => Ok(None),
356        }
357    }
358
359    /// 分享提取码验证响应
360    #[derive(Serialize, Deserialize, Debug, Getters)]
361    #[getset(get = "pub")]
362    pub struct ShareVerifyResult {
363        data: ShareVerifyData,
364    }
365    #[derive(Serialize, Deserialize, Debug, Getters, Clone)]
366    #[getset(get = "pub")]
367    pub struct ShareVerifyData {
368        /// 加密后的提取码,后续分享操作需携带
369        spwd: String,
370    }
371
372    /// 分享文件列表响应
373    #[derive(Serialize, Deserialize, Debug, Getters)]
374    #[getset(get = "pub")]
375    pub struct ShareFileListResult {
376        data: ShareFileListData,
377    }
378    #[derive(Serialize, Deserialize, Debug, Getters, Clone)]
379    #[getset(get = "pub")]
380    pub struct ShareFileListData {
381        /// 文件总数
382        #[serde(default)]
383        count: u64,
384        /// 文件信息列表
385        list: Vec<ShareFileInfo>,
386    }
387    #[derive(Serialize, Deserialize, Debug, Getters, Clone)]
388    #[getset(get = "pub")]
389    pub struct ShareFileInfo {
390        /// 文件类型,1 视频、2 音频、3 图片、4 文档、5 应用、6 其他、7 种子
391        category: i32,
392        /// 文件在云端的唯一标识
393        #[serde(deserialize_with = "from_str_or_int", default)]
394        fsid: Option<String>,
395        /// 是否是目录,0为否,1为是
396        #[serde(alias = "isdir", default)]
397        is_dir: i32,
398        /// 文件名
399        server_filename: String,
400        /// 完整路径
401        #[serde(default)]
402        path: String,
403        /// 文件大小
404        #[serde(default)]
405        size: u64,
406        /// 云端哈希
407        #[serde(default)]
408        md5: Option<String>,
409    }
410
411    /// 分享文件下载响应
412    #[derive(Serialize, Deserialize, Debug, Getters)]
413    #[getset(get = "pub")]
414    pub struct ShareDownloadResult {
415        data: ShareDownloadData,
416    }
417    #[derive(Serialize, Deserialize, Debug, Getters, Clone)]
418    #[getset(get = "pub")]
419    pub struct ShareDownloadData {
420        /// 下载地址
421        #[serde(default)]
422        dlink: Option<String>,
423    }
424
425    impl PcsUserInfo {
426        /// 返回当前用户账号允许上次文件的分片大小,用于上传大文件时的文件切片
427        // https://pan.baidu.com/union/doc/nksg0s9vi
428        // 如果文件大小小于等于4MB,无需切片,直接上传即可
429        // 授权用户为普通用户时,单个分片大小固定为4MB,单文件总大小上限为4GB
430        // 授权用户为普通会员时,单个分片大小上限为16MB,单文件总大小上限为10GB
431        // 授权用户为超级会员时,用户单个分片大小上限为32MB,单文件总大小上限为20GB
432        pub fn get_user_block_slice_size(&self) -> u64 {
433            // 授权用户为普通用户时,单个分片大小固定为4MB,单文件总大小上限为4GB
434            // 授权用户为普通会员时,单个分片大小上限为16MB,单文件总大小上限为10GB
435            // 授权用户为超级会员时,用户单个分片大小上限为32MB,单文件总大小上限为20GB
436            // 目前只支持 4MB
437            match self.vip_type {
438                0 => {
439                    // 普通用户
440                    4 * 1024 * 1024
441                }
442                1 => {
443                    // 普通会员
444                    16 * 1024 * 1024
445                }
446                2 => {
447                    // 超级会员
448                    32 * 1024 * 1024
449                }
450                _ => {
451                    // 其他类型
452                    4 * 1024 * 1024
453                }
454            }
455        }
456
457        /// 返回用户最大可上传文件大小
458        // https://pan.baidu.com/union/doc/nksg0s9vi
459        // 如果文件大小小于等于4MB,无需切片,直接上传即可
460        // 授权用户为普通用户时,单个分片大小固定为4MB,单文件总大小上限为4GB
461        // 授权用户为普通会员时,单个分片大小上限为16MB,单文件总大小上限为10GB
462        // 授权用户为超级会员时,用户单个分片大小上限为32MB,单文件总大小上限为20GB
463        pub fn get_user_max_upload_file_size(&self) -> u64 {
464            match self.vip_type {
465                0 => {
466                    // 普通用户
467                    4 * 1024 * 1024 * 1024
468                }
469                1 => {
470                    // 普通会员
471                    10 * 1024 * 1024 * 1024
472                }
473                2 => {
474                    // 超级会员
475                    20 * 1024 * 1024 * 1024
476                }
477                _ => {
478                    // 其他类型
479                    4 * 1024 * 1024 * 1024
480                }
481            }
482        }
483    }
484}
485
486// 顶层模块:自定义 DNS 解析能力(源文件位于 src/dns.rs)
487pub mod dns;