Struct Models

Source
pub struct Models { /* private fields */ }
Expand description

A collection of Model used by the Models to define models.

This collection allows you to manage multiple models efficiently, facilitating the process of defining and manipulating them within your application.

§Note

Usually, there is little point in creating models at runtime. In some cases, it is necessary to define them with a 'static lifetime, for example, to address compatibility issues with certain asynchronous libraries such as Axum. There are multiple ways to achieve this, including the once_cell::sync::Lazy crate, or the LazyLock from the standard library, which is available when the relevant Rust feature is enabled.

§Example using once_cell::sync::Lazy

use native_db::*;
use once_cell::sync::Lazy;

// The lifetime of the models needs to be longer or equal to the lifetime of the database.
// In many cases, it is simpler to use a static variable but it is not mandatory.
static MODELS: Lazy<Models> = Lazy::new(|| {
    let mut models = Models::new();
    // It's a good practice to define the models by specifying the version
    models.define::<data::v1::Person>().unwrap();
    models
});

fn main() -> Result<(), db_type::Error> {
    // Initialize the database with the models
    let db = Builder::new().create_in_memory(&MODELS)?;
    Ok(())
}

Implementations§

Source§

impl Models

Source

pub fn new() -> Self

Create a new collection of Models.

Source

pub fn define<T: ToInput>(&mut self) -> Result<()>

Defines a table using the given model.

Native DB depends on native_model to define the model. By default, native_model uses serde to serialize and deserialize the data, but you can use any other serialization library. See the documentation of native_model for more information. In the examples below, we import serde and use the Serialize and Deserialize traits.

§Primary Key

The primary key is mandatory, and you must:

  • Define it.
  • Define only one.

If the primary key is not defined, the compiler will return an error: Primary key is not set.

There are two ways to define a primary key:

  1. On a Field:

    • Use the #[primary_key] attribute on the field that will serve as the primary key.
    • The type of the field will be used as the primary key type.
  2. With a Custom Method:

    • Use the #[native_db(primary_key(<method_name> -> <return_type>))] attribute on the type (enum, struct, tuple struct, or unit struct).
    • Implement a method with the given <method_name> that returns the primary key of type <return_type>.
    • Important: You must specify both the method name and the return type using the syntax primary_key(<method_name> -> <return_type>). The type must be specified because it is used at runtime to check the query types.

The primary key is unique, so you can’t have two instances of the model with the same primary key saved in the database.

§Defining a Simple Model with a Primary Key on a Field
use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[native_model(id=1, version=1)]
#[native_db]
struct Data {
    #[primary_key]
    id: u64,
}

fn main() -> Result<(), db_type::Error> {
    let mut models = Models::new();
    models.define::<Data>()
}

In this example, we have:

  • One primary key named id of type u64, defined directly on the field using the #[primary_key] attribute.
§Defining a Model with a Method as Primary Key
use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[native_model(id=1, version=1)]
#[native_db(
    primary_key(custom_id -> u32)
)]
struct Data(u64);

impl Data {
    fn custom_id(&self) -> u32 {
        (self.0 + 1) as u32
    }
}

In this example, we have:

  • One primary key named custom_id of type u32, defined using a custom method. The method custom_id computes and returns the primary key value.
§Secondary Key

The secondary key is flexible, and you can:

  • Define it or not.
  • Define one or more.

There are two ways to define a secondary key:

  1. On a Field:

    • Use the #[secondary_key] attribute on the field that will serve as a secondary key.
    • The type of the field will be used as the secondary key type.
  2. With a Custom Method:

    • Use the #[native_db(secondary_key(<method_name> -> <return_type>, <options>))] attribute on the type.
    • Implement a method with the given <method_name> that returns the secondary key value of type <return_type>.
    • Important: You must specify both the method name and the return type using the syntax secondary_key(<method_name> -> <return_type>, <options>). The type must be specified because it is used at runtime to check the query types.

The secondary key can have two options:

§Defining a Model with a Secondary Key on a Field
use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[native_model(id=1, version=1)]
#[native_db]
struct Data {
    #[primary_key]
    id: u64,
    #[secondary_key]
    name: String,
}

In the above example, we have:

  • One primary key named id of type u64, defined on the field.
  • One secondary key named name of type String, defined on the field using the #[secondary_key] attribute.
§Defining a Model with an Optional and Unique Secondary Key
use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[native_model(id=1, version=1)]
#[native_db]
struct Data {
    #[primary_key]
    id: u64,
    #[secondary_key(unique, optional)]
    name: Option<String>,
}

In the above example, we have:

  • One primary key named id of type u64, defined on the field.

  • One secondary key named name of type Option<String>, defined on the field with options unique and optional.

  • Note: The secondary key can be unique, optional, or both.

§Unique

This means that each instance of the model must have a unique value for the secondary key. If the value is not unique, the insert method will return an error.

§Optional

This means that an instance of the model can have a value for the secondary key or not. When optional is set, the value must be an Option. If the value is not an Option, the compiler will return an error: error[E0282]: type annotations needed: cannot infer type.

Under the hood, the secondary key is stored in a separate redb table. So if the secondary key is optional, the value will be stored in the table only if the value is not None.

§Defining a Model with a Custom Optional Secondary Key
use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[native_model(id=1, version=1)]
#[native_db(
    secondary_key(custom_name -> Option<String>, optional)
)]
struct Data {
    #[primary_key]
    id: u64,
    #[secondary_key]
    name: String,
    flag: bool,
}

impl Data {
    fn custom_name(&self) -> Option<String> {
        if self.flag {
            Some(self.name.clone().to_uppercase())
        } else {
            None
        }
    }
}

In the above example, we have:

  • One primary key named id of type u64, defined on the field.
  • One secondary key named name of type String, defined on the field.
  • One custom secondary key named custom_name of type Option<String>, defined using a custom method with the option optional.

The method custom_name returns an Option<String> based on some logic involving the flag field.

§Defining Multiple Models

To define multiple models, you must use different id values for each model. If you use the same id for two models, the program will panic with the message: The table <table_name> has the same native model version as the table <table_name> and it's not allowed.

Example:

use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[native_model(id=1, version=1)]
#[native_db]
struct Animal {
    #[primary_key]
    name: String,
}

#[derive(Serialize, Deserialize)]
#[native_model(id=2, version=1)]
#[native_db]
struct Vegetable {
    #[primary_key]
    name: String,
}

fn main() -> Result<(), db_type::Error> {
    let mut models = Models::new();
    models.define::<Animal>()?;
    models.define::<Vegetable>()
}

In the above example, we have:

  • We have two models, Animal and Vegetable.
  • Both have:
    • One primary key named name of type String, defined on the field.
  • Each model has a unique id (id=1 for Animal, id=2 for Vegetable), which is necessary to avoid conflicts.

Trait Implementations§

Source§

impl Debug for Models

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Models

Source§

fn default() -> Models

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Models

§

impl RefUnwindSafe for Models

§

impl Send for Models

§

impl Sync for Models

§

impl Unpin for Models

§

impl UnwindSafe for Models

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.