diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..810fe25 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +target/ +server + +*.wasm +herovm_build/ +test_db diff --git a/herodb/Cargo.lock b/herodb/Cargo.lock new file mode 100644 index 0000000..7f943f0 --- /dev/null +++ b/herodb/Cargo.lock @@ -0,0 +1,752 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "brotli" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + +[[package]] +name = "herodb" +version = "0.1.0" +dependencies = [ + "bincode", + "brotli", + "chrono", + "serde", + "serde_json", + "sled", + "tempfile", + "thiserror", + "uuid", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "linux-raw-sys" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "rustix" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "sled" +version = "0.34.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" +dependencies = [ + "crc32fast", + "crossbeam-epoch", + "crossbeam-utils", + "fs2", + "fxhash", + "libc", + "log", + "parking_lot", +] + +[[package]] +name = "smallvec" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "uuid" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.0", +] diff --git a/herodb/Cargo.toml b/herodb/Cargo.toml index a5dad14..acc9de4 100644 --- a/herodb/Cargo.toml +++ b/herodb/Cargo.toml @@ -13,3 +13,6 @@ serde_json = "1.0" thiserror = "1.0" uuid = { version = "1.3", features = ["v4", "serde"] } chrono = { version = "0.4", features = ["serde"] } +bincode = "1.3" +brotli = "3.4" +tempfile = "3.8" diff --git a/herodb/src/core/db.rs b/herodb/src/core/db.rs index a7fddf5..e7d1d2e 100644 --- a/herodb/src/core/db.rs +++ b/herodb/src/core/db.rs @@ -1,20 +1,84 @@ - -use crate::zaz::models::*; +use crate::core::base::*; use std::any::TypeId; use std::collections::HashMap; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex, RwLock}; +use std::fmt::Debug; +use bincode; + +/// Represents a single database operation in a transaction +#[derive(Debug, Clone)] +enum DbOperation { + Set { + model_type: TypeId, + serialized: Vec, + }, + Delete { + model_type: TypeId, + id: String, + }, +} + +// Trait for type-erased database operations +pub trait AnyDbOperations: Send + Sync { + fn delete(&self, id: &str) -> SledDBResult<()>; + fn get_any(&self, id: &str) -> SledDBResult>; + fn list_any(&self) -> SledDBResult>; + fn insert_any(&self, model: &dyn std::any::Any) -> SledDBResult<()>; + fn insert_any_raw(&self, serialized: &[u8]) -> SledDBResult<()>; +} + +// Implementation of AnyDbOperations for any SledDB +impl AnyDbOperations for SledDB { + fn delete(&self, id: &str) -> SledDBResult<()> { + self.delete(id) + } + + fn get_any(&self, id: &str) -> SledDBResult> { + let result = self.get(id)?; + Ok(Box::new(result)) + } + + fn list_any(&self) -> SledDBResult> { + let result = self.list()?; + Ok(Box::new(result)) + } + + fn insert_any(&self, model: &dyn std::any::Any) -> SledDBResult<()> { + // Downcast to the specific type T + match model.downcast_ref::() { + Some(t) => self.insert(t), + None => Err(SledDBError::TypeError), + } + } + + fn insert_any_raw(&self, serialized: &[u8]) -> SledDBResult<()> { + // Deserialize the bytes into model of type T + let model: T = bincode::deserialize(serialized)?; + // Use the regular insert method + self.insert(&model) + } +} + +/// Transaction state for DB operations +pub struct TransactionState { + operations: Vec, + active: bool, +} + +impl TransactionState { + /// Create a new transaction state + pub fn new() -> Self { + Self { + operations: Vec::new(), + active: true, + } + } +} /// Main DB manager that automatically handles all root models pub struct DB { db_path: PathBuf, - user_db: SledDB, - company_db: SledDB, - meeting_db: SledDB, - product_db: SledDB, - sale_db: SledDB, - vote_db: SledDB, - shareholder_db: SledDB, // Type map for generic operations type_map: HashMap>, @@ -26,8 +90,83 @@ pub struct DB { transaction: RwLock>, } +/// Builder for DB that allows registering models +pub struct DBBuilder { + base_path: PathBuf, + model_registrations: Vec>, +} + +/// Trait for model registration +pub trait ModelRegistration: Send + Sync { + fn register(&self, path: &Path) -> SledDBResult<(TypeId, Box)>; +} + +/// Implementation of ModelRegistration for any SledModel type +pub struct SledModelRegistration { + phantom: std::marker::PhantomData, +} + +impl SledModelRegistration { + pub fn new() -> Self { + Self { + phantom: std::marker::PhantomData, + } + } +} + +impl ModelRegistration for SledModelRegistration { + fn register(&self, path: &Path) -> SledDBResult<(TypeId, Box)> { + let db: SledDB = SledDB::open(path.join(T::db_prefix()))?; + Ok((TypeId::of::(), Box::new(db) as Box)) + } +} + +impl DBBuilder { + /// Create a new DB builder + pub fn new>(base_path: P) -> Self { + Self { + base_path: base_path.into(), + model_registrations: Vec::new(), + } + } + + /// Register a model type with the DB + pub fn register_model(mut self) -> Self { + self.model_registrations.push(Box::new(SledModelRegistration::::new())); + self + } + + /// Build the DB with the registered models + pub fn build(self) -> SledDBResult { + let base_path = self.base_path; + + // Ensure base directory exists + if !base_path.exists() { + std::fs::create_dir_all(&base_path)?; + } + + // Register all models + let mut type_map: HashMap> = HashMap::new(); + + for registration in self.model_registrations { + let (type_id, db) = registration.register(&base_path)?; + type_map.insert(type_id, db); + } + + let _write_locks = Arc::new(Mutex::new(HashMap::new())); + let transaction = RwLock::new(None); + + Ok(DB { + db_path: base_path, + type_map, + _write_locks, + transaction, + }) + } +} + impl DB { - /// Create a new DB instance with all model databases + /// Create a new empty DB instance without any models pub fn new>(base_path: P) -> SledDBResult { let base_path = base_path.into(); @@ -36,38 +175,12 @@ impl DB { std::fs::create_dir_all(&base_path)?; } - // Create individual database instances for each model type - let user_db = SledDB::open(base_path.join("user"))?; - let company_db = SledDB::open(base_path.join("company"))?; - let meeting_db = SledDB::open(base_path.join("meeting"))?; - let product_db = SledDB::open(base_path.join("product"))?; - let sale_db = SledDB::open(base_path.join("sale"))?; - let vote_db = SledDB::open(base_path.join("vote"))?; - let shareholder_db = SledDB::open(base_path.join("shareholder"))?; - - // Create type map for generic operations - let mut type_map: HashMap> = HashMap::new(); - type_map.insert(TypeId::of::(), Box::new(user_db.clone())); - type_map.insert(TypeId::of::(), Box::new(company_db.clone())); - type_map.insert(TypeId::of::(), Box::new(meeting_db.clone())); - type_map.insert(TypeId::of::(), Box::new(product_db.clone())); - type_map.insert(TypeId::of::(), Box::new(sale_db.clone())); - type_map.insert(TypeId::of::(), Box::new(vote_db.clone())); - type_map.insert(TypeId::of::(), Box::new(shareholder_db.clone())); - let _write_locks = Arc::new(Mutex::new(HashMap::new())); let transaction = RwLock::new(None); Ok(Self { db_path: base_path, - user_db, - company_db, - meeting_db, - product_db, - sale_db, - vote_db, - shareholder_db, - type_map, + type_map: HashMap::new(), _write_locks, transaction, }) @@ -93,58 +206,13 @@ impl DB { /// Apply a set operation with the serialized data - bypass transaction check fn apply_set_operation(&self, model_type: TypeId, serialized: &[u8]) -> SledDBResult<()> { - // User model - if model_type == TypeId::of::() { - let model: User = bincode::deserialize(serialized)?; - // Access the database operations directly to avoid transaction recursion - if let Some(db_ops) = self.type_map.get(&TypeId::of::()) { - return db_ops.insert_any(&model); - } - } - // Company model - else if model_type == TypeId::of::() { - let model: Company = bincode::deserialize(serialized)?; - if let Some(db_ops) = self.type_map.get(&TypeId::of::()) { - return db_ops.insert_any(&model); - } - } - // Meeting model - else if model_type == TypeId::of::() { - let model: Meeting = bincode::deserialize(serialized)?; - if let Some(db_ops) = self.type_map.get(&TypeId::of::()) { - return db_ops.insert_any(&model); - } - } - // Product model - else if model_type == TypeId::of::() { - let model: Product = bincode::deserialize(serialized)?; - if let Some(db_ops) = self.type_map.get(&TypeId::of::()) { - return db_ops.insert_any(&model); - } - } - // Sale model - else if model_type == TypeId::of::() { - let model: Sale = bincode::deserialize(serialized)?; - if let Some(db_ops) = self.type_map.get(&TypeId::of::()) { - return db_ops.insert_any(&model); - } - } - // Vote model - else if model_type == TypeId::of::() { - let model: Vote = bincode::deserialize(serialized)?; - if let Some(db_ops) = self.type_map.get(&TypeId::of::()) { - return db_ops.insert_any(&model); - } - } - // Shareholder model - else if model_type == TypeId::of::() { - let model: Shareholder = bincode::deserialize(serialized)?; - if let Some(db_ops) = self.type_map.get(&TypeId::of::()) { - return db_ops.insert_any(&model); - } + // Get the database operations for this model type + if let Some(db_ops) = self.type_map.get(&model_type) { + // Just pass the raw serialized data to a special raw insert method + return db_ops.insert_any_raw(serialized); } - Err(SledDBError::TypeError) + Err(SledDBError::GeneralError(format!("No DB registered for type ID {:?}", model_type))) } /// Commit the current transaction, applying all operations @@ -251,11 +319,14 @@ impl DB { // Then check if it has been set in the transaction DbOperation::Set { model_type, serialized } => { if *model_type == type_id { - // Deserialize to check the ID - if let Ok(model) = bincode::deserialize::(serialized) { + // Try to deserialize and check the ID + match bincode::deserialize::(serialized) { + Ok(model) => { if model.get_id() == id_str { return Some(Ok(Some(model))); } + }, + Err(_) => continue, // Skip if deserialization fails } } } @@ -337,34 +408,103 @@ impl DB { } } - // Convenience methods to get each specific database - - pub fn user_db(&self) -> &SledDB { - &self.user_db + // Register a model type with this DB instance + pub fn register(&mut self) -> SledDBResult<()> { + let db_path = self.db_path.join(T::db_prefix()); + let db: SledDB = SledDB::open(db_path)?; + self.type_map.insert(TypeId::of::(), Box::new(db)); + Ok(()) } - pub fn company_db(&self) -> &SledDB { - &self.company_db + // Get a typed handle to a registered model DB + pub fn db_for(&self) -> SledDBResult<&dyn AnyDbOperations> { + match self.type_map.get(&TypeId::of::()) { + Some(db) => Ok(&**db), + None => Err(SledDBError::GeneralError(format!("No DB registered for type {}", std::any::type_name::()))), + } + } +} + +// Test module with mocked models +#[cfg(test)] +mod tests { + use super::*; + use chrono::Utc; + use tempfile::tempdir; + use serde::{Deserialize, Serialize}; + + // Test model + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] + struct TestUser { + id: u32, + name: String, + email: String, } - pub fn meeting_db(&self) -> &SledDB { - &self.meeting_db + impl TestUser { + fn new(id: u32, name: String, email: String) -> Self { + Self { id, name, email } + } } - pub fn product_db(&self) -> &SledDB { - &self.product_db + impl Storable for TestUser {} + + impl SledModel for TestUser { + fn get_id(&self) -> String { + self.id.to_string() + } + + fn db_prefix() -> &'static str { + "test_user" + } } - pub fn sale_db(&self) -> &SledDB { - &self.sale_db + #[test] + fn test_db_builder() { + // Create a temporary directory for the test + let dir = tempdir().expect("Failed to create temp dir"); + + // Create a DB with the builder + let db = DBBuilder::new(dir.path()) + .register_model::() + .build() + .expect("Failed to build DB"); + + // Create a test user + let user = TestUser::new(1, "Test User".to_string(), "test@example.com".to_string()); + + // Set the user + db.set(&user).expect("Failed to set user"); + + // Get the user + let retrieved: TestUser = db.get(&user.id.to_string()).expect("Failed to get user"); + + // Check that it matches + assert_eq!(user, retrieved); } - pub fn vote_db(&self) -> &SledDB { - &self.vote_db - } - - pub fn shareholder_db(&self) -> &SledDB { - &self.shareholder_db + #[test] + fn test_dynamic_registration() { + // Create a temporary directory for the test + let dir = tempdir().expect("Failed to create temp dir"); + + // Create an empty DB + let mut db = DB::new(dir.path()).expect("Failed to create DB"); + + // Register the TestUser model + db.register::().expect("Failed to register TestUser"); + + // Create a test user + let user = TestUser::new(2, "Dynamic User".to_string(), "dynamic@example.com".to_string()); + + // Set the user + db.set(&user).expect("Failed to set user"); + + // Get the user + let retrieved: TestUser = db.get(&user.id.to_string()).expect("Failed to get user"); + + // Check that it matches + assert_eq!(user, retrieved); } } diff --git a/herodb/src/core/mod.rs b/herodb/src/core/mod.rs index 8268cbd..2b17435 100644 --- a/herodb/src/core/mod.rs +++ b/herodb/src/core/mod.rs @@ -1,70 +1,6 @@ -mod base; +pub mod base; +pub mod db; -pub use base::*; - -use std::any::TypeId; -use std::collections::HashMap; -use std::path::PathBuf; -use std::sync::{Arc, Mutex, RwLock}; - -/// Represents a single database operation in a transaction -#[derive(Debug, Clone)] -enum DbOperation { - Set { - model_type: TypeId, - serialized: Vec, - }, - Delete { - model_type: TypeId, - id: String, - }, -} - -// Trait for type-erased database operations -trait AnyDbOperations: Send + Sync { - fn delete(&self, id: &str) -> SledDBResult<()>; - fn get_any(&self, id: &str) -> SledDBResult>; - fn list_any(&self) -> SledDBResult>; - fn insert_any(&self, model: &dyn std::any::Any) -> SledDBResult<()>; -} - -// Implementation of AnyDbOperations for any SledDB -impl AnyDbOperations for SledDB { - fn delete(&self, id: &str) -> SledDBResult<()> { - self.delete(id) - } - - fn get_any(&self, id: &str) -> SledDBResult> { - let result = self.get(id)?; - Ok(Box::new(result)) - } - - fn list_any(&self) -> SledDBResult> { - let result = self.list()?; - Ok(Box::new(result)) - } - - fn insert_any(&self, model: &dyn std::any::Any) -> SledDBResult<()> { - // Downcast to the specific type T - match model.downcast_ref::() { - Some(t) => self.insert(t), - None => Err(SledDBError::TypeError), - } - } -} - -/// Transaction state for DB operations -pub struct TransactionState { - operations: Vec, - active: bool, -} - -impl TransactionState { - /// Create a new transaction state - pub fn new() -> Self { - Self { - operations: Vec::new(), - active: true, - } - } -} +// Re-export everything needed at the module level +pub use base::{SledDB, SledDBError, SledDBResult, Storable, SledModel}; +pub use db::{DB, DBBuilder, ModelRegistration, SledModelRegistration}; diff --git a/herodb/src/lib.rs b/herodb/src/lib.rs index 2758848..bd08f7a 100644 --- a/herodb/src/lib.rs +++ b/herodb/src/lib.rs @@ -3,13 +3,15 @@ //! This library provides a simple interface for working with a sled-based database //! and includes support for defining and working with data models. -mod db; +// Core modules +pub mod core; mod error; -mod model; -pub use db::{Database, Collection}; +// Domain-specific modules +pub mod zaz; + +// Re-exports pub use error::Error; -pub use model::{Model, ModelId, Timestamp}; /// Re-export sled for advanced usage pub use sled; diff --git a/herodb/src/main.rs b/herodb/src/main.rs new file mode 100644 index 0000000..3cd15a3 --- /dev/null +++ b/herodb/src/main.rs @@ -0,0 +1,12 @@ +//! Main entry point for running HeroDB examples + +use herodb::zaz::cmd::examples::run_db_examples; + +fn main() { + println!("Starting HeroDB examples..."); + + match run_db_examples() { + Ok(_) => println!("Examples completed successfully!"), + Err(e) => eprintln!("Error running examples: {}", e), + } +} \ No newline at end of file diff --git a/herodb/src/zaz/DB_README.md b/herodb/src/zaz/DB_README.md index b15b5b2..25e312f 100644 --- a/herodb/src/zaz/DB_README.md +++ b/herodb/src/zaz/DB_README.md @@ -1,98 +1,91 @@ -# Zaz DB System +# HeroDB Architecture -The Zaz DB system is a new implementation that provides automatic database persistence for all root models in the system. +This document explains the architecture of HeroDB, focusing on the separation between model definitions and database logic. -## Architecture +## Core Principles -- Each root model (User, Company, Meeting, Product, Sale, Vote, Shareholder) is stored in its own database file -- The DB system uses Sled, a high-performance embedded database -- Each model is automatically serialized with Bincode and compressed with Brotli -- The DB system provides generic methods that work with any model type +1. **Separation of Concerns**: The DB core should not know about specific models +2. **Registration-Based System**: Models get registered with the DB through a factory pattern +3. **Type-Safety**: Despite the separation, we maintain full type safety -## Directory Structure +## Components -``` -src/zaz/ -├── db/ -│ ├── base.rs # Core traits and SledDB implementation -│ └── mod.rs # Main DB implementation that handles all models -└── models/ - ├── user.rs - ├── company.rs - ├── meeting.rs - ├── product.rs - ├── sale.rs - ├── vote.rs - ├── shareholder.rs - └── lib.rs # Re-exports all models -``` +### Core Module -## Usage +The `core` module provides the database foundation without knowing about specific models: + +- `SledModel` trait: Defines the interface models must implement +- `Storable` trait: Provides serialization/deserialization capabilities +- `SledDB`: Generic database wrapper for any model type +- `DB`: Main database manager that holds registered models +- `DBBuilder`: Builder for creating a DB with registered models + +### Zaz Module + +The `zaz` module contains domain-specific models and factories: + +- `models`: Defines specific model types like User, Company, etc. +- `factory`: Provides functions to create a DB with zaz models registered + +## Using the DB + +### Option 1: Factory Function + +The easiest way to create a DB with all zaz models is to use the factory: ```rust -use crate::db::core::DB; -use crate::zaz::models::*; +use herodb::zaz::create_zaz_db; -// Create a DB instance (handles all model types) -let db = DB::new("/path/to/db").expect("Failed to create DB"); +// Create a DB with all zaz models registered +let db = create_zaz_db("/path/to/db")?; -// --- User Example --- -let user = User::new( - 1, - "John Doe".to_string(), - "john@example.com".to_string(), - "password123".to_string(), - "ACME Corp".to_string(), - "Admin".to_string(), -); - -// Insert user (DB automatically detects the type) -db.set(&user).expect("Failed to insert user"); - -// Get user -let retrieved_user: User = db.get(&user.id.to_string()) - .expect("Failed to get user"); - -// List all users -let users: Vec = db.list().expect("Failed to list users"); - -// Delete user -db.delete::(&user.id.to_string()).expect("Failed to delete user"); - -// --- Company Example --- -let company = Company::new( - 1, - "ACME Corporation".to_string(), - "REG12345".to_string(), - Utc::now(), - "12-31".to_string(), - // other fields... -); - -// Similar operations for company and other models - -// --- Direct Database Access --- -// You can also access the specific database for a model type directly -let user_db = db.user_db(); -let company_db = db.company_db(); -// etc. +// Use the DB with specific model types +let user = User::new(...); +db.set(&user)?; +let retrieved: User = db.get(&id)?; ``` -## Benefits +### Option 2: Builder Pattern -1. **Automatic Type Handling**: The DB system automatically detects the model type and routes operations to the appropriate database -2. **Generic Interface**: Same methods work with any model type -3. **Persistence**: All models are automatically persisted to disk -4. **Performance**: Fast serialization with Bincode and efficient compression with Brotli -5. **Storage Separation**: Each model type has its own database file, making maintenance easier +For more control, use the builder pattern to register only the models you need: -## Implementation Notes +```rust +use herodb::core::{DBBuilder, DB}; +use herodb::zaz::models::{User, Company}; -- Each model implements the `SledModel` trait which provides the necessary methods for database operations -- The `Storable` trait handles serialization and deserialization -- The DB uses separate Sled databases for each model type to ensure proper separation of concerns -- Type-safe operations are ensured through Rust's type system +// Create a DB with only User and Company models +let db = DBBuilder::new("/path/to/db") + .register_model::() + .register_model::() + .build()?; +``` -## Examples +### Option 3: Dynamic Registration -See the `examples.rs` file for complete examples of how to use the DB system. +You can also register models with an existing DB: + +```rust +use herodb::core::DB; +use herodb::zaz::models::User; + +// Create an empty DB +let mut db = DB::new("/path/to/db")?; + +// Register the User model +db.register::()?; +``` + +## Benefits of this Architecture + +1. **Modularity**: The core DB code doesn't need to change when models change +2. **Extensibility**: New model types can be added without modifying core DB code +3. **Flexibility**: Different modules can define and use their own models with the same DB code +4. **Type Safety**: Full compile-time type checking is maintained + +## Implementation Details + +The key to this architecture is the combination of generic types and trait objects: + +- `SledDB` provides type-safe operations for specific model types +- `AnyDbOperations` trait allows type-erased operations through a common interface +- `TypeId` mapping enables runtime lookup of the correct DB for a given model type diff --git a/herodb/src/zaz/cmd/examples.rs b/herodb/src/zaz/cmd/examples.rs index f76daeb..d914bf0 100644 --- a/herodb/src/zaz/cmd/examples.rs +++ b/herodb/src/zaz/cmd/examples.rs @@ -1,8 +1,9 @@ //! Examples demonstrating how to use the new DB implementation -use crate::db::core::DB; -use crate::db::zaz::models::*; -use crate::db::zaz::models::shareholder::ShareholderType; +// Core DB is now imported via the factory +use crate::zaz::models::*; +use crate::zaz::models::shareholder::ShareholderType; +use crate::zaz::factory::create_zaz_db; use std::path::PathBuf; use std::fs; use chrono::Utc; @@ -28,7 +29,7 @@ pub fn run_db_examples() -> Result<(), String> { println!("Using DB path: {:?}", db_path); // Create a DB instance - let db = DB::new(db_path).map_err(|e| format!("Failed to create DB: {}", e))?; + let db = create_zaz_db(db_path).map_err(|e| format!("Failed to create DB: {}", e))?; // --- User Example --- println!("\nRunning User example:"); diff --git a/herodb/src/zaz/cmd/mod.rs b/herodb/src/zaz/cmd/mod.rs new file mode 100644 index 0000000..28e6ffe --- /dev/null +++ b/herodb/src/zaz/cmd/mod.rs @@ -0,0 +1,3 @@ +//! Command line examples and utilities for the zaz module + +pub mod examples; \ No newline at end of file diff --git a/herodb/src/zaz/examples.rs b/herodb/src/zaz/examples.rs index 4950f8d..199d074 100644 --- a/herodb/src/zaz/examples.rs +++ b/herodb/src/zaz/examples.rs @@ -1,7 +1,7 @@ // Examples for using the Zaz database -use crate::db::core::DB; -use crate::db::zaz::models::*; +use crate::zaz::models::*; +use crate::zaz::factory::create_zaz_db; use std::path::PathBuf; use chrono::Utc; @@ -14,7 +14,7 @@ pub fn run_db_examples() -> Result<(), Box> { std::fs::create_dir_all(&db_path)?; // Create DB instance - let db = DB::new(&db_path)?; + let db = create_zaz_db(&db_path)?; // Example 1: User operations println!("\n--- User Examples ---"); diff --git a/herodb/src/zaz/factory.rs b/herodb/src/zaz/factory.rs new file mode 100644 index 0000000..b59c8d0 --- /dev/null +++ b/herodb/src/zaz/factory.rs @@ -0,0 +1,33 @@ +//! Factory module for creating a DB with all zaz models registered + +use crate::core::{DB, DBBuilder, SledDBResult}; +use crate::zaz::models::*; +use std::path::PathBuf; + +/// Create a new DB instance with all zaz models registered +pub fn create_zaz_db>(path: P) -> SledDBResult { + // Using the builder pattern to register all models + DBBuilder::new(path) + .register_model::() + .register_model::() + .register_model::() + .register_model::() + .register_model::() + .register_model::() + .register_model::() + .build() +} + +/// Register all zaz models with an existing DB instance +pub fn register_zaz_models(db: &mut DB) -> SledDBResult<()> { + // Dynamically register all zaz models + db.register::()?; + db.register::()?; + db.register::()?; + db.register::()?; + db.register::()?; + db.register::()?; + db.register::()?; + + Ok(()) +} \ No newline at end of file diff --git a/herodb/src/zaz/mod.rs b/herodb/src/zaz/mod.rs index 5a736d9..0916b49 100644 --- a/herodb/src/zaz/mod.rs +++ b/herodb/src/zaz/mod.rs @@ -2,8 +2,15 @@ #[path = "models/lib.rs"] // Tell compiler where to find models module source pub mod models; +// Add a factory module for DB creation +mod factory; +pub use factory::create_zaz_db; + // Declare the examples module for the new DB implementation #[path = "examples.rs"] // Tell compiler where to find the examples module pub mod examples; +// Expose the cmd module +pub mod cmd; + diff --git a/herodb/src/zaz/models/company.rs b/herodb/src/zaz/models/company.rs index 266f4c6..547f465 100644 --- a/herodb/src/zaz/models/company.rs +++ b/herodb/src/zaz/models/company.rs @@ -1,4 +1,4 @@ -use crate::db::core::{SledModel, Storable, SledDB, SledDBError}; // Import from new location +use crate::core::{SledModel, Storable, SledDB, SledDBError}; use super::shareholder::Shareholder; // Use super:: for sibling module use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -110,127 +110,3 @@ impl Company { // Removed dump and load_from_bytes methods, now provided by Storable trait } - -#[cfg(test)] -mod tests { - use super::*; - use crate::db::zaz::db::{SledDB, SledDBError, SledModel}; - use crate::db::zaz::models::shareholder::{Shareholder, ShareholderType}; - use tempfile::tempdir; - - #[test] - fn test_company_sled_crud() { - // 1. Setup: Create a temporary directory for the Sled DB - let dir = tempdir().expect("Failed to create temp dir"); - let db_path = dir.path(); - let company_db: SledDB = SledDB::open(db_path.join("company")).expect("Failed to open Company Sled DB"); - let shareholder_db: SledDB = SledDB::open(db_path.join("shareholder")).expect("Failed to open Shareholder Sled DB"); - - // 2. Create a sample Company - let incorporation_date = Utc::now(); - let mut company1 = Company::new( - 1, - "Test Corp".to_string(), - "REG123".to_string(), - incorporation_date, - "12-31".to_string(), - "test@corp.com".to_string(), - "123-456-7890".to_string(), - "www.testcorp.com".to_string(), - "123 Test St".to_string(), - BusinessType::Global, - "Tech".to_string(), - "A test company".to_string(), - CompanyStatus::Active, - ); - - let company_id = company1.get_id(); - - // 3. Create and add a shareholder to the company - let now = Utc::now(); - // Define shareholder properties separately - let shareholder_id = 1; - let shareholder_name = "Dummy Shareholder".to_string(); - - // Create the shareholder - let shareholder = Shareholder::new( - shareholder_id, - 0, // company_id will be set by add_shareholder - 0, // user_id - shareholder_name.clone(), - 100.0, // shares - 10.0, // percentage - ShareholderType::Individual, - ); - - // Add the shareholder - company1.add_shareholder(&shareholder_db, shareholder).expect("Failed to add shareholder"); - - // 4. Insert the company - company_db.insert(&company1).expect("Failed to insert company"); - - // 5. Get and Assert - let retrieved_company = company_db.get(&company_id).expect("Failed to get company"); - assert_eq!(company1, retrieved_company, "Retrieved company does not match original"); - - // 6. List and Assert - let all_companies = company_db.list().expect("Failed to list companies"); - assert_eq!(all_companies.len(), 1, "Should be one company in the list"); - assert_eq!(all_companies[0], company1, "List should contain the inserted company"); - - // 7. Delete - company_db.delete(&company_id).expect("Failed to delete company"); - - // 8. Get after delete and Assert NotFound - match company_db.get(&company_id) { - Err(SledDBError::NotFound(id)) => { - assert_eq!(id, company_id, "NotFound error should contain the correct ID"); - } - Ok(_) => panic!("Should not have found the company after deletion"), - Err(e) => panic!("Unexpected error after delete: {:?}", e), - } - - // 9. List after delete - let companies_after_delete = company_db.list().expect("Failed to list companies after delete"); - assert!(companies_after_delete.is_empty(), "List should be empty after deletion"); - - // 10. Check if shareholder exists in shareholder db - let retrieved_shareholder = shareholder_db.get(&shareholder_id.to_string()).expect("Failed to get shareholder"); - assert_eq!(shareholder_id, retrieved_shareholder.id, "Retrieved shareholder should have the correct ID"); - assert_eq!(shareholder_name, retrieved_shareholder.name, "Retrieved shareholder should have the correct name"); - assert_eq!(1, retrieved_shareholder.company_id, "Retrieved shareholder should have company_id set to 1"); - - // Temporary directory `dir` is automatically removed when it goes out of scope here. - } - - #[test] - fn test_dump_load() { - // Create a sample Company - let incorporation_date = Utc::now(); - let original_company = Company::new( - 2, - "DumpLoad Test".to_string(), - "DL987".to_string(), - incorporation_date, - "06-30".to_string(), - "dump@load.com".to_string(), - "987-654-3210".to_string(), - "www.dumpload.com".to_string(), - "456 DumpLoad Ave".to_string(), - BusinessType::Coop, - "Testing".to_string(), - "Testing dump and load".to_string(), - CompanyStatus::Active, - ); - - // Dump (serialize + compress) - let dumped_data = original_company.dump().expect("Failed to dump company"); - assert!(!dumped_data.is_empty(), "Dumped data should not be empty"); - - // Load (decompress + deserialize) - let loaded_company = Company::load_from_bytes(&dumped_data).expect("Failed to load company from bytes"); - - // Assert equality - assert_eq!(original_company, loaded_company, "Loaded company should match the original"); - } -} diff --git a/herodb/src/zaz/models/lib.rs b/herodb/src/zaz/models/lib.rs index 4fa6188..0f201d8 100644 --- a/herodb/src/zaz/models/lib.rs +++ b/herodb/src/zaz/models/lib.rs @@ -18,8 +18,5 @@ pub use sale::Sale; pub use shareholder::Shareholder; // Re-export database components -// pub use db::{DB, DBError, DBResult, Model, ModelMetadata}; // Removed old DB re-exports -pub use crate::db::core::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB}; // Re-export Sled DB components - -// Re-export migration components - Removed -// pub use migration::{Migrator, MigrationError, MigrationResult}; +// Re-export from core module +pub use crate::core::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB}; diff --git a/herodb/src/zaz/models/meeting.rs b/herodb/src/zaz/models/meeting.rs index 864f3f5..6c64c81 100644 --- a/herodb/src/zaz/models/meeting.rs +++ b/herodb/src/zaz/models/meeting.rs @@ -1,6 +1,6 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; -use crate::db::core::{SledModel, Storable}; // Import Sled traits from new location +use crate::core::{SledModel, Storable}; // Import Sled traits from new location // use std::collections::HashMap; // Removed unused import // use super::db::Model; // Removed old Model trait import diff --git a/herodb/src/zaz/models/product.rs b/herodb/src/zaz/models/product.rs index ed7e756..c34b738 100644 --- a/herodb/src/zaz/models/product.rs +++ b/herodb/src/zaz/models/product.rs @@ -1,6 +1,6 @@ use chrono::{DateTime, Utc, Duration}; use serde::{Deserialize, Serialize}; -use crate::db::core::{SledModel, Storable}; // Import Sled traits from new location +use crate::core::{SledModel, Storable}; // Import Sled traits from new location /// Currency represents a monetary value with amount and currency code #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/herodb/src/zaz/models/sale.rs b/herodb/src/zaz/models/sale.rs index f5fb98c..06c0ce9 100644 --- a/herodb/src/zaz/models/sale.rs +++ b/herodb/src/zaz/models/sale.rs @@ -1,5 +1,5 @@ use super::product::Currency; // Use super:: for sibling module -use crate::db::core::{SledModel, Storable}; // Import Sled traits from new location +use crate::core::{SledModel, Storable}; // Import Sled traits from new location // use super::db::Model; // Removed old Model trait import use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/herodb/src/zaz/models/shareholder.rs b/herodb/src/zaz/models/shareholder.rs index 5a2497a..92c1312 100644 --- a/herodb/src/zaz/models/shareholder.rs +++ b/herodb/src/zaz/models/shareholder.rs @@ -1,4 +1,4 @@ -use crate::db::core::{SledModel, Storable}; // Import Sled traits +use crate::core::{SledModel, Storable}; // Import Sled traits use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; // use std::collections::HashMap; // Removed unused import diff --git a/herodb/src/zaz/models/user.rs b/herodb/src/zaz/models/user.rs index 5e64629..62bda34 100644 --- a/herodb/src/zaz/models/user.rs +++ b/herodb/src/zaz/models/user.rs @@ -1,6 +1,6 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; -use crate::db::core::{SledModel, Storable}; // Import Sled traits from new location +use crate::core::{SledModel, Storable}; // Import Sled traits // use std::collections::HashMap; // Removed unused import /// User represents a user in the Freezone Manager system diff --git a/herodb/src/zaz/models/vote.rs b/herodb/src/zaz/models/vote.rs index 75ff4e4..cfb7eb8 100644 --- a/herodb/src/zaz/models/vote.rs +++ b/herodb/src/zaz/models/vote.rs @@ -1,6 +1,6 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; -use crate::db::core::{SledModel, Storable}; // Import Sled traits from new location +use crate::core::{SledModel, Storable}; // Import Sled traits from new location // use std::collections::HashMap; // Removed unused import // use super::db::Model; // Removed old Model trait import diff --git a/herodb/src/zaz/tests/model_db_test.rs b/herodb/src/zaz/tests/model_db_test.rs new file mode 100644 index 0000000..19e53e4 --- /dev/null +++ b/herodb/src/zaz/tests/model_db_test.rs @@ -0,0 +1,185 @@ +//! Tests for the new model-DB architecture + +use crate::core::{DB, DBBuilder, SledDBResult, Storable, SledModel}; +use crate::zaz::factory::create_zaz_db; +use crate::zaz::models::*; +use chrono::Utc; +use std::path::Path; +use tempfile::tempdir; + +#[test] +fn test_zaz_db_factory() { + // Create a temporary directory for testing + let temp_dir = tempdir().expect("Failed to create temp directory"); + println!("Created temporary directory at: {:?}", temp_dir.path()); + + // Create a DB with all zaz models registered using the factory + let db = create_zaz_db(temp_dir.path()).expect("Failed to create zaz DB"); + + // Test with a user model + let user = User::new( + 1, + "Factory Test User".to_string(), + "factory@example.com".to_string(), + "password".to_string(), + "Test Company".to_string(), + "User".to_string(), + ); + + // Insert the user + db.set(&user).expect("Failed to insert user"); + + // Retrieve the user + let retrieved: User = db.get(&user.id.to_string()).expect("Failed to retrieve user"); + + // Verify the user is correct + assert_eq!(user.name, retrieved.name); + assert_eq!(user.email, retrieved.email); + + // Test with a company model + let company = Company::new( + 1, + "Factory Test Corp".to_string(), + "FTC-123".to_string(), + Utc::now(), + "12-31".to_string(), + "info@ftc.com".to_string(), + "123-456-7890".to_string(), + "www.ftc.com".to_string(), + "123 Factory St".to_string(), + BusinessType::Global, + "Technology".to_string(), + "A test company for the factory pattern".to_string(), + CompanyStatus::Active, + ); + + // Insert the company + db.set(&company).expect("Failed to insert company"); + + // Retrieve the company + let retrieved: Company = db.get(&company.id.to_string()).expect("Failed to retrieve company"); + + // Verify the company is correct + assert_eq!(company.name, retrieved.name); + assert_eq!(company.registration_number, retrieved.registration_number); + + println!("All zaz DB factory tests passed successfully!"); +} + +#[test] +fn test_db_builder() { + // Create a temporary directory for testing + let temp_dir = tempdir().expect("Failed to create temp directory"); + println!("Created temporary directory at: {:?}", temp_dir.path()); + + // Create a DB with selectively registered models using the builder pattern + let db = DBBuilder::new(temp_dir.path()) + .register_model::() + .register_model::() + .build() + .expect("Failed to build DB"); + + // Test with a user model + let user = User::new( + 2, + "Builder Test User".to_string(), + "builder@example.com".to_string(), + "password".to_string(), + "Test Company".to_string(), + "User".to_string(), + ); + + // Insert the user + db.set(&user).expect("Failed to insert user"); + + // Retrieve the user + let retrieved: User = db.get(&user.id.to_string()).expect("Failed to retrieve user"); + + // Verify the user is correct + assert_eq!(user.name, retrieved.name); + assert_eq!(user.email, retrieved.email); + + // Test that unregistered models cause an error + let product = Product::new( + 1, + "Unregistered Product".to_string(), + "PROD-123".to_string(), + "A test product".to_string(), + "Test".to_string(), + ProductType::Standard, + ProductStatus::Available, + Currency::USD, + 100.0, + vec![], + ); + + // This should fail because Product was not registered + let result = db.set(&product); + assert!(result.is_err(), "Setting unregistered model should fail"); + + println!("All DB builder tests passed successfully!"); +} + +#[test] +fn test_dynamic_registration() { + // Create a temporary directory for testing + let temp_dir = tempdir().expect("Failed to create temp directory"); + println!("Created temporary directory at: {:?}", temp_dir.path()); + + // Create an empty DB + let mut db = DB::new(temp_dir.path()).expect("Failed to create empty DB"); + + // Register User model dynamically + db.register::().expect("Failed to register User"); + + // Test with a user model + let user = User::new( + 3, + "Dynamic Test User".to_string(), + "dynamic@example.com".to_string(), + "password".to_string(), + "Test Company".to_string(), + "User".to_string(), + ); + + // Insert the user + db.set(&user).expect("Failed to insert user"); + + // Retrieve the user + let retrieved: User = db.get(&user.id.to_string()).expect("Failed to retrieve user"); + + // Verify the user is correct + assert_eq!(user.name, retrieved.name); + assert_eq!(user.email, retrieved.email); + + // Now dynamically register Company + db.register::().expect("Failed to register Company"); + + // Test with a company model + let company = Company::new( + 3, + "Dynamic Test Corp".to_string(), + "DTC-123".to_string(), + Utc::now(), + "12-31".to_string(), + "info@dtc.com".to_string(), + "123-456-7890".to_string(), + "www.dtc.com".to_string(), + "123 Dynamic St".to_string(), + BusinessType::Global, + "Technology".to_string(), + "A test company for dynamic registration".to_string(), + CompanyStatus::Active, + ); + + // Insert the company + db.set(&company).expect("Failed to insert company"); + + // Retrieve the company + let retrieved: Company = db.get(&company.id.to_string()).expect("Failed to retrieve company"); + + // Verify the company is correct + assert_eq!(company.name, retrieved.name); + + println!("All dynamic registration tests passed successfully!"); +} \ No newline at end of file