diff --git a/Cargo.lock b/Cargo.lock index 6df40c7..4f15570 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,1430 +1,1505 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "addr2line" version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstream" version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" dependencies = [ "anstyle", "once_cell_polyfill", "windows-sys 0.59.0", ] [[package]] name = "api" version = "0.1.0" dependencies = [ "axum", "libcollar", "log", "store", "thiserror", "tokio", "tower-http", "tracing", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" dependencies = [ "axum-core", "bytes", "form_urlencoded", "futures-util", "http", "http-body", "http-body-util", "hyper", "hyper-util", "itoa", "matchit", "memchr", "mime", "percent-encoding", "pin-project-lite", "rustversion", "serde", "serde_json", "serde_path_to_error", "serde_urlencoded", "sync_wrapper", "tokio", "tower", "tower-layer", "tower-service", "tracing", ] [[package]] name = "axum-core" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" dependencies = [ "bytes", "futures-core", "http", "http-body", "http-body-util", "mime", "pin-project-lite", "rustversion", "sync_wrapper", "tower-layer", "tower-service", "tracing", ] [[package]] name = "backtrace" version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", "windows-targets 0.52.6", ] [[package]] name = "bindgen" version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ "bitflags 2.9.1", "cexpr", "clang-sys", "itertools", "log", "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", "syn", ] [[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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cexpr" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ "nom", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clang-sys" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", "libloading", ] [[package]] name = "collar" version = "0.1.0" dependencies = [ "api", "env_logger", "libcollar", "log", "thiserror", "tokio", "tracing", "tracing-subscriber", ] [[package]] name = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[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 = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "env_filter" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", ] [[package]] name = "env_logger" version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", "env_filter", "jiff", "log", ] [[package]] name = "errno" version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "fs2" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" dependencies = [ "libc", "winapi", ] [[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", ] [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-task", "pin-project-lite", "pin-utils", ] [[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "http" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http-body" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", ] [[package]] name = "http-body-util" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", "http", "http-body", "pin-project-lite", ] [[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", "futures-util", "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", "smallvec", "tokio", ] [[package]] name = "hyper-util" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" dependencies = [ "bytes", "futures-core", "http", "http-body", "hyper", "pin-project-lite", "tokio", "tower-service", ] [[package]] name = "ifconfig" version = "0.1.0" dependencies = [ "errno", "libc", "log", "nix", "thiserror", ] [[package]] name = "instant" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", "serde", ] [[package]] name = "jiff-static" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libcollar" version = "0.1.0" dependencies = [ "ifconfig", "log", "ng", "serde", "store", "thiserror", "tokio", "tracing", ] [[package]] name = "libloading" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", "windows-targets 0.53.0", ] +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "lock_api" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "matchers" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ "regex-automata 0.1.10", ] [[package]] name = "matchit" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] [[package]] name = "mio" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.59.0", ] [[package]] name = "ng" version = "0.1.0" dependencies = [ "bindgen", "errno", "libc", "log", "socket2", "thiserror", ] [[package]] name = "nix" version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags 2.9.1", "cfg-if", "cfg_aliases", "libc", "memoffset", ] [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "nu-ansi-term" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", "winapi", ] [[package]] name = "object" version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[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 0.8.6", ] [[package]] name = "parking_lot" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core 0.9.11", ] [[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 0.2.16", "smallvec", "winapi", ] [[package]] name = "parking_lot_core" version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", "redox_syscall 0.5.12", "smallvec", "windows-targets 0.52.6", ] [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "portable-atomic" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] name = "portable-atomic-util" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" dependencies = [ "portable-atomic", ] [[package]] name = "prettyplease" version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", "syn", ] [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 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 = "redox_syscall" version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags 2.9.1", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.9", "regex-syntax 0.8.5", ] [[package]] name = "regex-automata" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ "regex-syntax 0.6.29", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax 0.8.5", ] [[package]] name = "regex-syntax" version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + [[package]] name = "rustversion" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[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 = "serde_path_to_error" version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" dependencies = [ "itoa", "serde", ] [[package]] name = "serde_urlencoded" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", "itoa", "ryu", "serde", ] [[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] [[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 0.11.2", ] [[package]] name = "smallvec" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "socket2" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "store" version = "0.1.0" dependencies = [ "log", "sled", + "tempfile", "thiserror", ] [[package]] name = "syn" version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "thread_local" version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", ] [[package]] name = "tokio" version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", "libc", "mio", "parking_lot 0.12.4", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tower" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", "pin-project-lite", "sync_wrapper", "tokio", "tower-layer", "tower-service", "tracing", ] [[package]] name = "tower-http" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" dependencies = [ "bitflags 2.9.1", "bytes", "http", "http-body", "pin-project-lite", "tower-layer", "tower-service", "tracing", ] [[package]] name = "tower-layer" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing-core" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", ] [[package]] name = "tracing-log" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[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 = "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-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows-targets" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" dependencies = [ "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", "windows_i686_gnullvm 0.53.0", "windows_i686_msvc 0.53.0", "windows_x86_64_gnu 0.53.0", "windows_x86_64_gnullvm 0.53.0", "windows_x86_64_msvc 0.53.0", ] [[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_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[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_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" [[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_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[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_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" [[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_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[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.1", +] diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml index e58282d..6c03b5a 100644 --- a/crates/store/Cargo.toml +++ b/crates/store/Cargo.toml @@ -1,11 +1,14 @@ [package] name = "store" version = "0.1.0" license.workspace = true edition.workspace = true authors.workspace = true [dependencies] log.workspace = true thiserror.workspace = true sled = "0.34.7" + +[dev-dependencies] +tempfile = "3.8" diff --git a/crates/store/examples/range_example.rs b/crates/store/examples/range_example.rs new file mode 100644 index 0000000..ea64471 --- /dev/null +++ b/crates/store/examples/range_example.rs @@ -0,0 +1,63 @@ +use store::{open, Result}; + +fn main() -> Result<()> { + // Open the store + let store = open()?; + let ranges = store.ranges(); + + // Define a range for user IDs with 1000 possible slots + ranges.define("user_ids", 1000)?; + + // Define a range for session tokens with 64 slots + ranges.define("session_tokens", 64)?; + + println!("Defined ranges:"); + for (name, size) in ranges.list_ranges()? { + println!(" {} (size: {})", name, size); + } + + // Assign some user IDs + let alice_id = ranges.assign("user_ids", "alice@example.com")?; + let bob_id = ranges.assign("user_ids", "bob@example.com")?; + let charlie_id = ranges.assign("user_ids", "charlie@example.com")?; + + println!("\nAssigned user IDs:"); + println!(" Alice: {}", alice_id); + println!(" Bob: {}", bob_id); + println!(" Charlie: {}", charlie_id); + + // Assign some session tokens + let session1 = ranges.assign("session_tokens", "abc123")?; + let session2 = ranges.assign("session_tokens", "def456")?; + + println!("\nAssigned session tokens:"); + println!(" Session 1: {}", session1); + println!(" Session 2: {}", session2); + + // List all assignments in user_ids range + println!("\nAll user ID assignments:"); + for (bit_pos, email) in ranges.list_range("user_ids")? { + println!(" {}: {}", bit_pos, email); + } + + // Get specific values + if let Some(email) = ranges.get("user_ids", alice_id)? { + println!("\nUser at position {}: {}", alice_id, email); + } + + // Remove Bob's assignment + let removed = ranges.unassign("user_ids", "bob@example.com")?; + println!("\nRemoved Bob: {}", removed); + + // Assign a new user - should reuse Bob's old slot + let dave_id = ranges.assign("user_ids", "dave@example.com")?; + println!("Dave got ID: {} (should be {})", dave_id, bob_id); + + // List updated assignments + println!("\nUpdated user ID assignments:"); + for (bit_pos, email) in ranges.list_range("user_ids")? { + println!(" {}: {}", bit_pos, email); + } + + Ok(()) +} \ No newline at end of file diff --git a/crates/store/src/error.rs b/crates/store/src/error.rs index a9dc7ad..8e22857 100644 --- a/crates/store/src/error.rs +++ b/crates/store/src/error.rs @@ -1,16 +1,31 @@ #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] StoreError(#[from] sled::Error), #[error(transparent)] TransactionError(#[from] sled::transaction::TransactionError), + #[error(transparent)] + Utf8Error(#[from] std::string::FromUtf8Error), + #[error("Namespace not defined")] UndefinedNamespace(String), #[error("Key is already reserved in the given namespace")] NamespaceKeyReserved(String, String), + + #[error("Range not defined")] + UndefinedRange(String), + + #[error("Range is full")] + RangeFull(String), + + #[error("Bit position out of range")] + BitOutOfRange(String, u64), + + #[error("Value not found in range")] + ValueNotInRange(String, String), } pub type Result = ::std::result::Result; diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs index 0dc2e3b..c39f7a0 100644 --- a/crates/store/src/lib.rs +++ b/crates/store/src/lib.rs @@ -1,9 +1,12 @@ mod error; mod store; pub use error::{Result, Error}; pub use store::Store; pub use store::open; pub mod namespace; pub use namespace::NamespaceStore; + +pub mod range; +pub use range::RangeStore; diff --git a/crates/store/src/range.rs b/crates/store/src/range.rs new file mode 100644 index 0000000..d026e82 --- /dev/null +++ b/crates/store/src/range.rs @@ -0,0 +1,508 @@ +//! # Range Store Module +//! +//! This module implements "ranges" which are buckets of unique range assignments +//! that map values to specific bit positions within fixed-size ranges. This is useful +//! for allocating unique identifiers, session tokens, or any scenario where you need +//! to assign sequential positions to values. +//! +//! ## Storage Design +//! +//! The RangeStore uses three sled trees: +//! +//! - **`names` tree**: Maps range names to their metadata +//! - Key: `name` (string) +//! - Value: `id || size` where `id` is a u64 from `generate_id()` and `size` is a u64 +//! +//! - **`map` tree**: Stores bitmaps for each range to track allocated positions +//! - Key: `id` (u64 from the `names` tree) +//! - Value: `bitmap` (binary data representing which bits are allocated) +//! +//! - **`assign` tree**: Maps range positions to their assigned values +//! - Key: `id || bit_position` where `id` is the range ID and `bit_position` is a u64 +//! - Value: `value` (the string value assigned to this position) +//! +//! ## API Operations +//! +//! - `define(name, size)`: Create a new range with the given name and maximum size +//! - `assign(name, value)`: Assign a value to the next available position in the range +//! - `get(name, position)`: Retrieve the value at a specific position +//! - `list_range(name)`: List all assignments in a range +//! - `list_ranges()`: List all defined ranges +//! - `unassign(name, value)`: Remove a value from the range and free its position +//! +//! ## Example Usage +//! +//! ```rust +//! use store::{open, Result}; +//! +//! fn example() -> Result<()> { +//! let store = open()?; +//! let ranges = store.ranges(); +//! +//! // Define a range for user IDs +//! ranges.define("user_ids", 1000)?; +//! +//! // Assign some users +//! let alice_id = ranges.assign("user_ids", "alice@example.com")?; // Returns 0 +//! let bob_id = ranges.assign("user_ids", "bob@example.com")?; // Returns 1 +//! +//! // Get a user by position +//! let email = ranges.get("user_ids", alice_id)?; // Some("alice@example.com") +//! +//! // Remove a user (frees the position for reuse) +//! ranges.unassign("user_ids", "bob@example.com")?; +//! +//! // Next assignment will reuse Bob's position +//! let charlie_id = ranges.assign("user_ids", "charlie@example.com")?; // Returns 1 +//! +//! Ok(()) +//! } +//! ``` + +use crate::Result; +use crate::Error; +use sled::Transactional; + +#[derive(Debug, Clone)] +pub struct RangeStore { + names: sled::Tree, + map: sled::Tree, + assign: sled::Tree, +} + +impl RangeStore { + pub(crate) fn open(db: &sled::Db) -> Result { + Ok(RangeStore { + names: db.open_tree("ranges/1/n")?, + map: db.open_tree("ranges/1/m")?, + assign: db.open_tree("ranges/1/a")?, + }) + } + + // Define a new range with a given name and size + pub fn define(&self, name: &str, size: u64) -> Result<()> { + (&self.names, &self.map).transaction(|(names, map)| { + match names.get(name)? { + Some(_) => Ok(()), // Range already exists + None => { + let id = names.generate_id()?; + + // Store name -> (id, size) + let mut value = Vec::new(); + value.extend_from_slice(&id.to_be_bytes()); + value.extend_from_slice(&size.to_be_bytes()); + names.insert(name, value)?; + + // Initialize bitmap for the range (all zeros) + let bitmap_size = (size + 7) / 8; // Round up to nearest byte + let bitmap = vec![0u8; bitmap_size as usize]; + map.insert(&id.to_be_bytes(), bitmap)?; + + Ok(()) + } + } + })?; + Ok(()) + } + + // Assign the next free bit in the range to a value + pub fn assign(&self, range_name: &str, value: &str) -> Result { + let result = (&self.names, &self.map, &self.assign).transaction(|(names, map, assign)| { + // Get range info + let (range_id, range_size) = match names.get(range_name)? { + Some(data) => { + if data.len() != 16 { + return Err(sled::transaction::ConflictableTransactionError::Abort(())); + } + let id_bytes: [u8; 8] = data[0..8].try_into() + .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?; + let size_bytes: [u8; 8] = data[8..16].try_into() + .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?; + (u64::from_be_bytes(id_bytes), u64::from_be_bytes(size_bytes)) + } + None => return Err(sled::transaction::ConflictableTransactionError::Abort(())), + }; + + // Get current bitmap + let mut bitmap = match map.get(&range_id.to_be_bytes())? { + Some(data) => data.to_vec(), + None => return Err(sled::transaction::ConflictableTransactionError::Abort(())), + }; + + // Find first free bit + let mut bit_position = None; + for bit in 0..range_size { + let byte_index = (bit / 8) as usize; + let bit_index = bit % 8; + + if byte_index < bitmap.len() { + let mask = 1u8 << bit_index; + if (bitmap[byte_index] & mask) == 0 { + // This bit is free + bitmap[byte_index] |= mask; + bit_position = Some(bit); + break; + } + } + } + + let bit_pos = match bit_position { + Some(pos) => pos, + None => return Err(sled::transaction::ConflictableTransactionError::Abort(())), + }; + + // Update bitmap + map.insert(&range_id.to_be_bytes(), bitmap)?; + + // Store assignment + let mut assign_key = Vec::new(); + assign_key.extend_from_slice(&range_id.to_be_bytes()); + assign_key.extend_from_slice(&bit_pos.to_be_bytes()); + assign.insert(assign_key, value.as_bytes())?; + + Ok(bit_pos) + }).map_err(|e| match e { + sled::transaction::TransactionError::Abort(()) => Error::RangeFull(range_name.to_string()), + sled::transaction::TransactionError::Storage(storage_err) => Error::StoreError(storage_err), + })?; + Ok(result) + } + + // Get a value from a specific bit position in the range + pub fn get(&self, range_name: &str, bit_position: u64) -> Result> { + let result = (&self.names, &self.assign).transaction(|(names, assign)| { + // Get range info + let (range_id, range_size) = match names.get(range_name)? { + Some(data) => { + if data.len() != 16 { + return Ok(None); + } + let id_bytes: [u8; 8] = data[0..8].try_into() + .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?; + let size_bytes: [u8; 8] = data[8..16].try_into() + .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?; + (u64::from_be_bytes(id_bytes), u64::from_be_bytes(size_bytes)) + } + None => return Ok(None), + }; + + if bit_position >= range_size { + return Err(sled::transaction::ConflictableTransactionError::Abort(())); + } + + // Get assignment + let mut assign_key = Vec::new(); + assign_key.extend_from_slice(&range_id.to_be_bytes()); + assign_key.extend_from_slice(&bit_position.to_be_bytes()); + + match assign.get(assign_key)? { + Some(value_bytes) => { + let value = String::from_utf8(value_bytes.to_vec()) + .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?; + Ok(Some(value)) + } + None => Ok(None), + } + }).map_err(|e| match e { + sled::transaction::TransactionError::Abort(()) => Error::BitOutOfRange(range_name.to_string(), bit_position), + sled::transaction::TransactionError::Storage(storage_err) => Error::StoreError(storage_err), + })?; + Ok(result) + } + + // List all assignments in a range + pub fn list_range(&self, range_name: &str) -> Result> { + // Get range info first + let range_id = match self.names.get(range_name)? { + Some(data) => { + if data.len() != 16 { + return Ok(Vec::new()); + } + let id_bytes: [u8; 8] = data[0..8].try_into() + .map_err(|_| Error::StoreError(sled::Error::Unsupported("Invalid range data".to_string())))?; + u64::from_be_bytes(id_bytes) + } + None => return Ok(Vec::new()), + }; + + let mut assignments = Vec::new(); + let prefix = range_id.to_be_bytes(); + + for result in self.assign.scan_prefix(&prefix) { + let (key, value) = result?; + if key.len() == 16 { // 8 bytes for range_id + 8 bytes for bit_position + let bit_position_bytes: [u8; 8] = key[8..16].try_into() + .map_err(|_| Error::StoreError(sled::Error::Unsupported("Invalid key format".to_string())))?; + let bit_position = u64::from_be_bytes(bit_position_bytes); + let value_str = String::from_utf8(value.to_vec())?; + assignments.push((bit_position, value_str)); + } + } + + assignments.sort_by_key(|&(bit_pos, _)| bit_pos); + Ok(assignments) + } + + // List all ranges + pub fn list_ranges(&self) -> Result> { + let mut ranges = Vec::new(); + + for result in self.names.iter() { + let (key, value) = result?; + if value.len() == 16 { + let name = String::from_utf8(key.to_vec())?; + let size_bytes: [u8; 8] = value[8..16].try_into() + .map_err(|_| Error::StoreError(sled::Error::Unsupported("Invalid size data".to_string())))?; + let size = u64::from_be_bytes(size_bytes); + ranges.push((name, size)); + } + } + + ranges.sort_by(|a, b| a.0.cmp(&b.0)); + Ok(ranges) + } + + // Unassign a value from the range (find by value and remove) + pub fn unassign(&self, range_name: &str, value: &str) -> Result { + // First find the assignment outside of transaction + let range_id = match self.names.get(range_name)? { + Some(data) => { + if data.len() != 16 { + return Ok(false); + } + let id_bytes: [u8; 8] = data[0..8].try_into() + .map_err(|_| Error::StoreError(sled::Error::Unsupported("Invalid range data".to_string())))?; + u64::from_be_bytes(id_bytes) + } + None => return Ok(false), + }; + + // Find the assignment with this value + let prefix = range_id.to_be_bytes(); + let mut found_bit = None; + + for result in self.assign.scan_prefix(&prefix) { + let (key, stored_value) = result?; + if key.len() == 16 { + let stored_value_str = String::from_utf8(stored_value.to_vec())?; + if stored_value_str == value { + let bit_position_bytes: [u8; 8] = key[8..16].try_into() + .map_err(|_| Error::StoreError(sled::Error::Unsupported("Invalid key format".to_string())))?; + found_bit = Some(u64::from_be_bytes(bit_position_bytes)); + break; + } + } + } + + let bit_position = match found_bit { + Some(pos) => pos, + None => return Ok(false), + }; + + // Now perform the removal in a transaction + let result = (&self.map, &self.assign).transaction(|(map, assign)| { + // Remove assignment + let mut assign_key = Vec::new(); + assign_key.extend_from_slice(&range_id.to_be_bytes()); + assign_key.extend_from_slice(&bit_position.to_be_bytes()); + assign.remove(assign_key)?; + + // Update bitmap + let mut bitmap = match map.get(&range_id.to_be_bytes())? { + Some(data) => data.to_vec(), + None => return Err(sled::transaction::ConflictableTransactionError::Abort(())), + }; + + let byte_index = (bit_position / 8) as usize; + let bit_index = bit_position % 8; + + if byte_index < bitmap.len() { + let mask = 1u8 << bit_index; + bitmap[byte_index] &= !mask; // Clear the bit + map.insert(&range_id.to_be_bytes(), bitmap)?; + } + + Ok(true) + }).map_err(|e| match e { + sled::transaction::TransactionError::Abort(()) => Error::ValueNotInRange(range_name.to_string(), value.to_string()), + sled::transaction::TransactionError::Storage(storage_err) => Error::StoreError(storage_err), + })?; + Ok(result) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::tempdir; + + fn create_test_store() -> Result { + let temp_dir = tempdir().unwrap(); + let db = sled::open(temp_dir.path())?; + RangeStore::open(&db) + } + + #[test] + fn test_define_range() -> Result<()> { + let store = create_test_store()?; + + // Define a range + store.define("test_range", 64)?; + + // Defining the same range again should not error + store.define("test_range", 64)?; + + Ok(()) + } + + #[test] + fn test_assign_and_get() -> Result<()> { + let store = create_test_store()?; + + // Define range first + store.define("test_range", 64)?; + + // Assign a value + let bit_pos = store.assign("test_range", "test_value")?; + assert_eq!(bit_pos, 0); // First assignment should be at position 0 + + // Get the value + let value = store.get("test_range", bit_pos)?; + assert_eq!(value, Some("test_value".to_string())); + + Ok(()) + } + + #[test] + fn test_multiple_assignments() -> Result<()> { + let store = create_test_store()?; + + // Define range + store.define("test_range", 64)?; + + // Assign multiple values + let pos1 = store.assign("test_range", "value1")?; + let pos2 = store.assign("test_range", "value2")?; + let pos3 = store.assign("test_range", "value3")?; + + assert_eq!(pos1, 0); + assert_eq!(pos2, 1); + assert_eq!(pos3, 2); + + // Verify all values + assert_eq!(store.get("test_range", 0)?, Some("value1".to_string())); + assert_eq!(store.get("test_range", 1)?, Some("value2".to_string())); + assert_eq!(store.get("test_range", 2)?, Some("value3".to_string())); + + Ok(()) + } + + #[test] + fn test_list_range() -> Result<()> { + let store = create_test_store()?; + + // Define range + store.define("test_range", 64)?; + + // Assign some values + store.assign("test_range", "alpha")?; + store.assign("test_range", "beta")?; + store.assign("test_range", "gamma")?; + + // List assignments + let assignments = store.list_range("test_range")?; + assert_eq!(assignments.len(), 3); + assert_eq!(assignments[0], (0, "alpha".to_string())); + assert_eq!(assignments[1], (1, "beta".to_string())); + assert_eq!(assignments[2], (2, "gamma".to_string())); + + Ok(()) + } + + #[test] + fn test_list_ranges() -> Result<()> { + let store = create_test_store()?; + + // Define multiple ranges + store.define("range_a", 32)?; + store.define("range_b", 64)?; + store.define("range_c", 128)?; + + // List all ranges + let ranges = store.list_ranges()?; + assert_eq!(ranges.len(), 3); + assert_eq!(ranges[0], ("range_a".to_string(), 32)); + assert_eq!(ranges[1], ("range_b".to_string(), 64)); + assert_eq!(ranges[2], ("range_c".to_string(), 128)); + + Ok(()) + } + + #[test] + fn test_unassign() -> Result<()> { + let store = create_test_store()?; + + // Define range + store.define("test_range", 64)?; + + // Assign values + store.assign("test_range", "value1")?; + store.assign("test_range", "value2")?; + store.assign("test_range", "value3")?; + + // Unassign middle value + let removed = store.unassign("test_range", "value2")?; + assert!(removed); + + // Verify it's gone + assert_eq!(store.get("test_range", 1)?, None); + + // Other values should still be there + assert_eq!(store.get("test_range", 0)?, Some("value1".to_string())); + assert_eq!(store.get("test_range", 2)?, Some("value3".to_string())); + + // Assign a new value - should reuse the freed slot + let new_pos = store.assign("test_range", "new_value")?; + assert_eq!(new_pos, 1); // Should reuse position 1 + + Ok(()) + } + + #[test] + fn test_range_full() -> Result<()> { + let store = create_test_store()?; + + // Define small range + store.define("small_range", 2)?; + + // Fill it up + store.assign("small_range", "value1")?; + store.assign("small_range", "value2")?; + + // Try to assign one more - should fail + let result = store.assign("small_range", "value3"); + assert!(result.is_err()); + + Ok(()) + } + + #[test] + fn test_undefined_range() -> Result<()> { + let store = create_test_store()?; + + // Try operations on undefined range + let result = store.assign("undefined_range", "value"); + assert!(result.is_err()); + + let value = store.get("undefined_range", 0)?; + assert_eq!(value, None); + + let assignments = store.list_range("undefined_range")?; + assert_eq!(assignments.len(), 0); + + let removed = store.unassign("undefined_range", "value")?; + assert!(!removed); + + Ok(()) + } +} diff --git a/crates/store/src/store.rs b/crates/store/src/store.rs index 2621f92..acef081 100644 --- a/crates/store/src/store.rs +++ b/crates/store/src/store.rs @@ -1,46 +1,52 @@ use crate::Result; #[derive(Debug, Clone)] pub struct Db { prefix: String, db: sled::Db } #[derive(Debug, Clone)] pub struct Store { db: Db, namespaces: crate::namespace::NamespaceStore, + ranges: crate::range::RangeStore, } impl Db { pub fn open(path: String, prefix: String) -> Result { let db = sled::open(path)?; Ok(Db { prefix, db }) } pub fn open_tree(&self, name: &str) -> Result { Ok(self.db.open_tree(self.tree_path(name))?) } pub fn tree_path(&self, name: &str) -> String { format!("t1/{}/{}", self.prefix, name) } } pub fn open() -> Result { let db = Db::open("libcollar_store".to_string(), "bonefire".to_string())?; Ok(Store { namespaces: crate::namespace::NamespaceStore::open(&db.db)?, + ranges: crate::range::RangeStore::open(&db.db)?, db: db, }) } impl Store { fn tree_path(&self, name: &str) -> String { self.db.tree_path(name) } pub fn namespaces(&self) -> &crate::namespace::NamespaceStore { &self.namespaces } + + pub fn ranges(&self) -> &crate::range::RangeStore { + &self.ranges + } }