use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::attribute_value::AttributeValue;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum KeyType {
#[serde(rename = "HASH")]
Hash,
#[serde(rename = "RANGE")]
Range,
}
impl KeyType {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Hash => "HASH",
Self::Range => "RANGE",
}
}
}
impl std::fmt::Display for KeyType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ScalarAttributeType {
S,
N,
B,
Unknown(String),
}
impl ScalarAttributeType {
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::S => "S",
Self::N => "N",
Self::B => "B",
Self::Unknown(s) => s.as_str(),
}
}
#[must_use]
pub fn is_valid_key_type(&self) -> bool {
matches!(self, Self::S | Self::N | Self::B)
}
}
impl Serialize for ScalarAttributeType {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for ScalarAttributeType {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
match s.as_str() {
"S" => Ok(Self::S),
"N" => Ok(Self::N),
"B" => Ok(Self::B),
_ => Ok(Self::Unknown(s)),
}
}
}
impl std::fmt::Display for ScalarAttributeType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum TableStatus {
#[serde(rename = "CREATING")]
Creating,
#[serde(rename = "ACTIVE")]
Active,
#[serde(rename = "DELETING")]
Deleting,
#[serde(rename = "UPDATING")]
Updating,
#[serde(rename = "ARCHIVING")]
Archiving,
#[serde(rename = "ARCHIVED")]
Archived,
#[serde(rename = "INACCESSIBLE_ENCRYPTION_CREDENTIALS")]
InaccessibleEncryptionCredentials,
}
impl TableStatus {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Creating => "CREATING",
Self::Active => "ACTIVE",
Self::Deleting => "DELETING",
Self::Updating => "UPDATING",
Self::Archiving => "ARCHIVING",
Self::Archived => "ARCHIVED",
Self::InaccessibleEncryptionCredentials => "INACCESSIBLE_ENCRYPTION_CREDENTIALS",
}
}
}
impl std::fmt::Display for TableStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub enum BillingMode {
Provisioned,
#[default]
PayPerRequest,
Unknown(String),
}
impl BillingMode {
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::Provisioned => "PROVISIONED",
Self::PayPerRequest => "PAY_PER_REQUEST",
Self::Unknown(s) => s.as_str(),
}
}
}
impl Serialize for BillingMode {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for BillingMode {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
match s.as_str() {
"PROVISIONED" => Ok(Self::Provisioned),
"PAY_PER_REQUEST" => Ok(Self::PayPerRequest),
_ => Ok(Self::Unknown(s)),
}
}
}
impl std::fmt::Display for BillingMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub enum ProjectionType {
#[default]
#[serde(rename = "ALL")]
All,
#[serde(rename = "KEYS_ONLY")]
KeysOnly,
#[serde(rename = "INCLUDE")]
Include,
}
impl ProjectionType {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::All => "ALL",
Self::KeysOnly => "KEYS_ONLY",
Self::Include => "INCLUDE",
}
}
}
impl std::fmt::Display for ProjectionType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum StreamViewType {
#[serde(rename = "KEYS_ONLY")]
KeysOnly,
#[serde(rename = "NEW_IMAGE")]
NewImage,
#[serde(rename = "OLD_IMAGE")]
OldImage,
#[serde(rename = "NEW_AND_OLD_IMAGES")]
NewAndOldImages,
}
impl StreamViewType {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::KeysOnly => "KEYS_ONLY",
Self::NewImage => "NEW_IMAGE",
Self::OldImage => "OLD_IMAGE",
Self::NewAndOldImages => "NEW_AND_OLD_IMAGES",
}
}
}
impl std::fmt::Display for StreamViewType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub enum SseType {
#[default]
#[serde(rename = "AES256")]
Aes256,
#[serde(rename = "KMS")]
Kms,
}
impl SseType {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Aes256 => "AES256",
Self::Kms => "KMS",
}
}
}
impl std::fmt::Display for SseType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SseStatus {
#[serde(rename = "ENABLING")]
Enabling,
#[serde(rename = "ENABLED")]
Enabled,
#[serde(rename = "DISABLING")]
Disabling,
#[serde(rename = "DISABLED")]
Disabled,
#[serde(rename = "UPDATING")]
Updating,
}
impl SseStatus {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Enabling => "ENABLING",
Self::Enabled => "ENABLED",
Self::Disabling => "DISABLING",
Self::Disabled => "DISABLED",
Self::Updating => "UPDATING",
}
}
}
impl std::fmt::Display for SseStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum IndexStatus {
#[serde(rename = "CREATING")]
Creating,
#[serde(rename = "UPDATING")]
Updating,
#[serde(rename = "DELETING")]
Deleting,
#[serde(rename = "ACTIVE")]
Active,
}
impl IndexStatus {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Creating => "CREATING",
Self::Updating => "UPDATING",
Self::Deleting => "DELETING",
Self::Active => "ACTIVE",
}
}
}
impl std::fmt::Display for IndexStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub enum ReturnValue {
#[default]
#[serde(rename = "NONE")]
None,
#[serde(rename = "ALL_OLD")]
AllOld,
#[serde(rename = "UPDATED_OLD")]
UpdatedOld,
#[serde(rename = "ALL_NEW")]
AllNew,
#[serde(rename = "UPDATED_NEW")]
UpdatedNew,
}
impl ReturnValue {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::None => "NONE",
Self::AllOld => "ALL_OLD",
Self::UpdatedOld => "UPDATED_OLD",
Self::AllNew => "ALL_NEW",
Self::UpdatedNew => "UPDATED_NEW",
}
}
}
impl std::fmt::Display for ReturnValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub enum ReturnConsumedCapacity {
#[serde(rename = "INDEXES")]
Indexes,
#[serde(rename = "TOTAL")]
Total,
#[default]
#[serde(rename = "NONE")]
None,
}
impl ReturnConsumedCapacity {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Indexes => "INDEXES",
Self::Total => "TOTAL",
Self::None => "NONE",
}
}
#[must_use]
pub fn should_report(&self) -> bool {
!matches!(self, Self::None)
}
#[must_use]
pub fn should_report_indexes(&self) -> bool {
matches!(self, Self::Indexes)
}
}
impl std::fmt::Display for ReturnConsumedCapacity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub enum ReturnItemCollectionMetrics {
#[serde(rename = "SIZE")]
Size,
#[default]
#[serde(rename = "NONE")]
None,
}
impl ReturnItemCollectionMetrics {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Size => "SIZE",
Self::None => "NONE",
}
}
#[must_use]
pub fn should_report(&self) -> bool {
matches!(self, Self::Size)
}
}
impl std::fmt::Display for ReturnItemCollectionMetrics {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub enum Select {
#[default]
#[serde(rename = "ALL_ATTRIBUTES")]
AllAttributes,
#[serde(rename = "ALL_PROJECTED_ATTRIBUTES")]
AllProjectedAttributes,
#[serde(rename = "SPECIFIC_ATTRIBUTES")]
SpecificAttributes,
#[serde(rename = "COUNT")]
Count,
}
impl Select {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::AllAttributes => "ALL_ATTRIBUTES",
Self::AllProjectedAttributes => "ALL_PROJECTED_ATTRIBUTES",
Self::SpecificAttributes => "SPECIFIC_ATTRIBUTES",
Self::Count => "COUNT",
}
}
}
impl std::fmt::Display for Select {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub enum ConditionalOperator {
#[default]
#[serde(rename = "AND")]
And,
#[serde(rename = "OR")]
Or,
}
impl ConditionalOperator {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::And => "AND",
Self::Or => "OR",
}
}
}
impl std::fmt::Display for ConditionalOperator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ComparisonOperator {
#[serde(rename = "EQ")]
Eq,
#[serde(rename = "NE")]
Ne,
#[serde(rename = "LE")]
Le,
#[serde(rename = "LT")]
Lt,
#[serde(rename = "GE")]
Ge,
#[serde(rename = "GT")]
Gt,
#[serde(rename = "NOT_NULL")]
NotNull,
#[serde(rename = "NULL")]
Null,
#[serde(rename = "CONTAINS")]
Contains,
#[serde(rename = "NOT_CONTAINS")]
NotContains,
#[serde(rename = "BEGINS_WITH")]
BeginsWith,
#[serde(rename = "IN")]
In,
#[serde(rename = "BETWEEN")]
Between,
}
impl ComparisonOperator {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Eq => "EQ",
Self::Ne => "NE",
Self::Le => "LE",
Self::Lt => "LT",
Self::Ge => "GE",
Self::Gt => "GT",
Self::NotNull => "NOT_NULL",
Self::Null => "NULL",
Self::Contains => "CONTAINS",
Self::NotContains => "NOT_CONTAINS",
Self::BeginsWith => "BEGINS_WITH",
Self::In => "IN",
Self::Between => "BETWEEN",
}
}
}
impl std::fmt::Display for ComparisonOperator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct KeySchemaElement {
pub attribute_name: String,
pub key_type: KeyType,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct AttributeDefinition {
pub attribute_name: String,
pub attribute_type: ScalarAttributeType,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct BillingModeSummary {
#[serde(skip_serializing_if = "Option::is_none")]
pub billing_mode: Option<BillingMode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_update_to_pay_per_request_date_time: Option<f64>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ProvisionedThroughput {
pub read_capacity_units: i64,
pub write_capacity_units: i64,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ProvisionedThroughputDescription {
pub read_capacity_units: i64,
pub write_capacity_units: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub number_of_decreases_today: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_increase_date_time: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_decrease_date_time: Option<f64>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Projection {
#[serde(skip_serializing_if = "Option::is_none")]
pub projection_type: Option<ProjectionType>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub non_key_attributes: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct GlobalSecondaryIndex {
pub index_name: String,
pub key_schema: Vec<KeySchemaElement>,
pub projection: Projection,
#[serde(skip_serializing_if = "Option::is_none")]
pub provisioned_throughput: Option<ProvisionedThroughput>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct GlobalSecondaryIndexDescription {
#[serde(skip_serializing_if = "Option::is_none")]
pub index_name: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub key_schema: Vec<KeySchemaElement>,
#[serde(skip_serializing_if = "Option::is_none")]
pub projection: Option<Projection>,
#[serde(skip_serializing_if = "Option::is_none")]
pub index_status: Option<IndexStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub backfilling: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub provisioned_throughput: Option<ProvisionedThroughputDescription>,
#[serde(skip_serializing_if = "Option::is_none")]
pub index_size_bytes: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub item_count: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub index_arn: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct LocalSecondaryIndex {
pub index_name: String,
pub key_schema: Vec<KeySchemaElement>,
pub projection: Projection,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct LocalSecondaryIndexDescription {
#[serde(skip_serializing_if = "Option::is_none")]
pub index_name: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub key_schema: Vec<KeySchemaElement>,
#[serde(skip_serializing_if = "Option::is_none")]
pub projection: Option<Projection>,
#[serde(skip_serializing_if = "Option::is_none")]
pub index_size_bytes: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub item_count: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub index_arn: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct StreamSpecification {
pub stream_enabled: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream_view_type: Option<StreamViewType>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SSESpecification {
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(rename = "SSEType", skip_serializing_if = "Option::is_none")]
pub sse_type: Option<SseType>,
#[serde(rename = "KMSMasterKeyId", skip_serializing_if = "Option::is_none")]
pub kms_master_key_id: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SSEDescription {
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<SseStatus>,
#[serde(rename = "SSEType", skip_serializing_if = "Option::is_none")]
pub sse_type: Option<SseType>,
#[serde(rename = "KMSMasterKeyId", skip_serializing_if = "Option::is_none")]
pub kms_master_key_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub inaccessible_encryption_date_time: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TimeToLiveSpecification {
pub attribute_name: String,
pub enabled: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TimeToLiveDescription {
#[serde(skip_serializing_if = "Option::is_none")]
pub attribute_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time_to_live_status: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TransactWriteItem {
#[serde(skip_serializing_if = "Option::is_none")]
pub condition_check: Option<ConditionCheck>,
#[serde(skip_serializing_if = "Option::is_none")]
pub put: Option<TransactPut>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delete: Option<TransactDelete>,
#[serde(skip_serializing_if = "Option::is_none")]
pub update: Option<TransactUpdate>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ConditionCheck {
pub table_name: String,
pub key: HashMap<String, AttributeValue>,
pub condition_expression: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub expression_attribute_names: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expression_attribute_values: Option<HashMap<String, AttributeValue>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub return_values_on_condition_check_failure: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TransactPut {
pub table_name: String,
pub item: HashMap<String, AttributeValue>,
#[serde(skip_serializing_if = "Option::is_none")]
pub condition_expression: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expression_attribute_names: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expression_attribute_values: Option<HashMap<String, AttributeValue>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub return_values_on_condition_check_failure: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TransactDelete {
pub table_name: String,
pub key: HashMap<String, AttributeValue>,
#[serde(skip_serializing_if = "Option::is_none")]
pub condition_expression: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expression_attribute_names: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expression_attribute_values: Option<HashMap<String, AttributeValue>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub return_values_on_condition_check_failure: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TransactUpdate {
pub table_name: String,
pub key: HashMap<String, AttributeValue>,
pub update_expression: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub condition_expression: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expression_attribute_names: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expression_attribute_values: Option<HashMap<String, AttributeValue>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub return_values_on_condition_check_failure: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TransactGetItem {
pub get: Get,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Get {
pub table_name: String,
pub key: HashMap<String, AttributeValue>,
#[serde(skip_serializing_if = "Option::is_none")]
pub projection_expression: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expression_attribute_names: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CancellationReason {
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub item: Option<HashMap<String, AttributeValue>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ItemResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub item: Option<HashMap<String, AttributeValue>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Tag {
pub key: String,
pub value: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TableDescription {
#[serde(skip_serializing_if = "Option::is_none")]
pub table_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub table_status: Option<TableStatus>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub key_schema: Vec<KeySchemaElement>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub attribute_definitions: Vec<AttributeDefinition>,
#[serde(skip_serializing_if = "Option::is_none")]
pub creation_date_time: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub item_count: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub table_size_bytes: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub table_arn: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub table_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub billing_mode_summary: Option<BillingModeSummary>,
#[serde(skip_serializing_if = "Option::is_none")]
pub provisioned_throughput: Option<ProvisionedThroughputDescription>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub global_secondary_indexes: Vec<GlobalSecondaryIndexDescription>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub local_secondary_indexes: Vec<LocalSecondaryIndexDescription>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream_specification: Option<StreamSpecification>,
#[serde(skip_serializing_if = "Option::is_none")]
pub latest_stream_arn: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub latest_stream_label: Option<String>,
#[serde(rename = "SSEDescription", skip_serializing_if = "Option::is_none")]
pub sse_description: Option<SSEDescription>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deletion_protection_enabled: Option<bool>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Capacity {
#[serde(skip_serializing_if = "Option::is_none")]
pub read_capacity_units: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub write_capacity_units: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub capacity_units: Option<f64>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ConsumedCapacity {
#[serde(skip_serializing_if = "Option::is_none")]
pub table_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub capacity_units: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub read_capacity_units: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub write_capacity_units: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub table: Option<Capacity>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub local_secondary_indexes: HashMap<String, Capacity>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub global_secondary_indexes: HashMap<String, Capacity>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ItemCollectionMetrics {
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub item_collection_key: HashMap<String, AttributeValue>,
#[serde(
rename = "SizeEstimateRangeGB",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub size_estimate_range_gb: Vec<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Condition {
pub comparison_operator: ComparisonOperator,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub attribute_value_list: Vec<AttributeValue>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub enum AttributeAction {
#[default]
#[serde(rename = "PUT")]
Put,
#[serde(rename = "DELETE")]
Delete,
#[serde(rename = "ADD")]
Add,
}
impl AttributeAction {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Put => "PUT",
Self::Delete => "DELETE",
Self::Add => "ADD",
}
}
}
impl std::fmt::Display for AttributeAction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct AttributeValueUpdate {
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<AttributeValue>,
#[serde(skip_serializing_if = "Option::is_none")]
pub action: Option<AttributeAction>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ExpectedAttributeValue {
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<AttributeValue>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exists: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comparison_operator: Option<ComparisonOperator>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub attribute_value_list: Vec<AttributeValue>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct KeysAndAttributes {
pub keys: Vec<HashMap<String, AttributeValue>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub projection_expression: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expression_attribute_names: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub consistent_read: Option<bool>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub attributes_to_get: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct WriteRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub put_request: Option<PutRequest>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delete_request: Option<DeleteRequest>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct PutRequest {
pub item: HashMap<String, AttributeValue>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct DeleteRequest {
pub key: HashMap<String, AttributeValue>,
}
pub type Item = HashMap<String, AttributeValue>;
pub type Key = HashMap<String, AttributeValue>;
pub type ExpressionAttributeNames = HashMap<String, String>;
pub type ExpressionAttributeValues = HashMap<String, AttributeValue>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_should_serialize_key_schema_element() {
let elem = KeySchemaElement {
attribute_name: "pk".to_owned(),
key_type: KeyType::Hash,
};
let json = serde_json::to_string(&elem).expect("serialize KeySchemaElement");
assert_eq!(json, r#"{"AttributeName":"pk","KeyType":"HASH"}"#);
}
#[test]
fn test_should_roundtrip_attribute_definition() {
let def = AttributeDefinition {
attribute_name: "id".to_owned(),
attribute_type: ScalarAttributeType::S,
};
let json = serde_json::to_string(&def).expect("serialize AttributeDefinition");
let parsed: AttributeDefinition =
serde_json::from_str(&json).expect("deserialize AttributeDefinition");
assert_eq!(def.attribute_name, parsed.attribute_name);
assert_eq!(def.attribute_type, parsed.attribute_type);
}
#[test]
fn test_should_serialize_table_status() {
let status = TableStatus::Active;
let json = serde_json::to_string(&status).expect("serialize TableStatus");
assert_eq!(json, r#""ACTIVE""#);
}
#[test]
fn test_should_default_billing_mode_to_pay_per_request() {
let mode = BillingMode::default();
assert_eq!(mode, BillingMode::PayPerRequest);
}
#[test]
fn test_should_default_return_value_to_none() {
let rv = ReturnValue::default();
assert_eq!(rv, ReturnValue::None);
}
#[test]
fn test_should_serialize_provisioned_throughput() {
let pt = ProvisionedThroughput {
read_capacity_units: 5,
write_capacity_units: 10,
};
let json = serde_json::to_string(&pt).expect("serialize ProvisionedThroughput");
assert_eq!(json, r#"{"ReadCapacityUnits":5,"WriteCapacityUnits":10}"#);
}
#[test]
fn test_should_skip_none_fields_in_table_description() {
let desc = TableDescription {
table_name: Some("TestTable".to_owned()),
table_status: Some(TableStatus::Active),
..Default::default()
};
let json = serde_json::to_string(&desc).expect("serialize TableDescription");
assert!(json.contains(r#""TableName":"TestTable""#));
assert!(json.contains(r#""TableStatus":"ACTIVE""#));
assert!(!json.contains("TableArn"));
assert!(!json.contains("KeySchema"));
assert!(!json.contains("GlobalSecondaryIndexes"));
}
#[test]
fn test_should_roundtrip_projection() {
let proj = Projection {
projection_type: Some(ProjectionType::Include),
non_key_attributes: vec!["email".to_owned(), "name".to_owned()],
};
let json = serde_json::to_string(&proj).expect("serialize Projection");
let parsed: Projection = serde_json::from_str(&json).expect("deserialize Projection");
assert_eq!(proj.projection_type, parsed.projection_type);
assert_eq!(proj.non_key_attributes, parsed.non_key_attributes);
}
#[test]
fn test_should_serialize_write_request_with_put() {
let mut item = HashMap::new();
item.insert("id".to_owned(), AttributeValue::S("123".to_owned()));
let req = WriteRequest {
put_request: Some(PutRequest { item }),
delete_request: Option::None,
};
let json = serde_json::to_string(&req).expect("serialize WriteRequest");
assert!(json.contains("PutRequest"));
assert!(!json.contains("DeleteRequest"));
}
#[test]
fn test_should_serialize_write_request_with_delete() {
let mut key = HashMap::new();
key.insert("id".to_owned(), AttributeValue::S("456".to_owned()));
let req = WriteRequest {
put_request: Option::None,
delete_request: Some(DeleteRequest { key }),
};
let json = serde_json::to_string(&req).expect("serialize WriteRequest");
assert!(json.contains("DeleteRequest"));
assert!(!json.contains("PutRequest"));
}
#[test]
fn test_should_serialize_condition() {
let cond = Condition {
comparison_operator: ComparisonOperator::Eq,
attribute_value_list: vec![AttributeValue::S("test".to_owned())],
};
let json = serde_json::to_string(&cond).expect("serialize Condition");
assert!(json.contains(r#""ComparisonOperator":"EQ""#));
assert!(json.contains("AttributeValueList"));
}
#[test]
fn test_should_roundtrip_consumed_capacity() {
let cap = ConsumedCapacity {
table_name: Some("Orders".to_owned()),
capacity_units: Some(5.0),
..Default::default()
};
let json = serde_json::to_string(&cap).expect("serialize ConsumedCapacity");
let parsed: ConsumedCapacity =
serde_json::from_str(&json).expect("deserialize ConsumedCapacity");
assert_eq!(cap.table_name, parsed.table_name);
assert_eq!(cap.capacity_units, parsed.capacity_units);
}
#[test]
fn test_should_serialize_sse_specification() {
let sse = SSESpecification {
enabled: Some(true),
sse_type: Some(SseType::Kms),
kms_master_key_id: Some("arn:aws:kms:us-east-1:123456789012:key/abc".to_owned()),
};
let json = serde_json::to_string(&sse).expect("serialize SSESpecification");
assert!(json.contains(r#""SSEType":"KMS""#));
assert!(json.contains(r#""KMSMasterKeyId":"arn:aws:kms"#));
}
#[test]
fn test_should_serialize_tag() {
let tag = Tag {
key: "Environment".to_owned(),
value: "Production".to_owned(),
};
let json = serde_json::to_string(&tag).expect("serialize Tag");
assert_eq!(json, r#"{"Key":"Environment","Value":"Production"}"#);
}
#[test]
fn test_should_serialize_stream_specification() {
let spec = StreamSpecification {
stream_enabled: true,
stream_view_type: Some(StreamViewType::NewAndOldImages),
};
let json = serde_json::to_string(&spec).expect("serialize StreamSpecification");
assert!(json.contains(r#""StreamEnabled":true"#));
assert!(json.contains(r#""StreamViewType":"NEW_AND_OLD_IMAGES""#));
}
#[test]
fn test_should_serialize_keys_and_attributes() {
let mut key = HashMap::new();
key.insert("pk".to_owned(), AttributeValue::S("user-1".to_owned()));
let ka = KeysAndAttributes {
keys: vec![key],
projection_expression: Some("id, #n".to_owned()),
expression_attribute_names: Some(HashMap::from([("#n".to_owned(), "name".to_owned())])),
consistent_read: Some(true),
attributes_to_get: Vec::new(),
};
let json = serde_json::to_string(&ka).expect("serialize KeysAndAttributes");
assert!(json.contains("ProjectionExpression"));
assert!(json.contains("ExpressionAttributeNames"));
assert!(json.contains("ConsistentRead"));
assert!(!json.contains("AttributesToGet"));
}
#[test]
fn test_should_roundtrip_global_secondary_index() {
let gsi = GlobalSecondaryIndex {
index_name: "gsi-email".to_owned(),
key_schema: vec![KeySchemaElement {
attribute_name: "email".to_owned(),
key_type: KeyType::Hash,
}],
projection: Projection {
projection_type: Some(ProjectionType::All),
non_key_attributes: Vec::new(),
},
provisioned_throughput: Option::None,
};
let json = serde_json::to_string(&gsi).expect("serialize GlobalSecondaryIndex");
let parsed: GlobalSecondaryIndex =
serde_json::from_str(&json).expect("deserialize GlobalSecondaryIndex");
assert_eq!(gsi.index_name, parsed.index_name);
assert_eq!(gsi.key_schema.len(), parsed.key_schema.len());
}
#[test]
fn test_should_display_all_enum_variants() {
assert_eq!(KeyType::Hash.to_string(), "HASH");
assert_eq!(KeyType::Range.to_string(), "RANGE");
assert_eq!(ScalarAttributeType::S.to_string(), "S");
assert_eq!(ScalarAttributeType::N.to_string(), "N");
assert_eq!(ScalarAttributeType::B.to_string(), "B");
assert_eq!(TableStatus::Active.to_string(), "ACTIVE");
assert_eq!(TableStatus::Creating.to_string(), "CREATING");
assert_eq!(BillingMode::PayPerRequest.to_string(), "PAY_PER_REQUEST");
assert_eq!(ProjectionType::KeysOnly.to_string(), "KEYS_ONLY");
assert_eq!(
StreamViewType::NewAndOldImages.to_string(),
"NEW_AND_OLD_IMAGES"
);
assert_eq!(SseType::Kms.to_string(), "KMS");
assert_eq!(SseStatus::Enabled.to_string(), "ENABLED");
assert_eq!(IndexStatus::Active.to_string(), "ACTIVE");
assert_eq!(ReturnValue::AllNew.to_string(), "ALL_NEW");
assert_eq!(ReturnConsumedCapacity::Indexes.to_string(), "INDEXES");
assert_eq!(ReturnItemCollectionMetrics::Size.to_string(), "SIZE");
assert_eq!(Select::Count.to_string(), "COUNT");
assert_eq!(ConditionalOperator::And.to_string(), "AND");
assert_eq!(ComparisonOperator::BeginsWith.to_string(), "BEGINS_WITH");
}
#[test]
fn test_should_report_consumed_capacity_settings() {
assert!(!ReturnConsumedCapacity::None.should_report());
assert!(ReturnConsumedCapacity::Total.should_report());
assert!(ReturnConsumedCapacity::Indexes.should_report());
assert!(!ReturnConsumedCapacity::Total.should_report_indexes());
assert!(ReturnConsumedCapacity::Indexes.should_report_indexes());
}
#[test]
fn test_should_report_item_collection_metrics_settings() {
assert!(!ReturnItemCollectionMetrics::None.should_report());
assert!(ReturnItemCollectionMetrics::Size.should_report());
}
#[test]
fn test_should_deserialize_table_description_from_dynamodb_json() {
let json = r#"{
"TableName": "Users",
"TableStatus": "ACTIVE",
"KeySchema": [
{"AttributeName": "pk", "KeyType": "HASH"},
{"AttributeName": "sk", "KeyType": "RANGE"}
],
"AttributeDefinitions": [
{"AttributeName": "pk", "AttributeType": "S"},
{"AttributeName": "sk", "AttributeType": "S"}
],
"CreationDateTime": 1709136000.0,
"ItemCount": 42,
"TableSizeBytes": 8192,
"TableArn": "arn:aws:dynamodb:us-east-1:123456789012:table/Users",
"TableId": "abc-123-def",
"ProvisionedThroughput": {
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 10,
"NumberOfDecreasesToday": 0
},
"BillingModeSummary": {
"BillingMode": "PROVISIONED"
}
}"#;
let desc: TableDescription =
serde_json::from_str(json).expect("deserialize TableDescription");
assert_eq!(desc.table_name.as_deref(), Some("Users"));
assert_eq!(desc.table_status, Some(TableStatus::Active));
assert_eq!(desc.key_schema.len(), 2);
assert_eq!(desc.attribute_definitions.len(), 2);
assert_eq!(desc.item_count, Some(42));
assert_eq!(desc.table_size_bytes, Some(8192));
assert_eq!(desc.table_id.as_deref(), Some("abc-123-def"));
assert_eq!(
desc.billing_mode_summary
.as_ref()
.and_then(|b| b.billing_mode.as_ref()),
Some(&BillingMode::Provisioned)
);
}
#[test]
fn test_should_roundtrip_all_comparison_operators() {
let operators = [
ComparisonOperator::Eq,
ComparisonOperator::Ne,
ComparisonOperator::Le,
ComparisonOperator::Lt,
ComparisonOperator::Ge,
ComparisonOperator::Gt,
ComparisonOperator::NotNull,
ComparisonOperator::Null,
ComparisonOperator::Contains,
ComparisonOperator::NotContains,
ComparisonOperator::BeginsWith,
ComparisonOperator::In,
ComparisonOperator::Between,
];
for op in &operators {
let json = serde_json::to_string(op).expect("serialize ComparisonOperator");
let parsed: ComparisonOperator =
serde_json::from_str(&json).expect("deserialize ComparisonOperator");
assert_eq!(op, &parsed);
}
}
#[test]
fn test_should_roundtrip_all_return_values() {
let values = [
ReturnValue::None,
ReturnValue::AllOld,
ReturnValue::UpdatedOld,
ReturnValue::AllNew,
ReturnValue::UpdatedNew,
];
for rv in &values {
let json = serde_json::to_string(rv).expect("serialize ReturnValue");
let parsed: ReturnValue = serde_json::from_str(&json).expect("deserialize ReturnValue");
assert_eq!(rv, &parsed);
}
}
#[test]
fn test_should_serialize_item_collection_metrics() {
let mut key = HashMap::new();
key.insert("pk".to_owned(), AttributeValue::S("user-1".to_owned()));
let metrics = ItemCollectionMetrics {
item_collection_key: key,
size_estimate_range_gb: vec![0.5, 1.0],
};
let json = serde_json::to_string(&metrics).expect("serialize ItemCollectionMetrics");
assert!(json.contains("ItemCollectionKey"));
assert!(json.contains("SizeEstimateRangeGB"));
}
#[test]
fn test_should_serialize_billing_mode_summary() {
let summary = BillingModeSummary {
billing_mode: Some(BillingMode::PayPerRequest),
last_update_to_pay_per_request_date_time: Some(1_709_136_000.0),
};
let json = serde_json::to_string(&summary).expect("serialize BillingModeSummary");
assert!(json.contains(r#""BillingMode":"PAY_PER_REQUEST""#));
assert!(json.contains("LastUpdateToPayPerRequestDateTime"));
}
#[test]
fn test_should_serialize_gsi_description_with_all_fields() {
let desc = GlobalSecondaryIndexDescription {
index_name: Some("gsi-status".to_owned()),
key_schema: vec![KeySchemaElement {
attribute_name: "status".to_owned(),
key_type: KeyType::Hash,
}],
projection: Some(Projection {
projection_type: Some(ProjectionType::All),
..Default::default()
}),
index_status: Some(IndexStatus::Active),
backfilling: Some(false),
provisioned_throughput: Some(ProvisionedThroughputDescription {
read_capacity_units: 5,
write_capacity_units: 5,
..Default::default()
}),
index_size_bytes: Some(1024),
item_count: Some(10),
index_arn: Some(
"arn:aws:dynamodb:us-east-1:123456789012:table/T/index/gsi-status".to_owned(),
),
};
let json = serde_json::to_string(&desc).expect("serialize GlobalSecondaryIndexDescription");
let parsed: GlobalSecondaryIndexDescription =
serde_json::from_str(&json).expect("deserialize GlobalSecondaryIndexDescription");
assert_eq!(desc.index_name, parsed.index_name);
assert_eq!(desc.index_status, parsed.index_status);
assert_eq!(desc.item_count, parsed.item_count);
}
}