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

dynamodel

该库提供了一种 derive 宏,用于在您的对象与 HashMap<String, AttributeValue> 之间实现转换

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 日

#416 in Rust 模式

MIT 许可证

30KB
309

dynamodel

Version License Test

该库提供了一种 derive 宏,用于在您的对象与 HashMap<String, AttributeValue> 之间实现转换。

Derive 宏 Dynamodel

Dynamodel derive 宏实现了这三个特质,以便更方便地使用 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 特质。

使用

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 包一样,您可以通过此类属性修改默认行为。

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(进入= "...")] 字段类型 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许可证发布。

依赖项

~17MB
~272K SLoC