1use serde::Deserialize;
2use std::str::FromStr;
3
4#[derive(Clone, Debug)]
5pub(crate) struct Multipart<'a> {
6 part_number: u32,
7 upload_id: &'a str,
8}
9
10impl<'a> Multipart<'a> {
11 pub fn query_string(&self) -> String {
12 format!(
13 "?partNumber={}&uploadId={}",
14 self.part_number,
15 percent_encoding::percent_encode(
16 self.upload_id.as_bytes(),
17 percent_encoding::NON_ALPHANUMERIC
18 )
19 )
20 }
21
22 pub fn new(part_number: u32, upload_id: &'a str) -> Self {
23 Multipart {
24 part_number,
25 upload_id,
26 }
27 }
28}
29
30#[derive(Deserialize, Debug, Clone)]
31pub struct Owner {
32 #[serde(rename = "DisplayName")]
33 pub display_name: Option<String>,
35 #[serde(rename = "ID")]
36 pub id: String,
38}
39
40#[derive(Deserialize, Debug, Clone)]
41pub struct Object {
42 #[serde(rename = "LastModified")]
43 pub last_modified: String,
45 #[serde(rename = "ETag")]
46 pub e_tag: Option<String>,
49 #[serde(rename = "StorageClass")]
50 pub storage_class: Option<String>,
52 #[serde(rename = "Key")]
53 pub key: String,
55 #[serde(rename = "Owner")]
56 pub owner: Option<Owner>,
58 #[serde(rename = "Size")]
59 pub size: u64,
61}
62
63#[derive(Deserialize, Debug, Clone)]
64pub struct CommonPrefix {
65 #[serde(rename = "Prefix")]
66 pub prefix: String,
68}
69
70#[derive(Deserialize, Debug, Default, Clone)]
72pub struct HeadObjectResult {
73 #[serde(rename = "AcceptRanges")]
74 pub accept_ranges: Option<String>,
76 #[serde(rename = "CacheControl")]
77 pub cache_control: Option<String>,
79 #[serde(rename = "ContentDisposition")]
80 pub content_disposition: Option<String>,
82 #[serde(rename = "ContentEncoding")]
83 pub content_encoding: Option<String>,
85 #[serde(rename = "ContentLanguage")]
86 pub content_language: Option<String>,
88 #[serde(rename = "ContentLength")]
89 pub content_length: Option<u64>,
91 #[serde(rename = "ContentType")]
92 pub content_type: Option<String>,
94 #[serde(rename = "DeleteMarker")]
95 pub delete_marker: Option<bool>,
97 #[serde(rename = "ETag")]
98 pub e_tag: Option<String>,
100 #[serde(rename = "Expiration")]
101 pub expiration: Option<String>,
104 #[serde(rename = "Expires")]
105 pub expires: Option<String>,
107 #[serde(rename = "LastModified")]
108 pub last_modified: Option<String>,
110 #[serde(rename = "Metadata", default)]
111 pub metadata: Option<::std::collections::HashMap<String, String>>,
113 #[serde(rename = "MissingMeta")]
114 pub missing_meta: Option<i64>,
117 #[serde(rename = "ObjectLockLegalHoldStatus")]
118 pub object_lock_legal_hold_status: Option<String>,
121 #[serde(rename = "ObjectLockMode")]
122 pub object_lock_mode: Option<String>,
124 #[serde(rename = "ObjectLockRetainUntilDate")]
125 pub object_lock_retain_until_date: Option<String>,
128 #[serde(rename = "PartsCount")]
129 pub parts_count: Option<i64>,
131 #[serde(rename = "ReplicationStatus")]
132 pub replication_status: Option<String>,
134 #[serde(rename = "RequestCharged")]
135 pub request_charged: Option<String>,
136 #[serde(rename = "Restore")]
137 pub restore: Option<String>,
140 #[serde(rename = "SseCustomerAlgorithm")]
141 pub sse_customer_algorithm: Option<String>,
143 #[serde(rename = "SseCustomerKeyMd5")]
144 pub sse_customer_key_md5: Option<String>,
146 #[serde(rename = "SsekmsKeyId")]
147 pub ssekms_key_id: Option<String>,
149 #[serde(rename = "ServerSideEncryption")]
150 pub server_side_encryption: Option<String>,
153 #[serde(rename = "StorageClass")]
154 pub storage_class: Option<String>,
156 #[serde(rename = "VersionId")]
157 pub version_id: Option<String>,
159 #[serde(rename = "WebsiteRedirectLocation")]
160 pub website_redirect_location: Option<String>,
162}
163
164trait GetAndConvertHeaders {
165 fn get_and_convert<T: FromStr>(&self, header: &str) -> Option<T>;
166 fn get_string(&self, header: &str) -> Option<String>;
167}
168
169impl GetAndConvertHeaders for http::header::HeaderMap {
170 fn get_and_convert<T: FromStr>(&self, header: &str) -> Option<T> {
171 self.get(header)?.to_str().ok()?.parse::<T>().ok()
172 }
173 fn get_string(&self, header: &str) -> Option<String> {
174 Some(self.get(header)?.to_str().ok()?.to_owned())
175 }
176}
177
178impl From<&http::HeaderMap> for HeadObjectResult {
179 fn from(headers: &http::HeaderMap) -> Self {
180 let mut result = HeadObjectResult {
181 accept_ranges: headers.get_string("accept-ranges"),
182 cache_control: headers.get_string("Cache-Control"),
183 content_disposition: headers.get_string("Content-Disposition"),
184 content_encoding: headers.get_string("Content-Encoding"),
185 content_language: headers.get_string("Content-Language"),
186 content_length: headers.get_and_convert("Content-Length"),
187 content_type: headers.get_string("Content-Type"),
188 delete_marker: headers.get_and_convert("x-amz-delete-marker"),
189 e_tag: headers.get_string("ETag"),
190 expiration: headers.get_string("x-amz-expiration"),
191 expires: headers.get_string("Expires"),
192 last_modified: headers.get_string("Last-Modified"),
193 ..Default::default()
194 };
195 let mut values = ::std::collections::HashMap::new();
196 for (key, value) in headers.iter() {
197 if key.as_str().starts_with("x-amz-meta-")
198 && let Ok(value) = value.to_str()
199 {
200 values.insert(
201 key.as_str()["x-amz-meta-".len()..].to_owned(),
202 value.to_owned(),
203 );
204 }
205 }
206 result.metadata = Some(values);
207 result.missing_meta = headers.get_and_convert("x-amz-missing-meta");
208 result.object_lock_legal_hold_status = headers.get_string("x-amz-object-lock-legal-hold");
209 result.object_lock_mode = headers.get_string("x-amz-object-lock-mode");
210 result.object_lock_retain_until_date =
211 headers.get_string("x-amz-object-lock-retain-until-date");
212 result.parts_count = headers.get_and_convert("x-amz-mp-parts-count");
213 result.replication_status = headers.get_string("x-amz-replication-status");
214 result.request_charged = headers.get_string("x-amz-request-charged");
215 result.restore = headers.get_string("x-amz-restore");
216 result.sse_customer_algorithm =
217 headers.get_string("x-amz-server-side-encryption-customer-algorithm");
218 result.sse_customer_key_md5 =
219 headers.get_string("x-amz-server-side-encryption-customer-key-MD5");
220 result.ssekms_key_id = headers.get_string("x-amz-server-side-encryption-aws-kms-key-id");
221 result.server_side_encryption = headers.get_string("x-amz-server-side-encryption");
222 result.storage_class = headers.get_string("x-amz-storage-class");
223 result.version_id = headers.get_string("x-amz-version-id");
224 result.website_redirect_location = headers.get_string("x-amz-website-redirect-location");
225 result
226 }
227}
228
229#[derive(Deserialize, Debug, Clone)]
230pub struct ListBucketResult {
231 #[serde(rename = "Name")]
232 pub name: String,
234 #[serde(rename = "Delimiter")]
235 pub delimiter: Option<String>,
237 #[serde(rename = "MaxKeys")]
238 pub max_keys: Option<i32>,
240 #[serde(rename = "Prefix")]
241 pub prefix: Option<String>,
243 #[serde(rename = "ContinuationToken")] #[serde(alias = "Marker")] pub continuation_token: Option<String>,
248 #[serde(rename = "EncodingType")]
249 pub encoding_type: Option<String>,
251 #[serde(default, rename = "IsTruncated")]
252 pub is_truncated: bool,
261 #[serde(rename = "NextContinuationToken", default)] #[serde(alias = "NextMarker")] pub next_continuation_token: Option<String>,
264 #[serde(rename = "Contents", default)]
265 pub contents: Vec<Object>,
267 #[serde(rename = "CommonPrefixes", default)]
268 pub common_prefixes: Option<Vec<CommonPrefix>>,
271}
272
273#[derive(Deserialize, Debug)]
274pub(crate) struct InitiateMultipartUploadResponse {
275 #[serde(rename = "Bucket")]
276 _bucket: String,
277 #[allow(dead_code)]
280 #[serde(rename = "Key")]
281 pub key: String,
282 #[serde(rename = "UploadId")]
283 pub upload_id: String,
284}
285
286#[derive(Debug)]
287pub struct PutStreamResponse {
288 pub status_code: u16,
289 pub uploaded_bytes: usize,
290}