opendal/services/gcs/
delete.rs1use std::sync::Arc;
19
20use http::StatusCode;
21
22use super::core::*;
23use super::error::parse_error;
24use crate::raw::oio::BatchDeleteResult;
25use crate::raw::*;
26use crate::*;
27
28pub struct GcsDeleter {
29 core: Arc<GcsCore>,
30}
31
32impl GcsDeleter {
33 pub fn new(core: Arc<GcsCore>) -> Self {
34 Self { core }
35 }
36}
37
38impl oio::BatchDelete for GcsDeleter {
39 async fn delete_once(&self, path: String, _: OpDelete) -> Result<()> {
40 let resp = self.core.gcs_delete_object(&path).await?;
41
42 if resp.status().is_success() || resp.status() == StatusCode::NOT_FOUND {
44 Ok(())
45 } else {
46 Err(parse_error(resp))
47 }
48 }
49
50 async fn delete_batch(&self, batch: Vec<(String, OpDelete)>) -> Result<BatchDeleteResult> {
51 let paths: Vec<String> = batch.into_iter().map(|(p, _)| p).collect();
52 let resp = self.core.gcs_delete_objects(paths.clone()).await?;
53
54 let status = resp.status();
55
56 if status != StatusCode::OK {
59 return Err(parse_error(resp));
60 }
61
62 let boundary = parse_multipart_boundary(resp.headers())?.ok_or_else(|| {
63 Error::new(
64 ErrorKind::Unexpected,
65 "gcs batch delete response content type is empty",
66 )
67 })?;
68 let multipart: Multipart<MixedPart> = Multipart::new()
69 .with_boundary(boundary)
70 .parse(resp.into_body().to_bytes())?;
71 let parts = multipart.into_parts();
72
73 let mut batched_result = BatchDeleteResult::default();
74
75 for (i, part) in parts.into_iter().enumerate() {
76 let resp = part.into_response();
77 let path = paths[i].clone();
79
80 if resp.status().is_success() || resp.status() == StatusCode::NOT_FOUND {
82 batched_result.succeeded.push((path, OpDelete::default()));
83 } else {
84 batched_result
85 .failed
86 .push((path, OpDelete::default(), parse_error(resp)));
87 }
88 }
89
90 if batched_result.succeeded.is_empty() {
92 let err = batched_result.failed.remove(0).2;
93 return Err(err);
94 }
95
96 Ok(batched_result)
97 }
98}