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}