| Crates.io | dynamodel |
| lib.rs | dynamodel |
| version | 0.6.0 |
| created_at | 2024-05-18 17:31:07.083023+00 |
| updated_at | 2025-04-06 02:43:35.242195+00 |
| description | This library provides a derive macro to implement conversions between your object and `HashMap |
| homepage | https://github.com/kaicoh/dynamodel |
| repository | https://github.com/kaicoh/dynamodel |
| max_upload_size | |
| id | 1244411 |
| size | 72,743 |
This library provides a derive macro to implement conversions between
your object and HashMap<String, AttributeValue>.
DynamodelThe Dynamodel derive macro implements these three traits to use
aws-sdk-dynamodb more comfortably.
Into<HashMap<String, AttributeValue>>TryFrom<HashMap<String, AttributeValue>>AttributeValueConvertible trait enables the types that implement it to be converted from and to AttributeValue.#[derive(Dynamodel)] Convertible
struct YourStruct { ... } <===========> HashMap<String, AttributeValue>
#[derive(Dynamodel)] Convertible
enum YourEnum { ... } <===========> HashMap<String, AttributeValue>
DynamodelTo use the Dynamodel macro, all types of your object's fields must implement
the AttributeValueConvertible trait.
By default, these types automatically implement the AttributeValueConvertible
trait, so no additional code is required when using these types.
| Type | AttributeValue variant |
|---|---|
String |
AttributeValue::S("...") |
u8, u16, u32, u64, u128, usizei8, i16, i32, i64, i128, isizef32, f64 |
AttributeValue::N("...") |
bool |
AttributeValue::Bool(...) |
Vec of any types that implement AttributeValueConvertible |
AttributeValue::L([...]) |
Any types that implement Dynamodel macro |
AttributeValue::M({ ... }) |
The last row of the above table shows that once you apply the Dynamodel macro to your object,
it also implements the AttributeValueConvertible trait for your object.
So, you can create nested structures of objects that apply the Dynamodel macro.
If you want to use additional types, you need to implement the AttributeValueConvertible
trait for your type.
use dynamodel::Dynamodel;
use std::collections::HashMap;
use aws_sdk_dynamodb::types::AttributeValue;
#[derive(Dynamodel, Debug, Clone, PartialEq)]
struct Person {
first_name: String,
last_name: String,
age: u8,
}
let person = Person {
first_name: "Kanji".into(),
last_name: "Tanaka".into(),
age: 23,
};
let item: HashMap<String, AttributeValue> = [
("first_name".to_string(), AttributeValue::S("Kanji".into())),
("last_name".to_string(), AttributeValue::S("Tanaka".into())),
("age".to_string(), AttributeValue::N("23".into()))
].into();
// Convert from Person into HashMap<String, AttributeValue>.
let converted: HashMap<String, AttributeValue> = person.clone().into();
assert_eq!(converted, item);
// Convert from HashMap<String, AttributeValue> into Person.
// This conversion uses std::convert::TryFrom trait, so this returns a Result.
let converted: Person = item.try_into().unwrap();
assert_eq!(converted, person);
Like the Serde crate, you can modify the default behavior
through attributes like this.
use dynamodel::{Dynamodel, ConvertError};
use std::collections::HashMap;
use aws_sdk_dynamodb::{types::AttributeValue, primitives::Blob};
// Vec<u8> is converted to AttributeValue::L by default,
// but this case, the `data` field is converted to AttributeValue::B.
#[derive(Dynamodel)]
struct BinaryData {
#[dynamodel(into = "to_blob", try_from = "from_blob")]
data: Vec<u8>
}
fn to_blob(value: Vec<u8>) -> AttributeValue {
AttributeValue::B(Blob::new(value))
}
fn from_blob(value: &AttributeValue) -> Result<Vec<u8>, ConvertError> {
value.as_b()
.map(|b| b.clone().into_inner())
.map_err(|err| ConvertError::AttributeValueUnmatched("B".to_string(), err.clone()))
}
The function definition must meet these conditions.
| Field attribute | Argument | Return |
|---|---|---|
#[dynamodel(into = "...")] |
field type |
AttributeValue |
#[dynamodel(try_from = "...")] |
&AttributeValue |
Result<field type, ConvertError> |
The following diagram shows that both Video and VideoStats are stored in the same table.

#[derive(Dynamodel)]
#[dynamodel(extra = "VideoStats::sort_key", rename_all = "PascalCase")]
struct VideoStats {
#[dynamodel(rename = "PK")]
id: String,
view_count: u64,
}
impl VideoStats {
fn sort_key(&self) -> HashMap<String, AttributeValue> {
[
("SK".to_string(), AttributeValue::S("VideoStats".into())),
].into()
}
}
And suppose you want to add a VideoComment object that is sortable by timestamp, like this.

#[derive(Dynamodel)]
#[dynamodel(rename_all = "PascalCase")]
struct VideoComment {
#[dynamodel(rename = "PK")]
id: String,
#[dynamodel(rename = "SK", into = "sort_key", try_from = "get_timestamp")]
timestamp: String,
content: String,
}
fn sort_key(timestamp: String) -> AttributeValue {
AttributeValue::S(format!("VideoComment#{timestamp}"))
}
fn get_timestamp(value: &AttributeValue) -> Result<String, ConvertError> {
value.as_s()
.map(|v| v.split('#').last().unwrap().to_string())
.map_err(|e| ConvertError::AttributeValueUnmatched("S".into(), e.clone()))
}
For more features, refer to this wiki.
This software is released under the MIT License.