dynamodb-facade 0.1.1

A typed facade over aws-sdk-dynamodb with expression builders and batch/transaction support
Documentation
mod attribute_list;
mod attribute_ref;
mod attributes;
mod markers;

pub(crate) use attribute_ref::*;
pub(crate) use markers::*;

pub use attribute_list::*;
pub use attributes::*;

mod sealed_traits {
    /// Seals [`KeySchemaKind`](super::KeySchemaKind) so only `SimpleKey` and `CompositeKey` can implement it.
    pub trait KeySchemaKindSeal {}
}

crate::utils::impl_sealed_marker_types!(
    /// Sealed marker trait for key schema kinds.
    ///
    /// Implemented only by [`SimpleKey`] and [`CompositeKey`]. This trait is
    /// sealed and cannot be implemented outside of this crate. It is used as a
    /// bound on [`KeySchema::Kind`] to restrict the set of valid key schema
    /// configurations.
    KeySchemaKind,
    sealed_traits::KeySchemaKindSeal;
    /// Marker type indicating a key schema with a partition key only (no sort key).
    ///
    /// Used as the `Kind` associated type on [`KeySchema`] implementations
    /// generated by [`key_schema!`](crate::key_schema) or
    /// [`table_definitions!`](crate::table_definitions) when no `SortKey` is
    /// declared. A table or index with this kind implements
    /// [`SimpleKeySchema`].
    ///
    /// # Examples
    ///
    /// ```
    /// # use dynamodb_facade::test_fixtures::*;
    /// use dynamodb_facade::{key_schema, KeySchema, SimpleKey};
    ///
    /// key_schema! {
    ///     SimpleSchema {
    ///         type PartitionKey = PK;
    ///     }
    /// }
    ///
    /// fn _assert<KS: KeySchema<Kind = SimpleKey>>() {}
    /// _assert::<SimpleSchema>();
    /// ```
    SimpleKey,
    /// Marker type indicating a key schema with both a partition key and a sort key.
    ///
    /// Used as the `Kind` associated type on [`KeySchema`] implementations
    /// generated by [`key_schema!`](crate::key_schema) or
    /// [`table_definitions!`](crate::table_definitions) when both `PartitionKey`
    /// and `SortKey` are declared. A table or index with this kind implements
    /// [`CompositeKeySchema`].
    ///
    /// # Examples
    ///
    /// ```
    /// # use dynamodb_facade::test_fixtures::*;
    /// use dynamodb_facade::{key_schema, CompositeKey, KeySchema};
    ///
    /// key_schema! {
    ///     CompositeSchema {
    ///         type PartitionKey = PK;
    ///         type SortKey = SK;
    ///     }
    /// }
    ///
    /// fn _assert<KS: KeySchema<Kind = CompositeKey>>() {}
    /// _assert::<CompositeSchema>();
    /// ```
    CompositeKey
);

/// Ensures that a [`KeySchema`] implementation is internally consistent.
///
/// Prevents invalid key schema declarations — for example, declaring a
/// composite key schema without providing a sort key type. This trait is
/// automatically satisfied for correctly-formed key schemas; you do not
/// implement or call it directly.
pub trait ValidKeySchema<KSK: KeySchemaKind> {}

/// Defines the key schema for a DynamoDB table or index.
///
/// A key schema specifies the partition key attribute and the key kind
/// ([`SimpleKey`] or [`CompositeKey`]). Implementations are generated by
/// [`key_schema!`](crate::key_schema), [`table_definitions!`](crate::table_definitions),
/// and [`index_definitions!`](crate::index_definitions) — you rarely implement
/// this trait manually.
///
/// For composite key schemas, also implement [`CompositeKeySchema`] to expose
/// the sort key type. For simple key schemas, also implement [`SimpleKeySchema`].
///
/// # Examples
///
/// ```
/// # use dynamodb_facade::test_fixtures::*;
/// use dynamodb_facade::{KeySchema, TableSchema};
///
/// fn _assert<KS: KeySchema>() {}
/// // PlatformTable's KeySchema associated type implements the KeySchema trait.
/// _assert::<TableSchema<PlatformTable>>();
/// ```
pub trait KeySchema: ValidKeySchema<Self::Kind> {
    /// Whether this schema has a sort key ([`CompositeKey`]) or not ([`SimpleKey`]).
    type Kind: KeySchemaKind;
    /// The partition key attribute definition.
    type PartitionKey: AttributeDefinition;
}

/// A [`KeySchema`] with only a partition key (no sort key).
///
/// Implemented automatically by [`key_schema!`](crate::key_schema),
/// [`table_definitions!`](crate::table_definitions) and
/// [`index_definitions!`](crate::index_definitions) when no `SortKey` is
/// declared. Tables and indexes with this schema do not expose sort-key
/// methods on their query builders at compile time.
///
/// # Examples
///
/// ```
/// # use dynamodb_facade::test_fixtures::*;
/// use dynamodb_facade::{IndexSchema, SimpleKeySchema};
///
/// fn _assert_simple<KS: SimpleKeySchema>() {}
/// // TypeIndex has only a PartitionKey.
/// _assert_simple::<IndexSchema<PlatformTable, TypeIndex>>();
/// ```
pub trait SimpleKeySchema: KeySchema<Kind = SimpleKey> {}

