This commit is contained in:
kristof 2025-04-03 09:16:00 +02:00
parent 30dade3d06
commit 46785c3410
22 changed files with 1351 additions and 405 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
target/
server
*.wasm
herovm_build/
test_db

752
herodb/Cargo.lock generated Normal file
View File

@ -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",
]

View File

@ -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"

View File

@ -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<u8>,
},
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<Box<dyn std::any::Any>>;
fn list_any(&self) -> SledDBResult<Box<dyn std::any::Any>>;
fn insert_any(&self, model: &dyn std::any::Any) -> SledDBResult<()>;
fn insert_any_raw(&self, serialized: &[u8]) -> SledDBResult<()>;
}
// Implementation of AnyDbOperations for any SledDB<T>
impl<T: SledModel> AnyDbOperations for SledDB<T> {
fn delete(&self, id: &str) -> SledDBResult<()> {
self.delete(id)
}
fn get_any(&self, id: &str) -> SledDBResult<Box<dyn std::any::Any>> {
let result = self.get(id)?;
Ok(Box::new(result))
}
fn list_any(&self) -> SledDBResult<Box<dyn std::any::Any>> {
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::<T>() {
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<DbOperation>,
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<User>,
company_db: SledDB<Company>,
meeting_db: SledDB<Meeting>,
product_db: SledDB<Product>,
sale_db: SledDB<Sale>,
vote_db: SledDB<Vote>,
shareholder_db: SledDB<Shareholder>,
// Type map for generic operations
type_map: HashMap<TypeId, Box<dyn AnyDbOperations>>,
@ -26,8 +90,83 @@ pub struct DB {
transaction: RwLock<Option<TransactionState>>,
}
/// Builder for DB that allows registering models
pub struct DBBuilder {
base_path: PathBuf,
model_registrations: Vec<Box<dyn ModelRegistration>>,
}
/// Trait for model registration
pub trait ModelRegistration: Send + Sync {
fn register(&self, path: &Path) -> SledDBResult<(TypeId, Box<dyn AnyDbOperations>)>;
}
/// Implementation of ModelRegistration for any SledModel type
pub struct SledModelRegistration<T: SledModel> {
phantom: std::marker::PhantomData<T>,
}
impl<T: SledModel> SledModelRegistration<T> {
pub fn new() -> Self {
Self {
phantom: std::marker::PhantomData,
}
}
}
impl<T: SledModel> ModelRegistration for SledModelRegistration<T> {
fn register(&self, path: &Path) -> SledDBResult<(TypeId, Box<dyn AnyDbOperations>)> {
let db: SledDB<T> = SledDB::open(path.join(T::db_prefix()))?;
Ok((TypeId::of::<T>(), Box::new(db) as Box<dyn AnyDbOperations>))
}
}
impl DBBuilder {
/// Create a new DB builder
pub fn new<P: Into<PathBuf>>(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<T: SledModel>(mut self) -> Self {
self.model_registrations.push(Box::new(SledModelRegistration::<T>::new()));
self
}
/// Build the DB with the registered models
pub fn build(self) -> SledDBResult<DB> {
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<TypeId, Box<dyn AnyDbOperations>> = 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<P: Into<PathBuf>>(base_path: P) -> SledDBResult<Self> {
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<TypeId, Box<dyn AnyDbOperations>> = HashMap::new();
type_map.insert(TypeId::of::<User>(), Box::new(user_db.clone()));
type_map.insert(TypeId::of::<Company>(), Box::new(company_db.clone()));
type_map.insert(TypeId::of::<Meeting>(), Box::new(meeting_db.clone()));
type_map.insert(TypeId::of::<Product>(), Box::new(product_db.clone()));
type_map.insert(TypeId::of::<Sale>(), Box::new(sale_db.clone()));
type_map.insert(TypeId::of::<Vote>(), Box::new(vote_db.clone()));
type_map.insert(TypeId::of::<Shareholder>(), 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::<User>() {
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::<User>()) {
return db_ops.insert_any(&model);
}
}
// Company model
else if model_type == TypeId::of::<Company>() {
let model: Company = bincode::deserialize(serialized)?;
if let Some(db_ops) = self.type_map.get(&TypeId::of::<Company>()) {
return db_ops.insert_any(&model);
}
}
// Meeting model
else if model_type == TypeId::of::<Meeting>() {
let model: Meeting = bincode::deserialize(serialized)?;
if let Some(db_ops) = self.type_map.get(&TypeId::of::<Meeting>()) {
return db_ops.insert_any(&model);
}
}
// Product model
else if model_type == TypeId::of::<Product>() {
let model: Product = bincode::deserialize(serialized)?;
if let Some(db_ops) = self.type_map.get(&TypeId::of::<Product>()) {
return db_ops.insert_any(&model);
}
}
// Sale model
else if model_type == TypeId::of::<Sale>() {
let model: Sale = bincode::deserialize(serialized)?;
if let Some(db_ops) = self.type_map.get(&TypeId::of::<Sale>()) {
return db_ops.insert_any(&model);
}
}
// Vote model
else if model_type == TypeId::of::<Vote>() {
let model: Vote = bincode::deserialize(serialized)?;
if let Some(db_ops) = self.type_map.get(&TypeId::of::<Vote>()) {
return db_ops.insert_any(&model);
}
}
// Shareholder model
else if model_type == TypeId::of::<Shareholder>() {
let model: Shareholder = bincode::deserialize(serialized)?;
if let Some(db_ops) = self.type_map.get(&TypeId::of::<Shareholder>()) {
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::<T>(serialized) {
// Try to deserialize and check the ID
match bincode::deserialize::<T>(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<User> {
&self.user_db
// Register a model type with this DB instance
pub fn register<T: SledModel>(&mut self) -> SledDBResult<()> {
let db_path = self.db_path.join(T::db_prefix());
let db: SledDB<T> = SledDB::open(db_path)?;
self.type_map.insert(TypeId::of::<T>(), Box::new(db));
Ok(())
}
pub fn company_db(&self) -> &SledDB<Company> {
&self.company_db
// Get a typed handle to a registered model DB
pub fn db_for<T: SledModel>(&self) -> SledDBResult<&dyn AnyDbOperations> {
match self.type_map.get(&TypeId::of::<T>()) {
Some(db) => Ok(&**db),
None => Err(SledDBError::GeneralError(format!("No DB registered for type {}", std::any::type_name::<T>()))),
}
}
}
// 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<Meeting> {
&self.meeting_db
impl TestUser {
fn new(id: u32, name: String, email: String) -> Self {
Self { id, name, email }
}
}
pub fn product_db(&self) -> &SledDB<Product> {
&self.product_db
impl Storable for TestUser {}
impl SledModel for TestUser {
fn get_id(&self) -> String {
self.id.to_string()
}
pub fn sale_db(&self) -> &SledDB<Sale> {
&self.sale_db
fn db_prefix() -> &'static str {
"test_user"
}
}
pub fn vote_db(&self) -> &SledDB<Vote> {
&self.vote_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::<TestUser>()
.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 shareholder_db(&self) -> &SledDB<Shareholder> {
&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::<TestUser>().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);
}
}

View File

@ -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<u8>,
},
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<Box<dyn std::any::Any>>;
fn list_any(&self) -> SledDBResult<Box<dyn std::any::Any>>;
fn insert_any(&self, model: &dyn std::any::Any) -> SledDBResult<()>;
}
// Implementation of AnyDbOperations for any SledDB<T>
impl<T: SledModel> AnyDbOperations for SledDB<T> {
fn delete(&self, id: &str) -> SledDBResult<()> {
self.delete(id)
}
fn get_any(&self, id: &str) -> SledDBResult<Box<dyn std::any::Any>> {
let result = self.get(id)?;
Ok(Box::new(result))
}
fn list_any(&self) -> SledDBResult<Box<dyn std::any::Any>> {
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::<T>() {
Some(t) => self.insert(t),
None => Err(SledDBError::TypeError),
}
}
}
/// Transaction state for DB operations
pub struct TransactionState {
operations: Vec<DbOperation>,
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};

View File

@ -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;

12
herodb/src/main.rs Normal file
View File

@ -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),
}
}

View File

@ -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<T>`: 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<User> = db.list().expect("Failed to list users");
// Delete user
db.delete::<User>(&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::<User>()
.register_model::<Company>()
.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::<User>()?;
```
## 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<T>` 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

View File

@ -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:");

View File

@ -0,0 +1,3 @@
//! Command line examples and utilities for the zaz module
pub mod examples;

View File

@ -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<dyn std::error::Error>> {
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 ---");

33
herodb/src/zaz/factory.rs Normal file
View File

@ -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<P: Into<PathBuf>>(path: P) -> SledDBResult<DB> {
// Using the builder pattern to register all models
DBBuilder::new(path)
.register_model::<User>()
.register_model::<Company>()
.register_model::<Meeting>()
.register_model::<Product>()
.register_model::<Sale>()
.register_model::<Vote>()
.register_model::<Shareholder>()
.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::<User>()?;
db.register::<Company>()?;
db.register::<Meeting>()?;
db.register::<Product>()?;
db.register::<Sale>()?;
db.register::<Vote>()?;
db.register::<Shareholder>()?;
Ok(())
}

View File

@ -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;

View File

@ -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<Company> = SledDB::open(db_path.join("company")).expect("Failed to open Company Sled DB");
let shareholder_db: SledDB<Shareholder> = 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");
}
}

View File

@ -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};

View File

@ -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

View File

@ -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)]

View File

@ -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};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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::<User>()
.register_model::<Company>()
.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::<User>().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::<Company>().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!");
}