s3m/s3/actions/
abortmultipartupload.rs1use crate::{
7 s3::actions::{Action, response_error},
8 s3::{S3, request, tools},
9};
10use anyhow::{Result, anyhow};
11use reqwest::Method;
12use std::collections::BTreeMap;
13
14#[derive(Debug, Default)]
15pub struct AbortMultipartUpload<'a> {
16 key: &'a str,
17 upload_id: &'a str,
18}
19
20impl<'a> AbortMultipartUpload<'a> {
21 #[must_use]
22 pub const fn new(key: &'a str, upload_id: &'a str) -> Self {
23 Self { key, upload_id }
24 }
25
26 pub async fn request(&self, s3: &S3) -> Result<String> {
30 let (url, headers) = &self.sign(s3, tools::sha256_digest("").as_ref(), None, None)?;
31
32 let response = request::request(
33 s3.client(),
34 url.clone(),
35 self.http_method()?,
36 headers,
37 None,
38 None,
39 None,
40 )
41 .await?;
42
43 if response.status().is_success() {
44 Ok(response.text().await?)
45 } else {
46 Err(anyhow!(response_error(response).await?))
47 }
48 }
49}
50
51impl Action for AbortMultipartUpload<'_> {
53 fn http_method(&self) -> Result<Method> {
54 Ok(Method::from_bytes(b"DELETE")?)
55 }
56
57 fn headers(&self) -> Option<BTreeMap<&str, &str>> {
58 None
59 }
60
61 fn query_pairs(&self) -> Option<BTreeMap<&str, &str>> {
62 let mut map: BTreeMap<&str, &str> = BTreeMap::new();
64
65 map.insert("uploadId", self.upload_id);
67
68 Some(map)
69 }
70
71 fn path(&self) -> Option<Vec<&str>> {
72 Some(self.key.split('/').collect())
73 }
74}
75
76#[cfg(test)]
77#[allow(
78 clippy::unwrap_used,
79 clippy::expect_used,
80 clippy::panic,
81 clippy::indexing_slicing,
82 clippy::unnecessary_wraps
83)]
84mod tests {
85 use super::*;
86 use crate::s3::{Credentials, Region, S3};
87 use secrecy::SecretString;
88
89 #[test]
90 fn test_method() {
91 let action = AbortMultipartUpload::new("key", "uid");
92 assert_eq!(Method::DELETE, action.http_method().unwrap());
93 }
94
95 #[test]
96 fn test_query_pairs() {
97 let action = AbortMultipartUpload::new("key", "uid");
98 let mut map = BTreeMap::new();
99 map.insert("uploadId", "uid");
100 assert_eq!(Some(map), action.query_pairs());
101 }
102
103 #[test]
104 fn test_path() {
105 let action = AbortMultipartUpload::new("key", "uid");
106 assert_eq!(Some(vec!["key"]), action.path());
107 }
108
109 #[test]
110 fn test_sign() {
111 let s3 = S3::new(
112 &Credentials::new(
113 "AKIAIOSFODNN7EXAMPLE",
114 &SecretString::new("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY".into()),
115 ),
116 &"us-west-1".parse::<Region>().unwrap(),
117 Some("awsexamplebucket1".to_string()),
118 false,
119 );
120 let action = AbortMultipartUpload::new("key", "uid");
121 let (url, headers) = action
122 .sign(&s3, tools::sha256_digest("").as_ref(), None, None)
123 .unwrap();
124 assert_eq!(
125 "https://s3.us-west-1.amazonaws.com/awsexamplebucket1/key?uploadId=uid",
126 url.as_str()
127 );
128 assert!(
129 headers
130 .get("authorization")
131 .unwrap()
132 .starts_with("AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE")
133 );
134 }
135}