1use std::collections::HashMap;
17
18use serde::{Deserialize, Serialize};
19
20use crate::attribute_value::AttributeValue;
21
22#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub enum KeyType {
31 #[serde(rename = "HASH")]
33 Hash,
34 #[serde(rename = "RANGE")]
36 Range,
37}
38
39impl KeyType {
40 #[must_use]
42 pub fn as_str(&self) -> &'static str {
43 match self {
44 Self::Hash => "HASH",
45 Self::Range => "RANGE",
46 }
47 }
48}
49
50impl std::fmt::Display for KeyType {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 f.write_str(self.as_str())
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Hash)]
62pub enum ScalarAttributeType {
63 S,
65 N,
67 B,
69 Unknown(String),
71}
72
73impl ScalarAttributeType {
74 #[must_use]
76 pub fn as_str(&self) -> &str {
77 match self {
78 Self::S => "S",
79 Self::N => "N",
80 Self::B => "B",
81 Self::Unknown(s) => s.as_str(),
82 }
83 }
84
85 #[must_use]
87 pub fn is_valid_key_type(&self) -> bool {
88 matches!(self, Self::S | Self::N | Self::B)
89 }
90}
91
92impl Serialize for ScalarAttributeType {
93 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
94 serializer.serialize_str(self.as_str())
95 }
96}
97
98impl<'de> Deserialize<'de> for ScalarAttributeType {
99 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
100 let s = String::deserialize(deserializer)?;
101 match s.as_str() {
102 "S" => Ok(Self::S),
103 "N" => Ok(Self::N),
104 "B" => Ok(Self::B),
105 _ => Ok(Self::Unknown(s)),
106 }
107 }
108}
109
110impl std::fmt::Display for ScalarAttributeType {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 f.write_str(self.as_str())
113 }
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
118pub enum TableStatus {
119 #[serde(rename = "CREATING")]
121 Creating,
122 #[serde(rename = "ACTIVE")]
124 Active,
125 #[serde(rename = "DELETING")]
127 Deleting,
128 #[serde(rename = "UPDATING")]
130 Updating,
131 #[serde(rename = "ARCHIVING")]
133 Archiving,
134 #[serde(rename = "ARCHIVED")]
136 Archived,
137 #[serde(rename = "INACCESSIBLE_ENCRYPTION_CREDENTIALS")]
139 InaccessibleEncryptionCredentials,
140}
141
142impl TableStatus {
143 #[must_use]
145 pub fn as_str(&self) -> &'static str {
146 match self {
147 Self::Creating => "CREATING",
148 Self::Active => "ACTIVE",
149 Self::Deleting => "DELETING",
150 Self::Updating => "UPDATING",
151 Self::Archiving => "ARCHIVING",
152 Self::Archived => "ARCHIVED",
153 Self::InaccessibleEncryptionCredentials => "INACCESSIBLE_ENCRYPTION_CREDENTIALS",
154 }
155 }
156}
157
158impl std::fmt::Display for TableStatus {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 f.write_str(self.as_str())
161 }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
166pub enum BillingMode {
167 Provisioned,
169 #[default]
171 PayPerRequest,
172 Unknown(String),
174}
175
176impl BillingMode {
177 #[must_use]
179 pub fn as_str(&self) -> &str {
180 match self {
181 Self::Provisioned => "PROVISIONED",
182 Self::PayPerRequest => "PAY_PER_REQUEST",
183 Self::Unknown(s) => s.as_str(),
184 }
185 }
186}
187
188impl Serialize for BillingMode {
189 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
190 serializer.serialize_str(self.as_str())
191 }
192}
193
194impl<'de> Deserialize<'de> for BillingMode {
195 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
196 let s = String::deserialize(deserializer)?;
197 match s.as_str() {
198 "PROVISIONED" => Ok(Self::Provisioned),
199 "PAY_PER_REQUEST" => Ok(Self::PayPerRequest),
200 _ => Ok(Self::Unknown(s)),
201 }
202 }
203}
204
205impl std::fmt::Display for BillingMode {
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 f.write_str(self.as_str())
208 }
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
213pub enum ProjectionType {
214 #[default]
216 #[serde(rename = "ALL")]
217 All,
218 #[serde(rename = "KEYS_ONLY")]
220 KeysOnly,
221 #[serde(rename = "INCLUDE")]
223 Include,
224}
225
226impl ProjectionType {
227 #[must_use]
229 pub fn as_str(&self) -> &'static str {
230 match self {
231 Self::All => "ALL",
232 Self::KeysOnly => "KEYS_ONLY",
233 Self::Include => "INCLUDE",
234 }
235 }
236}
237
238impl std::fmt::Display for ProjectionType {
239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240 f.write_str(self.as_str())
241 }
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
246pub enum StreamViewType {
247 #[serde(rename = "KEYS_ONLY")]
249 KeysOnly,
250 #[serde(rename = "NEW_IMAGE")]
252 NewImage,
253 #[serde(rename = "OLD_IMAGE")]
255 OldImage,
256 #[serde(rename = "NEW_AND_OLD_IMAGES")]
258 NewAndOldImages,
259}
260
261impl StreamViewType {
262 #[must_use]
264 pub fn as_str(&self) -> &'static str {
265 match self {
266 Self::KeysOnly => "KEYS_ONLY",
267 Self::NewImage => "NEW_IMAGE",
268 Self::OldImage => "OLD_IMAGE",
269 Self::NewAndOldImages => "NEW_AND_OLD_IMAGES",
270 }
271 }
272}
273
274impl std::fmt::Display for StreamViewType {
275 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276 f.write_str(self.as_str())
277 }
278}
279
280#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
282pub enum SseType {
283 #[default]
285 #[serde(rename = "AES256")]
286 Aes256,
287 #[serde(rename = "KMS")]
289 Kms,
290}
291
292impl SseType {
293 #[must_use]
295 pub fn as_str(&self) -> &'static str {
296 match self {
297 Self::Aes256 => "AES256",
298 Self::Kms => "KMS",
299 }
300 }
301}
302
303impl std::fmt::Display for SseType {
304 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
305 f.write_str(self.as_str())
306 }
307}
308
309#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
311pub enum SseStatus {
312 #[serde(rename = "ENABLING")]
314 Enabling,
315 #[serde(rename = "ENABLED")]
317 Enabled,
318 #[serde(rename = "DISABLING")]
320 Disabling,
321 #[serde(rename = "DISABLED")]
323 Disabled,
324 #[serde(rename = "UPDATING")]
326 Updating,
327}
328
329impl SseStatus {
330 #[must_use]
332 pub fn as_str(&self) -> &'static str {
333 match self {
334 Self::Enabling => "ENABLING",
335 Self::Enabled => "ENABLED",
336 Self::Disabling => "DISABLING",
337 Self::Disabled => "DISABLED",
338 Self::Updating => "UPDATING",
339 }
340 }
341}
342
343impl std::fmt::Display for SseStatus {
344 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
345 f.write_str(self.as_str())
346 }
347}
348
349#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
351pub enum IndexStatus {
352 #[serde(rename = "CREATING")]
354 Creating,
355 #[serde(rename = "UPDATING")]
357 Updating,
358 #[serde(rename = "DELETING")]
360 Deleting,
361 #[serde(rename = "ACTIVE")]
363 Active,
364}
365
366impl IndexStatus {
367 #[must_use]
369 pub fn as_str(&self) -> &'static str {
370 match self {
371 Self::Creating => "CREATING",
372 Self::Updating => "UPDATING",
373 Self::Deleting => "DELETING",
374 Self::Active => "ACTIVE",
375 }
376 }
377}
378
379impl std::fmt::Display for IndexStatus {
380 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
381 f.write_str(self.as_str())
382 }
383}
384
385#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
387pub enum ReturnValue {
388 #[default]
390 #[serde(rename = "NONE")]
391 None,
392 #[serde(rename = "ALL_OLD")]
394 AllOld,
395 #[serde(rename = "UPDATED_OLD")]
397 UpdatedOld,
398 #[serde(rename = "ALL_NEW")]
400 AllNew,
401 #[serde(rename = "UPDATED_NEW")]
403 UpdatedNew,
404}
405
406impl ReturnValue {
407 #[must_use]
409 pub fn as_str(&self) -> &'static str {
410 match self {
411 Self::None => "NONE",
412 Self::AllOld => "ALL_OLD",
413 Self::UpdatedOld => "UPDATED_OLD",
414 Self::AllNew => "ALL_NEW",
415 Self::UpdatedNew => "UPDATED_NEW",
416 }
417 }
418}
419
420impl std::fmt::Display for ReturnValue {
421 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
422 f.write_str(self.as_str())
423 }
424}
425
426#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
428pub enum ReturnConsumedCapacity {
429 #[serde(rename = "INDEXES")]
431 Indexes,
432 #[serde(rename = "TOTAL")]
434 Total,
435 #[default]
437 #[serde(rename = "NONE")]
438 None,
439}
440
441impl ReturnConsumedCapacity {
442 #[must_use]
444 pub fn as_str(&self) -> &'static str {
445 match self {
446 Self::Indexes => "INDEXES",
447 Self::Total => "TOTAL",
448 Self::None => "NONE",
449 }
450 }
451
452 #[must_use]
454 pub fn should_report(&self) -> bool {
455 !matches!(self, Self::None)
456 }
457
458 #[must_use]
460 pub fn should_report_indexes(&self) -> bool {
461 matches!(self, Self::Indexes)
462 }
463}
464
465impl std::fmt::Display for ReturnConsumedCapacity {
466 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
467 f.write_str(self.as_str())
468 }
469}
470
471#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
473pub enum ReturnItemCollectionMetrics {
474 #[serde(rename = "SIZE")]
476 Size,
477 #[default]
479 #[serde(rename = "NONE")]
480 None,
481}
482
483impl ReturnItemCollectionMetrics {
484 #[must_use]
486 pub fn as_str(&self) -> &'static str {
487 match self {
488 Self::Size => "SIZE",
489 Self::None => "NONE",
490 }
491 }
492
493 #[must_use]
495 pub fn should_report(&self) -> bool {
496 matches!(self, Self::Size)
497 }
498}
499
500impl std::fmt::Display for ReturnItemCollectionMetrics {
501 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
502 f.write_str(self.as_str())
503 }
504}
505
506#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
508pub enum Select {
509 #[default]
511 #[serde(rename = "ALL_ATTRIBUTES")]
512 AllAttributes,
513 #[serde(rename = "ALL_PROJECTED_ATTRIBUTES")]
515 AllProjectedAttributes,
516 #[serde(rename = "SPECIFIC_ATTRIBUTES")]
518 SpecificAttributes,
519 #[serde(rename = "COUNT")]
521 Count,
522}
523
524impl Select {
525 #[must_use]
527 pub fn as_str(&self) -> &'static str {
528 match self {
529 Self::AllAttributes => "ALL_ATTRIBUTES",
530 Self::AllProjectedAttributes => "ALL_PROJECTED_ATTRIBUTES",
531 Self::SpecificAttributes => "SPECIFIC_ATTRIBUTES",
532 Self::Count => "COUNT",
533 }
534 }
535}
536
537impl std::fmt::Display for Select {
538 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
539 f.write_str(self.as_str())
540 }
541}
542
543#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
545pub enum ConditionalOperator {
546 #[default]
548 #[serde(rename = "AND")]
549 And,
550 #[serde(rename = "OR")]
552 Or,
553}
554
555impl ConditionalOperator {
556 #[must_use]
558 pub fn as_str(&self) -> &'static str {
559 match self {
560 Self::And => "AND",
561 Self::Or => "OR",
562 }
563 }
564}
565
566impl std::fmt::Display for ConditionalOperator {
567 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
568 f.write_str(self.as_str())
569 }
570}
571
572#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
578pub enum ComparisonOperator {
579 #[serde(rename = "EQ")]
581 Eq,
582 #[serde(rename = "NE")]
584 Ne,
585 #[serde(rename = "LE")]
587 Le,
588 #[serde(rename = "LT")]
590 Lt,
591 #[serde(rename = "GE")]
593 Ge,
594 #[serde(rename = "GT")]
596 Gt,
597 #[serde(rename = "NOT_NULL")]
599 NotNull,
600 #[serde(rename = "NULL")]
602 Null,
603 #[serde(rename = "CONTAINS")]
605 Contains,
606 #[serde(rename = "NOT_CONTAINS")]
608 NotContains,
609 #[serde(rename = "BEGINS_WITH")]
611 BeginsWith,
612 #[serde(rename = "IN")]
614 In,
615 #[serde(rename = "BETWEEN")]
617 Between,
618}
619
620impl ComparisonOperator {
621 #[must_use]
623 pub fn as_str(&self) -> &'static str {
624 match self {
625 Self::Eq => "EQ",
626 Self::Ne => "NE",
627 Self::Le => "LE",
628 Self::Lt => "LT",
629 Self::Ge => "GE",
630 Self::Gt => "GT",
631 Self::NotNull => "NOT_NULL",
632 Self::Null => "NULL",
633 Self::Contains => "CONTAINS",
634 Self::NotContains => "NOT_CONTAINS",
635 Self::BeginsWith => "BEGINS_WITH",
636 Self::In => "IN",
637 Self::Between => "BETWEEN",
638 }
639 }
640}
641
642impl std::fmt::Display for ComparisonOperator {
643 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
644 f.write_str(self.as_str())
645 }
646}
647
648#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
657#[serde(rename_all = "PascalCase")]
658pub struct KeySchemaElement {
659 pub attribute_name: String,
661 pub key_type: KeyType,
663}
664
665#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
670#[serde(rename_all = "PascalCase")]
671pub struct AttributeDefinition {
672 pub attribute_name: String,
674 pub attribute_type: ScalarAttributeType,
676}
677
678#[derive(Debug, Clone, Default, Serialize, Deserialize)]
684#[serde(rename_all = "PascalCase")]
685pub struct BillingModeSummary {
686 #[serde(skip_serializing_if = "Option::is_none")]
688 pub billing_mode: Option<BillingMode>,
689 #[serde(skip_serializing_if = "Option::is_none")]
691 pub last_update_to_pay_per_request_date_time: Option<f64>,
692}
693
694#[derive(Debug, Clone, Default, Serialize, Deserialize)]
699#[serde(rename_all = "PascalCase")]
700pub struct ProvisionedThroughput {
701 pub read_capacity_units: i64,
703 pub write_capacity_units: i64,
705}
706
707#[derive(Debug, Clone, Default, Serialize, Deserialize)]
712#[serde(rename_all = "PascalCase")]
713pub struct ProvisionedThroughputDescription {
714 pub read_capacity_units: i64,
716 pub write_capacity_units: i64,
718 #[serde(skip_serializing_if = "Option::is_none")]
720 pub number_of_decreases_today: Option<i64>,
721 #[serde(skip_serializing_if = "Option::is_none")]
723 pub last_increase_date_time: Option<f64>,
724 #[serde(skip_serializing_if = "Option::is_none")]
726 pub last_decrease_date_time: Option<f64>,
727}
728
729#[derive(Debug, Clone, Default, Serialize, Deserialize)]
738#[serde(rename_all = "PascalCase")]
739pub struct Projection {
740 #[serde(skip_serializing_if = "Option::is_none")]
742 pub projection_type: Option<ProjectionType>,
743 #[serde(default, skip_serializing_if = "Vec::is_empty")]
745 pub non_key_attributes: Vec<String>,
746}
747
748#[derive(Debug, Clone, Serialize, Deserialize)]
756#[serde(rename_all = "PascalCase")]
757pub struct GlobalSecondaryIndex {
758 pub index_name: String,
760 pub key_schema: Vec<KeySchemaElement>,
762 pub projection: Projection,
764 #[serde(skip_serializing_if = "Option::is_none")]
766 pub provisioned_throughput: Option<ProvisionedThroughput>,
767}
768
769#[derive(Debug, Clone, Default, Serialize, Deserialize)]
773#[serde(rename_all = "PascalCase")]
774pub struct GlobalSecondaryIndexDescription {
775 #[serde(skip_serializing_if = "Option::is_none")]
777 pub index_name: Option<String>,
778 #[serde(default, skip_serializing_if = "Vec::is_empty")]
780 pub key_schema: Vec<KeySchemaElement>,
781 #[serde(skip_serializing_if = "Option::is_none")]
783 pub projection: Option<Projection>,
784 #[serde(skip_serializing_if = "Option::is_none")]
786 pub index_status: Option<IndexStatus>,
787 #[serde(skip_serializing_if = "Option::is_none")]
789 pub backfilling: Option<bool>,
790 #[serde(skip_serializing_if = "Option::is_none")]
792 pub provisioned_throughput: Option<ProvisionedThroughputDescription>,
793 #[serde(skip_serializing_if = "Option::is_none")]
795 pub index_size_bytes: Option<i64>,
796 #[serde(skip_serializing_if = "Option::is_none")]
798 pub item_count: Option<i64>,
799 #[serde(skip_serializing_if = "Option::is_none")]
801 pub index_arn: Option<String>,
802}
803
804#[derive(Debug, Clone, Serialize, Deserialize)]
809#[serde(rename_all = "PascalCase")]
810pub struct LocalSecondaryIndex {
811 pub index_name: String,
813 pub key_schema: Vec<KeySchemaElement>,
815 pub projection: Projection,
817}
818
819#[derive(Debug, Clone, Default, Serialize, Deserialize)]
823#[serde(rename_all = "PascalCase")]
824pub struct LocalSecondaryIndexDescription {
825 #[serde(skip_serializing_if = "Option::is_none")]
827 pub index_name: Option<String>,
828 #[serde(default, skip_serializing_if = "Vec::is_empty")]
830 pub key_schema: Vec<KeySchemaElement>,
831 #[serde(skip_serializing_if = "Option::is_none")]
833 pub projection: Option<Projection>,
834 #[serde(skip_serializing_if = "Option::is_none")]
836 pub index_size_bytes: Option<i64>,
837 #[serde(skip_serializing_if = "Option::is_none")]
839 pub item_count: Option<i64>,
840 #[serde(skip_serializing_if = "Option::is_none")]
842 pub index_arn: Option<String>,
843}
844
845#[derive(Debug, Clone, Default, Serialize, Deserialize)]
851#[serde(rename_all = "PascalCase")]
852pub struct StreamSpecification {
853 pub stream_enabled: bool,
855 #[serde(skip_serializing_if = "Option::is_none")]
857 pub stream_view_type: Option<StreamViewType>,
858}
859
860#[derive(Debug, Clone, Default, Serialize, Deserialize)]
868#[serde(rename_all = "PascalCase")]
869pub struct SSESpecification {
870 #[serde(skip_serializing_if = "Option::is_none")]
872 pub enabled: Option<bool>,
873 #[serde(rename = "SSEType", skip_serializing_if = "Option::is_none")]
875 pub sse_type: Option<SseType>,
876 #[serde(rename = "KMSMasterKeyId", skip_serializing_if = "Option::is_none")]
878 pub kms_master_key_id: Option<String>,
879}
880
881#[derive(Debug, Clone, Default, Serialize, Deserialize)]
885#[serde(rename_all = "PascalCase")]
886pub struct SSEDescription {
887 #[serde(skip_serializing_if = "Option::is_none")]
889 pub status: Option<SseStatus>,
890 #[serde(rename = "SSEType", skip_serializing_if = "Option::is_none")]
892 pub sse_type: Option<SseType>,
893 #[serde(rename = "KMSMasterKeyId", skip_serializing_if = "Option::is_none")]
895 pub kms_master_key_id: Option<String>,
896 #[serde(skip_serializing_if = "Option::is_none")]
898 pub inaccessible_encryption_date_time: Option<f64>,
899}
900
901#[derive(Debug, Clone, Serialize, Deserialize)]
907#[serde(rename_all = "PascalCase")]
908pub struct TimeToLiveSpecification {
909 pub attribute_name: String,
911 pub enabled: bool,
913}
914
915#[derive(Debug, Clone, Serialize, Deserialize)]
917#[serde(rename_all = "PascalCase")]
918pub struct TimeToLiveDescription {
919 #[serde(skip_serializing_if = "Option::is_none")]
921 pub attribute_name: Option<String>,
922 #[serde(skip_serializing_if = "Option::is_none")]
924 pub time_to_live_status: Option<String>,
925}
926
927#[derive(Debug, Clone, Serialize, Deserialize)]
935#[serde(rename_all = "PascalCase")]
936pub struct TransactWriteItem {
937 #[serde(skip_serializing_if = "Option::is_none")]
939 pub condition_check: Option<ConditionCheck>,
940 #[serde(skip_serializing_if = "Option::is_none")]
942 pub put: Option<TransactPut>,
943 #[serde(skip_serializing_if = "Option::is_none")]
945 pub delete: Option<TransactDelete>,
946 #[serde(skip_serializing_if = "Option::is_none")]
948 pub update: Option<TransactUpdate>,
949}
950
951#[derive(Debug, Clone, Serialize, Deserialize)]
953#[serde(rename_all = "PascalCase")]
954pub struct ConditionCheck {
955 pub table_name: String,
957 pub key: HashMap<String, AttributeValue>,
959 pub condition_expression: String,
961 #[serde(skip_serializing_if = "Option::is_none")]
963 pub expression_attribute_names: Option<HashMap<String, String>>,
964 #[serde(skip_serializing_if = "Option::is_none")]
966 pub expression_attribute_values: Option<HashMap<String, AttributeValue>>,
967 #[serde(skip_serializing_if = "Option::is_none")]
969 pub return_values_on_condition_check_failure: Option<String>,
970}
971
972#[derive(Debug, Clone, Serialize, Deserialize)]
974#[serde(rename_all = "PascalCase")]
975pub struct TransactPut {
976 pub table_name: String,
978 pub item: HashMap<String, AttributeValue>,
980 #[serde(skip_serializing_if = "Option::is_none")]
982 pub condition_expression: Option<String>,
983 #[serde(skip_serializing_if = "Option::is_none")]
985 pub expression_attribute_names: Option<HashMap<String, String>>,
986 #[serde(skip_serializing_if = "Option::is_none")]
988 pub expression_attribute_values: Option<HashMap<String, AttributeValue>>,
989 #[serde(skip_serializing_if = "Option::is_none")]
991 pub return_values_on_condition_check_failure: Option<String>,
992}
993
994#[derive(Debug, Clone, Serialize, Deserialize)]
996#[serde(rename_all = "PascalCase")]
997pub struct TransactDelete {
998 pub table_name: String,
1000 pub key: HashMap<String, AttributeValue>,
1002 #[serde(skip_serializing_if = "Option::is_none")]
1004 pub condition_expression: Option<String>,
1005 #[serde(skip_serializing_if = "Option::is_none")]
1007 pub expression_attribute_names: Option<HashMap<String, String>>,
1008 #[serde(skip_serializing_if = "Option::is_none")]
1010 pub expression_attribute_values: Option<HashMap<String, AttributeValue>>,
1011 #[serde(skip_serializing_if = "Option::is_none")]
1013 pub return_values_on_condition_check_failure: Option<String>,
1014}
1015
1016#[derive(Debug, Clone, Serialize, Deserialize)]
1018#[serde(rename_all = "PascalCase")]
1019pub struct TransactUpdate {
1020 pub table_name: String,
1022 pub key: HashMap<String, AttributeValue>,
1024 pub update_expression: String,
1026 #[serde(skip_serializing_if = "Option::is_none")]
1028 pub condition_expression: Option<String>,
1029 #[serde(skip_serializing_if = "Option::is_none")]
1031 pub expression_attribute_names: Option<HashMap<String, String>>,
1032 #[serde(skip_serializing_if = "Option::is_none")]
1034 pub expression_attribute_values: Option<HashMap<String, AttributeValue>>,
1035 #[serde(skip_serializing_if = "Option::is_none")]
1037 pub return_values_on_condition_check_failure: Option<String>,
1038}
1039
1040#[derive(Debug, Clone, Serialize, Deserialize)]
1042#[serde(rename_all = "PascalCase")]
1043pub struct TransactGetItem {
1044 pub get: Get,
1046}
1047
1048#[derive(Debug, Clone, Serialize, Deserialize)]
1050#[serde(rename_all = "PascalCase")]
1051pub struct Get {
1052 pub table_name: String,
1054 pub key: HashMap<String, AttributeValue>,
1056 #[serde(skip_serializing_if = "Option::is_none")]
1058 pub projection_expression: Option<String>,
1059 #[serde(skip_serializing_if = "Option::is_none")]
1061 pub expression_attribute_names: Option<HashMap<String, String>>,
1062}
1063
1064#[derive(Debug, Clone, Serialize, Deserialize)]
1066#[serde(rename_all = "PascalCase")]
1067pub struct CancellationReason {
1068 #[serde(skip_serializing_if = "Option::is_none")]
1070 pub code: Option<String>,
1071 #[serde(skip_serializing_if = "Option::is_none")]
1073 pub message: Option<String>,
1074 #[serde(skip_serializing_if = "Option::is_none")]
1076 pub item: Option<HashMap<String, AttributeValue>>,
1077}
1078
1079#[derive(Debug, Clone, Serialize, Deserialize)]
1081#[serde(rename_all = "PascalCase")]
1082pub struct ItemResponse {
1083 #[serde(skip_serializing_if = "Option::is_none")]
1085 pub item: Option<HashMap<String, AttributeValue>>,
1086}
1087
1088#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1094#[serde(rename_all = "PascalCase")]
1095pub struct Tag {
1096 pub key: String,
1098 pub value: String,
1100}
1101
1102#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1110#[serde(rename_all = "PascalCase")]
1111pub struct TableDescription {
1112 #[serde(skip_serializing_if = "Option::is_none")]
1114 pub table_name: Option<String>,
1115 #[serde(skip_serializing_if = "Option::is_none")]
1117 pub table_status: Option<TableStatus>,
1118 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1120 pub key_schema: Vec<KeySchemaElement>,
1121 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1123 pub attribute_definitions: Vec<AttributeDefinition>,
1124 #[serde(skip_serializing_if = "Option::is_none")]
1126 pub creation_date_time: Option<f64>,
1127 #[serde(skip_serializing_if = "Option::is_none")]
1129 pub item_count: Option<i64>,
1130 #[serde(skip_serializing_if = "Option::is_none")]
1132 pub table_size_bytes: Option<i64>,
1133 #[serde(skip_serializing_if = "Option::is_none")]
1135 pub table_arn: Option<String>,
1136 #[serde(skip_serializing_if = "Option::is_none")]
1138 pub table_id: Option<String>,
1139 #[serde(skip_serializing_if = "Option::is_none")]
1141 pub billing_mode_summary: Option<BillingModeSummary>,
1142 #[serde(skip_serializing_if = "Option::is_none")]
1144 pub provisioned_throughput: Option<ProvisionedThroughputDescription>,
1145 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1147 pub global_secondary_indexes: Vec<GlobalSecondaryIndexDescription>,
1148 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1150 pub local_secondary_indexes: Vec<LocalSecondaryIndexDescription>,
1151 #[serde(skip_serializing_if = "Option::is_none")]
1153 pub stream_specification: Option<StreamSpecification>,
1154 #[serde(skip_serializing_if = "Option::is_none")]
1156 pub latest_stream_arn: Option<String>,
1157 #[serde(skip_serializing_if = "Option::is_none")]
1159 pub latest_stream_label: Option<String>,
1160 #[serde(rename = "SSEDescription", skip_serializing_if = "Option::is_none")]
1162 pub sse_description: Option<SSEDescription>,
1163 #[serde(skip_serializing_if = "Option::is_none")]
1165 pub deletion_protection_enabled: Option<bool>,
1166}
1167
1168#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1174#[serde(rename_all = "PascalCase")]
1175pub struct Capacity {
1176 #[serde(skip_serializing_if = "Option::is_none")]
1178 pub read_capacity_units: Option<f64>,
1179 #[serde(skip_serializing_if = "Option::is_none")]
1181 pub write_capacity_units: Option<f64>,
1182 #[serde(skip_serializing_if = "Option::is_none")]
1184 pub capacity_units: Option<f64>,
1185}
1186
1187#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1191#[serde(rename_all = "PascalCase")]
1192pub struct ConsumedCapacity {
1193 #[serde(skip_serializing_if = "Option::is_none")]
1195 pub table_name: Option<String>,
1196 #[serde(skip_serializing_if = "Option::is_none")]
1198 pub capacity_units: Option<f64>,
1199 #[serde(skip_serializing_if = "Option::is_none")]
1201 pub read_capacity_units: Option<f64>,
1202 #[serde(skip_serializing_if = "Option::is_none")]
1204 pub write_capacity_units: Option<f64>,
1205 #[serde(skip_serializing_if = "Option::is_none")]
1207 pub table: Option<Capacity>,
1208 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
1210 pub local_secondary_indexes: HashMap<String, Capacity>,
1211 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
1213 pub global_secondary_indexes: HashMap<String, Capacity>,
1214}
1215
1216#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1225#[serde(rename_all = "PascalCase")]
1226pub struct ItemCollectionMetrics {
1227 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
1229 pub item_collection_key: HashMap<String, AttributeValue>,
1230 #[serde(
1232 rename = "SizeEstimateRangeGB",
1233 default,
1234 skip_serializing_if = "Vec::is_empty"
1235 )]
1236 pub size_estimate_range_gb: Vec<f64>,
1237}
1238
1239#[derive(Debug, Clone, Serialize, Deserialize)]
1248#[serde(rename_all = "PascalCase")]
1249pub struct Condition {
1250 pub comparison_operator: ComparisonOperator,
1252 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1254 pub attribute_value_list: Vec<AttributeValue>,
1255}
1256
1257#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
1266pub enum AttributeAction {
1267 #[default]
1269 #[serde(rename = "PUT")]
1270 Put,
1271 #[serde(rename = "DELETE")]
1273 Delete,
1274 #[serde(rename = "ADD")]
1276 Add,
1277}
1278
1279impl AttributeAction {
1280 #[must_use]
1282 pub fn as_str(&self) -> &'static str {
1283 match self {
1284 Self::Put => "PUT",
1285 Self::Delete => "DELETE",
1286 Self::Add => "ADD",
1287 }
1288 }
1289}
1290
1291impl std::fmt::Display for AttributeAction {
1292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1293 f.write_str(self.as_str())
1294 }
1295}
1296
1297#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1299#[serde(rename_all = "PascalCase")]
1300pub struct AttributeValueUpdate {
1301 #[serde(skip_serializing_if = "Option::is_none")]
1303 pub value: Option<AttributeValue>,
1304 #[serde(skip_serializing_if = "Option::is_none")]
1306 pub action: Option<AttributeAction>,
1307}
1308
1309#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1311#[serde(rename_all = "PascalCase")]
1312pub struct ExpectedAttributeValue {
1313 #[serde(skip_serializing_if = "Option::is_none")]
1315 pub value: Option<AttributeValue>,
1316 #[serde(skip_serializing_if = "Option::is_none")]
1318 pub exists: Option<bool>,
1319 #[serde(skip_serializing_if = "Option::is_none")]
1321 pub comparison_operator: Option<ComparisonOperator>,
1322 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1324 pub attribute_value_list: Vec<AttributeValue>,
1325}
1326
1327#[derive(Debug, Clone, Serialize, Deserialize)]
1335#[serde(rename_all = "PascalCase")]
1336pub struct KeysAndAttributes {
1337 pub keys: Vec<HashMap<String, AttributeValue>>,
1339 #[serde(skip_serializing_if = "Option::is_none")]
1341 pub projection_expression: Option<String>,
1342 #[serde(skip_serializing_if = "Option::is_none")]
1344 pub expression_attribute_names: Option<HashMap<String, String>>,
1345 #[serde(skip_serializing_if = "Option::is_none")]
1347 pub consistent_read: Option<bool>,
1348 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1350 pub attributes_to_get: Vec<String>,
1351}
1352
1353#[derive(Debug, Clone, Serialize, Deserialize)]
1357#[serde(rename_all = "PascalCase")]
1358pub struct WriteRequest {
1359 #[serde(skip_serializing_if = "Option::is_none")]
1361 pub put_request: Option<PutRequest>,
1362 #[serde(skip_serializing_if = "Option::is_none")]
1364 pub delete_request: Option<DeleteRequest>,
1365}
1366
1367#[derive(Debug, Clone, Serialize, Deserialize)]
1369#[serde(rename_all = "PascalCase")]
1370pub struct PutRequest {
1371 pub item: HashMap<String, AttributeValue>,
1373}
1374
1375#[derive(Debug, Clone, Serialize, Deserialize)]
1377#[serde(rename_all = "PascalCase")]
1378pub struct DeleteRequest {
1379 pub key: HashMap<String, AttributeValue>,
1381}
1382
1383pub type Item = HashMap<String, AttributeValue>;
1389
1390pub type Key = HashMap<String, AttributeValue>;
1392
1393pub type ExpressionAttributeNames = HashMap<String, String>;
1395
1396pub type ExpressionAttributeValues = HashMap<String, AttributeValue>;
1398
1399#[cfg(test)]
1400mod tests {
1401 use super::*;
1402
1403 #[test]
1404 fn test_should_serialize_key_schema_element() {
1405 let elem = KeySchemaElement {
1406 attribute_name: "pk".to_owned(),
1407 key_type: KeyType::Hash,
1408 };
1409 let json = serde_json::to_string(&elem).expect("serialize KeySchemaElement");
1410 assert_eq!(json, r#"{"AttributeName":"pk","KeyType":"HASH"}"#);
1411 }
1412
1413 #[test]
1414 fn test_should_roundtrip_attribute_definition() {
1415 let def = AttributeDefinition {
1416 attribute_name: "id".to_owned(),
1417 attribute_type: ScalarAttributeType::S,
1418 };
1419 let json = serde_json::to_string(&def).expect("serialize AttributeDefinition");
1420 let parsed: AttributeDefinition =
1421 serde_json::from_str(&json).expect("deserialize AttributeDefinition");
1422 assert_eq!(def.attribute_name, parsed.attribute_name);
1423 assert_eq!(def.attribute_type, parsed.attribute_type);
1424 }
1425
1426 #[test]
1427 fn test_should_serialize_table_status() {
1428 let status = TableStatus::Active;
1429 let json = serde_json::to_string(&status).expect("serialize TableStatus");
1430 assert_eq!(json, r#""ACTIVE""#);
1431 }
1432
1433 #[test]
1434 fn test_should_default_billing_mode_to_pay_per_request() {
1435 let mode = BillingMode::default();
1436 assert_eq!(mode, BillingMode::PayPerRequest);
1437 }
1438
1439 #[test]
1440 fn test_should_default_return_value_to_none() {
1441 let rv = ReturnValue::default();
1442 assert_eq!(rv, ReturnValue::None);
1443 }
1444
1445 #[test]
1446 fn test_should_serialize_provisioned_throughput() {
1447 let pt = ProvisionedThroughput {
1448 read_capacity_units: 5,
1449 write_capacity_units: 10,
1450 };
1451 let json = serde_json::to_string(&pt).expect("serialize ProvisionedThroughput");
1452 assert_eq!(json, r#"{"ReadCapacityUnits":5,"WriteCapacityUnits":10}"#);
1453 }
1454
1455 #[test]
1456 fn test_should_skip_none_fields_in_table_description() {
1457 let desc = TableDescription {
1458 table_name: Some("TestTable".to_owned()),
1459 table_status: Some(TableStatus::Active),
1460 ..Default::default()
1461 };
1462 let json = serde_json::to_string(&desc).expect("serialize TableDescription");
1463 assert!(json.contains(r#""TableName":"TestTable""#));
1464 assert!(json.contains(r#""TableStatus":"ACTIVE""#));
1465 assert!(!json.contains("TableArn"));
1467 assert!(!json.contains("KeySchema"));
1468 assert!(!json.contains("GlobalSecondaryIndexes"));
1469 }
1470
1471 #[test]
1472 fn test_should_roundtrip_projection() {
1473 let proj = Projection {
1474 projection_type: Some(ProjectionType::Include),
1475 non_key_attributes: vec!["email".to_owned(), "name".to_owned()],
1476 };
1477 let json = serde_json::to_string(&proj).expect("serialize Projection");
1478 let parsed: Projection = serde_json::from_str(&json).expect("deserialize Projection");
1479 assert_eq!(proj.projection_type, parsed.projection_type);
1480 assert_eq!(proj.non_key_attributes, parsed.non_key_attributes);
1481 }
1482
1483 #[test]
1484 fn test_should_serialize_write_request_with_put() {
1485 let mut item = HashMap::new();
1486 item.insert("id".to_owned(), AttributeValue::S("123".to_owned()));
1487 let req = WriteRequest {
1488 put_request: Some(PutRequest { item }),
1489 delete_request: Option::None,
1490 };
1491 let json = serde_json::to_string(&req).expect("serialize WriteRequest");
1492 assert!(json.contains("PutRequest"));
1493 assert!(!json.contains("DeleteRequest"));
1494 }
1495
1496 #[test]
1497 fn test_should_serialize_write_request_with_delete() {
1498 let mut key = HashMap::new();
1499 key.insert("id".to_owned(), AttributeValue::S("456".to_owned()));
1500 let req = WriteRequest {
1501 put_request: Option::None,
1502 delete_request: Some(DeleteRequest { key }),
1503 };
1504 let json = serde_json::to_string(&req).expect("serialize WriteRequest");
1505 assert!(json.contains("DeleteRequest"));
1506 assert!(!json.contains("PutRequest"));
1507 }
1508
1509 #[test]
1510 fn test_should_serialize_condition() {
1511 let cond = Condition {
1512 comparison_operator: ComparisonOperator::Eq,
1513 attribute_value_list: vec![AttributeValue::S("test".to_owned())],
1514 };
1515 let json = serde_json::to_string(&cond).expect("serialize Condition");
1516 assert!(json.contains(r#""ComparisonOperator":"EQ""#));
1517 assert!(json.contains("AttributeValueList"));
1518 }
1519
1520 #[test]
1521 fn test_should_roundtrip_consumed_capacity() {
1522 let cap = ConsumedCapacity {
1523 table_name: Some("Orders".to_owned()),
1524 capacity_units: Some(5.0),
1525 ..Default::default()
1526 };
1527 let json = serde_json::to_string(&cap).expect("serialize ConsumedCapacity");
1528 let parsed: ConsumedCapacity =
1529 serde_json::from_str(&json).expect("deserialize ConsumedCapacity");
1530 assert_eq!(cap.table_name, parsed.table_name);
1531 assert_eq!(cap.capacity_units, parsed.capacity_units);
1532 }
1533
1534 #[test]
1535 fn test_should_serialize_sse_specification() {
1536 let sse = SSESpecification {
1537 enabled: Some(true),
1538 sse_type: Some(SseType::Kms),
1539 kms_master_key_id: Some("arn:aws:kms:us-east-1:123456789012:key/abc".to_owned()),
1540 };
1541 let json = serde_json::to_string(&sse).expect("serialize SSESpecification");
1542 assert!(json.contains(r#""SSEType":"KMS""#));
1543 assert!(json.contains(r#""KMSMasterKeyId":"arn:aws:kms"#));
1544 }
1545
1546 #[test]
1547 fn test_should_serialize_tag() {
1548 let tag = Tag {
1549 key: "Environment".to_owned(),
1550 value: "Production".to_owned(),
1551 };
1552 let json = serde_json::to_string(&tag).expect("serialize Tag");
1553 assert_eq!(json, r#"{"Key":"Environment","Value":"Production"}"#);
1554 }
1555
1556 #[test]
1557 fn test_should_serialize_stream_specification() {
1558 let spec = StreamSpecification {
1559 stream_enabled: true,
1560 stream_view_type: Some(StreamViewType::NewAndOldImages),
1561 };
1562 let json = serde_json::to_string(&spec).expect("serialize StreamSpecification");
1563 assert!(json.contains(r#""StreamEnabled":true"#));
1564 assert!(json.contains(r#""StreamViewType":"NEW_AND_OLD_IMAGES""#));
1565 }
1566
1567 #[test]
1568 fn test_should_serialize_keys_and_attributes() {
1569 let mut key = HashMap::new();
1570 key.insert("pk".to_owned(), AttributeValue::S("user-1".to_owned()));
1571 let ka = KeysAndAttributes {
1572 keys: vec![key],
1573 projection_expression: Some("id, #n".to_owned()),
1574 expression_attribute_names: Some(HashMap::from([("#n".to_owned(), "name".to_owned())])),
1575 consistent_read: Some(true),
1576 attributes_to_get: Vec::new(),
1577 };
1578 let json = serde_json::to_string(&ka).expect("serialize KeysAndAttributes");
1579 assert!(json.contains("ProjectionExpression"));
1580 assert!(json.contains("ExpressionAttributeNames"));
1581 assert!(json.contains("ConsistentRead"));
1582 assert!(!json.contains("AttributesToGet"));
1583 }
1584
1585 #[test]
1586 fn test_should_roundtrip_global_secondary_index() {
1587 let gsi = GlobalSecondaryIndex {
1588 index_name: "gsi-email".to_owned(),
1589 key_schema: vec![KeySchemaElement {
1590 attribute_name: "email".to_owned(),
1591 key_type: KeyType::Hash,
1592 }],
1593 projection: Projection {
1594 projection_type: Some(ProjectionType::All),
1595 non_key_attributes: Vec::new(),
1596 },
1597 provisioned_throughput: Option::None,
1598 };
1599 let json = serde_json::to_string(&gsi).expect("serialize GlobalSecondaryIndex");
1600 let parsed: GlobalSecondaryIndex =
1601 serde_json::from_str(&json).expect("deserialize GlobalSecondaryIndex");
1602 assert_eq!(gsi.index_name, parsed.index_name);
1603 assert_eq!(gsi.key_schema.len(), parsed.key_schema.len());
1604 }
1605
1606 #[test]
1607 fn test_should_display_all_enum_variants() {
1608 assert_eq!(KeyType::Hash.to_string(), "HASH");
1610 assert_eq!(KeyType::Range.to_string(), "RANGE");
1611 assert_eq!(ScalarAttributeType::S.to_string(), "S");
1612 assert_eq!(ScalarAttributeType::N.to_string(), "N");
1613 assert_eq!(ScalarAttributeType::B.to_string(), "B");
1614 assert_eq!(TableStatus::Active.to_string(), "ACTIVE");
1615 assert_eq!(TableStatus::Creating.to_string(), "CREATING");
1616 assert_eq!(BillingMode::PayPerRequest.to_string(), "PAY_PER_REQUEST");
1617 assert_eq!(ProjectionType::KeysOnly.to_string(), "KEYS_ONLY");
1618 assert_eq!(
1619 StreamViewType::NewAndOldImages.to_string(),
1620 "NEW_AND_OLD_IMAGES"
1621 );
1622 assert_eq!(SseType::Kms.to_string(), "KMS");
1623 assert_eq!(SseStatus::Enabled.to_string(), "ENABLED");
1624 assert_eq!(IndexStatus::Active.to_string(), "ACTIVE");
1625 assert_eq!(ReturnValue::AllNew.to_string(), "ALL_NEW");
1626 assert_eq!(ReturnConsumedCapacity::Indexes.to_string(), "INDEXES");
1627 assert_eq!(ReturnItemCollectionMetrics::Size.to_string(), "SIZE");
1628 assert_eq!(Select::Count.to_string(), "COUNT");
1629 assert_eq!(ConditionalOperator::And.to_string(), "AND");
1630 assert_eq!(ComparisonOperator::BeginsWith.to_string(), "BEGINS_WITH");
1631 }
1632
1633 #[test]
1634 fn test_should_report_consumed_capacity_settings() {
1635 assert!(!ReturnConsumedCapacity::None.should_report());
1636 assert!(ReturnConsumedCapacity::Total.should_report());
1637 assert!(ReturnConsumedCapacity::Indexes.should_report());
1638 assert!(!ReturnConsumedCapacity::Total.should_report_indexes());
1639 assert!(ReturnConsumedCapacity::Indexes.should_report_indexes());
1640 }
1641
1642 #[test]
1643 fn test_should_report_item_collection_metrics_settings() {
1644 assert!(!ReturnItemCollectionMetrics::None.should_report());
1645 assert!(ReturnItemCollectionMetrics::Size.should_report());
1646 }
1647
1648 #[test]
1649 fn test_should_deserialize_table_description_from_dynamodb_json() {
1650 let json = r#"{
1651 "TableName": "Users",
1652 "TableStatus": "ACTIVE",
1653 "KeySchema": [
1654 {"AttributeName": "pk", "KeyType": "HASH"},
1655 {"AttributeName": "sk", "KeyType": "RANGE"}
1656 ],
1657 "AttributeDefinitions": [
1658 {"AttributeName": "pk", "AttributeType": "S"},
1659 {"AttributeName": "sk", "AttributeType": "S"}
1660 ],
1661 "CreationDateTime": 1709136000.0,
1662 "ItemCount": 42,
1663 "TableSizeBytes": 8192,
1664 "TableArn": "arn:aws:dynamodb:us-east-1:123456789012:table/Users",
1665 "TableId": "abc-123-def",
1666 "ProvisionedThroughput": {
1667 "ReadCapacityUnits": 5,
1668 "WriteCapacityUnits": 10,
1669 "NumberOfDecreasesToday": 0
1670 },
1671 "BillingModeSummary": {
1672 "BillingMode": "PROVISIONED"
1673 }
1674 }"#;
1675 let desc: TableDescription =
1676 serde_json::from_str(json).expect("deserialize TableDescription");
1677 assert_eq!(desc.table_name.as_deref(), Some("Users"));
1678 assert_eq!(desc.table_status, Some(TableStatus::Active));
1679 assert_eq!(desc.key_schema.len(), 2);
1680 assert_eq!(desc.attribute_definitions.len(), 2);
1681 assert_eq!(desc.item_count, Some(42));
1682 assert_eq!(desc.table_size_bytes, Some(8192));
1683 assert_eq!(desc.table_id.as_deref(), Some("abc-123-def"));
1684 assert_eq!(
1685 desc.billing_mode_summary
1686 .as_ref()
1687 .and_then(|b| b.billing_mode.as_ref()),
1688 Some(&BillingMode::Provisioned)
1689 );
1690 }
1691
1692 #[test]
1693 fn test_should_roundtrip_all_comparison_operators() {
1694 let operators = [
1695 ComparisonOperator::Eq,
1696 ComparisonOperator::Ne,
1697 ComparisonOperator::Le,
1698 ComparisonOperator::Lt,
1699 ComparisonOperator::Ge,
1700 ComparisonOperator::Gt,
1701 ComparisonOperator::NotNull,
1702 ComparisonOperator::Null,
1703 ComparisonOperator::Contains,
1704 ComparisonOperator::NotContains,
1705 ComparisonOperator::BeginsWith,
1706 ComparisonOperator::In,
1707 ComparisonOperator::Between,
1708 ];
1709 for op in &operators {
1710 let json = serde_json::to_string(op).expect("serialize ComparisonOperator");
1711 let parsed: ComparisonOperator =
1712 serde_json::from_str(&json).expect("deserialize ComparisonOperator");
1713 assert_eq!(op, &parsed);
1714 }
1715 }
1716
1717 #[test]
1718 fn test_should_roundtrip_all_return_values() {
1719 let values = [
1720 ReturnValue::None,
1721 ReturnValue::AllOld,
1722 ReturnValue::UpdatedOld,
1723 ReturnValue::AllNew,
1724 ReturnValue::UpdatedNew,
1725 ];
1726 for rv in &values {
1727 let json = serde_json::to_string(rv).expect("serialize ReturnValue");
1728 let parsed: ReturnValue = serde_json::from_str(&json).expect("deserialize ReturnValue");
1729 assert_eq!(rv, &parsed);
1730 }
1731 }
1732
1733 #[test]
1734 fn test_should_serialize_item_collection_metrics() {
1735 let mut key = HashMap::new();
1736 key.insert("pk".to_owned(), AttributeValue::S("user-1".to_owned()));
1737 let metrics = ItemCollectionMetrics {
1738 item_collection_key: key,
1739 size_estimate_range_gb: vec![0.5, 1.0],
1740 };
1741 let json = serde_json::to_string(&metrics).expect("serialize ItemCollectionMetrics");
1742 assert!(json.contains("ItemCollectionKey"));
1743 assert!(json.contains("SizeEstimateRangeGB"));
1744 }
1745
1746 #[test]
1747 fn test_should_serialize_billing_mode_summary() {
1748 let summary = BillingModeSummary {
1749 billing_mode: Some(BillingMode::PayPerRequest),
1750 last_update_to_pay_per_request_date_time: Some(1_709_136_000.0),
1751 };
1752 let json = serde_json::to_string(&summary).expect("serialize BillingModeSummary");
1753 assert!(json.contains(r#""BillingMode":"PAY_PER_REQUEST""#));
1754 assert!(json.contains("LastUpdateToPayPerRequestDateTime"));
1755 }
1756
1757 #[test]
1758 fn test_should_serialize_gsi_description_with_all_fields() {
1759 let desc = GlobalSecondaryIndexDescription {
1760 index_name: Some("gsi-status".to_owned()),
1761 key_schema: vec![KeySchemaElement {
1762 attribute_name: "status".to_owned(),
1763 key_type: KeyType::Hash,
1764 }],
1765 projection: Some(Projection {
1766 projection_type: Some(ProjectionType::All),
1767 ..Default::default()
1768 }),
1769 index_status: Some(IndexStatus::Active),
1770 backfilling: Some(false),
1771 provisioned_throughput: Some(ProvisionedThroughputDescription {
1772 read_capacity_units: 5,
1773 write_capacity_units: 5,
1774 ..Default::default()
1775 }),
1776 index_size_bytes: Some(1024),
1777 item_count: Some(10),
1778 index_arn: Some(
1779 "arn:aws:dynamodb:us-east-1:123456789012:table/T/index/gsi-status".to_owned(),
1780 ),
1781 };
1782 let json = serde_json::to_string(&desc).expect("serialize GlobalSecondaryIndexDescription");
1783 let parsed: GlobalSecondaryIndexDescription =
1784 serde_json::from_str(&json).expect("deserialize GlobalSecondaryIndexDescription");
1785 assert_eq!(desc.index_name, parsed.index_name);
1786 assert_eq!(desc.index_status, parsed.index_status);
1787 assert_eq!(desc.item_count, parsed.item_count);
1788 }
1789}