From ce7519225e65dbf95be4c9e49a6def83fbd320eb Mon Sep 17 00:00:00 2001 From: "Philip (a-0)" <@ph:a-0.me> Date: Sat, 10 Feb 2024 19:59:21 +0100 Subject: [PATCH] Migrated from cozodb to BonsaiDB. Relevant to #3 --- Cargo.lock | 1424 ++++++++++++++++- ubisync-lib/src/peer.rs | 8 +- ubisync-lib/src/types/app_id.rs | 2 +- ubisync-lib/src/types/element.rs | 8 +- ubisync-lib/src/types/element_id.rs | 18 +- ubisync-lib/src/types/message_id.rs | 2 +- ubisync-lib/src/types/peer_id.rs | 12 +- ubisync-lib/src/types/pot.rs | 2 +- ubisync-lib/src/types/pot_id.rs | 2 +- ubisync/Cargo.toml | 2 +- ubisync/src/api/v0/app.rs | 22 +- ubisync/src/api/v0/element.rs | 16 +- ubisync/src/comm/message_processor.rs | 20 +- ubisync/src/config.rs | 4 +- ubisync/src/lib.rs | 18 +- ubisync/src/state/api_state.rs | 144 +- ubisync/src/state/comm_state.rs | 126 +- ubisync/src/state/database/as_key.rs | 93 ++ .../src/state/database/collections/apps.rs | 277 ++++ .../state/database/collections/elements.rs | 232 +++ ubisync/src/state/database/collections/mod.rs | 21 + .../src/state/database/collections/peers.rs | 106 ++ .../database/collections/pot_memberships.rs | 173 ++ .../src/state/database/collections/pots.rs | 58 + ubisync/src/state/database/mod.rs | 27 + ubisync/src/state/mod.rs | 158 +- ubisync/src/state/queries/apps.rs | 312 ---- ubisync/src/state/queries/elements.rs | 218 --- ubisync/src/state/queries/mod.rs | 51 - ubisync/src/state/queries/peers.rs | 50 - ubisync/src/state/queries/pots.rs | 130 -- ubisync/src/state/schema.rs | 53 - ubisync/tests/api.rs | 4 +- 33 files changed, 2709 insertions(+), 1084 deletions(-) create mode 100644 ubisync/src/state/database/as_key.rs create mode 100644 ubisync/src/state/database/collections/apps.rs create mode 100644 ubisync/src/state/database/collections/elements.rs create mode 100644 ubisync/src/state/database/collections/mod.rs create mode 100644 ubisync/src/state/database/collections/peers.rs create mode 100644 ubisync/src/state/database/collections/pot_memberships.rs create mode 100644 ubisync/src/state/database/collections/pots.rs create mode 100644 ubisync/src/state/database/mod.rs delete mode 100644 ubisync/src/state/queries/apps.rs delete mode 100644 ubisync/src/state/queries/elements.rs delete mode 100644 ubisync/src/state/queries/mod.rs delete mode 100644 ubisync/src/state/queries/peers.rs delete mode 100644 ubisync/src/state/queries/pots.rs delete mode 100644 ubisync/src/state/schema.rs diff --git a/Cargo.lock b/Cargo.lock index 87cd4aa..a0332b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,33 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "actionable" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9e6839049e5ad3a410c0fcd32ee25e7eb1f0fb9333310b48314ad6686d09f5" +dependencies = [ + "actionable-macros", + "async-trait", + "serde", + "thiserror", +] + +[[package]] +name = "actionable-macros" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219df0f6a405dcf4f2b0fbb85bc2dafde06d89662d3da7b7f350f3515d65d15d" +dependencies = [ + "darling 0.13.4", + "ident_case", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "thiserror", +] + [[package]] name = "addr2line" version = "0.21.0" @@ -17,6 +44,41 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array 0.14.7", +] + +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.7.7" @@ -65,6 +127,54 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.79" @@ -80,6 +190,34 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arc-bytes" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3de7bfea323262a3d319ed4ed16a960f07395bd903ac650f5e345b32401567d5" +dependencies = [ + "serde", + "thiserror", +] + +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + [[package]] name = "arrayvec" version = "0.7.4" @@ -118,6 +256,34 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62af46d040ba9df09edc6528dae9d8e49f5f3e82f55b7d2ec31a733c38dbc49d" +[[package]] +name = "attribute-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c94f43ede6f25dab1dea046bff84d85dea61bd49aba7a9011ad66c0d449077b" +dependencies = [ + "attribute-derive-macro", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "attribute-derive-macro" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b409e2b2d2dc206d2c0ad3575a93f001ae21a1593e2d0c69b69c308e63f3b422" +dependencies = [ + "collection_literals", + "interpolator", + "manyhow", + "proc-macro-utils", + "proc-macro2", + "quote", + "quote-use", + "syn 2.0.48", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -216,12 +382,39 @@ dependencies = [ "backtrace", ] +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "basic-toml" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5" +dependencies = [ + "serde", +] + [[package]] name = "bincode" version = "1.3.3" @@ -243,6 +436,28 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.7.3" @@ -273,6 +488,109 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "bonsaidb" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8e0684e6d0a625039c24880ddd23ff5eec068b4f47a5f7150ec261812bdf7b1" +dependencies = [ + "bonsaidb-core", + "bonsaidb-local", + "derive-where", +] + +[[package]] +name = "bonsaidb-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c75825d18ec94b4530afd246b638c318934416808683d6cf6971bf9590f5e48" +dependencies = [ + "actionable", + "arc-bytes", + "async-trait", + "blake3", + "bonsaidb-macros", + "bytecount", + "circulate", + "derive-where", + "futures", + "itertools 0.11.0", + "num-traits", + "ordered-varint", + "pot", + "rand 0.8.5", + "serde", + "sha2 0.10.8", + "thiserror", + "tinyvec", + "transmog", + "transmog-pot", + "zeroize", +] + +[[package]] +name = "bonsaidb-local" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa13e68a85fef7b5c5ebcf90cd3b9d1f821a4ddb6f0e3fbe1150071b5226078a" +dependencies = [ + "argon2", + "async-trait", + "bincode", + "bonsaidb-core", + "bonsaidb-utils", + "byteorder", + "chacha20poly1305", + "clap", + "crossterm", + "derive-where", + "easy-parallel", + "flume 0.11.0", + "fs2", + "futures", + "hpke", + "itertools 0.11.0", + "lockedbox", + "log", + "lz4_flex 0.11.2", + "nebari", + "once_cell", + "p256 0.13.2", + "parking_lot", + "pot", + "rand 0.8.5", + "serde", + "sysinfo", + "thiserror", + "tokio", + "tracing", + "transmog-versions", + "watchable", + "zeroize", +] + +[[package]] +name = "bonsaidb-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c96879fa66046a8d550de9c52f93fa99feea4b24e81917f883c39b5eb11f54" +dependencies = [ + "attribute-derive", + "manyhow", + "proc-macro-crate", + "proc-macro2", + "quote", + "quote-use", + "syn 2.0.48", + "trybuild", +] + +[[package]] +name = "bonsaidb-utils" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "184e5ac9b34e6d55fb0a8437bd279650942b25d3ee95b74f3fed2bb5032d5f93" + [[package]] name = "bumpalo" version = "3.14.0" @@ -291,6 +609,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + [[package]] name = "byteorder" version = "1.5.0" @@ -337,6 +661,30 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + [[package]] name = "chrono" version = "0.4.31" @@ -374,6 +722,71 @@ dependencies = [ "phf_codegen", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "circulate" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56d670e28743ed41e3cd4388cf7fc928bcf9cedf515b63443a2e13fc4c986df" +dependencies = [ + "arc-bytes", + "flume 0.11.0", + "futures", + "parking_lot", + "pot", + "serde", +] + +[[package]] +name = "clap" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + [[package]] name = "cloudabi" version = "0.0.3" @@ -393,6 +806,39 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "collection_literals" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + [[package]] name = "core-foundation" version = "0.9.4" @@ -430,7 +876,7 @@ dependencies = [ "env_logger", "fast2s", "graph", - "itertools", + "itertools 0.12.0", "jieba-rs", "lazy_static", "log", @@ -491,6 +937,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crossbeam" version = "0.8.3" @@ -556,6 +1017,61 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.4.1", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -563,6 +1079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array 0.14.7", + "rand_core 0.6.4", "typenum", ] @@ -587,6 +1104,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "cxx" version = "1.0.114" @@ -631,14 +1157,38 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + [[package]] name = "darling" version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.3", + "darling_macro 0.20.3", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", ] [[package]] @@ -655,13 +1205,24 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn 1.0.109", +] + [[package]] name = "darling_macro" version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ - "darling_core", + "darling_core 0.20.3", "quote", "syn 2.0.48", ] @@ -696,6 +1257,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -706,6 +1287,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derive-where" +version = "1.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "digest" version = "0.8.1" @@ -722,7 +1314,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", + "subtle", ] [[package]] @@ -734,12 +1328,72 @@ dependencies = [ "litrs", ] +[[package]] +name = "easy-parallel" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2afbb9b0aef60e4f0d2b18129b6c0dff035a6f7dbbd17c2f38c1432102ee223c" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der 0.7.8", + "digest 0.10.7", + "elliptic-curve 0.13.8", + "rfc6979", + "signature", + "spki", +] + [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array 0.14.7", + "group 0.12.1", + "hkdf", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.5", + "digest 0.10.7", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1 0.7.3", + "subtle", + "zeroize", +] + [[package]] name = "encoding_rs" version = "0.8.33" @@ -778,6 +1432,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "event-listener" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "failure" version = "0.1.8" @@ -829,6 +1494,51 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -859,6 +1569,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -913,6 +1633,17 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "futures-sink" version = "0.3.30" @@ -934,6 +1665,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -966,8 +1698,10 @@ version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ + "serde", "typenum", "version_check 0.9.4", + "zeroize", ] [[package]] @@ -983,12 +1717,28 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug 0.3.0", + "polyval", +] + [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "graph" version = "0.3.1" @@ -1029,6 +1779,28 @@ dependencies = [ "thiserror", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.3.22" @@ -1067,6 +1839,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1100,6 +1882,46 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hpke" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf39e5461bfdc6ad0fbc97067519fcaf96a7a2e67f24cc0eb8a1e7c0c45af792" +dependencies = [ + "aead", + "aes-gcm", + "byteorder", + "chacha20poly1305", + "digest 0.10.7", + "generic-array 0.14.7", + "hkdf", + "hmac", + "p256 0.11.1", + "rand_core 0.6.4", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "http" version = "0.2.11" @@ -1325,6 +2147,21 @@ dependencies = [ "serde", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "interpolator" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" + [[package]] name = "ipnet" version = "2.9.0" @@ -1348,6 +2185,15 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.0" @@ -1463,12 +2309,30 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "lockedbox" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdc79cda55181ba0697546137970767eb171057835b1940ae92bd3eef20571ac" +dependencies = [ + "memsec", +] + [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + [[package]] name = "lz4-sys" version = "1.9.4" @@ -1488,6 +2352,38 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "lz4_flex" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "912b45c753ff5f7f5208307e8ace7d2a2e30d024e26d3509f3dce546c044ce15" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "manyhow" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b76546495d933baa165075b95c0a15e8f7ef75e53f56b19b7144d80fd52bd" +dependencies = [ + "manyhow-macros", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "manyhow-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ba072c0eadade3160232e70893311f1f8903974488096e2eb8e48caba2f0cf1" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", +] + [[package]] name = "matchit" version = "0.7.3" @@ -1519,6 +2415,17 @@ dependencies = [ "libc", ] +[[package]] +name = "memsec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa0916b001582d253822171bd23f4a0229d32b9507fae236f5da8cad515ba7c" +dependencies = [ + "getrandom", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "miette" version = "5.10.0" @@ -1586,6 +2493,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.48.0", ] @@ -1595,6 +2503,9 @@ name = "nanorand" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] [[package]] name = "native-tls" @@ -1628,6 +2539,25 @@ dependencies = [ "serde", ] +[[package]] +name = "nebari" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d803ae55feaf2b1e177b2d4c3315468f4ccc215c5f9a83d31edfb862803f5b5e" +dependencies = [ + "arc-bytes", + "backtrace", + "byteorder", + "crc", + "flume 0.10.14", + "lru", + "num_cpus", + "once_cell", + "parking_lot", + "thiserror", + "tracing", +] + [[package]] name = "nom" version = "4.2.3" @@ -1638,6 +2568,15 @@ dependencies = [ "version_check 0.1.5", ] +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1765,6 +2704,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "openssl" version = "0.10.62" @@ -1818,6 +2763,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-varint" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9cc9f18ab4bad1e01726bda1259feb8f11e5e76308708a966b4c0136e9db34c" + [[package]] name = "overload" version = "0.1.1" @@ -1830,6 +2781,27 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "elliptic-curve 0.12.3", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve 0.13.8", + "primeorder", + "sha2 0.10.8", +] + [[package]] name = "page_size" version = "0.4.2" @@ -1840,6 +2812,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + [[package]] name = "parking_lot" version = "0.12.1" @@ -1872,6 +2850,17 @@ dependencies = [ "regex", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "paste" version = "1.0.14" @@ -1888,6 +2877,15 @@ dependencies = [ "serde", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -2009,12 +3007,57 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.8", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash", +] + +[[package]] +name = "pot" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df842bdb3b0553a411589e64aaa1a7d0c0259f72fabcedfaa841683ae3019d80" +dependencies = [ + "byteorder", + "half", + "serde", + "tracing", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2027,6 +3070,15 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve 0.13.8", +] + [[package]] name = "priority-queue" version = "1.3.2" @@ -2037,6 +3089,51 @@ dependencies = [ "indexmap 1.9.3", ] +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check 0.9.4", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check 0.9.4", +] + +[[package]] +name = "proc-macro-utils" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f59e109e2f795a5070e69578c4dc101068139f74616778025ae1011d4cd41a8" +dependencies = [ + "proc-macro2", + "quote", + "smallvec", +] + [[package]] name = "proc-macro2" version = "1.0.75" @@ -2061,6 +3158,29 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "quote-use" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7b5abe3fe82fdeeb93f44d66a7b444dedf2e4827defb0a8e69c437b2de2ef94" +dependencies = [ + "quote", + "quote-use-macros", + "syn 2.0.48", +] + +[[package]] +name = "quote-use-macros" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ea44c7e20f16017a76a245bb42188517e13d16dcb1aa18044bc406cdc3f4af" +dependencies = [ + "derive-where", + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "rand" version = "0.5.6" @@ -2221,6 +3341,16 @@ dependencies = [ "winreg", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ring" version = "0.17.7" @@ -2367,6 +3497,33 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array 0.14.7", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.8", + "generic-array 0.14.7", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -2475,7 +3632,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" dependencies = [ - "darling", + "darling 0.20.3", "proc-macro2", "quote", "syn 2.0.48", @@ -2515,7 +3672,7 @@ dependencies = [ "block-buffer 0.7.3", "digest 0.8.1", "fake-simd", - "opaque-debug", + "opaque-debug 0.2.3", ] [[package]] @@ -2538,6 +3695,27 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -2547,6 +3725,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "simple_asn1" version = "0.6.2" @@ -2616,6 +3804,19 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.8", +] [[package]] name = "sqlite" @@ -2659,6 +3860,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "supports-color" version = "2.1.0" @@ -2694,7 +3901,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec5f895272298fe2ed7c8f15dcee10b00ce396c8caebd602275fd10f49797d02" dependencies = [ "bincode", - "lz4_flex", + "lz4_flex 0.10.0", "miniz_oxide", "serde", "tempfile", @@ -2740,6 +3947,20 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "sysinfo" +version = "0.29.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "winapi", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -2932,6 +4153,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.4.13" @@ -3018,12 +4256,55 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "transmog" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a7f05cba0120a41e81c7309f084e8b1014118ed19857d6e878c79f0fc4efac" + +[[package]] +name = "transmog-pot" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f777f5fd9b33fa0fd78c5b5ce0e23273098a4f3ff37e3a9b22733b43e71f914e" +dependencies = [ + "pot", + "serde", + "transmog", +] + +[[package]] +name = "transmog-versions" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8950fe6741bdec0c5efb79db30d0f976951653996ba7bcf81763d04ea014135c" +dependencies = [ + "ordered-varint", + "thiserror", + "transmog", +] + [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "trybuild" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9d3ba662913483d6722303f619e75ea10b7855b0f8e0d72799cf8621bb488f" +dependencies = [ + "basic-toml", + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", +] + [[package]] name = "twox-hash" version = "1.6.3" @@ -3047,9 +4328,9 @@ version = "0.1.0" dependencies = [ "anyhow", "axum", - "cozo", + "bonsaidb", "i2p", - "itertools", + "itertools 0.12.0", "jsonwebtoken", "reqwest", "serde", @@ -3074,7 +4355,7 @@ dependencies = [ "chrono", "cozo", "i2p", - "itertools", + "itertools 0.12.0", "jsonwebtoken", "reqwest", "serde", @@ -3145,6 +4426,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -3162,6 +4453,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.6.1" @@ -3278,6 +4575,18 @@ version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +[[package]] +name = "watchable" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45b42a2f611916b5965120a9cde2b60f2db4454826dd9ad5e6f47c24a5b3b259" +dependencies = [ + "event-listener", + "futures-util", + "parking_lot", + "thiserror", +] + [[package]] name = "web-sys" version = "0.3.66" @@ -3334,6 +4643,15 @@ dependencies = [ "windows-targets 0.52.0", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3352,6 +4670,21 @@ dependencies = [ "windows-targets 0.52.0", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -3382,6 +4715,12 @@ dependencies = [ "windows_x86_64_msvc 0.52.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3394,6 +4733,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3406,6 +4751,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3418,6 +4769,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3430,6 +4787,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -3442,6 +4805,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3454,6 +4823,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3466,6 +4841,15 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "winnow" +version = "0.5.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1931d78a9c73861da0134f453bb1f790ce49b2e30eba8410b4b79bac72b46a2d" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -3496,6 +4880,26 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "zstd-sys" version = "2.0.9+zstd.1.5.5" diff --git a/ubisync-lib/src/peer.rs b/ubisync-lib/src/peer.rs index a1b9711..51c3bfe 100644 --- a/ubisync-lib/src/peer.rs +++ b/ubisync-lib/src/peer.rs @@ -5,15 +5,15 @@ use serde::{Deserialize, Serialize}; use crate::types::PeerId; -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Peer { id: PeerId, - name: String, + name: Option, family: Vec, } impl Peer { - pub fn new(id: PeerId, name: String) -> Self { + pub fn new(id: PeerId, name: Option) -> Self { Peer { id: id, name: name, @@ -29,7 +29,7 @@ impl Peer { self.id.clone() } - pub fn name(&self) -> String { + pub fn name(&self) -> Option { self.name.clone() } } diff --git a/ubisync-lib/src/types/app_id.rs b/ubisync-lib/src/types/app_id.rs index 91208e5..ae5f4ea 100644 --- a/ubisync-lib/src/types/app_id.rs +++ b/ubisync-lib/src/types/app_id.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Clone, Debug, Default, Ord, PartialOrd, PartialEq, Eq, Hash)] pub struct AppId(Uuid); impl AppId { diff --git a/ubisync-lib/src/types/element.rs b/ubisync-lib/src/types/element.rs index d3c5680..4b90443 100644 --- a/ubisync-lib/src/types/element.rs +++ b/ubisync-lib/src/types/element.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use super::{ElementContent, ElementId, MessageId, PotId}; -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Element { // Uuid identifying the element itself id: ElementId, @@ -46,4 +46,10 @@ impl Element { pub fn pot(&self) -> &Option { &self.pot } + pub fn latest_message(&self) -> &Option { + &self.latest_message + } + pub fn local_changes(&self) -> bool { + self.local_changes + } } diff --git a/ubisync-lib/src/types/element_id.rs b/ubisync-lib/src/types/element_id.rs index e6c6a46..fd1eaef 100644 --- a/ubisync-lib/src/types/element_id.rs +++ b/ubisync-lib/src/types/element_id.rs @@ -1,12 +1,16 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)] pub struct ElementId(Uuid); impl ElementId { pub fn new() -> Self { ElementId(Uuid::new_v4()) } + + pub fn bytes(&self) -> &[u8; 16] { + self.0.as_bytes() + } } impl ToString for ElementId { @@ -27,3 +31,15 @@ impl TryFrom<&str> for ElementId { serde_json::from_str(value) } } + +impl From for ElementId { + fn from(value: Uuid) -> Self { + ElementId {0: value} + } +} + +impl From<[u8; 16]> for ElementId { + fn from(value: [u8; 16]) -> Self { + ElementId {0: Uuid::from_bytes(value)} + } +} \ No newline at end of file diff --git a/ubisync-lib/src/types/message_id.rs b/ubisync-lib/src/types/message_id.rs index 8d253d7..c15b984 100644 --- a/ubisync-lib/src/types/message_id.rs +++ b/ubisync-lib/src/types/message_id.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct MessageId(Uuid); impl MessageId { diff --git a/ubisync-lib/src/types/peer_id.rs b/ubisync-lib/src/types/peer_id.rs index 14f6d70..2095119 100644 --- a/ubisync-lib/src/types/peer_id.rs +++ b/ubisync-lib/src/types/peer_id.rs @@ -1,8 +1,8 @@ use anyhow::bail; -use i2p::net::{I2pSocketAddr, ToI2pSocketAddrs}; +use i2p::net::{I2pAddr, I2pSocketAddr, ToI2pSocketAddrs}; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct PeerId { i2p_addr: I2pSocketAddr, } @@ -54,3 +54,11 @@ impl From for I2pSocketAddr { value.i2p_addr } } + +impl Default for PeerId { + fn default() -> Self { + PeerId { + i2p_addr: I2pSocketAddr::new(I2pAddr::new(""), 0), + } + } +} diff --git a/ubisync-lib/src/types/pot.rs b/ubisync-lib/src/types/pot.rs index a0a58e2..8678ce1 100644 --- a/ubisync-lib/src/types/pot.rs +++ b/ubisync-lib/src/types/pot.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use super::PotId; -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Pot { pub id: PotId, pub app_type: String, diff --git a/ubisync-lib/src/types/pot_id.rs b/ubisync-lib/src/types/pot_id.rs index b580b3c..4c896b0 100644 --- a/ubisync-lib/src/types/pot_id.rs +++ b/ubisync-lib/src/types/pot_id.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] pub struct PotId(Uuid); impl PotId { pub fn new() -> Self { diff --git a/ubisync/Cargo.toml b/ubisync/Cargo.toml index d9717f5..a6c4514 100644 --- a/ubisync/Cargo.toml +++ b/ubisync/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] anyhow = "1.0.71" axum = { version = "0.7.2", features = [ "macros" ] } +bonsaidb = { version = "0.5.0", features = [ "local-full" ] } itertools = "0.12.0" -cozo = { version = "0.7.5", features = [ "storage-rocksdb", "requests", "graph-algo" ] } jsonwebtoken = "9.2.0" serde = { version = "1.0.166", features = [ "derive" ] } serde_json = "1.0.99" diff --git a/ubisync/src/api/v0/app.rs b/ubisync/src/api/v0/app.rs index 9ad190f..dce9afb 100644 --- a/ubisync/src/api/v0/app.rs +++ b/ubisync/src/api/v0/app.rs @@ -24,7 +24,7 @@ use ubisync_lib::{ use crate::state::ApiState; -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct App { pub id: AppId, pub app_type: String, @@ -52,7 +52,7 @@ pub(super) async fn auth( s.jwt_decoding_key(), s.jwt_validation(), ) { - if let Ok(true) = s.app_exists(&token.claims.sub) { + if let Ok(true) = s.app_exists(token.claims.sub.clone()) { debug!("Authentication for {:?} succeeded.", &token.claims.sub); request.extensions_mut().insert(token.claims.sub); return next.run(request).await; @@ -72,7 +72,7 @@ pub(super) async fn register( // Maybe ask for consent by user // If user wants registration, proceed - let result = s.add_app(&body.name, &body.description, &body.app_type); + let result = s.add_app(body.name, body.description, body.app_type); match result { Ok(id) => { @@ -111,7 +111,7 @@ pub(super) async fn set_default_pot( app_id: Extension, Json(body): Json, ) -> Response { - match s.set_app_default_pot(&app_id.0, &body.pot_id) { + match s.set_app_default_pot(app_id.0, body.pot_id) { Ok(_) => ( StatusCode::OK, Json { @@ -131,14 +131,18 @@ pub(super) async fn get_default_pot( app_id: Extension, Json(_): Json, ) -> Response { - match s.get_default_pot(&app_id.0) { - Ok(p) => ( + match s.get_default_pot(app_id.0) { + Ok(Some(p)) => ( StatusCode::OK, Json { 0: AppGetDefaultPotResponse { pot: p }, }, ) .into_response(), + Ok(_) => { + warn!("Pot not found"); + StatusCode::NOT_FOUND.into_response() + } Err(e) => { warn!("No default pot found: {}", e); StatusCode::NOT_FOUND.into_response() @@ -151,7 +155,7 @@ pub(super) async fn create_pot( app_id: Extension, Json(body): Json, ) -> Response { - let app = match s.get_app(&app_id.0) { + let app = match s.get_app(app_id.0.clone()) { Ok(a) => a, Err(e) => { debug!("Failed to fetch app: {}", e); @@ -162,11 +166,11 @@ pub(super) async fn create_pot( Some(t) => t, None => app.app_type, }; - match s.create_pot(&app_id.0, &inferred_app_type) { + match s.create_pot(app_id.0.clone(), inferred_app_type) { Ok(id) => { // If this is the first pot for this app, set it as default if app.default_pot.is_none() { - match s.set_app_default_pot(&app_id.0, &id) { + match s.set_app_default_pot(app_id.0, id.clone()) { Ok(_) => ( StatusCode::OK, Json { diff --git a/ubisync/src/api/v0/element.rs b/ubisync/src/api/v0/element.rs index 94210a2..05925b7 100644 --- a/ubisync/src/api/v0/element.rs +++ b/ubisync/src/api/v0/element.rs @@ -23,7 +23,7 @@ pub(super) async fn get( app: Extension, s: Extension>, ) -> Response { - let element = s.get_element(&id, &app); + let element = s.get_element(id, app.0); match element { Ok(el) => ( StatusCode::OK, @@ -46,8 +46,12 @@ pub(super) async fn create( ) -> Response { let pot_id = match req.pot { Some(p) => p, - None => match s.get_default_pot(&app.0) { - Ok(p) => p.id, + None => match s.get_default_pot(app.0) { + Ok(Some(p)) => p.id, + Ok(_) => { + warn!("Pot not found"); + return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + }, Err(e) => { warn!("Element create request did not provide pot id, and no default pot for requesting app was found: {}", e); return StatusCode::INTERNAL_SERVER_ERROR.into_response(); @@ -55,7 +59,7 @@ pub(super) async fn create( }, }; - let element_id = s.create_element(&req.content, &pot_id); + let element_id = s.create_element(req.content, pot_id); debug!("{:?}", element_id); match element_id { Ok(id) => ( @@ -75,7 +79,7 @@ pub(super) async fn set( s: Extension>, Json(req): Json, ) -> Response { - let res = s.write_element_content(&id, &app, &req.content); + let res = s.write_element_content(id, app.0, req.content); match res { Ok(_) => ( StatusCode::OK, @@ -89,7 +93,7 @@ pub(super) async fn set( } pub(super) async fn remove(Path(id): Path, s: Extension>) -> Response { - let res = s.remove_element(&id); + let res = s.remove_element(id); match res { Ok(_) => ( StatusCode::OK, diff --git a/ubisync/src/comm/message_processor.rs b/ubisync/src/comm/message_processor.rs index 3041f48..6627b0a 100644 --- a/ubisync/src/comm/message_processor.rs +++ b/ubisync/src/comm/message_processor.rs @@ -1,5 +1,6 @@ use tracing::debug; +use ubisync_lib::peer::Peer; use ubisync_lib::types::PeerId; use ubisync_lib::messages::{Message, MessageContent}; @@ -10,23 +11,32 @@ pub fn handle(state: &CommState, peer: &PeerId, message: Message) { debug!("Handling message now: {:?}", message); match message.content() { MessageContent::Hello { peer_name } => { - state.set_peer(peer, peer_name).expect("State failed"); + state + .set_peer(Peer::new(peer.to_owned(), Some(peer_name.to_string()))) + .expect("State failed"); } MessageContent::CreateElement { id, content, pot } => { state - .add_received_element(id, content, message.id(), pot) + .add_received_element( + id.to_owned(), + content.to_owned(), + Some(message.id().to_owned()), + pot.to_owned(), + ) .expect("State failed"); } MessageContent::SetElement { id, content } => { state - .update_element_content(id, content, message.id()) + .update_element_content(id.to_owned(), content.to_owned(), message.id().to_owned()) .expect("State failed"); } MessageContent::RemoveElement { id } => { - state.remove_element(id).expect("State failed"); + state.remove_element(id.to_owned()).expect("State failed"); } MessageContent::AddPot { id, app_type } => { - state.add_pot(id, app_type).expect("State failed"); + state + .add_pot(id.to_owned(), app_type.to_string()) + .expect("State failed"); } } } diff --git a/ubisync/src/config.rs b/ubisync/src/config.rs index c67f7b5..9fb569a 100644 --- a/ubisync/src/config.rs +++ b/ubisync/src/config.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Config { pub i2p_private_key: Option, - pub database_location: String, + pub database_location: Option, pub api_config: ApiConfig, pub jwt_secret: String, } @@ -12,7 +12,7 @@ impl Default for Config { fn default() -> Self { Config { i2p_private_key: None, - database_location: "mem".to_string(), + database_location: None, api_config: Default::default(), jwt_secret: "insecuresecret".to_string(), } diff --git a/ubisync/src/lib.rs b/ubisync/src/lib.rs index b8d46e5..9f7a906 100644 --- a/ubisync/src/lib.rs +++ b/ubisync/src/lib.rs @@ -4,9 +4,10 @@ use anyhow::bail; use api::{v0::app::App, Api, ApiBuilder}; use comm::CommHandle; use config::Config; -use node_events::UbisyncNodeEvent; use i2p::net::I2pSocketAddr; +use node_events::UbisyncNodeEvent; use state::{ApiState, CommState, State}; + use ubisync_lib::{ peer::Peer, types::{AppId, PeerId, PotId}, @@ -26,7 +27,7 @@ pub struct Ubisync { impl Ubisync { pub async fn new(config: &Config) -> anyhow::Result { - let state = State::new(&config.database_location).await?; + let state = State::new(config.database_location.clone()).await?; let comm_handle = Arc::new(CommHandle::new(CommState::new(state.clone()), config)?); state.set_comm_handle(comm_handle.clone()); @@ -44,8 +45,11 @@ impl Ubisync { }) } - pub fn set_node_event_callback(&self, cb: CallbackFunction, node: Arc) - where + pub fn set_node_event_callback( + &self, + cb: CallbackFunction, + node: Arc, + ) where CallbackFunction: Fn(UbisyncNodeEvent, Arc) + Send + Sync + 'static, { self.state_handle.set_node_event_callback(cb, node); @@ -57,14 +61,14 @@ impl Ubisync { pub fn add_peer(&self, p: impl TryInto) -> anyhow::Result<()> { match p.try_into() { - Ok(peer) => self.state_handle.set_peer(&peer), + Ok(peer) => self.state_handle.set_peer(peer), Err(e) => bail!(e), } } pub fn add_peer_from_id(&self, id: PeerId) -> anyhow::Result<()> { // TODO: resolve peer's name before setting - self.state_handle.set_peer(&Peer::new(id, "".to_string())) + self.state_handle.set_peer(Peer::new(id, None)) } pub fn get_apps(&self) -> Vec { @@ -79,7 +83,7 @@ impl Ubisync { self.comm_handle.i2p_address() } - pub fn add_pot_member(&self, pot: &PotId, app: &AppId) -> anyhow::Result<()> { + pub fn add_pot_member(&self, pot: PotId, app: AppId) -> anyhow::Result<()> { self.state_handle.add_pot_member(pot, app) } } diff --git a/ubisync/src/state/api_state.rs b/ubisync/src/state/api_state.rs index b80c795..49d881d 100644 --- a/ubisync/src/state/api_state.rs +++ b/ubisync/src/state/api_state.rs @@ -1,9 +1,7 @@ use std::{sync::Arc, time::Duration}; use anyhow::Error; -use cozo::DbInstance; use jsonwebtoken::{DecodingKey, EncodingKey, Validation}; -use serde_with::chrono::Utc; use tracing::debug; use ubisync_lib::{ api::events::AppEvent, @@ -11,9 +9,9 @@ use ubisync_lib::{ types::{AppId, Element, ElementContent, ElementId, Pot, PotId}, }; -use crate::{api::v0::app::App, state::queries}; +use crate::api::v0::app::App; -use super::State; +use super::{database::StateDB, State}; pub struct ApiState { state: Arc, @@ -36,50 +34,54 @@ impl ApiState { api_state } - pub fn add_app(&self, name: &str, description: &str, app_type: &str) -> anyhow::Result { + pub fn add_app( + &self, + name: String, + description: String, + app_type: String, + ) -> anyhow::Result { let id = AppId::new(); - let last_access = Utc::now(); - queries::apps::add(self.db(), &id, &last_access, name, description, app_type)?; + self.db().add_app(id.clone(), name, description, app_type)?; debug!("Successfully added app"); Ok(id) } - pub fn app_exists(&self, id: &AppId) -> anyhow::Result { - queries::apps::exists(self.db(), id) + pub fn app_exists(&self, id: AppId) -> anyhow::Result { + Ok(self.db().get_app(id)?.is_some()) } - pub fn get_app(&self, id: &AppId) -> anyhow::Result { - queries::apps::get(self.db(), id) + pub fn get_app(&self, id: AppId) -> anyhow::Result { + self.db() + .get_app(id) + .map(|app_opt| app_opt.ok_or(Error::msg("Failed to find app")))? } - pub fn create_element( - &self, - content: &ElementContent, - pot: &PotId, - ) -> anyhow::Result { + pub fn create_element(&self, content: ElementContent, pot: PotId) -> anyhow::Result { let id = ElementId::new(); - queries::elements::add(self.db(), &id, &content, None, true, pot)?; - debug!("Added element {{{}}}", &id.to_string()); + self.db() + .add_element(id.clone(), content.clone(), None, false, pot.clone())?; + debug!("Added element {{{}}}", id.to_string()); self.state.send_to_peers(MessageContent::CreateElement { id: id.clone(), - content: content.clone(), - pot: pot.clone(), + content: content, + pot: pot, }); + Ok(id) } pub fn write_element_content( &self, - id: &ElementId, - app: &AppId, - content: &ElementContent, + id: ElementId, + app: AppId, + content: ElementContent, ) -> anyhow::Result<()> { - if queries::elements::get_app_access(self.db(), id, app)? { - queries::elements::set_content(self.db(), id, content)?; - queries::elements::set_local_changes(self.db(), id, true)?; - debug!("Wrote element content {{{}}}", &id.to_string()); + if self.db().app_has_access(app, id.clone())? { + self.db().set_element_content(id.clone(), content)?; + self.db().set_element_local_changes(id.clone(), true)?; + debug!("Wrote element content {{{}}}", id.to_string()); Ok(()) } else { @@ -89,35 +91,37 @@ impl ApiState { } } - pub fn remove_element(&self, id: &ElementId) -> anyhow::Result<()> { - let res = self.state.remove_element(id); - debug!("Removed element {{{}}}", &id.to_string()); - - res + pub fn remove_element(&self, id: ElementId) -> anyhow::Result<()> { + self.db() + .remove_element(id.clone()) + .inspect(|_| debug!("Removed element {{{}}}", id.to_string())) } - pub fn set_app_default_pot(&self, app_id: &AppId, pot_id: &PotId) -> anyhow::Result<()> { - queries::apps::set_default_pot(self.db(), app_id, pot_id) + pub fn set_app_default_pot(&self, app_id: AppId, pot_id: PotId) -> anyhow::Result<()> { + self.db().set_default_pot(app_id, pot_id) } - pub fn get_default_pot(&self, app_id: &AppId) -> anyhow::Result { - queries::apps::get_default_pot(self.db(), app_id) + pub fn get_default_pot(&self, app_id: AppId) -> anyhow::Result> { + self.db().get_default_pot(app_id) } - pub fn create_pot(&self, app_id: &AppId, app_type: &str) -> anyhow::Result { + pub fn create_pot(&self, app_id: AppId, app_type: String) -> anyhow::Result { let pot_id = PotId::new(); - queries::apps::create_pot(self.db(), &pot_id, app_id, app_type)?; + self.db().add_pot(pot_id.clone(), app_type.clone())?; + self.db().add_pot_membership(pot_id.clone(), app_id)?; self.state.send_to_peers(MessageContent::AddPot { - id: pot_id.to_owned(), - app_type: app_type.to_string(), + id: pot_id.clone(), + app_type: app_type, }); Ok(pot_id) } - pub fn get_element(&self, id: &ElementId, app: &AppId) -> anyhow::Result { - if queries::elements::get_app_access(self.db(), id, app)? { - self.state.get_element(id) + pub fn get_element(&self, id: ElementId, app: AppId) -> anyhow::Result { + if self.db().app_has_access(app, id.clone())? { + self.db() + .get_element(id) + .ok_or(Error::msg("Could not get element")) } else { Err(Error::msg( "Element does not exist or app does not have access to it", @@ -163,12 +167,12 @@ impl ApiState { } else { Err(Error::msg("Failed to lock on receiver mutex")) } - }, + } Err(e) => Err(e), } } - fn db(&self) -> &DbInstance { + fn db(&self) -> &StateDB { &self.state.db } } @@ -191,15 +195,26 @@ mod tests { .init(); let state = ApiState::new( - State::new("mem").await.unwrap(), + State::new(None).await.unwrap(), "abcdabcdabcdabcdabcdabcdabcdabcd", ); - let app_id = state.add_app("appname", "appdesc", "apptype").unwrap(); - let pot_id = state.create_pot(&app_id, "apptype").unwrap(); - let id = state - .create_element(&ElementContent::Text("Test-text".to_string()), &pot_id) + let app_id = state + .add_app( + "appname".to_string(), + "appdesc".to_string(), + "apptype".to_string(), + ) .unwrap(); - let el = state.get_element(&id, &app_id).unwrap(); + let pot_id = state + .create_pot(app_id.clone(), "apptype".to_string()) + .unwrap(); + let id = state + .create_element( + ElementContent::Text("Test-text".to_string()), + pot_id.clone(), + ) + .unwrap(); + let el = state.get_element(id, app_id).unwrap(); assert_eq!( ElementContent::Text("Test-text".to_string()), el.content().to_owned() @@ -215,22 +230,33 @@ mod tests { .init(); let state = ApiState::new( - State::new("mem").await.unwrap(), + State::new(None).await.unwrap(), "abcdabcdabcdabcdabcdabcdabcdabcd", ); - let app_id = state.add_app("appname", "appdesc", "apptype").unwrap(); - let pot_id = state.create_pot(&app_id, "apptype").unwrap(); + let app_id = state + .add_app( + "appname".to_string(), + "appdesc".to_string(), + "apptype".to_string(), + ) + .unwrap(); + let pot_id = state + .create_pot(app_id.clone(), "apptype".to_string()) + .unwrap(); let id = state - .create_element(&ElementContent::Text("Test-text".to_string()), &pot_id) + .create_element( + ElementContent::Text("Test-text".to_string()), + pot_id.clone(), + ) .unwrap(); state .write_element_content( - &id, - &app_id, - &ElementContent::Text("Test-text 2".to_string()), + id.clone(), + app_id.clone(), + ElementContent::Text("Test-text 2".to_string()), ) .unwrap(); - let el = state.get_element(&id, &app_id).unwrap(); + let el = state.get_element(id, app_id).unwrap(); assert_eq!( ElementContent::Text("Test-text 2".to_string()), el.content().to_owned() diff --git a/ubisync/src/state/comm_state.rs b/ubisync/src/state/comm_state.rs index d310a1f..ccb984b 100644 --- a/ubisync/src/state/comm_state.rs +++ b/ubisync/src/state/comm_state.rs @@ -1,17 +1,16 @@ use std::sync::Arc; -use cozo::DbInstance; use tracing::debug; use ubisync_lib::{ api::events::AppEvent, peer::Peer, - types::{Element, ElementContent, ElementId, MessageId, PeerId, PotId}, + types::{Element, ElementContent, ElementId, MessageId, PotId}, }; -use crate::{node_events::UbisyncNodeEvent, state::queries}; +use crate::node_events::UbisyncNodeEvent; -use super::State; +use super::{database::StateDB, State}; pub struct CommState { state: Arc, @@ -24,85 +23,74 @@ impl CommState { pub fn add_received_element( &self, - id: &ElementId, - content: &ElementContent, - latest_message: &MessageId, - pot_id: &PotId, + id: ElementId, + content: ElementContent, + latest_message: Option, + pot_id: PotId, ) -> anyhow::Result<()> { - queries::elements::add( - self.db(), - &id, - &content, - Some(latest_message.to_owned()), - false, - pot_id, - )?; - debug!("Added element {{{}}}", &id.to_string()); - - Ok(()) + self.db() + .add_element(id.clone(), content, latest_message, false, pot_id) + .inspect(|_| debug!("Added element {{{}}}", id.to_string())) } pub fn update_element_content( &self, - id: &ElementId, - content: &ElementContent, - latest_message: &MessageId, + id: ElementId, + content: ElementContent, + latest_message: MessageId, ) -> anyhow::Result<()> { //TODO: resolve potential conflicts with local changes - queries::elements::set_content(self.db(), id, content)?; - queries::elements::set_latest_message(self.db(), id, Some(latest_message.to_owned()))?; - debug!("Updated element {{{}}}", &id.to_string()); + self.db().set_element_content(id.clone(), content)?; + self.db() + .set_element_latest_message(id.clone(), Some(latest_message))?; + debug!("Updated element {{{}}}", id.to_string()); - let _ = self.state.emit_app_event( - queries::pots::get_pot_members( - self.db(), - &queries::elements::get(self.db(), &id)? - .pot() - .as_ref() - .unwrap(), - )? - .get(0) - .unwrap(), - AppEvent::ElementUpdate { id: id.clone() }, - ); + if let Some(el) = self.db().get_element(id.clone()) { + if let Some(pot) = el.pot() { + if let Ok(apps) = self.db().get_pot_members(pot.to_owned()) { + for app in apps { + self.state + .emit_app_event(&app, AppEvent::ElementUpdate { id: id.clone() }) + } + } + } + } Ok(()) } - pub fn remove_element(&self, id: &ElementId) -> anyhow::Result<()> { - let res = self.state.remove_element(id); - debug!("Removed element {{{}}}", &id.to_string()); - - res + pub fn remove_element(&self, id: ElementId) -> anyhow::Result<()> { + self.db() + .remove_element(id.clone()) + .inspect(|_| debug!("Removed element {{{}}}", &id.to_string())) } - pub fn get_element(&self, id: &ElementId) -> anyhow::Result { - self.state.get_element(id) + pub fn get_element(&self, id: ElementId) -> Option { + self.db().get_element(id) } - pub fn set_peer(&self, id: &PeerId, name: &str) -> anyhow::Result<()> { - queries::peers::put(self.db(), id, name)?; - debug!("Set peer {} with address {}.", &name, id.to_string()); - - Ok(()) + pub fn set_peer(&self, peer: Peer) -> anyhow::Result<()> { + self.db() + .add_peer(peer.clone()) + .inspect(|_| debug!("Added peer {:?}.", peer)) } pub fn get_peers(&self) -> anyhow::Result> { self.state.get_peers() } - pub fn add_pot(&self, id: &PotId, app_type: &str) -> anyhow::Result<()> { - queries::pots::add(self.db(), id, app_type)?; + pub fn add_pot(&self, id: PotId, app_type: String) -> anyhow::Result<()> { + self.db().add_pot(id.clone(), app_type.clone())?; let _ = self.state.emit_node_event(UbisyncNodeEvent::NewPot { - id: id.clone(), - app_type: app_type.to_string(), + id: id, + app_type: app_type, }); Ok(()) } - fn db(&self) -> &DbInstance { + fn db(&self) -> &StateDB { &self.state.db } } @@ -124,18 +112,18 @@ mod tests { .with_max_level(Level::DEBUG) .init(); - let state = CommState::new(State::new("mem").await.unwrap()); + let state = CommState::new(State::new(None).await.unwrap()); let id = ElementId::new(); let pot_id = PotId::new(); state .add_received_element( - &id, - &ElementContent::Text("Test-text".to_string()), - &MessageId::new(), - &pot_id, + id.clone(), + ElementContent::Text("Test-text".to_string()), + Some(MessageId::new()), + pot_id, ) .unwrap(); - let el = state.get_element(&id).unwrap(); + let el = state.get_element(id).unwrap(); assert_eq!( ElementContent::Text("Test-text".to_string()), el.content().to_owned() @@ -150,25 +138,25 @@ mod tests { .with_max_level(Level::DEBUG) .init(); - let state = CommState::new(State::new("mem").await.unwrap()); + let state = CommState::new(State::new(None).await.unwrap()); let id = ElementId::new(); let pot_id = PotId::new(); state .add_received_element( - &id, - &ElementContent::Text("Test-text".to_string()), - &MessageId::new(), - &pot_id, + id.clone(), + ElementContent::Text("Test-text".to_string()), + Some(MessageId::new()), + pot_id, ) .unwrap(); state .update_element_content( - &id, - &ElementContent::Text("Test-text 2".to_string()), - &MessageId::new(), + id.clone(), + ElementContent::Text("Test-text 2".to_string()), + MessageId::new(), ) .unwrap(); - let el = state.get_element(&id).unwrap(); + let el = state.get_element(id).unwrap(); assert_eq!( ElementContent::Text("Test-text 2".to_string()), el.content().to_owned() diff --git a/ubisync/src/state/database/as_key.rs b/ubisync/src/state/database/as_key.rs new file mode 100644 index 0000000..1409c95 --- /dev/null +++ b/ubisync/src/state/database/as_key.rs @@ -0,0 +1,93 @@ +use std::{fmt::Display, ops::{Deref, DerefMut}}; + +use bonsaidb::core::key::{Key, KeyEncoding}; +use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize}; + +#[derive(Debug)] +pub(crate) struct KeyEncodingError; + +impl Display for KeyEncodingError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Error in handling a bonsaidb-encoded key") + } +} + +impl std::error::Error for KeyEncodingError {} + + +pub(crate) trait SerdeCompatibleKey: Serialize + DeserializeOwned + Default + Clone + Send + Sync {} +impl SerdeCompatibleKey for T where T: Serialize + DeserializeOwned + Default + Clone + Send + Sync {} + +#[derive(Clone, Debug, Default, Serialize, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) struct AsKey(T); + +impl AsKey +{ + pub fn new(key: T) -> Self { + AsKey(key) + } +} + +impl Deref for AsKey { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for AsKey { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<'de, T> Deserialize<'de> for AsKey +where + T: SerdeCompatibleKey, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let t = T::deserialize(deserializer)?; + Ok(AsKey(t)) + } +} + +impl<'k, T: SerdeCompatibleKey> Key<'k> for AsKey +{ + const CAN_OWN_BYTES: bool = false; + + fn first_value() -> Result { + Ok(AsKey::default()) + } + + fn from_ord_bytes<'e>( + bytes: bonsaidb::core::key::ByteSource<'k, 'e>, + ) -> Result { + match serde_json::from_slice(&*bytes) as Result { + Ok(k) => Ok(AsKey(k)), + Err(_) => Err(KeyEncodingError), + } + } +} + +impl KeyEncoding for AsKey +{ + type Error = KeyEncodingError; + const LENGTH: Option = None; + + fn describe(visitor: &mut Visitor) + where + Visitor: bonsaidb::core::key::KeyVisitor, + { + visitor.visit_type(bonsaidb::core::key::KeyKind::Bytes) + } + + fn as_ord_bytes(&self) -> Result, Self::Error> { + Ok(std::borrow::Cow::Owned( + serde_json::to_vec(&self.0).unwrap(), + )) + } +} diff --git a/ubisync/src/state/database/collections/apps.rs b/ubisync/src/state/database/collections/apps.rs new file mode 100644 index 0000000..a90c0d2 --- /dev/null +++ b/ubisync/src/state/database/collections/apps.rs @@ -0,0 +1,277 @@ +use anyhow::{anyhow, Error}; +use bonsaidb::core::schema::{Collection, SerializedCollection}; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use serde_with::chrono::{DateTime, Utc}; +use ubisync_lib::types::{AppId, ElementId, Pot, PotId}; + +use crate::{ + api::v0::app::App, + state::database::{as_key::AsKey, StateDB}, +}; + +#[derive(Debug, Serialize, Deserialize, Collection, PartialEq, Clone)] +#[collection(name = "apps", views = [])] +pub(super) struct DbApps { + #[natural_id] + pub(super) id: AsKey, + pub(super) last_access: DateTime, + pub(super) app_type: String, + pub(super) name: String, + pub(super) description: String, + pub(super) default_pot: Option, +} + +impl From for App { + fn from(value: DbApps) -> Self { + App { + id: (*value.id).clone(), + app_type: value.app_type, + name: value.name, + description: value.description, + default_pot: value.default_pot, + last_access: value.last_access, + } + } +} + +impl StateDB { + pub fn add_app( + &self, + id: AppId, + name: String, + description: String, + app_type: String, + ) -> anyhow::Result<()> { + DbApps::push( + DbApps { + id: AsKey::new(id), + last_access: Utc::now(), + app_type, + name, + description, + default_pot: None, + }, + &self.db, + ) + .map(|_| ()) + .map_err(|e| anyhow!(e)) + } + + pub fn set_default_pot(&self, id: AppId, pot: PotId) -> anyhow::Result<()> { + DbApps::get(&AsKey::new(id), &self.db) + .unwrap() + .unwrap() + .modify(&self.db, |app| app.contents.default_pot = Some(pot.clone())) + .map_err(|e| anyhow!(e)) + } + + pub fn get_default_pot(&self, id: AppId) -> anyhow::Result> { + let pot_id = DbApps::get(&AsKey::new(id), &self.db)? + .map(|app| app.contents.default_pot) + .ok_or(Error::msg("App not found"))? + .ok_or(Error::msg("Could not get default pot"))?; + self.get_pot(pot_id) + } + + pub fn get_all_apps(&self) -> anyhow::Result> { + Ok(DbApps::all(&self.db) + .query()? + .iter() + .map(|app| app.contents.clone().into()) + .collect_vec()) + } + + pub fn get_all_app_ids(&self) -> anyhow::Result> { + Ok(DbApps::all(&self.db) + .query()? + .iter() + .map(|app| (*app.contents.id).clone()) + .collect_vec()) + } + + pub fn app_has_access(&self, app: AppId, element: ElementId) -> anyhow::Result { + if let Some(el) = self.get_element(element) { + Ok(self + .get_pot_members( + el.pot() + .clone() + .ok_or(Error::msg("Could not fetch pot members"))?, + )? + .contains(&app)) + } else { + Err(Error::msg("Element not found")) + } + } + + pub fn get_app(&self, id: AppId) -> anyhow::Result> { + DbApps::get(&AsKey::new(id), &self.db) + .map(|app_option| app_option.map(|app| app.contents.into())) + .map_err(|e| anyhow!(e)) + } +} + +#[cfg(test)] +mod tests { + use ubisync_lib::types::{AppId, ElementContent, ElementId, Pot, PotId}; + + use crate::{api::v0::app::App, state::database::StateDB}; + + #[test] + fn add_get() { + let db = StateDB::init(None); + let app_id = AppId::new(); + db.add_app( + app_id.clone(), + "app name".to_string(), + "description".to_string(), + "app_type".to_string(), + ) + .unwrap(); + + let retrieved_app = db.get_app(app_id.clone()).unwrap(); + + match retrieved_app { + Some(App { + id, + app_type, + name, + description, + default_pot, + last_access: _, + }) => { + assert_eq!( + (id, app_type, name, description, default_pot), + ( + app_id, + "app_type".to_string(), + "app name".to_string(), + "description".to_string(), + None + ) + ) + } + None => assert!(false), + } + } + + #[test] + fn get_default_pot() { + let db = StateDB::init(None); + let app_id = AppId::new(); + db.add_app( + app_id.clone(), + "app name".to_string(), + "description".to_string(), + "app_type".to_string(), + ) + .unwrap(); + + assert_eq!(db.get_default_pot(app_id).unwrap(), None) + } + + #[test] + fn set_default_pot() { + let db = StateDB::init(None); + let app_id = AppId::new(); + db.add_app( + app_id.clone(), + "app name".to_string(), + "description".to_string(), + "app_type".to_string(), + ) + .unwrap(); + + let pot = Pot::new(PotId::new(), "app_type".to_string()); + db.add_pot(pot.id.clone(), pot.app_type.clone()).unwrap(); + db.set_default_pot(app_id.clone(), pot.id.clone()).unwrap(); + + assert_eq!(db.get_default_pot(app_id).unwrap(), Some(pot)) + } + + #[test] + fn get_apps() { + let db = StateDB::init(None); + + assert_eq!(db.get_all_apps().unwrap(), vec![]); + + let (app1, app2) = (AppId::new(), AppId::new()); + + db.add_app( + app1, + "name1".to_string(), + "desc1".to_string(), + "type1".to_string(), + ) + .unwrap(); + db.add_app( + app2, + "name2".to_string(), + "desc2".to_string(), + "type2".to_string(), + ) + .unwrap(); + + assert_eq!(db.get_all_apps().unwrap().len(), 2); + } + + #[test] + fn get_app_ids() { + let db = StateDB::init(None); + assert_eq!(db.get_all_app_ids().unwrap(), vec![]); + + let (app1, app2) = (AppId::new(), AppId::new()); + + db.add_app( + app1.clone(), + "name1".to_string(), + "desc1".to_string(), + "type1".to_string(), + ) + .unwrap(); + db.add_app( + app2.clone(), + "name2".to_string(), + "desc2".to_string(), + "type2".to_string(), + ) + .unwrap(); + + assert_eq!(db.get_all_app_ids().unwrap(), vec![app1, app2]) + } + + #[test] + fn app_access() { + let db = StateDB::init(None); + let app_id = AppId::new(); + let pot_id = PotId::new(); + let element_id = ElementId::new(); + + db.add_app( + app_id.clone(), + "name".to_string(), + "description".to_string(), + "app_type".to_string(), + ) + .unwrap(); + + db.add_pot(pot_id.clone(), "app_type".to_string()).unwrap(); + db.add_element( + element_id.clone(), + ElementContent::Text("Text".to_string()), + None, + false, + pot_id.clone(), + ) + .unwrap(); + + assert_eq!( + db.app_has_access(app_id.clone(), element_id.clone()) + .unwrap(), + false + ); + + db.add_pot_membership(pot_id, app_id.clone()).unwrap(); + assert_eq!(db.app_has_access(app_id, element_id).unwrap(), true); + } +} diff --git a/ubisync/src/state/database/collections/elements.rs b/ubisync/src/state/database/collections/elements.rs new file mode 100644 index 0000000..4cd979c --- /dev/null +++ b/ubisync/src/state/database/collections/elements.rs @@ -0,0 +1,232 @@ +use anyhow::{anyhow, Error}; +use bonsaidb::core::schema::{Collection, SerializedCollection}; +use serde::{Deserialize, Serialize}; +use ubisync_lib::types::{Element, ElementContent, ElementId, MessageId, PotId}; + +use crate::state::database::{as_key::AsKey, StateDB}; + +#[derive(Debug, Serialize, Deserialize, Collection, PartialEq, Clone)] +#[collection(name = "elements", views = [])] +pub(super) struct DbElement { + #[natural_id] + pub(super) id: AsKey, + pub(super) content: ElementContent, + pub(super) latest_message: Option, + pub(super) local_changes: bool, + pub(super) pot: PotId, +} + +impl From for Element { + fn from(value: DbElement) -> Self { + Element::from(( + (*value.id).clone(), + Some(value.pot), + value.content, + value.latest_message, + value.local_changes, + )) + } +} + +impl StateDB { + pub fn add_element( + &self, + id: ElementId, + content: ElementContent, + latest_message: Option, + local_changes: bool, + pot: PotId, + ) -> anyhow::Result<()> { + DbElement::push( + DbElement { + id: AsKey::new(id), + content, + latest_message, + local_changes, + pot, + }, + &self.db, + ) + .map(|_| ()) + .map_err(|e| anyhow!(e)) + } + + pub fn get_element(&self, id: ElementId) -> Option { + DbElement::get(&AsKey::new(id), &self.db) + .ok()? + .map(|el| el.contents.into()) + } + + pub fn set_element_content( + &self, + id: ElementId, + content: ElementContent, + ) -> anyhow::Result<()> { + DbElement::get(&AsKey::new(id), &self.db) + .map_err(|e| anyhow!(e))? + .ok_or(Error::msg("Could not find element by id"))? + .modify(&self.db, |t| t.contents.content = content.clone()) + .map_err(|e| anyhow!(e)) + } + + pub fn set_element_latest_message( + &self, + id: ElementId, + message: Option, + ) -> anyhow::Result<()> { + DbElement::get(&AsKey::new(id), &self.db) + .map_err(|e| anyhow!(e))? + .ok_or(Error::msg("Could not find element by id"))? + .modify(&self.db, |t| t.contents.latest_message = message.clone()) + .map_err(|e| anyhow!(e)) + } + + pub fn set_element_local_changes( + &self, + id: ElementId, + local_changes: bool, + ) -> anyhow::Result<()> { + DbElement::get(&AsKey::new(id), &self.db) + .map_err(|e| anyhow!(e))? + .ok_or(Error::msg("Could not find element by id"))? + .modify(&self.db, |t| t.contents.local_changes = local_changes) + .map_err(|e| anyhow!(e)) + } + + pub fn remove_element(&self, id: ElementId) -> anyhow::Result<()> { + DbElement::get(&AsKey::new(id), &self.db) + .map_err(|e| anyhow!(e))? + .ok_or(Error::msg("Could not find element by id"))? + .delete(&self.db) + .map_err(|e| anyhow!(e)) + } +} + +#[cfg(test)] +mod tests { + use ubisync_lib::types::{ElementContent, ElementId, MessageId, PotId}; + + use crate::state::database::StateDB; + + #[test] + fn add_get() { + let db = StateDB::init(None); + let pot_id = PotId::new(); + let element_id = ElementId::new(); + db.add_element( + element_id.clone(), + ElementContent::Text("Content!!!".to_string()), + None, + false, + pot_id.clone(), + ) + .unwrap(); + + let retrieved_element = db.get_element(element_id.clone()); + + assert_eq!( + Some( + ( + element_id, + Some(pot_id), + ElementContent::Text("Content!!!".to_string()), + None, + false + ) + .into() + ), + retrieved_element + ) + } + + #[test] + fn set_content() { + let db = StateDB::init(None); + let element_id = ElementId::new(); + db.add_element( + element_id.clone(), + ElementContent::Text("Content!!!".to_string()), + None, + false, + PotId::new(), + ) + .unwrap(); + db.set_element_content( + element_id.clone(), + ElementContent::Text("New Content!!!".to_string()), + ) + .unwrap(); + + assert_eq!( + db.get_element(element_id).unwrap().content().to_owned(), + ElementContent::Text("New Content!!!".to_string()) + ) + } + + #[test] + fn set_latest_message() { + let db = StateDB::init(None); + + let element_id = ElementId::new(); + db.add_element( + element_id.clone(), + ElementContent::Text("Content!!!".to_string()), + None, + false, + PotId::new(), + ) + .unwrap(); + + assert_eq!( + db.get_element(element_id.clone()) + .unwrap() + .latest_message() + .to_owned(), + None + ); + + let msg_id = MessageId::new(); + db.set_element_latest_message(element_id.clone(), Some(msg_id.clone())) + .unwrap(); + + assert_eq!( + db.get_element(element_id) + .unwrap() + .latest_message() + .to_owned(), + Some(msg_id) + ) + } + + #[test] + fn set_local_changes() { + let db = StateDB::init(None); + + let element_id = ElementId::new(); + db.add_element( + element_id.clone(), + ElementContent::Text("Content!!!".to_string()), + None, + false, + PotId::new(), + ) + .unwrap(); + + assert_eq!( + db.get_element(element_id.clone()) + .unwrap().local_changes(), + false + ); + + db.set_element_local_changes(element_id.clone(), true) + .unwrap(); + + assert_eq!( + db.get_element(element_id) + .unwrap() + .local_changes(), + true + ) + } +} + diff --git a/ubisync/src/state/database/collections/mod.rs b/ubisync/src/state/database/collections/mod.rs new file mode 100644 index 0000000..f7d6304 --- /dev/null +++ b/ubisync/src/state/database/collections/mod.rs @@ -0,0 +1,21 @@ +use bonsaidb::core::schema::Schema; + +use apps::DbApps; +use elements::DbElement; +use peers::DbPeers; +use pot_memberships::DbPotMemberships; +use pots::DbPots; + +mod apps; +mod elements; +mod peers; +mod pot_memberships; +mod pots; + +#[derive(Schema, Debug)] +#[schema(name = "ubisync", collections = [DbElement, DbPotMemberships, DbApps, DbPots, DbPeers])] +pub struct UbisyncSchema; + +#[cfg(test)] +mod tests {} + diff --git a/ubisync/src/state/database/collections/peers.rs b/ubisync/src/state/database/collections/peers.rs new file mode 100644 index 0000000..5b21e38 --- /dev/null +++ b/ubisync/src/state/database/collections/peers.rs @@ -0,0 +1,106 @@ +use anyhow::{anyhow, Error}; +use bonsaidb::core::schema::{Collection, SerializedCollection}; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use ubisync_lib::{ + peer::Peer, + types::{self, PeerId}, +}; + +use crate::state::database::{as_key::AsKey, StateDB}; + +#[derive(Debug, Serialize, Deserialize, Collection, PartialEq, Clone)] +#[collection(name = "peers", views = [])] +pub(super) struct DbPeers { + #[natural_id] + pub(super) id: AsKey, + pub(super) name: Option, +} + +impl From for Peer { + fn from(value: DbPeers) -> Self { + Peer::new((*value.id).clone(), value.name) + } +} + +impl StateDB { + pub fn add_peer(&self, peer: Peer) -> anyhow::Result<()> { + DbPeers::push( + DbPeers { + id: AsKey::new(peer.id()), + name: peer.name(), + }, + &self.db, + ) + .map(|_| ()) + .map_err(|e| anyhow!(e)) + } + + pub fn get_peer(&self, id: PeerId) -> anyhow::Result> { + DbPeers::get(&AsKey::new(id), &self.db) + .map(|doc| doc.map(|peer| Peer::new((*peer.contents.id).clone(), peer.contents.name))) + .map_err(|e| anyhow!(e)) + } + + pub fn get_all_peers(&self) -> anyhow::Result> { + DbPeers::all(&self.db) + .query() + .map(|peers| peers.iter().map(|p| p.contents.clone().into()).collect_vec()) + .map_err(|e| anyhow!(e)) + } + + pub fn set_peer_name(&self, id: PeerId, name: &Option) -> anyhow::Result<()> { + DbPeers::get(&AsKey::new(id), &self.db) + .map_err(|e| anyhow!(e))? + .ok_or(Error::msg("Peer not found"))? + .modify(&self.db, |doc| doc.contents.name = name.clone()) + .map_err(|e| anyhow!(e)) + } +} + +#[cfg(test)] +mod tests { + use ubisync_lib::{peer::Peer, types::PeerId}; + + use crate::state::database::StateDB; + + #[test] + fn add_get() { + let db = StateDB::init(None); + + let peer = Peer::new(PeerId::default(), Some("Peer name".to_string())); + db.add_peer(peer.clone()).unwrap(); + + let retrieved_peer = db.get_peer(peer.id()).unwrap(); + + assert_eq!(Some(peer), retrieved_peer) + } + + #[test] + fn get_all() { + let db = StateDB::init(None); + + let peer = Peer::new(PeerId::default(), Some("Peer name".to_string())); + db.add_peer(peer.clone()).unwrap(); + + let all_peers = db.get_all_peers().unwrap(); + assert_eq!(all_peers, vec![peer]); + } + + #[test] + fn set_peer_name() { + let db = StateDB::init(None); + + let peer = Peer::new(PeerId::default(), Some("Peer name".to_string())); + db.add_peer(peer.clone()).unwrap(); + db.set_peer_name(peer.id().clone(), &Some("New peer name".to_string())) + .unwrap(); + + let retrieved_peer = db.get_peer(peer.id()).unwrap(); + + assert_eq!( + Some(Peer::new(peer.id(), Some("New peer name".to_string()))), + retrieved_peer + ) + } +} diff --git a/ubisync/src/state/database/collections/pot_memberships.rs b/ubisync/src/state/database/collections/pot_memberships.rs new file mode 100644 index 0000000..085da84 --- /dev/null +++ b/ubisync/src/state/database/collections/pot_memberships.rs @@ -0,0 +1,173 @@ +use anyhow::{anyhow, Error}; +use bonsaidb::core::{ + connection::Connection, + document::Emit, + schema::{Collection, MapReduce, SerializedCollection, View, ViewSchema}, +}; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use ubisync_lib::types::{AppId, PotId}; + +use crate::state::database::{as_key::AsKey, StateDB}; + +#[derive(Debug, Serialize, Deserialize, Collection, PartialEq, Clone)] +#[collection(name = "pot_memberships", primary_key = u128, views = [DbPotMembershipsByPotId, DbPotMembershipsByAppId, DbPotMembershipsByBothIds])] +pub(super) struct DbPotMemberships { + pub(super) pot_id: AsKey, + pub(super) app_id: AsKey, +} + +#[derive(Debug, Clone, View, ViewSchema)] +#[view(collection = DbPotMemberships, key = AsKey, value = Vec, name = "by-pot-id")] +pub(super) struct DbPotMembershipsByPotId; + +impl MapReduce for DbPotMembershipsByPotId { + fn map<'doc>( + &self, + document: &'doc bonsaidb::core::document::BorrowedDocument<'_>, + ) -> bonsaidb::core::schema::ViewMapResult<'doc, Self> { + let entry = DbPotMemberships::document_contents(document)?; + document + .header + .emit_key_and_value(entry.pot_id, vec![(*entry.app_id).clone()]) + } + + fn reduce( + &self, + mappings: &[bonsaidb::core::schema::MappedValue< + Self::MappedKey<'_>, + ::Value, + >], + _rereduce: bool, + ) -> Result<::Value, bonsaidb::core::Error> { + Ok(mappings + .iter() + .map(|mapping| mapping.value.clone()) + .concat()) + } +} + +#[derive(Debug, Clone, View, ViewSchema)] +#[view(collection = DbPotMemberships, key = AsKey, value = Vec, name = "by-app-id")] +pub(super) struct DbPotMembershipsByAppId; + +impl MapReduce for DbPotMembershipsByAppId { + fn map<'doc>( + &self, + document: &'doc bonsaidb::core::document::BorrowedDocument<'_>, + ) -> bonsaidb::core::schema::ViewMapResult<'doc, Self> { + let entry = DbPotMemberships::document_contents(document)?; + document + .header + .emit_key_and_value(entry.app_id, vec![(*entry.pot_id).clone()]) + } + + fn reduce( + &self, + mappings: &[bonsaidb::core::schema::MappedValue< + Self::MappedKey<'_>, + ::Value, + >], + _rereduce: bool, + ) -> Result<::Value, bonsaidb::core::Error> { + Ok(mappings + .iter() + .map(|mapping| mapping.value.clone()) + .concat()) + } +} + +#[derive(Debug, Clone, View, ViewSchema)] +#[view(collection = DbPotMemberships, key = (AsKey, AsKey), value = DbPotMemberships, name = "by-both")] +pub(super) struct DbPotMembershipsByBothIds; + +impl MapReduce for DbPotMembershipsByBothIds { + fn map<'doc>( + &self, + document: &'doc bonsaidb::core::document::BorrowedDocument<'_>, + ) -> bonsaidb::core::schema::ViewMapResult<'doc, Self> { + let content = DbPotMemberships::document_contents(document)?; + document + .header + .emit_key_and_value((content.pot_id.clone(), content.app_id.clone()), content) + } +} + +impl StateDB { + pub fn add_pot_membership(&self, pot: PotId, app: AppId) -> anyhow::Result<()> { + if let Err(_) = self.get_pot(pot.clone()) { + Err(Error::msg( + "A member was meant to be added to a pot which does not exist.", + )) + } else if let Err(_) = self.get_app(app.clone()) { + Err(Error::msg( + "A member app which does not exist was meant to be added to a pot", + )) + } else { + DbPotMemberships::push( + DbPotMemberships { + pot_id: AsKey::new(pot), + app_id: AsKey::new(app), + }, + &self.db, + ) + .map(|_| ()) + .map_err(|e| anyhow!(e)) + } + } + + pub fn get(&self, pot: PotId, app: AppId) -> anyhow::Result> { + self.db + .view::() + .with_key(&(AsKey::new(pot), AsKey::new(app))) + .query_with_collection_docs() + .map(|_| Some(())) + .map_err(|e| anyhow!(e)) + } + + pub fn get_pot_members(&self, pot: PotId) -> anyhow::Result> { + self.db + .view::() + .with_key(&AsKey::new(pot)) + .reduce() + .map_err(|e| anyhow!(e)) + } + + pub fn get_app_pot_memberships(&self, app: AppId) -> anyhow::Result> { + self.db + .view::() + .with_key(&AsKey::new(app)) + .reduce() + .map_err(|e| anyhow!(e)) + } +} + +#[cfg(test)] +mod tests { + use ubisync_lib::types::{AppId, PotId}; + + use crate::state::database::StateDB; + + #[test] + fn add_get() { + let db = StateDB::init(None); + let pot_id = PotId::new(); + let app_id = AppId::new(); + db.add_pot(pot_id.clone(), "app_type".to_string()).unwrap(); + db.add_app( + app_id.clone(), + "name".to_string(), + "description".to_string(), + "app_type".to_string(), + ) + .unwrap(); + db.add_pot_membership(pot_id.clone(), app_id.clone()) + .unwrap(); + + let retrieved_members = db.get_pot_members(pot_id.clone()).unwrap(); + assert_eq!(vec![app_id.clone()], retrieved_members); + + let retrieved_memberships = db.get_app_pot_memberships(app_id).unwrap(); + assert_eq!(vec![pot_id], retrieved_memberships) + } +} diff --git a/ubisync/src/state/database/collections/pots.rs b/ubisync/src/state/database/collections/pots.rs new file mode 100644 index 0000000..215316f --- /dev/null +++ b/ubisync/src/state/database/collections/pots.rs @@ -0,0 +1,58 @@ +use anyhow::anyhow; +use bonsaidb::core::schema::{Collection, SerializedCollection}; +use serde::{Deserialize, Serialize}; +use ubisync_lib::types::{Pot, PotId}; + +use crate::state::database::{as_key::AsKey, StateDB}; + +#[derive(Debug, Serialize, Deserialize, Collection, PartialEq, Clone)] +#[collection(name = "pots", views = [])] +pub(super) struct DbPots { + #[natural_id] + pub(super) id: AsKey, + pub(super) app_type: String, +} + +impl From for Pot { + fn from(value: DbPots) -> Self { + Pot {id: (*value.id).clone(), app_type: value.app_type} + } +} + +impl StateDB { + pub fn add_pot(&self, id: PotId, app_type: String) -> anyhow::Result<()> { + DbPots::push( + DbPots { + id: AsKey::new(id), + app_type, + }, + &self.db, + ) + .map(|_| ()) + .map_err(|e| anyhow!(e)) + } + + pub fn get_pot(&self, id: PotId) -> anyhow::Result> { + DbPots::get(&AsKey::new(id), &self.db) + .map(|pot_opt| pot_opt.map(|pot| pot.contents.into())) + .map_err(|e| anyhow!(e)) + } +} + +#[cfg(test)] +mod tests { + use ubisync_lib::types::{Pot, PotId}; + + use crate::state::database::StateDB; + + + #[test] + fn add_get() { + let db = StateDB::init(None); + let pot_id = PotId::new(); + db.add_pot(pot_id.clone(), "app_type".to_string()).unwrap(); + + let retrieved_pot = db.get_pot(pot_id.clone()).unwrap(); + assert_eq!(retrieved_pot, Some(Pot {id: pot_id, app_type: "app_type".to_string()})) + } +} \ No newline at end of file diff --git a/ubisync/src/state/database/mod.rs b/ubisync/src/state/database/mod.rs new file mode 100644 index 0000000..bd78728 --- /dev/null +++ b/ubisync/src/state/database/mod.rs @@ -0,0 +1,27 @@ +use bonsaidb::local::{ + config::{Builder, StorageConfiguration}, + Database as BonsaiDb, +}; +use uuid::Uuid; + +use self::collections::UbisyncSchema; + +mod as_key; +mod collections; + +pub struct StateDB { + db: BonsaiDb, +} + +impl StateDB { + pub fn init(path: Option) -> Self { + let storage_conf = match path { + Some(p) => StorageConfiguration::new(p), + None => StorageConfiguration::new(format!("/tmp/{}", Uuid::new_v4())), + // None => StorageConfiguration::default().memory_only() + }; + StateDB { + db: BonsaiDb::open::(storage_conf).unwrap(), + } + } +} diff --git a/ubisync/src/state/mod.rs b/ubisync/src/state/mod.rs index b8dfefb..78e0804 100644 --- a/ubisync/src/state/mod.rs +++ b/ubisync/src/state/mod.rs @@ -1,3 +1,4 @@ +use crate::{api::v0::app::App, comm::CommHandle, node_events::UbisyncNodeEvent, Ubisync}; use std::{ collections::HashMap, sync::{ @@ -5,18 +6,6 @@ use std::{ Arc, Mutex, RwLock, }, }; - -use anyhow::Error; -use cozo::DbInstance; -use tracing::{debug, error, warn}; - -mod api_state; -mod comm_state; -mod queries; -mod schema; - -pub use api_state::ApiState; -pub use comm_state::CommState; use ubisync_lib::{ api::events::AppEvent, messages::{Message, MessageContent}, @@ -24,37 +13,37 @@ use ubisync_lib::{ types::{AppId, Element, ElementContent, ElementId, PotId, Tag}, }; -use crate::{api::v0::app::App, comm::CommHandle, node_events::UbisyncNodeEvent, Ubisync}; +use anyhow::Error; +use tracing::{debug, warn}; + +mod api_state; +mod comm_state; +mod database; + +pub use api_state::ApiState; +pub use comm_state::CommState; + +use self::database::StateDB; pub struct State { - db: DbInstance, + db: StateDB, comm_handle: RwLock>>, app_event_channels: RwLock, Arc>>)>>, node_event_callback: RwLock>>, } impl State { - pub async fn new(db_location: &str) -> anyhow::Result> { - let db = match db_location { - "mem" => DbInstance::new("mem", "", Default::default()), - path => DbInstance::new("rocksdb", path, Default::default()), - }; - match db { - Ok(d) => { - //TODO: change "add schema" to "ensure newest schema" - schema::add_schema(&d)?; + pub async fn new(db_path: Option) -> anyhow::Result> { + let db = StateDB::init(db_path); - let state = Arc::new(State { - db: d, - comm_handle: RwLock::new(None), - app_event_channels: Default::default(), - node_event_callback: RwLock::new(None), - }); - state.init_event_channels(); - Ok(state) - } - Err(e) => Err(Error::msg(format!("{:?}", e))), - } + let state = Arc::new(State { + db, + comm_handle: RwLock::new(None), + app_event_channels: Default::default(), + node_event_callback: RwLock::new(None), + }); + state.init_event_channels(); + Ok(state) } pub fn set_comm_handle(&self, handle: Arc) { @@ -78,76 +67,70 @@ impl State { } pub fn get_apps(&self) -> anyhow::Result> { - queries::apps::get_all(&self.db) + self.db.get_all_apps() } pub fn set_element_content( &self, - element_id: &ElementId, - content: &ElementContent, + element_id: ElementId, + content: ElementContent, ) -> anyhow::Result<()> { - let res = queries::elements::set_content(&self.db, element_id, content); - debug!( - "Set content of element with id {:?}: {:?}", - element_id, - self.get_element(element_id) - ); - - self.send_to_peers(MessageContent::SetElement { - id: element_id.clone(), - content: content.clone(), - }); - res - } - - pub fn remove_element(&self, element_id: &ElementId) -> anyhow::Result<()> { - let res = queries::elements::remove(&self.db, element_id); - - self.send_to_peers(MessageContent::RemoveElement { - id: element_id.clone(), - }); - res - } - - pub fn get_element(&self, id: &ElementId) -> anyhow::Result { - queries::elements::get(&self.db, id) - } - - pub fn get_elements_by_tag(&self, tag: &Tag) -> Vec { - queries::elements::get_by_tag(&self.db, tag) - .map_err(|e| { - error!("{}", e); - e + self.db + .set_element_content(element_id.clone(), content.clone()) + .inspect(|_| { + self.send_to_peers(MessageContent::SetElement { + id: element_id.clone(), + content: content.clone(), + }) }) - .unwrap_or(vec![]) } - pub fn set_peer(&self, peer: &Peer) -> anyhow::Result<()> { - queries::peers::put(&self.db, &peer.id(), &peer.name()) + pub fn remove_element(&self, element_id: ElementId) -> anyhow::Result<()> { + self.db.remove_element(element_id.clone()).inspect(|_| { + self.send_to_peers(MessageContent::RemoveElement { + id: element_id.clone(), + }) + }) + } + + pub fn get_element(&self, id: ElementId) -> Option { + self.db.get_element(id) + } + + pub fn get_elements_by_tag(&self, _tag: &Tag) -> Vec { + todo!() + } + + pub fn set_peer(&self, peer: Peer) -> anyhow::Result<()> { + self.db.add_peer(peer) } pub fn get_peers(&self) -> anyhow::Result> { - queries::peers::get(&self.db) + self.db.get_all_peers() } - pub fn add_pot_member(&self, pot: &PotId, app: &AppId) -> anyhow::Result<()> { - debug!("Pot: {:?}", queries::pots::get(&self.db, pot)); - match queries::pots::add_pot_member(&self.db, pot, app) { - Ok(_) => { + pub fn add_pot_member(&self, pot: PotId, app: AppId) -> anyhow::Result<()> { + let p = self + .db + .get_pot(pot.clone())? + .ok_or(Error::msg("Could not find pot"))?; + self.db + .add_pot_membership(pot.clone(), app.clone()) + .inspect(|_| { self.emit_app_event( - app, + &app, AppEvent::NewPot { id: pot.clone(), - app_type: queries::pots::get(&self.db, pot)?.app_type, + app_type: p.app_type, }, - ); - Ok(()) - } - Err(e) => Err(e), - } + ) + }) } - pub fn get_event_receiver(&self, app: &AppId) -> anyhow::Result>>> { + pub fn get_event_receiver( + &self, + app: &AppId, + ) -> anyhow::Result>>> { self.create_event_channel_if_not_exists(app.clone()); match self.app_event_channels.read() { Ok(map) => match map.get(&app) { @@ -184,8 +167,7 @@ impl State { debug!("Emitting app event failed: {:?}", e); } } - } - else { + } else { debug!("Failed to get channels"); } } @@ -204,7 +186,7 @@ impl State { } fn init_event_channels(&self) { - let app_ids = queries::apps::get_all_ids(&self.db).unwrap_or(vec![]); + let app_ids = self.db.get_all_app_ids().unwrap(); for app in app_ids { self.create_event_channel_if_not_exists(app); } diff --git a/ubisync/src/state/queries/apps.rs b/ubisync/src/state/queries/apps.rs deleted file mode 100644 index 081a7b3..0000000 --- a/ubisync/src/state/queries/apps.rs +++ /dev/null @@ -1,312 +0,0 @@ -use std::collections::BTreeMap; - -use anyhow::{bail, Error}; -use cozo::{DataValue, DbInstance, Num, ScriptMutability}; -use itertools::Itertools; -use serde_with::chrono::{DateTime, Utc}; -use tracing::{debug, warn}; -use ubisync_lib::types::{AppId, Pot, PotId}; - -use crate::{api::v0::app::App, run_query}; - -pub fn add( - db: &DbInstance, - id: &AppId, - last_access: &DateTime, - name: &str, - description: &str, - app_type: &str, -) -> anyhow::Result<()> { - let params = vec![ - ( - "id", - DataValue::Str(serde_json::to_string(&id).unwrap().into()), - ), - ( - "last_access", - DataValue::Num(Num::Int(last_access.timestamp())), - ), - ("name", DataValue::Str(name.into())), - ("description", DataValue::Str(description.into())), - ("app_type", DataValue::Str(app_type.into())), - ("default_pot", DataValue::Null), - ]; - - match run_query!( - &db, - ":insert apps {id => last_access, app_type, name, description, default_pot}", - params, - cozo::ScriptMutability::Mutable - ) { - Ok(_) => Ok(()), - Err(report) => bail!(report), - } -} - -pub fn exists(db: &DbInstance, id: &AppId) -> anyhow::Result { - let mut params = BTreeMap::new(); - params.insert( - "id".to_string(), - DataValue::Str(serde_json::to_string(&id)?.into()), - ); - - let result = db.run_script( - "?[name] := *apps[$id, last_access, app_type, name, description, default_pot]", - params, - cozo::ScriptMutability::Immutable, - ); - - if let Ok(rows) = result { - return Ok(rows.rows.len() == 1); - } - - Err(Error::msg("Could not check whether app is registered")) -} - -pub fn get_all(db: &DbInstance) -> anyhow::Result> { - match db.run_script( - "?[id, last_access, app_type, name, description, default_pot] := *apps[id, last_access, app_type, name, description, default_pot]", - BTreeMap::default(), - ScriptMutability::Immutable) { - Ok(named_rows) => { - Ok(named_rows.rows.iter().filter_map(|row| { - if let [ - DataValue::Str(id), - DataValue::Num(Num::Int(last_access)), - DataValue::Str(app_type), - DataValue::Str(name), - DataValue::Str(description), - default_pot, - ] = row.as_slice() { - Some(App { - id: serde_json::from_str(&id).unwrap(), - app_type: app_type.to_string(), - last_access: DateTime::from_timestamp(last_access.to_owned(), 0).unwrap(), - name: name.to_string(), - description: description.to_string(), - default_pot: default_pot.get_str().map(|str| serde_json::from_str(str).unwrap())} - ) - } - else { - None - } - }).collect_vec()) - }, - Err(e) => bail!(e), - } -} - -pub fn get_all_ids(db: &DbInstance) -> anyhow::Result> { - let result = db.run_script( - "?[id] := *apps{id}", - BTreeMap::new(), - ScriptMutability::Immutable, - ); - - match result { - Ok(named_rows) => Ok(named_rows - .rows - .iter() - .filter_map(|row| { - if let [DataValue::Str(app_id)] = row.as_slice() { - serde_json::from_str::(app_id).ok() - } else { - None - } - }) - .collect()), - Err(e) => bail!(e), - } -} - -pub fn get(db: &DbInstance, id: &AppId) -> anyhow::Result { - let mut params = BTreeMap::new(); - params.insert( - "id".to_string(), - DataValue::Str(serde_json::to_string(&id)?.into()), - ); - - let result = db.run_script( - "?[last_access, app_type, name, description, default_pot] := *apps[$id, last_access, app_type, name, description, default_pot]", - params, - cozo::ScriptMutability::Immutable, - ); - - if let Ok(rows) = result { - if let Some(firstrow) = rows.rows.first() { - if let [DataValue::Num(Num::Int(ts)), DataValue::Str(app_type), DataValue::Str(name), DataValue::Str(desc), default_pot] = - firstrow.as_slice() - { - let last_access = DateTime::from_timestamp(ts.to_owned(), 0) - .ok_or(Error::msg("Failed to deserialize timestamp"))?; - let pot_id = match default_pot { - DataValue::Str(dpid) => Some(serde_json::from_str(dpid)?), - _ => None, - }; - return Ok(App { - id: id.clone(), - app_type: app_type.to_string(), - last_access, - name: name.to_string(), - description: desc.to_string(), - default_pot: pot_id, - }); - } - } - } - - Err(Error::msg("Could not find app")) -} - -pub fn set_default_pot(db: &DbInstance, id: &AppId, pot: &PotId) -> anyhow::Result<()> { - let params = vec![ - ("id", DataValue::Str(serde_json::to_string(id)?.into())), - ( - "default_pot", - DataValue::Str(serde_json::to_string(pot)?.into()), - ), - ]; - - match run_query!( - &db, - ":update apps {id => default_pot}", - params.clone(), - ScriptMutability::Mutable - ) { - Ok(_) => match run_query!( - &db, - ":put pot_memberships {pot_id = default_pot, app_id = id}", - params, - ScriptMutability::Mutable - ) { - Ok(_) => Ok(()), - Err(report) => bail!(report), - }, - Err(report) => bail!(report), - } -} - -pub fn get_default_pot(db: &DbInstance, app: &AppId) -> anyhow::Result { - let mut params = BTreeMap::new(); - params.insert( - "app_id".to_string(), - DataValue::Str(serde_json::to_string(&app)?.into()), - ); - - let result = db.run_script( - " - default_pot[pot_id] := *apps{id: $app_id, default_pot: pot_id} - ?[pot_id, app_type] := default_pot[pot_id], *pots[pot_id, app_type] - ", - params.clone(), - ScriptMutability::Immutable, - ); - - match result { - Ok(rows) => { - if let Some(firstrow) = rows.rows.first() { - if let [DataValue::Str(pot_id_str), DataValue::Str(app_type)] = firstrow.as_slice() - { - Ok(Pot { - id: serde_json::from_str(&pot_id_str)?, - app_type: app_type.to_string(), - }) - } else { - Err(Error::msg("Failed to deserialize query result")) - } - } else { - Err(Error::msg("App not found")) - } - } - Err(e) => bail!(e), - } -} - -pub fn create_pot( - db: &DbInstance, - pot_id: &PotId, - app_id: &AppId, - app_type: &str, -) -> anyhow::Result<()> { - let params = vec![ - ( - "pot_id", - DataValue::Str(serde_json::to_string(pot_id)?.into()), - ), - ( - "app_id", - DataValue::Str(serde_json::to_string(app_id)?.into()), - ), - ("app_type", DataValue::Str(app_type.into())), - ]; - - match run_query!( - &db, - ":insert pots {id = pot_id => app_type}", - params.clone(), - ScriptMutability::Mutable - ) { - Ok(_) => match run_query!( - &db, - ":insert pot_memberships {pot_id => app_id}", - params, - ScriptMutability::Mutable - ) { - Ok(_) => Ok(()), - Err(e) => { - warn!("{:?}", e); - bail!(e) - } - }, - Err(e) => { - warn!("{:?}", e); - bail!(e) - } - } -} - -#[cfg(test)] -mod tests { - use cozo::DbInstance; - use itertools::Itertools; - use serde_with::chrono::Utc; - use tracing::{debug, Level}; - use ubisync_lib::types::{AppId, PotId}; - - use crate::state::{queries::pots, schema}; - - #[test] - pub fn default_pot() { - tracing_subscriber::fmt() - .pretty() - .with_max_level(Level::DEBUG) - .init(); - - let db = DbInstance::new("mem", "", Default::default()).unwrap(); - schema::add_schema(&db).unwrap(); - let app_id = AppId::new(); - let pot_id = PotId::new(); - super::add(&db, &app_id, &Utc::now(), "name", "description", "app_type").unwrap(); - pots::add(&db, &pot_id, "app_type").unwrap(); - super::set_default_pot(&db, &app_id, &pot_id).unwrap(); - - debug!("Result: {:?}", super::get_default_pot(&db, &app_id)); - } - - #[test] - pub fn add_and_get_all() { - tracing_subscriber::fmt() - .pretty() - .with_max_level(Level::DEBUG) - .init(); - - let db = DbInstance::new("mem", "", Default::default()).unwrap(); - schema::add_schema(&db).unwrap(); - super::add(&db, &AppId::new(), &Utc::now(), "app1", "description", "app_type").unwrap(); - super::add(&db, &AppId::new(), &Utc::now(), "app2", "description", "app_type").unwrap(); - let all_ids = super::get_all_ids(&db).unwrap(); - let all_apps = super::get_all(&db).unwrap(); - assert_eq!(all_ids.len(), 2); - assert_eq!(all_ids, all_apps.iter().map(|app| app.id.clone()).collect_vec()); - } -} diff --git a/ubisync/src/state/queries/elements.rs b/ubisync/src/state/queries/elements.rs deleted file mode 100644 index 0e79d67..0000000 --- a/ubisync/src/state/queries/elements.rs +++ /dev/null @@ -1,218 +0,0 @@ -use std::collections::BTreeMap; - -use anyhow::{bail, Error}; -use cozo::{DataValue, DbInstance, JsonData, ScriptMutability}; -use serde_json::Value; -use tracing::error; - -use crate::{ - run_query, - state::{Element, ElementContent, ElementId}, -}; - -use ubisync_lib::types::{AppId, MessageId, PotId, Tag}; - -pub fn add( - db: &DbInstance, - id: &ElementId, - content: &ElementContent, - latest_message: Option, - local_changes: bool, - pot: &PotId, -) -> anyhow::Result<()> { - let params = vec![ - ("id", DataValue::Str(serde_json::to_string(&id)?.into())), - ( - "content", - DataValue::Json(JsonData(serde_json::to_value(content)?)), - ), - ( - "latest_message", - match latest_message { - Some(m) => DataValue::Str(serde_json::to_string(&m)?.into()), - None => DataValue::Null, - }, - ), - ("local_changes", DataValue::Bool(local_changes)), - ("pot", DataValue::Str(serde_json::to_string(pot)?.into())), - ]; - - match run_query!( - &db, - ":insert elements {id => content, latest_message, local_changes, pot}", - params, - ScriptMutability::Mutable - ) { - Ok(_) => Ok(()), - Err(report) => bail!(report), - } -} - -pub fn set_content( - db: &DbInstance, - id: &ElementId, - content: &ElementContent, -) -> anyhow::Result<()> { - set_property( - db, - id, - "content", - DataValue::Json(JsonData(serde_json::to_value(content)?)), - ) -} - -pub fn set_latest_message( - db: &DbInstance, - id: &ElementId, - latest_message: Option, -) -> anyhow::Result<()> { - set_property( - db, - id, - "latest_message", - match latest_message { - Some(m) => DataValue::Str(serde_json::to_string(&m)?.into()), - None => DataValue::Null, - }, - ) -} - -pub fn set_local_changes( - db: &DbInstance, - id: &ElementId, - local_changes: bool, -) -> anyhow::Result<()> { - set_property(db, id, "local_changes", DataValue::Bool(local_changes)) -} - -pub fn remove(db: &DbInstance, id: &ElementId) -> anyhow::Result<()> { - match run_query!( - &db, - ":delete elements {id}", - vec![("id", DataValue::Str(serde_json::to_string(&id)?.into()))], - cozo::ScriptMutability::Mutable - ) { - Ok(_) => Ok(()), - Err(report) => bail!(report), - } -} - -pub fn get(db: &DbInstance, id: &ElementId) -> anyhow::Result { - let mut params = BTreeMap::new(); - params.insert( - "id".to_string(), - DataValue::Str(serde_json::to_string(&id)?.into()), - ); - - let result = db.run_script(" - ?[content, latest_message, local_changes, pot] := *elements[$id, content, latest_message, local_changes, pot] - ", params, cozo::ScriptMutability::Immutable); - match result { - Ok(val) => { - if let Some(firstrow) = val.rows.first() { - if let [DataValue::Json(JsonData(content)), latest_message, DataValue::Bool(local_changes), DataValue::Str(pot_id)] = - firstrow.as_slice() - { - return Ok(Element::from(( - id.to_owned(), - Some(serde_json::from_str(pot_id)?), - serde_json::from_value(content.to_owned())?, - match latest_message { - DataValue::Str(s) => Some(serde_json::from_str(s)?), - _ => None, - }, - local_changes.to_owned(), - ))); - } - return Err(Error::msg("Could not parse db result as Element")); - } else { - return Err(Error::msg("No rows returned for element query")); - } - } - Err(report) => bail!(report), - } -} - -pub fn get_app_access(db: &DbInstance, id: &ElementId, app: &AppId) -> anyhow::Result { - let mut params = BTreeMap::new(); - params.insert( - "id".to_string(), - DataValue::Str(serde_json::to_string(&id)?.into()), - ); - params.insert( - "app_id".to_string(), - DataValue::Str(serde_json::to_string(&app)?.into()), - ); - - //TODO id in script is not bound to $id parameter - let result = db.run_script( - " - memberships[pot_id] := *pot_memberships{pot_id, app_id} - ?[id] := memberships[pot_id], *elements{id, pot: pot_id} - ", - params.clone(), - cozo::ScriptMutability::Immutable, - ); - - match result { - Ok(named_rows) => Ok(named_rows.rows.len() > 0), - Err(report) => bail!(report), - } -} - -pub fn get_by_tag(db: &DbInstance, tag: &Tag) -> anyhow::Result> { - let mut params = BTreeMap::new(); - params.insert( - "tag".to_string(), - DataValue::Str(serde_json::to_string(tag)?.into()), - ); - - let result = db.run_script( - " - ?[element] := *tags[$tag, element] - ", - params, - cozo::ScriptMutability::Immutable, - ); - - match result { - Ok(named_rows) => { - let mut element_ids = vec![]; - for row in named_rows.rows { - if let [DataValue::Json(JsonData(Value::String(element_id)))] = row.as_slice() { - match serde_json::from_str(&element_id) { - Ok(id) => element_ids.push(id), - Err(e) => { - error!("Error parsing element id {}: {}", element_id, e); - continue; - } - } - } - } - Ok(element_ids) - } - Err(report) => bail!(report), - } -} - -fn set_property( - db: &DbInstance, - id: &ElementId, - key: &str, - value: DataValue, -) -> anyhow::Result<()> { - let params = vec![ - ("id", DataValue::Str(serde_json::to_string(id)?.into())), - (key, value), - ]; - - match run_query!( - &db, - format!(":update elements {{id => {key}}}"), - params, - ScriptMutability::Mutable - ) { - Ok(_) => Ok(()), - Err(report) => bail!(report), - } -} diff --git a/ubisync/src/state/queries/mod.rs b/ubisync/src/state/queries/mod.rs deleted file mode 100644 index 3ed09c5..0000000 --- a/ubisync/src/state/queries/mod.rs +++ /dev/null @@ -1,51 +0,0 @@ -pub mod apps; -pub mod elements; -pub mod peers; -pub mod pots; - -#[macro_export] -macro_rules! build_query { - ($payload:expr, $params:expr) => {{ - use cozo::DataValue; - use std::collections::BTreeMap; - // Build parameters map - let mut params_map: BTreeMap = Default::default(); - let mut parameters_init = String::new(); - - if $params.len() > 0 { - for (name, value) in $params { - let _: &str = name; // only for type annotation - params_map.insert(name.to_string(), value); - } - - // First line: Initialize parameters, make them available in CozoScript - use itertools::Itertools; - parameters_init += "?["; - parameters_init += ¶ms_map - .iter() - .map(|(name, _)| name) - .format(", ") - .to_string(); - parameters_init += "] <- [["; - parameters_init += ¶ms_map - .iter() - .map(|(name, _)| format!("${}", name)) - .format(", ") - .to_string(); - parameters_init += "]]"; - } - - // Return query string and parameters map - (format!("{}\n\n{}", parameters_init, $payload), params_map) - }}; -} - -use build_query; - -#[macro_export] -macro_rules! run_query { - ($db:expr, $payload:expr, $params:expr, $mutability:expr) => {{ - let (query, parameters) = crate::state::queries::build_query!($payload, $params); - $db.run_script(query.as_str(), parameters, $mutability) - }}; -} diff --git a/ubisync/src/state/queries/peers.rs b/ubisync/src/state/queries/peers.rs deleted file mode 100644 index 083a899..0000000 --- a/ubisync/src/state/queries/peers.rs +++ /dev/null @@ -1,50 +0,0 @@ -use anyhow::Error; -use cozo::{DataValue, DbInstance, ScriptMutability}; -use ubisync_lib::{peer::Peer, types::PeerId}; - -use crate::run_query; - -pub fn put(db: &DbInstance, id: &PeerId, name: &str) -> anyhow::Result<()> { - let params = vec![ - ("id", DataValue::Str(serde_json::to_string(id)?.into())), - ("name", DataValue::Str(serde_json::to_string(name)?.into())), - ]; - - match run_query!( - &db, - ":put peers {id => name}", - params, - ScriptMutability::Mutable - ) { - Ok(_) => Ok(()), - Err(report) => Err(Error::msg(format!("Query failed: {}", report))), - } -} - -pub fn get(db: &DbInstance) -> anyhow::Result> { - let result = db.run_script( - " - ?[id, name] := *peers{id, name} - ", - Default::default(), - cozo::ScriptMutability::Immutable, - ); - match result { - Ok(rows) => Ok(rows - .rows - .into_iter() - .map(|row| match row.as_slice() { - [DataValue::Str(id_string), DataValue::Str(name_string)] => { - if let Ok(id) = serde_json::from_str(&id_string) { - Some(Peer::new(id, name_string.as_str().to_string())) - } else { - None - } - } - _ => None, - }) - .flatten() - .collect()), - Err(report) => Err(Error::msg(format!("Query failed: {}", report))), - } -} diff --git a/ubisync/src/state/queries/pots.rs b/ubisync/src/state/queries/pots.rs deleted file mode 100644 index c46b803..0000000 --- a/ubisync/src/state/queries/pots.rs +++ /dev/null @@ -1,130 +0,0 @@ -use std::collections::BTreeMap; - -use anyhow::Error; -use cozo::{DataValue, DbInstance, ScriptMutability}; -use tracing::debug; -use ubisync_lib::types::{AppId, Pot, PotId}; - -use crate::run_query; - -pub fn add(db: &DbInstance, id: &PotId, app_type: &str) -> anyhow::Result<()> { - let params = vec![ - ("id", DataValue::Str(serde_json::to_string(id)?.into())), - ( - "app_type", - DataValue::Str(serde_json::to_string(app_type)?.into()), - ), - ]; - - match run_query!( - &db, - ":put pots {id => app_type}", - params, - ScriptMutability::Mutable - ) { - Ok(_) => Ok(()), - Err(report) => Err(Error::msg(format!("Query failed: {}", report))), - } -} - -pub fn get(db: &DbInstance, id: &PotId) -> anyhow::Result { - let mut params = BTreeMap::new(); - params.insert( - "id".to_string(), - DataValue::Str(serde_json::to_string(&id)?.into()), - ); - let result = db.run_script( - " - ?[app_type] := *pots[$id, app_type] - ", - params, - cozo::ScriptMutability::Immutable, - ); - match result { - Ok(rows) => { - if let Some(firstrow) = rows.rows.first() { - if let [DataValue::Str(app_type)] = firstrow.as_slice() { - Ok(Pot::new( - id.clone(), - serde_json::from_str(app_type)?, - )) - } else { - Err(Error::msg("Could not parse result from query")) - } - } else { - Err(Error::msg("Pot not found")) - } - } - Err(report) => Err(Error::msg(format!("Query failed: {}", report))), - } -} - -pub fn add_pot_member(db: &DbInstance, pot: &PotId, app: &AppId) -> anyhow::Result<()> { - let params = vec![ - ("pot_id", DataValue::Str(serde_json::to_string(pot)?.into())), - ("app_id", DataValue::Str(serde_json::to_string(app)?.into())), - ]; - - match run_query!( - &db, - ":insert pot_memberships {pot_id, app_id}", - params, - ScriptMutability::Mutable - ) { - Ok(_) => Ok(()), - Err(report) => Err(Error::msg(format!("Query failed: {}", report))), - } -} - -pub fn get_pot_members(db: &DbInstance, id: &PotId) -> anyhow::Result> { - let mut params = BTreeMap::new(); - params.insert( - "id".to_string(), - DataValue::Str(serde_json::to_string(&id)?.into()), - ); - let result = db.run_script( - " - ?[app_id] := *pot_memberships{id: $id, app_id} - ", - params, - cozo::ScriptMutability::Immutable, - ); - match result { - Ok(rows) => Ok(rows - .rows - .iter() - .map(|row| { - if let [DataValue::Str(app_id_string)] = row.as_slice() { - if let Ok(app_id) = serde_json::from_str::(&app_id_string) { - Some(app_id) - } else { - None - } - } else { - None - } - }) - .flatten() - .collect()), - Err(report) => Err(Error::msg(format!("Query failed: {}", report))), - } -} - -#[cfg(test)] -mod tests { - use cozo::DbInstance; - use ubisync_lib::types::PotId; - - use crate::state::schema; - - #[test] - pub fn add_and_get() { - let db = DbInstance::new("mem", "", Default::default()).unwrap(); - schema::add_schema(&db).unwrap(); - let pot_id = PotId::new(); - super::add(&db, &pot_id, "app_type").unwrap(); - let returned = super::get(&db, &pot_id).unwrap(); - assert_eq!(pot_id, returned.id); - assert_eq!("app_type", returned.app_type); - } -} \ No newline at end of file diff --git a/ubisync/src/state/schema.rs b/ubisync/src/state/schema.rs deleted file mode 100644 index e950033..0000000 --- a/ubisync/src/state/schema.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::collections::BTreeMap; - -use anyhow::Error; -use cozo::DbInstance; - -pub fn add_schema(db: &DbInstance) -> anyhow::Result<()> { - let params = BTreeMap::new(); - match db.run_script( - " - {:create apps { - id: String, - => - last_access: Int, - app_type: String, - name: String, - description: String, - default_pot: String?, - }} - {:create peers { - id: String, - => - name: String, - }} - {:create pots { - id: String, - => - app_type: String - }} - {:create pot_memberships { - pot_id: String, - => - app_id: String, - }} - {:create elements { - id: String, - => - content: Json, - latest_message: String?, - local_changes: Bool, - pot: String, - }} - {:create tags { - tag: String, - element: String, - }} - ", - params, - cozo::ScriptMutability::Mutable, - ) { - Ok(_) => Ok(()), - Err(e) => Err(Error::msg(format!("Failed to set up schema: {}", e))), - } -} diff --git a/ubisync/tests/api.rs b/ubisync/tests/api.rs index 8fb3bad..1fb75b3 100644 --- a/ubisync/tests/api.rs +++ b/ubisync/tests/api.rs @@ -43,7 +43,7 @@ async fn two_nodes_element_creation() { move |ev, node| { if let UbisyncNodeEvent::NewPot { id, app_type } = ev { debug!("callback called"); - node.add_pot_member(&id, &app_id2).unwrap(); + node.add_pot_member(id, app_id2.clone()).unwrap(); } }, ubi2.clone(), @@ -105,7 +105,7 @@ async fn two_nodes_api_event() { move |ev, node| { debug!("callback called"); if let UbisyncNodeEvent::NewPot { id, app_type } = ev { - node.add_pot_member(&id, &app_id1).unwrap(); + node.add_pot_member(id, app_id1.clone()).unwrap(); } }, ubi1.clone(),