1#![allow(clippy::field_reassign_with_default)]
2use crate::{ClientError, ClientResult, Response};
3
4#[async_trait::async_trait]
5pub trait PermissionOps {
6 async fn add_if_not_exists(
23 &self,
24 file_id: &str,
25 email_address: &str,
26 email_message: &str,
27 role: &str,
28 type_: &str,
29 use_domain_admin_access: bool,
30 send_notification_email: bool,
31 ) -> ClientResult<Response<crate::types::Permission>>;
32}
33
34#[async_trait::async_trait]
35impl PermissionOps for crate::permissions::Permissions {
36 async fn add_if_not_exists(
53 &self,
54 file_id: &str,
55 email_address: &str,
56 email_message: &str,
57 role: &str,
58 type_: &str,
59 use_domain_admin_access: bool,
60 send_notification_email: bool,
61 ) -> ClientResult<Response<crate::types::Permission>> {
62 let resp = self
65 .list_all(
66 file_id,
67 "", true, true, use_domain_admin_access,
71 )
72 .await?;
73
74 for perm in resp.body {
76 if perm.email_address == email_address && perm.role == role && perm.type_ == type_ {
77 return Ok(Response::new(resp.status, resp.headers, perm));
79 }
80 }
81
82 let perm = crate::types::Permission {
84 allow_file_discovery: None,
85 deleted: None,
86 display_name: String::new(),
87 domain: String::new(),
88 email_address: email_address.to_string(),
89 expiration_time: None,
90 id: String::new(),
91 kind: String::new(),
92 permission_details: Default::default(),
93 photo_link: String::new(),
94 role: role.to_string(),
95 team_drive_permission_details: Default::default(),
96 type_: type_.to_string(),
97 view: String::new(),
98 };
99
100 self.create(
102 file_id,
103 email_message,
104 false, send_notification_email,
106 true, true, false, use_domain_admin_access,
110 &perm,
111 )
112 .await
113 }
114}
115
116#[async_trait::async_trait]
117pub trait FileOps {
118 async fn get_by_name(
120 &self,
121 drive_id: &str,
122 parent_id: &str,
123 name: &str,
124 ) -> ClientResult<Response<Vec<crate::types::File>>>;
125
126 async fn create_or_update(
130 &self,
131 drive_id: &str,
132 parent_id: &str,
133 name: &str,
134 mime_type: &str,
135 contents: &[u8],
136 ) -> ClientResult<Response<crate::types::File>>;
137
138 async fn download_by_id(&self, id: &str) -> ClientResult<Response<bytes::Bytes>>;
140
141 async fn create_folder(
143 &self,
144 drive_id: &str,
145 parent_id: &str,
146 name: &str,
147 ) -> ClientResult<Response<crate::types::File>>;
148
149 async fn get_contents_by_id(&self, id: &str) -> ClientResult<Response<String>>;
151
152 async fn delete_by_name(
154 &self,
155 drive_id: &str,
156 parent_id: &str,
157 name: &str,
158 ) -> ClientResult<Response<()>>;
159}
160
161#[async_trait::async_trait]
162impl FileOps for crate::files::Files {
163 async fn get_by_name(
165 &self,
166 drive_id: &str,
167 parent_id: &str,
168 name: &str,
169 ) -> ClientResult<Response<Vec<crate::types::File>>> {
170 let mut query = format!("name = '{}'", name);
171 if !parent_id.is_empty() {
172 query = format!("{} and '{}' in parents", query, parent_id);
173 }
174
175 self.list_all(
176 "drive", drive_id, true, "", false, "", &query, "", true, false, "", )
188 .await
189 }
190
191 async fn create_or_update(
195 &self,
196 drive_id: &str,
197 parent_id: &str,
198 name: &str,
199 mime_type: &str,
200 contents: &[u8],
201 ) -> ClientResult<Response<crate::types::File>> {
202 let mut f: crate::types::File = Default::default();
204 let mut method = reqwest::Method::POST;
205 let mut uri = "https://www.googleapis.com/upload/drive/v3/files".to_string();
206
207 let files = self
209 .get_by_name(drive_id, parent_id, name)
210 .await
211 .map(|resp| resp.body)
212 .unwrap_or_default();
213 if files.is_empty() {
214 f.name = name.to_string();
216 f.mime_type = mime_type.to_string();
217 if !parent_id.is_empty() {
218 f.parents = vec![parent_id.to_string()];
219 } else {
220 f.parents = vec![drive_id.to_string()];
221 }
222
223 uri += "?uploadType=resumable&supportsAllDrives=true&includeItemsFromAllDrives=true";
224
225 } else if let Some(f) = files.first() {
227 method = reqwest::Method::PATCH;
228 let mut f = f.clone();
229 uri += &format!(
230 "/{}?uploadType=resumable&supportsAllDrives=true&includeItemsFromAllDrives=true",
231 f.id
232 );
233
234 f.id = "".to_string();
235 f.drive_id = "".to_string();
236 f.kind = "".to_string();
237 f.original_filename = f.name.to_string();
238 } else {
239 f.name = name.to_string();
241 f.mime_type = mime_type.to_string();
242 if !parent_id.is_empty() {
243 f.parents = vec![parent_id.to_string()];
244 } else {
245 f.parents = vec![drive_id.to_string()];
246 }
247
248 uri += "?uploadType=resumable&supportsAllDrives=true&includeItemsFromAllDrives=true";
249
250 }
252
253 let resp = self
255 .client
256 .request_raw(
257 method,
258 &uri,
259 crate::Message {
260 body: Some(reqwest::Body::from(serde_json::to_vec(&f)?)),
261 content_type: None,
262 },
263 )
264 .await?;
265
266 let location = resp
268 .headers()
269 .get("Location")
270 .ok_or(ClientError::HttpError {
271 status: resp.status(),
272 headers: resp.headers().clone(),
273 error: "Missing Location header".to_string(),
274 })?
275 .to_str()?;
276
277 self.client
279 .request_with_mime(reqwest::Method::PUT, location, contents, mime_type)
280 .await
281 }
282
283 async fn download_by_id(&self, id: &str) -> ClientResult<Response<bytes::Bytes>> {
285 let resp = self
286 .client
287 .request_raw(
288 reqwest::Method::GET,
289 &self.client.url(
290 &format!("/files/{}?supportsAllDrives=true&alt=media", id),
291 None,
292 ),
293 crate::Message::default(),
294 )
295 .await?;
296
297 Ok(Response::new(
298 resp.status(),
299 resp.headers().clone(),
300 resp.bytes().await?,
301 ))
302 }
303
304 async fn create_folder(
306 &self,
307 drive_id: &str,
308 parent_id: &str,
309 name: &str,
310 ) -> ClientResult<Response<crate::types::File>> {
311 let folder_mime_type = "application/vnd.google-apps.folder";
312 let mut file: crate::types::File = Default::default();
313 file.name = name.to_string();
315 file.mime_type = folder_mime_type.to_string();
316 if !parent_id.is_empty() {
317 file.parents = vec![parent_id.to_string()];
318 } else {
319 file.parents = vec![drive_id.to_string()];
320 }
321
322 let mut query = format!(
323 "name = '{}' and mimeType = 'application/vnd.google-apps.folder'",
324 name
325 );
326 if !parent_id.is_empty() {
327 query = format!("{} and '{}' in parents", query, parent_id);
328 }
329
330 let resp = self
332 .list_all(
333 "drive", drive_id, true, "", false, "", &query, "", true, false, "", )
345 .await?;
346
347 if !resp.body.is_empty() {
348 return Ok(Response::new(
349 resp.status,
350 resp.headers,
351 resp.body.into_iter().next().unwrap(),
352 ));
353 }
354
355 let resp: Response<crate::types::File> = self
357 .client
358 .post(
359 &self.client.url(
360 "/files?supportsAllDrives=true&includeItemsFromAllDrives=true",
361 None,
362 ),
363 crate::Message {
364 body: Some(reqwest::Body::from(serde_json::to_vec(&file)?)),
365 content_type: None,
366 },
367 )
368 .await?;
369
370 Ok(resp)
371 }
372
373 async fn get_contents_by_id(&self, id: &str) -> ClientResult<Response<String>> {
376 let mut query_ = String::new();
377 let query_args = ["mime_type=text/plain".to_string()];
378 for (i, n) in query_args.iter().enumerate() {
379 if i > 0 {
380 query_.push('&');
381 }
382 query_.push_str(n);
383 }
384 let url = self.client.url(
385 &format!(
386 "/files/{}/export?{}",
387 crate::progenitor_support::encode_path(id),
388 query_
389 ),
390 None,
391 );
392 let resp = self
393 .client
394 .request_raw(reqwest::Method::GET, &url, crate::Message::default())
395 .await?;
396
397 Ok(Response::new(
398 resp.status(),
399 resp.headers().clone(),
400 resp.text().await?,
401 ))
402 }
403
404 async fn delete_by_name(
406 &self,
407 drive_id: &str,
408 parent_id: &str,
409 name: &str,
410 ) -> ClientResult<Response<()>> {
411 let resp = self.get_by_name(drive_id, parent_id, name).await?;
413
414 if resp.body.is_empty() {
415 return Ok(Response::new(resp.status, resp.headers, ()));
417 }
418
419 self.delete(
421 &resp.body.first().unwrap().id,
422 true, true, )
425 .await
426 }
427}
428
429#[async_trait::async_trait]
430pub trait DriveOps {
431 async fn get_by_name(&self, name: &str) -> ClientResult<Response<crate::types::Drive>>;
433}
434
435#[async_trait::async_trait]
436impl DriveOps for crate::drives::Drives {
437 async fn get_by_name(&self, name: &str) -> ClientResult<Response<crate::types::Drive>> {
439 let resp = self
440 .list_all(
441 "", true, )
444 .await?;
445
446 for drive in resp.body {
447 if drive.name == name {
448 return Ok(Response::new(resp.status, resp.headers, drive));
449 }
450 }
451
452 Err(ClientError::DriveNotFound {
453 name: name.to_string(),
454 })
455 }
456}