Crate dynomite[−][src]
Expand description
Dynomite is the set of high-level interfaces making interacting with AWS DynamoDB more productive.
💡To learn more about DynamoDB, see this helpful guide.
Data Modeling
Dynomite adapts Rust’s native types to DynamoDB’s core components to form a coherent interface.
The Attribute type
provides conversion interfaces to and from Rust’s native scalar types which represent
DynamoDB’s notion of “attributes”. The goal of this type is to make representing
AWS typed values feel more natural and ergonomic in Rust. Where a conversion is not available you can implement Attribute
for your own
types to leverage higher level functionality.
The Item trait provides conversion interfaces for complex types which represent DynamoDB’s notion of “items”.
💡 A cargo feature named "derive"
makes it easy to derive Item
instances for your custom types. This feature is enabled by default.
use dynomite::{Item, Attributes}; use uuid::Uuid; #[derive(Item)] struct Order { #[dynomite(partition_key)] user: Uuid, #[dynomite(sort_key)] order_id: Uuid, color: Option<String>, }
Attributes
#[derive(Item)]
Used to define a top-level DynamoDB item.
Generates a <ItemName>Key
struct with only partition_key/sort_key
fields to be used for type-safe primary key construction.
This automatically derives Attributes
too.
For the Order
struct from the example higher this will generate an OrderKey
struct like this:
#[derive(Attributes)] struct OrderKey { user: Uuid, order_id: Uuid, }
Use it to safely and conveniently construct the primary key:
use dynomite::{ dynamodb::{DynamoDb, GetItemInput}, Attributes, FromAttributes, }; use std::{convert::TryFrom, error::Error}; use uuid::Uuid; async fn get_order( client: impl DynamoDb, user: Uuid, order_id: Uuid, ) -> Result<Option<Order>, Box<dyn Error>> { // Use the generated `OrderKey` struct to create a primary key let key = OrderKey { user, order_id }; // Convert stronly-typed `OrderKey` to a map of `rusoto_dynamodb::AttributeValue` let key: Attributes = key.into(); let result = client .get_item(GetItemInput { table_name: "orders".into(), key, ..Default::default() }) .await?; Ok(result .item .map(|item| Order::try_from(item).expect("Invalid order, db corruption?"))) }
-
#[dynomite(partition_key)]
- required attribute, expected to be applied the target partition attribute field with a derivable DynamoDB attribute value of String, Number or Binary -
#[dynomite(sort_key)]
- optional attribute, may be applied to one target sort attribute field with an derivable DynamoDB attribute value of String, Number or Binary -
All other attributes are the same as for
#[derive(Attributes)]
#[derive(Attributes)]
Used to derive an implementation of From/IntoAttributes
trait to allow for
serializing/deserializing map-like types into AttributeValue
.
This also generates TryFrom<Attributes>
and Into<Attributes>
implementations.
-
#[dynomite(rename = "actualName")]
- optional attribute, may be applied to any item attribute field, useful when the DynamoDB table you’re interfacing with has attributes whose names don’t following Rust’s naming conventions -
#[dynomite(skip_serializing_if = "expr_that_returns_function")]
- place this on a field that should be skipped in the output map entirely if the given function returnstrue
. The value of this attribute must be a path to a function that satisfies the signatureFnOnce(&T) -> bool
, whereT
is the field type (possibly after some auto-deref coertions).This is is inspired by
#[serde(skip_serializing_if = "...")]
.This attribute may be used to skip serializing the empty set for example (which is not supported by current DynamoDB version, but it may be in future).
use dynomite::Attributes; use std::collections::HashSet; #[derive(Attributes)] struct UniqueStrings { #[dynomite(skip_serializing_if = "HashSet::is_empty")] strings: HashSet<String>, #[dynomite(skip_serializing_if = "is_99")] skip_if_99: u32, } fn is_99(&num: &u32) -> bool { num == 99 }
-
#[dynomite(default)]
- useDefault::default
implementation of the field type if the attribute is absent when deserializing fromAttributes
use dynomite::Attributes; #[derive(Attributes)] struct Todos { // use Default value of the field if it is absent in DynamoDb (empty vector) #[dynomite(default)] items: Vec<String>, list_name: String, }
-
#[dynomite(flatten)]
- flattens the fields of other struct that also derivesAttributes
into the current struct.💡 If this attribute is placed onto a field, no other
dynomite
attributes are alowed on this field (this restriction may be relaxed in future).This is reminiscent of
#[serde(flatten)]
. The order of declaration offlatten
ed fields matters, if the struct has to fields with#[dynomite(flatten)]
attribute the one that appears higher in code will be evaluated before the other one. This is crucial when you want to collect additional properties into a map:use dynomite::{Attributes, Item}; #[derive(Item)] struct ShoppingCart { #[dynomite(partition_key)] id: String, // A separate struct to store data without any id #[dynomite(flatten)] data: ShoppingCartData, // Collect all other additional attributes into a map // Beware that the order of declaration will affect the order of // evaluation, so this "wildcard" flatten clause should be the last member #[dynomite(flatten)] remaining_props: Attributes, } // `Attributes` doesn't require neither of #[dynomite(partition_key/sort_key)] #[derive(Attributes)] struct ShoppingCartData { name: String, total_price: u32, }
Fat enums
Fat enums are naturally supported by #[derive(Attribute)]
.
As for now, there is a limitation that the members of the enum must be
either unit or one-element tuple variants. This restriction will be relaxed
in future versions of dynomite
.
Deriving Attributes
on fat enums currently uses
internally tagged enum pattern (inspired by serde).
Thus, you have to explicitly specify the field name of enum tag
via the tag
attribute on an enum.
For example, the following definition:
use dynomite::Attributes; #[derive(Attributes)] // Name of the field where to store the discriminant in DynamoDB #[dynomite(tag = "kind")] enum Shape { Rectangle(Rectangle), // Use `rename` to change the **value** of the tag for a particular variant // by default the tag for a particular variant is the name of the variant verbatim #[dynomite(rename = "my_circle")] Circle(Circle), Unknown, } #[derive(Attributes)] struct Circle { radius: u32, } #[derive(Attributes)] struct Rectangle { width: u32, height: u32, }
corresponds to the following representation in DynamoDB for each enum variant:
Rectangle
:{ "kind": "Rectangle", "width": 42, "height": 64 }
Circle
:{ "kind": "my_circle", "radius": 54 }
Unknown
:{ "kind": "Unknown" }
If you have a plain old enum (without any data fields), you should use
#[derive(Attribute)]
instead.
#[derive(Attribute)]
Derives an implementation of Attribute
for the plain enum.
If you want to use a fat enum see this paragraph instead.
The enum istelf will be represented as a string with the name of the variant
it represents.
In contrast, having #[derive(Attributes)]
on an enum
makes it to be represented as an object with a tag field,
which implies an additional layer of indirection.
use dynomite::{Attribute, Item}; #[derive(Attribute)] enum UserRole { Admin, Moderator, Regular, } #[derive(Item)] struct User { #[dynomite(partition_key)] id: String, role: UserRole, }
This data model will have the following representation in DynamoDB:
{
"id": "d97de525-c81d-46d4-b945-d01b3a0f9165",
"role": "Admin"
}
role
field here may be any of Admin
, Moderator
, or Regular
strings.
Rusoto extensions
By importing the dynomite::DynamoDbExt trait, dynomite adds client interfaces for creating async Stream-based auto pagination interfaces.
Robust retries
By importing the dynomite::Retries trait, dynomite provides an interface for adding configuration retry policies so your rusoto DynamoDb clients.
Errors
Some operations which require coercion from AWS to Rust types may fail which results in an AttributeError.
Cargo Features
This crate has a few cargo features of note.
uuid
Enabled by default, the uuid
feature adds support for implementing Attribute
for
the uuid crate’s type Uuid
, a useful
type for producing and representing
unique identifiers for items that satisfy effective characteristics for partition keys
chrono
Enabled by default, the chrono
feature adds an implementation of Attribute
for
the std’s SystemTime and chrono DateTime
types which
internally use rfc3339 timestamps.
derive
Enabled by default, the derive
feature enables the use of the dynomite derive feature which
allows you to simply add #[derive(Item)]
to your structs.
rustls
Disabled by default, the rustls
feature overrides Rusoto’s default tls
dependency on OpenSSL, replacing it with a rustls
based tls implementation. When you
enable this feature. It will also enable uuid
and derive
by default.
To disable any of these features
[dependencies.dynomite]
version = "xxx"
default-features = false
features = ["feature-you-want"]
Re-exports
pub use rusoto_dynamodb as dynamodb; | |
pub use crate::retry::Retries; | |
pub use crate::error::AttributeError; |
Modules
error | Dynomite error types |
retry | Retry functionality |
Macros
attr_map | Creates a |
Traits
Attribute | A type capable of being converted into an or from and AWS |
DynamoDbExt | Extension methods for DynamoDb client types |
FromAttributes | A type capable of being produced from a set of string keys and |
IntoAttributes | A type capable of being serialized into a set of string keys and |
Item | A type which can be converted to and from a set of String keys and
|
Type Definitions
Attributes | Type alias for map of named attribute values |