Skip to main content

minio_rsc/client/
args.rs

1use std::collections::HashMap;
2
3use http::{
4    header::{HeaderName, IntoHeaderName},
5    HeaderMap,
6};
7
8use crate::{
9    datatype::{
10        FromXml, InitiateMultipartUploadResult, ObjectLockConfiguration, RetentionMode, Tagging,
11        ToXml,
12    },
13    error::Result,
14    sse::{Sse, SseCustomerKey},
15    time::UtcTime,
16    utils::urlencode,
17};
18
19use super::QueryMap;
20
21/// Custom request parameters for bucket operations.
22/// ## parmas
23/// - `bucket_name`: The bucket name.
24/// - `region`: *Optional*, The bucket region.
25/// - `expected_bucket_owner`: *Optional*, The account ID of the expected bucket owner.
26/// - `extra_headers`: *Optional*, Extra headers for advanced usage.
27///
28/// **Note**: Some parameters are only valid in specific methods
29#[derive(Debug, Clone)]
30pub struct BucketArgs {
31    pub(crate) name: String,
32    pub(crate) region: Option<String>,
33    pub(crate) expected_bucket_owner: Option<String>,
34    pub(crate) extra_headers: Option<HeaderMap>,
35}
36
37impl BucketArgs {
38    pub fn new<S: Into<String>>(bucket_name: S) -> Self {
39        Self {
40            name: bucket_name.into(),
41            region: None,
42            expected_bucket_owner: None,
43            extra_headers: None,
44        }
45    }
46
47    /// Set object region
48    pub fn region(mut self, region: Option<String>) -> Self {
49        self.region = region;
50        self
51    }
52
53    /// Set the account ID of the expected bucket owner.
54    pub fn expected_bucket_owner(mut self, expected_bucket_owner: Option<String>) -> Self {
55        self.expected_bucket_owner = expected_bucket_owner;
56        self
57    }
58
59    /// Set extra headers for advanced usage.
60    pub fn extra_headers(mut self, extra_headers: Option<HeaderMap>) -> Self {
61        self.extra_headers = extra_headers;
62        self
63    }
64}
65
66impl<S> From<S> for BucketArgs
67where
68    S: Into<String>,
69{
70    fn from(s: S) -> Self {
71        Self::new(s)
72    }
73}
74
75/// A source object definition for `copy_object` and `upload_part_copy` method.
76#[derive(Debug, Clone)]
77pub struct CopySource {
78    bucket_name: String,
79    object_name: String,
80    region: Option<String>,
81    offset: usize,
82    length: usize,
83    version_id: Option<String>,
84    metadata_replace: bool,
85    ssec: Option<HeaderMap>,
86    match_etag: Option<String>,
87    not_match_etag: Option<String>,
88    modified_since: Option<String>,
89    unmodified_since: Option<String>,
90}
91
92impl CopySource {
93    pub fn new<T1: Into<String>, T2: Into<String>>(bucket_name: T1, object_name: T2) -> Self {
94        Self {
95            bucket_name: bucket_name.into(),
96            object_name: object_name.into(),
97            region: None,
98            version_id: None,
99            metadata_replace: false,
100            ssec: None,
101            match_etag: None,
102            not_match_etag: None,
103            modified_since: None,
104            unmodified_since: None,
105            offset: 0,
106            length: 0,
107        }
108    }
109
110    /// Set object region
111    pub fn region(mut self, region: Option<String>) -> Self {
112        self.region = region;
113        self
114    }
115
116    /// Used only in `upload_part_copy` method.
117    ///
118    /// **Note**: length must be greater than 0, or both length and offset are 0.
119    pub fn range(mut self, offset: usize, length: usize) -> Self {
120        self.offset = offset;
121        self.length = length;
122        self
123    }
124
125    /// When copying an object, preserve all metadata if set `false` (default) or specify new metadata.
126    pub fn metadata_replace(mut self, metadata_replace: bool) -> Self {
127        self.metadata_replace = metadata_replace;
128        self
129    }
130
131    /// Set version-ID of the object
132    pub fn version_id<T: Into<String>>(mut self, version_id: T) -> Self {
133        self.version_id = Some(version_id.into());
134        self
135    }
136
137    /// Set server-side encryption customer key
138    pub fn ssec(mut self, ssec: &SseCustomerKey) -> Self {
139        let mut header = ssec.headers();
140        header.extend(ssec.copy_headers());
141        self.ssec = Some(header);
142        self
143    }
144
145    pub fn match_etag(mut self, match_etag: Option<String>) -> Self {
146        self.match_etag = match_etag;
147        self
148    }
149
150    pub fn not_match_etag(mut self, not_match_etag: Option<String>) -> Self {
151        self.not_match_etag = not_match_etag;
152        self
153    }
154
155    pub fn modified_since(mut self, modified_since: Option<String>) -> Self {
156        self.modified_since = modified_since;
157        self
158    }
159
160    pub fn unmodified_since(mut self, unmodified_since: Option<String>) -> Self {
161        self.unmodified_since = unmodified_since;
162        self
163    }
164
165    pub(crate) fn args_headers(&self) -> HeaderMap {
166        let mut header = HeaderMap::new();
167        let mut copy_source =
168            urlencode(&format!("/{}/{}", self.bucket_name, self.object_name), true);
169        if let Some(version_id) = &self.version_id {
170            copy_source = copy_source + "?versionId=" + version_id;
171        }
172        header.insert("x-amz-copy-source", copy_source.parse().unwrap());
173        if let Some(value) = &self.match_etag {
174            header.insert("x-amz-copy-source-if-match", value.parse().unwrap());
175        }
176        if let Some(value) = &self.not_match_etag {
177            header.insert("x-amz-copy-source-if-none-match", value.parse().unwrap());
178        }
179        if self.metadata_replace {
180            header.insert("x-amz-metadata-directive", "REPLACE".parse().unwrap());
181        }
182        if let Some(value) = &self.modified_since {
183            header.insert(
184                "x-amz-copy-source-if-modified-since",
185                value.parse().unwrap(),
186            );
187        }
188        if let Some(value) = &self.unmodified_since {
189            header.insert(
190                "x-amz-copy-source-if-unmodified-since",
191                value.parse().unwrap(),
192            );
193        }
194        if self.offset > 0 || self.length > 0 {
195            let ranger = if self.length > 0 {
196                format!("bytes={}-{}", self.offset, self.offset + self.length - 1)
197            } else {
198                format!("bytes={}-", self.offset)
199            };
200            if let Ok(value) = ranger.parse() {
201                header.insert("x-amz-copy-source-range", value);
202            }
203        }
204        if let Some(ssec) = &self.ssec {
205            header.extend(ssec.clone());
206            for (k, v) in ssec {
207                header.insert(k, v.to_owned());
208            }
209        }
210        header
211    }
212}
213
214/// Custom request parameters for object operations.
215/// ## parmas
216/// - `name`: The key of object.
217/// - `version_id`: *Optional*, Version-ID of the object.
218/// - `content_type`: *Optional*, Content type of the object.
219/// - `ssec`: *Optional*, Server-side encryption customer key.
220/// - `offset`: *Optional*, Start byte position of object data.
221/// - `length`: *Optional*, Number of bytes of object data from offset.
222/// - `metadata`: *Optional*, user-defined metadata.
223/// - `extra_headers`: *Optional*, Extra headers for advanced usage.
224///
225/// **Note**: Some parameters are only valid in specific methods
226#[derive(Debug, Clone)]
227pub struct KeyArgs {
228    pub(crate) name: String,
229    pub(crate) version_id: Option<String>,
230    pub(crate) content_type: Option<String>,
231    pub(crate) ssec_headers: Option<HeaderMap>,
232    pub(crate) offset: usize,
233    pub(crate) length: usize,
234    pub(crate) extra_headers: Option<HeaderMap>,
235    pub(crate) metadata: HashMap<String, String>,
236}
237
238impl KeyArgs {
239    pub fn new<S: Into<String>>(name: S) -> Self {
240        Self {
241            name: name.into(),
242            extra_headers: None,
243            version_id: None,
244            content_type: None,
245            ssec_headers: None,
246            offset: 0,
247            length: 0,
248            metadata: Default::default(),
249        }
250    }
251
252    /// Set version-ID of the object
253    pub fn version_id(mut self, version_id: Option<String>) -> Self {
254        self.version_id = version_id;
255        self
256    }
257
258    /// Set content-type of the object
259    pub fn content_type(mut self, content_type: Option<String>) -> Self {
260        self.content_type = content_type;
261        self
262    }
263
264    /// Set extra headers for advanced usage.
265    pub fn extra_headers(mut self, extra_headers: Option<HeaderMap>) -> Self {
266        self.extra_headers = extra_headers;
267        self
268    }
269
270    /// Set server-side encryption customer key
271    pub fn ssec(mut self, ssec: &SseCustomerKey) -> Self {
272        self.ssec_headers = Some(ssec.headers());
273        self
274    }
275
276    /// Returns the range of this [`ObjectArgs`].
277    pub(crate) fn range(&self) -> Option<String> {
278        if self.offset > 0 || self.length > 0 {
279            Some(if self.length > 0 {
280                format!("bytes={}-{}", self.offset, self.offset + self.length - 1)
281            } else {
282                format!("bytes={}-", self.offset)
283            })
284        } else {
285            None
286        }
287    }
288
289    /// Set start byte position of object data when `download` an object.
290    /// Valid in the download operation of the object.
291    ///
292    /// Default: 0
293    pub fn offset(mut self, offset: usize) -> Self {
294        self.offset = offset;
295        self
296    }
297
298    /// Set number of bytes of object data from offset when `download` an object.
299    /// If set length 0, it means to the end of the object.
300    ///
301    /// Default: 0
302    pub fn length(mut self, length: usize) -> Self {
303        self.length = length;
304        self
305    }
306
307    /// Set user-defined metadata when `uploading` an object.
308    /// Metadata is a set of key-value pairs.
309    ///
310    /// key:
311    /// - requirement is ASCII and cannot contain non-ASCII characters
312    /// - Cannot contain invisible characters and spaces
313    /// - does't need to start with `x-amz-meta-`
314    /// - ignoring case
315    ///
316    pub fn metadata(mut self, metadata: HashMap<String, String>) -> Self {
317        self.metadata = metadata;
318        self
319    }
320
321    /// Returns the metadata header of this [`ObjectArgs`].
322    pub(crate) fn get_metadata_header(&self) -> Result<HeaderMap> {
323        let mut meta_header: HeaderMap = HeaderMap::new();
324        for (key, value) in &self.metadata {
325            let key = HeaderName::from_bytes(format!("x-amz-meta-{}", key).as_bytes())?;
326            meta_header.insert(key, value.parse()?);
327        }
328        Ok(meta_header)
329    }
330}
331
332impl<S> From<S> for KeyArgs
333where
334    S: Into<String>,
335{
336    fn from(name: S) -> Self {
337        Self::new(name)
338    }
339}
340
341/// Custom `list_multipart_uploads` request parameters
342#[derive(Debug, Clone)]
343pub struct ListMultipartUploadsArgs {
344    bucket_name: String,
345    delimiter: String,
346    encoding_type: String,
347    key_marker: Option<String>,
348    max_uploads: usize,
349    prefix: String,
350    upload_id_marker: Option<String>,
351    extra_headers: Option<HeaderMap>,
352    extra_query_params: Option<String>,
353    expected_bucket_owner: Option<String>,
354}
355
356impl ListMultipartUploadsArgs {
357    pub fn new(bucket_name: String) -> Self {
358        Self {
359            bucket_name,
360            delimiter: "".to_string(),
361            encoding_type: "".to_string(),
362            max_uploads: 1000,
363            prefix: "".to_string(),
364            key_marker: None,
365            upload_id_marker: None,
366            expected_bucket_owner: None,
367            extra_query_params: None,
368            extra_headers: None,
369        }
370    }
371
372    /// get bucket_name
373    pub fn bucket_name(&self) -> &str {
374        &self.bucket_name
375    }
376
377    /// set character you use to group keys.
378    pub fn delimiter<T: Into<String>>(mut self, delimiter: T) -> Self {
379        self.delimiter = delimiter.into();
380        self
381    }
382
383    /// set encoding type.
384    /// Valid Values: url
385    pub fn encoding_type<T: Into<String>>(mut self, encoding_type: T) -> Self {
386        self.encoding_type = encoding_type.into();
387        self
388    }
389
390    pub fn key_marker<T: Into<String>>(mut self, key_marker: T) -> Self {
391        self.key_marker = Some(key_marker.into());
392        self
393    }
394
395    pub fn upload_id_marker<T: Into<String>>(mut self, upload_id_marker: T) -> Self {
396        self.upload_id_marker = Some(upload_id_marker.into());
397        self
398    }
399
400    pub fn max_uploads(mut self, max_uploads: usize) -> Self {
401        self.max_uploads = max_uploads;
402        if self.max_uploads > 1000 {
403            self.max_uploads = 1000;
404        }
405        self
406    }
407
408    pub fn prefix<T: Into<String>>(mut self, prefix: T) -> Self {
409        self.prefix = prefix.into();
410        self
411    }
412
413    /// set bucket owner
414    pub fn expected_bucket_owner<T: Into<String>>(mut self, expected_bucket_owner: T) -> Self {
415        self.expected_bucket_owner = Some(expected_bucket_owner.into());
416        self
417    }
418
419    /// Set extra query parameters for advanced usage.
420    pub fn extra_query_params(mut self, extra_query_params: Option<String>) -> Self {
421        self.extra_query_params = extra_query_params;
422        self
423    }
424
425    /// Set extra headers for advanced usage.
426    pub fn extra_headers(mut self, extra_headers: Option<HeaderMap>) -> Self {
427        self.extra_headers = extra_headers;
428        self
429    }
430
431    pub(crate) fn args_query_map(&self) -> QueryMap {
432        let mut querys: QueryMap = QueryMap::default();
433        querys.insert("uploads".to_string(), "".to_string());
434        querys.insert("delimiter".to_string(), self.delimiter.to_string());
435        querys.insert("max-uploads".to_string(), self.max_uploads.to_string());
436        querys.insert("prefix".to_string(), self.prefix.to_string());
437        querys.insert("encoding-type".to_string(), self.encoding_type.to_string());
438        if let Some(encoding_type) = &self.key_marker {
439            querys.insert("key-marker".to_string(), encoding_type.to_string());
440        }
441        if let Some(delimiter) = &self.upload_id_marker {
442            querys.insert("upload-id-marker".to_string(), delimiter.clone());
443        }
444        return querys;
445    }
446
447    pub(crate) fn args_headers(&self) -> HeaderMap {
448        let mut headermap = HeaderMap::new();
449        if let Some(owner) = &self.expected_bucket_owner {
450            if let Ok(val) = owner.parse() {
451                headermap.insert("x-amz-expected-bucket-owner", val);
452            }
453        }
454        headermap
455    }
456}
457
458pub struct ListObjectVersionsArgs {
459    pub delimiter: Option<String>,
460    pub encoding_type: Option<String>,
461    pub extra_headers: Option<HeaderMap>,
462    /// Specifies the key to start with when listing objects in a bucket.
463    pub key_marker: Option<String>,
464    pub prefix: Option<String>,
465    /// Sets the maximum number of keys returned in the response. Default 1,000
466    pub max_keys: usize,
467    /// Specifies the object version you want to start listing from.
468    pub version_id_marker: Option<String>,
469}
470
471impl Default for ListObjectVersionsArgs {
472    fn default() -> Self {
473        Self {
474            extra_headers: None,
475            delimiter: None,
476            encoding_type: None,
477            max_keys: 1000,
478            prefix: None,
479            key_marker: None,
480            version_id_marker: None,
481        }
482    }
483}
484
485impl ListObjectVersionsArgs {
486    pub(crate) fn args_query_map(&self) -> QueryMap {
487        let mut querys: QueryMap = QueryMap::default();
488        querys.insert("versions".to_string(), "".to_string());
489        if let Some(delimiter) = &self.delimiter {
490            querys.insert("delimiter".to_string(), delimiter.clone());
491        }
492        if let Some(encoding_type) = &self.encoding_type {
493            querys.insert("encoding-type".to_string(), encoding_type.clone());
494        }
495        if let Some(key_marker) = &self.key_marker {
496            querys.insert("key-marker".to_string(), key_marker.clone());
497        }
498        if let Some(prefix) = &self.prefix {
499            querys.insert("prefix".to_string(), prefix.clone());
500        }
501        if let Some(version_id_marker) = &self.version_id_marker {
502            querys.insert("version-id-marker".to_string(), version_id_marker.clone());
503        }
504        querys.insert("max-keys".to_string(), format!("{}", self.max_keys));
505        querys
506    }
507}
508
509/// Custom `list_objects` request parameters
510/// ## parmas
511/// - prefix: Limits the response to keys that begin with the specified prefix.
512/// - delimiter: A delimiter is a character you use to group keys.
513/// - continuation_token: ContinuationToken indicates Amazon S3 that the list is being continued on this bucket with a token.
514/// - max_keys: Sets the maximum number of keys returned in the response. Default 1000
515/// - encoding_type:Encoding type used by Amazon S3 to encode object keys in the response.Valid Values: `url`
516#[derive(Debug, Clone)]
517pub struct ListObjectsArgs {
518    pub(crate) continuation_token: Option<String>,
519    pub(crate) delimiter: Option<String>,
520    pub(crate) use_encoding_type: bool,
521    pub(crate) fetch_owner: bool,
522    pub(crate) start_after: Option<String>,
523    pub(crate) max_keys: usize,
524    pub(crate) prefix: Option<String>,
525    pub(crate) extra_headers: Option<HeaderMap>,
526}
527
528impl Default for ListObjectsArgs {
529    fn default() -> Self {
530        Self {
531            continuation_token: None,
532            delimiter: None,
533            fetch_owner: false,
534            max_keys: 1000,
535            prefix: None,
536            start_after: None,
537            use_encoding_type: false,
538            extra_headers: None,
539        }
540    }
541}
542
543impl ListObjectsArgs {
544    pub fn continuation_token<T: Into<String>>(mut self, token: T) -> Self {
545        self.continuation_token = Some(token.into());
546        self
547    }
548
549    pub fn delimiter<T: Into<String>>(mut self, delimiter: T) -> Self {
550        self.delimiter = Some(delimiter.into());
551        self
552    }
553
554    pub fn use_encoding_type(mut self, use_encoding_type: bool) -> Self {
555        self.use_encoding_type = use_encoding_type;
556        self
557    }
558
559    pub fn fetch_owner(mut self, fetch_owner: bool) -> Self {
560        self.fetch_owner = fetch_owner;
561        self
562    }
563
564    pub fn start_after<T: Into<String>>(mut self, start_after: T) -> Self {
565        self.start_after = Some(start_after.into());
566        self
567    }
568
569    pub fn max_keys(mut self, max_keys: usize) -> Self {
570        self.max_keys = max_keys;
571        if self.max_keys > 1000 {
572            self.max_keys = 1000;
573        }
574        self
575    }
576
577    pub fn prefix<T: Into<String>>(mut self, prefix: T) -> Self {
578        self.prefix = Some(prefix.into());
579        self
580    }
581
582    /// Set extra headers for advanced usage.
583    pub fn extra_headers(mut self, extra_headers: Option<HeaderMap>) -> Self {
584        self.extra_headers = extra_headers;
585        self
586    }
587
588    pub(crate) fn args_query_map(&self) -> QueryMap {
589        let mut querys: QueryMap = QueryMap::default();
590        querys.insert("list-type".to_string(), "2".to_string());
591
592        if self.use_encoding_type {
593            querys.insert("encoding-type".to_string(), "url".to_string());
594        }
595        if let Some(delimiter) = &self.delimiter {
596            querys.insert("delimiter".to_string(), delimiter.clone());
597        }
598        if let Some(token) = &self.continuation_token {
599            querys.insert("continuation-token".to_string(), token.clone());
600        }
601        if self.fetch_owner {
602            querys.insert("fetch-owner".to_string(), "true".to_string());
603        }
604        if let Some(prefix) = &self.prefix {
605            querys.insert("prefix".to_string(), prefix.clone());
606        }
607        if let Some(start_after) = &self.start_after {
608            querys.insert("start-after".to_string(), start_after.clone());
609        }
610        querys.insert("max-keys".to_string(), format!("{}", self.max_keys));
611        return querys;
612    }
613}
614
615/// Custom request parameters for multiUpload operations.
616///
617/// Used in `abort_multipart_upload`, `complete_multipart_upload`, `create_multipart_upload`,
618/// `MultipartUploadArgs`, `upload_part`, `upload_part_copy` method.
619#[derive(Debug, Clone)]
620pub struct MultipartUploadTask {
621    bucket: String,
622    key: String,
623    upload_id: String,
624    bucket_owner: Option<String>,
625    content_type: Option<String>,
626    ssec_header: Option<HeaderMap>,
627}
628
629impl From<InitiateMultipartUploadResult> for MultipartUploadTask {
630    fn from(i: InitiateMultipartUploadResult) -> Self {
631        Self::new(i.bucket, i.key, i.upload_id, None, None, None)
632    }
633}
634
635impl MultipartUploadTask {
636    pub fn new(
637        bucket: String,
638        key: String,
639        upload_id: String,
640        bucket_owner: Option<String>,
641        content_type: Option<String>,
642        ssec_header: Option<HeaderMap>,
643    ) -> Self {
644        Self {
645            bucket,
646            key,
647            upload_id,
648            bucket_owner,
649            content_type,
650            ssec_header,
651        }
652    }
653
654    pub fn bucket(&self) -> &str {
655        self.bucket.as_ref()
656    }
657
658    pub fn key(&self) -> &str {
659        self.key.as_ref()
660    }
661
662    pub fn upload_id(&self) -> &str {
663        self.upload_id.as_ref()
664    }
665
666    pub fn content_type(&self) -> Option<&String> {
667        self.content_type.as_ref()
668    }
669
670    pub fn bucket_owner(&self) -> Option<&String> {
671        self.bucket_owner.as_ref()
672    }
673
674    pub fn ssec_header(&self) -> Option<&HeaderMap> {
675        self.ssec_header.as_ref()
676    }
677
678    pub(crate) fn set_ssec_header(&mut self, ssec_header: Option<HeaderMap>) {
679        self.ssec_header = ssec_header;
680    }
681
682    pub(crate) fn set_bucket_owner(&mut self, bucket_owner: Option<String>) {
683        self.bucket_owner = bucket_owner;
684    }
685}
686
687/// The container element for Object Lock configuration parameters.\
688/// see `put_object_lock_configuration` and `get_object_lock_configuration` API.
689///
690/// **Note**: both `mode` and `duration` settings will be effective.
691#[derive(Debug, Clone, Default)]
692pub struct ObjectLockConfig {
693    /// Valid Values: GOVERNANCE | COMPLIANCE
694    mode: String,
695    /// The date on which this Object Lock Retention will expire.
696    duration: usize,
697    /// Valid Values: Days | Years
698    duration_unit: String,
699}
700
701impl ObjectLockConfig {
702    pub fn new(duration: usize, is_day: bool, is_governance: bool) -> Self {
703        let mut obj = Self::default();
704        obj.config(duration, is_day, is_governance);
705        obj
706    }
707
708    /// - is_day: set period `Days` if true, otherwise set mode `Years`
709    /// - is_governance: set mode `GOVERNANCE` if true, otherwise set mode `COMPLIANCE`.
710    pub fn config(&mut self, duration: usize, is_day: bool, is_governance: bool) {
711        self.duration = duration;
712        self.duration_unit = (if is_day { "Days" } else { "Years" }).to_string();
713        self.mode = (if is_governance {
714            "GOVERNANCE"
715        } else {
716            "COMPLIANCE"
717        })
718        .to_string();
719    }
720
721    /// The date on which this Object Lock Retention will expire.
722    pub fn duration(&self) -> usize {
723        self.duration
724    }
725
726    /// Valid Values: GOVERNANCE | COMPLIANCE
727    pub fn mode(&self) -> &str {
728        self.mode.as_ref()
729    }
730
731    /// period, Valid Values: Days | Years | Empty String
732    pub fn period(&self) -> &str {
733        self.duration_unit.as_ref()
734    }
735}
736
737impl ToXml for ObjectLockConfig {
738    fn to_xml(&self) -> crate::error::Result<String> {
739        let mut result =
740            "<ObjectLockConfiguration><ObjectLockEnabled>Enabled</ObjectLockEnabled>".to_string();
741        if !self.mode.is_empty() && !self.duration_unit.is_empty() {
742            result += "<Rule><DefaultRetention>";
743            result += &format!("<Mode>{}</Mode>", self.mode);
744            result += &format!(
745                "<{}>{}</{}>",
746                self.duration_unit, self.duration, self.duration_unit
747            );
748            result += "</DefaultRetention></Rule>";
749        }
750        result += "</ObjectLockConfiguration>";
751        Ok(result)
752    }
753}
754
755impl FromXml for ObjectLockConfig {
756    fn from_xml(value: String) -> crate::error::Result<Self> {
757        let obj = crate::xml::de::from_str::<ObjectLockConfiguration>(&value)?;
758        if let Some(rule) = obj.rule {
759            let mode = if rule.default_retention.mode == RetentionMode::GOVERNANCE {
760                "GOVERNANCE"
761            } else {
762                "COMPLIANCE"
763            };
764            if let Some(duration) = rule.default_retention.days {
765                Ok(Self {
766                    mode: mode.to_owned(),
767                    duration,
768                    duration_unit: "Days".to_owned(),
769                })
770            } else if let Some(duration) = rule.default_retention.years {
771                Ok(Self {
772                    mode: mode.to_owned(),
773                    duration,
774                    duration_unit: "Years".to_owned(),
775                })
776            } else {
777                Ok(Default::default())
778            }
779        } else {
780            Ok(Default::default())
781        }
782    }
783}
784
785/// Custom request parameters for presigned URL
786/// ## param
787/// - bucket_name: Name of the bucket.
788/// - object_name: Object name in the bucket.
789/// - expires: Expiry in seconds; defaults to 7 days.
790/// - headers: Optional response_headers argument to specify response fields like date, size, type of file, data about server, etc.
791/// - request_date: Optional request_date argument to specify a different request date. Default is current date.
792/// - version_id: Version ID of the object.
793/// - querys: Extra query parameters for advanced usage.
794#[derive(Clone)]
795pub struct PresignedArgs {
796    pub(crate) region: Option<String>,
797    pub(crate) bucket_name: String,
798    pub(crate) object_name: String,
799    pub(crate) version_id: Option<String>,
800    pub(crate) expires: usize,
801    pub(crate) request_date: Option<UtcTime>,
802    pub(crate) headers: Option<HeaderMap>,
803    pub(crate) querys: QueryMap,
804}
805
806impl PresignedArgs {
807    pub fn new<T1: Into<String>, T2: Into<String>>(bucket_name: T1, object_name: T2) -> Self {
808        Self {
809            region: None,
810            bucket_name: bucket_name.into(),
811            object_name: object_name.into(),
812            version_id: None,
813            expires: 604800,
814            request_date: None,
815            headers: None,
816            querys: QueryMap::new(),
817        }
818    }
819
820    pub fn region<T: Into<String>>(mut self, region: T) -> Self {
821        self.region = Some(region.into());
822        self
823    }
824
825    pub fn version_id<T: Into<String>>(mut self, version_id: T) -> Self {
826        self.version_id = Some(version_id.into());
827        self
828    }
829
830    pub fn regirequest_date(mut self, request_date: UtcTime) -> Self {
831        self.request_date = Some(request_date);
832        self
833    }
834
835    pub fn expires(mut self, expires: usize) -> Self {
836        self.expires = expires;
837        self
838    }
839
840    pub fn headers(mut self, header: HeaderMap) -> Self {
841        self.headers = Some(header);
842        self
843    }
844
845    pub fn header<K>(mut self, key: K, value: &str) -> Self
846    where
847        K: IntoHeaderName,
848    {
849        let mut headers = self.headers.unwrap_or(HeaderMap::new());
850        if let Ok(value) = value.parse() {
851            headers.insert(key, value);
852        }
853        self.headers = Some(headers);
854        self
855    }
856
857    pub fn querys(mut self, querys: QueryMap) -> Self {
858        self.querys = querys;
859        self
860    }
861
862    pub fn query<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
863        self.querys.insert(key.into(), value.into());
864        self
865    }
866
867    pub fn query_string(mut self, query_str: &str) -> Self {
868        self.querys.merge_str(query_str);
869        self
870    }
871
872    pub fn apply<F>(self, apply: F) -> Self
873    where
874        F: FnOnce(Self) -> Self,
875    {
876        apply(self)
877    }
878}
879
880/// Tags
881/// - request XML of put_bucket_tags API and put_object_tags API
882/// - response XML of set_bucket_tags API and set_object_tags API.
883#[derive(Debug, Clone)]
884pub struct Tags(HashMap<String, String>);
885
886impl Tags {
887    pub fn new() -> Self {
888        Self(HashMap::new())
889    }
890
891    pub fn to_query(&self) -> String {
892        self.0
893            .iter()
894            .map(|(key, value)| format!("{}={}", urlencode(key, false), urlencode(value, false)))
895            .collect::<Vec<String>>()
896            .join("&")
897    }
898
899    pub fn insert<K: Into<String>, V: Into<String>>(&mut self, key: K, value: V) -> &mut Self {
900        self.0.insert(key.into(), value.into());
901        self
902    }
903
904    pub fn into_map(self) -> HashMap<String, String> {
905        self.0
906    }
907}
908
909impl From<HashMap<String, String>> for Tags {
910    fn from(inner: HashMap<String, String>) -> Self {
911        Self(inner)
912    }
913}
914
915impl std::ops::Deref for Tags {
916    type Target = HashMap<String, String>;
917
918    fn deref(&self) -> &Self::Target {
919        &self.0
920    }
921}
922
923impl std::ops::DerefMut for Tags {
924    fn deref_mut(&mut self) -> &mut Self::Target {
925        &mut self.0
926    }
927}
928
929impl From<Tagging> for Tags {
930    fn from(tagging: Tagging) -> Self {
931        let mut map = HashMap::new();
932        for tag in tagging.tag_set.tags {
933            map.insert(tag.key, tag.value);
934        }
935        Self(map)
936    }
937}
938
939impl FromXml for Tags {
940    fn from_xml(v: String) -> crate::error::Result<Self> {
941        crate::xml::de::from_string::<Tagging>(v)
942            .map(Into::into)
943            .map_err(Into::into)
944    }
945}
946
947impl ToXml for Tags {
948    fn to_xml(&self) -> crate::error::Result<String> {
949        let mut result = "<Tagging><TagSet>".to_string();
950        for (key, value) in &self.0 {
951            result += &format!("<Tag><Key>{}</Key><Value>{}</Value></Tag>", key, value);
952        }
953        result += "</TagSet></Tagging>";
954        return Ok(result);
955    }
956}