s3/
serde_types.rs

1#[derive(Deserialize, Debug)]
2pub struct InitiateMultipartUploadResponse {
3    #[serde(rename = "Bucket")]
4    _bucket: String,
5    #[serde(rename = "Key")]
6    pub key: String,
7    #[serde(rename = "UploadId")]
8    pub upload_id: String,
9}
10
11/// Owner information for the object
12#[derive(Deserialize, Debug, Clone)]
13pub struct Owner {
14    #[serde(rename = "DisplayName")]
15    /// Object owner's name.
16    pub display_name: Option<String>,
17    #[serde(rename = "ID")]
18    /// Object owner's ID.
19    pub id: String,
20}
21
22// <GetObjectAttributesOutput>
23//    <ETag>string</ETag>
24//    <Checksum>
25//       <ChecksumCRC32>string</ChecksumCRC32>
26//       <ChecksumCRC32C>string</ChecksumCRC32C>
27//       <ChecksumSHA1>string</ChecksumSHA1>
28//       <ChecksumSHA256>string</ChecksumSHA256>
29//    </Checksum>
30//    <ObjectParts>
31//       <IsTruncated>boolean</IsTruncated>
32//       <MaxParts>integer</MaxParts>
33//       <NextPartNumberMarker>integer</NextPartNumberMarker>
34//       <PartNumberMarker>integer</PartNumberMarker>
35//       <Part>
36//          <ChecksumCRC32>string</ChecksumCRC32>
37//          <ChecksumCRC32C>string</ChecksumCRC32C>
38//          <ChecksumSHA1>string</ChecksumSHA1>
39//          <ChecksumSHA256>string</ChecksumSHA256>
40//          <PartNumber>integer</PartNumber>
41//          <Size>long</Size>
42//       </Part>
43//       ...
44//       <PartsCount>integer</PartsCount>
45//    </ObjectParts>
46//    <StorageClass>string</StorageClass>
47//    <ObjectSize>long</ObjectSize>
48// </GetObjectAttributesOutput>
49#[derive(Deserialize, Debug)]
50pub struct GetObjectAttributesOutput {
51    #[serde(rename = "ETag")]
52    pub etag: String,
53    #[serde(rename = "Checksum")]
54    pub checksum: Checksum,
55    #[serde(rename = "ObjectParts")]
56    pub object_parts: ObjectParts,
57    #[serde(rename = "StorageClass")]
58    pub storage_class: String,
59    #[serde(rename = "ObjectSize")]
60    pub object_size: u64,
61}
62
63#[derive(Deserialize, Debug)]
64pub struct Checksum {
65    #[serde(rename = "ChecksumCRC32")]
66    pub checksum_crc32: String,
67    #[serde(rename = "ChecksumCRC32C")]
68    pub checksum_crc32c: String,
69    #[serde(rename = "ChecksumSHA1")]
70    pub checksum_sha1: String,
71    #[serde(rename = "ChecksumSHA256")]
72    pub checksum_sha256: String,
73}
74
75#[derive(Deserialize, Debug)]
76pub struct ObjectParts {
77    #[serde(rename = "IsTruncated")]
78    pub is_truncated: bool,
79    #[serde(rename = "MaxParts")]
80    pub max_parts: i32,
81    #[serde(rename = "NextPartNumberMarker")]
82    pub next_part_number_marker: i32,
83    #[serde(rename = "PartNumberMarker")]
84    pub part_number_marker: i32,
85    #[serde(rename = "Part")]
86    pub part: Vec<AttributesPart>,
87    #[serde(rename = "PartsCount")]
88    pub parts_count: u64,
89}
90
91#[derive(Deserialize, Debug)]
92pub struct AttributesPart {
93    #[serde(rename = "ChecksumCRC32")]
94    pub checksum_crc32: String,
95    #[serde(rename = "ChecksumCRC32C")]
96    pub checksum_crc32c: String,
97    #[serde(rename = "ChecksumSHA1")]
98    pub checksum_sha1: String,
99    #[serde(rename = "ChecksumSHA256")]
100    pub checksum_sha256: String,
101    #[serde(rename = "PartNumber")]
102    pub part_number: i32,
103    #[serde(rename = "Size")]
104    pub size: u64,
105}
106
107/// An individual object in a `ListBucketResult`
108#[derive(Deserialize, Debug, Clone)]
109pub struct Object {
110    #[serde(rename = "LastModified")]
111    /// Date and time the object was last modified.
112    pub last_modified: String,
113    #[serde(rename = "ETag")]
114    /// The entity tag is an MD5 hash of the object. The ETag only reflects changes to the
115    /// contents of an object, not its metadata.
116    pub e_tag: Option<String>,
117    #[serde(rename = "StorageClass")]
118    /// STANDARD | STANDARD_IA | REDUCED_REDUNDANCY | GLACIER
119    pub storage_class: Option<String>,
120    #[serde(rename = "Key")]
121    /// The object's key
122    pub key: String,
123    #[serde(rename = "Owner")]
124    /// Bucket owner
125    pub owner: Option<Owner>,
126    #[serde(rename = "Size")]
127    /// Size in bytes of the object.
128    pub size: u64,
129}
130
131/// An individual upload in a `ListMultipartUploadsResult`
132#[derive(Deserialize, Debug, Clone)]
133pub struct MultipartUpload {
134    #[serde(rename = "Initiated")]
135    /// Date and time the multipart upload was initiated
136    pub initiated: String,
137    #[serde(rename = "StorageClass")]
138    /// STANDARD | STANDARD_IA | REDUCED_REDUNDANCY | GLACIER
139    pub storage_class: String,
140    #[serde(rename = "Key")]
141    /// The object's key
142    pub key: String,
143    #[serde(rename = "Owner")]
144    /// Bucket owner
145    pub owner: Option<Owner>,
146    #[serde(rename = "UploadId")]
147    /// The identifier of the upload
148    pub id: String,
149}
150
151use std::fmt::{self};
152
153impl fmt::Display for CompleteMultipartUploadData {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        let mut parts = String::new();
156        for part in self.parts.clone() {
157            parts.push_str(&part.to_string())
158        }
159        write!(
160            f,
161            "<CompleteMultipartUpload>{}</CompleteMultipartUpload>",
162            parts
163        )
164    }
165}
166
167impl CompleteMultipartUploadData {
168    pub fn len(&self) -> usize {
169        self.to_string().len()
170    }
171
172    pub fn is_empty(&self) -> bool {
173        self.to_string().len() == 0
174    }
175}
176
177#[derive(Debug, Clone)]
178pub struct CompleteMultipartUploadData {
179    pub parts: Vec<Part>,
180}
181
182#[derive(Debug, Clone, Serialize)]
183pub struct Part {
184    #[serde(rename = "PartNumber")]
185    pub part_number: u32,
186    #[serde(rename = "ETag")]
187    pub etag: String,
188}
189
190impl fmt::Display for Part {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        write!(f, "<Part>").expect("Can't fail");
193        write!(f, "<PartNumber>{}</PartNumber>", self.part_number).expect("Can't fail");
194        write!(f, "<ETag>{}</ETag>", self.etag).expect("Can't fail");
195        write!(f, "</Part>")
196    }
197}
198
199#[derive(Deserialize, Debug, Clone)]
200pub struct BucketLocationResult {
201    #[serde(rename = "$value")]
202    pub region: String,
203}
204
205/// The parsed result of a s3 bucket listing
206///
207/// This accepts the ListBucketResult format returned for both ListObjects and ListObjectsV2
208#[derive(Deserialize, Debug, Clone)]
209pub struct ListBucketResult {
210    #[serde(rename = "Name")]
211    /// Name of the bucket.
212    pub name: String,
213    #[serde(rename = "Delimiter")]
214    /// A delimiter is a character you use to group keys.
215    pub delimiter: Option<String>,
216    #[serde(rename = "MaxKeys")]
217    /// Sets the maximum number of keys returned in the response body.
218    pub max_keys: Option<i32>,
219    #[serde(rename = "Prefix")]
220    /// Limits the response to keys that begin with the specified prefix.
221    pub prefix: Option<String>,
222    #[serde(rename = "ContinuationToken")] // for ListObjectsV2 request
223    #[serde(alias = "Marker")] // for ListObjects request
224    /// Indicates where in the bucket listing begins. It is included in the response if
225    /// it was sent with the request.
226    pub continuation_token: Option<String>,
227    #[serde(rename = "EncodingType")]
228    /// Specifies the encoding method to used
229    pub encoding_type: Option<String>,
230    #[serde(
231        default,
232        rename = "IsTruncated",
233        deserialize_with = "super::deserializer::bool_deserializer"
234    )]
235    ///  Specifies whether (true) or not (false) all of the results were returned.
236    ///  If the number of results exceeds that specified by MaxKeys, all of the results
237    ///  might not be returned.
238
239    /// When the response is truncated (that is, the IsTruncated element value in the response
240    /// is true), you can use the key name in in 'next_continuation_token' as a marker in the
241    /// subsequent request to get next set of objects. Amazon S3 lists objects in UTF-8 character
242    /// encoding in lexicographical order.
243    pub is_truncated: bool,
244    #[serde(rename = "NextContinuationToken", default)] // for ListObjectsV2 request
245    #[serde(alias = "NextMarker")] // for ListObjects request
246    pub next_continuation_token: Option<String>,
247    #[serde(rename = "Contents", default)]
248    /// Metadata about each object returned.
249    pub contents: Vec<Object>,
250    #[serde(rename = "CommonPrefixes", default)]
251    /// All of the keys rolled up into a common prefix count as a single return when
252    /// calculating the number of returns.
253    pub common_prefixes: Option<Vec<CommonPrefix>>,
254}
255
256/// The parsed result of a s3 bucket listing of uploads
257#[derive(Deserialize, Debug, Clone)]
258pub struct ListMultipartUploadsResult {
259    #[serde(rename = "Bucket")]
260    /// Name of the bucket.
261    pub name: String,
262    #[serde(rename = "NextKeyMarker")]
263    /// When the response is truncated (that is, the IsTruncated element value in the response
264    /// is true), you can use the key name in this field as a marker in the subsequent request
265    /// to get next set of objects. Amazon S3 lists objects in UTF-8 character encoding in
266    /// lexicographical order.
267    pub next_marker: Option<String>,
268    #[serde(rename = "Prefix")]
269    /// The prefix, present if the request contained a prefix too, shows the search root for the
270    /// uploads listed in this structure.
271    pub prefix: Option<String>,
272    #[serde(rename = "KeyMarker")]
273    /// Indicates where in the bucket listing begins.
274    pub marker: Option<String>,
275    #[serde(rename = "EncodingType")]
276    /// Specifies the encoding method to used
277    pub encoding_type: Option<String>,
278    #[serde(
279        rename = "IsTruncated",
280        deserialize_with = "super::deserializer::bool_deserializer"
281    )]
282    ///  Specifies whether (true) or not (false) all of the results were returned.
283    ///  If the number of results exceeds that specified by MaxKeys, all of the results
284    ///  might not be returned.
285    pub is_truncated: bool,
286    #[serde(rename = "Upload", default)]
287    /// Metadata about each upload returned.
288    pub uploads: Vec<MultipartUpload>,
289    #[serde(rename = "CommonPrefixes", default)]
290    /// All of the keys rolled up into a common prefix count as a single return when
291    /// calculating the number of returns.
292    pub common_prefixes: Option<Vec<CommonPrefix>>,
293}
294
295/// `CommonPrefix` is used to group keys
296#[derive(Deserialize, Debug, Clone)]
297pub struct CommonPrefix {
298    #[serde(rename = "Prefix")]
299    /// Keys that begin with the indicated prefix.
300    pub prefix: String,
301}
302
303// Taken from https://github.com/rusoto/rusoto
304#[derive(Deserialize, Debug, Default, Clone)]
305pub struct HeadObjectResult {
306    #[serde(rename = "AcceptRanges")]
307    /// Indicates that a range of bytes was specified.
308    pub accept_ranges: Option<String>,
309    #[serde(rename = "CacheControl")]
310    /// Specifies caching behavior along the request/reply chain.
311    pub cache_control: Option<String>,
312    #[serde(rename = "ContentDisposition")]
313    /// Specifies presentational information for the object.
314    pub content_disposition: Option<String>,
315    #[serde(rename = "ContentEncoding")]
316    /// Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field.
317    pub content_encoding: Option<String>,
318    #[serde(rename = "ContentLanguage")]
319    /// The language the content is in.
320    pub content_language: Option<String>,
321    #[serde(rename = "ContentLength")]
322    /// Size of the body in bytes.
323    pub content_length: Option<i64>,
324    #[serde(rename = "ContentType")]
325    /// A standard MIME type describing the format of the object data.
326    pub content_type: Option<String>,
327    #[serde(rename = "DeleteMarker")]
328    /// Specifies whether the object retrieved was (true) or was not (false) a Delete Marker.
329    pub delete_marker: Option<bool>,
330    #[serde(rename = "ETag")]
331    /// An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL.
332    pub e_tag: Option<String>,
333    #[serde(rename = "Expiration")]
334    /// If the object expiration is configured, the response includes this header. It includes the expiry-date and rule-id key-value pairs providing object expiration information.
335    /// The value of the rule-id is URL encoded.
336    pub expiration: Option<String>,
337    #[serde(rename = "Expires")]
338    /// The date and time at which the object is no longer cacheable.
339    pub expires: Option<String>,
340    #[serde(rename = "LastModified")]
341    /// Last modified date of the object
342    pub last_modified: Option<String>,
343    #[serde(rename = "Metadata", default)]
344    /// A map of metadata to store with the object in S3.
345    pub metadata: Option<::std::collections::HashMap<String, String>>,
346    #[serde(rename = "MissingMeta")]
347    /// This is set to the number of metadata entries not returned in x-amz-meta headers. This can happen if you create metadata using an API like SOAP that supports more flexible metadata than
348    /// the REST API. For example, using SOAP, you can create metadata whose values are not legal HTTP headers.
349    pub missing_meta: Option<i64>,
350    #[serde(rename = "ObjectLockLegalHoldStatus")]
351    /// Specifies whether a legal hold is in effect for this object. This header is only returned if the requester has the s3:GetObjectLegalHold permission.
352    /// This header is not returned if the specified version of this object has never had a legal hold applied.
353    pub object_lock_legal_hold_status: Option<String>,
354    #[serde(rename = "ObjectLockMode")]
355    /// The Object Lock mode, if any, that's in effect for this object.
356    pub object_lock_mode: Option<String>,
357    #[serde(rename = "ObjectLockRetainUntilDate")]
358    /// The date and time when the Object Lock retention period expires.
359    /// This header is only returned if the requester has the s3:GetObjectRetention permission.
360    pub object_lock_retain_until_date: Option<String>,
361    #[serde(rename = "PartsCount")]
362    /// The count of parts this object has.
363    pub parts_count: Option<i64>,
364    #[serde(rename = "ReplicationStatus")]
365    /// If your request involves a bucket that is either a source or destination in a replication rule.
366    pub replication_status: Option<String>,
367    #[serde(rename = "RequestCharged")]
368    pub request_charged: Option<String>,
369    #[serde(rename = "Restore")]
370    /// If the object is an archived object (an object whose storage class is GLACIER), the response includes this header if either the archive restoration is in progress or an archive copy is already restored.
371    /// If an archive copy is already restored, the header value indicates when Amazon S3 is scheduled to delete the object copy.
372    pub restore: Option<String>,
373    #[serde(rename = "SseCustomerAlgorithm")]
374    /// If server-side encryption with a customer-provided encryption key was requested, the response will include this header confirming the encryption algorithm used.
375    pub sse_customer_algorithm: Option<String>,
376    #[serde(rename = "SseCustomerKeyMd5")]
377    /// If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round-trip message integrity verification of the customer-provided encryption key.
378    pub sse_customer_key_md5: Option<String>,
379    #[serde(rename = "SsekmsKeyId")]
380    /// If present, specifies the ID of the AWS Key Management Service (AWS KMS) symmetric customer managed customer master key (CMK) that was used for the object.
381    pub ssekms_key_id: Option<String>,
382    #[serde(rename = "ServerSideEncryption")]
383    /// If the object is stored using server-side encryption either with an AWS KMS customer master key (CMK) or an Amazon S3-managed encryption key,
384    /// The response includes this header with the value of the server-side encryption algorithm used when storing this object in Amazon S3 (for example, AES256, aws:kms).
385    pub server_side_encryption: Option<String>,
386    #[serde(rename = "StorageClass")]
387    /// Provides storage class information of the object. Amazon S3 returns this header for all objects except for S3 Standard storage class objects.
388    pub storage_class: Option<String>,
389    #[serde(rename = "VersionId")]
390    /// Version of the object.
391    pub version_id: Option<String>,
392    #[serde(rename = "WebsiteRedirectLocation")]
393    /// If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata.
394    pub website_redirect_location: Option<String>,
395}
396
397#[derive(Deserialize, Debug)]
398pub struct AwsError {
399    #[serde(rename = "Code")]
400    pub code: String,
401    #[serde(rename = "Message")]
402    pub message: String,
403    #[serde(rename = "RequestId")]
404    pub request_id: String,
405}
406
407#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
408#[serde(rename = "CORSConfiguration")]
409pub struct CorsConfiguration {
410    #[serde(rename = "CORSRule")]
411    rules: Vec<CorsRule>,
412}
413
414impl CorsConfiguration {
415    pub fn new(rules: Vec<CorsRule>) -> Self {
416        CorsConfiguration { rules }
417    }
418}
419
420impl fmt::Display for CorsConfiguration {
421    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422        let cors = quick_xml::se::to_string(&self).map_err(|_| fmt::Error)?;
423        let preamble = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
424        let cors = format!("{}{}", preamble, cors);
425        let cors = cors.replace(
426            "<CORSConfiguration>",
427            "<CORSConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">",
428        );
429
430        write!(f, "{}", cors)
431    }
432}
433
434#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
435pub struct CorsRule {
436    #[serde(rename = "AllowedHeader")]
437    #[serde(skip_serializing_if = "Option::is_none")]
438    allowed_headers: Option<Vec<String>>,
439    #[serde(rename = "AllowedMethod")]
440    allowed_methods: Vec<String>,
441    #[serde(rename = "AllowedOrigin")]
442    allowed_origins: Vec<String>,
443    #[serde(rename = "ExposeHeader")]
444    #[serde(skip_serializing_if = "Option::is_none")]
445    expose_headers: Option<Vec<String>>,
446    #[serde(skip_serializing_if = "Option::is_none")]
447    #[serde(rename = "ID")]
448    id: Option<String>,
449    #[serde(rename = "MaxAgeSeconds")]
450    #[serde(skip_serializing_if = "Option::is_none")]
451    max_age_seconds: Option<u32>,
452}
453
454impl CorsRule {
455    pub fn new(
456        allowed_headers: Option<Vec<String>>,
457        allowed_methods: Vec<String>,
458        allowed_origins: Vec<String>,
459        expose_headers: Option<Vec<String>>,
460        id: Option<String>,
461        max_age_seconds: Option<u32>,
462    ) -> Self {
463        Self {
464            allowed_headers,
465            allowed_methods,
466            allowed_origins,
467            expose_headers,
468            id,
469            max_age_seconds,
470        }
471    }
472}
473
474#[derive(Serialize, Deserialize, Clone, Debug)]
475#[serde(rename = "LifecycleConfiguration")]
476pub struct BucketLifecycleConfiguration {
477    #[serde(rename = "Rule")]
478    pub rules: Vec<LifecycleRule>,
479}
480
481impl BucketLifecycleConfiguration {
482    pub fn new(rules: Vec<LifecycleRule>) -> Self {
483        BucketLifecycleConfiguration { rules }
484    }
485}
486
487#[derive(Serialize, Deserialize, Debug, Clone, Default)]
488pub struct LifecycleRule {
489    #[serde(
490        rename = "AbortIncompleteMultipartUpload",
491        skip_serializing_if = "Option::is_none"
492    )]
493    pub abort_incomplete_multipart_upload: Option<AbortIncompleteMultipartUpload>,
494
495    #[serde(rename = "Expiration", skip_serializing_if = "Option::is_none")]
496    pub expiration: Option<Expiration>,
497
498    #[serde(rename = "Filter", skip_serializing_if = "Option::is_none")]
499    pub filter: Option<LifecycleFilter>,
500
501    #[serde(rename = "ID", skip_serializing_if = "Option::is_none")]
502    pub id: Option<String>,
503
504    #[serde(
505        rename = "NoncurrentVersionExpiration",
506        skip_serializing_if = "Option::is_none"
507    )]
508    pub noncurrent_version_expiration: Option<NoncurrentVersionExpiration>,
509
510    #[serde(
511        rename = "NoncurrentVersionTransition",
512        skip_serializing_if = "Option::is_none"
513    )]
514    pub noncurrent_version_transition: Option<Vec<NoncurrentVersionTransition>>,
515
516    #[serde(rename = "Status")]
517    /// Valid Values: Enabled | Disabled
518    pub status: String,
519
520    #[serde(rename = "Transition", skip_serializing_if = "Option::is_none")]
521    pub transition: Option<Vec<Transition>>,
522}
523
524pub struct LifecycleRuleBuilder {
525    lifecycle_rule: LifecycleRule,
526}
527
528impl LifecycleRule {
529    pub fn builder(status: &str) -> LifecycleRuleBuilder {
530        LifecycleRuleBuilder::new(status)
531    }
532}
533
534impl LifecycleRuleBuilder {
535    pub fn new(status: &str) -> LifecycleRuleBuilder {
536        LifecycleRuleBuilder {
537            lifecycle_rule: LifecycleRule {
538                status: status.to_string(),
539                ..Default::default()
540            },
541        }
542    }
543
544    pub fn abort_incomplete_multipart_upload(
545        mut self,
546        abort_incomplete_multipart_upload: AbortIncompleteMultipartUpload,
547    ) -> LifecycleRuleBuilder {
548        self.lifecycle_rule.abort_incomplete_multipart_upload =
549            Some(abort_incomplete_multipart_upload);
550        self
551    }
552
553    pub fn expiration(mut self, expiration: Expiration) -> LifecycleRuleBuilder {
554        self.lifecycle_rule.expiration = Some(expiration);
555        self
556    }
557
558    pub fn filter(mut self, filter: LifecycleFilter) -> LifecycleRuleBuilder {
559        self.lifecycle_rule.filter = Some(filter);
560        self
561    }
562
563    pub fn id(mut self, id: &str) -> LifecycleRuleBuilder {
564        self.lifecycle_rule.id = Some(id.to_string());
565        self
566    }
567
568    pub fn noncurrent_version_expiration(
569        mut self,
570        noncurrent_version_expiration: NoncurrentVersionExpiration,
571    ) -> LifecycleRuleBuilder {
572        self.lifecycle_rule.noncurrent_version_expiration = Some(noncurrent_version_expiration);
573        self
574    }
575
576    pub fn noncurrent_version_transition(
577        mut self,
578        noncurrent_version_transition: Vec<NoncurrentVersionTransition>,
579    ) -> LifecycleRuleBuilder {
580        self.lifecycle_rule.noncurrent_version_transition = Some(noncurrent_version_transition);
581        self
582    }
583
584    pub fn transition(mut self, transition: Vec<Transition>) -> LifecycleRuleBuilder {
585        self.lifecycle_rule.transition = Some(transition);
586        self
587    }
588
589    pub fn build(self) -> LifecycleRule {
590        self.lifecycle_rule
591    }
592}
593#[derive(Serialize, Deserialize, Debug, Clone)]
594pub struct AbortIncompleteMultipartUpload {
595    #[serde(
596        rename = "DaysAfterInitiation",
597        skip_serializing_if = "Option::is_none"
598    )]
599    pub days_after_initiation: Option<i32>,
600}
601
602impl AbortIncompleteMultipartUpload {
603    pub fn new(days_after_initiation: Option<i32>) -> Self {
604        Self {
605            days_after_initiation,
606        }
607    }
608}
609
610#[derive(Serialize, Deserialize, Debug, Clone, Default)]
611pub struct Expiration {
612    /// Indicates at what date the object is to be moved or deleted. The date value must conform to the ISO 8601 format. The time is always midnight UTC.
613    #[serde(rename = "Date", skip_serializing_if = "Option::is_none")]
614    pub date: Option<String>,
615
616    #[serde(rename = "Days", skip_serializing_if = "Option::is_none")]
617    pub days: Option<u32>,
618
619    /// Indicates whether Amazon S3 will remove a delete marker with no noncurrent versions. If set to true, the delete marker will be expired; if set to false the policy takes no action. This cannot be specified with Days or Date in a Lifecycle Expiration Policy.
620    #[serde(
621        rename = "ExpiredObjectDeleteMarker",
622        skip_serializing_if = "Option::is_none"
623    )]
624    pub expired_object_delete_marker: Option<bool>,
625}
626
627impl Expiration {
628    pub fn new(
629        date: Option<String>,
630        days: Option<u32>,
631        expired_object_delete_marker: Option<bool>,
632    ) -> Self {
633        Self {
634            date,
635            days,
636            expired_object_delete_marker,
637        }
638    }
639}
640
641#[derive(Serialize, Deserialize, Debug, Clone, Default)]
642pub struct LifecycleFilter {
643    #[serde(rename = "And", skip_serializing_if = "Option::is_none")]
644    pub and: Option<And>,
645
646    #[serde(
647        rename = "ObjectSizeGreaterThan",
648        skip_serializing_if = "Option::is_none"
649    )]
650    pub object_size_greater_than: Option<i64>,
651
652    #[serde(rename = "ObjectSizeLessThan", skip_serializing_if = "Option::is_none")]
653    pub object_size_less_than: Option<i64>,
654
655    #[serde(rename = "Prefix", skip_serializing_if = "Option::is_none")]
656    pub prefix: Option<String>,
657
658    #[serde(rename = "Tag", skip_serializing_if = "Option::is_none")]
659    pub tag: Option<Tag>,
660}
661impl LifecycleFilter {
662    pub fn new(
663        and: Option<And>,
664        object_size_greater_than: Option<i64>,
665        object_size_less_than: Option<i64>,
666        prefix: Option<String>,
667        tag: Option<Tag>,
668    ) -> Self {
669        Self {
670            and,
671            object_size_greater_than,
672            object_size_less_than,
673            prefix,
674            tag,
675        }
676    }
677}
678
679#[derive(Serialize, Deserialize, Debug, Clone)]
680pub struct And {
681    #[serde(
682        rename = "ObjectSizeGreaterThan",
683        skip_serializing_if = "Option::is_none"
684    )]
685    pub object_size_greater_than: Option<i64>,
686
687    #[serde(rename = "ObjectSizeLessThan", skip_serializing_if = "Option::is_none")]
688    pub object_size_less_than: Option<i64>,
689
690    #[serde(rename = "Prefix", skip_serializing_if = "Option::is_none")]
691    pub prefix: Option<String>,
692
693    #[serde(rename = "Tag", skip_serializing_if = "Option::is_none")]
694    pub tags: Option<Vec<Tag>>,
695}
696
697impl And {
698    pub fn new(
699        object_size_greater_than: Option<i64>,
700        object_size_less_than: Option<i64>,
701        prefix: Option<String>,
702        tags: Option<Vec<Tag>>,
703    ) -> Self {
704        Self {
705            object_size_greater_than,
706            object_size_less_than,
707            prefix,
708            tags,
709        }
710    }
711}
712
713#[derive(Serialize, Deserialize, Debug, Clone)]
714pub struct Tag {
715    #[serde(rename = "Key")]
716    pub key: String,
717
718    #[serde(rename = "Value")]
719    pub value: String,
720}
721
722impl Tag {
723    pub fn new(key: &str, value: &str) -> Self {
724        Self {
725            key: key.to_string(),
726            value: value.to_string(),
727        }
728    }
729}
730
731#[derive(Serialize, Deserialize, Debug, Clone)]
732pub struct NoncurrentVersionExpiration {
733    #[serde(
734        rename = "NewerNoncurrentVersions",
735        skip_serializing_if = "Option::is_none"
736    )]
737    pub newer_noncurrent_versions: Option<i32>,
738
739    #[serde(rename = "NoncurrentDays", skip_serializing_if = "Option::is_none")]
740    pub noncurrent_days: Option<i32>,
741}
742
743impl NoncurrentVersionExpiration {
744    pub fn new(newer_noncurrent_versions: Option<i32>, noncurrent_days: Option<i32>) -> Self {
745        NoncurrentVersionExpiration {
746            newer_noncurrent_versions,
747            noncurrent_days,
748        }
749    }
750}
751
752#[derive(Serialize, Deserialize, Debug, Clone)]
753pub struct NoncurrentVersionTransition {
754    #[serde(
755        rename = "NewerNoncurrentVersions",
756        skip_serializing_if = "Option::is_none"
757    )]
758    pub newer_noncurrent_versions: Option<i32>,
759
760    #[serde(rename = "NoncurrentDays", skip_serializing_if = "Option::is_none")]
761    pub noncurrent_days: Option<i32>,
762
763    #[serde(rename = "StorageClass", skip_serializing_if = "Option::is_none")]
764    /// Valid Values: GLACIER | STANDARD_IA | ONEZONE_IA | INTELLIGENT_TIERING | DEEP_ARCHIVE | GLACIER_IR
765    pub storage_class: Option<String>,
766}
767
768impl NoncurrentVersionTransition {
769    pub fn new(
770        newer_noncurrent_versions: Option<i32>,
771        noncurrent_days: Option<i32>,
772        storage_class: Option<String>,
773    ) -> Self {
774        NoncurrentVersionTransition {
775            newer_noncurrent_versions,
776            noncurrent_days,
777            storage_class,
778        }
779    }
780}
781
782#[derive(Serialize, Deserialize, Debug, Clone)]
783pub struct Transition {
784    #[serde(rename = "Date", skip_serializing_if = "Option::is_none")]
785    pub date: Option<String>,
786
787    #[serde(rename = "Days", skip_serializing_if = "Option::is_none")]
788    pub days: Option<u32>,
789    /// Valid Values: GLACIER | STANDARD_IA | ONEZONE_IA | INTELLIGENT_TIERING | DEEP_ARCHIVE | GLACIER_IR
790    #[serde(rename = "StorageClass")]
791    pub storage_class: Option<String>,
792}
793
794impl Transition {
795    pub fn new(date: Option<String>, days: Option<u32>, storage_class: Option<String>) -> Self {
796        Transition {
797            date,
798            days,
799            storage_class,
800        }
801    }
802}
803
804#[cfg(test)]
805mod test {
806    use crate::serde_types::{
807        AbortIncompleteMultipartUpload, BucketLifecycleConfiguration, Expiration, LifecycleFilter,
808        LifecycleRule, NoncurrentVersionExpiration, NoncurrentVersionTransition, Transition,
809    };
810
811    use super::{CorsConfiguration, CorsRule};
812
813    #[test]
814    fn cors_config_serde() {
815        let rule = CorsRule {
816            allowed_headers: Some(vec!["Authorization".to_string(), "Header2".to_string()]),
817            allowed_methods: vec!["GET".to_string(), "DELETE".to_string()],
818            allowed_origins: vec!["*".to_string()],
819            expose_headers: None,
820            id: Some("lala".to_string()),
821            max_age_seconds: None,
822        };
823
824        let config = CorsConfiguration {
825            rules: vec![rule.clone(), rule],
826        };
827
828        let se = quick_xml::se::to_string(&config).unwrap();
829        assert_eq!(
830            se,
831            r#"<CORSConfiguration><CORSRule><AllowedHeader>Authorization</AllowedHeader><AllowedHeader>Header2</AllowedHeader><AllowedMethod>GET</AllowedMethod><AllowedMethod>DELETE</AllowedMethod><AllowedOrigin>*</AllowedOrigin><ID>lala</ID></CORSRule><CORSRule><AllowedHeader>Authorization</AllowedHeader><AllowedHeader>Header2</AllowedHeader><AllowedMethod>GET</AllowedMethod><AllowedMethod>DELETE</AllowedMethod><AllowedOrigin>*</AllowedOrigin><ID>lala</ID></CORSRule></CORSConfiguration>"#
832        )
833    }
834
835    #[test]
836    fn lifecycle_config_serde() {
837        let rule = LifecycleRule {
838            abort_incomplete_multipart_upload: Some(AbortIncompleteMultipartUpload {
839                days_after_initiation: Some(30),
840            }),
841            expiration: Some(Expiration {
842                date: Some("2024-06-017".to_string()),
843                days: Some(30),
844                expired_object_delete_marker: Some(true),
845            }),
846            filter: Some(LifecycleFilter {
847                and: None,
848                object_size_greater_than: Some(10),
849                object_size_less_than: Some(50),
850                prefix: None,
851                tag: None,
852            }),
853            id: Some("lala".to_string()),
854            noncurrent_version_expiration: Some(NoncurrentVersionExpiration {
855                newer_noncurrent_versions: Some(30),
856                noncurrent_days: Some(30),
857            }),
858            noncurrent_version_transition: Some(vec![NoncurrentVersionTransition {
859                newer_noncurrent_versions: Some(30),
860                noncurrent_days: Some(30),
861                storage_class: Some("GLACIER".to_string()),
862            }]),
863            status: "Enabled".to_string(),
864            transition: Some(vec![Transition {
865                date: Some("2024-06-017".to_string()),
866                days: Some(30),
867                storage_class: Some("GLACIER".to_string()),
868            }]),
869        };
870
871        let config = BucketLifecycleConfiguration { rules: vec![rule] };
872
873        let se = quick_xml::se::to_string(&config).unwrap();
874        assert_eq!(
875            se,
876            r#"<LifecycleConfiguration><Rule><AbortIncompleteMultipartUpload><DaysAfterInitiation>30</DaysAfterInitiation></AbortIncompleteMultipartUpload><Expiration><Date>2024-06-017</Date><Days>30</Days><ExpiredObjectDeleteMarker>true</ExpiredObjectDeleteMarker></Expiration><Filter><ObjectSizeGreaterThan>10</ObjectSizeGreaterThan><ObjectSizeLessThan>50</ObjectSizeLessThan></Filter><ID>lala</ID><NoncurrentVersionExpiration><NewerNoncurrentVersions>30</NewerNoncurrentVersions><NoncurrentDays>30</NoncurrentDays></NoncurrentVersionExpiration><NoncurrentVersionTransition><NewerNoncurrentVersions>30</NewerNoncurrentVersions><NoncurrentDays>30</NoncurrentDays><StorageClass>GLACIER</StorageClass></NoncurrentVersionTransition><Status>Enabled</Status><Transition><Date>2024-06-017</Date><Days>30</Days><StorageClass>GLACIER</StorageClass></Transition></Rule></LifecycleConfiguration>"#
877        )
878    }
879}