rustfs_rsc/client/
args.rs

1use std::collections::HashMap;
2
3use hyper::{
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    pub fn bucket_name(&self) -> &str {
373        &self.bucket_name
374    }
375
376    pub fn delimiter<T: Into<String>>(mut self, delimiter: T) -> Self {
377        self.delimiter = delimiter.into();
378        self
379    }
380
381    pub fn encoding_type<T: Into<String>>(mut self, encoding_type: T) -> Self {
382        self.encoding_type = encoding_type.into();
383        self
384    }
385
386    pub fn key_marker<T: Into<String>>(mut self, key_marker: T) -> Self {
387        self.key_marker = Some(key_marker.into());
388        self
389    }
390
391    pub fn upload_id_marker<T: Into<String>>(mut self, upload_id_marker: T) -> Self {
392        self.upload_id_marker = Some(upload_id_marker.into());
393        self
394    }
395
396    pub fn max_uploads(mut self, max_uploads: usize) -> Self {
397        self.max_uploads = max_uploads;
398        if self.max_uploads > 1000 {
399            self.max_uploads = 1000;
400        }
401        self
402    }
403
404    pub fn prefix<T: Into<String>>(mut self, prefix: T) -> Self {
405        self.prefix = prefix.into();
406        self
407    }
408
409    pub fn expected_bucket_owner<T: Into<String>>(mut self, expected_bucket_owner: T) -> Self {
410        self.expected_bucket_owner = Some(expected_bucket_owner.into());
411        self
412    }
413
414    /// Set extra query parameters for advanced usage.
415    pub fn extra_query_params(mut self, extra_query_params: Option<String>) -> Self {
416        self.extra_query_params = extra_query_params;
417        self
418    }
419
420    /// Set extra headers for advanced usage.
421    pub fn extra_headers(mut self, extra_headers: Option<HeaderMap>) -> Self {
422        self.extra_headers = extra_headers;
423        self
424    }
425
426    pub(crate) fn args_query_map(&self) -> QueryMap {
427        let mut querys: QueryMap = QueryMap::default();
428        querys.insert("uploads".to_string(), "".to_string());
429        querys.insert("delimiter".to_string(), self.delimiter.to_string());
430        querys.insert("max-uploads".to_string(), self.max_uploads.to_string());
431        querys.insert("prefix".to_string(), self.prefix.to_string());
432        querys.insert("encoding-type".to_string(), self.encoding_type.to_string());
433        if let Some(encoding_type) = &self.key_marker {
434            querys.insert("key-marker".to_string(), encoding_type.to_string());
435        }
436        if let Some(delimiter) = &self.upload_id_marker {
437            querys.insert("upload-id-marker".to_string(), delimiter.clone());
438        }
439        return querys;
440    }
441
442    pub(crate) fn args_headers(&self) -> HeaderMap {
443        let mut headermap = HeaderMap::new();
444        if let Some(owner) = &self.expected_bucket_owner {
445            if let Ok(val) = owner.parse() {
446                headermap.insert("x-amz-expected-bucket-owner", val);
447            }
448        }
449        headermap
450    }
451}
452
453pub struct ListObjectVersionsArgs {
454    pub delimiter: Option<String>,
455    pub encoding_type: Option<String>,
456    pub extra_headers: Option<HeaderMap>,
457    /// Specifies the key to start with when listing objects in a bucket.
458    pub key_marker: Option<String>,
459    pub prefix: Option<String>,
460    /// Sets the maximum number of keys returned in the response. Default 1,000
461    pub max_keys: usize,
462    /// Specifies the object version you want to start listing from.
463    pub version_id_marker: Option<String>,
464}
465
466impl Default for ListObjectVersionsArgs {
467    fn default() -> Self {
468        Self {
469            extra_headers: None,
470            delimiter: None,
471            encoding_type: None,
472            max_keys: 1000,
473            prefix: None,
474            key_marker: None,
475            version_id_marker: None,
476        }
477    }
478}
479
480impl ListObjectVersionsArgs {
481    pub(crate) fn args_query_map(&self) -> QueryMap {
482        let mut querys: QueryMap = QueryMap::default();
483        querys.insert("versions".to_string(), "".to_string());
484        if let Some(delimiter) = &self.delimiter {
485            querys.insert("delimiter".to_string(), delimiter.clone());
486        }
487        if let Some(encoding_type) = &self.encoding_type {
488            querys.insert("encoding-type".to_string(), encoding_type.clone());
489        }
490        if let Some(key_marker) = &self.key_marker {
491            querys.insert("key-marker".to_string(), key_marker.clone());
492        }
493        if let Some(prefix) = &self.prefix {
494            querys.insert("prefix".to_string(), prefix.clone());
495        }
496        if let Some(version_id_marker) = &self.version_id_marker {
497            querys.insert("version-id-marker".to_string(), version_id_marker.clone());
498        }
499        querys.insert("max-keys".to_string(), format!("{}", self.max_keys));
500        querys
501    }
502}
503
504/// Custom `list_objects` request parameters
505/// ## parmas
506/// - prefix: Limits the response to keys that begin with the specified prefix.
507/// - delimiter: A delimiter is a character you use to group keys.
508/// - continuation_token: ContinuationToken indicates Amazon S3 that the list is being continued on this bucket with a token.
509/// - max_keys: Sets the maximum number of keys returned in the response. Default 1000
510/// - encoding_type:Encoding type used by Amazon S3 to encode object keys in the response.Valid Values: `url`
511#[derive(Debug, Clone)]
512pub struct ListObjectsArgs {
513    pub(crate) continuation_token: Option<String>,
514    pub(crate) delimiter: Option<String>,
515    pub(crate) use_encoding_type: bool,
516    pub(crate) fetch_owner: bool,
517    pub(crate) start_after: Option<String>,
518    pub(crate) max_keys: usize,
519    pub(crate) prefix: Option<String>,
520    pub(crate) extra_headers: Option<HeaderMap>,
521}
522
523impl Default for ListObjectsArgs {
524    fn default() -> Self {
525        Self {
526            continuation_token: None,
527            delimiter: None,
528            fetch_owner: false,
529            max_keys: 1000,
530            prefix: None,
531            start_after: None,
532            use_encoding_type: false,
533            extra_headers: None,
534        }
535    }
536}
537
538impl ListObjectsArgs {
539    pub fn continuation_token<T: Into<String>>(mut self, token: T) -> Self {
540        self.continuation_token = Some(token.into());
541        self
542    }
543
544    pub fn delimiter<T: Into<String>>(mut self, delimiter: T) -> Self {
545        self.delimiter = Some(delimiter.into());
546        self
547    }
548
549    pub fn use_encoding_type(mut self, use_encoding_type: bool) -> Self {
550        self.use_encoding_type = use_encoding_type;
551        self
552    }
553
554    pub fn fetch_owner(mut self, fetch_owner: bool) -> Self {
555        self.fetch_owner = fetch_owner;
556        self
557    }
558
559    pub fn start_after<T: Into<String>>(mut self, start_after: T) -> Self {
560        self.start_after = Some(start_after.into());
561        self
562    }
563
564    pub fn max_keys(mut self, max_keys: usize) -> Self {
565        self.max_keys = max_keys;
566        if self.max_keys > 1000 {
567            self.max_keys = 1000;
568        }
569        self
570    }
571
572    pub fn prefix<T: Into<String>>(mut self, prefix: T) -> Self {
573        self.prefix = Some(prefix.into());
574        self
575    }
576
577    /// Set extra headers for advanced usage.
578    pub fn extra_headers(mut self, extra_headers: Option<HeaderMap>) -> Self {
579        self.extra_headers = extra_headers;
580        self
581    }
582
583    pub(crate) fn args_query_map(&self) -> QueryMap {
584        let mut querys: QueryMap = QueryMap::default();
585        querys.insert("list-type".to_string(), "2".to_string());
586
587        if self.use_encoding_type {
588            querys.insert("encoding-type".to_string(), "url".to_string());
589        }
590        if let Some(delimiter) = &self.delimiter {
591            querys.insert("delimiter".to_string(), delimiter.clone());
592        }
593        if let Some(token) = &self.continuation_token {
594            querys.insert("continuation-token".to_string(), token.clone());
595        }
596        if self.fetch_owner {
597            querys.insert("fetch-owner".to_string(), "true".to_string());
598        }
599        if let Some(prefix) = &self.prefix {
600            querys.insert("prefix".to_string(), prefix.clone());
601        }
602        if let Some(start_after) = &self.start_after {
603            querys.insert("start-after".to_string(), start_after.clone());
604        }
605        querys.insert("max-keys".to_string(), format!("{}", self.max_keys));
606        return querys;
607    }
608}
609
610/// Custom request parameters for multiUpload operations.
611///
612/// Used in `abort_multipart_upload`, `complete_multipart_upload`, `create_multipart_upload`,
613/// `MultipartUploadArgs`, `upload_part`, `upload_part_copy` method.
614#[derive(Debug, Clone)]
615pub struct MultipartUploadTask {
616    bucket: String,
617    key: String,
618    upload_id: String,
619    bucket_owner: Option<String>,
620    content_type: Option<String>,
621    ssec_header: Option<HeaderMap>,
622}
623
624impl From<InitiateMultipartUploadResult> for MultipartUploadTask {
625    fn from(i: InitiateMultipartUploadResult) -> Self {
626        Self::new(i.bucket, i.key, i.upload_id, None, None, None)
627    }
628}
629
630impl MultipartUploadTask {
631    pub fn new(
632        bucket: String,
633        key: String,
634        upload_id: String,
635        bucket_owner: Option<String>,
636        content_type: Option<String>,
637        ssec_header: Option<HeaderMap>,
638    ) -> Self {
639        Self {
640            bucket,
641            key,
642            upload_id,
643            bucket_owner,
644            content_type,
645            ssec_header,
646        }
647    }
648
649    pub fn bucket(&self) -> &str {
650        self.bucket.as_ref()
651    }
652
653    pub fn key(&self) -> &str {
654        self.key.as_ref()
655    }
656
657    pub fn upload_id(&self) -> &str {
658        self.upload_id.as_ref()
659    }
660
661    pub fn content_type(&self) -> Option<&String> {
662        self.content_type.as_ref()
663    }
664
665    pub fn bucket_owner(&self) -> Option<&String> {
666        self.bucket_owner.as_ref()
667    }
668
669    pub fn ssec_header(&self) -> Option<&HeaderMap> {
670        self.ssec_header.as_ref()
671    }
672
673    pub(crate) fn set_ssec(&mut self, ssec: SseCustomerKey) {
674        self.ssec_header = Some(ssec.headers());
675    }
676
677    pub(crate) fn set_ssec_header(&mut self, ssec_header: Option<HeaderMap>) {
678        self.ssec_header = ssec_header;
679    }
680
681    pub(crate) fn set_bucket_owner(&mut self, bucket_owner: Option<String>) {
682        self.bucket_owner = bucket_owner;
683    }
684}
685
686/// The container element for Object Lock configuration parameters.\
687/// see `put_object_lock_configuration` and `get_object_lock_configuration` API.
688///
689/// **Note**: both `mode` and `duration` settings will be effective.
690#[derive(Debug, Clone, Default)]
691pub struct ObjectLockConfig {
692    /// Valid Values: GOVERNANCE | COMPLIANCE
693    mode: String,
694    /// The date on which this Object Lock Retention will expire.
695    duration: usize,
696    /// Valid Values: Days | Years
697    duration_unit: String,
698}
699
700impl ObjectLockConfig {
701    pub fn new(duration: usize, is_day: bool, is_governance: bool) -> Self {
702        let mut obj = Self::default();
703        obj.config(duration, is_day, is_governance);
704        obj
705    }
706
707    /// - is_day: set period `Days` if true, otherwise set mode `Years`
708    /// - is_governance: set mode `GOVERNANCE` if true, otherwise set mode `COMPLIANCE`.
709    pub fn config(&mut self, duration: usize, is_day: bool, is_governance: bool) {
710        self.duration = duration;
711        self.duration_unit = (if is_day { "Days" } else { "Years" }).to_string();
712        self.mode = (if is_governance {
713            "GOVERNANCE"
714        } else {
715            "COMPLIANCE"
716        })
717        .to_string();
718    }
719
720    /// The date on which this Object Lock Retention will expire.
721    pub fn duration(&self) -> usize {
722        self.duration
723    }
724
725    /// Valid Values: GOVERNANCE | COMPLIANCE
726    pub fn mode(&self) -> &str {
727        self.mode.as_ref()
728    }
729
730    /// period, Valid Values: Days | Years | Empty String
731    pub fn period(&self) -> &str {
732        self.duration_unit.as_ref()
733    }
734}
735
736impl ToXml for ObjectLockConfig {
737    fn to_xml(&self) -> crate::error::Result<String> {
738        let mut result =
739            "<ObjectLockConfiguration><ObjectLockEnabled>Enabled</ObjectLockEnabled>".to_string();
740        if !self.mode.is_empty() && !self.duration_unit.is_empty() {
741            result += "<Rule><DefaultRetention>";
742            result += &format!("<Mode>{}</Mode>", self.mode);
743            result += &format!(
744                "<{}>{}</{}>",
745                self.duration_unit, self.duration, self.duration_unit
746            );
747            result += "</DefaultRetention></Rule>";
748        }
749        result += "</ObjectLockConfiguration>";
750        Ok(result)
751    }
752}
753
754impl FromXml for ObjectLockConfig {
755    fn from_xml(value: String) -> crate::error::Result<Self> {
756        let obj = crate::xml::de::from_str::<ObjectLockConfiguration>(&value)?;
757        if let Some(rule) = obj.rule {
758            let mode = if rule.default_retention.mode == RetentionMode::GOVERNANCE {
759                "GOVERNANCE"
760            } else {
761                "COMPLIANCE"
762            };
763            if let Some(duration) = rule.default_retention.days {
764                Ok(Self {
765                    mode: mode.to_owned(),
766                    duration,
767                    duration_unit: "Days".to_owned(),
768                })
769            } else if let Some(duration) = rule.default_retention.years {
770                Ok(Self {
771                    mode: mode.to_owned(),
772                    duration,
773                    duration_unit: "Years".to_owned(),
774                })
775            } else {
776                Ok(Default::default())
777            }
778        } else {
779            Ok(Default::default())
780        }
781    }
782}
783
784/// Custom request parameters for presigned URL
785/// ## param
786/// - bucket_name: Name of the bucket.
787/// - object_name: Object name in the bucket.
788/// - expires: Expiry in seconds; defaults to 7 days.
789/// - headers: Optional response_headers argument to specify response fields like date, size, type of file, data about server, etc.
790/// - request_date: Optional request_date argument to specify a different request date. Default is current date.
791/// - version_id: Version ID of the object.
792/// - querys: Extra query parameters for advanced usage.
793#[derive(Clone)]
794pub struct PresignedArgs {
795    pub(crate) region: Option<String>,
796    pub(crate) bucket_name: String,
797    pub(crate) object_name: String,
798    pub(crate) version_id: Option<String>,
799    pub(crate) expires: usize,
800    pub(crate) request_date: Option<UtcTime>,
801    pub(crate) headers: Option<HeaderMap>,
802    pub(crate) querys: QueryMap,
803}
804
805impl PresignedArgs {
806    pub fn new<T1: Into<String>, T2: Into<String>>(bucket_name: T1, object_name: T2) -> Self {
807        Self {
808            region: None,
809            bucket_name: bucket_name.into(),
810            object_name: object_name.into(),
811            version_id: None,
812            expires: 604800,
813            request_date: None,
814            headers: None,
815            querys: QueryMap::new(),
816        }
817    }
818
819    pub fn region<T: Into<String>>(mut self, region: T) -> Self {
820        self.region = Some(region.into());
821        self
822    }
823
824    pub fn version_id<T: Into<String>>(mut self, version_id: T) -> Self {
825        self.version_id = Some(version_id.into());
826        self
827    }
828
829    pub fn regirequest_date(mut self, request_date: UtcTime) -> Self {
830        self.request_date = Some(request_date);
831        self
832    }
833
834    pub fn expires(mut self, expires: usize) -> Self {
835        self.expires = expires;
836        self
837    }
838
839    pub fn headers(mut self, header: HeaderMap) -> Self {
840        self.headers = Some(header);
841        self
842    }
843
844    pub fn header<K>(mut self, key: K, value: &str) -> Self
845    where
846        K: IntoHeaderName,
847    {
848        let mut headers = self.headers.unwrap_or(HeaderMap::new());
849        if let Ok(value) = value.parse() {
850            headers.insert(key, value);
851        }
852        self.headers = Some(headers);
853        self
854    }
855
856    pub fn querys(mut self, querys: QueryMap) -> Self {
857        self.querys = querys;
858        self
859    }
860
861    pub fn query<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
862        self.querys.insert(key.into(), value.into());
863        self
864    }
865
866    pub fn query_string(mut self, query_str: &str) -> Self {
867        self.querys.merge_str(query_str);
868        self
869    }
870
871    pub fn apply<F>(self, apply: F) -> Self
872    where
873        F: FnOnce(Self) -> Self,
874    {
875        apply(self)
876    }
877}
878
879/// Tags
880/// - request XML of put_bucket_tags API and put_object_tags API
881/// - response XML of set_bucket_tags API and set_object_tags API.
882#[derive(Debug, Clone)]
883pub struct Tags(HashMap<String, String>);
884
885impl Tags {
886    pub fn new() -> Self {
887        Self(HashMap::new())
888    }
889
890    pub fn to_query(&self) -> String {
891        self.0
892            .iter()
893            .map(|(key, value)| format!("{}={}", urlencode(key, false), urlencode(value, false)))
894            .collect::<Vec<String>>()
895            .join("&")
896    }
897
898    pub fn insert<K: Into<String>, V: Into<String>>(&mut self, key: K, value: V) -> &mut Self {
899        self.0.insert(key.into(), value.into());
900        self
901    }
902
903    pub fn into_map(self) -> HashMap<String, String> {
904        self.0
905    }
906}
907
908impl From<HashMap<String, String>> for Tags {
909    fn from(inner: HashMap<String, String>) -> Self {
910        Self(inner)
911    }
912}
913
914impl std::ops::Deref for Tags {
915    type Target = HashMap<String, String>;
916
917    fn deref(&self) -> &Self::Target {
918        &self.0
919    }
920}
921
922impl std::ops::DerefMut for Tags {
923    fn deref_mut(&mut self) -> &mut Self::Target {
924        &mut self.0
925    }
926}
927
928impl From<Tagging> for Tags {
929    fn from(tagging: Tagging) -> Self {
930        let mut map = HashMap::new();
931        for tag in tagging.tag_set.tags {
932            map.insert(tag.key, tag.value);
933        }
934        Self(map)
935    }
936}
937
938impl FromXml for Tags {
939    fn from_xml(v: String) -> crate::error::Result<Self> {
940        crate::xml::de::from_string::<Tagging>(v)
941            .map(Into::into)
942            .map_err(Into::into)
943    }
944}
945
946impl ToXml for Tags {
947    fn to_xml(&self) -> crate::error::Result<String> {
948        let mut result = "<Tagging><TagSet>".to_string();
949        for (key, value) in &self.0 {
950            result += &format!("<Tag><Key>{}</Key><Value>{}</Value></Tag>", key, value);
951        }
952        result += "</TagSet></Tagging>";
953        return Ok(result);
954    }
955}