native_db_32bit/database_builder.rs
1use crate::db_type::Result;
2use crate::table_definition::NativeModelOptions;
3use crate::{watch, Database, DatabaseModel, Input};
4#[cfg(not(target_has_atomic = "64"))]
5use portable_atomic::AtomicU64;
6use std::collections::HashMap;
7use std::path::Path;
8#[cfg(target_has_atomic = "64")]
9use std::sync::atomic::AtomicU64;
10use std::sync::{Arc, RwLock};
11
12/// Builder that allows you to create a [`Database`](crate::Database) instance via [`create`](Self::create) or [`open`](Self::open) etc. and [define](Self::define) models.
13#[derive(Debug)]
14pub struct DatabaseBuilder {
15 cache_size_bytes: Option<usize>,
16 models_builder: HashMap<String, ModelBuilder>,
17}
18
19impl DatabaseBuilder {
20 fn new_rdb_builder(&self) -> redb::Builder {
21 let mut redb_builder = redb::Builder::new();
22 if let Some(cache_size_bytes) = self.cache_size_bytes {
23 redb_builder.set_cache_size(cache_size_bytes);
24 }
25 redb_builder
26 }
27
28 fn init<'a>(&'a self, redb_database: redb::Database) -> Result<Database<'a>> {
29 let mut database = Database {
30 instance: redb_database,
31 primary_table_definitions: HashMap::new(),
32 watchers: Arc::new(RwLock::new(watch::Watchers::new())),
33 watchers_counter_id: AtomicU64::new(0),
34 };
35
36 for (_, model_builder) in &self.models_builder {
37 database.seed_model(&model_builder)?;
38 }
39
40 Ok(database)
41 }
42}
43
44impl DatabaseBuilder {
45 /// Similar to [redb::Builder::new()](https://docs.rs/redb/latest/redb/struct.Builder.html#method.new).
46 pub fn new() -> Self {
47 Self {
48 cache_size_bytes: None,
49 models_builder: HashMap::new(),
50 }
51 }
52
53 /// Similar to [redb::Builder::set_cache_size()](https://docs.rs/redb/latest/redb/struct.Builder.html#method.set_cache_size).
54 pub fn set_cache_size(&mut self, bytes: usize) -> &mut Self {
55 self.cache_size_bytes = Some(bytes);
56 self
57 }
58
59 /// Creates a new `Db` instance using the given path.
60 ///
61 /// Similar to [redb::Builder.create(...)](https://docs.rs/redb/latest/redb/struct.Builder.html#method.create)
62 pub fn create(&self, path: impl AsRef<Path>) -> Result<Database> {
63 let db = self.new_rdb_builder().create(path)?;
64 // Ok(Self::from_redb(db))
65 self.init(db)
66 }
67
68 /// Similar to [redb::Builder::open(...)](https://docs.rs/redb/latest/redb/struct.Builder.html#method.open)
69 pub fn open(&self, path: impl AsRef<Path>) -> Result<Database> {
70 let db = self.new_rdb_builder().open(path)?;
71 // Ok(Self::from_redb(db))
72 self.init(db)
73 }
74
75 /// Creates a new [`Database`](crate::Database) instance in memory.
76 pub fn create_in_memory(&self) -> Result<Database> {
77 let in_memory_backend = redb::backends::InMemoryBackend::new();
78 let db = self.new_rdb_builder();
79 let db = db.create_with_backend(in_memory_backend)?;
80 // Ok(Self::from_redb(db))
81 self.init(db)
82 }
83
84 /// Defines a table using the given model.
85 ///
86 /// Native DB depends of `native_model` to define the model.
87 /// And `native_model` by default uses [`serde`](https://serde.rs/) to serialize and deserialize the data but
88 /// you can use any other serialization library see the documentation of [`native_model`](https://github.com/vincent-herlemont/native_model) for more information.
89 /// So in the example below we import `serde` and we use the `Serialize` and `Deserialize` traits.
90 ///
91 /// # Primary key
92 ///
93 /// The primary key is *strict*, you **must**:
94 /// - define it.
95 /// - define only one.
96 ///
97 /// If the primary key is not defined, the compiler will return an error `Primary key is not set`.
98 ///
99 /// You can define with two ways:
100 /// - `#[primary_key]` on the field
101 /// - `#[native_db(primary_key(<method_name>))]` on any type `enum`, `struct`, `tuple struct` or `unit struct`.
102 ///
103 /// The primary key is **unique**, so you can't have two instances of the model with the same primary key saved in the database.
104 ///
105 /// ## Define a simple model with a primary key
106 /// ```rust
107 /// use native_db::*;
108 /// use native_model::{native_model, Model};
109 /// use serde::{Deserialize, Serialize};
110 ///
111 /// #[derive(Serialize, Deserialize)]
112 /// #[native_model(id=1, version=1)]
113 /// #[native_db]
114 /// struct Data {
115 /// #[primary_key]
116 /// id: u64,
117 /// }
118 ///
119 /// fn main() -> Result<(), db_type::Error> {
120 /// let mut builder = DatabaseBuilder::new();
121 /// builder.define::<Data>()
122 /// }
123 /// ```
124 /// ## Define a model with a method as primary key
125 /// ```rust
126 /// use native_db::*;
127 /// use native_model::{native_model, Model};
128 /// use serde::{Deserialize, Serialize};
129 ///
130 /// #[derive(Serialize, Deserialize)]
131 /// #[native_model(id=1, version=1)]
132 /// #[native_db(
133 /// primary_key(custom_id)
134 /// )]
135 /// struct Data(u64);
136 ///
137 /// impl Data {
138 /// fn custom_id(&self) -> u32 {
139 /// (self.0 + 1) as u32
140 /// }
141 /// }
142 ///
143 /// ```
144 ///
145 /// ## Secondary key
146 ///
147 /// The secondary key is *flexible*, you can:
148 /// - define it or not.
149 /// - define one or more.
150 ///
151 /// You can define with two ways:
152 /// - `#[secondary_key]` on the field
153 /// - `#[native_db(secondary_key(<method_name>, <options>))]` on any type `enum`, `struct`, `tuple struct` or `unit struct`.
154 ///
155 /// The secondary key can have two options:
156 /// - [`unique`](#unique) (default: false)
157 /// - [`optional`](#optional) (default: false)
158 ///
159 /// ## Define a model with a secondary key
160 /// ```rust
161 /// use native_db::*;
162 /// use native_model::{native_model, Model};
163 /// use serde::{Deserialize, Serialize};
164 ///
165 /// #[derive(Serialize, Deserialize)]
166 /// #[native_model(id=1, version=1)]
167 /// #[native_db]
168 /// struct Data {
169 /// #[primary_key]
170 /// id: u64,
171 /// #[secondary_key]
172 /// name: String,
173 /// }
174 /// ```
175 ///
176 /// ## Define a model wit a secondary key optional and unique
177 /// ```rust
178 /// use native_db::*;
179 /// use native_model::{native_model, Model};
180 /// use serde::{Deserialize, Serialize};
181 ///
182 /// #[derive(Serialize, Deserialize)]
183 /// #[native_model(id=1, version=1)]
184 /// #[native_db]
185 /// struct Data {
186 /// #[primary_key]
187 /// id: u64,
188 /// #[secondary_key(unique, optional)]
189 /// name: Option<String>,
190 /// }
191 /// ```
192 /// - Note: the secondary key can be `unique` **or** `optional` as well.
193 ///
194 /// ## Unique
195 ///
196 /// This means that each instance of the model must have a unique value for the secondary key.
197 /// If the value is not unique, the [`insert`](crate::transaction::RwTransaction::insert) method will return an error.
198 ///
199 /// ## Optional
200 ///
201 /// This means that an instance of the model can have a value for the secondary key or not.
202 /// When`optional` is set the value **must** be an [`Option`](https://doc.rust-lang.org/std/option/enum.Option.html).
203 /// if the value is not an [`Option`](https://doc.rust-lang.org/std/option/enum.Option.html) the compiler will return
204 /// an error `error[E0282]: type annotations needed: cannot infer type`.
205 ///
206 /// Under the hood, the secondary key is stored in a separate redb table. So if the secondary key is optional,
207 /// the value will be stored in the table only if the value is not `None`.
208 ///
209 /// # Define a model with a secondary key and a custom secondary key optional
210 /// ```rust
211 /// use native_db::*;
212 /// use native_model::{native_model, Model};
213 /// use serde::{Deserialize, Serialize};
214 ///
215 /// #[derive(Serialize, Deserialize)]
216 /// #[native_model(id=1, version=1)]
217 /// #[native_db(
218 /// secondary_key(custom_name, optional)
219 /// )]
220 /// struct Data {
221 /// #[primary_key]
222 /// id: u64,
223 /// #[secondary_key]
224 /// name: String,
225 /// flag: bool,
226 /// }
227 ///
228 /// impl Data {
229 /// fn custom_name(&self) -> Option<String> {
230 /// if self.flag {
231 /// Some(self.name.clone().to_uppercase())
232 /// } else {
233 /// None
234 /// }
235 /// }
236 /// }
237 /// ```
238 /// # Define multiple models
239 ///
240 /// To define multiple models, you **must** use different `id` for each model. If you use the same `id` for two models,
241 /// 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`.
242 ///
243 /// Example:
244 /// ```rust
245 /// use native_db::*;
246 /// use native_model::{native_model, Model};
247 /// use serde::{Deserialize, Serialize};
248 ///
249 /// #[derive(Serialize, Deserialize)]
250 /// #[native_model(id=1, version=1)]
251 /// #[native_db]
252 /// struct Animal {
253 /// #[primary_key]
254 /// name: String,
255 /// }
256 ///
257 /// #[derive(Serialize, Deserialize)]
258 /// #[native_model(id=2, version=1)]
259 /// #[native_db]
260 /// struct Vegetable {
261 /// #[primary_key]
262 /// name: String,
263 /// }
264 ///
265 /// fn main() -> Result<(), db_type::Error> {
266 /// let mut builder = DatabaseBuilder::new();
267 /// builder.define::<Animal>()?;
268 /// builder.define::<Vegetable>()
269 /// }
270 /// ```
271 pub fn define<T: Input>(&mut self) -> Result<()> {
272 let mut new_model_builder = ModelBuilder {
273 model: T::native_db_model(),
274 native_model_options: NativeModelOptions::default(),
275 };
276
277 new_model_builder.native_model_options.native_model_id = T::native_model_id();
278 new_model_builder.native_model_options.native_model_version = T::native_model_version();
279
280 // Set native model legacy
281 for model in self.models_builder.values_mut() {
282 if model.native_model_options.native_model_version
283 > new_model_builder.native_model_options.native_model_version
284 {
285 model.native_model_options.native_model_legacy = false;
286 new_model_builder.native_model_options.native_model_legacy = true;
287 } else {
288 model.native_model_options.native_model_legacy = true;
289 new_model_builder.native_model_options.native_model_legacy = false;
290 }
291
292 // Panic if native model version are the same
293 if model.native_model_options.native_model_id
294 == new_model_builder.native_model_options.native_model_id
295 && model.native_model_options.native_model_version
296 == new_model_builder.native_model_options.native_model_version
297 {
298 panic!(
299 "The table {} has the same native model version as the table {} and it's not allowed",
300 model.model.primary_key.unique_table_name,
301 new_model_builder.model.primary_key.unique_table_name,
302 );
303 }
304 }
305
306 self.models_builder.insert(
307 new_model_builder
308 .model
309 .primary_key
310 .unique_table_name
311 .clone(),
312 new_model_builder,
313 );
314
315 // for secondary_key in model.secondary_keys {
316 // model_builder.secondary_tables.insert(
317 // secondary_key.clone(),
318 // redb::TableDefinition::new(&secondary_key.table_name).into(),
319 // );
320 // }
321 // self.primary_table_definitions
322 // .insert(model.primary_key.table_name, primary_table_definition);
323
324 Ok(())
325 }
326}
327
328#[derive(Debug)]
329pub(crate) struct ModelBuilder {
330 pub(crate) model: DatabaseModel,
331 pub(crate) native_model_options: NativeModelOptions,
332}