aws_multipart_upload/client/
part.rs

1use super::UploadId;
2use crate::complete_upload::CompleteMultipartUploadOutput as CompleteResponse;
3use crate::error::{ErrorRepr, Result};
4use crate::part_upload::UploadPartOutput as UploadResponse;
5
6use aws_sdk_s3::primitives::ByteStream;
7use bytes::{BufMut as _, BytesMut};
8use std::borrow::Cow;
9use std::fmt::{self, Display, Formatter};
10use std::io::{Result as IoResult, Write};
11use std::ops::{Deref, DerefMut};
12
13/// Body of the multipart upload request.
14///
15/// This type dereferences to [`BytesMut`], so in particular supports the methods
16/// of [`BufMut`], which is the preferred way of writing data to a `PartBody`.
17///
18/// `PartBody` also implements [`Write`], so it can also be used in combination
19/// with the class of external writer types that are parametrized by `Write`.
20///
21/// [`BufMut`]: bytes::BufMut
22#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
23pub struct PartBody(BytesMut);
24
25impl PartBody {
26    /// Construct a body from [`BytesMut`].
27    pub fn new(bytes: BytesMut) -> Self {
28        Self(bytes)
29    }
30
31    /// Returns an empty `PartBody` to write to that has pre-allocated capacity.
32    pub fn with_capacity(capacity: usize) -> Self {
33        let bytes = BytesMut::with_capacity(capacity);
34        Self(bytes)
35    }
36
37    /// Current size in bytes of the `PartBody`.
38    pub fn size(&self) -> usize {
39        self.0.len()
40    }
41
42    /// Convert this type into a [`ByteStream`], which is the type required by
43    /// the SDK in the request to AWS to add a part to a multipart upload.
44    ///
45    /// This conversion is zero-cost as it only involves methods that do a
46    /// ref-count increment on the inner byte buffer structure.
47    pub fn as_sdk_body(&mut self) -> ByteStream {
48        let buf = self.split();
49        let bytes = buf.freeze();
50        bytes.into()
51    }
52}
53
54impl Write for PartBody {
55    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
56        let bytes = buf.len();
57        self.reserve(bytes);
58        self.put(buf);
59        Ok(bytes)
60    }
61
62    fn flush(&mut self) -> IoResult<()> {
63        Ok(())
64    }
65}
66
67impl From<BytesMut> for PartBody {
68    fn from(value: BytesMut) -> Self {
69        Self(value)
70    }
71}
72
73impl Deref for PartBody {
74    type Target = BytesMut;
75
76    fn deref(&self) -> &Self::Target {
77        &self.0
78    }
79}
80
81impl DerefMut for PartBody {
82    fn deref_mut(&mut self) -> &mut Self::Target {
83        &mut self.0
84    }
85}
86
87impl AsRef<[u8]> for PartBody {
88    fn as_ref(&self) -> &[u8] {
89        self.deref().as_ref()
90    }
91}
92
93/// Number we assign to a part when uploading.
94///
95/// This, along with the entity tag found in the response, is required in the
96/// request to complete a multipart upload because it identifies the where the
97/// part goes when assembling the full object.
98#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
99pub struct PartNumber(i32);
100
101impl Default for PartNumber {
102    fn default() -> Self {
103        Self(1)
104    }
105}
106
107impl PartNumber {
108    /// Create a new `PartNumber` from a plain integer.
109    ///
110    /// Note that new uploads are required to start with a part number of 1,
111    /// which is how `PartNumber: Default`.
112    ///
113    /// With a handle on a current upload, [`increment`](PartNumber::increment)
114    /// should be used to create the next `PartNumber` when one has just been
115    /// added.
116    ///
117    /// Otherwise, use this when resuming a previous, partial upload.
118    pub fn new(n: i32) -> Self {
119        Self(n)
120    }
121
122    /// Increment the `PartNumber` by 1, returning the previous part number.
123    pub fn increment(&mut self) -> PartNumber {
124        self.0 += 1;
125        PartNumber(self.0 - 1)
126    }
127}
128
129impl Deref for PartNumber {
130    type Target = i32;
131
132    fn deref(&self) -> &Self::Target {
133        &self.0
134    }
135}
136
137impl DerefMut for PartNumber {
138    fn deref_mut(&mut self) -> &mut Self::Target {
139        &mut self.0
140    }
141}
142
143impl Display for PartNumber {
144    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
145        write!(f, "part_{}", self.0)
146    }
147}
148
149/// AWS entity tag.
150///
151/// This value is a hash of an object. It is assigned to an uploaded part and
152/// returned in the response from a part upload request.
153///
154/// It is also assigned to a completed upload and found in a successful complete
155/// upload response.
156#[derive(Debug, Clone, Default)]
157pub struct EntityTag(Cow<'static, str>);
158
159impl EntityTag {
160    fn new<T: Into<Cow<'static, str>>>(etag: T) -> Self {
161        Self(etag.into())
162    }
163
164    pub(crate) fn try_from_upload_resp(value: &UploadResponse) -> Result<Self, ErrorRepr> {
165        value
166            .e_tag
167            .as_deref()
168            .map(Self::from)
169            .ok_or_else(|| ErrorRepr::Missing("UploadResponse", "e_tag"))
170    }
171
172    pub(crate) fn try_from_complete_resp(value: &CompleteResponse) -> Result<Self, ErrorRepr> {
173        value
174            .e_tag
175            .as_deref()
176            .map(Self::from)
177            .ok_or_else(|| ErrorRepr::Missing("CompleteResponse", "e_tag"))
178    }
179}
180
181impl Deref for EntityTag {
182    type Target = str;
183
184    fn deref(&self) -> &str {
185        &self.0
186    }
187}
188
189impl AsRef<str> for EntityTag {
190    fn as_ref(&self) -> &str {
191        self
192    }
193}
194
195impl Display for EntityTag {
196    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
197        self.0.fmt(f)
198    }
199}
200
201impl From<&str> for EntityTag {
202    fn from(value: &str) -> Self {
203        Self::new(value.to_string())
204    }
205}
206
207impl From<String> for EntityTag {
208    fn from(value: String) -> Self {
209        Self(Cow::Owned(value))
210    }
211}
212
213/// The value for a successful part upload request.
214///
215/// All `CompletedPart`s need to be retained in order to construct a valid
216/// complete upload request.
217#[derive(Debug, Clone)]
218pub struct CompletedPart {
219    /// The ID of the upload this part was added to.
220    pub id: UploadId,
221    /// The entity tag of the uploaded part is a hash of the object in S3 that
222    /// was created by uploading this part.
223    pub etag: EntityTag,
224    /// The incrementing integer starting with 1 that identifies this part in the
225    /// part upload.
226    pub part_number: PartNumber,
227    /// The size of this part in bytes.
228    pub part_size: usize,
229}
230
231impl CompletedPart {
232    /// Create a new value from entity tag and part number used in the upload.
233    pub fn new(id: UploadId, etag: EntityTag, part_number: PartNumber, part_size: usize) -> Self {
234        Self {
235            id,
236            etag,
237            part_number,
238            part_size,
239        }
240    }
241}
242
243/// All completed part uploads for a multipart upload.
244#[derive(Debug, Clone, Default)]
245pub struct CompletedParts(Vec<CompletedPart>);
246
247impl CompletedParts {
248    /// Add a new [`CompletedPart`] to this collection.
249    pub fn push(&mut self, part: CompletedPart) {
250        self.0.push(part);
251    }
252
253    /// Extend this `CompletedParts` by the values from another.
254    pub fn extend(&mut self, other: CompletedParts) {
255        self.0.extend(other.0);
256        self.sort_ascending();
257    }
258
259    /// Returns the number of parts that have been successfully uploaded.
260    pub fn count(&self) -> usize {
261        self.0.len()
262    }
263
264    /// Returns the current size in bytes of this upload.
265    pub fn size(&self) -> usize {
266        self.0.iter().map(|p| p.part_size).sum()
267    }
268
269    /// Get the largest part number assigned, which ordinarily is the most
270    /// recently uploaded part.
271    pub fn max_part_number(&self) -> PartNumber {
272        match self.0.iter().max_by_key(|p| p.part_number) {
273            Some(part) => part.part_number,
274            _ => PartNumber::default(),
275        }
276    }
277
278    /// Sort the `CompletedPart`s in increasing order by part number.
279    ///
280    /// It is an error to make a request where the completed parts are not in
281    /// order.
282    pub fn sort_ascending(&mut self) {
283        self.sort_by_key(|part| part.part_number);
284    }
285}
286
287impl Deref for CompletedParts {
288    type Target = [CompletedPart];
289
290    fn deref(&self) -> &Self::Target {
291        &self.0
292    }
293}
294
295impl DerefMut for CompletedParts {
296    fn deref_mut(&mut self) -> &mut Self::Target {
297        &mut self.0
298    }
299}
300
301impl From<&CompletedParts> for aws_sdk_s3::types::CompletedMultipartUpload {
302    fn from(value: &CompletedParts) -> Self {
303        let completed_parts = value.0.iter().fold(Vec::new(), |mut acc, v| {
304            acc.push(
305                aws_sdk_s3::types::CompletedPart::builder()
306                    .e_tag(v.etag.to_string())
307                    .part_number(*v.part_number)
308                    .build(),
309            );
310
311            acc
312        });
313
314        aws_sdk_s3::types::CompletedMultipartUpload::builder()
315            .set_parts(Some(completed_parts))
316            .build()
317    }
318}