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}