/// A [`KeySchema`] with both a partition key and a sort key.
///
/// Implemented automatically by [`key_schema!`](crate::key_schema),
/// [`table_definitions!`](crate::table_definitions) and
/// [`index_definitions!`](crate::index_definitions) when both `PartitionKey`
/// and `SortKey` are declared. Tables and indexes with this schema expose
/// sort-key methods on their query builders (e.g. `sk_eq`, `sk_begins_with`).
///
/// # Examples
///
/// ```
/// # use dynamodb_facade::test_fixtures::*;
/// use dynamodb_facade::{CompositeKeySchema, TableSchema};
///
/// fn _assert_composite<KS: CompositeKeySchema>() {}
/// // PlatformTable's key schema has both PK and SK.
/// _assert_composite::<TableSchema<PlatformTable>>();
/// ```
pub trait CompositeKeySchema: KeySchema<Kind = CompositeKey> {
    /// The sort key attribute definition.
    type SortKey: AttributeDefinition;
}
impl<KS: KeySchema<Kind = SimpleKey> + SimpleKeySchema> ValidKeySchema<SimpleKey> for KS {}
impl<KS: KeySchema<Kind = CompositeKey> + CompositeKeySchema> ValidKeySchema<CompositeKey> for KS {}

/// Defines a DynamoDB table: its name and key schema.
///
/// Implementations are generated by [`table_definitions!`](crate::table_definitions).
/// The associated [`KeySchema`] determines whether the table uses a simple or
/// composite key, and which attribute definitions serve as partition and sort keys.
///
/// All items, keys, and operation builders are scoped to a specific table
/// through this trait, ensuring you cannot accidentally mix items or keys
/// from different tables.
///
/// # Examples
///
/// ```
/// # use dynamodb_facade::test_fixtures::*;
/// use dynamodb_facade::{table_definitions, TableDefinition};
///
/// table_definitions! {
///     MyTable {
///         type PartitionKey = PK;
///         type SortKey = SK;
///         fn table_name() -> String { "platform".to_owned() }
///     }
/// }
///
/// fn expect_table<TD: TableDefinition>() {}
/// expect_table::<MyTable>();
/// assert_eq!(MyTable::table_name(), "platform");
/// ```
pub trait TableDefinition {
    /// The key schema for this table.
    type KeySchema: KeySchema;
    /// Returns the DynamoDB table name.
    fn table_name() -> String;
}

/// Convenience alias for the [`KeySchema`] of a [`TableDefinition`].
///
/// Use this alias in generic bounds and function signatures to avoid
/// spelling out the full associated-type projection every time you need
/// to refer to a table's key schema type.
///
/// # Examples
///
/// ```
/// # use dynamodb_facade::test_fixtures::*;
/// use dynamodb_facade::{CompositeKeySchema, TableDefinition, TableSchema};
///
/// // Require that a table's key schema is composite (has both PK and SK).
/// fn requires_composite_table<TD: TableDefinition>()
/// where
///     TableSchema<TD>: CompositeKeySchema,
/// {}
///
/// // PlatformTable has PK + SK, so this compiles.
/// requires_composite_table::<PlatformTable>();
/// ```
pub type TableSchema<TD> = <TD as TableDefinition>::KeySchema;

/// Defines a DynamoDB Global Secondary Index (GSI) or Local Secondary Index (GSI) on
/// a specific table.
///
/// Implementations are generated by [`index_definitions!`](crate::index_definitions).
/// Each index is associated with a parent table and has its own [`KeySchema`]
/// describing the index's partition (and optional sort) key.
///
/// Index types are used as turbofish parameters on query methods such as
/// `T::query_index::<MyIndex>(client, key_condition)`.
///
/// # Examples
///
/// ```
/// # use dynamodb_facade::test_fixtures::*;
/// use dynamodb_facade::{index_definitions, IndexDefinition};
///
/// index_definitions! {
///     #[table = PlatformTable]
///     MyIndex {
///         type PartitionKey = ItemType;
///         fn index_name() -> String { "iCustomIndex".to_owned() }
///     }
/// }
///
/// fn expect_index<TD: IndexDefinition<PlatformTable>>() {}
/// expect_index::<MyIndex>();
/// assert_eq!(MyIndex::index_name(), "iCustomIndex");
/// ```
pub trait IndexDefinition<TD: TableDefinition> {
    /// The key schema for this index.
    type KeySchema: KeySchema;
    /// Returns the DynamoDB index name.
    fn index_name() -> String;
}

/// Convenience alias for the [`KeySchema`] of an [`IndexDefinition`].
///
/// Use this alias in generic bounds and function signatures to avoid
/// spelling out the full associated-type projection every time you need
/// to refer to an index's key schema type.
///
/// # Examples
///
/// ```
/// # use dynamodb_facade::test_fixtures::*;
/// use dynamodb_facade::{IndexDefinition, IndexSchema, SimpleKeySchema};
///
/// // Require that an index's key schema is simple (partition key only).
/// fn requires_simple_index<I: IndexDefinition<PlatformTable>>()
/// where
///     IndexSchema<PlatformTable, I>: SimpleKeySchema,
/// {}
///
/// // TypeIndex has only a partition key, so this compiles.
/// requires_simple_index::<TypeIndex>();
/// ```
pub type IndexSchema<TD, I> = <I as IndexDefinition<TD>>::KeySchema;