Skip to main content

has_attributes

Macro has_attributes 

Source
macro_rules! has_attributes {
    {
        $item:ty {
            $(
                $attr:path {$($blk:tt)+}
            )+
        }
    } => { ... };
    {
        @inner $attr:path ; $item:ty {
            fn attribute_value ($id:ident) -> $outty:ty $produce:block
            fn attribute_id ($(&)?$self:ident) -> $idty:ty $extract:block
        }
    } => { ... };
    {
        @inner $attr:path ; $item:ty {
            fn attribute_value ($id:ident) -> $outty:ty $produce:block
        }
    } => { ... };
    {
        @inner $attr:path ; $item:ty {
            fn $id_fct:ident ($(&)?$self:ident) -> $idty:ty $extract:block
            fn $value_fct:ident ($id:ident) -> $outty:ty $produce:block
        }
    } => { ... };
    {
        @inner $attr:path ; $item:ty {
            const VALUE: $t:ty = $v:expr;
        }
    } => { ... };
    ($($tt:tt)*) => { ... };
}
Expand description

Manually implements HasAttribute or HasConstAttribute for a type.

Use this macro when you need to implement the attribute traits without going through dynamodb_item! — for example, when writing a manual DynamoDBItem implementation or when adding attribute bindings to a type that is already wired to a table.

§Syntax

Each attribute block uses one of two forms:

Constant attribute — implements HasConstAttribute:

has_attributes! {
    MyType {
        MyAttr { const VALUE: AttrValueType = expr; }
    }
}

Dynamic attribute — implements HasAttribute:

has_attributes! {
    MyType {
        MyAttr {
            fn attribute_id(&self) -> IdType { ... }
            fn attribute_value(id) -> ValueType { ... }
        }
    }
}

The attribute_id and attribute_value functions may appear in either order. If attribute_id is omitted, it defaults to returning NoId.

Multiple attribute blocks can appear in a single invocation.

§The attribute_idattribute_value pipeline

For dynamic attributes, the return type of attribute_id(&self) is always the input type of attribute_value(id). The two functions form a pipeline: attribute_id extracts a lightweight identifier from &self, and attribute_value transforms it into the final DynamoDB value. This separation allows for independant usages of the methods, in particular it powers the “_by_id” variants of the get/update/delete operations.

§The 'id lifetime

When attribute_id returns a reference — typically &str — you must annotate it with the 'id lifetime: &'id str. This lifetime comes from the Id<'id> associated type on HasAttribute and must be used exactly as-is.

The typical use-case is when the identifier is a String field on the struct and the final attribute value is a formatted composition of that field (e.g. format!("USER#{id}")). Returning &'id str lets attribute_id borrow the field without cloning it, and attribute_value can then use the reference to produce an owned String.

If the attribute does not need data from &self (e.g. the value is always a constant), you can omit attribute_id entirely — the macro defaults it to returning NoId.

§Examples

use dynamodb_facade::has_attributes;

struct CourseStatus(String);

has_attributes! {
    CourseStatus {
        // Constant attribute: always the same value
        ItemType { const VALUE: &'static str = "COURSE_STATUS"; }

        // Dynamic attribute: attribute_id borrows &self.0 as &'id str,
        // then attribute_value receives that same &str to format the
        // DynamoDB value — no .clone() needed.
        SK {
            fn attribute_id(&self) -> &'id str { &self.0 }
            fn attribute_value(id) -> String { format!("STATUS#{id}") }
        }
    }
}

use dynamodb_facade::HasConstAttribute;
assert_eq!(<CourseStatus as HasConstAttribute<ItemType>>::VALUE, "COURSE_STATUS");

use dynamodb_facade::HasAttribute;
let status = CourseStatus("draft".to_owned());
assert_eq!(
    <CourseStatus as HasAttribute<SK>>::attribute(&status),
    "STATUS#draft".to_owned()
);