files_sdk/files/file_actions.rs
1//! File action operations
2//!
3//! This module provides specialized file operations including:
4//! - Begin upload (first stage of file upload process)
5//! - Copy files
6//! - Move files
7//! - Get metadata
8//!
9//! The most important operation here is `begin_upload`, which must be called
10//! before uploading any file to Files.com.
11
12use crate::{FileUploadPartEntity, FilesClient, Result};
13use serde_json::json;
14
15/// Handler for file action operations
16///
17/// Provides methods for specialized file operations that are separate
18/// from basic file CRUD operations.
19#[derive(Debug, Clone)]
20pub struct FileActionHandler {
21 client: FilesClient,
22}
23
24impl FileActionHandler {
25 /// Creates a new FileActionHandler
26 ///
27 /// # Arguments
28 ///
29 /// * `client` - FilesClient instance
30 pub fn new(client: FilesClient) -> Self {
31 Self { client }
32 }
33
34 /// Begin file upload (Stage 1 of upload process)
35 ///
36 /// This must be called before uploading any file. It returns upload URLs
37 /// and parameters needed to perform the actual upload.
38 ///
39 /// # Arguments
40 ///
41 /// * `path` - Destination path for the file
42 /// * `size` - Total size of file in bytes (optional)
43 /// * `mkdir_parents` - Create parent directories if they don't exist
44 ///
45 /// # Returns
46 ///
47 /// Returns a vector of `FileUploadPartEntity` containing upload URLs and parameters.
48 /// For small files, this will typically be a single element. For large files,
49 /// it may contain multiple parts for parallel upload.
50 ///
51 /// # Examples
52 ///
53 /// ```rust,no_run
54 /// use files_sdk::{FilesClient, FileActionHandler};
55 ///
56 /// # #[tokio::main]
57 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
58 /// let client = FilesClient::builder()
59 /// .api_key("your-api-key")
60 /// .build()?;
61 ///
62 /// let handler = FileActionHandler::new(client);
63 ///
64 /// // Begin upload for a 1KB file
65 /// let upload_info = handler
66 /// .begin_upload("/uploads/test.txt", Some(1024), true)
67 /// .await?;
68 ///
69 /// println!("Upload URL: {:?}", upload_info[0].upload_uri);
70 /// # Ok(())
71 /// # }
72 /// ```
73 pub async fn begin_upload(
74 &self,
75 path: &str,
76 size: Option<i64>,
77 mkdir_parents: bool,
78 ) -> Result<Vec<FileUploadPartEntity>> {
79 let mut body = json!({
80 "mkdir_parents": mkdir_parents,
81 });
82
83 if let Some(size) = size {
84 body["size"] = json!(size);
85 }
86
87 let endpoint = format!("/file_actions/begin_upload{}", path);
88 let response = self.client.post_raw(&endpoint, body).await?;
89
90 // Response can be a single object or an array
91 if response.is_array() {
92 Ok(serde_json::from_value(response)?)
93 } else {
94 // Single object - wrap in array
95 Ok(vec![serde_json::from_value(response)?])
96 }
97 }
98
99 /// Begin multi-part upload for large files
100 ///
101 /// For files larger than the default part size, this allows requesting
102 /// multiple upload parts for parallel uploading.
103 ///
104 /// # Arguments
105 ///
106 /// * `path` - Destination path for the file
107 /// * `size` - Total size of file in bytes
108 /// * `parts` - Number of parts to split the upload into
109 /// * `mkdir_parents` - Create parent directories if they don't exist
110 ///
111 /// # Returns
112 ///
113 /// Returns a vector of `FileUploadPartEntity`, one for each part
114 pub async fn begin_multipart_upload(
115 &self,
116 path: &str,
117 size: i64,
118 parts: i32,
119 mkdir_parents: bool,
120 ) -> Result<Vec<FileUploadPartEntity>> {
121 let body = json!({
122 "size": size,
123 "parts": parts,
124 "mkdir_parents": mkdir_parents,
125 });
126
127 let endpoint = format!("/file_actions/begin_upload{}", path);
128 let response = self.client.post_raw(&endpoint, body).await?;
129
130 // Response should be an array for multipart
131 if response.is_array() {
132 Ok(serde_json::from_value(response)?)
133 } else {
134 Ok(vec![serde_json::from_value(response)?])
135 }
136 }
137
138 /// Copy a file to a new location
139 ///
140 /// # Arguments
141 ///
142 /// * `path` - Source file path
143 /// * `destination` - Destination path
144 ///
145 /// # Examples
146 ///
147 /// ```rust,no_run
148 /// # use files_sdk::{FilesClient, FileActionHandler};
149 /// # #[tokio::main]
150 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
151 /// # let client = FilesClient::builder().api_key("key").build()?;
152 /// let handler = FileActionHandler::new(client);
153 /// handler.copy_file("/source/file.txt", "/dest/file.txt").await?;
154 /// # Ok(())
155 /// # }
156 /// ```
157 pub async fn copy_file(&self, path: &str, destination: &str) -> Result<()> {
158 let body = json!({
159 "destination": destination,
160 });
161
162 let endpoint = format!("/file_actions/copy{}", path);
163 self.client.post_raw(&endpoint, body).await?;
164 Ok(())
165 }
166
167 /// Move a file to a new location
168 ///
169 /// # Arguments
170 ///
171 /// * `path` - Source file path
172 /// * `destination` - Destination path
173 ///
174 /// # Examples
175 ///
176 /// ```rust,no_run
177 /// # use files_sdk::{FilesClient, FileActionHandler};
178 /// # #[tokio::main]
179 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
180 /// # let client = FilesClient::builder().api_key("key").build()?;
181 /// let handler = FileActionHandler::new(client);
182 /// handler.move_file("/source/file.txt", "/dest/file.txt").await?;
183 /// # Ok(())
184 /// # }
185 /// ```
186 pub async fn move_file(&self, path: &str, destination: &str) -> Result<()> {
187 let body = json!({
188 "destination": destination,
189 });
190
191 let endpoint = format!("/file_actions/move{}", path);
192 self.client.post_raw(&endpoint, body).await?;
193 Ok(())
194 }
195
196 /// Get file metadata without downloading
197 ///
198 /// This is useful when you want file information without generating
199 /// download URLs or logging download activity.
200 ///
201 /// # Arguments
202 ///
203 /// * `path` - File path
204 ///
205 /// # Examples
206 ///
207 /// ```rust,no_run
208 /// # use files_sdk::{FilesClient, FileActionHandler};
209 /// # #[tokio::main]
210 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
211 /// # let client = FilesClient::builder().api_key("key").build()?;
212 /// let handler = FileActionHandler::new(client);
213 /// let metadata = handler.get_metadata("/path/to/file.txt").await?;
214 /// println!("Size: {:?}", metadata.size);
215 /// # Ok(())
216 /// # }
217 /// ```
218 pub async fn get_metadata(&self, path: &str) -> Result<crate::FileEntity> {
219 let endpoint = format!("/file_actions/metadata{}", path);
220 let response = self.client.post_raw(&endpoint, json!({})).await?;
221 Ok(serde_json::from_value(response)?)
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[test]
230 fn test_handler_creation() {
231 let client = FilesClient::builder().api_key("test-key").build().unwrap();
232 let _handler = FileActionHandler::new(client);
233 }
234}