Crates.io | dynamodel_derive |
lib.rs | dynamodel_derive |
version | 0.5.1 |
source | src |
created_at | 2024-05-18 17:28:34.060239 |
updated_at | 2024-06-07 13:12:45.861528 |
description | Macros 1.1 implementation of #[derive(Dynamodel)] |
homepage | https://github.com/kaicoh/dynamodel |
repository | https://github.com/kaicoh/dynamodel |
max_upload_size | |
id | 1244408 |
size | 35,896 |
This library provides a derive macro to implement conversions between
your object and HashMap<String, AttributeValue>
.
Dynamodel
The 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>
Dynamodel
To 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, usize i8, i16, i32, i64, i128, isize f32, 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.