Skip to main content

baidu_netdisk_sdk/file/
management.rs

1use std::future::Future;
2
3/// File management module
4///
5/// Provides file and folder management functionality (create, rename, delete, move, copy)
6use log::{debug, info};
7use serde::{Deserialize, Serialize};
8
9use super::FileClient;
10use crate::errors::{NetDiskError, NetDiskResult};
11
12/// Extension trait for file management operations
13pub(crate) trait FileManagementExt {
14    fn create_folder(&self, path: &str) -> impl Future<Output = NetDiskResult<FolderInfo>> + Send;
15
16    fn create_folder_with_options(
17        &self,
18        path: &str,
19        options: FolderCreateOptions,
20    ) -> impl Future<Output = NetDiskResult<FolderInfo>> + Send;
21
22    fn rename(&self, path: &str, new_name: &str) -> impl Future<Output = NetDiskResult<()>> + Send;
23
24    fn delete(&self, path: &str) -> impl Future<Output = NetDiskResult<()>> + Send;
25
26    fn move_file(&self, path: &str, dest: &str) -> impl Future<Output = NetDiskResult<()>> + Send;
27
28    fn copy_file(&self, path: &str, dest: &str) -> impl Future<Output = NetDiskResult<()>> + Send;
29}
30
31impl FileManagementExt for FileClient {
32    async fn create_folder(&self, path: &str) -> NetDiskResult<FolderInfo> {
33        self.create_folder_with_options(path, FolderCreateOptions::default())
34            .await
35    }
36
37    async fn create_folder_with_options(
38        &self,
39        path: &str,
40        options: FolderCreateOptions,
41    ) -> NetDiskResult<FolderInfo> {
42        let token = self.token_getter.get_token().await?;
43        let params = [("method", "create"), ("access_token", &token.access_token)];
44
45        let form = [
46            ("path", path),
47            ("isdir", "1"),
48            ("rtype", &options.rtype.to_string()),
49            ("mode", &options.mode.to_string()),
50        ];
51
52        debug!(
53            "Creating folder at path: {} with options: {:?}",
54            path, options
55        );
56
57        let response: FolderResponse = self
58            .http_client()
59            .post_form("/rest/2.0/xpan/file", Some(&form), Some(&params))
60            .await?;
61
62        if response.errno != 0 {
63            let errmsg = response.errmsg.as_deref().unwrap_or("Unknown error");
64            return Err(NetDiskError::api_error(response.errno, errmsg));
65        }
66
67        info!("Folder created successfully at: {}", path);
68
69        Ok(FolderInfo {
70            fs_id: response.fs_id,
71            path: response.path.unwrap_or_else(|| path.to_string()),
72            ctime: response.ctime,
73            mtime: response.mtime,
74            isdir: response.isdir,
75            category: response.category,
76        })
77    }
78
79    async fn rename(&self, path: &str, new_name: &str) -> NetDiskResult<()> {
80        let token = self.token_getter.get_token().await?;
81        let filelist = serde_json::json!([
82            {
83                "path": path,
84                "newname": new_name
85            }
86        ]);
87
88        let params = [
89            ("method", "filemanager"),
90            ("access_token", &token.access_token),
91            ("opera", "rename"),
92        ];
93
94        let form = [
95            ("async", "0"),
96            ("filelist", &filelist.to_string()),
97            ("ondup", "overwrite"),
98        ];
99
100        debug!("Renaming path: {} to: {}", path, new_name);
101
102        let response: FileOperationResponse = self
103            .http_client()
104            .post_form("/rest/2.0/xpan/file", Some(&form), Some(&params))
105            .await?;
106
107        if response.errno != 0 {
108            return Err(NetDiskError::api_error(response.errno, "Rename failed"));
109        }
110
111        info!("Renamed successfully: {} -> {}", path, new_name);
112        Ok(())
113    }
114
115    async fn delete(&self, path: &str) -> NetDiskResult<()> {
116        let token = self.token_getter.get_token().await?;
117        let filelist = serde_json::json!([
118            {
119                "path": path
120            }
121        ]);
122
123        let params = [
124            ("method", "filemanager"),
125            ("access_token", &token.access_token),
126            ("opera", "delete"),
127        ];
128
129        let form = [
130            ("async", "0"),
131            ("filelist", &filelist.to_string()),
132            ("ondup", "overwrite"),
133        ];
134
135        debug!("Deleting path: {}", path);
136
137        let response: FileOperationResponse = self
138            .http_client()
139            .post_form("/rest/2.0/xpan/file", Some(&form), Some(&params))
140            .await?;
141
142        if response.errno != 0 {
143            return Err(NetDiskError::api_error(response.errno, "Delete failed"));
144        }
145
146        info!("Deleted successfully: {}", path);
147        Ok(())
148    }
149
150    async fn move_file(&self, path: &str, dest: &str) -> NetDiskResult<()> {
151        let token = self.token_getter.get_token().await?;
152        let filename = path.rsplit('/').next().unwrap_or(path);
153
154        let filelist = serde_json::json!([
155            {
156                "path": path,
157                "dest": dest,
158                "newname": filename,
159                "ondup": "fail"
160            }
161        ]);
162
163        let params = [
164            ("method", "filemanager"),
165            ("access_token", &token.access_token),
166            ("opera", "move"),
167        ];
168
169        let form = [
170            ("async", "0"),
171            ("filelist", &filelist.to_string()),
172            ("ondup", "fail"),
173        ];
174
175        debug!("Moving path: {} to: {}", path, dest);
176
177        let response: FileOperationResponse = self
178            .http_client()
179            .post_form("/rest/2.0/xpan/file", Some(&form), Some(&params))
180            .await?;
181
182        if response.errno != 0 {
183            return Err(NetDiskError::api_error(response.errno, "Move failed"));
184        }
185
186        info!("Moved successfully: {} -> {}", path, dest);
187        Ok(())
188    }
189
190    async fn copy_file(&self, path: &str, dest: &str) -> NetDiskResult<()> {
191        let token = self.token_getter.get_token().await?;
192        let filename = path.rsplit('/').next().unwrap_or(path);
193
194        let filelist = serde_json::json!([
195            {
196                "path": path,
197                "dest": dest,
198                "newname": filename,
199                "ondup": "fail"
200            }
201        ]);
202
203        let params = [
204            ("method", "filemanager"),
205            ("access_token", &token.access_token),
206            ("opera", "copy"),
207        ];
208
209        let form = [
210            ("async", "0"),
211            ("filelist", &filelist.to_string()),
212            ("ondup", "fail"),
213        ];
214
215        debug!("Copying path: {} to: {}", path, dest);
216
217        let response: FileOperationResponse = self
218            .http_client()
219            .post_form("/rest/2.0/xpan/file", Some(&form), Some(&params))
220            .await?;
221
222        if response.errno != 0 {
223            return Err(NetDiskError::api_error(response.errno, "Copy failed"));
224        }
225
226        info!("Copied successfully: {} -> {}", path, dest);
227        Ok(())
228    }
229}
230
231#[derive(Debug, Deserialize, Serialize, Default)]
232pub struct FolderCreateOptions {
233    pub rtype: i32,
234    pub mode: i32,
235}
236
237impl FolderCreateOptions {
238    pub fn new() -> Self {
239        Self::default()
240    }
241
242    pub fn rtype(mut self, rtype: i32) -> Self {
243        self.rtype = rtype;
244        self
245    }
246
247    pub fn mode(mut self, mode: i32) -> Self {
248        self.mode = mode;
249        self
250    }
251}
252
253#[derive(Debug, Deserialize, Serialize, Clone)]
254pub struct FolderInfo {
255    pub fs_id: Option<u64>,
256    pub path: String,
257    pub ctime: Option<u64>,
258    pub mtime: Option<u64>,
259    pub isdir: Option<i32>,
260    pub category: Option<i32>,
261}
262
263#[derive(Debug, Deserialize)]
264struct FolderResponse {
265    errno: i32,
266    errmsg: Option<String>,
267    fs_id: Option<u64>,
268    path: Option<String>,
269    ctime: Option<u64>,
270    mtime: Option<u64>,
271    isdir: Option<i32>,
272    category: Option<i32>,
273}
274
275#[derive(Debug, Deserialize)]
276#[allow(dead_code)]
277struct FileOperationResponse {
278    errno: i32,
279    info: Option<Vec<FileProcessStatus>>,
280    taskid: Option<u64>,
281}
282
283#[derive(Debug, Deserialize)]
284#[allow(dead_code)]
285struct FileProcessStatus {
286    errno: i32,
287    path: String,
288}