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