#macro-derive #redb #model #generate-table #schema

redb_model

Redb model derive macro and DTO type conversion

15 releases (7 breaking)

0.9.1 Oct 1, 2024
0.8.0 Sep 2, 2024

#389 in Rust patterns

Download history 255/week @ 2024-08-05 427/week @ 2024-08-12 248/week @ 2024-08-26 253/week @ 2024-09-02 33/week @ 2024-09-16 2/week @ 2024-09-23 333/week @ 2024-09-30

375 downloads per month

MIT/Apache

17KB
85 lines

Redb Model

A derive macro for generating redb table definitions and (optionally) DTO object conversion methods/implementations.

Functionality

At a minimum, deriving Model on a named struct will implement the Model trait, declaring redb::TableDefinition as an associated constant.

#[derive(Model)]
struct User {
    #[entry(position(key))]
    id: u32,
    #[entry(position(value))]
    name: String,
    #[entry(position(value))]
    email: String,
}

assert_eq!(User::DEFINITION.name(), "User");

In the example below, we specify the table name as "outbound_edge", and generate an implementation of ModelExt, providing common DTO functionality.

#
#[derive(Model)]
#[model(name = "outbound_edge", impl_ext)]
struct Edge {
    #[entry(position(key))]
    source: u32,
    #[entry(position(key))]
    target: u32,
    #[entry(position(value))]
    label: String,
}

assert_eq!(Edge::DEFINITION.name(), "outbound_edge");
// ModelExt::from_values
let edge = Edge::from_values(((0, 1), "label".to_owned()));

let txn = db.begin_write().unwrap();
{
    let mut table = txn.open_table(Edge::DEFINITION).unwrap();
    // ModelExt::as_values
    let (k, v) = edge.as_values();
    table.insert(k, v).unwrap();
}
txn.commit().unwrap();

Struct Attributes

A model can be customized with the model attribute, providing any of the following arguments:

Argument Description Type Default
table_name The table name passed to the definition String <Struct name> (case-sensitive)
table_type Table type, either table or multimap_table String table
impl_ext Implement ModelExt for the type bool false
impl_from Implement From<T>, mapping T to the from_values(T) and from_guards(T) methods of ModelExt. Requires impl_ext. bool false

Field Attributes

Values can be customized with the entry attribute. Each field must be specified as either key or value with the position argument. Note that composite variables (multiple key or value fields) are combined as tuples in the order they are defined in the struct.

Argument Description Type Default
position The position of the field in an entry, either a key or a value. String N/A

Implementation details

General notes regarding trait and type generation.

Type Aliases

As of version 0.9.0, generic arguments have been removed from Model and ModelExt in exchange for type aliases. This decision was made to simplify trait definitions. Take for example the trait below, where we avoid specifying any concrete types;

#[derive(Model)]
#[model(impl_ext)]
struct User {
    #[entry(position(key))]
    user_id: [u8; 16],
}

#[derive(Model)]
#[model(impl_ext)]
struct Article {
    #[entry(position(key))]
    article_id: [u8; 16],
    #[entry(position(value))]
    author_id: [u8; 16],
}

/// Get the key of model `T` from the implementing model.
trait SharedKey<'a, T: ModelExt<'a>>: ModelExt<'a> {
    /// Consume the instance, returning the key of model `T`.
    fn into_shared_key(self) -> T::ModelKey;
    /// Get a reference to the key of model `T`.
    fn shared_key_ref(&'a self) -> <T::RedbKey as redb::Value>::SelfType<'a>;
}


impl<'a> SharedKey<'a, User> for Article {
    fn into_shared_key(self) -> <User as ModelExt<'a>>::ModelKey {
        self.author_id
    }

    fn shared_key_ref(&'a self) -> <<User as ModelExt>::RedbKey as redb::Value>::SelfType<'a> {
        &self.author_id
    }
}

let article = Article::from_values(([0u8; 16], [0u8; 16]));
// Get the user id from an article.
let user_id = article.shared_key_ref();
// or
let user_id = SharedKey::<User>::shared_key_ref(&article);

String Table Definitions

redb_model will replace String with &str for table definitions. This is so that composite (tuple) variables can be borrowed and passed to database handlers without destructuring the DTO.

Unit type values

The unit type () must be passed if no value is defined.

#[derive(Model)]
#[model(name = "outbound_edge", impl_ext)]
struct Edge {
    #[entry(position(key))]
    source: [u8; 16],
    #[entry(position(key))]
    target: [u8; 16],
}
let k = ([0; 16], [1; 16]);
let v = ();
let e = Edge::from_values((k, v));

License: MIT OR Apache-2.0

Dependencies

~1.3–1.8MB
~42K SLoC