backblaze_b2_client/tasks/upload/
options.rs1use crate::{
2 definitions::{
3 bodies::B2StartLargeFileUploadBody,
4 headers::{B2UploadFileHeaders, B2UploadPartHeaders},
5 shared::{B2BucketFileRetention, B2FileLegalHold, B2ServerSideEncryption},
6 },
7 throttle::Throttle,
8 util::{InvalidValue, IsValid, RetryStrategy, SizeUnit},
9};
10
11#[derive(Debug)]
13pub struct FileUploadOptions {
14 pub large_file_cutoff: u64,
17 pub file_load_strategy: LargeFileLoadStrategy,
20 pub speed_throttle: Option<Throttle<u64>>,
27 pub retry_strategy: RetryStrategy,
30 pub options: B2FileUploadSettings,
33}
34
35impl Default for FileUploadOptions {
36 fn default() -> Self {
37 Self {
38 large_file_cutoff: SizeUnit::MEBIBYTE * 200,
39 file_load_strategy: Default::default(),
40 speed_throttle: None,
41 retry_strategy: Default::default(),
42 options: Default::default(),
43 }
44 }
45}
46
47impl IsValid for FileUploadOptions {
48 fn is_valid(&self) -> Result<(), InvalidValue> {
49 if self.large_file_cutoff < SizeUnit::MEBIBYTE * 5
50 && self.large_file_cutoff > SizeUnit::GIBIBYTE * 5
51 {
52 return Err(InvalidValue {
53 object_name: "FileUploadOptions".into(),
54 value_name: "large_file_cutoff".into(),
55 value_as_string: SizeUnit::from(self.large_file_cutoff as f64).to_string(),
56 expected: "5 MiB - 5 GiB".into(),
57 });
58 }
59
60 Ok(())
61 }
62}
63
64#[derive(Debug)]
66pub enum LargeFileLoadStrategy {
67 Constant(ConstantLargeFileLoadStrategy),
68 Dynamic(Box<dyn DynamicLargeFileLoadStrategy + Send + Sync>),
69}
70
71impl Default for LargeFileLoadStrategy {
72 fn default() -> Self {
73 Self::Dynamic(Box::new(DefaultLargeFileLoadStrategy))
74 }
75}
76
77#[derive(Debug, Clone)]
84pub struct ConstantLargeFileLoadStrategy {
85 pub part_size: u64,
88 pub chunk_size: u16,
91}
92
93impl IsValid for ConstantLargeFileLoadStrategy {
94 fn is_valid(&self) -> Result<(), InvalidValue> {
95 if self.chunk_size < 1 {
96 return Err(InvalidValue {
97 object_name: "ConstantLargeFileLoadStrategy".into(),
98 value_name: "chunk_size".into(),
99 value_as_string: self.chunk_size.to_string(),
100 expected: "at least 1".into(),
101 });
102 }
103
104 if self.part_size < SizeUnit::MEBIBYTE * 5 && self.part_size > SizeUnit::GIBIBYTE * 5 {
105 return Err(InvalidValue {
106 object_name: "ConstantLargeFileLoadStrategy".into(),
107 value_name: "part_size".into(),
108 value_as_string: SizeUnit::from(self.part_size as f64).to_string(),
109 expected: "5 MiB - 5 GiB".into(),
110 });
111 }
112
113 Ok(())
114 }
115}
116
117impl Default for ConstantLargeFileLoadStrategy {
118 fn default() -> Self {
119 Self {
120 part_size: SizeUnit::MEBIBYTE * 5,
121 chunk_size: 3,
122 }
123 }
124}
125
126pub trait DynamicLargeFileLoadStrategy: std::fmt::Debug {
128 fn get_load_strategy(&self, file_size: u64) -> ConstantLargeFileLoadStrategy;
129}
130
131#[derive(Debug)]
132pub struct DefaultLargeFileLoadStrategy;
133
134impl DynamicLargeFileLoadStrategy for DefaultLargeFileLoadStrategy {
135 fn get_load_strategy(&self, file_size: u64) -> ConstantLargeFileLoadStrategy {
136 let chunk_size = ((file_size / (SizeUnit::MEBIBYTE * 5)) / 200).max(3);
138 let chunk_size = chunk_size.min(u16::MAX as u64) as u16;
139
140 ConstantLargeFileLoadStrategy {
141 part_size: SizeUnit::MEBIBYTE * 5,
142 chunk_size,
143 }
144 }
145}
146
147#[derive(Clone, Debug)]
149pub struct B2FileUploadSettings {
150 pub content_type: String,
152 pub src_last_modified_millis: Option<u64>,
153 pub b2_content_disposition: Option<String>,
154 pub b2_content_language: Option<String>,
155 pub b2_expires: Option<String>,
156 pub b2_cache_control: Option<String>,
157 pub b2_content_encoding: Option<String>,
158 pub custom_upload_timestamp: Option<u64>,
159 pub legal_hold: Option<B2FileLegalHold>,
160 pub file_retention: Option<B2BucketFileRetention>,
161 pub server_side_encryption: Option<B2ServerSideEncryption>,
162}
163
164impl B2FileUploadSettings {
165 pub(super) fn apply_file_upload(self, mut u: B2UploadFileHeaders) -> B2UploadFileHeaders {
166 u.content_type = self.content_type;
167 u.src_last_modified_millis = self.src_last_modified_millis;
168 u.b2_content_disposition = self.b2_content_disposition;
169 u.b2_content_language = self.b2_content_language;
170 u.b2_expires = self.b2_expires;
171 u.b2_cache_control = self.b2_cache_control;
172 u.b2_content_encoding = self.b2_content_encoding;
173 u.custom_upload_timestamp = self.custom_upload_timestamp;
174 u.legal_hold = self.legal_hold;
175
176 if let Some(retention) = self.file_retention {
177 u.retention_mode = retention.mode;
178 u.retention_retain_until_timestamp = retention.retain_until_timestamp;
179 }
180
181 u.server_side_encryption = self.server_side_encryption;
182
183 u
184 }
185
186 pub(super) fn apply_large_file_upload(
187 self,
188 mut u: B2StartLargeFileUploadBody,
189 ) -> B2StartLargeFileUploadBody {
190 u.content_type = self.content_type;
191 u.custom_upload_timestamp = self.custom_upload_timestamp;
192 u.legal_hold = self.legal_hold;
193 u.file_retention = self.file_retention;
194 u.server_side_encryption = self.server_side_encryption;
195
196 if let Some(ref mut info) = u.file_info {
197 if let Some(v) = self.src_last_modified_millis {
198 info.insert("src_last_modified_millis".into(), v.to_string());
199 }
200
201 if let Some(v) = self.b2_content_disposition {
202 info.insert("b2-content-disposition".into(), v);
203 }
204
205 if let Some(v) = self.b2_content_language {
206 info.insert("b2-content-language".into(), v);
207 }
208
209 if let Some(v) = self.b2_expires {
210 info.insert("b2-expires".into(), v);
211 }
212
213 if let Some(v) = self.b2_cache_control {
214 info.insert("b2-cache-control".into(), v);
215 }
216
217 if let Some(v) = self.b2_content_encoding {
218 info.insert("b2-content-encoding".into(), v);
219 }
220 }
221
222 u
223 }
224
225 pub(super) fn apply_file_part_upload(self, mut u: B2UploadPartHeaders) -> B2UploadPartHeaders {
226 if let Some(enc) = self.server_side_encryption {
227 use B2ServerSideEncryption::*;
228
229 match enc {
230 SseB2 { algorithm } => {
231 u.server_side_encryption_customer_algorithm = Some(algorithm);
232 }
233 SseC {
234 algorithm,
235 customer_key,
236 customer_key_md5,
237 } => {
238 u.server_side_encryption_customer_algorithm = Some(algorithm);
239 u.server_side_encryption_customer_key = Some(customer_key);
240 u.server_side_encryption_customer_key_md5 = Some(customer_key_md5);
241 }
242 Disabled => {}
243 }
244 }
245
246 u
247 }
248}
249
250impl Default for B2FileUploadSettings {
251 fn default() -> Self {
252 Self {
253 content_type: "b2/x-auto".into(),
254 src_last_modified_millis: None,
255 b2_content_disposition: None,
256 b2_content_language: None,
257 b2_expires: None,
258 b2_cache_control: None,
259 b2_content_encoding: None,
260 custom_upload_timestamp: None,
261 legal_hold: None,
262 file_retention: None,
263 server_side_encryption: None,
264 }
265 }
266}