Skip to main content

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