deta_rust/drive/
mod.rs

1//! Deta-drive service SDK.
2//! Check [deta docs](https://docs.deta.sh/docs/drive/http) for more information.
3
4use crate::deta_client::DetaClient;
5pub mod models;
6mod requests;
7use crate::constants;
8use crate::error::Result;
9use crate::utils;
10
11/// Stores the necessary information and methods to
12/// work with the [deta-drive](https://docs.deta.sh/docs/drive/http) API.
13pub struct Drive {
14    base_url: String,
15    x_api_key: String,
16}
17
18impl Drive {
19    /// Creates an `Drive` instance.
20    pub fn new(client: &DetaClient, drive_name: &str) -> Self {
21        let base_url = format!(
22            "{}/{}/{}",
23            constants::DRIVE_API_URL,
24            client.project_id(),
25            drive_name
26        );
27
28        let x_api_key = client.api_key().to_owned();
29
30        Self {
31            base_url,
32            x_api_key,
33        }
34    }
35
36    async fn get_chunked_upload_object(
37        &self,
38        name: &str,
39    ) -> Result<models::InitializeChunkedUpload> {
40        let response =
41            requests::initialize_chunked_upload_request(&self.base_url, &self.x_api_key, name)
42                .await?;
43        Ok(utils::parse_response_body(response).await?)
44    }
45
46    async fn perform_chunked_upload(
47        &self,
48        name: &str,
49        data: Vec<u8>,
50    ) -> Result<models::EndChunkedUpload> {
51        let bytes: bytes::Bytes = data.into();
52        let upload_id = self.get_chunked_upload_object(name).await?.upload_id;
53        let content_length = bytes.len();
54        let chunk_size = constants::MAX_DATA_CHUNK_SIZE;
55        let mut part = 1;
56
57        for idx in (0..content_length).step_by(chunk_size) {
58            let end = content_length.min(idx + chunk_size);
59            let chunk = bytes.slice(idx..end);
60            let upload_result = requests::upload_chunk_request(
61                &self.base_url,
62                &self.x_api_key,
63                name,
64                &upload_id,
65                part,
66                chunk,
67            )
68            .await;
69            if let Err(error) = upload_result {
70                requests::abort_chunked_upload_request(
71                    &self.base_url,
72                    &self.x_api_key,
73                    name,
74                    &upload_id,
75                )
76                .await?;
77                return Err(error);
78            }
79            part += 1;
80        }
81
82        let response =
83            requests::end_chunked_upload_request(&self.base_url, &self.x_api_key, name, &upload_id)
84                .await?;
85        Ok(utils::parse_response_body(response).await?)
86    }
87
88    /// Uploads the file to the server.
89    /// If the amount of data to be uploaded exceeds 10MB, chunked uploading will be used.
90    pub async fn put_file(
91        &self,
92        name: &str,
93        data: Vec<u8>,
94        content_type: Option<&str>,
95    ) -> Result<PutFileResult> {
96        if data.len() <= constants::MAX_DATA_CHUNK_SIZE {
97            let response = requests::put_file_request(
98                &self.base_url,
99                &self.x_api_key,
100                name,
101                data,
102                content_type,
103            )
104            .await?;
105            return Ok(PutFileResult::SinglePut(
106                utils::parse_response_body(response).await?,
107            ));
108        }
109
110        Ok(PutFileResult::ChunkedUpload(
111            self.perform_chunked_upload(name, data).await?,
112        ))
113    }
114
115    /// Returns a raw data as type [`bytes::Bytes`](bytes::Bytes).
116    pub async fn get_file_as_buffer(&self, name: &str) -> Result<Option<bytes::Bytes>> {
117        let response_result =
118            requests::get_file_request(&self.base_url, &self.x_api_key, name).await;
119
120        if let Err(ref error) = response_result {
121            if error.is_not_found() {
122                return Ok(None);
123            }
124        }
125
126        let response = response_result?;
127        let bytes = response.bytes().await?;
128        Ok(Some(bytes))
129    }
130
131    /// Returns a raw data as type `Vec<u8>`.
132    pub async fn get_file_as_u8_vec(&self, name: &str) -> Result<Option<Vec<u8>>> {
133        let bytes = self.get_file_as_buffer(name).await?;
134        if bytes.is_none() {
135            return Ok(None);
136        }
137        let bytes = bytes.unwrap();
138        Ok(Some(bytes.to_vec()))
139    }
140
141    /// Lists file names.
142    pub async fn list_files(
143        &self,
144        limit: Option<u32>,
145        prefix: Option<&str>,
146        last_name: Option<&str>,
147    ) -> Result<models::ListFiles> {
148        let response =
149            requests::list_files_request(&self.base_url, &self.x_api_key, limit, prefix, last_name)
150                .await?;
151        return Ok(utils::parse_response_body(response).await?);
152    }
153
154    /// Deletes files by the names specified in the slice.
155    pub async fn delete_files(&self, names: &[String]) -> Result<models::DeleteFiles> {
156        let response =
157            requests::delete_files_request(&self.base_url, &self.x_api_key, names).await?;
158        return Ok(utils::parse_response_body(response).await?);
159    }
160}
161
162/// Positive response variants to file upload.
163
164#[derive(Debug, Clone)]
165pub enum PutFileResult {
166    /// File size less than or equal to 10MB.
167    SinglePut(models::PutFile),
168    /// File size greater than 10MB.
169    ChunkedUpload(models::EndChunkedUpload),
170}