9 releases (breaking)

0.8.0 Oct 18, 2024
0.7.0 Oct 1, 2024
0.6.0 Sep 2, 2024
0.5.0 Sep 2, 2024
0.1.0 Aug 7, 2024

#1426 in Rust patterns

Download history 305/week @ 2024-08-06 173/week @ 2024-08-13 106/week @ 2024-08-20 229/week @ 2024-08-27 95/week @ 2024-09-03 18/week @ 2024-09-10 11/week @ 2024-09-17 10/week @ 2024-09-24 197/week @ 2024-10-01 16/week @ 2024-10-08 158/week @ 2024-10-15 15/week @ 2024-10-22 1/week @ 2024-10-29 1/week @ 2024-11-05

178 downloads per month
Used in 2 crates

MIT/Apache

4KB

Redb Model

A derive macro for generating redb table definitions and 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,
}

// redb::TableDefinition::<u32, (String, String)>
assert_eq!(User::DEFINITION.name(), "User");

In the example below, the table name is specified as "outbound_edge", and the label field is declared as &str, rather than String in the table. The impl_ext argument on the struct generates an implementation of ModelExt, giving access to methods for type conversion between the DTO types and those of the redb key/value. In this case, calling any of the as_ methods will Copy the u32 fields, and borrow the String field as a &str.

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

// redb::TableDefinition::<(u32, u32), &str>
assert_eq!(Edge::DEFINITION.name(), "outbound_edge");

let edge0 = Edge {
    source: 0,
    target: 1,
    label: String::from("label"),
};

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

// ModelExt::as_key
let k = edge0.as_key();
let txn = db.begin_read().unwrap();
// Model::DEFINITION
let table = txn.open_table(Edge::DEFINITION).unwrap();
let edge1 = table
    .get(k)
    .unwrap()
    // ModelExt::from_key_and_guard
    .map(|guard| Edge::from_key_and_guard((k, &guard)))
    .unwrap();

assert_eq!(edge0, edge1);

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 Literal <struct name> (case-sensitive)
table_type Table type, either table or multimap Literal table
impl_ext Implement ModelExt for the type bool false
impl_from Implement From<T>, mapping T to ModelExt::from_values(T) and ModelExt::from_guards(T). bool false

Note that impl_from uses methods of impl_ext and therefore requires both arguments to be specified.

Field Attributes

Values can be customized with the entry attribute. Each field must specify position as key or value, and (optionally) provide an alternate redb_type. When generating a ModelExt definition (providing impl_ext as a struct argument), from and into operations may need to be explicit. Note that composite variables (multiple key or value fields) are combined as tuples in the order they are defined.

Argument Description Type Default
position The position of the field in an entry, either a key or a value. enum (key or value) None
redb_type The type defined in the redb::TableDefinition or redb::MultimapTableDefinition. Type Field Type
from The operation to convert from the redb_type. Expression See below.
into The operation to convert into the redb_type. Expression See below.

Conversion from a redb value has the following default behavior (impl_ext only):

  • If no redb_type is specified, the value is assumed to implement Copy and passed directly to the DTO.
  • If the specified redb_type is a reference (is prefixed by &), to_owned is called on the value.
  • If the specified redb_type is not a reference, into is called on the value.

Conversion into a redb value takes a type reference, and has the following default behavior (impl_ext only):

  • If no redb_type is specified, the value is assumed to implement Copy and dereferenced.
  • If the specified redb_type is a reference (is prefixed by &), the value is passed as a reference.
  • If the specified redb_type is not a reference, into is called on the value.

For user defined types, typically implementing From<RedbType> for FieldType and Into<RedbType> for &FieldType will satisfy type conversion.

#[derive(Copy, Clone)]
struct Wrapper(u32);

// Default `from` operation for `Wrapper`.
impl From<u32> for Wrapper {
    fn from(value: u32) -> Self {
        Wrapper(value)
    }
}

// Default `into` operation for `&Wrapper`.
impl Into<u32> for &Wrapper {
    fn into(self) -> u32 {
        self.0
    }
}

#[derive(Model)]
#[model(impl_ext)]
struct MyModel {
    #[entry(position = "key", redb_type = "u32")]
    key3: Wrapper
}

For external types where a newtype wrapper is not desired, or where the type does not implement Copy, the from and into operations must be explicit. Both the from and into argument accept a variable named after the field. For from expressions, this argument is the redb_type, while for into expressions, this is a reference of the field value. Note that while the operations are named from and into, there is no constraint on what operations can be used, as is demonstrated below.

use secrecy::{ExposeSecret, SecretString};
use uuid::Uuid;

#[derive(Model)]
#[model(impl_ext)]
struct SecretModel {
    #[entry(
        position = "key",
        redb_type = "[u8; 16]",
        from = "Uuid::from_bytes(id)", // `id` is the `redb_type` (`[u8; 16]`).
        into = "id.into_bytes()" // `id` is `&Uuid`.
    )]
    id: Uuid,
    #[entry(
        position = "value",
        redb_type = "&str",
        from = "SecretString::from(secret)", // `secret` is the `redb_type` (`&str`).
        into = "secret.expose_secret()" // `secret` is `&SecretString`.
    )]
    secret: SecretString,
}

Type Aliases

Generated definitions of the ModelExt traits defines type aliases for the common key and value tuples used extensively in generated code. These may be useful for extending the functionality of models without explicitly stating key/value types.

Alias Description
ModelExt::RedbKey The K argument for the given redb definition.
ModelExt::RedbValue The V argument for the given redb definition.
ModelExt::ModelKey A tuple of the owned key type(s) defined in the model.
ModelExt::ModelValue A tuple of the owned value type(s) defined in the model.

License: MIT OR Apache-2.0


lib.rs:

Traits for the redb_model crate.

Dependencies

~680KB
~17K SLoC