From 7a3e1a351c197d03bff59b8c9ab1b59b0bf3df58 Mon Sep 17 00:00:00 2001 From: librelois <c@elo.tf> Date: Thu, 12 Nov 2020 19:53:19 +0100 Subject: [PATCH] [ref] kv_typed: migrate proc_macro db_schema into macro_rule --- Cargo.lock | 12 +- Cargo.toml | 3 +- rust-bins/duniter-dbex/src/main.rs | 2 +- rust-libs/duniter-dbs-read-ops/src/lib.rs | 16 +- .../src/bc/identities.rs | 6 +- rust-libs/duniter-dbs-write-ops/src/bc/txs.rs | 8 +- rust-libs/duniter-dbs-write-ops/src/bc/uds.rs | 14 +- rust-libs/duniter-dbs-write-ops/src/gva/tx.rs | 10 +- rust-libs/duniter-dbs-write-ops/src/lib.rs | 2 +- rust-libs/duniter-dbs-write-ops/src/txs_mp.rs | 14 +- rust-libs/duniter-dbs/Cargo.toml | 1 + rust-libs/duniter-dbs/src/bc_v1.rs | 62 ++-- rust-libs/duniter-dbs/src/bc_v2.rs | 8 +- rust-libs/duniter-dbs/src/cm_v1.rs | 4 +- rust-libs/duniter-dbs/src/gva_v1.rs | 21 +- rust-libs/duniter-dbs/src/lib.rs | 6 +- rust-libs/duniter-dbs/src/txs_mp_v2.rs | 13 +- rust-libs/duniter-dbs/src/values.rs | 1 - .../src/values/block_number_array_db.rs | 11 - .../duniter-dbs/src/values/hash_array_db.rs | 26 -- .../duniter-dbs/tests/test_read_write.rs | 10 +- rust-libs/duniter-gva/src/queries.rs | 2 +- rust-libs/duniter-gva/src/subscriptions.rs | 2 +- rust-libs/duniter-server/src/lib.rs | 8 +- rust-libs/tools/kv_typed/Cargo.toml | 5 +- .../kv_typed/benches/compare_backends.rs | 14 +- rust-libs/tools/kv_typed/src/as_bytes.rs | 11 +- rust-libs/tools/kv_typed/src/db_schema.rs | 194 +++++++++++++ rust-libs/tools/kv_typed/src/explorer.rs | 123 ++++++-- rust-libs/tools/kv_typed/src/from_bytes.rs | 14 - rust-libs/tools/kv_typed/src/key.rs | 3 - rust-libs/tools/kv_typed/src/lib.rs | 273 +----------------- rust-libs/tools/kv_typed/src/value.rs | 76 ++++- rust-libs/tools/kv_typed/tests/db_schema.rs | 216 -------------- .../tools/kv_typed/tests/test_db_schema.rs | 199 +++++++++++++ rust-libs/tools/kv_typed_code_gen/Cargo.toml | 19 -- .../tools/kv_typed_code_gen/src/col_schema.rs | 81 ------ .../kv_typed_code_gen/src/db_readable.rs | 43 --- .../tools/kv_typed_code_gen/src/db_schema.rs | 48 --- .../kv_typed_code_gen/src/db_writable.rs | 90 ------ rust-libs/tools/kv_typed_code_gen/src/lib.rs | 229 --------------- 41 files changed, 680 insertions(+), 1220 deletions(-) delete mode 100644 rust-libs/duniter-dbs/src/values/hash_array_db.rs create mode 100644 rust-libs/tools/kv_typed/src/db_schema.rs delete mode 100644 rust-libs/tools/kv_typed/tests/db_schema.rs create mode 100644 rust-libs/tools/kv_typed/tests/test_db_schema.rs delete mode 100644 rust-libs/tools/kv_typed_code_gen/Cargo.toml delete mode 100644 rust-libs/tools/kv_typed_code_gen/src/col_schema.rs delete mode 100644 rust-libs/tools/kv_typed_code_gen/src/db_readable.rs delete mode 100644 rust-libs/tools/kv_typed_code_gen/src/db_schema.rs delete mode 100644 rust-libs/tools/kv_typed_code_gen/src/db_writable.rs delete mode 100644 rust-libs/tools/kv_typed_code_gen/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 43b8d55ed..ac57f4423 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1083,6 +1083,7 @@ dependencies = [ "log", "mockall", "once_cell", + "paste", "rand 0.7.3", "serde", "serde_json", @@ -1870,7 +1871,6 @@ dependencies = [ "cfg-if 0.1.10", "criterion", "flume", - "kv_typed_code_gen", "leveldb_minimal", "lmdb-zero", "maybe-async", @@ -1889,16 +1889,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "kv_typed_code_gen" -version = "0.1.0" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "lazy_static" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index af836bf38..006d7cf59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,8 +38,7 @@ members = [ "rust-libs/duniter-gva", "rust-libs/duniter-mempools", "rust-libs/duniter-server", - "rust-libs/tools/kv_typed", - "rust-libs/tools/kv_typed_code_gen" + "rust-libs/tools/kv_typed" ] [patch.crates-io] diff --git a/rust-bins/duniter-dbex/src/main.rs b/rust-bins/duniter-dbex/src/main.rs index f203b5ddd..bc2f22c07 100644 --- a/rust-bins/duniter-dbex/src/main.rs +++ b/rust-bins/duniter-dbex/src/main.rs @@ -269,7 +269,7 @@ fn apply_subcommand<DB: DbExplorable>( } } SubCommand::Schema => { - show_db_schema(db.list_collections()); + show_db_schema(DB::list_collections()); } SubCommand::Migrate => unreachable!(), }; diff --git a/rust-libs/duniter-dbs-read-ops/src/lib.rs b/rust-libs/duniter-dbs-read-ops/src/lib.rs index 489691fb8..d98ccbd14 100644 --- a/rust-libs/duniter-dbs-read-ops/src/lib.rs +++ b/rust-libs/duniter-dbs-read-ops/src/lib.rs @@ -32,20 +32,8 @@ use dubp::documents::transaction::TransactionDocumentV10; use dubp::{common::prelude::BlockNumber, wallet::prelude::SourceAmount}; use duniter_dbs::bc_v2::BcV2DbReadable; use duniter_dbs::{ - kv_typed::prelude::*, - BlockMetaV2, //TxsMpV2DbWritable, - //WalletConditionsV2 - //BlockNumberArrayV2, SourceAmountValV2, UtxosOfScriptV1 - //GvaV1Db, - BlockNumberKeyV2, - GvaV1DbReadable, - //GvaV1DbWritable, - HashKeyV2, - //PendingTxDbV2, - PubKeyKeyV2, - TxDbV2, - //TxsMpV2Db, - TxsMpV2DbReadable, + kv_typed::prelude::*, BlockMetaV2, BlockNumberKeyV2, GvaV1DbReadable, HashKeyV2, PubKeyKeyV2, + TxDbV2, TxsMpV2DbReadable, }; use resiter::map::Map; use std::ops::{Bound, RangeBounds}; diff --git a/rust-libs/duniter-dbs-write-ops/src/bc/identities.rs b/rust-libs/duniter-dbs-write-ops/src/bc/identities.rs index ed42c5014..ccc01d65e 100644 --- a/rust-libs/duniter-dbs-write-ops/src/bc/identities.rs +++ b/rust-libs/duniter-dbs-write-ops/src/bc/identities.rs @@ -14,12 +14,12 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. use crate::*; -use duniter_dbs::bc_v2::IdentityEvent; +use duniter_dbs::bc_v2::IdentitiesEvent; use duniter_dbs::IdtyDbV2; pub(crate) fn update_identities<B: Backend>( block: &DubpBlockV10, - identities: &mut TxColRw<B::Col, IdentityEvent>, + identities: &mut TxColRw<B::Col, IdentitiesEvent>, ) -> KvResult<()> { for idty in block.identities() { let pubkey = idty.issuers()[0]; @@ -61,7 +61,7 @@ pub(crate) fn update_identities<B: Backend>( pub(crate) fn revert_identities<B: Backend>( block: &DubpBlockV10, - identities: &mut TxColRw<B::Col, IdentityEvent>, + identities: &mut TxColRw<B::Col, IdentitiesEvent>, ) -> KvResult<()> { for mb in block.joiners() { let pubkey = mb.issuers()[0]; diff --git a/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs b/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs index 5e4b35a95..07ddfe1f7 100644 --- a/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs +++ b/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs @@ -14,11 +14,11 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. use crate::*; -use duniter_dbs::{bc_v2::UdEvent, UdIdV2}; +use duniter_dbs::{bc_v2::UdsEvent, UdIdV2}; pub(crate) fn apply_txs<B: Backend>( block_txs: &[TransactionDocumentV10], - uds: &mut TxColRw<B::Col, UdEvent>, + uds: &mut TxColRw<B::Col, UdsEvent>, ) -> KvResult<()> { for tx in block_txs { for input in tx.get_inputs() { @@ -36,7 +36,7 @@ pub(crate) fn apply_txs<B: Backend>( pub(crate) fn revert_txs<B: Backend>( block_txs: &[TransactionDocumentV10], - uds: &mut TxColRw<B::Col, UdEvent>, + uds: &mut TxColRw<B::Col, UdsEvent>, ) -> KvResult<()> { for tx in block_txs { for input in tx.get_inputs() { @@ -45,7 +45,7 @@ pub(crate) fn revert_txs<B: Backend>( block_number, }) = input.id { - uds.upsert(UdIdV2(issuer, block_number), EmptyValue); + uds.upsert(UdIdV2(issuer, block_number), ()); } } } diff --git a/rust-libs/duniter-dbs-write-ops/src/bc/uds.rs b/rust-libs/duniter-dbs-write-ops/src/bc/uds.rs index 0eac289a6..140f770c8 100644 --- a/rust-libs/duniter-dbs-write-ops/src/bc/uds.rs +++ b/rust-libs/duniter-dbs-write-ops/src/bc/uds.rs @@ -15,18 +15,18 @@ use crate::*; use duniter_dbs::{ - bc_v2::{IdentityEvent, UdEvent, UdsRevalEvent}, + bc_v2::{IdentitiesEvent, UdsEvent, UdsRevalEvent}, UdIdV2, }; // ["uds_reval", uds_reval, BlockNumberKeyV2, SourceAmountValV2,], -// ["uds", uds, UdIdV2, EmptyValue,], +// ["uds", uds, UdIdV2, (),], pub(crate) fn create_uds<B: Backend>( block_number: BlockNumber, dividend: SourceAmount, - identities: &mut TxColRw<B::Col, IdentityEvent>, - uds: &mut TxColRw<B::Col, UdEvent>, + identities: &mut TxColRw<B::Col, IdentitiesEvent>, + uds: &mut TxColRw<B::Col, UdsEvent>, uds_reval: &mut TxColRw<B::Col, UdsRevalEvent>, ) -> KvResult<()> { let previous_ud_amount = uds_reval @@ -41,15 +41,15 @@ pub(crate) fn create_uds<B: Backend>( .collect::<KvResult<Vec<_>>>() })?; for member in members { - uds.upsert(UdIdV2(member, block_number), EmptyValue); + uds.upsert(UdIdV2(member, block_number), ()); } Ok(()) } pub(crate) fn revert_uds<B: Backend>( block_number: BlockNumber, - identities: &mut TxColRw<B::Col, IdentityEvent>, - uds: &mut TxColRw<B::Col, UdEvent>, + identities: &mut TxColRw<B::Col, IdentitiesEvent>, + uds: &mut TxColRw<B::Col, UdsEvent>, uds_reval: &mut TxColRw<B::Col, UdsRevalEvent>, ) -> KvResult<()> { let previous_reval_block_number = uds_reval diff --git a/rust-libs/duniter-dbs-write-ops/src/gva/tx.rs b/rust-libs/duniter-dbs-write-ops/src/gva/tx.rs index cf714942a..e221091b4 100644 --- a/rust-libs/duniter-dbs-write-ops/src/gva/tx.rs +++ b/rust-libs/duniter-dbs-write-ops/src/gva/tx.rs @@ -40,7 +40,7 @@ pub(crate) fn write_gva_tx<B: Backend>( // Insert on col `txs_by_issuer` for pubkey in tx.issuers() { let mut hashs = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default(); - hashs.0.insert(tx_hash); + hashs.insert(tx_hash); txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs); } // Insert on col `txs_by_recipient` @@ -48,7 +48,7 @@ pub(crate) fn write_gva_tx<B: Backend>( let mut hashs = txs_by_recipient .get(&PubKeyKeyV2(pubkey))? .unwrap_or_default(); - hashs.0.insert(tx_hash); + hashs.insert(tx_hash); txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs); } @@ -193,14 +193,14 @@ pub(crate) fn revert_tx<B: Backend>( fn remove_tx<B: Backend>( txs_by_issuer: &mut TxColRw<B::Col, TxsByIssuerEvent>, txs_by_recipient: &mut TxColRw<B::Col, TxsByRecipientEvent>, - txs: &mut TxColRw<B::Col, TxEvent>, + txs: &mut TxColRw<B::Col, TxsEvent>, tx_hash: &Hash, tx_db: &TxDbV2, ) -> KvResult<()> { // Remove tx hash in col `txs_by_issuer` for pubkey in tx_db.tx.issuers() { let mut hashs_ = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default(); - hashs_.0.remove(&tx_hash); + hashs_.remove(&tx_hash); txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs_) } // Remove tx hash in col `txs_by_recipient` @@ -208,7 +208,7 @@ fn remove_tx<B: Backend>( let mut hashs_ = txs_by_recipient .get(&PubKeyKeyV2(pubkey))? .unwrap_or_default(); - hashs_.0.remove(&tx_hash); + hashs_.remove(&tx_hash); txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs_) } // Remove tx itself diff --git a/rust-libs/duniter-dbs-write-ops/src/lib.rs b/rust-libs/duniter-dbs-write-ops/src/lib.rs index 42026eab3..f03e95980 100644 --- a/rust-libs/duniter-dbs-write-ops/src/lib.rs +++ b/rust-libs/duniter-dbs-write-ops/src/lib.rs @@ -37,7 +37,7 @@ use dubp::documents::{ transaction::TransactionDocumentV10, }; use dubp::wallet::prelude::*; -use duniter_dbs::gva_v1::{TxEvent, TxsByIssuerEvent, TxsByRecipientEvent}; +use duniter_dbs::gva_v1::{TxsByIssuerEvent, TxsByRecipientEvent, TxsEvent}; use duniter_dbs::{ kv_typed::prelude::*, BlockMetaV2, BlockNumberKeyV2, DuniterDbs, GvaV1Db, GvaV1DbReadable, GvaV1DbWritable, HashKeyV2, PendingTxDbV2, PubKeyKeyV2, PubKeyValV2, SourceAmountValV2, TxDbV2, diff --git a/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs b/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs index 3765a2d46..c5ee49925 100644 --- a/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs +++ b/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs @@ -41,7 +41,7 @@ pub fn add_pending_tx< B: Backend, F: FnOnce( &TransactionDocumentV10, - &TxColRw<B::Col, duniter_dbs::txs_mp_v2::TxEvent>, + &TxColRw<B::Col, duniter_dbs::txs_mp_v2::TxsEvent>, ) -> KvResult<()>, >( control: F, @@ -61,12 +61,12 @@ pub fn add_pending_tx< control(&tx, &txs)?; // Insert on col `txs_by_recv_time` let mut hashs = txs_by_recv_time.get(&received_time)?.unwrap_or_default(); - hashs.0.insert(tx_hash); + hashs.insert(tx_hash); txs_by_recv_time.upsert(received_time, hashs); // Insert on col `txs_by_issuer` for pubkey in tx.issuers() { let mut hashs = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default(); - hashs.0.insert(tx.get_hash()); + hashs.insert(tx.get_hash()); txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs); } // Insert on col `txs_by_recipient` @@ -74,7 +74,7 @@ pub fn add_pending_tx< let mut hashs = txs_by_recipient .get(&PubKeyKeyV2(pubkey))? .unwrap_or_default(); - hashs.0.insert(tx.get_hash()); + hashs.insert(tx.get_hash()); txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs); } // Insert tx itself @@ -107,7 +107,7 @@ pub fn trim_expired_non_written_txs<B: Backend>( let hashs = txs_mp_db.txs_by_recv_time().iter(..limit_time, |it| { it.map_ok(|(k, v)| { times.push(k); - v.0 + v }) .flatten_ok() .collect::<KvResult<SmallVec<[Hash; 4]>>>() @@ -133,7 +133,7 @@ fn remove_one_pending_tx<B: Backend>(txs_mp_db: &TxsMpV2Db<B>, tx_hash: Hash) -> // Remove tx hash in col `txs_by_issuer` for pubkey in tx.0.issuers() { let mut hashs_ = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default(); - hashs_.0.remove(&tx_hash); + hashs_.remove(&tx_hash); txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs_) } // Remove tx hash in col `txs_by_recipient` @@ -141,7 +141,7 @@ fn remove_one_pending_tx<B: Backend>(txs_mp_db: &TxsMpV2Db<B>, tx_hash: Hash) -> let mut hashs_ = txs_by_recipient .get(&PubKeyKeyV2(pubkey))? .unwrap_or_default(); - hashs_.0.remove(&tx_hash); + hashs_.remove(&tx_hash); txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs_) } // Remove tx itself diff --git a/rust-libs/duniter-dbs/Cargo.toml b/rust-libs/duniter-dbs/Cargo.toml index 599a96b58..0e00c5750 100644 --- a/rust-libs/duniter-dbs/Cargo.toml +++ b/rust-libs/duniter-dbs/Cargo.toml @@ -20,6 +20,7 @@ dubp = { version = "0.30.0" } kv_typed = { path = "../tools/kv_typed", default-features = false } log = "0.4.8" mockall = { version = "0.8.0", optional = true } +paste = "1.0.2" rand = "0.7.3" serde = { version = "1.0.105", features = ["derive"] } serde_json = "1.0.53" diff --git a/rust-libs/duniter-dbs/src/bc_v1.rs b/rust-libs/duniter-dbs/src/bc_v1.rs index 870bea9ad..bf01ede5e 100644 --- a/rust-libs/duniter-dbs/src/bc_v1.rs +++ b/rust-libs/duniter-dbs/src/bc_v1.rs @@ -18,139 +18,139 @@ use crate::*; db_schema!( BcV1, [ - ["level_blockchain", main_blocks, BlockNumberKeyV1, BlockDbV1,], + ["level_blockchain", MainBlocks, BlockNumberKeyV1, BlockDbV1], [ "level_blockchain/idty", - mb_idty, + MbIdty, PubKeyKeyV1, - BlockNumberArrayV1, + BlockNumberArrayV1 ], [ "level_blockchain/certs", - mb_certs, + MbCerts, PubKeyKeyV1, BlockNumberArrayV1 ], [ "level_blockchain/joiners", - mb_joiners, + MbJoiners, PubKeyKeyV1, BlockNumberArrayV1 ], [ "level_blockchain/actives", - mb_actives, + MbActives, PubKeyKeyV1, BlockNumberArrayV1 ], [ "level_blockchain/leavers", - mb_leavers, + MbLeavers, PubKeyKeyV1, BlockNumberArrayV1 ], [ "level_blockchain/excluded", - mb_excluded, + MbExcluded, PubKeyKeyV1, BlockNumberArrayV1 ], [ "level_blockchain/revoked", - mb_revoked, + MbRevoked, PubKeyAndSigV1, BlockNumberArrayV1 ], [ "level_blockchain/dividends", - mb_dividends, + MbDividends, AllKeyV1, BlockNumberArrayV1 ], [ "level_blockchain/transactions", - mb_transactions, + MbTransactions, AllKeyV1, BlockNumberArrayV1 ], [ "level_blockchain/forks", - fork_blocks, + ForkBlocks, BlockstampKeyV1, BlockDbV1 ], - ["level_bindex", bindex, BlockNumberKeyV1, BlockHeadDbV1], - ["level_iindex", iindex, PubKeyKeyV1, IIndexDbV1], + ["level_bindex", Bindex, BlockNumberKeyV1, BlockHeadDbV1], + ["level_iindex", Iindex, PubKeyKeyV1, IIndexDbV1], [ "level_iindex/hash", - iindex_hash, + IindexHash, HashKeyV1, PublicKeySingletonDbV1 ], - ["level_iindex/kick", iindex_kick, PubKeyKeyV1, KickDbV1], + ["level_iindex/kick", IindexKick, PubKeyKeyV1, KickDbV1], [ "level_iindex/writtenOn", - iindex_written_on, + IindexWrittenOn, BlockNumberKeyV1, PublicKeyArrayDbV1 ], - ["level_iindex/uid", uids, UidKeyV1, PublicKeySingletonDbV1], - ["level_mindex", mindex, PubKeyKeyV1, MIndexDbV1], + ["level_iindex/uid", Uids, UidKeyV1, PublicKeySingletonDbV1], + ["level_mindex", Mindex, PubKeyKeyV1, MIndexDbV1], [ "level_mindex/expiresOn", - mindex_expires_on, + MindexExpiresOn, TimestampKeyV1, PublicKeyArrayDbV1 ], [ "level_mindex/revokesOn", - mindex_revokes_on, + MindexRevokesOn, TimestampKeyV1, PublicKeyArrayDbV1 ], [ "level_mindex/writtenOn", - mindex_written_on, + MindexWrittenOn, BlockNumberKeyV1, PublicKeyArrayDbV1 ], - ["level_cindex", cindex, PubKeyKeyV1, CIndexDbV1], + ["level_cindex", Cindex, PubKeyKeyV1, CIndexDbV1], [ "level_cindex/expiresOn", - cindex_expires_on, + CindexExpiresOn, BlockNumberKeyV1, PublicKeyArrayDbV1 ], [ "level_cindex/writtenOn", - cindex_written_on, + CindexWrittenOn, BlockNumberKeyV1, PublicKeyArrayDbV1 ], ["level_wallet", Wallet, WalletConditionsV1, WalletDbV1], - ["level_dividend", uds, PubKeyKeyV1, UdEntryDbV1], + ["level_dividend", Uds, PubKeyKeyV1, UdEntryDbV1], [ "level_dividend/level_dividend_trim_index", - uds_trim, + UdsTrim, BlockNumberKeyV1, PublicKeyArrayDbV1 ], - ["level_sindex", sindex, SourceKeyV1, SIndexDBV1], + ["level_sindex", Sindex, SourceKeyV1, SIndexDBV1], [ "level_sindex/written_on", - sindex_written_on, + SindexWrittenOn, BlockNumberKeyV1, SourceKeyArrayDbV1 ], [ "level_sindex/consumed_on", - sindex_consumed_on, + SindexConsumedOn, BlockNumberKeyV1, SourceKeyArrayDbV1 ], [ "level_sindex/conditions", - sindex_conditions, + SindexConditions, WalletConditionsV1, SourceKeyArrayDbV1 ], diff --git a/rust-libs/duniter-dbs/src/bc_v2.rs b/rust-libs/duniter-dbs/src/bc_v2.rs index 79db9a391..4ab7b559b 100644 --- a/rust-libs/duniter-dbs/src/bc_v2.rs +++ b/rust-libs/duniter-dbs/src/bc_v2.rs @@ -18,9 +18,9 @@ use crate::*; db_schema!( BcV2, [ - ["blocks_meta", blocks_meta, BlockNumberKeyV2, BlockMetaV2,], - ["uds_reval", uds_reval, BlockNumberKeyV2, SourceAmountValV2,], - ["identities", identities, PubKeyKeyV2, IdtyDbV2,], - ["uds", uds, UdIdV2, EmptyValue,], + ["blocks_meta", BlocksMeta, BlockNumberKeyV2, BlockMetaV2], + ["uds_reval", UdsReval, BlockNumberKeyV2, SourceAmountValV2], + ["identities", Identities, PubKeyKeyV2, IdtyDbV2], + ["uds", Uds, UdIdV2, ()], ] ); diff --git a/rust-libs/duniter-dbs/src/cm_v1.rs b/rust-libs/duniter-dbs/src/cm_v1.rs index 0f3b2b61f..7e1eb4e1e 100644 --- a/rust-libs/duniter-dbs/src/cm_v1.rs +++ b/rust-libs/duniter-dbs/src/cm_v1.rs @@ -18,7 +18,7 @@ use crate::*; db_schema!( CmV1, [ - //["self_pubkey", self_pubkey, EmptyKey, PubKeyValV2,], - ["self_peer_card", self_peer_card, EmptyKey, PeerCardDbV1,], + //["self_pubkey", self_pubkey, (), PubKeyValV2,], + ["self_peer_card", SelfPeerCard, (), PeerCardDbV1], ] ); diff --git a/rust-libs/duniter-dbs/src/gva_v1.rs b/rust-libs/duniter-dbs/src/gva_v1.rs index a3623490b..b796d1fad 100644 --- a/rust-libs/duniter-dbs/src/gva_v1.rs +++ b/rust-libs/duniter-dbs/src/gva_v1.rs @@ -18,26 +18,21 @@ use crate::*; db_schema!( GvaV1, [ - ["uids_index", uids_index, String, PubKeyValV2,], - ["txs", txs, HashKeyV2, TxDbV2,], - ["txs_by_issuer", txs_by_issuer, PubKeyKeyV2, HashBTSetV2,], - [ - "txs_by_recipient", - txs_by_recipient, - PubKeyKeyV2, - HashBTSetV2, - ], + ["uids_index", UidsIndex, String, PubKeyValV2], + ["txs", Txs, HashKeyV2, TxDbV2], + ["txs_by_issuer", TxsByIssuer, PubKeyKeyV2, BTreeSet<Hash>], + ["txs_by_recipient", TxsByRecipient, PubKeyKeyV2, BTreeSet<Hash>], [ "scripts_by_pubkey", - scripts_by_pubkey, + ScriptsByPubkey, PubKeyKeyV2, - WalletScriptArrayV2, + WalletScriptArrayV2 ], [ "utxos_by_script", - utxos_by_script, + UtxosByScript, WalletConditionsV2, - UtxosOfScriptV1, + UtxosOfScriptV1 ], ] ); diff --git a/rust-libs/duniter-dbs/src/lib.rs b/rust-libs/duniter-dbs/src/lib.rs index ebbe6c13c..b43c04d56 100644 --- a/rust-libs/duniter-dbs/src/lib.rs +++ b/rust-libs/duniter-dbs/src/lib.rs @@ -58,7 +58,7 @@ pub use crate::errors::Result; pub use crate::open_dbs::open_dbs; // Export profession types -pub use bc_v1::{BcV1Db, BcV1DbReadable, BcV1DbRo, BcV1DbWritable, MainBlockEvent, UidEvent}; +pub use bc_v1::{BcV1Db, BcV1DbReadable, BcV1DbRo, BcV1DbWritable, MainBlocksEvent, UidsEvent}; pub use gva_v1::{GvaV1Db, GvaV1DbReadable, GvaV1DbRo, GvaV1DbWritable}; pub use keys::all::AllKeyV1; pub use keys::block_number::{BlockNumberKeyV1, BlockNumberKeyV2}; @@ -75,9 +75,8 @@ pub use txs_mp_v2::{TxsMpV2Db, TxsMpV2DbReadable, TxsMpV2DbRo, TxsMpV2DbWritable pub use values::block_db::{BlockDbEnum, BlockDbV1, TransactionInBlockDbV1}; pub use values::block_head_db::BlockHeadDbV1; pub use values::block_meta::BlockMetaV2; -pub use values::block_number_array_db::{BlockNumberArrayV1, BlockNumberArrayV2}; +pub use values::block_number_array_db::BlockNumberArrayV1; pub use values::cindex_db::CIndexDbV1; -pub use values::hash_array_db::HashBTSetV2; pub use values::idty_db::IdtyDbV2; pub use values::iindex_db::IIndexDbV1; pub use values::kick_db::KickDbV1; @@ -103,6 +102,7 @@ pub(crate) use dubp::common::crypto::keys::ed25519::{PublicKey, Signature}; pub(crate) use dubp::common::crypto::keys::{PublicKey as _, Signature as _}; pub(crate) use dubp::common::prelude::*; pub(crate) use dubp::documents::dubp_wallet::prelude::*; +pub(crate) use kv_typed::db_schema; pub(crate) use kv_typed::prelude::*; pub(crate) use serde::{Deserialize, Serialize}; pub(crate) use smallvec::SmallVec; diff --git a/rust-libs/duniter-dbs/src/txs_mp_v2.rs b/rust-libs/duniter-dbs/src/txs_mp_v2.rs index ceec9ec31..95b6cbaeb 100644 --- a/rust-libs/duniter-dbs/src/txs_mp_v2.rs +++ b/rust-libs/duniter-dbs/src/txs_mp_v2.rs @@ -18,14 +18,9 @@ use crate::*; db_schema!( TxsMpV2, [ - ["txs", txs, HashKeyV2, PendingTxDbV2,], - ["txs_by_issuer", txs_by_issuer, PubKeyKeyV2, HashBTSetV2,], - [ - "txs_by_recipient", - txs_by_recipient, - PubKeyKeyV2, - HashBTSetV2, - ], - ["txs_by_received_time", txs_by_recv_time, i64, HashBTSetV2,], + ["txs", Txs, HashKeyV2, PendingTxDbV2], + ["txs_by_issuer", TxsByIssuer, PubKeyKeyV2, BTreeSet<Hash>], + ["txs_by_recipient", TxsByRecipient, PubKeyKeyV2, BTreeSet<Hash>], + ["txs_by_received_time", TxsByRecvTime, i64, BTreeSet<Hash>], ] ); diff --git a/rust-libs/duniter-dbs/src/values.rs b/rust-libs/duniter-dbs/src/values.rs index 3c8cb10b2..8f9369419 100644 --- a/rust-libs/duniter-dbs/src/values.rs +++ b/rust-libs/duniter-dbs/src/values.rs @@ -18,7 +18,6 @@ pub mod block_head_db; pub mod block_meta; pub mod block_number_array_db; pub mod cindex_db; -pub mod hash_array_db; pub mod idty_db; pub mod iindex_db; pub mod kick_db; diff --git a/rust-libs/duniter-dbs/src/values/block_number_array_db.rs b/rust-libs/duniter-dbs/src/values/block_number_array_db.rs index 20286a3e0..9fbf01ae2 100644 --- a/rust-libs/duniter-dbs/src/values/block_number_array_db.rs +++ b/rust-libs/duniter-dbs/src/values/block_number_array_db.rs @@ -52,14 +52,3 @@ impl ExplorableValue for BlockNumberArrayV1 { serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e))) } } - -#[derive(Debug, PartialEq)] -#[repr(transparent)] -pub struct BlockNumberArrayV2(pub SmallVec<[BlockNumber; 16]>); -kv_typed::impl_value_for_smallvec_zc!(BlockNumberArrayV2, BlockNumber, 16); - -impl ToDumpString for BlockNumberArrayV2 { - fn to_dump_string(&self) -> String { - todo!() - } -} diff --git a/rust-libs/duniter-dbs/src/values/hash_array_db.rs b/rust-libs/duniter-dbs/src/values/hash_array_db.rs deleted file mode 100644 index 8ff946a9c..000000000 --- a/rust-libs/duniter-dbs/src/values/hash_array_db.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2020 Éloïs SANCHEZ. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -use crate::*; - -#[derive(Debug, Default, PartialEq)] -pub struct HashBTSetV2(pub BTreeSet<Hash>); -kv_typed::impl_value_for_btreeset_zc!(HashBTSetV2, Hash); - -impl ToDumpString for HashBTSetV2 { - fn to_dump_string(&self) -> String { - todo!() - } -} diff --git a/rust-libs/duniter-dbs/tests/test_read_write.rs b/rust-libs/duniter-dbs/tests/test_read_write.rs index ee06bb030..60db378d8 100644 --- a/rust-libs/duniter-dbs/tests/test_read_write.rs +++ b/rust-libs/duniter-dbs/tests/test_read_write.rs @@ -18,7 +18,7 @@ use dubp::common::crypto::keys::PublicKey as _; use dubp::common::prelude::*; use duniter_dbs::kv_typed::prelude::*; use duniter_dbs::{ - BcV1Db, BcV1DbReadable, BcV1DbWritable, BlockDbV1, BlockNumberKeyV1, MainBlockEvent, + BcV1Db, BcV1DbReadable, BcV1DbWritable, BlockDbV1, BlockNumberKeyV1, MainBlocksEvent, PublicKeySingletonDbV1, Result, UidKeyV1, }; use kv_typed::channel::TryRecvError; @@ -152,7 +152,7 @@ fn write_read_delete_b0_test<B: Backend>(db: &BcV1Db<B>) -> Result<()> { let event = &events[0]; assert_eq!( event, - &MainBlockEvent::Upsert { + &MainBlocksEvent::Upsert { key: BlockNumberKeyV1(BlockNumber(0)), value: b0, }, @@ -184,7 +184,7 @@ fn write_read_delete_b0_test<B: Backend>(db: &BcV1Db<B>) -> Result<()> { let event = &events[0]; assert_eq!( event, - &MainBlockEvent::Remove { + &MainBlocksEvent::Remove { key: BlockNumberKeyV1(BlockNumber(0)), }, ); @@ -388,11 +388,11 @@ fn batch_test<B: Backend>(db: &BcV1Db<B>) -> Result<()> { assert!(assert_eq_pairs( [&events[0], &events[1]], [ - &MainBlockEvent::Upsert { + &MainBlocksEvent::Upsert { key: BlockNumberKeyV1(BlockNumber(0)), value: b0, }, - &MainBlockEvent::Upsert { + &MainBlocksEvent::Upsert { key: BlockNumberKeyV1(BlockNumber(1)), value: b1, } diff --git a/rust-libs/duniter-gva/src/queries.rs b/rust-libs/duniter-gva/src/queries.rs index ea08f16cf..744263987 100644 --- a/rust-libs/duniter-gva/src/queries.rs +++ b/rust-libs/duniter-gva/src/queries.rs @@ -49,7 +49,7 @@ impl Node { Ok(data .dbs_pool - .execute(move |dbs| dbs.cm_db.self_peer_card().get(&EmptyKey)) + .execute(move |dbs| dbs.cm_db.self_peer_card().get(&())) .await?? .map(Into::into)) } diff --git a/rust-libs/duniter-gva/src/subscriptions.rs b/rust-libs/duniter-gva/src/subscriptions.rs index 3504e5fe0..5be753736 100644 --- a/rust-libs/duniter-gva/src/subscriptions.rs +++ b/rust-libs/duniter-gva/src/subscriptions.rs @@ -37,7 +37,7 @@ impl SubscriptionRoot { r.into_stream().filter_map(|events| { let mut txs = Vec::new(); for event in events.deref() { - if let duniter_dbs::txs_mp_v2::TxEvent::Upsert { + if let duniter_dbs::txs_mp_v2::TxsEvent::Upsert { value: ref pending_tx, .. } = event diff --git a/rust-libs/duniter-server/src/lib.rs b/rust-libs/duniter-server/src/lib.rs index e99820f34..77bdad24b 100644 --- a/rust-libs/duniter-server/src/lib.rs +++ b/rust-libs/duniter-server/src/lib.rs @@ -61,7 +61,7 @@ pub struct DuniterServer { conf: DuniterServerConf, current: Option<BlockMetaV2>, dbs_pool: fast_threadpool::ThreadPoolSyncHandler<DuniterDbs>, - pending_txs_subscriber: flume::Receiver<Arc<Events<duniter_dbs::txs_mp_v2::TxEvent>>>, + pending_txs_subscriber: flume::Receiver<Arc<Events<duniter_dbs::txs_mp_v2::TxsEvent>>>, txs_mempool: TxsMempool, } @@ -165,10 +165,10 @@ impl DuniterServer { use std::ops::Deref as _; for event in events.deref() { match event { - duniter_dbs::txs_mp_v2::TxEvent::Upsert { key, value } => { + duniter_dbs::txs_mp_v2::TxsEvent::Upsert { key, value } => { new_pending_txs.insert(key.0, value.0.clone()); } - duniter_dbs::txs_mp_v2::TxEvent::Remove { key } => { + duniter_dbs::txs_mp_v2::TxsEvent::Remove { key } => { new_pending_txs.remove(&key.0); } _ => (), @@ -307,7 +307,7 @@ impl DuniterServer { dbs.cm_db .self_peer_card_write() .upsert( - EmptyKey, + (), duniter_dbs::PeerCardDbV1 { version: new_peer_card.version, currency: new_peer_card.currency, diff --git a/rust-libs/tools/kv_typed/Cargo.toml b/rust-libs/tools/kv_typed/Cargo.toml index b7973453a..d1d549612 100644 --- a/rust-libs/tools/kv_typed/Cargo.toml +++ b/rust-libs/tools/kv_typed/Cargo.toml @@ -14,7 +14,6 @@ path = "src/lib.rs" [dependencies] cfg-if = "0.1.10" flume = "0.9.1" -kv_typed_code_gen = { path = "../kv_typed_code_gen" } leveldb_minimal = { version = "0.1.0", optional = true } lmdb-zero = { version = "0.4.4", optional = true } mockall = { version = "0.8.0", optional = true } @@ -45,7 +44,7 @@ unwrap = "1.2.1" criterion = { version = "0.3.1" } [features] -default = ["sled_backend"] +#default = ["sled_backend"] async = [] explorer = ["rayon", "regex", "serde_json"] @@ -55,5 +54,5 @@ sled_backend = ["sled"] #mock = ["mockall"] -#default = ["explorer"] +default = ["sled_backend", "explorer"] #default = ["mock"] diff --git a/rust-libs/tools/kv_typed/benches/compare_backends.rs b/rust-libs/tools/kv_typed/benches/compare_backends.rs index cce394b8f..d21049b49 100644 --- a/rust-libs/tools/kv_typed/benches/compare_backends.rs +++ b/rust-libs/tools/kv_typed/benches/compare_backends.rs @@ -2,7 +2,7 @@ use criterion::{criterion_group, criterion_main, Criterion, /*, AxisScale, PlotC use kv_typed::prelude::*; use std::{fmt::Debug, path::PathBuf}; -db_schema!(Test, [["c1", col_1, u32, String],]); +kv_typed::db_schema!(Test, [["c1", Col1, u32, String],]); //const LEVELDB_DIR_PATH: &str = "/dev/shm/kv_typed/benches/compare_backends/leveldb"; //const LMDB_DIR_PATH: &str = "/dev/shm/kv_typed/benches/compare_backends/lmdb"; const LEVELDB_DIR_PATH: &str = "/home/elois/tmp/kv_typed/benches/compare_backends/leveldb"; @@ -13,28 +13,28 @@ static LARGE_VAL: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZab fn read_n_entries<B: Backend>(db: &TestDb<B>, n: u32, val: String) { for i in 0..n { - assert_eq!(db.col_1().get(&i).expect("db err"), Some(val.clone())); + assert_eq!(db.col1().get(&i).expect("db err"), Some(val.clone())); } - /*db.col_1().iter(.., |iter| { + /*db.col1().iter(.., |iter| { let mut iter = iter.values(); for _ in 0..n { assert_eq!(iter.next_res().expect(""), Some(val.clone())); - //assert_eq!(db.col_1().get(&i).expect(""), Some(val.clone())); + //assert_eq!(db.col1().get(&i).expect(""), Some(val.clone())); } assert_eq!(iter.next_res().expect(""), None); });*/ } fn remove_and_write_n_entries<B: Backend>(db: &TestDb<B>, n: u32, val: String) { for i in 0..n { - db.col_1_write().remove(i).expect("fail to write"); - db.col_1_write() + db.col1_write().remove(i).expect("fail to write"); + db.col1_write() .upsert(i, val.clone()) .expect("fail to write"); } } fn write_n_entries<B: Backend>(db: &TestDb<B>, n: u32, val: String) { for i in 0..n { - db.col_1_write() + db.col1_write() .upsert(i, val.clone()) .expect("fail to write"); } diff --git a/rust-libs/tools/kv_typed/src/as_bytes.rs b/rust-libs/tools/kv_typed/src/as_bytes.rs index bfa37e87f..0ba24d8e0 100644 --- a/rust-libs/tools/kv_typed/src/as_bytes.rs +++ b/rust-libs/tools/kv_typed/src/as_bytes.rs @@ -28,16 +28,7 @@ impl KeyAsBytes for () { } fn fill_bytes(&self, _: &mut [u8]) {} } -impl KeyAsBytes for EmptyKey { - fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T { - f(&[]) - } - fn fill_bytes_size(&self) -> Option<usize> { - Some(0) - } - fn fill_bytes(&self, _: &mut [u8]) {} -} -impl ValueAsBytes for EmptyValue { +impl ValueAsBytes for () { fn as_bytes<T, F: FnMut(&[u8]) -> Result<T, KvError>>(&self, mut f: F) -> Result<T, KvError> { f(&[]) } diff --git a/rust-libs/tools/kv_typed/src/db_schema.rs b/rust-libs/tools/kv_typed/src/db_schema.rs new file mode 100644 index 000000000..925c04d7c --- /dev/null +++ b/rust-libs/tools/kv_typed/src/db_schema.rs @@ -0,0 +1,194 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +#[macro_export] +macro_rules! db_schema { + ($db_name:ident, [ $([$col_path:literal, $col_name:ident, $K:ty, $V:ty]),*, ]) => { + paste::paste! { + $( + // Define each collection event type + #[derive(Debug, PartialEq)] + pub enum [<$col_name Event>] { + Upsert { key: $K, value: $V }, + Remove { key: $K }, + RemoveAll, + } + impl kv_typed::prelude::EventTrait for [<$col_name Event>] { + type K = $K; + type V = $V; + + fn clear() -> Self { Self::RemoveAll } + fn upsert(k: Self::K, v: Self::V) -> Self { Self::Upsert { key: k, value: v, } } + fn remove(k: Self::K) -> Self { Self::Remove { key: k } } + } + )* + // Inner module used to hide internals types that must not be exposed on public api + pub use __inner::{[<$db_name Db>], [<$db_name DbRo>], [<$db_name DbWritable>], [<$db_name DbReadable>]}; + mod __inner { + use super::*; + use kv_typed::prelude::*; + // DbCollections + #[derive(Clone, Debug)] + pub struct [<$db_name ColsRo>]<BC: BackendCol> { + $([<$col_name:snake>]: ColRo<BC, [<$col_name Event>]>,)* + } + #[derive(Clone, Debug)] + pub struct [<$db_name ColsRw>]<BC: BackendCol> { + $([<$col_name:snake>]: ColRw<BC, [<$col_name Event>]>,)* + } + impl<BC: BackendCol> [<$db_name ColsRw>]<BC> { + fn to_ro(&self) -> [<$db_name ColsRo>]<BC> { + [<$db_name ColsRo>] { + $([<$col_name:snake>]: self.[<$col_name:snake>].to_ro().clone(),)* + } + } + } + // Db + #[derive(Debug)] + pub struct [<$db_name Db>]<B: Backend> { + collections: [<$db_name ColsRw>]<B::Col>, + } + impl<B: Backend> [<$db_name Db>]<B> { + pub const NAME: &'static str = stringify!([<$db_name:snake>]); + } + impl<B: Backend> Clone for [<$db_name Db>]<B> { + fn clone(&self) -> Self { + [<$db_name Db>] { + collections: self.collections.clone(), + } + } + } + #[cfg(feature = "explorer")] + impl<B: Backend> kv_typed::explorer::DbExplorable for [<$db_name Db>]<B> { + fn explore<'a>( + &self, + collection_name: &str, + action: kv_typed::explorer::ExplorerAction<'a>, + stringify_json_value: fn(serde_json::Value) -> serde_json::Value, + ) -> KvResult<std::result::Result<kv_typed::explorer::ExplorerActionResponse, StringErr>> { + $( if stringify!([<$col_name:snake>]) == collection_name { + return action.exec(&self.collections.[<$col_name:snake>], stringify_json_value); + } )* + Ok(Err(StringErr(format!("collection '{}' not exist in database '{}'.", collection_name, stringify!([<$db_name Db>]))))) + } + fn list_collections() -> Vec<(&'static str, &'static str, &'static str)> { + vec![ + $((stringify!([<$col_name:snake>]), stringify!($K), stringify!($V)),)* + ] + } + } + // Batch + pub struct [<$db_name DbBatch>]<B: Backend> { + $([<$col_name:snake>]: Batch<B::Col, ColRw<B::Col, [<$col_name Event>]>>,)* + } + impl<B: Backend> Default for [<$db_name DbBatch>]<B> { + fn default() -> Self { + [<$db_name DbBatch>] { + $([<$col_name:snake>]: Batch::default(),)* + } + } + } + impl<B: Backend> [<$db_name DbBatch>]<B> { + $(pub fn [<$col_name:snake>](&mut self) -> &mut Batch<B::Col, ColRw<B::Col, [<$col_name Event>]>> { &mut self.[<$col_name:snake>] })* + } + // DbRo + #[derive(Debug)] + pub struct [<$db_name DbRo>]<B: Backend> { + collections: [<$db_name ColsRo>]<B::Col>, + } + impl<B: Backend> [<$db_name DbRo>]<B> { + pub const NAME: &'static str = stringify!([<$db_name:snake>]); + } + impl<B: Backend> Clone for [<$db_name DbRo>]<B> { + fn clone(&self) -> Self { + [<$db_name DbRo>] { + collections: self.collections.clone(), + } + } + } + // Read operations + pub trait [<$db_name DbReadable>]: Sized { + type Backend: Backend; + + $(fn [<$col_name:snake>](&self) -> &ColRo<<Self::Backend as Backend>::Col, [<$col_name Event>]>;)* + } + impl<B: Backend> [<$db_name DbReadable>] for [<$db_name Db>]<B> { + type Backend = B; + + $(fn [<$col_name:snake>](&self) -> &ColRo<B::Col, [<$col_name Event>]> { &self.collections.[<$col_name:snake>].to_ro() })* + } + impl<B: Backend> [<$db_name DbReadable>] for [<$db_name DbRo>]<B>{ + type Backend = B; + + $(fn [<$col_name:snake>](&self) -> &ColRo<B::Col, [<$col_name Event>]> { &self.collections.[<$col_name:snake>] })* + } + // Write operations + pub trait [<$db_name DbWritable>]: [<$db_name DbReadable>] { + type Backend: Backend; + type Batch; + $(type [<$col_name ColRw>]: DbCollectionRw;)* + type DbRo: Sized; + + fn get_ro_handler(&self) -> Self::DbRo; + fn open( + backend_conf: <<Self as [<$db_name DbWritable>]>::Backend as kv_typed::backend::Backend>::Conf, + ) -> KvResult <Self>; + fn new_batch(&self) -> Self::Batch; + fn save(&self) -> KvResult<()>; + fn write_batch(&self, batch: Self::Batch) -> KvResult<()>; + $(fn [<$col_name:snake _write>](&self) -> &Self::[<$col_name ColRw>];)* + } + impl<B: Backend> [<$db_name DbWritable>] for [<$db_name Db>]<B> { + type Backend = B; + type Batch = [<$db_name DbBatch>]<B>; + $(type [<$col_name ColRw>] = ColRw<B::Col, [<$col_name Event>]>;)* + type DbRo = [<$db_name DbRo>]<B>; + + #[inline(always)] + fn get_ro_handler(&self) -> Self::DbRo { + [<$db_name DbRo>] { + collections: self.collections.to_ro(), + } + } + #[inline(always)] + fn new_batch(&self) -> Self::Batch { + <[<$db_name DbBatch>]::<B>>::default() + } + fn write_batch(&self, batch: Self::Batch) -> KvResult<()> { + $(self.collections.[<$col_name:snake>].write_batch(batch.[<$col_name:snake>])?;)* + Ok(()) + } + fn open( + backend_conf: <<Self as [<$db_name DbWritable>]>::Backend as kv_typed::backend::Backend>::Conf, + ) -> KvResult <Self> { + let mut db = B::open(&backend_conf)?; + Ok([<$db_name Db>] { + collections: [<$db_name ColsRw>] { + $([<$col_name:snake>]: <ColRw<B::Col, [<$col_name Event>]>>::new( + db.open_col(&backend_conf, $col_path)? + ),)* + }, + }) + } + fn save(&self) -> KvResult<()> { + $(self.collections.[<$col_name:snake>].save()?;)* + Ok(()) + } + $(fn [<$col_name:snake _write>](&self) -> &ColRw<B::Col, [<$col_name Event>]> { &self.collections.[<$col_name:snake>] })* + } + } + } + }; +} diff --git a/rust-libs/tools/kv_typed/src/explorer.rs b/rust-libs/tools/kv_typed/src/explorer.rs index d07ff02b6..e3a4ab089 100644 --- a/rust-libs/tools/kv_typed/src/explorer.rs +++ b/rust-libs/tools/kv_typed/src/explorer.rs @@ -9,7 +9,7 @@ pub trait DbExplorable { action: ExplorerAction<'a>, stringify_json_value: fn(serde_json::Value) -> serde_json::Value, ) -> KvResult<Result<ExplorerActionResponse, StringErr>>; - fn list_collections(&self) -> Vec<(&'static str, &'static str, &'static str)>; + fn list_collections() -> Vec<(&'static str, &'static str, &'static str)>; } pub trait ExplorableKey: Sized { @@ -27,16 +27,6 @@ impl ExplorableKey for () { } } -impl ExplorableKey for EmptyKey { - fn from_explorer_str(_: &str) -> Result<Self, StringErr> { - Ok(EmptyKey) - } - - fn to_explorer_string(&self) -> KvResult<String> { - Ok(String::with_capacity(0)) - } -} - impl ExplorableKey for String { fn from_explorer_str(source: &str) -> Result<Self, StringErr> { Ok(source.to_owned()) @@ -78,16 +68,6 @@ impl ExplorableValue for () { } } -impl ExplorableValue for EmptyValue { - fn from_explorer_str(_: &str) -> Result<Self, StringErr> { - Ok(EmptyValue) - } - - fn to_explorer_json(&self) -> KvResult<serde_json::Value> { - Ok(serde_json::Value::String(String::with_capacity(0))) - } -} - impl ExplorableValue for String { fn from_explorer_str(source: &str) -> Result<Self, StringErr> { Ok(source.to_owned()) @@ -117,6 +97,107 @@ impl_explorable_value_for_numbers!( usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64 ); +impl<T, E> ExplorableValue for Vec<T> +where + T: Display + FromStr<Err = E>, + E: Display, +{ + fn from_explorer_str(source: &str) -> Result<Vec<T>, StringErr> { + if let serde_json::Value::Array(json_array) = + serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))? + { + let mut vec = Vec::with_capacity(json_array.len()); + for value in json_array { + if let serde_json::Value::String(string) = value { + vec.push(<T>::from_str(&string).map_err(|e| StringErr(format!("{}", e)))?); + } else { + return Err(StringErr(format!("Expected array of {}.", stringify!(T)))); + } + } + Ok(vec) + } else { + Err(StringErr(format!("Expected array of {}.", stringify!(T)))) + } + } + + fn to_explorer_json(&self) -> KvResult<serde_json::Value> { + Ok(serde_json::Value::Array( + self.iter() + .map(|elem| serde_json::Value::String(format!("{}", elem))) + .collect(), + )) + } +} + +macro_rules! impl_explorable_value_for_smallvec { + ($($N:literal),*) => {$( + impl<T, E> ExplorableValue for SmallVec<[T; $N]> + where + T: Display + FromStr<Err = E>, + E: Display, + { + fn from_explorer_str(source: &str) -> Result<SmallVec<[T; $N]>, StringErr> { + if let serde_json::Value::Array(json_array) = + serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))? + { + let mut svec = SmallVec::with_capacity(json_array.len()); + for value in json_array { + if let serde_json::Value::String(string) = value { + svec.push(<T>::from_str(&string).map_err(|e| StringErr(format!("{}", e)))?); + } else { + return Err(StringErr(format!("Expected array of {}.", stringify!(T)))); + } + } + Ok(svec) + } else { + Err(StringErr(format!("Expected array of {}.", stringify!(T)))) + } + } + + fn to_explorer_json(&self) -> KvResult<serde_json::Value> { + Ok(serde_json::Value::Array( + self.iter() + .map(|elem| serde_json::Value::String(format!("{}", elem))) + .collect(), + )) + } + } + )*}; +} +impl_explorable_value_for_smallvec!(2, 4, 8, 16, 32, 64); + +impl<T, E> ExplorableValue for BTreeSet<T> +where + T: Display + FromStr<Err = E> + Ord, + E: Display, +{ + fn from_explorer_str(source: &str) -> Result<BTreeSet<T>, StringErr> { + if let serde_json::Value::Array(json_array) = + serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))? + { + let mut bt_set = BTreeSet::new(); + for value in json_array { + if let serde_json::Value::String(string) = value { + bt_set.insert(<T>::from_str(&string).map_err(|e| StringErr(format!("{}", e)))?); + } else { + return Err(StringErr(format!("Expected array of {}.", stringify!(T)))); + } + } + Ok(bt_set) + } else { + Err(StringErr(format!("Expected array of {}.", stringify!(T)))) + } + } + + fn to_explorer_json(&self) -> KvResult<serde_json::Value> { + Ok(serde_json::Value::Array( + self.iter() + .map(|elem| serde_json::Value::String(format!("{}", elem))) + .collect(), + )) + } +} + #[derive(Debug)] pub enum ExplorerAction<'a> { Count, diff --git a/rust-libs/tools/kv_typed/src/from_bytes.rs b/rust-libs/tools/kv_typed/src/from_bytes.rs index ab05f4a5b..a8837d27d 100644 --- a/rust-libs/tools/kv_typed/src/from_bytes.rs +++ b/rust-libs/tools/kv_typed/src/from_bytes.rs @@ -14,20 +14,6 @@ impl FromBytes for () { Ok(()) } } -impl FromBytes for EmptyKey { - type Err = std::convert::Infallible; - - fn from_bytes(_: &[u8]) -> Result<Self, Self::Err> { - Ok(EmptyKey) - } -} -impl FromBytes for EmptyValue { - type Err = std::convert::Infallible; - - fn from_bytes(_: &[u8]) -> Result<Self, Self::Err> { - Ok(EmptyValue) - } -} macro_rules! impl_from_bytes_for_numbers { ($($T:ty),*) => {$( diff --git a/rust-libs/tools/kv_typed/src/key.rs b/rust-libs/tools/kv_typed/src/key.rs index 91e764c78..7d820dfa7 100644 --- a/rust-libs/tools/kv_typed/src/key.rs +++ b/rust-libs/tools/kv_typed/src/key.rs @@ -60,6 +60,3 @@ impl<T> Key for T where + Sized { } - -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct EmptyKey; diff --git a/rust-libs/tools/kv_typed/src/lib.rs b/rust-libs/tools/kv_typed/src/lib.rs index 8d7ff66cc..87eab5520 100644 --- a/rust-libs/tools/kv_typed/src/lib.rs +++ b/rust-libs/tools/kv_typed/src/lib.rs @@ -33,6 +33,7 @@ mod bytes; mod collection_inner; mod collection_ro; mod collection_rw; +mod db_schema; mod error; mod event; #[cfg(feature = "explorer")] @@ -81,13 +82,12 @@ pub mod prelude { pub use crate::iter::{ keys::KvIterKeys, values::KvIterValues, EntryIter, KvIter, ResultIter, ReversableIterator, }; - pub use crate::key::{EmptyKey, Key}; + pub use crate::key::Key; pub use crate::subscription::{NewSubscribers, Subscriber, Subscribers}; pub use crate::transactional_read::{TransactionalRead, TxColRo}; pub use crate::transactional_write::{DbTxCollectionRw, TransactionalWrite, TxColRw}; pub use crate::utils::arc::Arc; - pub use crate::value::{EmptyValue, Value, ValueSliceZc, ValueZc}; - pub use kv_typed_code_gen::db_schema; + pub use crate::value::{Value, ValueSliceZc, ValueZc}; } // Internal crate imports @@ -109,273 +109,10 @@ pub(crate) use std::{ collections::{BTreeSet, HashSet}, convert::TryInto, error::Error, - fmt::Debug, + fmt::{Debug, Display}, iter::FromIterator, marker::PhantomData, ops::{Bound, RangeBounds}, + str::FromStr, }; pub(crate) use thiserror::Error; - -#[macro_export] -/// $Elem must implement Display + FromStr + zerocopy::AsBytes + zerocopy::FromBytes -macro_rules! impl_value_for_vec_zc { - ($T:ty, $Elem:ty) => { - impl ValueAsBytes for $T { - fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, f: F) -> KvResult<T> { - self.0.as_bytes(f) - } - } - impl FromBytes for $T { - type Err = StringErr; - - fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> { - Ok(Self(Vec::<$Elem>::from_bytes(bytes)?)) - } - } - impl ValueSliceZc for $T { - type Elem = $Elem; - - fn prefix_len() -> usize { - 0 - } - } - #[cfg(feature = "explorer")] - use std::str::FromStr as _; - #[cfg(feature = "explorer")] - impl ExplorableValue for $T { - fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> { - if let serde_json::Value::Array(json_array) = - serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))? - { - let mut vec = Vec::with_capacity(json_array.len()); - for value in json_array { - if let serde_json::Value::String(string) = value { - vec.push( - <$Elem>::from_str(&string) - .map_err(|e| StringErr(format!("{}", e)))?, - ); - } else { - return Err(StringErr(format!( - "Expected array of {}.", - stringify!($Elem) - ))); - } - } - Ok(Self(vec)) - } else { - Err(StringErr(format!( - "Expected array of {}.", - stringify!($Elem) - ))) - } - } - - fn to_explorer_json(&self) -> KvResult<serde_json::Value> { - Ok(serde_json::Value::Array( - self.0 - .iter() - .map(|elem| serde_json::Value::String(format!("{}", elem))) - .collect(), - )) - } - } - }; -} - -#[macro_export] -/// $Elem must implement Display + FromStr + zerocopy::AsBytes + zerocopy::FromBytes -macro_rules! impl_value_for_smallvec_zc { - ($T:ty, $Elem:ty, $N:literal) => { - impl ValueAsBytes for $T { - fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, f: F) -> KvResult<T> { - self.0.as_bytes(f) - } - } - impl FromBytes for $T { - type Err = StringErr; - - fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> { - Ok(Self(SmallVec::<[$Elem; $N]>::from_bytes(bytes)?)) - } - } - impl ValueSliceZc for $T { - type Elem = $Elem; - - fn prefix_len() -> usize { - 0 - } - } - #[cfg(feature = "explorer")] - use std::str::FromStr as _; - #[cfg(feature = "explorer")] - impl ExplorableValue for $T { - fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> { - if let serde_json::Value::Array(json_array) = - serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))? - { - let mut svec = SmallVec::with_capacity(json_array.len()); - for value in json_array { - if let serde_json::Value::String(string) = value { - svec.push( - <$Elem>::from_str(&string) - .map_err(|e| StringErr(format!("{}", e)))?, - ); - } else { - return Err(StringErr(format!( - "Expected array of {}.", - stringify!($Elem) - ))); - } - } - Ok(Self(svec)) - } else { - Err(StringErr(format!( - "Expected array of {}.", - stringify!($Elem) - ))) - } - } - - fn to_explorer_json(&self) -> KvResult<serde_json::Value> { - Ok(serde_json::Value::Array( - self.0 - .iter() - .map(|elem| serde_json::Value::String(format!("{}", elem))) - .collect(), - )) - } - } - }; -} - -#[macro_export] -/// $Elem must implement Display + FromStr + Ord + zerocopy::AsBytes + zerocopy::FromBytes -macro_rules! impl_value_for_btreeset_zc { - ($T:ty, $Elem:ty) => { - impl ValueAsBytes for $T { - fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, f: F) -> KvResult<T> { - self.0.as_bytes(f) - } - } - impl FromBytes for $T { - type Err = StringErr; - - fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> { - Ok(Self(BTreeSet::<$Elem>::from_bytes(bytes)?)) - } - } - impl ValueSliceZc for $T { - type Elem = $Elem; - - fn prefix_len() -> usize { - 0 - } - } - #[cfg(feature = "explorer")] - use std::str::FromStr as _; - #[cfg(feature = "explorer")] - impl ExplorableValue for $T { - fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> { - if let serde_json::Value::Array(json_array) = - serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))? - { - let mut col = BTreeSet::new(); - for value in json_array { - if let serde_json::Value::String(string) = value { - col.insert( - <$Elem>::from_str(&string) - .map_err(|e| StringErr(format!("{}", e)))?, - ); - } else { - return Err(StringErr(format!( - "Expected array of {}.", - stringify!($Elem) - ))); - } - } - Ok(Self(col)) - } else { - Err(StringErr(format!( - "Expected array of {}.", - stringify!($Elem) - ))) - } - } - - fn to_explorer_json(&self) -> KvResult<serde_json::Value> { - Ok(serde_json::Value::Array( - self.0 - .iter() - .map(|elem| serde_json::Value::String(format!("{}", elem))) - .collect(), - )) - } - } - }; -} - -#[macro_export] -/// $Elem must implement Display + Eq + FromStr + std::hash::Hash + zerocopy::AsBytes + zerocopy::FromBytes -macro_rules! impl_value_for_hashset_zc { - ($T:ty, $Elem:ty) => { - impl ValueAsBytes for $T { - fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, f: F) -> KvResult<T> { - self.0.as_bytes(f) - } - } - impl FromBytes for $T { - type Err = StringErr; - - fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> { - Ok(Self(HashSet::<$Elem>::from_bytes(bytes)?)) - } - } - impl ValueSliceZc for $T { - type Elem = $Elem; - - fn prefix_len() -> usize { - 0 - } - } - #[cfg(feature = "explorer")] - use std::str::FromStr as _; - #[cfg(feature = "explorer")] - impl ExplorableValue for $T { - fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> { - if let serde_json::Value::Array(json_array) = - serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))? - { - let mut col = HashSet::new(); - for value in json_array { - if let serde_json::Value::String(string) = value { - col.insert( - <$Elem>::from_str(&string) - .map_err(|e| StringErr(format!("{}", e)))?, - ); - } else { - return Err(StringErr(format!( - "Expected array of {}.", - stringify!($Elem) - ))); - } - } - Ok(Self(col)) - } else { - Err(StringErr(format!( - "Expected array of {}.", - stringify!($Elem) - ))) - } - } - - fn to_explorer_json(&self) -> KvResult<serde_json::Value> { - Ok(serde_json::Value::Array( - self.0 - .iter() - .map(|elem| serde_json::Value::String(format!("{}", elem))) - .collect(), - )) - } - } - }; -} diff --git a/rust-libs/tools/kv_typed/src/value.rs b/rust-libs/tools/kv_typed/src/value.rs index c2d383e67..92f0afd87 100644 --- a/rust-libs/tools/kv_typed/src/value.rs +++ b/rust-libs/tools/kv_typed/src/value.rs @@ -82,5 +82,77 @@ impl ValueSliceZc for String { } } -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct EmptyValue; +impl<T, E> ValueSliceZc for Vec<T> +where + T: 'static + + Copy + + Debug + + Default + + Display + + FromStr<Err = E> + + PartialEq + + Send + + Sized + + Sync + + zerocopy::AsBytes + + zerocopy::FromBytes, + E: Display, +{ + type Elem = T; + + fn prefix_len() -> usize { + 0 + } +} + +macro_rules! impl_value_slice_zc_for_smallvec { + ($($N:literal),*) => {$( + impl<T, E> ValueSliceZc for SmallVec<[T; $N]> + where + T: 'static + + Copy + + Debug + + Default + + Display + + FromStr<Err = E> + + PartialEq + + Send + + Sized + + Sync + + zerocopy::AsBytes + + zerocopy::FromBytes, + E: Display, + { + type Elem = T; + + fn prefix_len() -> usize { + 0 + } + } + )*}; +} +impl_value_slice_zc_for_smallvec!(2, 4, 8, 16, 32, 64); + +impl<T, E> ValueSliceZc for BTreeSet<T> +where + T: 'static + + Copy + + Debug + + Default + + Display + + FromStr<Err = E> + + Ord + + PartialEq + + Send + + Sized + + Sync + + zerocopy::AsBytes + + zerocopy::FromBytes, + E: Display, +{ + type Elem = T; + + fn prefix_len() -> usize { + 0 + } +} diff --git a/rust-libs/tools/kv_typed/tests/db_schema.rs b/rust-libs/tools/kv_typed/tests/db_schema.rs deleted file mode 100644 index a4b6c1352..000000000 --- a/rust-libs/tools/kv_typed/tests/db_schema.rs +++ /dev/null @@ -1,216 +0,0 @@ -mod tests { - use kv_typed::prelude::*; - use smallvec::SmallVec; - use std::fmt::Debug; - - #[derive(Clone, Debug, PartialEq)] - pub struct VecU128(Vec<u128>); - kv_typed::impl_value_for_vec_zc!(VecU128, u128); - - #[derive(Clone, Debug, PartialEq)] - pub struct SVecU128(SmallVec<[u128; 4]>); - kv_typed::impl_value_for_smallvec_zc!(SVecU128, u128, 4); - - use std::collections::BTreeSet; - #[derive(Clone, Debug, PartialEq)] - pub struct BTSetU128(BTreeSet<u128>); - kv_typed::impl_value_for_btreeset_zc!(BTSetU128, u128); - - use std::collections::HashSet; - #[derive(Clone, Debug, PartialEq)] - pub struct HashSetU128(HashSet<u128>); - kv_typed::impl_value_for_hashset_zc!(HashSetU128, u128); - - db_schema!( - TestV1, - [ - ["c1", col_1, i32, String,], - ["c2", col_2, usize, EmptyValue,], - ["c3", col_3, u64, VecU128], - ["c4", col_4, u64, BTSetU128], - ] - ); - - #[cfg(feature = "lmdb_backend")] - #[test] - fn test_db_schema_lmdb() -> KvResult<()> { - let tmp_dir = unwrap::unwrap!(tempdir::TempDir::new("kv_typed_lmdb")); - let db = TestV1Db::<kv_typed::backend::lmdb::Lmdb>::open( - kv_typed::backend::lmdb::LmdbConf::default().folder_path(tmp_dir.path().to_owned()), - )?; - - test_db_schema(&db) - } - - #[test] - fn test_db_schema_mem() -> KvResult<()> { - let db = TestV1Db::<kv_typed::backend::memory::Mem>::open( - kv_typed::backend::memory::MemConf::default(), - )?; - - test_db_schema(&db) - } - - //#[cfg(feature = "sled_backend")] - #[test] - fn test_db_schema_sled() -> KvResult<()> { - let db = TestV1Db::<Sled>::open(SledConf::default().temporary(true))?; - - test_db_schema(&db) - } - - fn test_db_schema<B: Backend>(db: &TestV1Db<B>) -> KvResult<()> { - let (sender, recv) = kv_typed::channel::unbounded(); - db.col_1().subscribe(sender)?; - - let db2 = db.clone(); - - let handler = std::thread::spawn(move || db2.col_1_write().upsert(3, "toto".to_owned())); - handler.join().expect("thread panic")?; - - let expected_events: Events<Col1Event> = smallvec::smallvec![Col1Event::Upsert { - key: 3, - value: "toto".to_owned(), - }]; - if let Ok(msg) = recv.recv() { - assert_eq!(msg.as_ref(), &expected_events,) - } else { - panic!("must be receive event") - } - - assert_eq!(db.col_1().get(&3)?, Some("toto".to_owned()),); - let d = db.col_1().get_ref_slice(&3, |bytes| { - let str_ = unsafe { core::str::from_utf8_unchecked(bytes) }; - assert_eq!("toto", str_); - assert_eq!(db.col_2().get(&3)?, None,); - Ok(str_.to_owned()) - })?; - assert_eq!(d, Some("toto".to_owned())); - - assert_eq!(db.col_2().get(&3)?, None,); - db.col_2_write().upsert(3, EmptyValue)?; - assert_eq!(db.col_2().get(&3)?, Some(EmptyValue),); - - db.col_1_write().upsert(5, "tutu".to_owned())?; - - db.col_1().iter(.., |mut iter| { - assert_eq!(iter.next_res()?, Some((3, "toto".to_owned()))); - assert_eq!(iter.next_res()?, Some((5, "tutu".to_owned()))); - assert_eq!(iter.next_res()?, None); - Ok::<(), KvError>(()) - })?; - - db.col_1().iter(.., |it| { - let mut iter = it.values().reverse(); - - assert_eq!(iter.next_res()?, Some("tutu".to_owned())); - assert_eq!(iter.next_res()?, Some("toto".to_owned())); - assert_eq!(iter.next_res()?, None); - Ok::<(), KvError>(()) - })?; - - db.col_1_write().upsert(7, "titi".to_owned())?; - - db.col_1().iter(.., |it| { - let mut iter = it.values().reverse().step_by(2); - - assert_eq!(iter.next_res()?, Some("titi".to_owned())); - assert_eq!(iter.next_res()?, Some("toto".to_owned())); - assert_eq!(iter.next_res()?, None); - - Ok::<(), KvError>(()) - })?; - - db.col_3_write().upsert(4, VecU128(vec![1, 2, 3]))?; - db.col_3().get_ref_slice(&4, |numbers| { - assert_eq!(numbers, &[1, 2, 3]); - Ok(()) - })?; - - // Test get_ref_slice - use std::iter::FromIterator as _; - db.col_4_write().upsert( - 4, - BTSetU128(BTreeSet::from_iter((&[3, 2, 4, 1]).iter().copied())), - )?; - db.col_4().get_ref_slice(&4, |numbers| { - assert_eq!(numbers, &[1, 2, 3, 4]); - Ok(()) - })?; - - // Test transactional - // A read tx should be opened when write tx not commited - let (s1, r1) = flume::bounded::<()>(0); - let (s2, r2) = flume::bounded::<()>(0); - let db_ro = db.get_ro_handler(); - let read_task = std::thread::spawn(move || { - r1.recv().expect("disconnected"); - (db_ro.col_3(), db_ro.col_4(), db_ro.col_2()).read(|(c3, c4, _c2)| { - c3.get_ref_slice(&4, |numbers| { - assert_eq!(numbers, &[1, 2, 3]); - Ok(()) - })?; - c3.iter(.., |it| { - let iter = it.keys(); - s2.send(()).expect("disconnected"); - assert_eq!(iter.collect::<KvResult<Vec<_>>>()?, vec![4]); - Ok::<(), KvError>(()) - })?; - c4.get_ref_slice(&4, |numbers| { - assert_eq!(numbers, &[1, 2, 3, 4]); - Ok(()) - })?; - Ok(()) - }) - }); - - let tres: KvResult<()> = (db.col_3_write(), db.col_4_write(), db.col_2_write()).write( - |(mut c3, mut c4, _c2)| { - s1.send(()).expect("disconnected"); - assert_eq!( - c3.iter(.., |it| it.keys().collect::<KvResult<Vec<_>>>())?, - vec![4] - ); - assert_eq!( - c3.iter(.., |it| it.values().collect::<KvResult<Vec<_>>>())?, - vec![VecU128(vec![1, 2, 3])] - ); - c3.upsert(42, VecU128(vec![5, 4, 6])); - assert_eq!( - c3.iter(.., |it| it.keys().collect::<KvResult<Vec<_>>>())?, - vec![4, 42] - ); - assert_eq!( - c3.iter(.., |it| it.reverse().keys().collect::<KvResult<Vec<_>>>())?, - vec![42, 4] - ); - c3.upsert(8, VecU128(vec![11, 12, 13])); - c3.remove(4); - assert_eq!( - c3.iter(.., |it| it.keys().collect::<KvResult<Vec<_>>>())?, - vec![8, 42] - ); - c3.iter(.., |it| { - let iter = it.reverse().keys(); - r2.recv().expect("disconnected"); - assert_eq!(iter.collect::<KvResult<Vec<_>>>()?, vec![42, 8]); - - Ok::<(), KvError>(()) - })?; - c4.upsert( - 4, - BTSetU128(BTreeSet::from_iter((&[7, 8, 6, 5]).iter().copied())), - ); - Ok(()) - }, - ); - tres?; - read_task.join().expect("read task panic")?; - - // Test clear() - db.col_4_write().clear()?; - assert_eq!(db.col_4().count()?, 0); - - Ok(()) - } -} diff --git a/rust-libs/tools/kv_typed/tests/test_db_schema.rs b/rust-libs/tools/kv_typed/tests/test_db_schema.rs new file mode 100644 index 000000000..75ef7c698 --- /dev/null +++ b/rust-libs/tools/kv_typed/tests/test_db_schema.rs @@ -0,0 +1,199 @@ +use kv_typed::backend::memory::Mem; +use kv_typed::db_schema; +use kv_typed::prelude::*; +use std::collections::BTreeSet; + +db_schema!( + TestV1, + [ + ["c1", Col1, i32, String], + ["c2", Col2, usize, ()], + ["c3", Col3, u64, Vec<u128>], + ["c4", Col4, u64, BTreeSet<u128>], + ] +); + +#[test] +fn test_macro_db() { + assert_eq!(Col1Event::RemoveAll, Col1Event::RemoveAll); + + #[cfg(feature = "explorer")] + { + use kv_typed::explorer::DbExplorable as _; + assert_eq!( + TestV1Db::<Mem>::list_collections(), + vec![ + ("col1", "i32", "String"), + ("col2", "usize", "()"), + ("col3", "u64", "Vec<u128>"), + ("col4", "u64", "BTreeSet<u128>") + ] + ); + } +} + +#[test] +fn test_db_mem() -> KvResult<()> { + let db = TestV1Db::<kv_typed::backend::memory::Mem>::open( + kv_typed::backend::memory::MemConf::default(), + )?; + + test_db(&db) +} + +//#[cfg(feature = "sled_backend")] +#[test] +fn test_db_sled() -> KvResult<()> { + let db = TestV1Db::<Sled>::open(SledConf::default().temporary(true))?; + + test_db(&db) +} + +fn test_db<B: Backend>(db: &TestV1Db<B>) -> KvResult<()> { + let (sender, recv) = kv_typed::channel::unbounded(); + db.col1().subscribe(sender)?; + + let db2 = db.clone(); + + let handler = std::thread::spawn(move || db2.col1_write().upsert(3, "toto".to_owned())); + handler.join().expect("thread panic")?; + + let expected_events: Events<Col1Event> = smallvec::smallvec![Col1Event::Upsert { + key: 3, + value: "toto".to_owned(), + }]; + if let Ok(msg) = recv.recv() { + assert_eq!(msg.as_ref(), &expected_events,) + } else { + panic!("must be receive event") + } + + assert_eq!(db.col1().get(&3)?, Some("toto".to_owned()),); + let d = db.col1().get_ref_slice(&3, |bytes| { + let str_ = unsafe { core::str::from_utf8_unchecked(bytes) }; + assert_eq!("toto", str_); + assert_eq!(db.col2().get(&3)?, None,); + Ok(str_.to_owned()) + })?; + assert_eq!(d, Some("toto".to_owned())); + + assert_eq!(db.col2().get(&3)?, None,); + db.col2_write().upsert(3, ())?; + assert_eq!(db.col2().get(&3)?, Some(()),); + + db.col1_write().upsert(5, "tutu".to_owned())?; + + db.col1().iter(.., |mut iter| { + assert_eq!(iter.next_res()?, Some((3, "toto".to_owned()))); + assert_eq!(iter.next_res()?, Some((5, "tutu".to_owned()))); + assert_eq!(iter.next_res()?, None); + Ok::<(), KvError>(()) + })?; + + db.col1().iter(.., |it| { + let mut iter = it.values().reverse(); + + assert_eq!(iter.next_res()?, Some("tutu".to_owned())); + assert_eq!(iter.next_res()?, Some("toto".to_owned())); + assert_eq!(iter.next_res()?, None); + Ok::<(), KvError>(()) + })?; + + db.col1_write().upsert(7, "titi".to_owned())?; + + db.col1().iter(.., |it| { + let mut iter = it.values().reverse().step_by(2); + + assert_eq!(iter.next_res()?, Some("titi".to_owned())); + assert_eq!(iter.next_res()?, Some("toto".to_owned())); + assert_eq!(iter.next_res()?, None); + + Ok::<(), KvError>(()) + })?; + + db.col3_write().upsert(4, vec![1, 2, 3])?; + db.col3().get_ref_slice(&4, |numbers| { + assert_eq!(numbers, &[1, 2, 3]); + Ok(()) + })?; + + // Test get_ref_slice + use std::iter::FromIterator as _; + db.col4_write() + .upsert(4, BTreeSet::from_iter((&[3, 2, 4, 1]).iter().copied()))?; + db.col4().get_ref_slice(&4, |numbers| { + assert_eq!(numbers, &[1, 2, 3, 4]); + Ok(()) + })?; + + // Test transactional + // A read tx should be opened when write tx not commited + let (s1, r1) = flume::bounded::<()>(0); + let (s2, r2) = flume::bounded::<()>(0); + let db_ro = db.get_ro_handler(); + let read_task = std::thread::spawn(move || { + r1.recv().expect("disconnected"); + (db_ro.col3(), db_ro.col4(), db_ro.col2()).read(|(c3, c4, _c2)| { + c3.get_ref_slice(&4, |numbers| { + assert_eq!(numbers, &[1, 2, 3]); + Ok(()) + })?; + c3.iter(.., |it| { + let iter = it.keys(); + s2.send(()).expect("disconnected"); + assert_eq!(iter.collect::<KvResult<Vec<_>>>()?, vec![4]); + Ok::<(), KvError>(()) + })?; + c4.get_ref_slice(&4, |numbers| { + assert_eq!(numbers, &[1, 2, 3, 4]); + Ok(()) + })?; + Ok(()) + }) + }); + + let tres: KvResult<()> = + (db.col3_write(), db.col4_write(), db.col2_write()).write(|(mut c3, mut c4, _c2)| { + s1.send(()).expect("disconnected"); + assert_eq!( + c3.iter(.., |it| it.keys().collect::<KvResult<Vec<_>>>())?, + vec![4] + ); + assert_eq!( + c3.iter(.., |it| it.values().collect::<KvResult<Vec<_>>>())?, + vec![vec![1, 2, 3]] + ); + c3.upsert(42, vec![5, 4, 6]); + assert_eq!( + c3.iter(.., |it| it.keys().collect::<KvResult<Vec<_>>>())?, + vec![4, 42] + ); + assert_eq!( + c3.iter(.., |it| it.reverse().keys().collect::<KvResult<Vec<_>>>())?, + vec![42, 4] + ); + c3.upsert(8, vec![11, 12, 13]); + c3.remove(4); + assert_eq!( + c3.iter(.., |it| it.keys().collect::<KvResult<Vec<_>>>())?, + vec![8, 42] + ); + c3.iter(.., |it| { + let iter = it.reverse().keys(); + r2.recv().expect("disconnected"); + assert_eq!(iter.collect::<KvResult<Vec<_>>>()?, vec![42, 8]); + + Ok::<(), KvError>(()) + })?; + c4.upsert(4, BTreeSet::from_iter((&[7, 8, 6, 5]).iter().copied())); + Ok(()) + }); + tres?; + read_task.join().expect("read task panic")?; + + // Test clear() + db.col4_write().clear()?; + assert_eq!(db.col4().count()?, 0); + + Ok(()) +} diff --git a/rust-libs/tools/kv_typed_code_gen/Cargo.toml b/rust-libs/tools/kv_typed_code_gen/Cargo.toml deleted file mode 100644 index 6f09757d6..000000000 --- a/rust-libs/tools/kv_typed_code_gen/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "kv_typed_code_gen" -version = "0.1.0" -authors = ["elois <c@elo.tf>"] -description = "Macros to generate code for kv_typed users" -repository = "https://git.duniter.org/nodes/typescript/duniter" -keywords = ["database", "macro", "key", "sled"] -license = "AGPL-3.0" -edition = "2018" - -[lib] -proc-macro = true -path = "src/lib.rs" - -[dependencies] -Inflector = "0.11.4" -proc-macro2 = "1.0.20" -quote = "1.0" -syn = { version = "1.0.39", features = ["full"] } diff --git a/rust-libs/tools/kv_typed_code_gen/src/col_schema.rs b/rust-libs/tools/kv_typed_code_gen/src/col_schema.rs deleted file mode 100644 index 085d0839a..000000000 --- a/rust-libs/tools/kv_typed_code_gen/src/col_schema.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) 2020 Éloïs SANCHEZ. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -use crate::*; - -pub(crate) struct ColSchema { - pub(crate) name_class: Ident, - pub(crate) name_snake: Ident, - pub(crate) path: LitStr, - pub(crate) key_ty: Ident, - pub(crate) value_ty: Ident, -} - -impl From<ExprArray> for ColSchema { - fn from(expr_array: ExprArray) -> Self { - let mut expr_iter = expr_array.elems.into_iter(); - - let col_path = if let Some(Expr::Lit(ExprLit { lit, .. })) = expr_iter.next() { - if let Lit::Str(lit_str) = lit { - lit_str - } else { - panic!("Collection must be defined by array of one literal followed by three identifiers"); - } - } else { - panic!( - "Collection must be defined by array of one literal followed by three identifiers" - ); - }; - let col_name = if let Some(Expr::Path(ExprPath { path, .. })) = expr_iter.next() { - path.get_ident() - .expect("Collection name must be a plain identifier") - .to_owned() - } else { - panic!( - "Collection must be defined by array of one literal followed by three identifiers" - ); - }; - let key_ty = if let Some(Expr::Path(ExprPath { path, .. })) = expr_iter.next() { - path.get_ident() - .expect("Collection key type must be a plain identifier") - .to_owned() - } else { - panic!( - "Collection must be defined by array of one literal followed by three identifiers" - ); - }; - let value_ty = if let Some(Expr::Path(ExprPath { path, .. })) = expr_iter.next() { - path.get_ident() - .expect("Collection value type must be a plain identifier") - .to_owned() - } else { - panic!( - "Collection must be defined by array of one literal followed by three identifiers" - ); - }; - if expr_iter.next().is_some() { - panic!( - "Collection must be defined by array of one literal followed by three identifiers" - ); - } - ColSchema { - path: col_path, - name_class: format_ident!("{}", ident_to_class_case_string(&col_name)), - name_snake: format_ident!("{}", ident_to_snake_case_string(&col_name)), - key_ty, - value_ty, - } - } -} diff --git a/rust-libs/tools/kv_typed_code_gen/src/db_readable.rs b/rust-libs/tools/kv_typed_code_gen/src/db_readable.rs deleted file mode 100644 index 0c5977d8e..000000000 --- a/rust-libs/tools/kv_typed_code_gen/src/db_readable.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2020 Éloïs SANCHEZ. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -use crate::*; - -#[allow(clippy::too_many_arguments)] -pub(crate) fn impl_db_readable( - db: &Ident, - db_ro: &Ident, - db_readable: &Ident, - col_field: &[Ident], - col_event_type: &[Ident], -) -> proc_macro2::TokenStream { - quote! { - pub trait #db_readable: Sized { - type Backend: Backend; - - #(fn #col_field(&self) -> &ColRo<<Self::Backend as Backend>::Col, #col_event_type>;)* - } - impl<B: Backend> #db_readable for #db<B> { - type Backend = B; - - #(fn #col_field(&self) -> &ColRo<B::Col, #col_event_type> { &self.collections.#col_field.to_ro() })* - } - impl<B: Backend> #db_readable for #db_ro<B>{ - type Backend = B; - - #(fn #col_field(&self) -> &ColRo<B::Col, #col_event_type> { &self.collections.#col_field })* - } - } -} diff --git a/rust-libs/tools/kv_typed_code_gen/src/db_schema.rs b/rust-libs/tools/kv_typed_code_gen/src/db_schema.rs deleted file mode 100644 index c60ee621f..000000000 --- a/rust-libs/tools/kv_typed_code_gen/src/db_schema.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2020 Éloïs SANCHEZ. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -use crate::*; - -/// Parses the following syntax -/// -/// DbVersion, [collection_name, ColKeyType, ColValueType] -/// -pub(crate) struct DbSchema { - pub(crate) version: Ident, - pub(crate) collections: VecExprArray, -} - -pub(crate) struct VecExprArray(pub(crate) Vec<ExprArray>); - -impl Parse for VecExprArray { - fn parse(input: ParseStream) -> Result<Self> { - let tmp = Punctuated::<ExprArray, Token![,]>::parse_terminated(input)?; - Ok(VecExprArray(tmp.into_iter().collect())) - } -} - -impl Parse for DbSchema { - fn parse(input: ParseStream) -> Result<Self> { - let version: Ident = input.parse()?; - input.parse::<Token![,]>()?; - let collections_group: Group = input.parse()?; - let collections: VecExprArray = syn::parse(collections_group.stream().into())?; - - Ok(DbSchema { - version, - collections, - }) - } -} diff --git a/rust-libs/tools/kv_typed_code_gen/src/db_writable.rs b/rust-libs/tools/kv_typed_code_gen/src/db_writable.rs deleted file mode 100644 index ae66a3129..000000000 --- a/rust-libs/tools/kv_typed_code_gen/src/db_writable.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (C) 2020 Éloïs SANCHEZ. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -use crate::*; - -#[allow(clippy::too_many_arguments)] -pub(crate) fn impl_db_writable( - db: &Ident, - db_ro: &Ident, - db_readable: &Ident, - db_writable: &Ident, - col_name_class: &[Ident], - col_field: &[Ident], - col_path: &[LitStr], - col_event_type: &[Ident], -) -> proc_macro2::TokenStream { - let db_batch = format_ident!("{}Batch", db); - let db_cols_rw = format_ident!("{}ColsRw", db); - let col_method_rw: Vec<Ident> = col_field - .iter() - .map(|field_name| format_ident!("{}_write", field_name)) - .collect(); - quote! { - pub trait #db_writable: #db_readable { - type Backend: Backend; - type Batch; - #(type #col_name_class: DbCollectionRw;)* - type DbRo: Sized; - - fn get_ro_handler(&self) -> Self::DbRo; - fn open( - backend_conf: <<Self as #db_writable>::Backend as kv_typed::backend::Backend>::Conf, - ) -> KvResult <Self>; - fn new_batch(&self) -> Self::Batch; - fn save(&self) -> KvResult<()>; - fn write_batch(&self, batch: Self::Batch) -> KvResult<()>; - #(fn #col_method_rw(&self) -> &Self::#col_name_class;)* - } - impl<B: Backend> #db_writable for #db<B> { - type Backend = B; - type Batch = #db_batch<B>; - #(type #col_name_class = ColRw<B::Col, #col_event_type>;)* - type DbRo = #db_ro<B>; - - #[inline(always)] - fn get_ro_handler(&self) -> Self::DbRo { - #db_ro { - collections: self.collections.to_ro(), - } - } - #[inline(always)] - fn new_batch(&self) -> Self::Batch { - <#db_batch::<B>>::default() - } - fn write_batch(&self, batch: Self::Batch) -> KvResult<()> { - #(self.collections.#col_field.write_batch(batch.#col_field)?;)* - Ok(()) - } - fn open( - backend_conf: <<Self as #db_writable>::Backend as kv_typed::backend::Backend>::Conf, - ) -> KvResult <Self> { - let mut db = B::open(&backend_conf)?; - Ok(#db { - collections: #db_cols_rw { - #(#col_field: <ColRw<B::Col, #col_event_type>>::new( - db.open_col(&backend_conf, #col_path)? - ),)* - }, - }) - } - fn save(&self) -> KvResult<()> { - #(self.collections.#col_field.save()?;)* - Ok(()) - } - #(fn #col_method_rw(&self) -> &ColRw<B::Col, #col_event_type> { &self.collections.#col_field })* - } - } -} diff --git a/rust-libs/tools/kv_typed_code_gen/src/lib.rs b/rust-libs/tools/kv_typed_code_gen/src/lib.rs deleted file mode 100644 index f4306e215..000000000 --- a/rust-libs/tools/kv_typed_code_gen/src/lib.rs +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (C) 2020 Éloïs SANCHEZ. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -//! Strongly typed key-value storage -//! -//! Macro to generate database code from their schema - -mod col_schema; -mod db_readable; -mod db_schema; -mod db_writable; - -use inflector::Inflector; -use proc_macro::TokenStream; -use proc_macro2::Group; -use quote::{format_ident, quote}; -use syn::{ - parse::{Parse, ParseStream, Result}, - parse_macro_input, - punctuated::Punctuated, - Expr, ExprArray, ExprLit, ExprPath, Ident, Lit, LitStr, Token, -}; - -use crate::col_schema::ColSchema; -use crate::db_readable::impl_db_readable; -use crate::db_schema::DbSchema; -use crate::db_writable::impl_db_writable; - -fn ident_to_class_case_string(ident: &Ident) -> String { - Inflector::to_class_case(&format!("{}", ident)) -} -fn ident_to_snake_case_string(ident: &Ident) -> String { - Inflector::to_snake_case(&format!("{}", ident)) -} - -#[allow(clippy::let_and_return)] -#[proc_macro] -pub fn db_schema(input: TokenStream) -> TokenStream { - let db_schema = parse_macro_input!(input as DbSchema); - - let version = db_schema.version; - let name = ident_to_snake_case_string(&version); - let collections: Vec<ColSchema> = db_schema - .collections - .0 - .into_iter() - .map(Into::into) - .collect(); - - let db = format_ident!("{}Db", version); - let db_batch = format_ident!("{}Batch", db); - let db_ro = format_ident!("{}Ro", db); - let db_cols_ro = format_ident!("{}ColsRo", db); - let db_cols_rw = format_ident!("{}ColsRw", db); - - let col_path: Vec<LitStr> = collections.iter().map(|col| col.path.clone()).collect(); - let col_field: Vec<Ident> = collections - .iter() - .map(|col| col.name_snake.clone()) - .collect(); - let col_event_type: Vec<Ident> = collections - .iter() - .map(|col| format_ident!("{}Event", &col.name_class)) - .collect(); - let col_key_type: Vec<Ident> = collections.iter().map(|col| col.key_ty.clone()).collect(); - let col_name_class: Vec<Ident> = collections - .iter() - .map(|col| col.name_class.clone()) - .collect(); - let col_name_class_rw: Vec<Ident> = collections - .iter() - .map(|col| format_ident!("{}Rw", &col.name_class)) - .collect(); - let col_value_type: Vec<Ident> = collections.iter().map(|col| col.value_ty.clone()).collect(); - - /*let define_each_db_collection: Vec<proc_macro2::TokenStream> = - collections.iter().map(define_db_collection).collect();*/ - let db_readable = format_ident!("{}Readable", db); - let impl_db_readable = impl_db_readable(&db, &db_ro, &db_readable, &col_field, &col_event_type); - let db_writable = format_ident!("{}Writable", db); - let impl_db_writable = impl_db_writable( - &db, - &db_ro, - &db_readable, - &db_writable, - &col_name_class_rw, - &col_field, - &col_path, - &col_event_type, - ); - - let expanded = quote! { - pub use __inner::{#db, #db_batch, #db_ro, #db_readable, #db_writable}; - #( - // Define each collection event type - #[derive(Debug, PartialEq)] - pub enum #col_event_type { - Upsert { key: #col_key_type, value: #col_value_type }, - Remove { key: #col_key_type }, - RemoveAll, - } - impl EventTrait for #col_event_type { - type K = #col_key_type; - type V = #col_value_type; - - fn clear() -> Self { Self::RemoveAll } - fn upsert(k: Self::K, v: Self::V) -> Self { Self::Upsert { key: k, value: v, } } - fn remove(k: Self::K) -> Self { Self::Remove { key: k } } - } - )* - // Mocks - #[cfg(feature = "mock")] - mockall::mock! { - pub #db_readable {} - - trait #db_readable { - type Backend = kv_typed::backend::mock::MockBackend; - #(type #col_name_class = kv_typed::prelude::MockColRo<#col_event_type>;)* - - #(fn #col_field(&self) -> &kv_typed::prelude::MockColRo<#col_event_type>;)* - } - } - // Inner module used to hide internals types that must not be exposed on public api - mod __inner { - use super::*; - // DbCollections - #[derive(Clone, Debug)] - pub struct #db_cols_ro<BC: BackendCol> { - #(#col_field: ColRo<BC, #col_event_type>,)* - } - #[derive(Clone, Debug)] - pub struct #db_cols_rw<BC: BackendCol> { - #(#col_field: ColRw<BC, #col_event_type>,)* - } - impl<BC: BackendCol> #db_cols_rw<BC> { - fn to_ro(&self) -> #db_cols_ro<BC> { - #db_cols_ro { - #(#col_field: self.#col_field.to_ro().clone(),)* - } - } - } - // Db - #[derive(Debug)] - pub struct #db<B: Backend> { - collections: #db_cols_rw<B::Col>, - } - impl<B: Backend> #db<B> { - pub const NAME: &'static str = #name; - } - impl<B: Backend> Clone for #db<B> { - fn clone(&self) -> Self { - #db { - collections: self.collections.clone(), - } - } - } - #[cfg(feature = "explorer")] - use kv_typed::prelude::StringErr; - #[cfg(feature = "explorer")] - impl<B: Backend> kv_typed::explorer::DbExplorable for #db<B> { - fn explore<'a>( - &self, - collection_name: &str, - action: kv_typed::explorer::ExplorerAction<'a>, - stringify_json_value: fn(serde_json::Value) -> serde_json::Value, - ) -> KvResult<std::result::Result<kv_typed::explorer::ExplorerActionResponse, StringErr>> { - #( if stringify!(#col_field) == collection_name { - return action.exec(&self.collections.#col_field, stringify_json_value); - } )* - Ok(Err(StringErr(format!("collection '{}' not exist in database '{}'.", collection_name, stringify!(#db))))) - } - fn list_collections(&self) -> Vec<(&'static str, &'static str, &'static str)> { - vec![ - #((stringify!(#col_field), stringify!(#col_key_type), stringify!(#col_value_type)),)* - ] - } - } - pub struct #db_batch<B: Backend> { - #(#col_field: Batch<B::Col, ColRw<B::Col, #col_event_type>>,)* - } - impl<B: Backend> Default for #db_batch<B> { - fn default() -> Self { - #db_batch { - #(#col_field: Batch::default(),)* - } - } - } - impl<B: Backend> #db_batch<B> { - #(pub fn #col_field(&mut self) -> &mut Batch<B::Col, ColRw<B::Col, #col_event_type>> { &mut self.#col_field })* - } - // DbRo - #[derive(Debug)] - pub struct #db_ro<B: Backend> { - collections: #db_cols_ro<B::Col>, - } - impl<B: Backend> #db_ro<B> { - pub const NAME: &'static str = #name; - } - impl<B: Backend> Clone for #db_ro<B> { - fn clone(&self) -> Self { - #db_ro { - collections: self.collections.clone(), - } - } - } - // Read operations - #impl_db_readable - // Write operations - #impl_db_writable - } - }; - - //let tokens = TokenStream::from(expanded); - //eprintln!("TOKENS: {:#}", tokens); - - TokenStream::from(expanded) -} -- GitLab