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#[derive(Deserialize, Debug, Clone)]
13pub struct Owner {
14 #[serde(rename = "DisplayName")]
15 pub display_name: Option<String>,
17 #[serde(rename = "ID")]
18 pub id: String,
20}
21
22#[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#[derive(Deserialize, Debug, Clone)]
109pub struct Object {
110 #[serde(rename = "LastModified")]
111 pub last_modified: String,
113 #[serde(rename = "ETag")]
114 pub e_tag: Option<String>,
117 #[serde(rename = "StorageClass")]
118 pub storage_class: Option<String>,
120 #[serde(rename = "Key")]
121 pub key: String,
123 #[serde(rename = "Owner")]
124 pub owner: Option<Owner>,
126 #[serde(rename = "Size")]
127 pub size: u64,
129}
130
131#[derive(Deserialize, Debug, Clone)]
133pub struct MultipartUpload {
134 #[serde(rename = "Initiated")]
135 pub initiated: String,
137 #[serde(rename = "StorageClass")]
138 pub storage_class: String,
140 #[serde(rename = "Key")]
141 pub key: String,
143 #[serde(rename = "Owner")]
144 pub owner: Option<Owner>,
146 #[serde(rename = "UploadId")]
147 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#[derive(Deserialize, Debug, Clone)]
209pub struct ListBucketResult {
210 #[serde(rename = "Name")]
211 pub name: String,
213 #[serde(rename = "Delimiter")]
214 pub delimiter: Option<String>,
216 #[serde(rename = "MaxKeys")]
217 pub max_keys: Option<i32>,
219 #[serde(rename = "Prefix")]
220 pub prefix: Option<String>,
222 #[serde(rename = "ContinuationToken")] #[serde(alias = "Marker")] pub continuation_token: Option<String>,
227 #[serde(rename = "EncodingType")]
228 pub encoding_type: Option<String>,
230 #[serde(
231 default,
232 rename = "IsTruncated",
233 deserialize_with = "super::deserializer::bool_deserializer"
234 )]
235 pub is_truncated: bool,
244 #[serde(rename = "NextContinuationToken", default)] #[serde(alias = "NextMarker")] pub next_continuation_token: Option<String>,
247 #[serde(rename = "Contents", default)]
248 pub contents: Vec<Object>,
250 #[serde(rename = "CommonPrefixes", default)]
251 pub common_prefixes: Option<Vec<CommonPrefix>>,
254}
255
256#[derive(Deserialize, Debug, Clone)]
258pub struct ListMultipartUploadsResult {
259 #[serde(rename = "Bucket")]
260 pub name: String,
262 #[serde(rename = "NextKeyMarker")]
263 pub next_marker: Option<String>,
268 #[serde(rename = "Prefix")]
269 pub prefix: Option<String>,
272 #[serde(rename = "KeyMarker")]
273 pub marker: Option<String>,
275 #[serde(rename = "EncodingType")]
276 pub encoding_type: Option<String>,
278 #[serde(
279 rename = "IsTruncated",
280 deserialize_with = "super::deserializer::bool_deserializer"
281 )]
282 pub is_truncated: bool,
286 #[serde(rename = "Upload", default)]
287 pub uploads: Vec<MultipartUpload>,
289 #[serde(rename = "CommonPrefixes", default)]
290 pub common_prefixes: Option<Vec<CommonPrefix>>,
293}
294
295#[derive(Deserialize, Debug, Clone)]
297pub struct CommonPrefix {
298 #[serde(rename = "Prefix")]
299 pub prefix: String,
301}
302
303#[derive(Deserialize, Debug, Default, Clone)]
305pub struct HeadObjectResult {
306 #[serde(rename = "AcceptRanges")]
307 pub accept_ranges: Option<String>,
309 #[serde(rename = "CacheControl")]
310 pub cache_control: Option<String>,
312 #[serde(rename = "ContentDisposition")]
313 pub content_disposition: Option<String>,
315 #[serde(rename = "ContentEncoding")]
316 pub content_encoding: Option<String>,
318 #[serde(rename = "ContentLanguage")]
319 pub content_language: Option<String>,
321 #[serde(rename = "ContentLength")]
322 pub content_length: Option<i64>,
324 #[serde(rename = "ContentType")]
325 pub content_type: Option<String>,
327 #[serde(rename = "DeleteMarker")]
328 pub delete_marker: Option<bool>,
330 #[serde(rename = "ETag")]
331 pub e_tag: Option<String>,
333 #[serde(rename = "Expiration")]
334 pub expiration: Option<String>,
337 #[serde(rename = "Expires")]
338 pub expires: Option<String>,
340 #[serde(rename = "LastModified")]
341 pub last_modified: Option<String>,
343 #[serde(rename = "Metadata", default)]
344 pub metadata: Option<::std::collections::HashMap<String, String>>,
346 #[serde(rename = "MissingMeta")]
347 pub missing_meta: Option<i64>,
350 #[serde(rename = "ObjectLockLegalHoldStatus")]
351 pub object_lock_legal_hold_status: Option<String>,
354 #[serde(rename = "ObjectLockMode")]
355 pub object_lock_mode: Option<String>,
357 #[serde(rename = "ObjectLockRetainUntilDate")]
358 pub object_lock_retain_until_date: Option<String>,
361 #[serde(rename = "PartsCount")]
362 pub parts_count: Option<i64>,
364 #[serde(rename = "ReplicationStatus")]
365 pub replication_status: Option<String>,
367 #[serde(rename = "RequestCharged")]
368 pub request_charged: Option<String>,
369 #[serde(rename = "Restore")]
370 pub restore: Option<String>,
373 #[serde(rename = "SseCustomerAlgorithm")]
374 pub sse_customer_algorithm: Option<String>,
376 #[serde(rename = "SseCustomerKeyMd5")]
377 pub sse_customer_key_md5: Option<String>,
379 #[serde(rename = "SsekmsKeyId")]
380 pub ssekms_key_id: Option<String>,
382 #[serde(rename = "ServerSideEncryption")]
383 pub server_side_encryption: Option<String>,
386 #[serde(rename = "StorageClass")]
387 pub storage_class: Option<String>,
389 #[serde(rename = "VersionId")]
390 pub version_id: Option<String>,
392 #[serde(rename = "WebsiteRedirectLocation")]
393 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(Debug, Clone)]
409pub struct ObjectIdentifier {
410 pub key: String,
412 pub version_id: Option<String>,
414}
415
416impl ObjectIdentifier {
417 pub fn new(key: impl Into<String>) -> Self {
418 Self {
419 key: key.into(),
420 version_id: None,
421 }
422 }
423
424 pub fn with_version(key: impl Into<String>, version_id: impl Into<String>) -> Self {
425 Self {
426 key: key.into(),
427 version_id: Some(version_id.into()),
428 }
429 }
430}
431
432#[derive(Debug, Clone)]
434pub struct DeleteObjectsRequest {
435 pub objects: Vec<ObjectIdentifier>,
436 pub quiet: bool,
438}
439
440impl fmt::Display for DeleteObjectsRequest {
441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 write!(
443 f,
444 r#"<Delete xmlns="http://s3.amazonaws.com/doc/2006-03-01/">"#
445 )
446 .expect("Can't fail");
447 if self.quiet {
448 write!(f, "<Quiet>true</Quiet>").expect("Can't fail");
449 }
450 for obj in &self.objects {
451 let escaped_key = quick_xml::escape::escape(&obj.key);
452 write!(f, "<Object><Key>{}</Key>", escaped_key).expect("Can't fail");
453 if let Some(ref vid) = obj.version_id {
454 let escaped_vid = quick_xml::escape::escape(vid);
455 write!(f, "<VersionId>{}</VersionId>", escaped_vid).expect("Can't fail");
456 }
457 write!(f, "</Object>").expect("Can't fail");
458 }
459 write!(f, "</Delete>")
460 }
461}
462
463impl DeleteObjectsRequest {
464 pub fn len(&self) -> usize {
465 self.to_string().len()
466 }
467
468 pub fn is_empty(&self) -> bool {
469 self.objects.is_empty()
470 }
471}
472
473#[derive(Deserialize, Debug, Clone)]
475pub struct DeletedObject {
476 #[serde(rename = "Key")]
477 pub key: String,
478 #[serde(rename = "VersionId")]
479 pub version_id: Option<String>,
480 #[serde(rename = "DeleteMarker")]
481 pub delete_marker: Option<bool>,
482 #[serde(rename = "DeleteMarkerVersionId")]
483 pub delete_marker_version_id: Option<String>,
484}
485
486#[derive(Deserialize, Debug, Clone)]
488pub struct DeleteError {
489 #[serde(rename = "Key")]
490 pub key: String,
491 #[serde(rename = "Code")]
492 pub code: String,
493 #[serde(rename = "Message")]
494 pub message: String,
495 #[serde(rename = "VersionId")]
496 pub version_id: Option<String>,
497}
498
499#[derive(Deserialize, Debug, Clone)]
501pub struct DeleteObjectsResult {
502 #[serde(rename = "Deleted", default)]
503 pub deleted: Vec<DeletedObject>,
504 #[serde(rename = "Error", default)]
505 pub errors: Vec<DeleteError>,
506}
507
508#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
509#[serde(rename = "CORSConfiguration")]
510pub struct CorsConfiguration {
511 #[serde(rename = "CORSRule")]
512 rules: Vec<CorsRule>,
513}
514
515impl CorsConfiguration {
516 pub fn new(rules: Vec<CorsRule>) -> Self {
517 CorsConfiguration { rules }
518 }
519}
520
521impl fmt::Display for CorsConfiguration {
522 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523 let cors = quick_xml::se::to_string(&self).map_err(|_| fmt::Error)?;
524 let preamble = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
525 let cors = format!("{}{}", preamble, cors);
526 let cors = cors.replace(
527 "<CORSConfiguration>",
528 "<CORSConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">",
529 );
530
531 write!(f, "{}", cors)
532 }
533}
534
535#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
536pub struct CorsRule {
537 #[serde(rename = "AllowedHeader")]
538 #[serde(skip_serializing_if = "Option::is_none")]
539 allowed_headers: Option<Vec<String>>,
540 #[serde(rename = "AllowedMethod")]
541 allowed_methods: Vec<String>,
542 #[serde(rename = "AllowedOrigin")]
543 allowed_origins: Vec<String>,
544 #[serde(rename = "ExposeHeader")]
545 #[serde(skip_serializing_if = "Option::is_none")]
546 expose_headers: Option<Vec<String>>,
547 #[serde(skip_serializing_if = "Option::is_none")]
548 #[serde(rename = "ID")]
549 id: Option<String>,
550 #[serde(rename = "MaxAgeSeconds")]
551 #[serde(skip_serializing_if = "Option::is_none")]
552 max_age_seconds: Option<u32>,
553}
554
555impl CorsRule {
556 pub fn new(
557 allowed_headers: Option<Vec<String>>,
558 allowed_methods: Vec<String>,
559 allowed_origins: Vec<String>,
560 expose_headers: Option<Vec<String>>,
561 id: Option<String>,
562 max_age_seconds: Option<u32>,
563 ) -> Self {
564 Self {
565 allowed_headers,
566 allowed_methods,
567 allowed_origins,
568 expose_headers,
569 id,
570 max_age_seconds,
571 }
572 }
573}
574
575#[derive(Serialize, Deserialize, Clone, Debug)]
576#[serde(rename = "LifecycleConfiguration")]
577pub struct BucketLifecycleConfiguration {
578 #[serde(rename = "Rule")]
579 pub rules: Vec<LifecycleRule>,
580}
581
582impl BucketLifecycleConfiguration {
583 pub fn new(rules: Vec<LifecycleRule>) -> Self {
584 BucketLifecycleConfiguration { rules }
585 }
586}
587
588#[derive(Serialize, Deserialize, Debug, Clone, Default)]
589pub struct LifecycleRule {
590 #[serde(
591 rename = "AbortIncompleteMultipartUpload",
592 skip_serializing_if = "Option::is_none"
593 )]
594 pub abort_incomplete_multipart_upload: Option<AbortIncompleteMultipartUpload>,
595
596 #[serde(rename = "Expiration", skip_serializing_if = "Option::is_none")]
597 pub expiration: Option<Expiration>,
598
599 #[serde(rename = "Filter", skip_serializing_if = "Option::is_none")]
600 pub filter: Option<LifecycleFilter>,
601
602 #[serde(rename = "ID", skip_serializing_if = "Option::is_none")]
603 pub id: Option<String>,
604
605 #[serde(
606 rename = "NoncurrentVersionExpiration",
607 skip_serializing_if = "Option::is_none"
608 )]
609 pub noncurrent_version_expiration: Option<NoncurrentVersionExpiration>,
610
611 #[serde(
612 rename = "NoncurrentVersionTransition",
613 skip_serializing_if = "Option::is_none"
614 )]
615 pub noncurrent_version_transition: Option<Vec<NoncurrentVersionTransition>>,
616
617 #[serde(rename = "Status")]
618 pub status: String,
620
621 #[serde(rename = "Transition", skip_serializing_if = "Option::is_none")]
622 pub transition: Option<Vec<Transition>>,
623}
624
625pub struct LifecycleRuleBuilder {
626 lifecycle_rule: LifecycleRule,
627}
628
629impl LifecycleRule {
630 pub fn builder(status: &str) -> LifecycleRuleBuilder {
631 LifecycleRuleBuilder::new(status)
632 }
633}
634
635impl LifecycleRuleBuilder {
636 pub fn new(status: &str) -> LifecycleRuleBuilder {
637 LifecycleRuleBuilder {
638 lifecycle_rule: LifecycleRule {
639 status: status.to_string(),
640 ..Default::default()
641 },
642 }
643 }
644
645 pub fn abort_incomplete_multipart_upload(
646 mut self,
647 abort_incomplete_multipart_upload: AbortIncompleteMultipartUpload,
648 ) -> LifecycleRuleBuilder {
649 self.lifecycle_rule.abort_incomplete_multipart_upload =
650 Some(abort_incomplete_multipart_upload);
651 self
652 }
653
654 pub fn expiration(mut self, expiration: Expiration) -> LifecycleRuleBuilder {
655 self.lifecycle_rule.expiration = Some(expiration);
656 self
657 }
658
659 pub fn filter(mut self, filter: LifecycleFilter) -> LifecycleRuleBuilder {
660 self.lifecycle_rule.filter = Some(filter);
661 self
662 }
663
664 pub fn id(mut self, id: &str) -> LifecycleRuleBuilder {
665 self.lifecycle_rule.id = Some(id.to_string());
666 self
667 }
668
669 pub fn noncurrent_version_expiration(
670 mut self,
671 noncurrent_version_expiration: NoncurrentVersionExpiration,
672 ) -> LifecycleRuleBuilder {
673 self.lifecycle_rule.noncurrent_version_expiration = Some(noncurrent_version_expiration);
674 self
675 }
676
677 pub fn noncurrent_version_transition(
678 mut self,
679 noncurrent_version_transition: Vec<NoncurrentVersionTransition>,
680 ) -> LifecycleRuleBuilder {
681 self.lifecycle_rule.noncurrent_version_transition = Some(noncurrent_version_transition);
682 self
683 }
684
685 pub fn transition(mut self, transition: Vec<Transition>) -> LifecycleRuleBuilder {
686 self.lifecycle_rule.transition = Some(transition);
687 self
688 }
689
690 pub fn build(self) -> LifecycleRule {
691 self.lifecycle_rule
692 }
693}
694#[derive(Serialize, Deserialize, Debug, Clone)]
695pub struct AbortIncompleteMultipartUpload {
696 #[serde(
697 rename = "DaysAfterInitiation",
698 skip_serializing_if = "Option::is_none"
699 )]
700 pub days_after_initiation: Option<i32>,
701}
702
703impl AbortIncompleteMultipartUpload {
704 pub fn new(days_after_initiation: Option<i32>) -> Self {
705 Self {
706 days_after_initiation,
707 }
708 }
709}
710
711#[derive(Serialize, Deserialize, Debug, Clone, Default)]
712pub struct Expiration {
713 #[serde(rename = "Date", skip_serializing_if = "Option::is_none")]
715 pub date: Option<String>,
716
717 #[serde(rename = "Days", skip_serializing_if = "Option::is_none")]
718 pub days: Option<u32>,
719
720 #[serde(
722 rename = "ExpiredObjectDeleteMarker",
723 skip_serializing_if = "Option::is_none"
724 )]
725 pub expired_object_delete_marker: Option<bool>,
726}
727
728impl Expiration {
729 pub fn new(
730 date: Option<String>,
731 days: Option<u32>,
732 expired_object_delete_marker: Option<bool>,
733 ) -> Self {
734 Self {
735 date,
736 days,
737 expired_object_delete_marker,
738 }
739 }
740}
741
742#[derive(Serialize, Deserialize, Debug, Clone, Default)]
743pub struct LifecycleFilter {
744 #[serde(rename = "And", skip_serializing_if = "Option::is_none")]
745 pub and: Option<And>,
746
747 #[serde(
748 rename = "ObjectSizeGreaterThan",
749 skip_serializing_if = "Option::is_none"
750 )]
751 pub object_size_greater_than: Option<i64>,
752
753 #[serde(rename = "ObjectSizeLessThan", skip_serializing_if = "Option::is_none")]
754 pub object_size_less_than: Option<i64>,
755
756 #[serde(rename = "Prefix", skip_serializing_if = "Option::is_none")]
757 pub prefix: Option<String>,
758
759 #[serde(rename = "Tag", skip_serializing_if = "Option::is_none")]
760 pub tag: Option<Tag>,
761}
762impl LifecycleFilter {
763 pub fn new(
764 and: Option<And>,
765 object_size_greater_than: Option<i64>,
766 object_size_less_than: Option<i64>,
767 prefix: Option<String>,
768 tag: Option<Tag>,
769 ) -> Self {
770 Self {
771 and,
772 object_size_greater_than,
773 object_size_less_than,
774 prefix,
775 tag,
776 }
777 }
778}
779
780#[derive(Serialize, Deserialize, Debug, Clone)]
781pub struct And {
782 #[serde(
783 rename = "ObjectSizeGreaterThan",
784 skip_serializing_if = "Option::is_none"
785 )]
786 pub object_size_greater_than: Option<i64>,
787
788 #[serde(rename = "ObjectSizeLessThan", skip_serializing_if = "Option::is_none")]
789 pub object_size_less_than: Option<i64>,
790
791 #[serde(rename = "Prefix", skip_serializing_if = "Option::is_none")]
792 pub prefix: Option<String>,
793
794 #[serde(rename = "Tag", skip_serializing_if = "Option::is_none")]
795 pub tags: Option<Vec<Tag>>,
796}
797
798impl And {
799 pub fn new(
800 object_size_greater_than: Option<i64>,
801 object_size_less_than: Option<i64>,
802 prefix: Option<String>,
803 tags: Option<Vec<Tag>>,
804 ) -> Self {
805 Self {
806 object_size_greater_than,
807 object_size_less_than,
808 prefix,
809 tags,
810 }
811 }
812}
813
814#[derive(Serialize, Deserialize, Debug, Clone)]
815pub struct Tag {
816 #[serde(rename = "Key")]
817 pub key: String,
818
819 #[serde(rename = "Value")]
820 pub value: String,
821}
822
823impl Tag {
824 pub fn new(key: &str, value: &str) -> Self {
825 Self {
826 key: key.to_string(),
827 value: value.to_string(),
828 }
829 }
830}
831
832#[derive(Serialize, Deserialize, Debug, Clone)]
833pub struct NoncurrentVersionExpiration {
834 #[serde(
835 rename = "NewerNoncurrentVersions",
836 skip_serializing_if = "Option::is_none"
837 )]
838 pub newer_noncurrent_versions: Option<i32>,
839
840 #[serde(rename = "NoncurrentDays", skip_serializing_if = "Option::is_none")]
841 pub noncurrent_days: Option<i32>,
842}
843
844impl NoncurrentVersionExpiration {
845 pub fn new(newer_noncurrent_versions: Option<i32>, noncurrent_days: Option<i32>) -> Self {
846 NoncurrentVersionExpiration {
847 newer_noncurrent_versions,
848 noncurrent_days,
849 }
850 }
851}
852
853#[derive(Serialize, Deserialize, Debug, Clone)]
854pub struct NoncurrentVersionTransition {
855 #[serde(
856 rename = "NewerNoncurrentVersions",
857 skip_serializing_if = "Option::is_none"
858 )]
859 pub newer_noncurrent_versions: Option<i32>,
860
861 #[serde(rename = "NoncurrentDays", skip_serializing_if = "Option::is_none")]
862 pub noncurrent_days: Option<i32>,
863
864 #[serde(rename = "StorageClass", skip_serializing_if = "Option::is_none")]
865 pub storage_class: Option<String>,
867}
868
869impl NoncurrentVersionTransition {
870 pub fn new(
871 newer_noncurrent_versions: Option<i32>,
872 noncurrent_days: Option<i32>,
873 storage_class: Option<String>,
874 ) -> Self {
875 NoncurrentVersionTransition {
876 newer_noncurrent_versions,
877 noncurrent_days,
878 storage_class,
879 }
880 }
881}
882
883#[derive(Serialize, Deserialize, Debug, Clone)]
884pub struct Transition {
885 #[serde(rename = "Date", skip_serializing_if = "Option::is_none")]
886 pub date: Option<String>,
887
888 #[serde(rename = "Days", skip_serializing_if = "Option::is_none")]
889 pub days: Option<u32>,
890 #[serde(rename = "StorageClass")]
892 pub storage_class: Option<String>,
893}
894
895impl Transition {
896 pub fn new(date: Option<String>, days: Option<u32>, storage_class: Option<String>) -> Self {
897 Transition {
898 date,
899 days,
900 storage_class,
901 }
902 }
903}
904
905#[cfg(test)]
906mod test {
907 use crate::serde_types::{
908 AbortIncompleteMultipartUpload, BucketLifecycleConfiguration, Expiration, LifecycleFilter,
909 LifecycleRule, NoncurrentVersionExpiration, NoncurrentVersionTransition, Transition,
910 };
911
912 use super::{
913 CorsConfiguration, CorsRule, DeleteObjectsRequest, DeleteObjectsResult, ObjectIdentifier,
914 };
915
916 #[test]
917 fn cors_config_serde() {
918 let rule = CorsRule {
919 allowed_headers: Some(vec!["Authorization".to_string(), "Header2".to_string()]),
920 allowed_methods: vec!["GET".to_string(), "DELETE".to_string()],
921 allowed_origins: vec!["*".to_string()],
922 expose_headers: None,
923 id: Some("lala".to_string()),
924 max_age_seconds: None,
925 };
926
927 let config = CorsConfiguration {
928 rules: vec![rule.clone(), rule],
929 };
930
931 let se = quick_xml::se::to_string(&config).unwrap();
932 assert_eq!(
933 se,
934 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>"#
935 )
936 }
937
938 #[test]
939 fn lifecycle_config_serde() {
940 let rule = LifecycleRule {
941 abort_incomplete_multipart_upload: Some(AbortIncompleteMultipartUpload {
942 days_after_initiation: Some(30),
943 }),
944 expiration: Some(Expiration {
945 date: Some("2024-06-017".to_string()),
946 days: Some(30),
947 expired_object_delete_marker: Some(true),
948 }),
949 filter: Some(LifecycleFilter {
950 and: None,
951 object_size_greater_than: Some(10),
952 object_size_less_than: Some(50),
953 prefix: None,
954 tag: None,
955 }),
956 id: Some("lala".to_string()),
957 noncurrent_version_expiration: Some(NoncurrentVersionExpiration {
958 newer_noncurrent_versions: Some(30),
959 noncurrent_days: Some(30),
960 }),
961 noncurrent_version_transition: Some(vec![NoncurrentVersionTransition {
962 newer_noncurrent_versions: Some(30),
963 noncurrent_days: Some(30),
964 storage_class: Some("GLACIER".to_string()),
965 }]),
966 status: "Enabled".to_string(),
967 transition: Some(vec![Transition {
968 date: Some("2024-06-017".to_string()),
969 days: Some(30),
970 storage_class: Some("GLACIER".to_string()),
971 }]),
972 };
973
974 let config = BucketLifecycleConfiguration { rules: vec![rule] };
975
976 let se = quick_xml::se::to_string(&config).unwrap();
977 assert_eq!(
978 se,
979 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>"#
980 )
981 }
982
983 #[test]
984 fn delete_objects_request_serialize() {
985 let request = DeleteObjectsRequest {
986 objects: vec![
987 ObjectIdentifier::new("file1.txt"),
988 ObjectIdentifier::new("file2.txt"),
989 ObjectIdentifier::with_version("file3.txt", "v1"),
990 ],
991 quiet: false,
992 };
993
994 let se = request.to_string();
995 assert_eq!(
996 se,
997 r#"<Delete xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Object><Key>file1.txt</Key></Object><Object><Key>file2.txt</Key></Object><Object><Key>file3.txt</Key><VersionId>v1</VersionId></Object></Delete>"#
998 )
999 }
1000
1001 #[test]
1002 fn delete_objects_request_serialize_quiet() {
1003 let request = DeleteObjectsRequest {
1004 objects: vec![ObjectIdentifier::new("file1.txt")],
1005 quiet: true,
1006 };
1007
1008 let se = request.to_string();
1009 assert_eq!(
1010 se,
1011 r#"<Delete xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Quiet>true</Quiet><Object><Key>file1.txt</Key></Object></Delete>"#
1012 )
1013 }
1014
1015 #[test]
1016 fn delete_objects_request_xml_escaping() {
1017 let request = DeleteObjectsRequest {
1018 objects: vec![ObjectIdentifier::new("file&name<>.txt")],
1019 quiet: false,
1020 };
1021
1022 let se = request.to_string();
1023 assert_eq!(
1024 se,
1025 r#"<Delete xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Object><Key>file&name<>.txt</Key></Object></Delete>"#
1026 )
1027 }
1028
1029 #[test]
1030 fn delete_objects_request_len() {
1031 let request = DeleteObjectsRequest {
1032 objects: vec![ObjectIdentifier::new("file1.txt")],
1033 quiet: false,
1034 };
1035
1036 assert_eq!(request.len(), request.to_string().len());
1037 assert!(!request.is_empty());
1038 }
1039
1040 #[test]
1041 fn delete_objects_result_deserialize() {
1042 let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
1043<DeleteResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
1044 <Deleted>
1045 <Key>file1.txt</Key>
1046 </Deleted>
1047 <Deleted>
1048 <Key>file2.txt</Key>
1049 <DeleteMarker>true</DeleteMarker>
1050 <DeleteMarkerVersionId>abc123</DeleteMarkerVersionId>
1051 </Deleted>
1052 <Error>
1053 <Key>file3.txt</Key>
1054 <Code>AccessDenied</Code>
1055 <Message>Access Denied</Message>
1056 </Error>
1057</DeleteResult>"#;
1058
1059 let result: DeleteObjectsResult = quick_xml::de::from_str(xml).unwrap();
1060 assert_eq!(result.deleted.len(), 2);
1061 assert_eq!(result.deleted[0].key, "file1.txt");
1062 assert_eq!(result.deleted[1].key, "file2.txt");
1063 assert_eq!(result.deleted[1].delete_marker, Some(true));
1064 assert_eq!(
1065 result.deleted[1].delete_marker_version_id,
1066 Some("abc123".to_string())
1067 );
1068 assert_eq!(result.errors.len(), 1);
1069 assert_eq!(result.errors[0].key, "file3.txt");
1070 assert_eq!(result.errors[0].code, "AccessDenied");
1071 assert_eq!(result.errors[0].message, "Access Denied");
1072 }
1073
1074 #[test]
1075 fn delete_objects_result_deserialize_empty() {
1076 let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
1077<DeleteResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
1078</DeleteResult>"#;
1079
1080 let result: DeleteObjectsResult = quick_xml::de::from_str(xml).unwrap();
1081 assert_eq!(result.deleted.len(), 0);
1082 assert_eq!(result.errors.len(), 0);
1083 }
1084}