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