s3/
command.rs

1use std::collections::HashMap;
2
3use crate::serde_types::{CompleteMultipartUploadData, CorsConfiguration};
4
5use crate::EMPTY_PAYLOAD_SHA;
6use sha2::{Digest, Sha256};
7
8pub enum HttpMethod {
9    Delete,
10    Get,
11    Put,
12    Post,
13    Head,
14}
15
16use std::fmt;
17
18impl fmt::Display for HttpMethod {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        match self {
21            HttpMethod::Delete => write!(f, "DELETE"),
22            HttpMethod::Get => write!(f, "GET"),
23            HttpMethod::Post => write!(f, "POST"),
24            HttpMethod::Put => write!(f, "PUT"),
25            HttpMethod::Head => write!(f, "HEAD"),
26        }
27    }
28}
29use crate::bucket_ops::BucketConfiguration;
30use http::HeaderMap;
31
32#[derive(Clone, Debug)]
33pub struct Multipart<'a> {
34    part_number: u32,
35    upload_id: &'a str,
36}
37
38impl<'a> Multipart<'a> {
39    pub fn query_string(&self) -> String {
40        format!(
41            "?partNumber={}&uploadId={}",
42            self.part_number, self.upload_id
43        )
44    }
45
46    pub fn new(part_number: u32, upload_id: &'a str) -> Self {
47        Multipart {
48            part_number,
49            upload_id,
50        }
51    }
52}
53
54#[derive(Clone, Debug, strum_macros::Display)]
55pub enum Command<'a> {
56    HeadObject,
57    CopyObject {
58        from: &'a str,
59    },
60    DeleteObject,
61    DeleteObjectTagging,
62    GetObject,
63    GetObjectTorrent,
64    GetObjectRange {
65        start: u64,
66        end: Option<u64>,
67    },
68    GetObjectTagging,
69    PutObject {
70        content: &'a [u8],
71        content_type: &'a str,
72        multipart: Option<Multipart<'a>>,
73    },
74    PutObjectTagging {
75        tags: &'a str,
76    },
77    ListMultipartUploads {
78        prefix: Option<&'a str>,
79        delimiter: Option<&'a str>,
80        key_marker: Option<String>,
81        max_uploads: Option<usize>,
82    },
83    ListObjects {
84        prefix: String,
85        delimiter: Option<String>,
86        marker: Option<String>,
87        max_keys: Option<usize>,
88    },
89    ListObjectsV2 {
90        prefix: String,
91        delimiter: Option<String>,
92        continuation_token: Option<String>,
93        start_after: Option<String>,
94        max_keys: Option<usize>,
95    },
96    GetBucketLocation,
97    PresignGet {
98        expiry_secs: u32,
99        custom_queries: Option<HashMap<String, String>>,
100    },
101    PresignPut {
102        expiry_secs: u32,
103        custom_headers: Option<HeaderMap>,
104    },
105    PresignPost {
106        expiry_secs: u32,
107        post_policy: String,
108    },
109    PresignDelete {
110        expiry_secs: u32,
111    },
112    InitiateMultipartUpload {
113        content_type: &'a str,
114    },
115    UploadPart {
116        part_number: u32,
117        content: &'a [u8],
118        upload_id: &'a str,
119    },
120    AbortMultipartUpload {
121        upload_id: &'a str,
122    },
123    CompleteMultipartUpload {
124        upload_id: &'a str,
125        data: CompleteMultipartUploadData,
126    },
127    CreateBucket {
128        config: BucketConfiguration,
129    },
130    DeleteBucket,
131    ListBuckets,
132    PutBucketCors {
133        configuration: CorsConfiguration,
134    },
135}
136
137impl<'a> Command<'a> {
138    pub fn http_verb(&self) -> HttpMethod {
139        match *self {
140            Command::GetObject
141            | Command::GetObjectTorrent
142            | Command::GetObjectRange { .. }
143            | Command::ListBuckets
144            | Command::ListObjects { .. }
145            | Command::ListObjectsV2 { .. }
146            | Command::GetBucketLocation
147            | Command::GetObjectTagging
148            | Command::ListMultipartUploads { .. }
149            | Command::PresignGet { .. } => HttpMethod::Get,
150            Command::PutObject { .. }
151            | Command::CopyObject { from: _ }
152            | Command::PutObjectTagging { .. }
153            | Command::PresignPut { .. }
154            | Command::UploadPart { .. }
155            | Command::PutBucketCors { .. }
156            | Command::CreateBucket { .. } => HttpMethod::Put,
157            Command::DeleteObject
158            | Command::DeleteObjectTagging
159            | Command::AbortMultipartUpload { .. }
160            | Command::PresignDelete { .. }
161            | Command::DeleteBucket => HttpMethod::Delete,
162            Command::InitiateMultipartUpload { .. } | Command::CompleteMultipartUpload { .. } => {
163                HttpMethod::Post
164            }
165            Command::HeadObject => HttpMethod::Head,
166            Command::PresignPost { .. } => HttpMethod::Post,
167        }
168    }
169
170    pub fn content_length(&self) -> usize {
171        match &self {
172            Command::CopyObject { from: _ } => 0,
173            Command::PutObject { content, .. } => content.len(),
174            Command::PutObjectTagging { tags } => tags.len(),
175            Command::UploadPart { content, .. } => content.len(),
176            Command::CompleteMultipartUpload { data, .. } => data.len(),
177            Command::CreateBucket { config } => {
178                if let Some(payload) = config.location_constraint_payload() {
179                    Vec::from(payload).len()
180                } else {
181                    0
182                }
183            }
184            _ => 0,
185        }
186    }
187
188    pub fn content_type(&self) -> String {
189        match self {
190            Command::InitiateMultipartUpload { content_type } => content_type.to_string(),
191            Command::PutObject { content_type, .. } => content_type.to_string(),
192            Command::CompleteMultipartUpload { .. } => "application/xml".into(),
193            _ => "text/plain".into(),
194        }
195    }
196
197    pub fn sha256(&self) -> String {
198        match &self {
199            Command::PutObject { content, .. } => {
200                let mut sha = Sha256::default();
201                sha.update(content);
202                hex::encode(sha.finalize().as_slice())
203            }
204            Command::PutObjectTagging { tags } => {
205                let mut sha = Sha256::default();
206                sha.update(tags.as_bytes());
207                hex::encode(sha.finalize().as_slice())
208            }
209            Command::CompleteMultipartUpload { data, .. } => {
210                let mut sha = Sha256::default();
211                sha.update(data.to_string().as_bytes());
212                hex::encode(sha.finalize().as_slice())
213            }
214            Command::CreateBucket { config } => {
215                if let Some(payload) = config.location_constraint_payload() {
216                    let mut sha = Sha256::default();
217                    sha.update(payload.as_bytes());
218                    hex::encode(sha.finalize().as_slice())
219                } else {
220                    EMPTY_PAYLOAD_SHA.into()
221                }
222            }
223            _ => EMPTY_PAYLOAD_SHA.into(),
224        }
225    }
226}