s3_wire/operation/multipart/
completion.rs1use http::HeaderMap;
2
3use super::{CompletedPart, PartNumber, UploadId};
4use crate::operation::{Checksum, ObjectKey, RequestIds};
5
6#[derive(Clone, derive_more::Debug, Eq, PartialEq)]
8pub struct CompleteMultipartUploadRequest {
9 pub key: ObjectKey,
11 upload_id: UploadId,
12 completed_parts: Vec<CompletedPart>,
13 #[debug("{:?}", "<redacted>")]
16 pub headers: HeaderMap,
17}
18
19impl CompleteMultipartUploadRequest {
20 pub fn new(
23 key: ObjectKey,
24 upload_id: UploadId,
25 completed_parts: Vec<CompletedPart>,
26 ) -> Result<Self, MultipartError> {
27 validate_completed_parts(&completed_parts)?;
28 Ok(Self {
29 key,
30 upload_id,
31 completed_parts,
32 headers: HeaderMap::new(),
33 })
34 }
35
36 pub fn with_headers(mut self, headers: HeaderMap) -> Self {
38 self.headers = headers;
39 self
40 }
41
42 pub fn completed_parts(&self) -> &[CompletedPart] {
44 &self.completed_parts
45 }
46
47 pub const fn upload_id(&self) -> &UploadId {
49 &self.upload_id
50 }
51}
52
53#[derive(Clone, Debug, Default, Eq, PartialEq)]
55pub struct CompleteMultipartUploadOutput {
56 pub location: Option<String>,
58 pub bucket: Option<String>,
60 pub key: Option<ObjectKey>,
62 pub e_tag: Option<String>,
64 pub version_id: Option<String>,
66 pub checksum: Checksum,
68 pub request_ids: RequestIds,
70}
71
72#[derive(Clone, derive_more::Debug, Eq, PartialEq)]
74pub struct AbortMultipartUploadRequest {
75 key: ObjectKey,
76 upload_id: UploadId,
77 #[debug("{:?}", "<redacted>")]
80 pub headers: HeaderMap,
81}
82
83impl AbortMultipartUploadRequest {
84 pub fn new(key: ObjectKey, upload_id: UploadId) -> Self {
86 Self {
87 key,
88 upload_id,
89 headers: HeaderMap::new(),
90 }
91 }
92
93 pub fn with_headers(mut self, headers: HeaderMap) -> Self {
95 self.headers = headers;
96 self
97 }
98
99 pub const fn key(&self) -> &ObjectKey {
101 &self.key
102 }
103
104 pub const fn upload_id(&self) -> &UploadId {
106 &self.upload_id
107 }
108}
109
110#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
112pub enum MultipartError {
113 #[error("multipart part number {0} is outside 1..=10000")]
115 InvalidPartNumber(u16),
116 #[error("multipart part {part_number} has an empty ETag")]
118 EmptyETag {
119 part_number: u16,
121 },
122 #[error("multipart part {0:?} was recorded more than once")]
124 DuplicatePart(PartNumber),
125 #[error("a multipart upload cannot be completed without parts")]
127 NoCompletedParts,
128 #[error("multipart part {next:?} does not follow {previous:?} in ascending order")]
130 OutOfOrder {
131 previous: PartNumber,
133 next: PartNumber,
135 },
136}
137
138fn validate_completed_parts(parts: &[CompletedPart]) -> Result<(), MultipartError> {
139 if parts.is_empty() {
140 return Err(MultipartError::NoCompletedParts);
141 }
142 for window in parts.windows(2) {
143 if window[1].part_number() <= window[0].part_number() {
144 return Err(MultipartError::OutOfOrder {
145 previous: window[0].part_number(),
146 next: window[1].part_number(),
147 });
148 }
149 }
150 Ok(())
151}