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