mdbx_derive_traits/
orm.rs

1use std::collections::HashMap;
2
3use libmdbx_remote::{DatabaseFlags, EnvironmentAny, RW, TransactionKind, WriteFlags};
4
5use crate::{
6    error::MDBXDeriveError,
7    key::{KeyObjectDecode, KeyObjectEncode},
8    table::{TableObjectDecode, TableObjectEncode},
9};
10
11pub fn type_eq<T: ?Sized, U: ?Sized>() -> bool {
12    typeid::of::<T>() == typeid::of::<U>()
13}
14
15pub trait MatchName {
16    fn match_name<T>(&self, name: Option<&str>) -> Option<&T>;
17    fn match_name_mut<T>(&mut self, name: Option<&str>) -> Option<&mut T>;
18}
19
20pub trait MDBXTables<E> {
21    fn create_all(
22        tx: &libmdbx_remote::TransactionAny<RW>,
23        flags: DatabaseFlags,
24    ) -> impl Future<Output = Result<HashMap<String, u32>, E>> + Send;
25}
26
27impl MatchName for () {
28    fn match_name<T>(&self, _name: Option<&str>) -> Option<&T> {
29        None
30    }
31    fn match_name_mut<T>(&mut self, _name: Option<&str>) -> Option<&mut T> {
32        None
33    }
34}
35
36impl<Head, Tail> MatchName for (Head, Tail)
37where
38    Head: MDBXTable,
39    Tail: MatchName,
40{
41    fn match_name<T>(&self, name: Option<&str>) -> Option<&T> {
42        if type_eq::<Head, T>() && name == Head::NAME {
43            unsafe { (&raw const self.0 as *const T).as_ref() }
44        } else {
45            self.1.match_name::<T>(name)
46        }
47    }
48
49    fn match_name_mut<T>(&mut self, name: Option<&str>) -> Option<&mut T> {
50        if type_eq::<Head, T>() && name == Head::NAME {
51            unsafe { (&raw mut self.0 as *mut T).as_mut() }
52        } else {
53            self.1.match_name_mut::<T>(name)
54        }
55    }
56}
57
58impl<E> MDBXTables<E> for () {
59    async fn create_all(
60        _tx: &libmdbx_remote::TransactionAny<RW>,
61        _flags: DatabaseFlags,
62    ) -> Result<HashMap<String, u32>, E> {
63        Ok(HashMap::new())
64    }
65}
66
67impl<Head, Tail, E> MDBXTables<E> for (Head, Tail)
68where
69    E: From<Head::Error> + 'static,
70    Head: MDBXTable,
71    Tail: MDBXTables<E>,
72{
73    async fn create_all(
74        tx: &libmdbx_remote::TransactionAny<RW>,
75        flags: DatabaseFlags,
76    ) -> Result<HashMap<String, u32>, E> {
77        let mut vals = HashMap::new();
78        let dbi = Head::create_table_tx(tx, flags).await?;
79        vals.insert(Head::NAME.map(|s| s.to_string()).unwrap_or_default(), dbi);
80        vals.extend(Tail::create_all(tx, flags).await?);
81        Ok(vals)
82    }
83}
84
85pub trait MDBXTable: Sized {
86    type Key: KeyObjectEncode + KeyObjectDecode + Send + Sync;
87    type Value: TableObjectEncode + TableObjectDecode + Send + Sync;
88    type Error: From<libmdbx_remote::ClientError> + From<MDBXDeriveError> + Send + 'static;
89    type Metadata: TableObjectEncode + TableObjectDecode + Send + Sync;
90    const NAME: Option<&'static str>;
91    const DUPSORT: bool = false;
92
93    fn open_table_tx<T: libmdbx_remote::TransactionKind>(
94        tx: &libmdbx_remote::TransactionAny<T>,
95    ) -> impl Future<Output = Result<u32, Self::Error>> + Send {
96        async {
97            let db = tx.open_db(Self::NAME).await?;
98            Ok(db.dbi())
99        }
100    }
101
102    fn open_table(
103        env: &libmdbx_remote::EnvironmentAny,
104    ) -> impl Future<Output = Result<u32, Self::Error>> + Send {
105        async {
106            let tx = env.begin_ro_txn().await?;
107            Self::open_table_tx(&tx).await
108        }
109    }
110
111    fn create_table_tx(
112        tx: &libmdbx_remote::TransactionAny<libmdbx_remote::RW>,
113        flags: libmdbx_remote::DatabaseFlags,
114    ) -> impl Future<Output = Result<u32, Self::Error>> + Send {
115        async move {
116            let db = tx.create_db(Self::NAME, flags).await?;
117            Ok(db.dbi())
118        }
119    }
120
121    fn create_table(
122        env: &libmdbx_remote::EnvironmentAny,
123        flags: libmdbx_remote::DatabaseFlags,
124    ) -> impl Future<Output = Result<u32, Self::Error>> + Send {
125        async move {
126            let tx = env.begin_rw_txn().await?;
127            Self::create_table_tx(&tx, flags).await
128        }
129    }
130
131    fn get_item(
132        env: &libmdbx_remote::EnvironmentAny,
133        key: &Self::Key,
134    ) -> impl Future<Output = Result<Option<Self::Value>, Self::Error>> + Send {
135        async move {
136            let tx = env.begin_ro_txn().await?;
137            Self::get_item_tx(&tx, None, key).await
138        }
139    }
140
141    fn get_item_tx<T: libmdbx_remote::TransactionKind>(
142        tx: &libmdbx_remote::TransactionAny<T>,
143        dbi: Option<u32>,
144        key: &Self::Key,
145    ) -> impl Future<Output = Result<Option<Self::Value>, Self::Error>> + Send {
146        async move {
147            let dbi = if let Some(dbi) = dbi {
148                dbi
149            } else {
150                Self::open_table_tx(tx).await?
151            };
152            let v = tx
153                .get::<Vec<u8>>(dbi, &key.key_encode()?)
154                .await?
155                .map(|v| Self::Value::table_decode(&v))
156                .transpose()?;
157
158            Ok(v)
159        }
160    }
161
162    fn put_item(
163        env: &libmdbx_remote::EnvironmentAny,
164        key: &Self::Key,
165        value: &Self::Value,
166        flags: libmdbx_remote::WriteFlags,
167    ) -> impl Future<Output = Result<(), Self::Error>> + Send {
168        async move {
169            let tx = env.begin_rw_txn().await?;
170            Self::put_item_tx(&tx, None, key, value, flags).await
171        }
172    }
173
174    fn put_item_tx(
175        tx: &libmdbx_remote::TransactionAny<libmdbx_remote::RW>,
176        dbi: Option<u32>,
177        key: &Self::Key,
178        value: &Self::Value,
179        flags: libmdbx_remote::WriteFlags,
180    ) -> impl Future<Output = Result<(), Self::Error>> + Send {
181        async move {
182            let dbi = if let Some(dbi) = dbi {
183                dbi
184            } else {
185                Self::create_table_tx(tx, libmdbx_remote::DatabaseFlags::default()).await?
186            };
187            tx.put(dbi, &key.key_encode()?, &value.table_encode()?, flags)
188                .await?;
189            Ok(())
190        }
191    }
192}
193
194pub trait HasMDBXEnvironment {
195    fn env(&self) -> &EnvironmentAny;
196}
197
198pub trait HasMDBXDBIStore {
199    fn dbis(&self) -> &HashMap<String, u32>;
200
201    fn dbi<T: MDBXTable>(&self) -> Option<u32> {
202        self.dbis()
203            .get(&T::NAME.map(|v| v.to_string()).unwrap_or_default())
204            .copied()
205    }
206}
207
208pub trait HasMDBXTables {
209    type Error: From<libmdbx_remote::ClientError> + From<MDBXDeriveError> + Send + 'static;
210    type Tables: MDBXTables<Self::Error>;
211}
212
213pub trait MDBXDatabase: Sized + Send + Sync + HasMDBXEnvironment + HasMDBXTables {
214    type Metadata: TableObjectEncode + TableObjectDecode + Send + Sync;
215    const METADATA_NAME: &'static [u8] = b"metadata";
216
217    fn create_all(
218        &self,
219        flags: DatabaseFlags,
220    ) -> impl Future<Output = Result<(), Self::Error>> + Send {
221        async move {
222            let tx = self.env().begin_rw_txn().await?;
223            Self::Tables::create_all(&tx, flags).await?;
224            Ok(())
225        }
226    }
227
228    fn write_metadata_tx(
229        &self,
230        dbi: Option<u32>,
231        tx: &libmdbx_remote::TransactionAny<RW>,
232        meta: &Self::Metadata,
233    ) -> impl Future<Output = Result<(), Self::Error>> + Send {
234        async move {
235            let dbi = if let Some(dbi) = dbi {
236                dbi
237            } else {
238                tx.open_db(None).await?.dbi()
239            };
240            Ok(tx
241                .put(
242                    dbi,
243                    Self::METADATA_NAME,
244                    &meta.table_encode()?,
245                    WriteFlags::default(),
246                )
247                .await?)
248        }
249    }
250
251    fn write_metadata(
252        &self,
253        meta: &Self::Metadata,
254    ) -> impl Future<Output = Result<(), Self::Error>> + Send {
255        async move {
256            let tx = self.env().begin_rw_txn().await?;
257            self.write_metadata_tx(None, &tx, meta).await?;
258            tx.commit().await?;
259            Ok(())
260        }
261    }
262
263    fn metadata_tx<K: TransactionKind>(
264        &self,
265        dbi: Option<u32>,
266        tx: &libmdbx_remote::TransactionAny<K>,
267    ) -> impl Future<Output = Result<Option<Self::Metadata>, Self::Error>> + Send {
268        async move {
269            let dbi = if let Some(dbi) = dbi {
270                dbi
271            } else {
272                tx.open_db(None).await?.dbi()
273            };
274            Ok(tx
275                .get::<Vec<u8>>(dbi, Self::METADATA_NAME)
276                .await?
277                .map(|v| Self::Metadata::table_decode(&v))
278                .transpose()?)
279        }
280    }
281
282    fn metadata(&self) -> impl Future<Output = Result<Option<Self::Metadata>, Self::Error>> + Send {
283        async move {
284            let tx = self.env().begin_ro_txn().await?;
285            self.metadata_tx(None, &tx).await
286        }
287    }
288}
289
290// macros to generate table/database
291
292#[macro_export]
293macro_rules! mdbx_dupsort_table {
294    (
295        $struct_name:ident,
296        $key_type:ty,
297        $value_type:ty
298    ) => {
299        $crate::mdbx_dupsort_table!($struct_name, $key_type, $value_type, mdbx_derive::Error, ());
300    };
301    (
302        $struct_name:ident,
303        $key_type:ty,
304        $value_type:ty,
305        $error_type:ty
306    ) => {
307        $crate::mdbx_dupsort_table!($struct_name, $key_type, $value_type, $error_type, ());
308    };
309    (
310        $struct_name:ident,
311        $key_type:ty,
312        $value_type:ty,
313        $error_type:ty,
314        $metadata_type:ty
315    ) => {
316        impl mdbx_derive::MDBXTable for $struct_name {
317            type Key = $key_type;
318            type Value = $value_type;
319            type Error = $error_type;
320            type Metadata = $metadata_type;
321
322            const DUPSORT: bool = true;
323            const NAME: Option<&'static str> = Some(stringify!($struct_name));
324        }
325    };
326}
327
328#[macro_export]
329macro_rules! mdbx_dupsort_table_def {
330    (
331        $struct_name:ident,
332        $key_type:ty,
333        $value_type:ty
334    ) => {
335        $crate::mdbx_dupsort_table_def!(
336            $struct_name,
337            $key_type,
338            $value_type,
339            mdbx_derive::Error,
340            ()
341        );
342    };
343    (
344        $struct_name:ident,
345        $key_type:ty,
346        $value_type:ty,
347        $error_type:ty
348    ) => {
349        $crate::mdbx_dupsort_table_def!($struct_name, $key_type, $value_type, $error_type, ());
350    };
351    (
352        $struct_name:ident,
353        $key_type:ty,
354        $value_type:ty,
355        $error_type:ty,
356        $metadata_type:ty
357    ) => {
358        #[derive(Clone, Debug, Copy, Default)]
359        pub struct $struct_name;
360
361        impl mdbx_derive::MDBXTable for $struct_name {
362            type Key = $key_type;
363            type Value = $value_type;
364            type Error = $error_type;
365            type Metadata = $metadata_type;
366
367            const DUPSORT: bool = true;
368            const NAME: Option<&'static str> = Some(stringify!($struct_name));
369        }
370    };
371}
372
373#[macro_export]
374macro_rules! mdbx_table {
375    (
376        $struct_name:ident,
377        $key_type:ty,
378        $value_type:ty
379    ) => {
380        $crate::mdbx_table!($struct_name, $key_type, $value_type, mdbx_derive::Error, ());
381    };
382    (
383        $struct_name:ident,
384        $key_type:ty,
385        $value_type:ty,
386        $error_type:ty
387    ) => {
388        $crate::mdbx_table!($struct_name, $key_type, $value_type, $error_type, ());
389    };
390    (
391        $struct_name:ident,
392        $key_type:ty,
393        $value_type:ty,
394        $error_type:ty,
395        $metadata_type:ty
396    ) => {
397        impl mdbx_derive::MDBXTable for $struct_name {
398            type Key = $key_type;
399            type Value = $value_type;
400            type Error = $error_type;
401            type Metadata = $metadata_type;
402
403            const DUPSORT: bool = false;
404            const NAME: Option<&'static str> = Some(stringify!($struct_name));
405        }
406    };
407}
408
409#[macro_export]
410macro_rules! mdbx_table_def {
411    (
412        $struct_name:ident,
413        $key_type:ty,
414        $value_type:ty
415    ) => {
416        $crate::mdbx_table_def!($struct_name, $key_type, $value_type, mdbx_derive::Error, ());
417    };
418    (
419        $struct_name:ident,
420        $key_type:ty,
421        $value_type:ty,
422        $error_type:ty
423    ) => {
424        $crate::mdbx_table_def!($struct_name, $key_type, $value_type, $error_type, ());
425    };
426    (
427        $struct_name:ident,
428        $key_type:ty,
429        $value_type:ty,
430        $error_type:ty,
431        $metadata_type:ty
432    ) => {
433        #[derive(Clone, Debug, Copy, Default)]
434        pub struct $struct_name;
435
436        impl mdbx_derive::MDBXTable for $struct_name {
437            type Key = $key_type;
438            type Value = $value_type;
439            type Error = $error_type;
440            type Metadata = $metadata_type;
441
442            const DUPSORT: bool = false;
443            const NAME: Option<&'static str> = Some(stringify!($struct_name));
444        }
445    };
446}
447
448#[macro_export]
449macro_rules! mdbx_database {
450    (
451        $db_name:ident,
452        $error_type:ty,
453        $metadata_type:ty,
454        $($tables:ty),+
455    ) => {
456        mdbx_derive::paste::paste! {
457            mdbx_derive::generate_dbi_struct!([<$db_name Dbi>], $error_type, $($tables),*);
458
459            #[derive(Debug, Clone)]
460            pub struct $db_name {
461                pub env: mdbx_derive::mdbx::EnvironmentAny,
462                pub dbis: [<$db_name Dbi>]
463            }
464
465            impl std::ops::Deref for $db_name {
466                type Target = mdbx_derive::mdbx::EnvironmentAny;
467                fn deref(&self) -> &Self::Target {
468                    &self.env
469                }
470            }
471
472            impl $db_name {
473                pub fn new(env: mdbx_derive::mdbx::EnvironmentAny, dbis: [<$db_name Dbi>]) -> Self {
474                    Self {
475                        env,
476                        dbis
477                    }
478                }
479
480                pub async fn open_create_tables_with_defaults(url: &str, defaults: mdbx_derive::mdbx::EnvironmentBuilder) -> Result<Self, $error_type> {
481                    let env =  mdbx_derive::mdbx::EnvironmentAny::open_with_defaults(url, defaults).await?;
482                    let dbis = [<$db_name Dbi>]::new(&env)
483                            .await?;
484                    Ok(Self::new(env, dbis))
485                }
486
487                pub async fn open_tables_with_defaults(url: &str, defaults: mdbx_derive::mdbx::EnvironmentBuilder) -> Result<Self, $error_type> {
488                    let env =  mdbx_derive::mdbx::EnvironmentAny::open_with_defaults(url, defaults).await?;
489                    let tx = env.begin_ro_txn().await?;
490                    let dbis = [<$db_name Dbi>]::new_ro(&tx)
491                            .await?;
492                    Ok(Self::new(env, dbis))
493                }
494            }
495        }
496
497        impl mdbx_derive::HasMDBXEnvironment for $db_name {
498            fn env(&self) -> &mdbx_derive::mdbx::EnvironmentAny {
499                &self.env
500            }
501        }
502
503        impl mdbx_derive::MDBXDatabase for $db_name {
504            type Metadata = $metadata_type;
505        }
506
507        impl mdbx_derive::HasMDBXTables for $db_name {
508            type Error = $error_type;
509            type Tables = mdbx_derive::tuple_list_type!($($tables),*);
510        }
511    };
512}