opendal/types/options.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Options module provides options definitions for operations.
19
20use crate::raw::{BytesRange, Timestamp};
21use std::collections::HashMap;
22
23/// Options for delete operations.
24#[derive(Debug, Clone, Default, Eq, PartialEq)]
25pub struct DeleteOptions {
26 /// The version of the file to delete.
27 pub version: Option<String>,
28 /// Whether to delete the target recursively.
29 ///
30 /// - If `false`, behaves like the traditional single-object delete.
31 /// - If `true`, all entries under the path (or sharing the prefix for file-like paths)
32 /// will be removed.
33 pub recursive: bool,
34}
35
36/// Options for list operations.
37
38#[derive(Debug, Clone, Default, Eq, PartialEq)]
39pub struct ListOptions {
40 /// The limit passed to underlying service to specify the max results
41 /// that could return per-request.
42 ///
43 /// Users could use this to control the memory usage of list operation.
44 pub limit: Option<usize>,
45 /// The start_after passes to underlying service to specify the specified key
46 /// to start listing from.
47 pub start_after: Option<String>,
48 /// The recursive is used to control whether the list operation is recursive.
49 ///
50 /// - If `false`, list operation will only list the entries under the given path.
51 /// - If `true`, list operation will list all entries that starts with given path.
52 ///
53 /// Default to `false`.
54 pub recursive: bool,
55 /// The version is used to control whether the object versions should be returned.
56 ///
57 /// - If `false`, list operation will not return with object versions
58 /// - If `true`, list operation will return with object versions if object versioning is supported
59 /// by the underlying service
60 ///
61 /// Default to `false`
62 pub versions: bool,
63 /// The deleted is used to control whether the deleted objects should be returned.
64 ///
65 /// - If `false`, list operation will not return with deleted objects
66 /// - If `true`, list operation will return with deleted objects if object versioning is supported
67 /// by the underlying service
68 ///
69 /// Default to `false`
70 pub deleted: bool,
71}
72
73/// Options for read operations.
74#[derive(Debug, Clone, Default, Eq, PartialEq)]
75pub struct ReadOptions {
76 /// Set `range` for this operation.
77 ///
78 /// If we have a file with size `n`.
79 ///
80 /// - `..` means read bytes in range `[0, n)` of file.
81 /// - `0..1024` and `..1024` means read bytes in range `[0, 1024)` of file
82 /// - `1024..` means read bytes in range `[1024, n)` of file
83 ///
84 /// The type implements `From<RangeBounds<u64>>`, so users can use `(1024..).into()` instead.
85 pub range: BytesRange,
86 /// Set `version` for this operation.
87 ///
88 /// This option can be used to retrieve the data of a specified version of the given path.
89 ///
90 /// If the version doesn't exist, an error with kind [`ErrorKind::NotFound`] will be returned.
91 pub version: Option<String>,
92
93 /// Set `if_match` for this operation.
94 ///
95 /// This option can be used to check if the file's `ETag` matches the given `ETag`.
96 ///
97 /// If file exists and it's etag doesn't match, an error with kind [`ErrorKind::ConditionNotMatch`]
98 /// will be returned.
99 pub if_match: Option<String>,
100 /// Set `if_none_match` for this operation.
101 ///
102 /// This option can be used to check if the file's `ETag` doesn't match the given `ETag`.
103 ///
104 /// If file exists and it's etag match, an error with kind [`ErrorKind::ConditionNotMatch`]
105 /// will be returned.
106 pub if_none_match: Option<String>,
107 /// Set `if_modified_since` for this operation.
108 ///
109 /// This option can be used to check if the file has been modified since the given timestamp.
110 ///
111 /// If file exists and it hasn't been modified since the specified time, an error with kind
112 /// [`ErrorKind::ConditionNotMatch`] will be returned.
113 pub if_modified_since: Option<Timestamp>,
114 /// Set `if_unmodified_since` for this operation.
115 ///
116 /// This feature can be used to check if the file hasn't been modified since the given timestamp.
117 ///
118 /// If file exists and it has been modified since the specified time, an error with kind
119 /// [`ErrorKind::ConditionNotMatch`] will be returned.
120 pub if_unmodified_since: Option<Timestamp>,
121
122 /// Set `concurrent` for the operation.
123 ///
124 /// OpenDAL by default to read file without concurrent. This is not efficient for cases when users
125 /// read large chunks of data. By setting `concurrent`, opendal will reading files concurrently
126 /// on support storage services.
127 ///
128 /// By setting `concurrent`, opendal will fetch chunks concurrently with
129 /// the give chunk size.
130 ///
131 /// Refer to [`crate::docs::performance`] for more details.
132 pub concurrent: usize,
133 /// Set `chunk` for the operation.
134 ///
135 /// OpenDAL will use services' preferred chunk size by default. Users can set chunk based on their own needs.
136 ///
137 /// Refer to [`crate::docs::performance`] for more details.
138 pub chunk: Option<usize>,
139 /// Controls the optimization strategy for range reads in [`Reader::fetch`].
140 ///
141 /// When performing range reads, if the gap between two requested ranges is smaller than
142 /// the configured `gap` size, OpenDAL will merge these ranges into a single read request
143 /// and discard the unrequested data in between. This helps reduce the number of API calls
144 /// to remote storage services.
145 ///
146 /// This optimization is particularly useful when performing multiple small range reads
147 /// that are close to each other, as it reduces the overhead of multiple network requests
148 /// at the cost of transferring some additional data.
149 ///
150 /// Refer to [`crate::docs::performance`] for more details.
151 pub gap: Option<usize>,
152
153 /// Specify the content-type header that should be sent back by the operation.
154 ///
155 /// This option is only meaningful when used along with presign.
156 pub override_content_type: Option<String>,
157 /// Specify the `cache-control` header that should be sent back by the operation.
158 ///
159 /// This option is only meaningful when used along with presign.
160 pub override_cache_control: Option<String>,
161 /// Specify the `content-disposition` header that should be sent back by the operation.
162 ///
163 /// This option is only meaningful when used along with presign.
164 pub override_content_disposition: Option<String>,
165}
166
167/// Options for reader operations.
168#[derive(Debug, Clone, Default, Eq, PartialEq)]
169pub struct ReaderOptions {
170 /// Set `version` for this operation.
171 ///
172 /// This option can be used to retrieve the data of a specified version of the given path.
173 ///
174 /// If the version doesn't exist, an error with kind [`ErrorKind::NotFound`] will be returned.
175 pub version: Option<String>,
176
177 /// Set `if_match` for this operation.
178 ///
179 /// This option can be used to check if the file's `ETag` matches the given `ETag`.
180 ///
181 /// If file exists and it's etag doesn't match, an error with kind [`ErrorKind::ConditionNotMatch`]
182 /// will be returned.
183 pub if_match: Option<String>,
184 /// Set `if_none_match` for this operation.
185 ///
186 /// This option can be used to check if the file's `ETag` doesn't match the given `ETag`.
187 ///
188 /// If file exists and it's etag match, an error with kind [`ErrorKind::ConditionNotMatch`]
189 /// will be returned.
190 pub if_none_match: Option<String>,
191 /// Set `if_modified_since` for this operation.
192 ///
193 /// This option can be used to check if the file has been modified since the given timestamp.
194 ///
195 /// If file exists and it hasn't been modified since the specified time, an error with kind
196 /// [`ErrorKind::ConditionNotMatch`] will be returned.
197 pub if_modified_since: Option<Timestamp>,
198 /// Set `if_unmodified_since` for this operation.
199 ///
200 /// This feature can be used to check if the file hasn't been modified since the given timestamp.
201 ///
202 /// If file exists and it has been modified since the specified time, an error with kind
203 /// [`ErrorKind::ConditionNotMatch`] will be returned.
204 pub if_unmodified_since: Option<Timestamp>,
205
206 /// Set `concurrent` for the operation.
207 ///
208 /// OpenDAL by default to read file without concurrent. This is not efficient for cases when users
209 /// read large chunks of data. By setting `concurrent`, opendal will reading files concurrently
210 /// on support storage services.
211 ///
212 /// By setting `concurrent`, opendal will fetch chunks concurrently with
213 /// the give chunk size.
214 ///
215 /// Refer to [`crate::docs::performance`] for more details.
216 pub concurrent: usize,
217 /// Set `chunk` for the operation.
218 ///
219 /// OpenDAL will use services' preferred chunk size by default. Users can set chunk based on their own needs.
220 ///
221 /// Refer to [`crate::docs::performance`] for more details.
222 pub chunk: Option<usize>,
223 /// Controls the optimization strategy for range reads in [`Reader::fetch`].
224 ///
225 /// When performing range reads, if the gap between two requested ranges is smaller than
226 /// the configured `gap` size, OpenDAL will merge these ranges into a single read request
227 /// and discard the unrequested data in between. This helps reduce the number of API calls
228 /// to remote storage services.
229 ///
230 /// This optimization is particularly useful when performing multiple small range reads
231 /// that are close to each other, as it reduces the overhead of multiple network requests
232 /// at the cost of transferring some additional data.
233 ///
234 /// Refer to [`crate::docs::performance`] for more details.
235 pub gap: Option<usize>,
236 /// Controls the number of prefetched bytes ranges that can be buffered in memory
237 /// during concurrent reading.
238 ///
239 /// When performing concurrent reads with `Reader`, this option limits how many
240 /// completed-but-not-yet-read chunks can be buffered. Once the number of buffered
241 /// chunks reaches this limit, no new read tasks will be spawned until some of the
242 /// buffered chunks are consumed.
243 ///
244 /// - Default value: 0 (no prefetching, strict back-pressure control)
245 /// - Set to a higher value to allow more aggressive prefetching at the cost of memory
246 ///
247 /// This option helps prevent memory exhaustion when reading large files with high
248 /// concurrency settings.
249 pub prefetch: usize,
250}
251
252/// Options for stat operations.
253#[derive(Debug, Clone, Default, Eq, PartialEq)]
254pub struct StatOptions {
255 /// Set `version` for this operation.
256 ///
257 /// This options can be used to retrieve the data of a specified version of the given path.
258 ///
259 /// If the version doesn't exist, an error with kind [`ErrorKind::NotFound`] will be returned.
260 pub version: Option<String>,
261
262 /// Set `if_match` for this operation.
263 ///
264 /// This option can be used to check if the file's `ETag` matches the given `ETag`.
265 ///
266 /// If file exists and it's etag doesn't match, an error with kind [`ErrorKind::ConditionNotMatch`]
267 /// will be returned.
268 pub if_match: Option<String>,
269 /// Set `if_none_match` for this operation.
270 ///
271 /// This option can be used to check if the file's `ETag` doesn't match the given `ETag`.
272 ///
273 /// If file exists and it's etag match, an error with kind [`ErrorKind::ConditionNotMatch`]
274 /// will be returned.
275 pub if_none_match: Option<String>,
276 /// Set `if_modified_since` for this operation.
277 ///
278 /// This option can be used to check if the file has been modified since the given timestamp.
279 ///
280 /// If file exists and it hasn't been modified since the specified time, an error with kind
281 /// [`ErrorKind::ConditionNotMatch`] will be returned.
282 pub if_modified_since: Option<Timestamp>,
283 /// Set `if_unmodified_since` for this operation.
284 ///
285 /// This feature can be used to check if the file hasn't been modified since the given timestamp.
286 ///
287 /// If file exists and it has been modified since the specified time, an error with kind
288 /// [`ErrorKind::ConditionNotMatch`] will be returned.
289 pub if_unmodified_since: Option<Timestamp>,
290
291 /// Specify the content-type header that should be sent back by the operation.
292 ///
293 /// This option is only meaningful when used along with presign.
294 pub override_content_type: Option<String>,
295 /// Specify the `cache-control` header that should be sent back by the operation.
296 ///
297 /// This option is only meaningful when used along with presign.
298 pub override_cache_control: Option<String>,
299 /// Specify the `content-disposition` header that should be sent back by the operation.
300 ///
301 /// This option is only meaningful when used along with presign.
302 pub override_content_disposition: Option<String>,
303}
304
305/// Options for write operations.
306#[derive(Debug, Clone, Default, Eq, PartialEq)]
307pub struct WriteOptions {
308 /// Sets append mode for this operation.
309 ///
310 /// ### Capability
311 ///
312 /// Check [`Capability::write_can_append`] before using this option.
313 ///
314 /// ### Behavior
315 ///
316 /// - By default, write operations overwrite existing files
317 /// - When append is set to true:
318 /// - New data will be appended to the end of existing file
319 /// - If file doesn't exist, it will be created
320 /// - If not supported, will return an error
321 ///
322 /// This operation allows adding data to existing files instead of overwriting them.
323 pub append: bool,
324
325 /// Sets Cache-Control header for this write operation.
326 ///
327 /// ### Capability
328 ///
329 /// Check [`Capability::write_with_cache_control`] before using this feature.
330 ///
331 /// ### Behavior
332 ///
333 /// - If supported, sets Cache-Control as system metadata on the target file
334 /// - The value should follow HTTP Cache-Control header format
335 /// - If not supported, the value will be ignored
336 ///
337 /// This operation allows controlling caching behavior for the written content.
338 ///
339 /// ### Use Cases
340 ///
341 /// - Setting browser cache duration
342 /// - Configuring CDN behavior
343 /// - Optimizing content delivery
344 /// - Managing cache invalidation
345 ///
346 /// ### References
347 ///
348 /// - [MDN Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control)
349 /// - [RFC 7234 Section 5.2](https://tools.ietf.org/html/rfc7234#section-5.2)
350 pub cache_control: Option<String>,
351 /// Sets `Content-Type` header for this write operation.
352 ///
353 /// ## Capability
354 ///
355 /// Check [`Capability::write_with_content_type`] before using this feature.
356 ///
357 /// ### Behavior
358 ///
359 /// - If supported, sets Content-Type as system metadata on the target file
360 /// - The value should follow MIME type format (e.g. "text/plain", "image/jpeg")
361 /// - If not supported, the value will be ignored
362 ///
363 /// This operation allows specifying the media type of the content being written.
364 pub content_type: Option<String>,
365 /// Sets Content-Disposition header for this write request.
366 ///
367 /// ### Capability
368 ///
369 /// Check [`Capability::write_with_content_disposition`] before using this feature.
370 ///
371 /// ### Behavior
372 ///
373 /// - If supported, sets Content-Disposition as system metadata on the target file
374 /// - The value should follow HTTP Content-Disposition header format
375 /// - Common values include:
376 /// - `inline` - Content displayed within browser
377 /// - `attachment` - Content downloaded as file
378 /// - `attachment; filename="example.jpg"` - Downloaded with specified filename
379 /// - If not supported, the value will be ignored
380 ///
381 /// This operation allows controlling how the content should be displayed or downloaded.
382 pub content_disposition: Option<String>,
383 /// Sets Content-Encoding header for this write request.
384 ///
385 /// ### Capability
386 ///
387 /// Check [`Capability::write_with_content_encoding`] before using this feature.
388 ///
389 /// ### Behavior
390 ///
391 /// - If supported, sets Content-Encoding as system metadata on the target file
392 /// - The value should follow HTTP Content-Encoding header format
393 /// - Common values include:
394 /// - `gzip` - Content encoded using gzip compression
395 /// - `deflate` - Content encoded using deflate compression
396 /// - `br` - Content encoded using Brotli compression
397 /// - `identity` - No encoding applied (default value)
398 /// - If not supported, the value will be ignored
399 ///
400 /// This operation allows specifying the encoding applied to the content being written.
401 pub content_encoding: Option<String>,
402 /// Sets user metadata for this write request.
403 ///
404 /// ### Capability
405 ///
406 /// Check [`Capability::write_with_user_metadata`] before using this feature.
407 ///
408 /// ### Behavior
409 ///
410 /// - If supported, the user metadata will be attached to the object during write
411 /// - Accepts key-value pairs where both key and value are strings
412 /// - Keys are case-insensitive in most services
413 /// - Services may have limitations for user metadata, for example:
414 /// - Key length is typically limited (e.g., 1024 bytes)
415 /// - Value length is typically limited (e.g., 4096 bytes)
416 /// - Total metadata size might be limited
417 /// - Some characters might be forbidden in keys
418 /// - If not supported, the metadata will be ignored
419 ///
420 /// User metadata provides a way to attach custom metadata to objects during write operations.
421 /// This metadata can be retrieved later when reading the object.
422 pub user_metadata: Option<HashMap<String, String>>,
423
424 /// Sets If-Match header for this write request.
425 ///
426 /// ### Capability
427 ///
428 /// Check [`Capability::write_with_if_match`] before using this feature.
429 ///
430 /// ### Behavior
431 ///
432 /// - If supported, the write operation will only succeed if the target's ETag matches the specified value
433 /// - The value should be a valid ETag string
434 /// - Common values include:
435 /// - A specific ETag value like `"686897696a7c876b7e"`
436 /// - `*` - Matches any existing resource
437 /// - If not supported, the value will be ignored
438 ///
439 /// This operation provides conditional write functionality based on ETag matching,
440 /// helping prevent unintended overwrites in concurrent scenarios.
441 pub if_match: Option<String>,
442 /// Sets If-None-Match header for this write request.
443 ///
444 /// Note: Certain services, like `s3`, support `if_not_exists` but not `if_none_match`.
445 /// Use `if_not_exists` if you only want to check whether a file exists.
446 ///
447 /// ### Capability
448 ///
449 /// Check [`Capability::write_with_if_none_match`] before using this feature.
450 ///
451 /// ### Behavior
452 ///
453 /// - If supported, the write operation will only succeed if the target's ETag does not match the specified value
454 /// - The value should be a valid ETag string
455 /// - Common values include:
456 /// - A specific ETag value like `"686897696a7c876b7e"`
457 /// - `*` - Matches if the resource does not exist
458 /// - If not supported, the value will be ignored
459 ///
460 /// This operation provides conditional write functionality based on ETag non-matching,
461 /// useful for preventing overwriting existing resources or ensuring unique writes.
462 pub if_none_match: Option<String>,
463 /// Sets the condition that write operation will succeed only if target does not exist.
464 ///
465 /// ### Capability
466 ///
467 /// Check [`Capability::write_with_if_not_exists`] before using this feature.
468 ///
469 /// ### Behavior
470 ///
471 /// - If supported, the write operation will only succeed if the target path does not exist
472 /// - Will return error if target already exists
473 /// - If not supported, the value will be ignored
474 ///
475 /// This operation provides a way to ensure write operations only create new resources
476 /// without overwriting existing ones, useful for implementing "create if not exists" logic.
477 pub if_not_exists: bool,
478
479 /// Sets concurrent write operations for this writer.
480 ///
481 /// ## Behavior
482 ///
483 /// - By default, OpenDAL writes files sequentially
484 /// - When concurrent is set:
485 /// - Multiple write operations can execute in parallel
486 /// - Write operations return immediately without waiting if tasks space are available
487 /// - Close operation ensures all writes complete in order
488 /// - Memory usage increases with concurrency level
489 /// - If not supported, falls back to sequential writes
490 ///
491 /// This feature significantly improves performance when:
492 /// - Writing large files
493 /// - Network latency is high
494 /// - Storage service supports concurrent uploads like multipart uploads
495 ///
496 /// ## Performance Impact
497 ///
498 /// Setting appropriate concurrency can:
499 /// - Increase write throughput
500 /// - Reduce total write time
501 /// - Better utilize available bandwidth
502 /// - Trade memory for performance
503 pub concurrent: usize,
504 /// Sets chunk size for buffered writes.
505 ///
506 /// ### Capability
507 ///
508 /// Check [`Capability::write_multi_min_size`] and [`Capability::write_multi_max_size`] for size limits.
509 ///
510 /// ### Behavior
511 ///
512 /// - By default, OpenDAL sets optimal chunk size based on service capabilities
513 /// - When chunk size is set:
514 /// - Data will be buffered until reaching chunk size
515 /// - One API call will be made per chunk
516 /// - Last chunk may be smaller than chunk size
517 /// - Important considerations:
518 /// - Some services require minimum chunk sizes (e.g. S3's EntityTooSmall error)
519 /// - Smaller chunks increase API calls and costs
520 /// - Larger chunks increase memory usage, but improve performance and reduce costs
521 ///
522 /// ### Performance Impact
523 ///
524 /// Setting appropriate chunk size can:
525 /// - Reduce number of API calls
526 /// - Improve overall throughput
527 /// - Lower operation costs
528 /// - Better utilize network bandwidth
529 pub chunk: Option<usize>,
530}
531
532/// Options for copy operations.
533#[derive(Debug, Clone, Default, Eq, PartialEq)]
534pub struct CopyOptions {
535 /// Sets the condition that copy operation will succeed only if target does not exist.
536 ///
537 /// ### Capability
538 ///
539 /// Check [`Capability::copy_with_if_not_exists`] before using this feature.
540 ///
541 /// ### Behavior
542 ///
543 /// - If supported, the copy operation will only succeed if the target path does not exist
544 /// - Will return error if target already exists
545 /// - If not supported, the value will be ignored
546 ///
547 /// This operation provides a way to ensure copy operations only create new resources
548 /// without overwriting existing ones, useful for implementing "copy if not exists" logic.
549 pub if_not_exists: bool,
550}