s3/
command.rs

1use std::collections::HashMap;
2
3use crate::serde_types::CompleteMultipartUploadData;
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)]
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}
132
133impl<'a> Command<'a> {
134    pub fn http_verb(&self) -> HttpMethod {
135        match *self {
136            Command::GetObject
137            | Command::GetObjectTorrent
138            | Command::GetObjectRange { .. }
139            | Command::ListObjects { .. }
140            | Command::ListObjectsV2 { .. }
141            | Command::GetBucketLocation
142            | Command::GetObjectTagging
143            | Command::ListMultipartUploads { .. }
144            | Command::PresignGet { .. } => HttpMethod::Get,
145            Command::PutObject { .. }
146            | Command::CopyObject { from: _ }
147            | Command::PutObjectTagging { .. }
148            | Command::PresignPut { .. }
149            | Command::UploadPart { .. }
150            | Command::CreateBucket { .. } => HttpMethod::Put,
151            Command::DeleteObject
152            | Command::DeleteObjectTagging
153            | Command::AbortMultipartUpload { .. }
154            | Command::PresignDelete { .. }
155            | Command::DeleteBucket => HttpMethod::Delete,
156            Command::InitiateMultipartUpload { .. } | Command::CompleteMultipartUpload { .. } => {
157                HttpMethod::Post
158            }
159            Command::HeadObject => HttpMethod::Head,
160            Command::PresignPost { .. } => HttpMethod::Post,
161        }
162    }
163
164    pub fn content_length(&self) -> usize {
165        match &self {
166            Command::CopyObject { from: _ } => 0,
167            Command::PutObject { content, .. } => content.len(),
168            Command::PutObjectTagging { tags } => tags.len(),
169            Command::UploadPart { content, .. } => content.len(),
170            Command::CompleteMultipartUpload { data, .. } => data.len(),
171            Command::CreateBucket { config } => {
172                if let Some(payload) = config.location_constraint_payload() {
173                    Vec::from(payload).len()
174                } else {
175                    0
176                }
177            }
178            _ => 0,
179        }
180    }
181
182    pub fn content_type(&self) -> String {
183        match self {
184            Command::InitiateMultipartUpload { content_type } => content_type.to_string(),
185            Command::PutObject { content_type, .. } => content_type.to_string(),
186            Command::CompleteMultipartUpload { .. } => "application/xml".into(),
187            _ => "text/plain".into(),
188        }
189    }
190
191    pub fn sha256(&self) -> String {
192        match &self {
193            Command::PutObject { content, .. } => {
194                let mut sha = Sha256::default();
195                sha.update(content);
196                hex::encode(sha.finalize().as_slice())
197            }
198            Command::PutObjectTagging { tags } => {
199                let mut sha = Sha256::default();
200                sha.update(tags.as_bytes());
201                hex::encode(sha.finalize().as_slice())
202            }
203            Command::CompleteMultipartUpload { data, .. } => {
204                let mut sha = Sha256::default();
205                sha.update(data.to_string().as_bytes());
206                hex::encode(sha.finalize().as_slice())
207            }
208            Command::CreateBucket { config } => {
209                if let Some(payload) = config.location_constraint_payload() {
210                    let mut sha = Sha256::default();
211                    sha.update(payload.as_bytes());
212                    hex::encode(sha.finalize().as_slice())
213                } else {
214                    EMPTY_PAYLOAD_SHA.into()
215                }
216            }
217            _ => EMPTY_PAYLOAD_SHA.into(),
218        }
219    }
220}