aws_multipart_upload/client/
mod.rs

1use self::part::CompletedPart;
2use self::request::*;
3use crate::create_upload::CreateMultipartUploadOutput as CreateResponse;
4use crate::error::{ErrorRepr, Result};
5use crate::uri::ObjectUri;
6
7use futures::future::LocalBoxFuture;
8use std::borrow::Cow;
9use std::fmt::{self, Formatter};
10use std::ops::Deref;
11use std::sync::Arc;
12
13pub mod part;
14pub mod request;
15mod sdk;
16pub use sdk::SdkClient;
17
18/// `SendRequest` represents the atomic operations in a multipart upload.
19pub trait SendRequest {
20    /// Send a request to create a new multipart upload, returning an
21    /// [`UploadData`] having the upload ID assignment.
22    fn send_create_upload_request(
23        &self,
24        req: CreateRequest,
25    ) -> impl Future<Output = Result<UploadData>>;
26
27    /// Send a request to upload a part to a multipart upload, returning the
28    /// [`CompletedPart`] containing entity tag and part number, which are required
29    /// in the subsequent complete upload request.
30    fn send_new_part_upload_request(
31        &self,
32        req: UploadPartRequest,
33    ) -> impl Future<Output = Result<CompletedPart>>;
34
35    /// Send a request to complete a multipart upload, returning a
36    /// [`CompletedUpload`], which has the unique entity tag of the object as well
37    /// as the object URI.
38    fn send_complete_upload_request(
39        &self,
40        req: CompleteRequest,
41    ) -> impl Future<Output = Result<CompletedUpload>>;
42
43    /// Send a request to abort a multipart upload returning an empty response if
44    /// successful.
45    fn send_abort_upload_request(&self, req: AbortRequest) -> impl Future<Output = Result<()>>;
46}
47
48impl<D, T> SendRequest for T
49where
50    D: SendRequest,
51    T: Deref<Target = D>,
52{
53    async fn send_create_upload_request(&self, req: CreateRequest) -> Result<UploadData> {
54        self.deref().send_create_upload_request(req).await
55    }
56
57    async fn send_new_part_upload_request(&self, req: UploadPartRequest) -> Result<CompletedPart> {
58        self.deref().send_new_part_upload_request(req).await
59    }
60
61    async fn send_complete_upload_request(&self, req: CompleteRequest) -> Result<CompletedUpload> {
62        self.deref().send_complete_upload_request(req).await
63    }
64
65    async fn send_abort_upload_request(&self, req: AbortRequest) -> Result<()> {
66        self.deref().send_abort_upload_request(req).await
67    }
68}
69
70/// A client of the multipart upload API.
71///
72/// This can be built from any type that implements `SendRequest`, such as the
73/// [`SdkClient`].
74#[derive(Clone)]
75pub struct UploadClient {
76    pub(crate) inner: Arc<dyn BoxedSendRequest>,
77}
78
79impl UploadClient {
80    /// Create a new `UploadClient`.
81    pub fn new<C>(client: C) -> Self
82    where
83        C: SendRequest + 'static,
84    {
85        let inner = SendRequestInner::new(client);
86        Self {
87            inner: Arc::new(inner),
88        }
89    }
90}
91
92impl SendRequest for UploadClient {
93    async fn send_create_upload_request(&self, req: CreateRequest) -> Result<UploadData> {
94        self.inner.send_create_upload(req).await
95    }
96
97    async fn send_new_part_upload_request(&self, req: UploadPartRequest) -> Result<CompletedPart> {
98        self.inner.send_upload_part(req).await
99    }
100
101    async fn send_complete_upload_request(&self, req: CompleteRequest) -> Result<CompletedUpload> {
102        self.inner.send_complete_upload(req).await
103    }
104
105    async fn send_abort_upload_request(&self, req: AbortRequest) -> Result<()> {
106        self.inner.send_abort_upload(req).await
107    }
108}
109
110impl fmt::Debug for UploadClient {
111    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
112        f.debug_struct("UploadClient")
113            .field("inner", &"SendRequest")
114            .finish()
115    }
116}
117
118/// ID assigned by AWS for this upload.
119#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
120pub struct UploadId(Cow<'static, str>);
121
122impl UploadId {
123    pub(crate) fn new<T: Into<Cow<'static, str>>>(id: T) -> Self {
124        Self(id.into())
125    }
126
127    pub(crate) fn try_from_create_resp(value: &CreateResponse) -> Result<Self, ErrorRepr> {
128        value
129            .upload_id
130            .as_deref()
131            .map(Self::from)
132            .ok_or_else(|| ErrorRepr::Missing("CreateResponse", "upload_id"))
133    }
134
135    pub(crate) fn is_empty(&self) -> bool {
136        self.0.is_empty()
137    }
138}
139
140impl Deref for UploadId {
141    type Target = str;
142
143    fn deref(&self) -> &str {
144        &self.0
145    }
146}
147
148impl fmt::Display for UploadId {
149    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
150        self.0.fmt(f)
151    }
152}
153
154impl From<&str> for UploadId {
155    fn from(value: &str) -> Self {
156        Self::new(value.to_string())
157    }
158}
159
160impl From<String> for UploadId {
161    fn from(value: String) -> Self {
162        Self(Cow::Owned(value))
163    }
164}
165
166/// Data identifying a multipart upload.
167///
168/// The `UploadId` assigned by AWS and the `ObjectUri` that the user created the
169/// upload with are required properties of any of the upload client's operations.
170///
171/// The [`SendCreateUpload`] request future resolves to this type if the request
172/// was successful.
173///
174/// [`SendCreateUpload`]: self::request::SendCreateUpload
175#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
176pub struct UploadData {
177    /// The ID for the upload assigned by AWS.
178    pub id: UploadId,
179    /// The S3 URI of the object being uploaded.
180    pub uri: ObjectUri,
181}
182
183impl UploadData {
184    /// Create a new value from an upload ID and object URI.
185    pub fn new<T, U>(id: T, uri: U) -> Self
186    where
187        T: Into<UploadId>,
188        U: Into<ObjectUri>,
189    {
190        Self {
191            id: id.into(),
192            uri: uri.into(),
193        }
194    }
195
196    /// Get an owned upload ID.
197    pub fn get_id(&self) -> UploadId {
198        self.id.clone()
199    }
200
201    /// Get an owned object URI.
202    pub fn get_uri(&self) -> ObjectUri {
203        self.uri.clone()
204    }
205}
206
207/// Object-safe `SendRequest`.
208pub(crate) trait BoxedSendRequest {
209    fn send_create_upload(&self, req: CreateRequest) -> LocalBoxFuture<'_, Result<UploadData>>;
210
211    fn send_upload_part(&self, req: UploadPartRequest)
212    -> LocalBoxFuture<'_, Result<CompletedPart>>;
213
214    fn send_complete_upload(
215        &self,
216        req: CompleteRequest,
217    ) -> LocalBoxFuture<'_, Result<CompletedUpload>>;
218
219    fn send_abort_upload(&self, req: AbortRequest) -> LocalBoxFuture<'_, Result<()>>;
220}
221
222/// Implements `BoxedSendRequest` for any `T: SendRequest` so that we can
223/// construct `UploadClient`.
224struct SendRequestInner<T>(T);
225
226impl<T: SendRequest> SendRequestInner<T> {
227    pub(super) fn new(inner: T) -> Self {
228        Self(inner)
229    }
230}
231
232impl<T: SendRequest> BoxedSendRequest for SendRequestInner<T> {
233    fn send_create_upload(&self, req: CreateRequest) -> LocalBoxFuture<'_, Result<UploadData>> {
234        Box::pin(self.0.send_create_upload_request(req))
235    }
236
237    fn send_upload_part(
238        &self,
239        req: UploadPartRequest,
240    ) -> LocalBoxFuture<'_, Result<CompletedPart>> {
241        Box::pin(self.0.send_new_part_upload_request(req))
242    }
243
244    fn send_complete_upload(
245        &self,
246        req: CompleteRequest,
247    ) -> LocalBoxFuture<'_, Result<CompletedUpload>> {
248        Box::pin(self.0.send_complete_upload_request(req))
249    }
250
251    fn send_abort_upload(&self, req: AbortRequest) -> LocalBoxFuture<'_, Result<()>> {
252        Box::pin(self.0.send_abort_upload_request(req))
253    }
254}