Expand description
§dynamodel
This library provide a derive macro to implement conversion between your struct and HashMap<String, AttributeValue>.
§Usage
use dynamodel::Dynamodel;
// Using `Dynamodel` macro, you can implement both
// `From<your struct> for HashMap<String, AttributeValue>` and
// `TryFrom<HashMap<String, AttributeValue>> for your struct` traits.
#[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);§Implicit conversion
This macro converts some types implicitly so you don’t have to add any code. These types are followings.
| Type | AttributeValue variant | memo |
|---|---|---|
String | AttributeValue::S | |
u8, u16, u32, u64, u128, usizei8, i16, i32, i64, i128, isizef32, f64 | AttributeValue::N | |
bool | AttributeValue::Bool | |
Any struct that implements Dynamodel macro | AttributeValue::M | |
Vec<inner type> | AttributeValue::L | The inner type must be one of the implicit conversion types. |
Option<inner type> | Depends on the inner type | The inner type must be one of the implicit conversion types. |
§Explicit conversion
Using field attribute, you can implement original conversion methods for any types like this.
use dynamodel::{Dynamodel, ConvertError};
// 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 satisfy these conditions.
| Conversion | Argument | Return |
|---|---|---|
field type => AttributeValue | field type | AttributeValue |
AttributeValue => field type | &AttributeValue | Result<field type,ConvertError> |
§Rename HashMap key
Like serde crate, you can rename HashMap key from your struct field name.
§Container attribute rename_all
The allowed values for rename_all attribute are UPPERCASE, PascalCase, camelCase,
SCREAMING_SNAKE_CASE, kebab-case and SCREAMING-KEBAB-CASE.
use dynamodel::Dynamodel;
// Use `rename_all` as container attribute.
#[derive(Dynamodel, Debug, Clone, PartialEq)]
#[dynamodel(rename_all = "PascalCase")]
struct Person {
first_name: String,
last_name: String,
}
let person = Person {
first_name: "Kanji".into(),
last_name: "Tanaka".into(),
};
let item: HashMap<String, AttributeValue> = [
("FirstName".to_string(), AttributeValue::S("Kanji".into())),
("LastName".to_string(), AttributeValue::S("Tanaka".into())),
].into();
let converted: HashMap<String, AttributeValue> = person.clone().into();
assert_eq!(converted, item);
let converted: Person = item.try_into().unwrap();
assert_eq!(converted, person);§Field attribute rename
You can also rename key using field attribute rename.
use dynamodel::Dynamodel;
#[derive(Dynamodel, Debug, Clone, PartialEq)]
struct Person {
// Use `rename` as field attribute.
#[dynamodel(rename = "GivenName")]
first_name: String,
#[dynamodel(rename = "FamilyName")]
last_name: String,
}
let person = Person {
first_name: "Kanji".into(),
last_name: "Tanaka".into(),
};
let item: HashMap<String, AttributeValue> = [
("GivenName".to_string(), AttributeValue::S("Kanji".into())),
("FamilyName".to_string(), AttributeValue::S("Tanaka".into())),
].into();
let converted: HashMap<String, AttributeValue> = person.clone().into();
assert_eq!(converted, item);
let converted: Person = item.try_into().unwrap();
assert_eq!(converted, person);§Single-table design
When you design your struct according to the single-table design, you want separate data storing in the struct and its talbe key.
For example, the following diagram shows both Video and VideoStats are stored in the same
table.

Using container attribute table_key, you can implement this structure.
The table_key value must be a path for function whose argument is a reference of the struct’s
instance and its return type is HashMap<String,
AttributeValue>.
use dynamodel::Dynamodel;
#[derive(Dynamodel, Debug, Clone, PartialEq)]
#[dynamodel(table_key = "VideoStats::key", rename_all = "PascalCase")]
struct VideoStats {
id: String,
view_count: u64,
}
impl VideoStats {
fn key(&self) -> HashMap<String, AttributeValue> {
[
("PK".to_string(), AttributeValue::S(self.id.clone())),
("SK".to_string(), AttributeValue::S("VideoStats".into())),
].into()
}
}
let stats = VideoStats {
id: "7cf27a02".into(),
view_count: 147,
};
let item: HashMap<String, AttributeValue> = [
("PK".to_string(), AttributeValue::S("7cf27a02".into())),
("SK".to_string(), AttributeValue::S("VideoStats".into())),
("Id".to_string(), AttributeValue::S("7cf27a02".into())),
("ViewCount".to_string(), AttributeValue::N("147".into())),
].into();
let converted: HashMap<String, AttributeValue> = stats.into();
assert_eq!(converted, item);Enums§
- A conversion error from AttributeValue to your struct field.