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