s3_wire/operation/multipart/
part.rs1use std::num::NonZeroU16;
2
3use http::HeaderMap;
4
5use super::{MultipartError, UploadId};
6use crate::operation::{Checksum, Conditions, CopySource, ObjectKey, RequestIds};
7use crate::stream::ByteStream;
8
9#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
11pub struct PartNumber(NonZeroU16);
12
13impl PartNumber {
14 pub const MAX: u16 = 10_000;
16
17 pub fn new(value: u16) -> Option<Self> {
19 NonZeroU16::new(value)
20 .filter(|value| value.get() <= Self::MAX)
21 .map(Self)
22 }
23
24 pub const fn get(self) -> u16 {
26 self.0.get()
27 }
28}
29
30#[derive(derive_more::Debug)]
32pub struct UploadPartRequest {
33 key: ObjectKey,
34 upload_id: UploadId,
35 part_number: PartNumber,
36 #[debug("{:?}", "<stream>")]
37 body: ByteStream,
38 checksum: Checksum,
39 #[debug("{:?}", "<redacted>")]
42 pub headers: HeaderMap,
43}
44
45impl UploadPartRequest {
46 pub fn new(
48 key: ObjectKey,
49 upload_id: UploadId,
50 part_number: PartNumber,
51 body: ByteStream,
52 ) -> Self {
53 Self {
54 key,
55 upload_id,
56 part_number,
57 body,
58 checksum: Checksum::default(),
59 headers: HeaderMap::new(),
60 }
61 }
62
63 pub fn with_headers(mut self, headers: HeaderMap) -> Self {
65 self.headers = headers;
66 self
67 }
68
69 pub const fn key(&self) -> &ObjectKey {
71 &self.key
72 }
73
74 pub const fn upload_id(&self) -> &UploadId {
76 &self.upload_id
77 }
78
79 pub const fn part_number(&self) -> PartNumber {
81 self.part_number
82 }
83
84 pub const fn body(&self) -> &ByteStream {
86 &self.body
87 }
88
89 pub fn body_mut(&mut self) -> &mut ByteStream {
91 &mut self.body
92 }
93
94 pub const fn checksum(&self) -> &Checksum {
96 &self.checksum
97 }
98
99 pub fn with_checksum(mut self, checksum: Checksum) -> Self {
101 self.checksum = checksum;
102 self
103 }
104
105 pub fn into_body(self) -> ByteStream {
107 self.body
108 }
109}
110
111#[derive(Clone, Debug, Eq, PartialEq)]
113pub struct UploadPartOutput {
114 pub part_number: PartNumber,
116 pub e_tag: String,
118 pub checksum: Checksum,
120 pub request_ids: RequestIds,
122}
123
124#[derive(Clone, Copy, Debug, Eq, PartialEq)]
126pub struct CopyPartRange {
127 start: u64,
128 end: u64,
129}
130
131impl CopyPartRange {
132 pub fn new(start: u64, end: u64) -> Result<Self, crate::operation::RangeError> {
134 if end < start {
135 return Err(crate::operation::RangeError { start, end });
136 }
137 Ok(Self { start, end })
138 }
139
140 pub const fn start(self) -> u64 {
142 self.start
143 }
144
145 pub const fn end(self) -> u64 {
147 self.end
148 }
149
150 pub(crate) fn header_value(self) -> String {
151 format!("bytes={}-{}", self.start, self.end)
152 }
153}
154
155#[derive(Clone, derive_more::Debug, Eq, PartialEq)]
157pub struct UploadPartCopyRequest {
158 destination: ObjectKey,
159 upload_id: UploadId,
160 part_number: PartNumber,
161 pub source: CopySource,
163 pub source_range: Option<CopyPartRange>,
165 pub source_conditions: Conditions,
167 #[debug("{:?}", "<redacted>")]
170 pub headers: HeaderMap,
171}
172
173impl UploadPartCopyRequest {
174 pub fn new(
176 destination: ObjectKey,
177 upload_id: UploadId,
178 part_number: PartNumber,
179 source: CopySource,
180 ) -> Self {
181 Self {
182 destination,
183 upload_id,
184 part_number,
185 source,
186 source_range: None,
187 source_conditions: Conditions::default(),
188 headers: HeaderMap::new(),
189 }
190 }
191
192 pub fn with_headers(mut self, headers: HeaderMap) -> Self {
194 self.headers = headers;
195 self
196 }
197
198 pub const fn destination(&self) -> &ObjectKey {
200 &self.destination
201 }
202
203 pub const fn upload_id(&self) -> &UploadId {
205 &self.upload_id
206 }
207
208 pub const fn part_number(&self) -> PartNumber {
210 self.part_number
211 }
212}
213
214#[derive(Clone, Debug, Eq, PartialEq)]
216pub struct UploadPartCopyOutput {
217 pub part_number: PartNumber,
219 pub e_tag: String,
221 pub last_modified: Option<time::OffsetDateTime>,
223 pub checksum: Checksum,
225 pub request_ids: RequestIds,
227}
228
229impl UploadPartCopyOutput {
230 pub fn completed_part(&self) -> Result<CompletedPart, MultipartError> {
232 CompletedPart::new(self.part_number.get(), self.e_tag.clone())
233 .map(|part| part.with_checksum(self.checksum.clone()))
234 }
235}
236
237#[derive(Clone, Debug, Eq, PartialEq)]
239pub struct CompletedPart {
240 part_number: PartNumber,
241 e_tag: String,
242 checksum: Checksum,
243}
244
245impl CompletedPart {
246 pub fn new(part_number: u16, e_tag: impl Into<String>) -> Result<Self, MultipartError> {
248 let part_number =
249 PartNumber::new(part_number).ok_or(MultipartError::InvalidPartNumber(part_number))?;
250 let e_tag = e_tag.into();
251 if e_tag.trim().is_empty() {
252 return Err(MultipartError::EmptyETag {
253 part_number: part_number.get(),
254 });
255 }
256 Ok(Self {
257 part_number,
258 e_tag,
259 checksum: Checksum::default(),
260 })
261 }
262
263 pub const fn part_number(&self) -> PartNumber {
265 self.part_number
266 }
267
268 pub fn e_tag(&self) -> &str {
270 &self.e_tag
271 }
272
273 pub const fn checksum(&self) -> &Checksum {
275 &self.checksum
276 }
277
278 pub fn with_checksum(mut self, checksum: Checksum) -> Self {
280 self.checksum = checksum;
281 self
282 }
283}