opendal/raw/
ops.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//! Ops provides the operation args struct like [`OpRead`] for user.
19//!
20//! By using ops, users can add more context for operation.
21
22use std::collections::HashMap;
23use std::time::Duration;
24
25use chrono::DateTime;
26use chrono::Utc;
27
28use crate::options;
29use crate::raw::*;
30
31/// Args for `create` operation.
32///
33/// The path must be normalized.
34#[derive(Debug, Clone, Default)]
35pub struct OpCreateDir {}
36
37impl OpCreateDir {
38    /// Create a new `OpCreateDir`.
39    pub fn new() -> Self {
40        Self::default()
41    }
42}
43
44/// Args for `delete` operation.
45///
46/// The path must be normalized.
47#[derive(Debug, Clone, Default, Eq, Hash, PartialEq)]
48pub struct OpDelete {
49    version: Option<String>,
50}
51
52impl OpDelete {
53    /// Create a new `OpDelete`.
54    pub fn new() -> Self {
55        Self::default()
56    }
57}
58
59impl OpDelete {
60    /// Change the version of this delete operation.
61    pub fn with_version(mut self, version: &str) -> Self {
62        self.version = Some(version.into());
63        self
64    }
65
66    /// Get the version of this delete operation.
67    pub fn version(&self) -> Option<&str> {
68        self.version.as_deref()
69    }
70}
71
72impl From<options::DeleteOptions> for OpDelete {
73    fn from(value: options::DeleteOptions) -> Self {
74        Self {
75            version: value.version,
76        }
77    }
78}
79
80/// Args for `delete` operation.
81///
82/// The path must be normalized.
83#[derive(Debug, Clone, Default)]
84pub struct OpDeleter {}
85
86impl OpDeleter {
87    /// Create a new `OpDelete`.
88    pub fn new() -> Self {
89        Self::default()
90    }
91}
92
93/// Args for `list` operation.
94#[derive(Debug, Clone, Default)]
95pub struct OpList {
96    /// The limit passed to underlying service to specify the max results
97    /// that could return per-request.
98    ///
99    /// Users could use this to control the memory usage of list operation.
100    limit: Option<usize>,
101    /// The start_after passes to underlying service to specify the specified key
102    /// to start listing from.
103    start_after: Option<String>,
104    /// The recursive is used to control whether the list operation is recursive.
105    ///
106    /// - If `false`, list operation will only list the entries under the given path.
107    /// - If `true`, list operation will list all entries that starts with given path.
108    ///
109    /// Default to `false`.
110    recursive: bool,
111    /// The version is used to control whether the object versions should be returned.
112    ///
113    /// - If `false`, list operation will not return with object versions
114    /// - If `true`, list operation will return with object versions if object versioning is supported
115    ///   by the underlying service
116    ///
117    /// Default to `false`
118    versions: bool,
119    /// The deleted is used to control whether the deleted objects should be returned.
120    ///
121    /// - If `false`, list operation will not return with deleted objects
122    /// - If `true`, list operation will return with deleted objects if object versioning is supported
123    ///   by the underlying service
124    ///
125    /// Default to `false`
126    deleted: bool,
127}
128
129impl OpList {
130    /// Create a new `OpList`.
131    pub fn new() -> Self {
132        Self::default()
133    }
134
135    /// Change the limit of this list operation.
136    pub fn with_limit(mut self, limit: usize) -> Self {
137        self.limit = Some(limit);
138        self
139    }
140
141    /// Get the limit of list operation.
142    pub fn limit(&self) -> Option<usize> {
143        self.limit
144    }
145
146    /// Change the start_after of this list operation.
147    pub fn with_start_after(mut self, start_after: &str) -> Self {
148        self.start_after = Some(start_after.into());
149        self
150    }
151
152    /// Get the start_after of list operation.
153    pub fn start_after(&self) -> Option<&str> {
154        self.start_after.as_deref()
155    }
156
157    /// The recursive is used to control whether the list operation is recursive.
158    ///
159    /// - If `false`, list operation will only list the entries under the given path.
160    /// - If `true`, list operation will list all entries that starts with given path.
161    ///
162    /// Default to `false`.
163    pub fn with_recursive(mut self, recursive: bool) -> Self {
164        self.recursive = recursive;
165        self
166    }
167
168    /// Get the current recursive.
169    pub fn recursive(&self) -> bool {
170        self.recursive
171    }
172
173    /// Change the concurrent of this list operation.
174    ///
175    /// The default concurrent is 1.
176    #[deprecated(since = "0.53.2", note = "concurrent in list is no-op")]
177    pub fn with_concurrent(self, concurrent: usize) -> Self {
178        let _ = concurrent;
179        self
180    }
181
182    /// Get the concurrent of list operation.
183    #[deprecated(since = "0.53.2", note = "concurrent in list is no-op")]
184    pub fn concurrent(&self) -> usize {
185        0
186    }
187
188    /// Change the version of this list operation
189    #[deprecated(since = "0.51.1", note = "use with_versions instead")]
190    pub fn with_version(mut self, version: bool) -> Self {
191        self.versions = version;
192        self
193    }
194
195    /// Change the version of this list operation
196    pub fn with_versions(mut self, versions: bool) -> Self {
197        self.versions = versions;
198        self
199    }
200
201    /// Get the version of this list operation
202    #[deprecated(since = "0.51.1", note = "use versions instead")]
203    pub fn version(&self) -> bool {
204        self.versions
205    }
206
207    /// Get the version of this list operation
208    pub fn versions(&self) -> bool {
209        self.versions
210    }
211
212    /// Change the deleted of this list operation
213    pub fn with_deleted(mut self, deleted: bool) -> Self {
214        self.deleted = deleted;
215        self
216    }
217
218    /// Get the deleted of this list operation
219    pub fn deleted(&self) -> bool {
220        self.deleted
221    }
222}
223
224impl From<options::ListOptions> for OpList {
225    fn from(value: options::ListOptions) -> Self {
226        Self {
227            limit: value.limit,
228            start_after: value.start_after,
229            recursive: value.recursive,
230            versions: value.versions,
231            deleted: value.deleted,
232        }
233    }
234}
235
236/// Args for `presign` operation.
237///
238/// The path must be normalized.
239#[derive(Debug, Clone)]
240pub struct OpPresign {
241    expire: Duration,
242
243    op: PresignOperation,
244}
245
246impl OpPresign {
247    /// Create a new `OpPresign`.
248    pub fn new(op: impl Into<PresignOperation>, expire: Duration) -> Self {
249        Self {
250            op: op.into(),
251            expire,
252        }
253    }
254
255    /// Get operation from op.
256    pub fn operation(&self) -> &PresignOperation {
257        &self.op
258    }
259
260    /// Get expire from op.
261    pub fn expire(&self) -> Duration {
262        self.expire
263    }
264
265    /// Consume OpPresign into (Duration, PresignOperation)
266    pub fn into_parts(self) -> (Duration, PresignOperation) {
267        (self.expire, self.op)
268    }
269}
270
271/// Presign operation used for presign.
272#[derive(Debug, Clone)]
273#[non_exhaustive]
274pub enum PresignOperation {
275    /// Presign a stat(head) operation.
276    Stat(OpStat),
277    /// Presign a read operation.
278    Read(OpRead),
279    /// Presign a write operation.
280    Write(OpWrite),
281    /// Presign a delete operation.
282    Delete(OpDelete),
283}
284
285impl From<OpStat> for PresignOperation {
286    fn from(op: OpStat) -> Self {
287        Self::Stat(op)
288    }
289}
290
291impl From<OpRead> for PresignOperation {
292    fn from(v: OpRead) -> Self {
293        Self::Read(v)
294    }
295}
296
297impl From<OpWrite> for PresignOperation {
298    fn from(v: OpWrite) -> Self {
299        Self::Write(v)
300    }
301}
302
303impl From<OpDelete> for PresignOperation {
304    fn from(v: OpDelete) -> Self {
305        Self::Delete(v)
306    }
307}
308
309/// Args for `read` operation.
310#[derive(Debug, Clone, Default)]
311pub struct OpRead {
312    range: BytesRange,
313    if_match: Option<String>,
314    if_none_match: Option<String>,
315    if_modified_since: Option<DateTime<Utc>>,
316    if_unmodified_since: Option<DateTime<Utc>>,
317    override_content_type: Option<String>,
318    override_cache_control: Option<String>,
319    override_content_disposition: Option<String>,
320    version: Option<String>,
321}
322
323impl OpRead {
324    /// Create a default `OpRead` which will read whole content of path.
325    pub fn new() -> Self {
326        Self::default()
327    }
328
329    /// Set the range of the option
330    pub fn with_range(mut self, range: BytesRange) -> Self {
331        self.range = range;
332        self
333    }
334
335    /// Get range from option
336    pub fn range(&self) -> BytesRange {
337        self.range
338    }
339
340    /// Returns a mutable range to allow updating.
341    pub(crate) fn range_mut(&mut self) -> &mut BytesRange {
342        &mut self.range
343    }
344
345    /// Sets the content-disposition header that should be sent back by the remote read operation.
346    pub fn with_override_content_disposition(mut self, content_disposition: &str) -> Self {
347        self.override_content_disposition = Some(content_disposition.into());
348        self
349    }
350
351    /// Returns the content-disposition header that should be sent back by the remote read
352    /// operation.
353    pub fn override_content_disposition(&self) -> Option<&str> {
354        self.override_content_disposition.as_deref()
355    }
356
357    /// Sets the cache-control header that should be sent back by the remote read operation.
358    pub fn with_override_cache_control(mut self, cache_control: &str) -> Self {
359        self.override_cache_control = Some(cache_control.into());
360        self
361    }
362
363    /// Returns the cache-control header that should be sent back by the remote read operation.
364    pub fn override_cache_control(&self) -> Option<&str> {
365        self.override_cache_control.as_deref()
366    }
367
368    /// Sets the content-type header that should be sent back by the remote read operation.
369    pub fn with_override_content_type(mut self, content_type: &str) -> Self {
370        self.override_content_type = Some(content_type.into());
371        self
372    }
373
374    /// Returns the content-type header that should be sent back by the remote read operation.
375    pub fn override_content_type(&self) -> Option<&str> {
376        self.override_content_type.as_deref()
377    }
378
379    /// Set the If-Match of the option
380    pub fn with_if_match(mut self, if_match: &str) -> Self {
381        self.if_match = Some(if_match.to_string());
382        self
383    }
384
385    /// Get If-Match from option
386    pub fn if_match(&self) -> Option<&str> {
387        self.if_match.as_deref()
388    }
389
390    /// Set the If-None-Match of the option
391    pub fn with_if_none_match(mut self, if_none_match: &str) -> Self {
392        self.if_none_match = Some(if_none_match.to_string());
393        self
394    }
395
396    /// Get If-None-Match from option
397    pub fn if_none_match(&self) -> Option<&str> {
398        self.if_none_match.as_deref()
399    }
400
401    /// Set the If-Modified-Since of the option
402    pub fn with_if_modified_since(mut self, v: DateTime<Utc>) -> Self {
403        self.if_modified_since = Some(v);
404        self
405    }
406
407    /// Get If-Modified-Since from option
408    pub fn if_modified_since(&self) -> Option<DateTime<Utc>> {
409        self.if_modified_since
410    }
411
412    /// Set the If-Unmodified-Since of the option
413    pub fn with_if_unmodified_since(mut self, v: DateTime<Utc>) -> Self {
414        self.if_unmodified_since = Some(v);
415        self
416    }
417
418    /// Get If-Unmodified-Since from option
419    pub fn if_unmodified_since(&self) -> Option<DateTime<Utc>> {
420        self.if_unmodified_since
421    }
422
423    /// Set the version of the option
424    pub fn with_version(mut self, version: &str) -> Self {
425        self.version = Some(version.to_string());
426        self
427    }
428
429    /// Get version from option
430    pub fn version(&self) -> Option<&str> {
431        self.version.as_deref()
432    }
433}
434
435/// Args for reader operation.
436#[derive(Debug, Clone)]
437pub struct OpReader {
438    /// The concurrent requests that reader can send.
439    concurrent: usize,
440    /// The chunk size of each request.
441    chunk: Option<usize>,
442    /// The gap size of each request.
443    gap: Option<usize>,
444    /// The maximum number of buffers that can be prefetched.
445    prefetch: usize,
446}
447
448impl Default for OpReader {
449    fn default() -> Self {
450        Self {
451            concurrent: 1,
452            chunk: None,
453            gap: None,
454            prefetch: 0,
455        }
456    }
457}
458
459impl OpReader {
460    /// Create a new `OpReader`.
461    pub fn new() -> Self {
462        Self::default()
463    }
464
465    /// Set the concurrent of the option
466    pub fn with_concurrent(mut self, concurrent: usize) -> Self {
467        self.concurrent = concurrent.max(1);
468        self
469    }
470
471    /// Get concurrent from option
472    pub fn concurrent(&self) -> usize {
473        self.concurrent
474    }
475
476    /// Set the chunk of the option
477    pub fn with_chunk(mut self, chunk: usize) -> Self {
478        self.chunk = Some(chunk.max(1));
479        self
480    }
481
482    /// Get chunk from option
483    pub fn chunk(&self) -> Option<usize> {
484        self.chunk
485    }
486
487    /// Set the gap of the option
488    pub fn with_gap(mut self, gap: usize) -> Self {
489        self.gap = Some(gap.max(1));
490        self
491    }
492
493    /// Get gap from option
494    pub fn gap(&self) -> Option<usize> {
495        self.gap
496    }
497
498    /// Set the prefetch of the option
499    pub fn with_prefetch(mut self, prefetch: usize) -> Self {
500        self.prefetch = prefetch;
501        self
502    }
503
504    /// Get prefetch from option
505    pub fn prefetch(&self) -> usize {
506        self.prefetch
507    }
508}
509
510impl From<options::ReadOptions> for (OpRead, OpReader) {
511    fn from(value: options::ReadOptions) -> Self {
512        (
513            OpRead {
514                range: value.range,
515                if_match: value.if_match,
516                if_none_match: value.if_none_match,
517                if_modified_since: value.if_modified_since,
518                if_unmodified_since: value.if_unmodified_since,
519                override_content_type: value.override_content_type,
520                override_cache_control: value.override_cache_control,
521                override_content_disposition: value.override_content_disposition,
522                version: value.version,
523            },
524            OpReader {
525                // Ensure concurrent is at least 1
526                concurrent: value.concurrent.max(1),
527                chunk: value.chunk,
528                gap: value.gap,
529                prefetch: 0,
530            },
531        )
532    }
533}
534
535impl From<options::ReaderOptions> for (OpRead, OpReader) {
536    fn from(value: options::ReaderOptions) -> Self {
537        (
538            OpRead {
539                range: BytesRange::default(),
540                if_match: value.if_match,
541                if_none_match: value.if_none_match,
542                if_modified_since: value.if_modified_since,
543                if_unmodified_since: value.if_unmodified_since,
544                override_content_type: None,
545                override_cache_control: None,
546                override_content_disposition: None,
547                version: value.version,
548            },
549            OpReader {
550                // Ensure concurrent is at least 1
551                concurrent: value.concurrent.max(1),
552                chunk: value.chunk,
553                gap: value.gap,
554                prefetch: value.prefetch,
555            },
556        )
557    }
558}
559
560/// Args for `stat` operation.
561#[derive(Debug, Clone, Default)]
562pub struct OpStat {
563    if_match: Option<String>,
564    if_none_match: Option<String>,
565    if_modified_since: Option<DateTime<Utc>>,
566    if_unmodified_since: Option<DateTime<Utc>>,
567    override_content_type: Option<String>,
568    override_cache_control: Option<String>,
569    override_content_disposition: Option<String>,
570    version: Option<String>,
571}
572
573impl OpStat {
574    /// Create a new `OpStat`.
575    pub fn new() -> Self {
576        Self::default()
577    }
578
579    /// Set the If-Match of the option
580    pub fn with_if_match(mut self, if_match: &str) -> Self {
581        self.if_match = Some(if_match.to_string());
582        self
583    }
584
585    /// Get If-Match from option
586    pub fn if_match(&self) -> Option<&str> {
587        self.if_match.as_deref()
588    }
589
590    /// Set the If-None-Match of the option
591    pub fn with_if_none_match(mut self, if_none_match: &str) -> Self {
592        self.if_none_match = Some(if_none_match.to_string());
593        self
594    }
595
596    /// Get If-None-Match from option
597    pub fn if_none_match(&self) -> Option<&str> {
598        self.if_none_match.as_deref()
599    }
600
601    /// Set the If-Modified-Since of the option
602    pub fn with_if_modified_since(mut self, v: DateTime<Utc>) -> Self {
603        self.if_modified_since = Some(v);
604        self
605    }
606
607    /// Get If-Modified-Since from option
608    pub fn if_modified_since(&self) -> Option<DateTime<Utc>> {
609        self.if_modified_since
610    }
611
612    /// Set the If-Unmodified-Since of the option
613    pub fn with_if_unmodified_since(mut self, v: DateTime<Utc>) -> Self {
614        self.if_unmodified_since = Some(v);
615        self
616    }
617
618    /// Get If-Unmodified-Since from option
619    pub fn if_unmodified_since(&self) -> Option<DateTime<Utc>> {
620        self.if_unmodified_since
621    }
622
623    /// Sets the content-disposition header that should be sent back by the remote read operation.
624    pub fn with_override_content_disposition(mut self, content_disposition: &str) -> Self {
625        self.override_content_disposition = Some(content_disposition.into());
626        self
627    }
628
629    /// Returns the content-disposition header that should be sent back by the remote read
630    /// operation.
631    pub fn override_content_disposition(&self) -> Option<&str> {
632        self.override_content_disposition.as_deref()
633    }
634
635    /// Sets the cache-control header that should be sent back by the remote read operation.
636    pub fn with_override_cache_control(mut self, cache_control: &str) -> Self {
637        self.override_cache_control = Some(cache_control.into());
638        self
639    }
640
641    /// Returns the cache-control header that should be sent back by the remote read operation.
642    pub fn override_cache_control(&self) -> Option<&str> {
643        self.override_cache_control.as_deref()
644    }
645
646    /// Sets the content-type header that should be sent back by the remote read operation.
647    pub fn with_override_content_type(mut self, content_type: &str) -> Self {
648        self.override_content_type = Some(content_type.into());
649        self
650    }
651
652    /// Returns the content-type header that should be sent back by the remote read operation.
653    pub fn override_content_type(&self) -> Option<&str> {
654        self.override_content_type.as_deref()
655    }
656
657    /// Set the version of the option
658    pub fn with_version(mut self, version: &str) -> Self {
659        self.version = Some(version.to_string());
660        self
661    }
662
663    /// Get version from option
664    pub fn version(&self) -> Option<&str> {
665        self.version.as_deref()
666    }
667}
668
669impl From<options::StatOptions> for OpStat {
670    fn from(value: options::StatOptions) -> Self {
671        Self {
672            if_match: value.if_match,
673            if_none_match: value.if_none_match,
674            if_modified_since: value.if_modified_since,
675            if_unmodified_since: value.if_unmodified_since,
676            override_content_type: value.override_content_type,
677            override_cache_control: value.override_cache_control,
678            override_content_disposition: value.override_content_disposition,
679            version: value.version,
680        }
681    }
682}
683
684/// Args for `write` operation.
685#[derive(Debug, Clone, Default)]
686pub struct OpWrite {
687    append: bool,
688    concurrent: usize,
689    content_type: Option<String>,
690    content_disposition: Option<String>,
691    content_encoding: Option<String>,
692    cache_control: Option<String>,
693    if_match: Option<String>,
694    if_none_match: Option<String>,
695    if_not_exists: bool,
696    user_metadata: Option<HashMap<String, String>>,
697}
698
699impl OpWrite {
700    /// Create a new `OpWrite`.
701    ///
702    /// If input path is not a file path, an error will be returned.
703    pub fn new() -> Self {
704        Self::default()
705    }
706
707    /// Get the append from op.
708    ///
709    /// The append is the flag to indicate that this write operation is an append operation.
710    pub fn append(&self) -> bool {
711        self.append
712    }
713
714    /// Set the append mode of op.
715    ///
716    /// If the append mode is set, the data will be appended to the end of the file.
717    ///
718    /// # Notes
719    ///
720    /// Service could return `Unsupported` if the underlying storage does not support append.
721    pub fn with_append(mut self, append: bool) -> Self {
722        self.append = append;
723        self
724    }
725
726    /// Get the content type from option
727    pub fn content_type(&self) -> Option<&str> {
728        self.content_type.as_deref()
729    }
730
731    /// Set the content type of option
732    pub fn with_content_type(mut self, content_type: &str) -> Self {
733        self.content_type = Some(content_type.to_string());
734        self
735    }
736
737    /// Get the content disposition from option
738    pub fn content_disposition(&self) -> Option<&str> {
739        self.content_disposition.as_deref()
740    }
741
742    /// Set the content disposition of option
743    pub fn with_content_disposition(mut self, content_disposition: &str) -> Self {
744        self.content_disposition = Some(content_disposition.to_string());
745        self
746    }
747
748    /// Get the content encoding from option
749    pub fn content_encoding(&self) -> Option<&str> {
750        self.content_encoding.as_deref()
751    }
752
753    /// Set the content encoding of option
754    pub fn with_content_encoding(mut self, content_encoding: &str) -> Self {
755        self.content_encoding = Some(content_encoding.to_string());
756        self
757    }
758
759    /// Get the cache control from option
760    pub fn cache_control(&self) -> Option<&str> {
761        self.cache_control.as_deref()
762    }
763
764    /// Set the content type of option
765    pub fn with_cache_control(mut self, cache_control: &str) -> Self {
766        self.cache_control = Some(cache_control.to_string());
767        self
768    }
769
770    /// Get the concurrent.
771    pub fn concurrent(&self) -> usize {
772        self.concurrent
773    }
774
775    /// Set the maximum concurrent write task amount.
776    pub fn with_concurrent(mut self, concurrent: usize) -> Self {
777        self.concurrent = concurrent;
778        self
779    }
780
781    /// Set the If-Match of the option
782    pub fn with_if_match(mut self, s: &str) -> Self {
783        self.if_match = Some(s.to_string());
784        self
785    }
786
787    /// Get If-Match from option
788    pub fn if_match(&self) -> Option<&str> {
789        self.if_match.as_deref()
790    }
791
792    /// Set the If-None-Match of the option
793    pub fn with_if_none_match(mut self, s: &str) -> Self {
794        self.if_none_match = Some(s.to_string());
795        self
796    }
797
798    /// Get If-None-Match from option
799    pub fn if_none_match(&self) -> Option<&str> {
800        self.if_none_match.as_deref()
801    }
802
803    /// Set the If-Not-Exist of the option
804    pub fn with_if_not_exists(mut self, b: bool) -> Self {
805        self.if_not_exists = b;
806        self
807    }
808
809    /// Get If-Not-Exist from option
810    pub fn if_not_exists(&self) -> bool {
811        self.if_not_exists
812    }
813
814    /// Set the user defined metadata of the op
815    pub fn with_user_metadata(mut self, metadata: HashMap<String, String>) -> Self {
816        self.user_metadata = Some(metadata);
817        self
818    }
819
820    /// Get the user defined metadata from the op
821    pub fn user_metadata(&self) -> Option<&HashMap<String, String>> {
822        self.user_metadata.as_ref()
823    }
824}
825
826/// Args for `writer` operation.
827#[derive(Debug, Clone, Default)]
828pub struct OpWriter {
829    chunk: Option<usize>,
830}
831
832impl OpWriter {
833    /// Create a new `OpWriter`.
834    pub fn new() -> Self {
835        Self::default()
836    }
837
838    /// Get the chunk from op.
839    ///
840    /// The chunk is used by service to decide the chunk size of the underlying writer.
841    pub fn chunk(&self) -> Option<usize> {
842        self.chunk
843    }
844
845    /// Set the chunk of op.
846    ///
847    /// If chunk is set, the data will be chunked by the underlying writer.
848    ///
849    /// ## NOTE
850    ///
851    /// Service could have their own minimum chunk size while perform write
852    /// operations like multipart uploads. So the chunk size may be larger than
853    /// the given buffer size.
854    pub fn with_chunk(mut self, chunk: usize) -> Self {
855        self.chunk = Some(chunk);
856        self
857    }
858}
859
860impl From<options::WriteOptions> for (OpWrite, OpWriter) {
861    fn from(value: options::WriteOptions) -> Self {
862        (
863            OpWrite {
864                append: value.append,
865                // Ensure concurrent is at least 1
866                concurrent: value.concurrent.max(1),
867                content_type: value.content_type,
868                content_disposition: value.content_disposition,
869                content_encoding: value.content_encoding,
870                cache_control: value.cache_control,
871                if_match: value.if_match,
872                if_none_match: value.if_none_match,
873                if_not_exists: value.if_not_exists,
874                user_metadata: value.user_metadata,
875            },
876            OpWriter { chunk: value.chunk },
877        )
878    }
879}
880
881/// Args for `copy` operation.
882#[derive(Debug, Clone, Default)]
883pub struct OpCopy {
884    if_not_exists: bool,
885}
886
887impl OpCopy {
888    /// Create a new `OpCopy`.
889    pub fn new() -> Self {
890        Self::default()
891    }
892
893    /// Set the if_not_exists flag for the operation.
894    ///
895    /// When set to true, the copy operation will only proceed if the destination
896    /// doesn't already exist.
897    pub fn with_if_not_exists(mut self, if_not_exists: bool) -> Self {
898        self.if_not_exists = if_not_exists;
899        self
900    }
901
902    /// Get if_not_exists flag.
903    pub fn if_not_exists(&self) -> bool {
904        self.if_not_exists
905    }
906}
907
908/// Args for `rename` operation.
909#[derive(Debug, Clone, Default)]
910pub struct OpRename {}
911
912impl OpRename {
913    /// Create a new `OpMove`.
914    pub fn new() -> Self {
915        Self::default()
916    }
917}