macro_rules! dynamodb_item {
{
#[table = $table:path]
$item:ty {
$(
#[$attr_mod:ident]
$modified_attr:path {$($modified_blk:tt)+}
)+
$(
$attr:path {$($blk:tt)+}
)*
}
} => { ... };
{
#[table = $table:path]
$item:ty {
$(
#[$attr_mod:ident]
$modified_attr:path {$($modified_blk:tt)+}
)*
$(
$attr_before:path {$($blk_before:tt)+}
)+
#[$attr_mod_after:ident]
$modified_attr_after:path {$($modified_blk_after:tt)+}
$($rest:tt)*
}
} => { ... };
{
@modtop
#[table = $table:path]
$item:ty {
#[partition_key]
$pk_attr:path {$($pk_blk:tt)+}
$(
#[$attr_mod:ident]
$modified_attr:path {$($modified_blk:tt)+}
)*
$(
$attr:path {$($blk:tt)+}
)*
}
} => { ... };
{
@modtop
#[table = $table:path]
$item:ty {
#[$first_attr_mod:ident]
$first_modified_attr:path {$($first_modified_blk:tt)+}
$(
#[$attr_mod:ident]
$modified_attr:path {$($modified_blk:tt)+}
)+
$(
$attr:path {$($blk:tt)+}
)*
}
} => { ... };
{
@pktop
#[table = $table:path]
$item:ty {
#[partition_key]
$pk_attr:path {$($pk_blk:tt)+}
#[sort_key]
$sk_attr:path {$($sk_blk:tt)+}
$(
#[$attr_mod:ident]
$modified_attr:path {$($modified_blk:tt)+}
)*
$(
$attr:path {$($blk:tt)+}
)*
$(
@barier
$(
#[$attr_mod_after:ident]
$modified_attr_after:path {$($modified_blk_after:tt)+}
)+
)?
}
} => { ... };
{
@pktop
#[table = $table:path]
$item:ty {
#[partition_key]
$pk_attr:path {$($pk_blk:tt)+}
#[$first_attr_mod:ident]
$first_modified_attr:path {$($first_modified_blk:tt)+}
$(
#[$attr_mod:ident]
$modified_attr:path {$($modified_blk:tt)+}
)*
$(
$attr:path {$($blk:tt)+}
)*
$(
@barier
$(
#[$attr_mod_after:ident]
$modified_attr_after:path {$($modified_blk_after:tt)+}
)+
)?
}
} => { ... };
{
@pktop
#[table = $table:path]
$item:ty {
#[partition_key]
$pk_attr:path {$($pk_blk:tt)+}
$(
$attr:path {$($blk:tt)+}
)*
$(
@barier
$(
#[$attr_mod_after:ident]
$modified_attr_after:path {$($modified_blk_after:tt)+}
)+
)?
}
} => { ... };
{
@allsorted
#[table = $table:path]
$item:ty {
#[partition_key]
$pk_attr:path {$($pk_blk:tt)+}
$(
#[sort_key]
$sk_attr:path {$($sk_blk:tt)+}
)?
$(
#[marker_only]
$marker_only_attr:path {$($marker_only_blk:tt)+}
)*
$(
$attr:path {$($blk:tt)+}
)*
}
} => { ... };
{
@dbitem $table:path; $item:ty {
$($attr:path)*
}
} => { ... };
{
#[table = $table:path]
$item:ty {$($tt:tt)*}
} => { ... };
($($tt:tt)*) => { ... };
}Expand description
Wires a Rust struct to a DynamoDB table by implementing
DynamoDBItem and the attribute traits.
This is the primary macro for defining how a Rust type maps to a DynamoDB item. It generates:
DynamoDBItem<TD>— with the correctAdditionalAttributestype derived from the non-key, non-#[marker_only]attribute blocks.HasAttributeorHasConstAttributefor every attribute block, including the partition key and sort key.
§Syntax
dynamodb_item! {
#[table = TableType]
StructType {
#[partition_key]
PkAttr { ... }
#[sort_key] // optional
SkAttr { ... }
#[marker_only] // optional; implements HasAttribute but excluded from AdditionalAttributes
OtherAttr { ... }
AdditionalAttr { ... }
...
}
}Each attribute block uses the same syntax as has_attributes!:
either const VALUE: T = expr; for constant attributes, or
fn attribute_id(&self) -> T { ... } + fn attribute_value(id) -> T { ... }
for dynamic attributes. The return type of attribute_id is always the
input type of attribute_value — the two form a pipeline.
When attribute_id returns a reference (typically borrowing a String
field to avoid cloning), annotate it with the 'id lifetime:
&'id str. This lifetime is dictated by the
Id<'id> associated type.
See has_attributes! for a detailed explanation.
§Attribute modifiers
#[partition_key]— marks the partition key attribute. Required.#[sort_key]— marks the sort key attribute. Optional; omit for simple-key tables.#[marker_only]— implementsHasAttributefor the attribute (e.g. for GSI membership) but does not add it toAdditionalAttributes, because the attribute is already serialized as part of the struct’s serde representation.
§Examples
Singleton item (constant PK + SK):
use dynamodb_facade::dynamodb_item;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
struct AppConfig {
pub feature_flags: Vec<String>,
}
dynamodb_item! {
#[table = PlatformTable]
AppConfig {
#[partition_key]
PK { const VALUE: &'static str = "APP_CONFIG"; }
#[sort_key]
SK { const VALUE: &'static str = "APP_CONFIG"; }
ItemType { const VALUE: &'static str = "APP_CONFIG"; }
}
}Variable PK, constant SK:
use dynamodb_facade::dynamodb_item;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Course {
pub id: String,
pub title: String,
pub email: String,
}
dynamodb_item! {
#[table = PlatformTable]
Course {
#[partition_key]
PK {
// Borrows self.id as &'id str — no clone needed.
// attribute_value then receives that same &str.
fn attribute_id(&self) -> &'id str { &self.id }
fn attribute_value(id) -> String { format!("COURSE#{id}") }
}
#[sort_key]
SK { const VALUE: &'static str = "COURSE"; }
// email is already part of the struct and serialized by serde,
// so use #[marker_only] to exclude it from AdditionalAttributes
#[marker_only]
Email {
fn attribute_id(&self) -> &'id str { &self.email }
fn attribute_value(id) -> String { id.to_owned() }
}
// The constant ItemType attribute is part of AdditionalAttributes
// and will be added to each Course item
ItemType { const VALUE: &'static str = "COURSE"; }
}
}