Skip to main content

s3_wire/operation/multipart/
part.rs

1use std::{fmt, num::NonZeroU16};
2
3use super::{MultipartError, UploadId};
4use crate::operation::{Checksum, ObjectKey, RequestIds};
5use crate::stream::ByteStream;
6
7/// A validated multipart part number in the range 1 through 10,000.
8#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
9pub struct PartNumber(NonZeroU16);
10
11impl PartNumber {
12    /// Highest part number accepted by S3.
13    pub const MAX: u16 = 10_000;
14
15    /// Constructs a part number in S3's supported range.
16    pub fn new(value: u16) -> Option<Self> {
17        NonZeroU16::new(value)
18            .filter(|value| value.get() <= Self::MAX)
19            .map(Self)
20    }
21
22    /// Returns the validated number.
23    pub const fn get(self) -> u16 {
24        self.0.get()
25    }
26}
27
28/// Request to upload one multipart part.
29pub struct UploadPartRequest {
30    key: ObjectKey,
31    upload_id: UploadId,
32    part_number: PartNumber,
33    body: ByteStream,
34    checksum: Checksum,
35}
36
37impl UploadPartRequest {
38    /// Constructs an upload-part request from validated identifiers.
39    pub fn new(
40        key: ObjectKey,
41        upload_id: UploadId,
42        part_number: PartNumber,
43        body: ByteStream,
44    ) -> Self {
45        Self {
46            key,
47            upload_id,
48            part_number,
49            body,
50            checksum: Checksum::default(),
51        }
52    }
53
54    /// Returns the destination object key.
55    pub const fn key(&self) -> &ObjectKey {
56        &self.key
57    }
58
59    /// Returns the validated multipart upload identifier.
60    pub const fn upload_id(&self) -> &UploadId {
61        &self.upload_id
62    }
63
64    /// Returns the validated part number.
65    pub const fn part_number(&self) -> PartNumber {
66        self.part_number
67    }
68
69    /// Returns the part body.
70    pub const fn body(&self) -> &ByteStream {
71        &self.body
72    }
73
74    /// Returns the part body mutably.
75    pub fn body_mut(&mut self) -> &mut ByteStream {
76        &mut self.body
77    }
78
79    /// Returns the optional checksum of the part.
80    pub const fn checksum(&self) -> &Checksum {
81        &self.checksum
82    }
83
84    /// Attaches a checksum to the request.
85    pub fn with_checksum(mut self, checksum: Checksum) -> Self {
86        self.checksum = checksum;
87        self
88    }
89
90    /// Consumes the request and returns its body.
91    pub fn into_body(self) -> ByteStream {
92        self.body
93    }
94}
95
96impl fmt::Debug for UploadPartRequest {
97    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
98        formatter
99            .debug_struct("UploadPartRequest")
100            .field("key", &self.key)
101            .field("upload_id", &self.upload_id)
102            .field("part_number", &self.part_number)
103            .field("body", &"<stream>")
104            .field("checksum", &self.checksum)
105            .finish()
106    }
107}
108
109/// Result of uploading one multipart part.
110#[derive(Clone, Debug, Eq, PartialEq)]
111pub struct UploadPartOutput {
112    /// Part number supplied by the caller.
113    pub part_number: PartNumber,
114    /// Entity tag required when completing the upload.
115    pub e_tag: String,
116    /// Checksums returned by the service.
117    pub checksum: Checksum,
118    /// Service request identifiers.
119    pub request_ids: RequestIds,
120}
121
122/// A validated completed-part descriptor.
123#[derive(Clone, Debug, Eq, PartialEq)]
124pub struct CompletedPart {
125    part_number: PartNumber,
126    e_tag: String,
127    checksum: Checksum,
128}
129
130impl CompletedPart {
131    /// Validates a part number and non-empty entity tag.
132    pub fn new(part_number: u16, e_tag: impl Into<String>) -> Result<Self, MultipartError> {
133        let part_number =
134            PartNumber::new(part_number).ok_or(MultipartError::InvalidPartNumber(part_number))?;
135        let e_tag = e_tag.into();
136        if e_tag.trim().is_empty() {
137            return Err(MultipartError::EmptyETag {
138                part_number: part_number.get(),
139            });
140        }
141        Ok(Self {
142            part_number,
143            e_tag,
144            checksum: Checksum::default(),
145        })
146    }
147
148    /// Returns the validated part number.
149    pub const fn part_number(&self) -> PartNumber {
150        self.part_number
151    }
152
153    /// Returns the non-empty entity tag.
154    pub fn e_tag(&self) -> &str {
155        &self.e_tag
156    }
157
158    /// Returns checksums associated with this part.
159    pub const fn checksum(&self) -> &Checksum {
160        &self.checksum
161    }
162
163    /// Attaches checksums returned by the part upload.
164    pub fn with_checksum(mut self, checksum: Checksum) -> Self {
165        self.checksum = checksum;
166        self
167    }
168}