1use std::future::Future;
2
3use bytes::Bytes;
4use http::{HeaderMap, Method, header};
5use serde::Serialize;
6
7use crate::body::NoneBody;
8use crate::error::Result;
9use crate::response::BinaryResponseProcessor;
10use crate::{Client, Ops, Prepared, QueryAuthOptions, Request};
11
12#[derive(Debug, Clone, Default, Serialize)]
14pub struct GetObjectParams {
15 #[serde(rename = "versionId")]
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub version_id: Option<String>,
19 #[serde(rename = "response-cache-control")]
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub response_cache_control: Option<String>,
23 #[serde(rename = "response-content-disposition")]
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub response_content_disposition: Option<String>,
27 #[serde(rename = "response-content-encoding")]
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub response_content_encoding: Option<String>,
31 #[serde(rename = "response-content-language")]
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub response_content_language: Option<String>,
35 #[serde(rename = "response-content-type")]
37 #[serde(skip_serializing_if = "Option::is_none")]
38 pub response_content_type: Option<String>,
39 #[serde(rename = "response-expires")]
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub response_expires: Option<String>,
43}
44
45impl GetObjectParams {
46 pub fn new() -> Self {
47 Self::default()
48 }
49
50 pub fn version_id(mut self, version_id: impl Into<String>) -> Self {
52 self.version_id = Some(version_id.into());
53 self
54 }
55
56 pub fn response_cache_control(mut self, cache_control: impl Into<String>) -> Self {
58 self.response_cache_control = Some(cache_control.into());
59 self
60 }
61
62 pub fn response_content_disposition(mut self, content_disposition: impl Into<String>) -> Self {
64 self.response_content_disposition = Some(content_disposition.into());
65 self
66 }
67
68 pub fn response_content_encoding(mut self, content_encoding: impl Into<String>) -> Self {
70 self.response_content_encoding = Some(content_encoding.into());
71 self
72 }
73
74 pub fn response_content_language(mut self, content_language: impl Into<String>) -> Self {
76 self.response_content_language = Some(content_language.into());
77 self
78 }
79
80 pub fn response_content_type(mut self, content_type: impl Into<String>) -> Self {
82 self.response_content_type = Some(content_type.into());
83 self
84 }
85
86 pub fn response_expires(mut self, expires: impl Into<String>) -> Self {
88 self.response_expires = Some(expires.into());
89 self
90 }
91}
92
93#[derive(Debug, Clone, Default)]
95pub struct GetObjectOptions {
96 pub range: Option<String>,
98 pub if_modified_since: Option<String>,
100 pub if_unmodified_since: Option<String>,
102 pub if_match: Option<String>,
104 pub if_none_match: Option<String>,
106 pub accept_encoding: Option<String>,
108}
109
110impl GetObjectOptions {
111 pub fn range(mut self, range: impl Into<String>) -> Self {
113 self.range = Some(range.into());
114 self
115 }
116
117 pub fn if_modified_since(mut self, time: impl Into<String>) -> Self {
119 self.if_modified_since = Some(time.into());
120 self
121 }
122
123 pub fn if_unmodified_since(mut self, time: impl Into<String>) -> Self {
125 self.if_unmodified_since = Some(time.into());
126 self
127 }
128
129 pub fn if_match(mut self, etag: impl Into<String>) -> Self {
131 self.if_match = Some(etag.into());
132 self
133 }
134
135 pub fn if_none_match(mut self, etag: impl Into<String>) -> Self {
137 self.if_none_match = Some(etag.into());
138 self
139 }
140
141 pub fn accept_encoding(mut self, encoding: impl Into<String>) -> Self {
143 self.accept_encoding = Some(encoding.into());
144 self
145 }
146}
147
148impl GetObjectOptions {
149 fn into_headers(self) -> Result<HeaderMap> {
150 let mut headers = HeaderMap::new();
151
152 if let Some(range) = self.range {
154 headers.insert(header::RANGE, range.parse()?);
155 }
156
157 if let Some(if_modified_since) = self.if_modified_since {
159 headers.insert(header::IF_MODIFIED_SINCE, if_modified_since.parse()?);
160 }
161
162 if let Some(if_unmodified_since) = self.if_unmodified_since {
163 headers.insert(header::IF_UNMODIFIED_SINCE, if_unmodified_since.parse()?);
164 }
165
166 if let Some(if_match) = self.if_match {
167 headers.insert(header::IF_MATCH, if_match.parse()?);
168 }
169
170 if let Some(if_none_match) = self.if_none_match {
171 headers.insert(header::IF_NONE_MATCH, if_none_match.parse()?);
172 }
173
174 if let Some(accept_encoding) = self.accept_encoding {
176 headers.insert(header::ACCEPT_ENCODING, accept_encoding.parse()?);
177 }
178
179 Ok(headers)
180 }
181}
182
183pub struct GetObject {
185 pub object_key: String,
186 pub params: GetObjectParams,
187 pub options: GetObjectOptions,
188}
189
190impl Ops for GetObject {
191 type Response = BinaryResponseProcessor;
192 type Body = NoneBody;
193 type Query = GetObjectParams;
194
195 fn prepare(self) -> Result<Prepared<GetObjectParams>> {
196 Ok(Prepared {
197 method: Method::GET,
198 key: Some(self.object_key),
199 query: Some(self.params),
200 headers: Some(self.options.into_headers()?),
201 ..Default::default()
202 })
203 }
204}
205
206pub trait GetObjectOperations {
208 fn get_object(
212 &self,
213 object_key: impl Into<String>,
214 params: GetObjectParams,
215 options: Option<GetObjectOptions>,
216 ) -> impl Future<Output = Result<Bytes>>;
217
218 fn presign_get_object(
219 &self,
220 object_key: impl Into<String>,
221 public: bool,
222 params: GetObjectParams,
223 options: Option<GetObjectOptions>,
224 query_auth_options: QueryAuthOptions,
225 ) -> impl Future<Output = Result<String>>;
226}
227
228impl GetObjectOperations for Client {
229 async fn get_object(
230 &self,
231 object_key: impl Into<String>,
232 params: GetObjectParams,
233 options: Option<GetObjectOptions>,
234 ) -> Result<Bytes> {
235 let ops = GetObject {
236 object_key: object_key.into(),
237 params,
238 options: options.unwrap_or_default(),
239 };
240
241 self.request(ops).await
242 }
243
244 async fn presign_get_object(
245 &self,
246 object_key: impl Into<String>,
247 public: bool,
248 params: GetObjectParams,
249 options: Option<GetObjectOptions>,
250 query_auth_options: QueryAuthOptions,
251 ) -> Result<String> {
252 let ops = GetObject {
253 object_key: object_key.into(),
254 params,
255 options: options.unwrap_or_default(),
256 };
257 self.presign(ops, public, Some(query_auth_options)).await
258 }
259}
260
261#[derive(Debug, Clone, Default)]
267pub struct GetObjectRequestBuilder {
268 params: GetObjectParams,
269 options: GetObjectOptions,
270}
271
272impl GetObjectRequestBuilder {
273 pub fn new() -> Self {
274 Self::default()
275 }
276
277 pub fn version_id(mut self, version_id: impl Into<String>) -> Self {
279 self.params.version_id = Some(version_id.into());
280 self
281 }
282
283 pub fn range(mut self, range: impl Into<String>) -> Self {
285 self.options.range = Some(range.into());
286 self
287 }
288
289 pub fn range_bytes(mut self, start: u64, end: Option<u64>) -> Self {
291 let range = match end {
292 Some(end) => format!("bytes={start}-{end}"),
293 None => format!("bytes={start}-"),
294 };
295 self.options.range = Some(range);
296 self
297 }
298
299 pub fn if_modified_since(mut self, time: impl Into<String>) -> Self {
301 self.options.if_modified_since = Some(time.into());
302 self
303 }
304
305 pub fn if_unmodified_since(mut self, time: impl Into<String>) -> Self {
307 self.options.if_unmodified_since = Some(time.into());
308 self
309 }
310
311 pub fn if_match(mut self, etag: impl Into<String>) -> Self {
313 self.options.if_match = Some(etag.into());
314 self
315 }
316
317 pub fn if_none_match(mut self, etag: impl Into<String>) -> Self {
319 self.options.if_none_match = Some(etag.into());
320 self
321 }
322
323 pub fn response_cache_control(mut self, cache_control: impl Into<String>) -> Self {
325 self.params.response_cache_control = Some(cache_control.into());
326 self
327 }
328
329 pub fn response_content_disposition(mut self, content_disposition: impl Into<String>) -> Self {
331 self.params.response_content_disposition = Some(content_disposition.into());
332 self
333 }
334
335 pub fn response_content_type(mut self, content_type: impl Into<String>) -> Self {
337 self.params.response_content_type = Some(content_type.into());
338 self
339 }
340
341 pub fn build(self) -> (GetObjectParams, Option<GetObjectOptions>) {
343 let options = if self.options.range.is_some()
344 || self.options.if_modified_since.is_some()
345 || self.options.if_unmodified_since.is_some()
346 || self.options.if_match.is_some()
347 || self.options.if_none_match.is_some()
348 || self.options.accept_encoding.is_some()
349 {
350 Some(self.options)
351 } else {
352 None
353 };
354
355 (self.params, options)
356 }
357}