Skip to main content

aws_multipart_upload/client/
mod.rs

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