...
This commit is contained in:
parent
30dade3d06
commit
46785c3410
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
target/
|
||||
server
|
||||
|
||||
*.wasm
|
||||
herovm_build/
|
||||
test_db
|
752
herodb/Cargo.lock
generated
Normal file
752
herodb/Cargo.lock
generated
Normal 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",
|
||||
]
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
fn db_prefix() -> &'static str {
|
||||
"test_user"
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sale_db(&self) -> &SledDB<Sale> {
|
||||
&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::<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 vote_db(&self) -> &SledDB<Vote> {
|
||||
&self.vote_db
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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
12
herodb/src/main.rs
Normal 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),
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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:");
|
||||
|
3
herodb/src/zaz/cmd/mod.rs
Normal file
3
herodb/src/zaz/cmd/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
//! Command line examples and utilities for the zaz module
|
||||
|
||||
pub mod examples;
|
@ -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
33
herodb/src/zaz/factory.rs
Normal 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(())
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
|
@ -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
|
||||
|
@ -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)]
|
||||
|
@ -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};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
185
herodb/src/zaz/tests/model_db_test.rs
Normal file
185
herodb/src/zaz/tests/model_db_test.rs
Normal 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!");
|
||||
}
|
Loading…
Reference in New Issue
Block a user