native_db/
models.rs

1use std::collections::HashMap;
2
3use crate::{db_type::Result, table_definition::NativeModelOptions, ModelBuilder, ToInput};
4
5/// A collection of [`Model`](crate::Model) used by the [`Models`](crate::Models) to
6/// [define](Self::define) models.
7///
8/// This collection allows you to manage multiple models efficiently, facilitating the process
9/// of defining and manipulating them within your application.
10///
11/// # Note
12/// 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](https://github.com/tokio-rs/axum).
13/// There are multiple ways to achieve this, including the [`once_cell::sync::Lazy`](https://docs.rs/once_cell/1.19.0/once_cell/sync/struct.Lazy.html) crate,
14/// or the [`LazyLock`](https://doc.rust-lang.org/std/sync/struct.LazyLock.html) from the standard library, which is available when the relevant Rust feature is enabled.
15///
16/// ## Example using `once_cell::sync::Lazy`
17///
18/// ```rust
19/// # pub mod data {
20/// #     use native_db::{native_db, ToKey};
21/// #     use native_model::{native_model, Model};
22/// #     use serde::{Deserialize, Serialize};
23/// #
24/// #     pub type Person = v1::Person;
25/// #
26/// #     pub mod v1 {
27/// #         use super::*;
28/// #         
29/// #         #[derive(Serialize, Deserialize, Debug)]
30/// #         #[native_model(id = 1, version = 1)]
31/// #         #[native_db]
32/// #         pub struct Person {
33/// #            #[primary_key]
34/// #            pub name: String,
35/// #         }
36/// #     }
37/// # }
38/// use native_db::*;
39/// use once_cell::sync::Lazy;
40///
41/// // The lifetime of the models needs to be longer or equal to the lifetime of the database.
42/// // In many cases, it is simpler to use a static variable but it is not mandatory.
43/// static MODELS: Lazy<Models> = Lazy::new(|| {
44///     let mut models = Models::new();
45///     // It's a good practice to define the models by specifying the version
46///     models.define::<data::v1::Person>().unwrap();
47///     models
48/// });
49///
50/// fn main() -> Result<(), db_type::Error> {
51///     // Initialize the database with the models
52///     let db = Builder::new().create_in_memory(&MODELS)?;
53///     Ok(())
54/// }
55/// ```
56#[derive(Debug, Default)]
57pub struct Models {
58    pub(crate) models_builder: HashMap<String, ModelBuilder>,
59}
60
61impl Models {
62    /// Create a new collection of Models.
63    pub fn new() -> Self {
64        Self {
65            models_builder: HashMap::new(),
66        }
67    }
68
69    /// Defines a table using the given model.
70    ///
71    /// Native DB depends on `native_model` to define the model.
72    /// By default, `native_model` uses [`serde`](https://serde.rs/) to serialize and deserialize the data, but
73    /// you can use any other serialization library. See the documentation of [`native_model`](https://github.com/vincent-herlemont/native_model) for more information.
74    /// In the examples below, we import `serde` and use the `Serialize` and `Deserialize` traits.
75    ///
76    /// # Primary Key
77    ///
78    /// The primary key is **mandatory**, and you **must**:
79    /// - Define it.
80    /// - Define only one.
81    ///
82    /// If the primary key is not defined, the compiler will return an error: `Primary key is not set`.
83    ///
84    /// There are two ways to define a primary key:
85    ///
86    /// 1. **On a Field**:
87    ///    - Use the `#[primary_key]` attribute on the field that will serve as the primary key.
88    ///    - The type of the field will be used as the primary key type.
89    ///
90    /// 2. **With a Custom Method**:
91    ///    - Use the `#[native_db(primary_key(<method_name> -> <return_type>))]` attribute on the type (`enum`, `struct`, `tuple struct`, or `unit struct`).
92    ///    - Implement a method with the given `<method_name>` that returns the primary key of type `<return_type>`.
93    ///    - **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.
94    ///
95    /// The primary key is **unique**, so you can't have two instances of the model with the same primary key saved in the database.
96    ///
97    /// ## Defining a Simple Model with a Primary Key on a Field
98    ///
99    /// ```rust
100    /// use native_db::*;
101    /// use native_model::{native_model, Model};
102    /// use serde::{Deserialize, Serialize};
103    ///
104    /// #[derive(Serialize, Deserialize)]
105    /// #[native_model(id=1, version=1)]
106    /// #[native_db]
107    /// struct Data {
108    ///     #[primary_key]
109    ///     id: u64,
110    /// }
111    ///
112    /// fn main() -> Result<(), db_type::Error> {
113    ///     let mut models = Models::new();
114    ///     models.define::<Data>()
115    /// }
116    /// ```
117    ///
118    /// In this example, we have:
119    /// - **One primary key** named `id` of type `u64`, defined directly on the field using the `#[primary_key]` attribute.
120    ///
121    /// ## Defining a Model with a Method as Primary Key
122    ///
123    /// ```rust
124    /// use native_db::*;
125    /// use native_model::{native_model, Model};
126    /// use serde::{Deserialize, Serialize};
127    ///
128    /// #[derive(Serialize, Deserialize)]
129    /// #[native_model(id=1, version=1)]
130    /// #[native_db(
131    ///     primary_key(custom_id -> u32)
132    /// )]
133    /// struct Data(u64);
134    ///
135    /// impl Data {
136    ///     fn custom_id(&self) -> u32 {
137    ///         (self.0 + 1) as u32
138    ///     }
139    /// }
140    /// ```
141    ///
142    /// In this example, we have:
143    /// - **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.
144    ///
145    /// # Secondary Key
146    ///
147    /// The secondary key is *flexible*, and you can:
148    /// - Define it or not.
149    /// - Define one or more.
150    ///
151    /// There are two ways to define a secondary key:
152    ///
153    /// 1. **On a Field**:
154    ///    - Use the `#[secondary_key]` attribute on the field that will serve as a secondary key.
155    ///    - The type of the field will be used as the secondary key type.
156    ///
157    /// 2. **With a Custom Method**:
158    ///    - Use the `#[native_db(secondary_key(<method_name> -> <return_type>, <options>))]` attribute on the type.
159    ///    - Implement a method with the given `<method_name>` that returns the secondary key value of type `<return_type>`.
160    ///    - **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.
161    ///
162    /// The secondary key can have two options:
163    /// - [`unique`](#unique) (default: false)
164    /// - [`optional`](#optional) (default: false)
165    ///
166    /// ## Defining a Model with a Secondary Key on a Field
167    ///
168    /// ```rust
169    /// use native_db::*;
170    /// use native_model::{native_model, Model};
171    /// use serde::{Deserialize, Serialize};
172    ///
173    /// #[derive(Serialize, Deserialize)]
174    /// #[native_model(id=1, version=1)]
175    /// #[native_db]
176    /// struct Data {
177    ///     #[primary_key]
178    ///     id: u64,
179    ///     #[secondary_key]
180    ///     name: String,
181    /// }
182    /// ```
183    ///
184    /// In the above example, we have:
185    /// - **One primary key** named `id` of type `u64`, defined on the field.
186    /// - **One secondary key** named `name` of type `String`, defined on the field using the `#[secondary_key]` attribute.
187    ///
188    /// ## Defining a Model with an Optional and Unique Secondary Key
189    ///
190    /// ```rust
191    /// use native_db::*;
192    /// use native_model::{native_model, Model};
193    /// use serde::{Deserialize, Serialize};
194    ///
195    /// #[derive(Serialize, Deserialize)]
196    /// #[native_model(id=1, version=1)]
197    /// #[native_db]
198    /// struct Data {
199    ///     #[primary_key]
200    ///     id: u64,
201    ///     #[secondary_key(unique, optional)]
202    ///     name: Option<String>,
203    /// }
204    /// ```
205    ///
206    /// In the above example, we have:
207    /// - **One primary key** named `id` of type `u64`, defined on the field.
208    /// - **One secondary key** named `name` of type `Option<String>`, defined on the field with options `unique` and `optional`.
209    ///
210    /// - **Note:** The secondary key can be `unique`, `optional`, or both.
211    ///
212    /// ## Unique
213    ///
214    /// This means that each instance of the model must have a unique value for the secondary key.
215    /// If the value is not unique, the [`insert`](crate::transaction::RwTransaction::insert) method will return an error.
216    ///
217    /// ## Optional
218    ///
219    /// This means that an instance of the model can have a value for the secondary key or not.
220    /// When `optional` is set, the value **must** be an [`Option`](https://doc.rust-lang.org/std/option/enum.Option.html).
221    /// If the value is not an [`Option`](https://doc.rust-lang.org/std/option/enum.Option.html), the compiler will return
222    /// an error: `error[E0282]: type annotations needed: cannot infer type`.
223    ///
224    /// Under the hood, the secondary key is stored in a separate `redb` table. So if the secondary key is optional,
225    /// the value will be stored in the table only if the value is not `None`.
226    ///
227    /// ## Defining a Model with a Custom Optional Secondary Key
228    ///
229    /// ```rust
230    /// use native_db::*;
231    /// use native_model::{native_model, Model};
232    /// use serde::{Deserialize, Serialize};
233    ///
234    /// #[derive(Serialize, Deserialize)]
235    /// #[native_model(id=1, version=1)]
236    /// #[native_db(
237    ///     secondary_key(custom_name -> Option<String>, optional)
238    /// )]
239    /// struct Data {
240    ///     #[primary_key]
241    ///     id: u64,
242    ///     #[secondary_key]
243    ///     name: String,
244    ///     flag: bool,
245    /// }
246    ///
247    /// impl Data {
248    ///     fn custom_name(&self) -> Option<String> {
249    ///         if self.flag {
250    ///             Some(self.name.clone().to_uppercase())
251    ///         } else {
252    ///             None
253    ///         }
254    ///     }
255    /// }
256    /// ```
257    ///
258    /// In the above example, we have:
259    /// - **One primary key** named `id` of type `u64`, defined on the field.
260    /// - **One secondary key** named `name` of type `String`, defined on the field.
261    /// - **One custom secondary key** named `custom_name` of type `Option<String>`, defined using a custom method with the option `optional`.
262    ///
263    /// The method `custom_name` returns an `Option<String>` based on some logic involving the `flag` field.
264    ///
265    /// # Defining Multiple Models
266    ///
267    /// To define multiple models, you **must** use different `id` values for each model. If you use the same `id` for two models,
268    /// 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`.
269    ///
270    /// Example:
271    ///
272    /// ```rust
273    /// use native_db::*;
274    /// use native_model::{native_model, Model};
275    /// use serde::{Deserialize, Serialize};
276    ///
277    /// #[derive(Serialize, Deserialize)]
278    /// #[native_model(id=1, version=1)]
279    /// #[native_db]
280    /// struct Animal {
281    ///     #[primary_key]
282    ///     name: String,
283    /// }
284    ///
285    /// #[derive(Serialize, Deserialize)]
286    /// #[native_model(id=2, version=1)]
287    /// #[native_db]
288    /// struct Vegetable {
289    ///     #[primary_key]
290    ///     name: String,
291    /// }
292    ///
293    /// fn main() -> Result<(), db_type::Error> {
294    ///     let mut models = Models::new();
295    ///     models.define::<Animal>()?;
296    ///     models.define::<Vegetable>()
297    /// }
298    /// ```
299    ///
300    /// In the above example, we have:
301    /// - We have two models, `Animal` and `Vegetable`.
302    /// - Both have:
303    ///   - **One primary key** named `name` of type `String`, defined on the field.
304    /// - Each model has a unique `id` (`id=1` for `Animal`, `id=2` for `Vegetable`), which is necessary to avoid conflicts.
305    pub fn define<T: ToInput>(&mut self) -> Result<()> {
306        let mut new_model_builder = ModelBuilder {
307            model: T::native_db_model(),
308            native_model_options: NativeModelOptions::default(),
309        };
310
311        new_model_builder.native_model_options.native_model_id = T::native_model_id();
312        new_model_builder.native_model_options.native_model_version = T::native_model_version();
313
314        // Set native model legacy
315        for model in self.models_builder.values_mut() {
316            if model.native_model_options.native_model_version
317                > new_model_builder.native_model_options.native_model_version
318            {
319                model.native_model_options.native_model_legacy = false;
320                new_model_builder.native_model_options.native_model_legacy = true;
321            } else {
322                model.native_model_options.native_model_legacy = true;
323                new_model_builder.native_model_options.native_model_legacy = false;
324            }
325
326            // Panic if native model version are the same
327            if model.native_model_options.native_model_id
328                == new_model_builder.native_model_options.native_model_id
329                && model.native_model_options.native_model_version
330                    == new_model_builder.native_model_options.native_model_version
331            {
332                panic!(
333                    "The table {} has the same native model version as the table {} and it's not allowed",
334                    model.model.primary_key.unique_table_name,
335                    new_model_builder.model.primary_key.unique_table_name,
336                );
337            }
338        }
339
340        self.models_builder.insert(
341            new_model_builder
342                .model
343                .primary_key
344                .unique_table_name
345                .clone(),
346            new_model_builder,
347        );
348
349        Ok(())
350    }
351}