#dynamo-db #derive #macro-derive #aws

dynamodel_derive

#[derive(Dynamodel)]宏的1.1实现

7个版本 (4个破坏性更新)

0.5.1 2024年6月7日
0.5.0 2024年6月6日
0.4.0 2024年6月3日
0.3.1 2024年5月26日
0.1.0 2024年5月18日

#9 in #dynamodb

Download history 301/week @ 2024-05-13 203/week @ 2024-05-20 73/week @ 2024-05-27 416/week @ 2024-06-03 24/week @ 2024-06-10

每月445次下载
用于 dynamodel

MIT许可证

36KB
777

dynamodel

Version License Test

此库提供了一个推导宏,用于在您的对象与HashMap之间实现转换。

推导宏 Dynamodel

Dynamodel推导宏实现了这三个特性,以便更方便地使用aws-sdk-dynamodb

  • Into<HashMap<String, AttributeValue>>
  • TryFrom<HashMap<String, AttributeValue>>
  • AttributeValueConvertible特质允许实现了它的类型在AttributeValue之间进行转换。
#[derive(Dynamodel)]        Convertible
struct YourStruct { ... }  <===========>  HashMap<String, AttributeValue>

#[derive(Dynamodel)]    Convertible
enum YourEnum { ... }  <===========>  HashMap<String, AttributeValue>

使用Dynamodel的要求

要使用Dynamodel宏,您的对象的所有字段类型都必须实现AttributeValueConvertible特质。

默认情况下,这些类型自动实现AttributeValueConvertible特质,因此在使用这些类型时不需要额外的代码。

类型 AttributeValue变体
String AttributeValue::S("...")
u8, u16, u32, u64, u128, usize
i8, i16, i32, i64, i128, isize
f32, f64
AttributeValue::N("...")
bool AttributeValue::Bool(...)
Vec 任何实现了AttributeValueConvertible的类型 AttributeValue::L([...])
任何实现了Dynamodel宏的类型 AttributeValue::M({ ... })

上表最后一行显示,一旦您将Dynamodel宏应用于您的对象,它也为您对象实现了AttributeValueConvertible特质。

因此,您可以创建应用了Dynamodel宏的对象的嵌套结构。

如果您想使用额外的类型,您需要为您自己的类型实现 AttributeValueConvertible trait。

用法

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);

修改默认行为

类似于 Serde crate,您可以通过以下这样的属性来修改默认行为。

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()))
}

函数定义必须满足以下条件。

字段属性 参数 返回值
#[dynamodel(into= "...")] 字段类型 AttributeValue
#[dynamodel(try_from= "...")] &AttributeValue 结果<字段类型,转换错误>

示例

单表设计

以下图表显示 VideoVideoStats 都存储在同一个表中。

videos 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()
    }
}

假设您想添加一个可按时间戳排序的 VideoComment 对象,如下所示。

video comments object

#[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()))
}

更多功能

有关更多功能,请参阅 此维基页面

许可

本软件根据 MIT 许可证 发布。

依赖

~0.7–1.2MB
~26K SLoC