8 releases (breaking)

0.7.0 Sep 2, 2024
0.6.0 Sep 2, 2024
0.5.0 Aug 26, 2024
0.4.0 Aug 13, 2024
0.1.0 Aug 7, 2024

#9 in #redb

Download history 269/week @ 2024-08-05 335/week @ 2024-08-12 156/week @ 2024-08-26 256/week @ 2024-09-02

837 downloads per month
Used in redb_model

MIT/Apache

26KB
566 lines

Redb Model

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

Functionality

This crate aims to unify database entry DTO definitions and redb::TableDefinition. Decorating a struct with #[derive(Model)] will implement Model for the type, define redb::TableDefinition as an associated constant, and generating helper methods for the type.

Example


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

let user = User {
    id: 0,
    name: "User".to_owned(),
    email: "user_email@email.com".to_owned(),
};

let txn = db.begin_write().unwrap();
{
    let mut table = txn.open_table(User::DEFINITION).unwrap();
    let (k, v) = user.as_values();
    table.insert(k, v).unwrap();
}
txn.commit().unwrap();

Specifying keys and values

Key(s) and value(s) are specified by decorating table fields with #[entry(position(...))], passing either key or value to the inner field. A composite key/value will be combined into a tuple.

#[derive(Model)]
struct User {
    #[entry(position(key))]
    uuid: [u8; 16],
    #[entry(position(value))]
    username: String,
    #[entry(position(value))]
    email: String,
}

let user_key = [0; 16];
let user_value = ("my_name".to_string(), "my_email@email.com".to_string());

let user = User::from_values((user_key, user_value));
let (k, v) = user.into_values();

// Only the value is a tuple for this model.
assert_eq!(k, [0; 16]);
assert_eq!(v, ("my_name".to_string(), "my_email@email.com".to_string()));

Specifying a table name

Table names default to the (case-sensitive) struct name. This can be overridden by decorating the struct with #[model(name = "...")] attribute.

#[derive(Model)]
#[model(name = "user_table")]
struct User {
    #[entry(position(key))]
    uuid: [u8; 16],
    #[entry(position(value))]
    username: String,
}

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

Type conversion

The generated implementation of the Model trait provides methods for instantiating, borrowing and taking the key/value pairs of the model DTO. Decorating the struct with #[model(impl_from)] will further implement From<T>, mapping T to the from_values(T) and from_guards(T) methods.

#[derive(Model)]
#[model(impl_from)]
struct User {
    #[entry(position(key))]
    uuid: [u8; 16],
    #[entry(position(value))]
    username: String,
    #[entry(position(value))]
    email: String,
}

let user_key = [0; 16];
let user_value = ("my_name".to_string(), "my_email@email.com".to_string());

let user: User = ((user_key, user_value)).into();

Implementation details

The following are general notes regarding implementation.

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. This is not currently possible with String.

const TABLE: TableDefinition<(String, String), ()> = TableDefinition::new("table");

let string_0 = "string_0".to_string();
let string_1 = "string_1".to_string();

let txn = db.begin_write().unwrap();
let mut table = txn.open_table(TABLE).unwrap();
// This doesn't work.
table.insert((&string_0, &string_1), ());
// Neither does this.
table.insert((string_0.as_str(), string_1.as_str()), ());
```rust

### Unit type values

The unit type `()` must be passed if no value is defined.
```rust
#[derive(Model)]
#[model(name = "outbound_edge")]
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));

Variable Ordering

All composite key/value variables are combined as a tuple in the order they are defined. This is intended but can be changed if there is any reason to do so.

The Model definition and redb::TableDefinition

The redb::TableDefinition uses 'static references of the types defined in the Model, with the exception of String which uses a 'static string slice. This is to ensure that calling as_values returns references suitable for database calls.

License: MIT OR Apache-2.0


lib.rs:

Derive macro for the redb_model crate.

Dependencies

~1.2–1.7MB
~40K SLoC