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#[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}