diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..05df001 --- /dev/null +++ b/.env.example @@ -0,0 +1,37 @@ +# Server Configuration +SERVER_PORT=8000 +ASSETS_DIR=./assets + +# Command-line Tool Paths +PMTILES_CMD=pmtiles +BZIP2_CMD=bzip2 +FIND_CMD=find + +# Database Configuration +LOCALITYSRV_DB_PATH=./assets/whosonfirst-data-admin-latest.db +LOCALITYSRV_CID_DB_PATH=./assets/locality-cid-mappings.db +LOCALITYSRV_LOCALITIES_DIR=./assets/localities +WHOSEONFIRST_DB_URL=https://data.geocode.earth/wof/dist/sqlite/whosonfirst-data-admin-latest.db.bz2 + +# Protomaps Configuration +PROTOMAPS_BUILDS_URL=https://build-metadata.protomaps.dev/builds.json + +# Local Planet PMTiles Configuration +# Set this to the path of a local planet.pmtiles file to use it instead of downloading from remote +# Example: PLANET_PMTILES_PATH=/path/to/planet.pmtiles +# If not set or empty, the system will fetch the latest planet pmtiles from the remote URL +PLANET_PMTILES_PATH= + +# Target Countries (comma-separated, empty or ALL for all countries) +TARGET_COUNTRIES=AE,AF + +# Codex Node Configuration +CODEX_DATA_DIR=./.codex-data +CODEX_STORAGE_QUOTA_GB=100 +CODEX_DISCOVERY_PORT=8090 +CODEX_LISTEN_ADDRS=/ip4/0.0.0.0/tcp/8090,/ip4/127.0.0.1/tcp/8090 +LOG_LEVEL=info + +# Performance Settings +MAX_CONCURRENT_EXTRACTIONS=10 +DB_CONNECTION_POOL_SIZE=10 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0120a4b..855ee44 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ *.bz2 *.db *.pmtiles -.kilocode \ No newline at end of file +.kilocode +.env +.codex-data +.codex-test-data \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 35cbfb4..0b4b41b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,18 +17,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", - "zeroize", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -38,52 +26,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "amplify" -version = "4.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f7fb4ac7c881e54a8e7015e399b6112a2a5bc958b6c89ac510840ff20273b31" -dependencies = [ - "amplify_derive", - "amplify_num", - "ascii", - "getrandom 0.2.16", - "getrandom 0.3.3", - "wasm-bindgen", -] - -[[package]] -name = "amplify_derive" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a6309e6b8d89b36b9f959b7a8fa093583b94922a0f6438a24fb08936de4d428" -dependencies = [ - "amplify_syn", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "amplify_num" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99bcb75a2982047f733547042fc3968c0f460dfcf7d90b90dea3b2744580e9ad" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "amplify_syn" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7736fb8d473c0d83098b5bac44df6a561e20470375cd8bcae30516dc889fd62a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "android_system_properties" version = "0.1.5" @@ -93,12 +35,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - [[package]] name = "anstream" version = "0.6.21" @@ -156,190 +92,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "arti-client" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a79ca5ce63b36033a5ccbfbcc7f919cbd93db61708543aa5e2e4917856205e7" -dependencies = [ - "async-trait", - "cfg-if", - "derive-deftly 1.3.0", - "derive_builder_fork_arti", - "derive_more", - "educe", - "fs-mistrust", - "futures", - "hostname-validator", - "humantime", - "humantime-serde", - "libc", - "once_cell", - "postage", - "rand 0.9.2", - "safelog", - "serde", - "thiserror 2.0.17", - "time", - "tor-async-utils", - "tor-basic-utils", - "tor-chanmgr", - "tor-circmgr", - "tor-config", - "tor-config-path", - "tor-dircommon", - "tor-dirmgr", - "tor-error", - "tor-guardmgr", - "tor-hsclient", - "tor-hscrypto", - "tor-hsservice", - "tor-keymgr", - "tor-linkspec", - "tor-llcrypto", - "tor-memquota", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-rtcompat", - "tracing", - "void", -] - -[[package]] -name = "ascii" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" - -[[package]] -name = "asn1-rs" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror 2.0.17", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", - "synstructure", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "assert_matches" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" - -[[package]] -name = "async-compression" -version = "0.4.32" +name = "async-stream" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a89bce6054c720275ac2432fbba080a66a2106a44a1b804553930ca6909f4e0" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ - "compression-codecs", - "compression-core", + "async-stream-impl", "futures-core", - "futures-io", "pin-project-lite", ] [[package]] -name = "async-native-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec" -dependencies = [ - "futures-util", - "native-tls", - "thiserror 1.0.69", - "url", -] - -[[package]] -name = "async-trait" -version = "0.1.89" +name = "async-stream-impl" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "async_executors" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a982d2f86de6137cc05c9db9a915a19886c97911f9790d04f174cede74be01a5" -dependencies = [ - "blanket", - "futures-core", - "futures-task", - "futures-util", - "pin-project", - "rustc_version", - "tokio", -] - -[[package]] -name = "asynchronous-codec" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" -dependencies = [ - "bytes", - "futures-sink", - "futures-util", - "memchr", - "pin-project-lite", -] - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "atomic" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" -dependencies = [ - "bytemuck", + "syn", ] [[package]] @@ -354,58 +125,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "axum" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" -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", - "serde_core", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "sync_wrapper", - "tower-layer", - "tower-service", - "tracing", -] - [[package]] name = "backtrace" version = "0.3.76" @@ -421,12 +140,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base64" version = "0.22.1" @@ -434,100 +147,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" - -[[package]] -name = "bincode" -version = "2.0.1" +name = "bindgen" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "serde", - "unty", + "bitflags", + "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.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blanket" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0b121a9fe0df916e362fb3271088d071159cdf11db0e4182d02152850756eff" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bstr" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" -dependencies = [ - "memchr", - "regex-automata", - "serde", -] - [[package]] name = "bumpalo" version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" -[[package]] -name = "by_address" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" - -[[package]] -name = "bytemuck" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" - -[[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" @@ -535,16 +185,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] -name = "caret" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4d27042e727de6261ee6391b834c6e1adec7031a03228cc1a67f95a3d8f2202" - -[[package]] -name = "cast" -version = "0.3.0" +name = "bytesize" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +checksum = "c99fa31e08a43eaa5913ef68d7e01c37a2bdce6ed648168239ad33b7d30a9cd8" [[package]] name = "cc" @@ -553,11 +197,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb" dependencies = [ "find-msvc-tools", - "jobserver", - "libc", "shlex", ] +[[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.3" @@ -571,52 +222,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-link 0.2.1", ] [[package]] -name = "ciborium" -version = "0.2.2" +name = "clang-sys" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", + "glob", + "libc", + "libloading", ] [[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", - "zeroize", -] - -[[package]] -name = "clap" -version = "4.5.48" +name = "clap" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" dependencies = [ @@ -633,7 +259,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", ] [[package]] @@ -645,7 +271,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -655,14 +281,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] -name = "coarsetime" -version = "0.1.36" +name = "codex-bindings" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91849686042de1b41cd81490edc83afbcb0abe5a9b6f2c4114f23ce8cca1bcf4" +checksum = "a0d795cce718186cfe94c0b970127e9d3af3a1a354dc4c11191602528a98b38a" dependencies = [ + "bindgen", + "bytesize", + "cc", + "chrono", + "futures", "libc", - "wasix", - "wasm-bindgen", + "log", + "once_cell", + "pkg-config", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", ] [[package]] @@ -672,4655 +308,1532 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] -name = "compression-codecs" -version = "0.4.31" +name = "core-foundation" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8a506ec4b81c460798f572caead636d57d3d7e940f998160f52bd254bf2d23" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ - "compression-core", - "flate2", - "liblzma", - "zstd", - "zstd-safe", + "core-foundation-sys", + "libc", ] [[package]] -name = "compression-core" -version = "0.4.29" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "concurrent-queue" -version = "2.5.0" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "crossbeam-utils", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "const-oid" -version = "0.9.6" +name = "dotenvy" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] -name = "convert_case" -version = "0.7.1" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" -dependencies = [ - "unicode-segmentation", -] +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] -name = "cookie-factory" -version = "0.3.3" +name = "encoding_rs" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "futures", + "cfg-if", ] [[package]] -name = "core-foundation" -version = "0.9.4" +name = "env_logger" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ - "core-foundation-sys", - "libc", + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", ] [[package]] -name = "core-foundation-sys" -version = "0.8.7" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "cpufeatures" -version = "0.2.17" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", + "windows-sys 0.61.2", ] [[package]] -name = "crc32fast" -version = "1.5.0" +name = "fallible-iterator" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] -name = "criterion" -version = "0.7.0" +name = "fallible-streaming-iterator" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" -dependencies = [ - "anes", - "cast", - "ciborium", - "clap", - "criterion-plot", - "itertools 0.13.0", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_json", - "tinytemplate", - "walkdir", -] +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] -name = "criterion-cycles-per-byte" -version = "0.7.0" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f82e634fea1e2312dc41e6c0ca7444c5d6e7a1ccf3cf4b8de559831c3dcc271" -dependencies = [ - "cfg-if", - "criterion", -] +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "criterion-plot" -version = "0.6.0" +name = "find-msvc-tools" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" -dependencies = [ - "cast", - "itertools 0.13.0", -] +checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" [[package]] -name = "crossbeam-deque" -version = "0.8.6" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "crossbeam-epoch" -version = "0.9.18" +name = "foldhash" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] -name = "crossbeam-queue" -version = "0.3.12" +name = "foreign-types" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "crossbeam-utils", + "foreign-types-shared", ] [[package]] -name = "crossbeam-utils" -version = "0.8.21" +name = "foreign-types-shared" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] -name = "crunchy" -version = "0.2.4" +name = "form_urlencoded" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] [[package]] -name = "crypto-bigint" -version = "0.5.5" +name = "futures" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] -name = "crypto-common" -version = "0.1.6" +name = "futures-channel" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ - "generic-array", - "typenum", + "futures-core", + "futures-sink", ] [[package]] -name = "ctr" -version = "0.9.2" +name = "futures-core" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] -name = "curve25519-dalek" -version = "4.1.3" +name = "futures-executor" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] -name = "darling" -version = "0.14.4" +name = "futures-sink" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] -name = "darling" -version = "0.21.3" +name = "futures-task" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" -dependencies = [ - "darling_core 0.21.3", - "darling_macro 0.21.3", -] +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] -name = "darling_core" -version = "0.14.4" +name = "futures-util" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", ] [[package]] -name = "darling_core" -version = "0.21.3" +name = "getrandom" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.11.1", - "syn 2.0.106", + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] -name = "darling_macro" -version = "0.14.4" +name = "getrandom" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ - "darling_core 0.14.4", - "quote", - "syn 1.0.109", + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.7+wasi-0.2.4", ] [[package]] -name = "darling_macro" -version = "0.21.3" +name = "gimli" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" -dependencies = [ - "darling_core 0.21.3", - "quote", - "syn 2.0.106", -] +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] -name = "data-encoding" -version = "2.9.0" +name = "glob" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] -name = "der" -version = "0.7.10" +name = "h2" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", ] [[package]] -name = "der-parser" -version = "10.0.0" +name = "hashbrown" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "asn1-rs", - "cookie-factory", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", + "foldhash", ] [[package]] -name = "deranged" -version = "0.5.4" +name = "hashbrown" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" -dependencies = [ - "powerfmt", - "serde_core", -] +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] -name = "derive-deftly" -version = "0.14.6" +name = "hashlink" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ea84d0109517cc2253d4a679bdda1e8989e9bd86987e9e4f75ffdda0095fd1" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "derive-deftly-macros 0.14.6", - "heck", + "hashbrown 0.15.5", ] [[package]] -name = "derive-deftly" -version = "1.3.0" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d308ebe4b10924331bd079044b418da7b227d724d3e2408567a47ad7c3da2a0" -dependencies = [ - "derive-deftly-macros 1.3.0", - "heck", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "derive-deftly-macros" -version = "0.14.6" +name = "hermit-abi" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357422a457ccb850dc8f1c1680e0670079560feaad6c2e247e3f345c4fab8a3f" -dependencies = [ - "heck", - "indexmap 2.11.4", - "itertools 0.14.0", - "proc-macro-crate", - "proc-macro2", - "quote", - "sha3", - "strum", - "syn 2.0.106", - "void", -] +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] -name = "derive-deftly-macros" -version = "1.3.0" +name = "http" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5f2b7218a51c827a11d22d1439b598121fac94bf9b99452e4afffe512d78c9" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ - "heck", - "indexmap 2.11.4", - "itertools 0.14.0", - "proc-macro-crate", - "proc-macro2", - "quote", - "sha3", - "strum", - "syn 2.0.106", - "void", + "bytes", + "fnv", + "itoa", ] [[package]] -name = "derive_builder_core_fork_arti" -version = "0.11.2" +name = "http-body" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c1b715c79be6328caa9a5e1a387a196ea503740f0722ec3dd8f67a9e72314d" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ - "darling 0.14.4", - "proc-macro2", - "quote", - "syn 1.0.109", + "bytes", + "http", ] [[package]] -name = "derive_builder_fork_arti" -version = "0.11.2" +name = "http-body-util" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eae24d595f4d0ecc90a9a5a6d11c2bd8dafe2375ec4a1ec63250e5ade7d228" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ - "derive_builder_macro_fork_arti", + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", ] [[package]] -name = "derive_builder_macro_fork_arti" -version = "0.11.2" +name = "httparse" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69887769a2489cd946bf782eb2b1bb2cb7bc88551440c94a765d4f040c08ebf3" -dependencies = [ - "derive_builder_core_fork_arti", - "syn 1.0.109", -] +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] -name = "derive_more" -version = "2.0.1" +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "hyper" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ - "derive_more-impl", + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", ] [[package]] -name = "derive_more-impl" -version = "2.0.1" +name = "hyper-rustls" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "syn 2.0.106", - "unicode-xid", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", ] [[package]] -name = "digest" -version = "0.10.7" +name = "hyper-tls" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", ] [[package]] -name = "directories" -version = "6.0.0" +name = "hyper-util" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ - "dirs-sys", + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", ] [[package]] -name = "dirs" -version = "6.0.0" +name = "iana-time-zone" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ - "dirs-sys", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", ] [[package]] -name = "dirs-sys" -version = "0.5.0" +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.61.2", + "cc", ] [[package]] -name = "displaydoc" -version = "0.2.5" +name = "icu_collections" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", ] [[package]] -name = "dotenvy" -version = "0.15.7" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] [[package]] -name = "downcast-rs" -version = "2.0.2" +name = "icu_normalizer" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] [[package]] -name = "dyn-clone" -version = "1.0.20" +name = "icu_normalizer_data" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] -name = "ecdsa" -version = "0.16.9" +name = "icu_properties" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", ] [[package]] -name = "ed25519" -version = "2.2.3" +name = "icu_properties_data" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] -name = "ed25519-dalek" -version = "2.2.0" +name = "icu_provider" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ - "curve25519-dalek", - "ed25519", - "merlin", - "rand_core 0.6.4", - "serde", - "sha2", - "subtle", - "zeroize", + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", ] [[package]] -name = "educe" -version = "0.4.23" +name = "idna" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ - "enum-ordinalize", - "proc-macro2", - "quote", - "syn 1.0.109", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "either" -version = "1.15.0" +name = "idna_adapter" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] [[package]] -name = "elliptic-curve" -version = "0.13.8" +name = "indexmap" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", + "equivalent", + "hashbrown 0.16.0", ] [[package]] -name = "encoding_rs" -version = "0.8.35" +name = "io-uring" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ + "bitflags", "cfg-if", + "libc", ] [[package]] -name = "enum-ordinalize" -version = "3.1.15" +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" dependencies = [ - "num-bigint", - "num-traits", - "proc-macro2", - "quote", - "syn 2.0.106", + "memchr", + "serde", ] [[package]] -name = "enum_dispatch" -version = "0.3.13" +name = "is-terminal" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.106", + "hermit-abi", + "libc", + "windows-sys 0.61.2", ] [[package]] -name = "equivalent" -version = "1.0.2" +name = "is_terminal_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] -name = "errno" -version = "0.3.14" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ - "libc", - "windows-sys 0.61.2", + "either", ] [[package]] -name = "event-listener" -version = "5.4.1" +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.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", + "once_cell", + "wasm-bindgen", ] [[package]] -name = "fallible-iterator" -version = "0.3.0" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" +name = "libc" +version = "0.2.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] -name = "fastrand" -version = "2.3.0" +name = "libloading" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] [[package]] -name = "ff" -version = "0.13.1" +name = "libsqlite3-sys" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" dependencies = [ - "rand_core 0.6.4", - "subtle", + "cc", + "pkg-config", + "vcpkg", ] [[package]] -name = "fiat-crypto" -version = "0.2.9" +name = "linux-raw-sys" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] -name = "figment" -version = "0.10.19" +name = "litemap" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "localitysrv" +version = "0.2.0" dependencies = [ - "atomic 0.6.1", + "anyhow", + "clap", + "codex-bindings", + "dotenvy", + "env_logger", + "futures", + "reqwest", + "rusqlite", "serde", - "toml 0.8.23", - "uncased", - "version_check", + "serde_json", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tokio-test", + "tracing", + "tracing-subscriber", ] [[package]] -name = "filetime" -version = "0.2.26" +name = "lock_api" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.60.2", + "scopeguard", ] [[package]] -name = "find-msvc-tools" -version = "0.1.3" +name = "log" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] -name = "flate2" -version = "1.1.4" +name = "memchr" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" -dependencies = [ - "crc32fast", - "miniz_oxide", -] +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] -name = "fluid-let" -version = "1.0.0" +name = "mime" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749cff877dc1af878a0b31a41dd221a753634401ea0ef2f87b62d3171522485a" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "fnv" -version = "1.0.7" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] -name = "foldhash" -version = "0.1.5" +name = "miniz_oxide" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] [[package]] -name = "foreign-types" -version = "0.3.2" +name = "mio" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ - "foreign-types-shared", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] -name = "foreign-types-shared" -version = "0.1.1" +name = "native-tls" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] [[package]] -name = "form_urlencoded" -version = "1.2.2" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "percent-encoding", + "memchr", + "minimal-lexical", ] [[package]] -name = "fs-mistrust" -version = "0.12.0" +name = "nu-ansi-term" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a157b06319bb4868718fd20177a0d3373d465e429d89cd0ee493d9f5918902" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "derive_builder_fork_arti", - "dirs", - "libc", - "pwd-grp", - "serde", - "thiserror 2.0.17", - "walkdir", + "windows-sys 0.52.0", ] [[package]] -name = "fslock" -version = "0.2.1" +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "libc", - "winapi", + "autocfg", ] [[package]] -name = "fslock-arti-fork" -version = "0.2.0" +name = "object" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21bd626aaab7b904b20bef6d9e06298914a0c8d9fb8b010483766b2e532791" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ - "libc", - "winapi", + "memchr", ] [[package]] -name = "fslock-guard" -version = "0.4.0" +name = "once_cell" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9a3b01c785eb3b4ef08373225d56b8a85ff2f9e88a71adaf7f97aa9519c1707" -dependencies = [ - "fslock-arti-fork", - "thiserror 2.0.17", - "winapi", -] +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "funty" -version = "2.0.0" +name = "once_cell_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] -name = "futures" -version = "0.3.31" +name = "openssl" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", ] [[package]] -name = "futures-channel" -version = "0.3.31" +name = "openssl-macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "futures-core", - "futures-sink", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "futures-core" -version = "0.3.31" +name = "openssl-probe" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] -name = "futures-executor" -version = "0.3.31" +name = "openssl-sys" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ - "futures-core", - "futures-task", - "futures-util", + "cc", + "libc", + "pkg-config", + "vcpkg", ] [[package]] -name = "futures-io" -version = "0.3.31" +name = "parking_lot" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] [[package]] -name = "futures-macro" -version = "0.3.31" +name = "parking_lot_core" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", ] [[package]] -name = "futures-sink" -version = "0.3.31" +name = "percent-encoding" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] -name = "futures-task" -version = "0.3.31" +name = "pin-project-lite" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] -name = "futures-util" -version = "0.3.31" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] -name = "generic-array" -version = "0.14.7" +name = "potential_utf" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ - "typenum", - "version_check", - "zeroize", + "zerovec", ] [[package]] -name = "getrandom" -version = "0.2.16" +name = "prettyplease" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "wasm-bindgen", + "proc-macro2", + "syn", ] [[package]] -name = "getrandom" -version = "0.3.3" +name = "proc-macro2" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasi 0.14.7+wasi-0.2.4", - "wasm-bindgen", + "unicode-ident", ] [[package]] -name = "getset" -version = "0.1.6" +name = "quote" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ - "proc-macro-error2", "proc-macro2", - "quote", - "syn 2.0.106", ] [[package]] -name = "gimli" -version = "0.32.3" +name = "r-efi" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "glob-match" -version = "0.2.1" +name = "redox_syscall" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] [[package]] -name = "group" -version = "0.13.0" +name = "regex" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] -name = "growable-bloom-filter" -version = "2.1.1" +name = "regex-automata" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d174ccb4ba660d431329e7f0797870d0a4281e36353ec4b4a3c5eab6c2cfb6f1" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" dependencies = [ - "serde", - "serde_bytes", - "serde_derive", - "xxhash-rust", + "aho-corasick", + "memchr", + "regex-syntax", ] [[package]] -name = "h2" -version = "0.4.12" +name = "regex-syntax" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + +[[package]] +name = "reqwest" +version = "0.12.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ - "atomic-waker", + "base64", "bytes", - "fnv", + "encoding_rs", "futures-core", - "futures-sink", + "futures-util", + "h2", "http", - "indexmap 2.11.4", - "slab", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", "tokio", + "tokio-native-tls", "tokio-util", - "tracing", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", ] [[package]] -name = "half" -version = "2.6.0" +name = "ring" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ + "cc", "cfg-if", - "crunchy", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", ] [[package]] -name = "hashbrown" -version = "0.12.3" +name = "rusqlite" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] [[package]] -name = "hashbrown" -version = "0.15.5" +name = "rustc-demangle" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] -name = "hashbrown" -version = "0.16.0" +name = "rustc-hash" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] -name = "hashlink" -version = "0.10.0" +name = "rustix" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "hashbrown 0.15.5", + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", ] [[package]] -name = "headers" -version = "0.4.1" +name = "rustls" +version = "0.23.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" dependencies = [ - "base64", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", ] [[package]] -name = "headers-core" -version = "0.3.0" +name = "rustls-pki-types" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ - "http", + "zeroize", ] [[package]] -name = "heck" -version = "0.5.0" +name = "rustls-webpki" +version = "0.103.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] [[package]] -name = "hex" -version = "0.4.3" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] -name = "hkdf" -version = "0.12.4" +name = "ryu" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] -name = "hmac" -version = "0.12.1" +name = "schannel" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "digest", + "windows-sys 0.61.2", ] [[package]] -name = "hostname-validator" -version = "1.1.1" +name = "scopeguard" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "http" -version = "1.3.1" +name = "security-framework" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bytes", - "fnv", - "itoa", + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", ] [[package]] -name = "http-body" -version = "1.0.1" +name = "security-framework-sys" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ - "bytes", - "http", + "core-foundation-sys", + "libc", ] [[package]] -name = "http-body-util" -version = "0.1.3" +name = "serde" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 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 = "humantime" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" - -[[package]] -name = "humantime-serde" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" -dependencies = [ - "humantime", - "serde", -] - -[[package]] -name = "hyper" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core 0.62.2", -] - -[[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 = "icu_collections" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" - -[[package]] -name = "icu_properties" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "potential_utf", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" - -[[package]] -name = "icu_provider" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" -dependencies = [ - "displaydoc", - "icu_locale_core", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" -dependencies = [ - "equivalent", - "hashbrown 0.16.0", - "serde", - "serde_core", -] - -[[package]] -name = "inotify" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" -dependencies = [ - "bitflags 2.9.4", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "generic-array", -] - -[[package]] -name = "inventory" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" -dependencies = [ - "rustversion", -] - -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - -[[package]] -name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - -[[package]] -name = "iri-string" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" -dependencies = [ - "memchr", - "serde", -] - -[[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 = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.3", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "k12" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dc5fdb62af2f520116927304f15d25b3c2667b4817b90efdc045194c912c54" -dependencies = [ - "digest", - "sha3", -] - -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "kqueue" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "libc" -version = "0.2.176" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" - -[[package]] -name = "liblzma" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c36d08cad03a3fbe2c4e7bb3a9e84c57e4ee4135ed0b065cade3d98480c648" -dependencies = [ - "liblzma-sys", -] - -[[package]] -name = "liblzma-sys" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b9596486f6d60c3bbe644c0e1be1aa6ccc472ad630fe8927b456973d7cb736" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "libm" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" - -[[package]] -name = "libredox" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" -dependencies = [ - "bitflags 2.9.4", - "libc", - "redox_syscall", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - -[[package]] -name = "litemap" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" - -[[package]] -name = "localitysrv" -version = "0.1.0" -dependencies = [ - "anyhow", - "arti-client", - "axum", - "clap", - "dotenvy", - "futures", - "headers", - "hyper", - "hyper-util", - "regex", - "reqwest", - "rusqlite", - "safelog", - "serde", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tokio-util", - "tor-cell", - "tor-hsservice", - "tor-proto", - "tower", - "tower-http", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" - -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - -[[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.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "memmap2" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" -dependencies = [ - "libc", -] - -[[package]] -name = "merlin" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" -dependencies = [ - "byteorder", - "keccak", - "rand_core 0.6.4", - "zeroize", -] - -[[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.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" -dependencies = [ - "libc", - "log", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", -] - -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[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 = "nonany" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b8866ec53810a9a4b3d434a29801e78c707430a9ae11c2db4b8b62bb9675a0" - -[[package]] -name = "notify" -version = "8.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" -dependencies = [ - "bitflags 2.9.4", - "inotify", - "kqueue", - "libc", - "log", - "mio", - "notify-types", - "walkdir", - "windows-sys 0.60.2", -] - -[[package]] -name = "notify-types" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" - -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_enum" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" -dependencies = [ - "num_enum_derive", - "rustversion", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "objc2-core-foundation" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" -dependencies = [ - "bitflags 2.9.4", -] - -[[package]] -name = "objc2-io-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" -dependencies = [ - "libc", - "objc2-core-foundation", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -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 = "oneshot-fused-workaround" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5480ab52bd005e9f14e3071d0227bfa204e16a496a719c58bfa013f880b41593" -dependencies = [ - "futures", -] - -[[package]] -name = "oorandom" -version = "11.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" - -[[package]] -name = "openssl" -version = "0.10.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-src" -version = "300.5.3+3.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6bad8cd0233b63971e232cc9c5e83039375b8586d2312f31fda85db8f888c2" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" -dependencies = [ - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "os_str_bytes" -version = "6.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" -dependencies = [ - "memchr", -] - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p521" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" -dependencies = [ - "base16ct", - "ecdsa", - "elliptic-curve", - "primeorder", - "rand_core 0.6.4", - "sha2", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link 0.2.1", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "phf" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" -dependencies = [ - "phf_macros", - "phf_shared", - "serde", -] - -[[package]] -name = "phf_generator" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" -dependencies = [ - "fastrand", - "phf_shared", -] - -[[package]] -name = "phf_macros" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "phf_shared" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[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 = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "postage" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" -dependencies = [ - "atomic 0.5.3", - "crossbeam-queue", - "futures", - "parking_lot", - "pin-project", - "static_assertions", - "thiserror 1.0.69", -] - -[[package]] -name = "potential_utf" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "priority-queue" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93980406f12d9f8140ed5abe7155acb10bb1e69ea55c88960b9c2f117445ef96" -dependencies = [ - "equivalent", - "indexmap 2.11.4", - "serde", -] - -[[package]] -name = "proc-macro-crate" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" -dependencies = [ - "toml_edit 0.23.6", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "proc-macro2" -version = "1.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "pwd-grp" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94fdf3867b7f2889a736f0022ea9386766280d2cca4bdbe41629ada9e4f3b8f" -dependencies = [ - "derive-deftly 0.14.6", - "libc", - "paste", - "thiserror 1.0.69", -] - -[[package]] -name = "quote" -version = "1.0.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.3", -] - -[[package]] -name = "rand_jitter" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16df48f071248e67b8fc5e866d9448d45c08ad8b672baaaf796e2f15e606ff0" -dependencies = [ - "libc", - "rand_core 0.9.3", - "winapi", -] - -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "rdrand" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92195228612ac8eed47adbc2ed0f04e513a4ccb98175b6f2bd04d963b533655" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.9.4", -] - -[[package]] -name = "redox_users" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror 2.0.17", -] - -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "regex" -version = "1.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" - -[[package]] -name = "reqwest" -version = "0.12.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "js-sys", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-native-tls", - "tokio-util", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", -] - -[[package]] -name = "retry-error" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b295404fa4a9e1e63537ccbd4e4b6309d9688bd70608ddc16d3b8af0389a673a" - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.16", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rsa" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "sha2", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[package]] -name = "rusqlite" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" -dependencies = [ - "bitflags 2.9.4", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", - "time", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - -[[package]] -name = "rustix" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" -dependencies = [ - "bitflags 2.9.4", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" -dependencies = [ - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" -dependencies = [ - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] -name = "safelog" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e75b0880210c750d9189aa2d1ef94075a5500ccd9e7e98ad868e017c17c4a4bc" -dependencies = [ - "derive_more", - "educe", - "either", - "fluid-let", - "thiserror 2.0.17", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "sanitize-filename" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc984f4f9ceb736a7bb755c3e3bd17dc56370af2600c9780dcc48c66453da34d" -dependencies = [ - "regex", -] - -[[package]] -name = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "schemars" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "schemars" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.9.4", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde_bytes" -version = "0.11.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "serde_ignored" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115dffd5f3853e06e746965a20dcbae6ee747ae30b543d91b0e089668bb07798" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] -name = "serde_json" -version = "1.0.145" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", - "serde_core", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_spanned" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" -dependencies = [ - "serde_core", -] - -[[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 = "serde_with" -version = "3.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5" -dependencies = [ - "base64", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.11.4", - "schemars 0.9.0", - "schemars 1.0.4", - "serde_core", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27" -dependencies = [ - "darling 0.21.3", - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - -[[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 = "shellexpand" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" -dependencies = [ - "bstr", - "dirs", - "os_str_bytes", -] - -[[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.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "slotmap" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" -dependencies = [ - "serde", - "version_check", -] - -[[package]] -name = "slotmap-careful" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d866fb978c1cf6d71abde4dce1905369edd0d0028ff9bc55e2431b83df7a36e8" -dependencies = [ - "paste", - "serde", - "slotmap", - "thiserror 2.0.17", - "void", -] - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "ssh-cipher" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" -dependencies = [ - "cipher", - "ssh-encoding", -] - -[[package]] -name = "ssh-encoding" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" -dependencies = [ - "base64ct", - "pem-rfc7468", - "sha2", -] - -[[package]] -name = "ssh-key" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3" -dependencies = [ - "num-bigint-dig", - "p256", - "p384", - "p521", - "rand_core 0.6.4", - "rsa", - "sec1", - "sha2", - "signature", - "ssh-cipher", - "ssh-encoding", - "subtle", - "zeroize", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" -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" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "sysinfo" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d" -dependencies = [ - "libc", - "memchr", - "ntapi", - "objc2-core-foundation", - "objc2-io-kit", - "windows", -] - -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags 2.9.4", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" -dependencies = [ - "fastrand", - "getrandom 0.3.3", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", -] - -[[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 2.0.106", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "time" -version = "0.3.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" - -[[package]] -name = "time-macros" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", + "serde_core", + "serde_derive", ] [[package]] -name = "tokio" -version = "1.47.1" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ - "backtrace", - "bytes", - "io-uring", - "libc", - "mio", - "pin-project-lite", - "signal-hook-registry", - "slab", - "socket2", - "tokio-macros", - "windows-sys 0.59.0", + "serde_derive", ] [[package]] -name = "tokio-macros" -version = "2.5.0" +name = "serde_derive" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", -] - -[[package]] -name = "toml" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" -dependencies = [ - "indexmap 2.11.4", - "serde_core", - "serde_spanned 1.0.2", - "toml_datetime 0.7.2", - "toml_parser", - "toml_writer", - "winnow", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_datetime" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap 2.11.4", - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_write", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.23.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" -dependencies = [ - "indexmap 2.11.4", - "toml_datetime 0.7.2", - "toml_parser", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" -dependencies = [ - "winnow", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - -[[package]] -name = "toml_writer" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109" - -[[package]] -name = "tor-async-utils" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad5e568ad4e025a68aa0395a146247609dd5b6d8c2141255f5e4f367e7fda8a" -dependencies = [ - "derive-deftly 1.3.0", - "educe", - "futures", - "oneshot-fused-workaround", - "pin-project", - "postage", - "thiserror 2.0.17", - "void", -] - -[[package]] -name = "tor-basic-utils" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30122645feee76f76ba1ad011b316a2b135d44a00c45ed9c14af58b32ad93b69" -dependencies = [ - "derive_more", - "hex", - "itertools 0.14.0", - "libc", - "paste", - "rand 0.9.2", - "rand_chacha 0.9.0", - "serde", - "slab", - "smallvec", - "thiserror 2.0.17", -] - -[[package]] -name = "tor-bytes" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc7fb465ba671ee1486d8bd1e0a8f546887c2ce034004c4c9b03a6227e1c381" -dependencies = [ - "bytes", - "derive-deftly 1.3.0", - "digest", - "educe", - "getrandom 0.3.3", - "safelog", - "thiserror 2.0.17", - "tor-error", - "tor-llcrypto", - "zeroize", -] - -[[package]] -name = "tor-cell" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ba1b43f22fab2daee3e0c902f1455b3aed8e086b2d83d8c60b36523b173d25" -dependencies = [ - "amplify", - "bitflags 2.9.4", - "bytes", - "caret", - "derive-deftly 1.3.0", - "derive_more", - "educe", - "itertools 0.14.0", - "paste", - "rand 0.9.2", - "smallvec", - "thiserror 2.0.17", - "tor-basic-utils", - "tor-bytes", - "tor-cert", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-memquota", - "tor-protover", - "tor-units", - "void", -] - -[[package]] -name = "tor-cert" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e63e2db09b6d6d3453f63d7d55796c9b10a7cd2bcc14e553666b1f3a84df66" -dependencies = [ - "caret", - "derive_builder_fork_arti", - "derive_more", - "digest", - "thiserror 2.0.17", - "tor-bytes", - "tor-checkable", - "tor-llcrypto", -] - -[[package]] -name = "tor-chanmgr" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd6924b1716b7d071221087e18eb911ff8331eca4bc2d896f2a03864ff67f2c" -dependencies = [ - "async-trait", - "caret", - "derive_builder_fork_arti", - "derive_more", - "educe", - "futures", - "oneshot-fused-workaround", - "postage", - "rand 0.9.2", - "safelog", - "serde", - "thiserror 2.0.17", - "tor-async-utils", - "tor-basic-utils", - "tor-cell", - "tor-config", - "tor-error", - "tor-keymgr", - "tor-linkspec", - "tor-llcrypto", - "tor-memquota", - "tor-netdir", - "tor-proto", - "tor-rtcompat", - "tor-socksproto", - "tor-units", - "tracing", - "void", -] - -[[package]] -name = "tor-checkable" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9839e9bb302f17447c350e290bb107084aca86c640882a91522f2059f6a686" -dependencies = [ - "humantime", - "signature", - "thiserror 2.0.17", - "tor-llcrypto", + "syn", ] [[package]] -name = "tor-circmgr" -version = "0.35.0" +name = "serde_json" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86ed519745136c7d90bb42efe4786dc7aa7548b92d9091ec8237cd16b9c12f" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "amplify", - "async-trait", - "cfg-if", - "derive-deftly 1.3.0", - "derive_builder_fork_arti", - "derive_more", - "downcast-rs", - "dyn-clone", - "educe", - "futures", - "humantime-serde", - "itertools 0.14.0", - "once_cell", - "oneshot-fused-workaround", - "pin-project", - "rand 0.9.2", - "retry-error", - "safelog", + "itoa", + "memchr", + "ryu", "serde", - "thiserror 2.0.17", - "tor-async-utils", - "tor-basic-utils", - "tor-cell", - "tor-chanmgr", - "tor-config", - "tor-dircommon", - "tor-error", - "tor-guardmgr", - "tor-linkspec", - "tor-memquota", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-relay-selection", - "tor-rtcompat", - "tor-units", - "tracing", - "void", - "weak-table", + "serde_core", ] [[package]] -name = "tor-config" -version = "0.35.0" +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb15df773842025010d885fbe862062ebaa342b799f9716273eaf733b92f2f45" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ - "amplify", - "cfg-if", - "derive-deftly 1.3.0", - "derive_builder_fork_arti", - "educe", - "either", - "figment", - "fs-mistrust", - "futures", - "itertools 0.14.0", - "notify", - "paste", - "postage", - "regex", + "form_urlencoded", + "itoa", + "ryu", "serde", - "serde-value", - "serde_ignored", - "strum", - "thiserror 2.0.17", - "toml 0.9.7", - "tor-basic-utils", - "tor-error", - "tor-rtcompat", - "tracing", - "void", ] [[package]] -name = "tor-config-path" -version = "0.35.0" +name = "sharded-slab" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c80d2784120508b5374a979cc0f6be0177ed870d176b0b31c94cf822200091dc" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ - "directories", - "serde", - "shellexpand", - "thiserror 2.0.17", - "tor-error", - "tor-general-addr", + "lazy_static", ] [[package]] -name = "tor-consdiff" -version = "0.35.0" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1690438c1fc778fc7c89c132e529365b1430d6afe03aeecbc2508324807bf0b" -dependencies = [ - "digest", - "hex", - "thiserror 2.0.17", - "tor-llcrypto", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "tor-dirclient" -version = "0.35.0" +name = "signal-hook-registry" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e730873fdc4b7f9545472c0d1cf0c43a7e89d6c996c234b6b548163010284c" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ - "async-compression", - "base64ct", - "derive_more", - "futures", - "hex", - "http", - "httparse", - "httpdate", - "itertools 0.14.0", - "memchr", - "thiserror 2.0.17", - "tor-circmgr", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-netdoc", - "tor-proto", - "tor-rtcompat", - "tracing", + "libc", ] [[package]] -name = "tor-dircommon" -version = "0.35.0" +name = "slab" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b60043697f94ec228f4fb6d30834a037774f2f3c2cdb0bdb805248f46b5320e" -dependencies = [ - "base64ct", - "derive_builder_fork_arti", - "getset", - "humantime", - "humantime-serde", - "serde", - "tor-basic-utils", - "tor-checkable", - "tor-config", - "tor-linkspec", - "tor-llcrypto", - "tor-netdoc", - "tracing", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] -name = "tor-dirmgr" -version = "0.35.0" +name = "smallvec" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f5e21a574acb35dd1a32960b10cb184db2e2ffbb4007abd3515951ce09d0f2" -dependencies = [ - "async-trait", - "base64ct", - "derive_builder_fork_arti", - "derive_more", - "digest", - "educe", - "event-listener", - "fs-mistrust", - "fslock", - "futures", - "hex", - "humantime", - "humantime-serde", - "itertools 0.14.0", - "memmap2", - "oneshot-fused-workaround", - "paste", - "postage", - "rand 0.9.2", - "rusqlite", - "safelog", - "scopeguard", - "serde", - "serde_json", - "signature", - "static_assertions", - "strum", - "thiserror 2.0.17", - "time", - "tor-async-utils", - "tor-basic-utils", - "tor-checkable", - "tor-circmgr", - "tor-config", - "tor-consdiff", - "tor-dirclient", - "tor-dircommon", - "tor-error", - "tor-guardmgr", - "tor-llcrypto", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-rtcompat", - "tracing", -] +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "tor-error" -version = "0.35.0" +name = "socket2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d766a5d11ddad7946cf8357ce7a1e948abdc3ad3ef06ed23f35af522dc089c" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ - "derive_more", - "futures", - "paste", - "retry-error", - "static_assertions", - "strum", - "thiserror 2.0.17", - "tracing", - "void", + "libc", + "windows-sys 0.59.0", ] [[package]] -name = "tor-general-addr" -version = "0.35.0" +name = "stable_deref_trait" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42cb5b5aec0584db2fba4a88c4e08fb09535ef61e4ef5674315a89e69ec31a2" -dependencies = [ - "derive_more", - "thiserror 2.0.17", - "void", -] +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "tor-guardmgr" -version = "0.35.0" +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0585a83a4c56b4f31f6fa2965e2f9c490c9f4d29fba2fedb5a9ee71009f793c0" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ - "amplify", - "base64ct", - "derive-deftly 1.3.0", - "derive_builder_fork_arti", - "derive_more", - "dyn-clone", - "educe", - "futures", - "humantime", - "humantime-serde", - "itertools 0.14.0", - "num_enum", - "oneshot-fused-workaround", - "pin-project", - "postage", - "rand 0.9.2", - "safelog", - "serde", - "strum", - "thiserror 2.0.17", - "tor-async-utils", - "tor-basic-utils", - "tor-config", - "tor-dircommon", - "tor-error", - "tor-linkspec", - "tor-llcrypto", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-relay-selection", - "tor-rtcompat", - "tor-units", - "tracing", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "tor-hsclient" -version = "0.35.0" +name = "sync_wrapper" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c0438676badc6265d6f34ce91c9b50fdb4dfe89ab85795f013fbfa80614304" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ - "async-trait", - "derive-deftly 1.3.0", - "derive_more", - "educe", - "either", - "futures", - "itertools 0.14.0", - "oneshot-fused-workaround", - "postage", - "rand 0.9.2", - "retry-error", - "safelog", - "slotmap-careful", - "strum", - "thiserror 2.0.17", - "tor-async-utils", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-checkable", - "tor-circmgr", - "tor-config", - "tor-dirclient", - "tor-error", - "tor-hscrypto", - "tor-keymgr", - "tor-linkspec", - "tor-llcrypto", - "tor-memquota", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-rtcompat", - "tracing", + "futures-core", ] [[package]] -name = "tor-hscrypto" -version = "0.35.0" +name = "synstructure" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9ee6e0dbec9ba11c3d046181a42dd4759e108de38e2b5927689edbdc458a51" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "cipher", - "data-encoding", - "derive-deftly 1.3.0", - "derive_more", - "digest", - "hex", - "humantime", - "itertools 0.14.0", - "paste", - "rand 0.9.2", - "safelog", - "serde", - "signature", - "subtle", - "thiserror 2.0.17", - "tor-basic-utils", - "tor-bytes", - "tor-error", - "tor-key-forge", - "tor-llcrypto", - "tor-memquota", - "tor-units", - "void", - "zeroize", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "tor-hsservice" -version = "0.35.0" +name = "system-configuration" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff555a245c3faf569a2bc8943e1d8463b1b3090fd5adc3c3f7a0c33c1dde4e5" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "amplify", - "async-trait", - "base64ct", - "cfg-if", - "derive-deftly 1.3.0", - "derive_builder_fork_arti", - "derive_more", - "digest", - "educe", - "fs-mistrust", - "futures", - "growable-bloom-filter", - "hex", - "humantime", - "itertools 0.14.0", - "k12", - "once_cell", - "oneshot-fused-workaround", - "postage", - "rand 0.9.2", - "rand_core 0.9.3", - "retry-error", - "safelog", - "serde", - "serde_with", - "strum", - "thiserror 2.0.17", - "tor-async-utils", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-circmgr", - "tor-config", - "tor-config-path", - "tor-dirclient", - "tor-error", - "tor-hscrypto", - "tor-keymgr", - "tor-linkspec", - "tor-llcrypto", - "tor-log-ratelim", - "tor-netdir", - "tor-netdoc", - "tor-persist", - "tor-proto", - "tor-protover", - "tor-relay-selection", - "tor-rtcompat", - "tracing", - "void", + "bitflags", + "core-foundation", + "system-configuration-sys", ] [[package]] -name = "tor-key-forge" -version = "0.35.0" +name = "system-configuration-sys" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa30066b80ade55a1b88a82b5320dfc50d1724918ad614ded8ecb4820c32062" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ - "derive-deftly 1.3.0", - "derive_more", - "downcast-rs", - "paste", - "rand 0.9.2", - "rsa", - "signature", - "ssh-key", - "thiserror 2.0.17", - "tor-bytes", - "tor-cert", - "tor-checkable", - "tor-error", - "tor-llcrypto", + "core-foundation-sys", + "libc", ] [[package]] -name = "tor-keymgr" -version = "0.35.0" +name = "tempfile" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e331dede46246977ae6722888329a60ef446df437f1a13ad2addcdff840692cc" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ - "amplify", - "arrayvec", - "cfg-if", - "derive-deftly 1.3.0", - "derive_builder_fork_arti", - "derive_more", - "downcast-rs", - "dyn-clone", - "fs-mistrust", - "glob-match", - "humantime", - "inventory", - "itertools 0.14.0", - "rand 0.9.2", - "safelog", - "serde", - "signature", - "ssh-key", - "thiserror 2.0.17", - "tor-basic-utils", - "tor-bytes", - "tor-config", - "tor-config-path", - "tor-error", - "tor-hscrypto", - "tor-key-forge", - "tor-llcrypto", - "tor-persist", - "tracing", - "visibility", - "walkdir", - "zeroize", + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.61.2", ] [[package]] -name = "tor-linkspec" -version = "0.35.0" +name = "termcolor" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9daa8b71777ecf02d317c200e96fd777d3668ddac4fc2fe3054216429b7917f" -dependencies = [ - "base64ct", - "by_address", - "caret", - "derive-deftly 1.3.0", - "derive_builder_fork_arti", - "derive_more", - "hex", - "itertools 0.14.0", - "safelog", - "serde", - "serde_with", - "strum", - "thiserror 2.0.17", - "tor-basic-utils", - "tor-bytes", - "tor-config", - "tor-llcrypto", - "tor-memquota", - "tor-protover", +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", ] [[package]] -name = "tor-llcrypto" -version = "0.35.0" +name = "thiserror" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95cb3920ea326ba2bb7c2674293655d045a1112eb93cc8ddcbf948bb59307a97" -dependencies = [ - "aes", - "base64ct", - "ctr", - "curve25519-dalek", - "der-parser", - "derive-deftly 1.3.0", - "derive_more", - "digest", - "ed25519-dalek", - "educe", - "getrandom 0.3.3", - "hex", - "rand 0.9.2", - "rand_chacha 0.9.0", - "rand_core 0.6.4", - "rand_core 0.9.3", - "rand_jitter", - "rdrand", - "rsa", - "safelog", - "serde", - "sha1", - "sha2", - "sha3", - "signature", - "subtle", - "thiserror 2.0.17", - "tor-error", - "tor-memquota", - "visibility", - "x25519-dalek", - "zeroize", +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", ] [[package]] -name = "tor-log-ratelim" -version = "0.35.0" +name = "thiserror" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "845d65304be6a614198027c4b2d1b35aaf073335c26df619d17e5f4027f2657f" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "futures", - "humantime", - "thiserror 2.0.17", - "tor-error", - "tor-rtcompat", - "tracing", - "weak-table", + "thiserror-impl 2.0.17", ] [[package]] -name = "tor-memquota" -version = "0.35.0" +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef375c3442a4ea74f0b6bf91a3eed660d55301b2e2f59b366aba4849b2321a6f" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "cfg-if", - "derive-deftly 1.3.0", - "derive_more", - "dyn-clone", - "educe", - "futures", - "itertools 0.14.0", - "paste", - "pin-project", - "serde", - "slotmap-careful", - "static_assertions", - "sysinfo", - "thiserror 2.0.17", - "tor-async-utils", - "tor-basic-utils", - "tor-config", - "tor-error", - "tor-log-ratelim", - "tor-rtcompat", - "tracing", - "void", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "tor-netdir" -version = "0.35.0" +name = "thiserror-impl" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "638b4e6507e3786488859d3c463fa73addbad4f788806c6972603727e527672e" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ - "async-trait", - "bitflags 2.9.4", - "derive_more", - "digest", - "futures", - "hex", - "humantime", - "itertools 0.14.0", - "num_enum", - "rand 0.9.2", - "serde", - "strum", - "thiserror 2.0.17", - "time", - "tor-basic-utils", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-netdoc", - "tor-protover", - "tor-units", - "tracing", - "typed-index-collections", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "tor-netdoc" -version = "0.35.0" +name = "thread_local" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dbc32d89e7ea2e2799168d0c453061647a727e39fc66f52e1bcb4c38c8dc433" -dependencies = [ - "amplify", - "base64ct", - "bitflags 2.9.4", - "cipher", - "derive-deftly 1.3.0", - "derive_builder_fork_arti", - "derive_more", - "digest", - "educe", - "hex", - "humantime", - "itertools 0.14.0", - "memchr", - "paste", - "phf", - "rand 0.9.2", - "serde", - "serde_with", - "signature", - "smallvec", - "strum", - "subtle", - "thiserror 2.0.17", - "time", - "tinystr", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-cert", - "tor-checkable", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-protover", - "tor-units", - "void", - "weak-table", - "zeroize", +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", ] [[package]] -name = "tor-persist" -version = "0.35.0" +name = "tinystr" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e41aea027686b05f21e0ad75aa2c0c9681a87f2f3130b6d6f7a7a8c06edd7b" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ - "amplify", - "derive-deftly 1.3.0", - "derive_more", - "filetime", - "fs-mistrust", - "fslock", - "fslock-guard", - "futures", - "itertools 0.14.0", - "oneshot-fused-workaround", - "paste", - "sanitize-filename", - "serde", - "serde_json", - "thiserror 2.0.17", - "time", - "tor-async-utils", - "tor-basic-utils", - "tor-error", - "tracing", - "void", + "displaydoc", + "zerovec", ] [[package]] -name = "tor-proto" -version = "0.35.0" +name = "tokio" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b95119789898b1b12e8f487745b70215e9f7d3df7c23325e4901ae65aec9703b" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ - "amplify", - "asynchronous-codec", - "bitvec", + "backtrace", "bytes", - "caret", - "cfg-if", - "cipher", - "coarsetime", - "criterion-cycles-per-byte", - "derive-deftly 1.3.0", - "derive_builder_fork_arti", - "derive_more", - "digest", - "educe", - "enum_dispatch", - "futures", - "futures-util", - "hkdf", - "hmac", - "itertools 0.14.0", - "nonany", - "oneshot-fused-workaround", - "pin-project", - "postage", - "rand 0.9.2", - "rand_core 0.9.3", - "safelog", - "slotmap-careful", - "smallvec", - "static_assertions", - "subtle", - "sync_wrapper", - "thiserror 2.0.17", - "tokio", - "tokio-util", - "tor-async-utils", - "tor-basic-utils", - "tor-bytes", - "tor-cell", - "tor-cert", - "tor-checkable", - "tor-config", - "tor-error", - "tor-hscrypto", - "tor-linkspec", - "tor-llcrypto", - "tor-log-ratelim", - "tor-memquota", - "tor-protover", - "tor-rtcompat", - "tor-rtmock", - "tor-units", - "tracing", - "typenum", - "visibility", - "void", - "zeroize", + "io-uring", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "slab", + "socket2", + "tokio-macros", + "windows-sys 0.59.0", ] [[package]] -name = "tor-protover" -version = "0.35.0" +name = "tokio-macros" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "484dc40a0ea58e8cc809ca2faf4df010327f7089ceafa6c8781a767260a34f6e" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ - "caret", - "paste", - "serde_with", - "thiserror 2.0.17", - "tor-bytes", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "tor-relay-selection" -version = "0.35.0" +name = "tokio-native-tls" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cc2b365bf5881b4380059e0636cc40e1fa18a1b3b050f78ce322c95139d467" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ - "rand 0.9.2", - "serde", - "tor-basic-utils", - "tor-linkspec", - "tor-netdir", - "tor-netdoc", + "native-tls", + "tokio", ] [[package]] -name = "tor-rtcompat" -version = "0.35.0" +name = "tokio-rustls" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591b0b0695e86c2958b8ab9c431f6fea17b544ef3ed3931bbfe96239fd5c9193" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "async-native-tls", - "async-trait", - "async_executors", - "asynchronous-codec", - "coarsetime", - "derive_more", - "dyn-clone", - "educe", - "futures", - "hex", - "libc", - "native-tls", - "paste", - "pin-project", - "thiserror 2.0.17", + "rustls", "tokio", - "tokio-util", - "tor-error", - "tor-general-addr", - "tracing", - "void", ] [[package]] -name = "tor-rtmock" -version = "0.35.0" +name = "tokio-stream" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdbf415d79f7a4d2a502039645a39d8bf0ff8af715e588575ac812b2baa7a91" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ - "amplify", - "assert_matches", - "async-trait", - "derive-deftly 1.3.0", - "derive_more", - "educe", - "futures", - "humantime", - "itertools 0.14.0", - "oneshot-fused-workaround", - "pin-project", - "priority-queue", - "slotmap-careful", - "strum", - "thiserror 2.0.17", - "tor-error", - "tor-general-addr", - "tor-rtcompat", - "tracing", - "tracing-test", - "void", + "futures-core", + "pin-project-lite", + "tokio", ] [[package]] -name = "tor-socksproto" -version = "0.35.0" +name = "tokio-test" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dbb9b68d9cf8e07eeafbca91ac11b7d9c4be1e674cb59830edfbac153333e7f" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" dependencies = [ - "amplify", - "caret", - "derive-deftly 1.3.0", - "educe", - "safelog", - "subtle", - "thiserror 2.0.17", - "tor-bytes", - "tor-error", + "async-stream", + "bytes", + "futures-core", + "tokio", + "tokio-stream", ] [[package]] -name = "tor-units" -version = "0.35.0" +name = "tokio-util" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48139f001dd6f409325b7c190ebcea1033b27f09042543946ab7aa4ad286257b" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ - "derive-deftly 1.3.0", - "derive_more", - "serde", - "thiserror 2.0.17", - "tor-memquota", + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", ] [[package]] @@ -5336,7 +1849,6 @@ dependencies = [ "tokio", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -5345,7 +1857,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.4", + "bitflags", "bytes", "futures-util", "http", @@ -5355,7 +1867,6 @@ dependencies = [ "tower", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -5376,7 +1887,6 @@ 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", @@ -5390,7 +1900,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -5420,100 +1930,32 @@ version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ - "matchers", "nu-ansi-term", - "once_cell", - "regex-automata", "sharded-slab", "smallvec", "thread_local", - "tracing", "tracing-core", "tracing-log", ] -[[package]] -name = "tracing-test" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68" -dependencies = [ - "tracing-core", - "tracing-subscriber", - "tracing-test-macro", -] - -[[package]] -name = "tracing-test-macro" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" -dependencies = [ - "quote", - "syn 2.0.106", -] - [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typed-index-collections" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd393dbd1e7b23e0cab7396570309b4068aa504e9dac2cd41d827583b4e9ab7" -dependencies = [ - "bincode", - "serde", -] - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "uncased" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" -dependencies = [ - "version_check", -] - [[package]] name = "unicode-ident" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "unty" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" - [[package]] name = "url" version = "2.5.7" @@ -5550,39 +1992,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "visibility" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "want" version = "0.3.1" @@ -5616,15 +2025,6 @@ dependencies = [ "wit-bindgen", ] -[[package]] -name = "wasix" -version = "0.12.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" -dependencies = [ - "wasi 0.11.1+wasi-snapshot-preview1", -] - [[package]] name = "wasm-bindgen" version = "0.2.104" @@ -5648,7 +2048,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.106", + "syn", "wasm-bindgen-shared", ] @@ -5683,7 +2083,7 @@ checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5710,12 +2110,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "weak-table" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549" - [[package]] name = "web-sys" version = "0.3.81" @@ -5726,22 +2120,6 @@ dependencies = [ "wasm-bindgen", ] -[[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-util" version = "0.1.11" @@ -5751,47 +2129,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[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" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections", - "windows-core 0.61.2", - "windows-future", - "windows-link 0.1.3", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", -] - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - [[package]] name = "windows-core" version = "0.62.2" @@ -5805,17 +2142,6 @@ dependencies = [ "windows-strings 0.5.1", ] -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading", -] - [[package]] name = "windows-implement" version = "0.60.2" @@ -5824,7 +2150,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -5835,7 +2161,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -5850,16 +2176,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", -] - [[package]] name = "windows-registry" version = "0.5.3" @@ -5976,15 +2292,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -6081,15 +2388,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" -[[package]] -name = "winnow" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] - [[package]] name = "wit-bindgen" version = "0.46.0" @@ -6102,33 +2400,6 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x25519-dalek" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" -dependencies = [ - "curve25519-dalek", - "rand_core 0.6.4", - "serde", - "zeroize", -] - -[[package]] -name = "xxhash-rust" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" - [[package]] name = "yoke" version = "0.8.0" @@ -6149,30 +2420,10 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", "synstructure", ] -[[package]] -name = "zerocopy" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - [[package]] name = "zerofrom" version = "0.1.6" @@ -6190,7 +2441,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", "synstructure", ] @@ -6199,20 +2450,6 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] [[package]] name = "zerotrie" @@ -6244,33 +2481,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" -dependencies = [ - "cc", - "pkg-config", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index 623ee26..b7c5de3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,46 +1,30 @@ [package] name = "localitysrv" -version = "0.1.0" +version = "0.2.0" edition = "2021" -description = "HTTP server built with Rust, Arti/Tor and Axum that serves .pmtiles for localities from around the world." +description = "Decentralized geographic data node using Codex network" authors = ["Xavier Saliniere "] license = "GPL-3.0-or-later" [dependencies] -axum = "0.8" -clap = { version = "4.5", features = ["derive"] } -tokio = { version = "1.47", features = [ - "net", - "signal", - "fs", - "process", - "sync", - "rt-multi-thread", -] } +# Core dependencies (keep) +tokio = { version = "1.47", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" anyhow = "1.0" rusqlite = { version = "0.37", features = ["bundled"] } -tower = "0.5" reqwest = { version = "0.12", features = ["json", "stream"] } dotenvy = "0.15" thiserror = "1.0" -tower-http = { version = "0.6", features = ["cors", "trace"] } -headers = "0.4" -regex = "1.0" +tracing = "0.1" +tracing-subscriber = "0.3" +clap = { version = "4.5", features = ["derive"] } + +# New Codex integration +codex-bindings = { version = "0.1.3" } futures = "0.3" -tokio-util = { version = "0.7", features = ["io"] } -# Tor hidden service dependencies -arti-client = { version = "0.35", features = [ - "onion-service-service", - "static", -] } -hyper = { version = "1", features = ["http1", "server"] } -hyper-util = { version = "0.1.1", features = ["tokio"] } -safelog = "0.6" -tor-cell = "0.35" -tor-hsservice = "0.35" -tor-proto = "0.35" -tracing = "0.1" -tracing-subscriber = "0.3.20" +[dev-dependencies] +tempfile = "3.23" +tokio-test = "0.4" +env_logger = "0.10" diff --git a/src/api/countries.rs b/src/api/countries.rs deleted file mode 100644 index 66f502c..0000000 --- a/src/api/countries.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::AppState; -use axum::{ - extract::{Query, State}, - Json, -}; - -#[derive(serde::Deserialize)] -pub struct CountryQueryParams { - pub page: Option, - pub limit: Option, - pub q: Option, -} - -pub async fn search_countries( - State(app_state): State, - Query(params): Query, -) -> Json { - let page = params.page.unwrap_or(1); - let limit = params.limit.unwrap_or(10); - let query = params.q.as_deref(); - - if page < 1 { - return Json(serde_json::json!({ - "success": false, - "error": "Page must be a positive integer" - })); - } - - let config = app_state.config.lock().await; - let countries_result = match app_state - .country_service - .get_countries_paginated( - &app_state.db_service, - &config.target_countries, - page, - limit, - query, - ) - .await - { - Ok(countries) => countries, - Err(e) => { - return Json(serde_json::json!({ - "success": false, - "error": format!("Failed to get countries: {}", e) - })); - } - }; - - let total = match app_state - .country_service - .get_countries_count(&app_state.db_service, &config.target_countries, query) - .await - { - Ok(count) => count, - Err(e) => { - return Json(serde_json::json!({ - "success": false, - "error": format!("Failed to get countries count: {}", e) - })); - } - }; - - let total_pages = (total as f64 / limit as f64).ceil() as u32; - - Json(serde_json::json!({ - "success": true, - "data": countries_result, - "pagination": { - "total": total, - "page": page, - "limit": limit, - "total_pages": total_pages - } - })) -} diff --git a/src/api/localities.rs b/src/api/localities.rs deleted file mode 100644 index d810df4..0000000 --- a/src/api/localities.rs +++ /dev/null @@ -1,127 +0,0 @@ -use crate::models::locality::LocalityInfo; -use crate::AppState; -use axum::{ - extract::{Path, Query, State}, - Json, -}; -use std::path::Path as StdPath; -use tokio::fs; - -#[derive(serde::Deserialize)] -pub struct LocalityQueryParams { - pub page: Option, - pub limit: Option, - pub q: Option, -} - -pub async fn search_localities( - State(app_state): State, - Path(country_code): Path, - Query(params): Query, -) -> Json { - let page = params.page.unwrap_or(1); - let limit = params.limit.unwrap_or(10); - let query = params.q.as_deref(); - - if page < 1 { - return Json(serde_json::json!({ - "success": false, - "error": "Page must be a positive integer" - })); - } - - let localities_result = match app_state - .db_service - .get_localities(&country_code, page, limit, query) - .await - { - Ok(localities) => localities, - Err(e) => { - return Json(serde_json::json!({ - "success": false, - "error": format!("Failed to get localities: {}", e) - })); - } - }; - - let total = match app_state - .db_service - .get_localities_count(&country_code, query) - .await - { - Ok(count) => count, - Err(e) => { - return Json(serde_json::json!({ - "success": false, - "error": format!("Failed to get localities count: {}", e) - })); - } - }; - - let total_pages = (total as f64 / limit as f64).ceil() as u32; - - let config = app_state.config.lock().await; - let country_code_clone = country_code.clone(); - let localities_info: Vec = - futures::future::join_all(localities_result.into_iter().map(|locality| { - let id = locality.id; - let name = locality.name; - let country = locality.country; - let placetype = locality.placetype; - let latitude = locality.latitude; - let longitude = locality.longitude; - let min_longitude = locality.min_longitude; - let min_latitude = locality.min_latitude; - let max_longitude = locality.max_longitude; - let max_latitude = locality.max_latitude; - let assets_dir = config.assets_dir.clone(); - let onion_address = config.onion_address.clone(); - let country_code_for_async = country_code_clone.clone(); - - async move { - let file_path = StdPath::new(&assets_dir) - .join("localities") - .join(&country_code_for_async) - .join(format!("{}.pmtiles", id)); - - let file_size = match fs::metadata(&file_path).await { - Ok(metadata) => metadata.len(), - Err(_) => 0, - }; - - let onion_link = format!( - "http://{}/countries/{}/localities/{}/pmtiles", - onion_address.unwrap(), - country_code_for_async, - id - ); - - LocalityInfo { - id, - name, - country, - placetype, - latitude, - longitude, - min_longitude, - min_latitude, - max_longitude, - max_latitude, - file_size, - onion_link, - } - } - })) - .await; - - Json(serde_json::json!({ - "success": true, - "data": localities_info, - "pagination": { - "total": total, - "page": page, - "limit": limit, - "total_pages": total_pages - } - })) -} diff --git a/src/api/mod.rs b/src/api/mod.rs deleted file mode 100644 index 08d1d86..0000000 --- a/src/api/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod countries; -pub mod localities; -pub mod pmtiles; diff --git a/src/api/pmtiles.rs b/src/api/pmtiles.rs deleted file mode 100644 index 2e7e170..0000000 --- a/src/api/pmtiles.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::AppState; -use axum::{ - body::Body, - extract::{Path, State}, - http::{HeaderMap, StatusCode}, - response::Response, -}; -use std::path::PathBuf; -use tokio::{ - fs::File, - io::{AsyncReadExt, AsyncSeekExt}, -}; - -pub async fn serve_pmtiles( - State(app_state): State, - Path((country_code, id)): Path<(String, String)>, - headers: HeaderMap, -) -> Result, StatusCode> { - let config = app_state.config.lock().await; - let file_path = PathBuf::from(&config.assets_dir) - .join("localities") - .join(country_code) - .join(format!("{}.pmtiles", id)); - - // Check if file exists and get its metadata - let metadata = match tokio::fs::metadata(&file_path).await { - Ok(metadata) => metadata, - Err(_) => return Err(StatusCode::NOT_FOUND), - }; - let file_size = metadata.len(); - - // Handle range requests (HTTP 206 Partial Content) - if let Some(range_header) = headers.get("Range") { - if let Ok(range_str) = range_header.to_str() { - // Parse range header: "bytes=start-end" or "bytes=start-" - if let Some(caps) = regex::Regex::new(r"bytes=(\d+)-(\d*)") - .unwrap() - .captures(range_str) - { - let start: u64 = caps[1].parse().unwrap_or(0); - let end = if caps[2].is_empty() { - file_size - 1 - } else { - caps[2].parse().unwrap_or(file_size - 1) - }; - - // Validate range - if start < file_size && end < file_size && start <= end { - let content_length = end - start + 1; - - // Open file and seek to start position - let mut file = match File::open(&file_path).await { - Ok(file) => file, - Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR), - }; - - match file.seek(std::io::SeekFrom::Start(start)).await { - Ok(_) => {} - Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR), - } - - // Read the specific range - let mut buffer = vec![0u8; content_length.try_into().unwrap_or(0)]; - match file.read_exact(&mut buffer).await { - Ok(_) => {} - Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR), - } - - // Return partial content response - return Ok(Response::builder() - .status(StatusCode::PARTIAL_CONTENT) - .header("Content-Type", "application/octet-stream") - .header("Content-Length", content_length.to_string()) - .header( - "Content-Range", - format!("bytes {}-{}/{}", start, end, file_size), - ) - .header("Accept-Ranges", "bytes") - .body(Body::from(buffer)) - .unwrap()); - } - } - } - } - - // Full file response (HTTP 200) - let file = match File::open(&file_path).await { - Ok(file) => file, - Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR), - }; - - let stream = tokio_util::io::ReaderStream::new(file); - let body = Body::from_stream(stream); - - Ok(Response::builder() - .status(StatusCode::OK) - .header("Content-Type", "application/octet-stream") - .header("Content-Length", file_size.to_string()) - .header("Accept-Ranges", "bytes") - .header( - "Content-Disposition", - format!("attachment; filename=\"{}.pmtiles\"", id), - ) - .body(body) - .unwrap()) -} diff --git a/src/config.rs b/src/config.rs index 1cb319f..2038b31 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,33 +1,94 @@ +use codex_bindings::{CodexConfig, LogLevel}; use dotenvy::dotenv; use std::env; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; -#[derive(Clone)] -pub struct Config { - pub server_port: u16, - pub assets_dir: String, +#[derive(Clone, Debug)] +pub struct LocalitySrvConfig { + // === Core Codex Configuration === + pub codex: CodexConfig, + + // === Storage Configuration === + pub data_dir: PathBuf, // Directory for Codex data + + // === Localitysrv Specific === + pub database_path: PathBuf, // WhosOnFirst database path + pub cid_database_path: PathBuf, // CID mappings database path + pub localities_dir: PathBuf, // Local PMTiles directory + + // === Tool Configuration === pub pmtiles_cmd: String, pub bzip2_cmd: String, pub find_cmd: String, pub whosonfirst_db_url: String, - pub protomaps_builds_url: String, pub planet_pmtiles_path: Option, pub target_countries: Vec, pub max_concurrent_extractions: usize, - pub db_connection_pool_size: u32, - pub onion_address: Option, } -impl Config { - pub fn from_env() -> Result { +impl LocalitySrvConfig { + pub fn from_env() -> Result> { dotenv().ok(); + // Core Codex settings + let codex_data_dir = + env::var("CODEX_DATA_DIR").unwrap_or_else(|_| "./.codex-data".to_string()); + let storage_quota_gb = env::var("CODEX_STORAGE_QUOTA_GB") + .unwrap_or_else(|_| "100".to_string()) + .parse::() + .unwrap_or(100); + let storage_quota = storage_quota_gb * 1024 * 1024 * 1024; // Convert GB to bytes + + let discovery_port = env::var("CODEX_DISCOVERY_PORT") + .unwrap_or_else(|_| "8090".to_string()) + .parse::() + .unwrap_or(8090); + + let listen_addrs: Vec = env::var("CODEX_LISTEN_ADDRS") + .unwrap_or_else(|_| { + format!( + "/ip4/0.0.0.0/tcp/{},/ip4/127.0.0.1/tcp/{}", + discovery_port, discovery_port + ) + }) + .split(',') + .map(|s| s.trim().to_string()) + .collect(); + + let log_level_str = env::var("LOG_LEVEL").unwrap_or_else(|_| "info".to_string()); + let log_level = match log_level_str.as_str() { + "debug" => LogLevel::Debug, + "info" => LogLevel::Info, + "warn" => LogLevel::Warn, + "error" => LogLevel::Error, + _ => LogLevel::Info, + }; + + // Localitysrv settings + let assets_dir = env::var("ASSETS_DIR").unwrap_or_else(|_| "./assets".to_string()); + let database_path = env::var("LOCALITYSRV_DB_PATH") + .unwrap_or_else(|_| format!("{}/whosonfirst-data-admin-latest.db", assets_dir)); + let cid_database_path = env::var("LOCALITYSRV_CID_DB_PATH") + .unwrap_or_else(|_| format!("{}/locality-cid-mappings.db", assets_dir)); + let localities_dir = env::var("LOCALITYSRV_LOCALITIES_DIR") + .unwrap_or_else(|_| format!("{}/localities", assets_dir)); + + // Create Codex configuration + let codex_config = CodexConfig::new() + .log_level(log_level) + .data_dir(&codex_data_dir) + .storage_quota(storage_quota) + .discovery_port(discovery_port) + .listen_addrs(listen_addrs.clone()); + Ok(Self { - server_port: env::var("SERVER_PORT") - .unwrap_or_else(|_| "8080".to_string()) - .parse() - .unwrap_or(8080), - assets_dir: env::var("ASSETS_DIR").unwrap_or_else(|_| "./assets".to_string()), + codex: codex_config, + data_dir: PathBuf::from(codex_data_dir), + database_path: PathBuf::from(database_path), + cid_database_path: PathBuf::from(cid_database_path), + localities_dir: PathBuf::from(localities_dir), + + // Tool configuration (keep existing) pmtiles_cmd: env::var("PMTILES_CMD").unwrap_or_else(|_| "pmtiles".to_string()), bzip2_cmd: env::var("BZIP2_CMD").unwrap_or_else(|_| "bzip2".to_string()), find_cmd: env::var("FIND_CMD").unwrap_or_else(|_| "find".to_string()), @@ -35,8 +96,6 @@ impl Config { "https://data.geocode.earth/wof/dist/sqlite/whosonfirst-data-admin-latest.db.bz2" .to_string() }), - protomaps_builds_url: env::var("PROTOMAPS_BUILDS_URL") - .unwrap_or_else(|_| "https://build-metadata.protomaps.dev/builds.json".to_string()), planet_pmtiles_path: env::var("PLANET_PMTILES_PATH") .ok() .filter(|s| !s.is_empty()), @@ -50,27 +109,13 @@ impl Config { .unwrap_or_else(|_| "10".to_string()) .parse() .unwrap_or(10), - db_connection_pool_size: env::var("DB_CONNECTION_POOL_SIZE") - .unwrap_or_else(|_| "10".to_string()) - .parse() - .unwrap_or(10), - onion_address: None, }) } - pub fn database_path(&self) -> PathBuf { - PathBuf::from(&self.assets_dir).join("whosonfirst-data-admin-latest.db") - } - - pub fn database_url(&self) -> String { - format!("sqlite://{}", self.database_path().display()) - } - pub fn country_codes_path(&self) -> PathBuf { - PathBuf::from(&self.assets_dir).join("country-codes.json") - } - - pub fn localities_dir(&self) -> PathBuf { - PathBuf::from(&self.assets_dir).join("localities") + self.database_path + .parent() + .unwrap_or_else(|| Path::new(".")) + .join("country-codes.json") } } diff --git a/src/initialization.rs b/src/initialization.rs index 79ff7a1..ab54fe1 100644 --- a/src/initialization.rs +++ b/src/initialization.rs @@ -1,5 +1,5 @@ use crate::cli::Args; -use crate::config::Config; +use crate::config::LocalitySrvConfig; use crate::services::{ country::CountryService, database::DatabaseService, extraction::ExtractionService, }; @@ -9,7 +9,7 @@ use std::path::Path; use tracing::{info, warn}; async fn download_and_decompress_database( - config: &Config, + config: &LocalitySrvConfig, compressed_path: &str, ) -> Result<(), Box> { info!("Downloading WhosOnFirst database..."); @@ -59,10 +59,10 @@ pub async fn ensure_tools_are_present(tools: &[&str]) -> Result<(), Box Result<(), Box> { - let database_path = config.database_path(); + let database_path = &config.database_path; let compressed_path = format!("{}.bz2", database_path.display()); if database_path.exists() { @@ -110,7 +110,7 @@ pub async fn ensure_database_is_present( pub async fn ensure_all_localities_present( extraction_service: &ExtractionService, country_service: &CountryService, - config: &Config, + config: &LocalitySrvConfig, db_service: &DatabaseService, args: &Args, ) -> Result<(), Box> { @@ -207,3 +207,129 @@ pub async fn ensure_all_localities_present( info!("Extraction skipped."); Ok(()) } + +/// Initialize and start the Codex node +pub async fn initialize_codex_node( + config: &LocalitySrvConfig, +) -> Result> { + info!("Initializing Codex node..."); + + // Create Codex configuration from the main config + let codex_config = config.codex.clone(); + + // Create node manager + let node_manager = crate::node::manager::CodexNodeManager::new(codex_config); + + // Start the node + node_manager.start().await?; + + info!("Codex node started successfully"); + Ok(node_manager) +} + +/// Ensure Codex data directory exists +pub async fn ensure_codex_data_directory( + config: &LocalitySrvConfig, +) -> Result<(), Box> { + let data_dir = &config.data_dir; + + if !data_dir.exists() { + info!( + "Creating Codex data directory with secure permissions: {:?}", + data_dir + ); + std::fs::create_dir_all(data_dir)?; + + // Set secure permissions (0700 - read/write/execute for owner only) + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let mut perms = std::fs::metadata(data_dir)?.permissions(); + perms.set_mode(0o700); + std::fs::set_permissions(data_dir, perms)?; + } + } else { + // Check and fix permissions if directory already exists + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let metadata = std::fs::metadata(data_dir)?; + let current_mode = metadata.permissions().mode(); + + // Check if permissions are too permissive (not 0700) + if current_mode & 0o077 != 0 { + info!( + "Fixing insecure permissions on Codex data directory: {:?}", + data_dir + ); + let mut perms = metadata.permissions(); + perms.set_mode(0o700); + std::fs::set_permissions(data_dir, perms)?; + } + } + } + + Ok(()) +} + +/// Check if localities are ready for upload (exist and not already uploaded) +pub async fn check_upload_readiness( + db_service: &DatabaseService, + extraction_service: &ExtractionService, + country_codes: &[String], +) -> Result, Box> { + let mut readiness_map = HashMap::new(); + + for country_code in country_codes { + let db_count = db_service.get_country_locality_count(country_code).await?; + let file_count = extraction_service + .get_pmtiles_file_count(country_code) + .await?; + + // Check how many are already uploaded - simplified for now + let uploaded_count = 0u32; + + let readiness = UploadReadiness { + total_localities: db_count, + extracted_files: file_count, + uploaded_files: uploaded_count, + ready_for_upload: file_count > uploaded_count, + }; + + readiness_map.insert(country_code.clone(), readiness); + } + + Ok(readiness_map) +} + +#[derive(Debug, Clone)] +pub struct UploadReadiness { + pub total_localities: u32, + pub extracted_files: u32, + pub uploaded_files: u32, + pub ready_for_upload: bool, +} + +/// Print upload readiness status +pub fn print_upload_readiness(readiness_map: &HashMap) { + info!("Upload Readiness Status:"); + info!("Country Code | Total | Extracted | Uploaded | Ready"); + info!("-------------|-------|-----------|----------|-------"); + + for (country_code, readiness) in readiness_map { + let ready = if readiness.ready_for_upload { + "✓ Yes" + } else { + "✗ No" + }; + + info!( + "{:12} | {:5} | {:9} | {:8} | {}", + country_code, + readiness.total_localities, + readiness.extracted_files, + readiness.uploaded_files, + ready + ); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d763917 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,12 @@ +//! Localitysrv - Decentralized geographic data node using Codex network +//! +//! This library provides the core functionality for localitysrv, +//! which serves pmtiles for localities worldwide through a decentralized Codex network. + +pub mod cli; +pub mod config; +pub mod initialization; +pub mod models; +pub mod node; +pub mod services; +pub mod utils; diff --git a/src/main.rs b/src/main.rs index 3f8dcb1..a7e1ee2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,45 +1,34 @@ -use crate::{ - api::{countries, localities, pmtiles}, - config::Config, - initialization::{ - ensure_all_localities_present, ensure_database_is_present, ensure_tools_are_present, - }, - services::tor::TorServiceManager, - services::{country::CountryService, database::DatabaseService, extraction::ExtractionService}, +use crate::config::LocalitySrvConfig; +use crate::initialization::{ + check_upload_readiness, ensure_all_localities_present, ensure_codex_data_directory, + ensure_database_is_present, ensure_tools_are_present, initialize_codex_node, + print_upload_readiness, }; -use axum::{ - routing::{get, Router}, - Json, +use crate::services::{ + country::CountryService, database::DatabaseService, extraction::ExtractionService, + node_ops::NodeOps, }; use clap::Parser; -use reqwest::StatusCode; use std::sync::Arc; -use tower_http::cors::CorsLayer; -use tracing::error; +use tokio::signal; +use tracing::{error, info, warn}; -mod api; mod cli; mod config; mod initialization; mod models; +mod node; mod services; mod utils; -#[derive(Clone)] -pub struct AppState { - pub config: Arc>, - pub db_service: Arc, - pub extraction_service: Arc, - pub country_service: Arc, -} - #[tokio::main] -async fn main() { +async fn main() -> Result<(), Box> { tracing_subscriber::fmt().init(); let args = cli::Args::parse(); - let config = match Config::from_env() { + // Load configuration + let config = match LocalitySrvConfig::from_env() { Ok(config) => Arc::new(config), Err(e) => { error!("Failed to load configuration: {}", e); @@ -47,6 +36,9 @@ async fn main() { } }; + info!("Starting localitysrv decentralized node..."); + + // Ensure required tools are present if let Err(e) = ensure_tools_are_present(&[&config.pmtiles_cmd, &config.bzip2_cmd, &config.find_cmd]).await { @@ -54,26 +46,39 @@ async fn main() { std::process::exit(1); } + // Ensure Codex data directory exists + if let Err(e) = ensure_codex_data_directory(&config).await { + error!("Failed to ensure Codex data directory: {}", e); + std::process::exit(1); + } + + // Ensure database is present if let Err(e) = ensure_database_is_present(&config, &args).await { error!("Failed to ensure database is present: {}", e); std::process::exit(1); } - let db_service = match DatabaseService::new( - &config.database_url(), - &config.database_path().to_string_lossy(), - &config.whosonfirst_db_url, - &config.bzip2_cmd, - ) - .await - { - Ok(service) => Arc::new(service), - Err(e) => { - error!("Failed to initialize database service: {}", e); - std::process::exit(1); - } - }; - + // Initialize WhosOnFirst database service (read-only) + let whosonfirst_db_service = + match DatabaseService::new(&config.database_path.to_string_lossy()).await { + Ok(service) => Arc::new(service), + Err(e) => { + error!("Failed to initialize WhosOnFirst database service: {}", e); + std::process::exit(1); + } + }; + + // Initialize CID mappings database service (read-write) + let cid_db_service = + match DatabaseService::new(&config.cid_database_path.to_string_lossy()).await { + Ok(service) => Arc::new(service), + Err(e) => { + error!("Failed to initialize CID database service: {}", e); + std::process::exit(1); + } + }; + + // Initialize country service let country_service = match CountryService::new(&config.country_codes_path()).await { Ok(service) => Arc::new(service), Err(e) => { @@ -82,13 +87,18 @@ async fn main() { } }; - let extraction_service = Arc::new(ExtractionService::new(config.clone(), db_service.clone())); + // Initialize extraction service (uses WhosOnFirst database) + let extraction_service = Arc::new(ExtractionService::new( + config.clone(), + whosonfirst_db_service.clone(), + )); + // Ensure all localities are extracted if let Err(e) = ensure_all_localities_present( &extraction_service, &country_service, &config, - &db_service, + &whosonfirst_db_service, &args, ) .await @@ -97,123 +107,94 @@ async fn main() { std::process::exit(1); } - tracing::info!("Initialization complete, starting services..."); - - let app_state = AppState { - config: Arc::new(tokio::sync::Mutex::new((*config).clone())), - db_service: db_service.clone(), - extraction_service: extraction_service.clone(), - country_service: country_service.clone(), - }; - - let app = Router::new() - .route("/countries", get(countries::search_countries)) - .route( - "/countries/{country_code}/localities", - get(localities::search_localities), - ) - .route( - "/countries/{country_code}/localities/{id}/pmtiles", - get(pmtiles::serve_pmtiles), - ) - .route( - "/health", - get({ - ( - StatusCode::OK, - Json(serde_json::json!({ "status": "healthy" })), - ) - }), - ) - .layer(CorsLayer::permissive()) - .with_state(app_state.clone()); - - let shutdown_signal = std::sync::Arc::new(tokio::sync::Notify::new()); - - let app_for_tor = app.clone(); - let app_for_regular = app.clone(); - - let shutdown_signal_tor = shutdown_signal.clone(); - let shutdown_signal_regular = shutdown_signal.clone(); - - let (onion_address_tx, onion_address_rx) = tokio::sync::oneshot::channel(); - - let app_state_for_tor = app_state.clone(); - - let tor_manager = TorServiceManager::new(app_for_tor, app_state_for_tor, shutdown_signal_tor); - - let tor_handle = tokio::spawn(async move { - tor_manager.run_with_retry(onion_address_tx).await; - }); - - let onion_address = match onion_address_rx.await { - Ok(address) => address, - Err(_) => { - error!("Failed to get onion address from Tor hidden service"); - return; + // Initialize Codex node + let node_manager = match initialize_codex_node(&config).await { + Ok(manager) => Arc::new(manager), + Err(e) => { + error!("Failed to initialize Codex node: {}", e); + std::process::exit(1); } }; - { - let mut config = app_state.config.lock().await; - config.onion_address = Some(onion_address.clone()); - } - - let regular_handle = tokio::spawn(async move { - run_axum_server(app_for_regular, config.clone(), shutdown_signal_regular).await; - }); + info!("Initialization complete, starting upload process..."); - tokio::select! { - _ = tor_handle => {} - _ = regular_handle => {} - _ = tokio::signal::ctrl_c() => { - tracing::info!("\nShutdown signal received, shutting down..."); - shutdown_signal.notify_waiters(); + // Check upload readiness + let readiness_map = check_upload_readiness( + &whosonfirst_db_service, + &extraction_service, + &config.target_countries, + ) + .await?; - tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; - } - } -} + print_upload_readiness(&readiness_map); -async fn run_axum_server( - app: Router, - config: Arc, - shutdown_signal: std::sync::Arc, -) { - tracing::info!("Starting Axum server..."); + // Create node operations service (uses CID database for storage, WhosOnFirst for lookups) + let node_ops = NodeOps::new_with_databases( + cid_db_service.clone(), + whosonfirst_db_service.clone(), + node_manager.clone(), + ); - let address = "127.0.0.1"; - let port = config.server_port; + // Process all localities for upload + if let Err(e) = node_ops.process_all_localities().await { + error!("Failed to process localities: {}", e); + std::process::exit(1); + } - let listener = match tokio::net::TcpListener::bind(&format!("{}:{}", address, port)).await { - Ok(listener) => { - tracing::info!("TCP listener bound to {}:{}", address, port); - listener + // Get final statistics + let stats = node_ops.get_stats().await; + info!( + "Upload process completed! Total uploaded: {}, Total failed: {}, Total bytes: {}", + stats.total_uploaded, stats.total_failed, stats.total_bytes_uploaded + ); + + // Get database statistics + let (total_mappings, unique_countries) = cid_db_service.get_cid_mapping_stats().await?; + info!( + "Database contains {} CID mappings across {} countries", + total_mappings, unique_countries + ); + + info!("All localities uploaded and CID mappings stored!"); + + match node_manager.get_peer_id().await { + Ok(peer_id) => { + info!("Node is now running and serving files to the network..."); + info!("Peer ID: {}", peer_id); + info!("Listen addresses:"); + for addr in &config.codex.listen_addrs { + info!(" {}", addr); + } } Err(e) => { - error!( - "TCP listener: Failed to bind to {}:{}: {}", - address, port, e - ); - return; + info!("Node is now running and serving files to the network..."); + warn!("Failed to get peer ID: {}", e); } - }; + } - let shutdown = shutdown_signal.clone(); + info!("Press Ctrl+C to stop the node gracefully"); - match axum::serve(listener, app) - .with_graceful_shutdown(async move { - tracing::info!("✓ Axum server successfully started"); - shutdown.notified().await; - tracing::info!("Axum server shutting down..."); - }) - .await - { - Ok(_) => { - tracing::info!("Axum server stopped successfully"); + // Keep the node running until interrupted + tokio::select! { + _ = async { + signal::ctrl_c().await.expect("Failed to listen for ctrl+c"); + } => { + info!("Received Ctrl+C, shutting down gracefully..."); } - Err(e) => { - error!("Axum server error: {}", e); + _ = async { + let mut sig_term = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) + .expect("Failed to setup SIGTERM handler"); + sig_term.recv().await; + } => { + info!("Received termination signal, shutting down gracefully..."); } } + + // Stop the node gracefully + if let Err(e) = node_manager.stop().await { + error!("Failed to stop Codex node: {}", e); + } + + info!("Node stopped successfully"); + Ok(()) } diff --git a/src/models/mod.rs b/src/models/mod.rs index 85a279e..03974dc 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,3 +1,3 @@ pub mod country; pub mod locality; -pub mod response; +pub mod storage; diff --git a/src/models/storage.rs b/src/models/storage.rs new file mode 100644 index 0000000..027f9c4 --- /dev/null +++ b/src/models/storage.rs @@ -0,0 +1,116 @@ +use std::collections::VecDeque; +use std::path::PathBuf; + +#[derive(Debug, Clone)] +pub struct PendingUpload { + pub country_code: String, + pub locality_id: u32, + pub file_path: PathBuf, +} + +impl PendingUpload { + pub fn new(country_code: String, locality_id: u32, file_path: PathBuf) -> Self { + Self { + country_code, + locality_id, + file_path, + } + } +} + +#[derive(Debug, Clone)] +pub struct CompletedUpload { + pub country_code: String, + pub locality_id: u32, + pub cid: String, + pub file_size: u64, +} + +impl CompletedUpload { + pub fn new(country_code: String, locality_id: u32, cid: String, file_size: u64) -> Self { + Self { + country_code, + locality_id, + cid, + file_size, + } + } +} + +#[derive(Debug)] +pub struct UploadQueue { + pending_uploads: VecDeque, + batch_size: usize, + max_queue_size: usize, +} + +impl UploadQueue { + pub fn new(batch_size: usize, max_queue_size: usize) -> Self { + Self { + pending_uploads: VecDeque::new(), + batch_size, + max_queue_size, + } + } + + pub fn add_upload(&mut self, upload: PendingUpload) -> Result<(), QueueError> { + if self.pending_uploads.len() >= self.max_queue_size { + return Err(QueueError::QueueFull); + } + self.pending_uploads.push_back(upload); + Ok(()) + } + + pub fn take_batch(&mut self) -> Vec { + let batch_size = std::cmp::min(self.batch_size, self.pending_uploads.len()); + (0..batch_size) + .map(|_| self.pending_uploads.pop_front().unwrap()) + .collect() + } + + pub fn is_full(&self) -> bool { + self.pending_uploads.len() >= self.batch_size + } + + pub fn is_empty(&self) -> bool { + self.pending_uploads.is_empty() + } +} + +#[derive(Debug, thiserror::Error)] +pub enum QueueError { + #[error("Upload queue is full")] + QueueFull, +} + +#[derive(Debug, Clone)] +pub struct UploadStats { + pub total_uploaded: u64, + pub total_failed: u64, + pub total_bytes_uploaded: u64, +} + +impl UploadStats { + pub fn new() -> Self { + Self { + total_uploaded: 0, + total_failed: 0, + total_bytes_uploaded: 0, + } + } + + pub fn increment_uploaded(&mut self, bytes: u64) { + self.total_uploaded += 1; + self.total_bytes_uploaded += bytes; + } + + pub fn increment_failed(&mut self) { + self.total_failed += 1; + } +} + +impl Default for UploadStats { + fn default() -> Self { + Self::new() + } +} diff --git a/src/node/manager.rs b/src/node/manager.rs new file mode 100644 index 0000000..f7bf823 --- /dev/null +++ b/src/node/manager.rs @@ -0,0 +1,155 @@ +use codex_bindings::callback::with_libcodex_lock; +use codex_bindings::{upload_file, CodexConfig, CodexNode, UploadOptions, UploadResult}; +use std::sync::Arc; +use thiserror::Error; +use tokio::sync::Mutex; +use tracing::{error, info, warn}; + +#[derive(Error, Debug)] +pub enum NodeManagerError { + #[error("Failed to create Codex node: {0}")] + NodeCreationFailed(String), + #[error("Failed to start node: {0}")] + NodeStartFailed(String), + #[error("Failed to stop node: {0}")] + NodeStopFailed(String), + #[error("Failed to destroy node: {0}")] + NodeDestroyFailed(String), + #[error("Thread safety error: {0}")] + ThreadSafetyError(String), + #[error("Node is not running")] + NodeNotRunning, + #[error("Node operation error: {0}")] + NodeOperationError(String), +} + +pub struct CodexNodeManager { + node: Arc>>, + config: CodexConfig, + is_running: Arc>, +} + +impl CodexNodeManager { + pub fn new(config: CodexConfig) -> Self { + Self { + node: Arc::new(Mutex::new(None)), + config, + is_running: Arc::new(Mutex::new(false)), + } + } + + /// Create and start the Codex node + pub async fn start(&self) -> Result<(), NodeManagerError> { + let mut node_guard = self.node.lock().await; + let mut running_guard = self.is_running.lock().await; + + if *running_guard { + warn!("Node is already running"); + return Ok(()); + } + + info!("Creating Codex node..."); + + // Create and start node directly like the integration test + let config = self.config.clone(); + let node = tokio::task::spawn_blocking(move || { + let mut node = CodexNode::new(config) + .map_err(|e| NodeManagerError::NodeCreationFailed(e.to_string()))?; + + node.start() + .map_err(|e| NodeManagerError::NodeStartFailed(e.to_string()))?; + + Ok::(node) + }) + .await + .map_err(|e| NodeManagerError::ThreadSafetyError(e.to_string()))??; + + *node_guard = Some(node); + *running_guard = true; + + info!("Codex node started successfully"); + Ok(()) + } + + /// Stop the Codex node + pub async fn stop(&self) -> Result<(), NodeManagerError> { + let mut node_guard = self.node.lock().await; + let mut running_guard = self.is_running.lock().await; + + if !*running_guard { + warn!("Node is not running"); + return Ok(()); + } + + if let Some(mut node) = node_guard.take() { + info!("Stopping Codex node..."); + + tokio::task::spawn_blocking(move || { + node.stop() + .map_err(|e| NodeManagerError::NodeStopFailed(e.to_string()))?; + node.destroy() + .map_err(|e| NodeManagerError::NodeDestroyFailed(e.to_string()))?; + Ok::<(), NodeManagerError>(()) + }) + .await + .map_err(|e| NodeManagerError::ThreadSafetyError(e.to_string()))??; + + info!("Codex node stopped and destroyed"); + } + + *running_guard = false; + Ok(()) + } + + /// Get node information + pub async fn get_peer_id(&self) -> Result { + let node = self.get_node().await?; + + tokio::task::spawn_blocking(move || { + with_libcodex_lock(|| { + node.peer_id() + .map_err(|e| NodeManagerError::NodeOperationError(e.to_string())) + }) + }) + .await + .map_err(|e| NodeManagerError::ThreadSafetyError(e.to_string()))? + } + + /// Get a reference to the managed node for operations + pub async fn get_node(&self) -> Result { + let node_guard = self.node.lock().await; + let running_guard = self.is_running.lock().await; + + if !*running_guard { + return Err(NodeManagerError::NodeNotRunning); + } + + node_guard + .as_ref() + .cloned() + .ok_or(NodeManagerError::NodeOperationError( + "Node not initialized".to_string(), + )) + } + + /// Upload a file using the managed node + pub async fn upload_file( + &self, + options: UploadOptions, + ) -> Result { + let node = self.get_node().await?; + + upload_file(&node, options) + .await + .map_err(|e| NodeManagerError::NodeOperationError(e.to_string())) + } +} + +impl Drop for CodexNodeManager { + fn drop(&mut self) { + // Note: This is a synchronous drop, so we can't use async operations here + // In a real implementation, you might want to ensure proper cleanup + // by calling stop() explicitly before the manager goes out of scope + info!("CodexNodeManager being dropped - ensure stop() was called explicitly"); + } +} diff --git a/src/node/mod.rs b/src/node/mod.rs new file mode 100644 index 0000000..ff8de9e --- /dev/null +++ b/src/node/mod.rs @@ -0,0 +1 @@ +pub mod manager; diff --git a/src/services/country.rs b/src/services/country.rs index 92e4d25..1336a0d 100644 --- a/src/services/country.rs +++ b/src/services/country.rs @@ -6,8 +6,6 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum CountryError { - #[error("Failed to load country codes: {0}")] - LoadFailed(String), #[error("IO error: {0}")] IoError(#[from] std::io::Error), #[error("JSON error: {0}")] @@ -51,140 +49,6 @@ impl CountryService { self.country_codes.get(country_code) } - pub async fn get_countries_paginated( - &self, - db_service: &crate::services::database::DatabaseService, - target_countries: &[String], - page: u32, - limit: u32, - query: Option<&str>, - ) -> Result, CountryError> { - let countries_to_process = self.get_countries_to_process(target_countries); - - let filtered_countries = if let Some(q) = query { - countries_to_process - .into_iter() - .filter(|code| { - if let Some(name) = self.country_codes.get(code) { - name.to_lowercase().contains(&q.to_lowercase()) - || code.to_lowercase().contains(&q.to_lowercase()) - } else { - false - } - }) - .collect() - } else { - countries_to_process - }; - - let mut countries = Vec::new(); - - // Get all countries with their counts - match db_service - .get_countries_locality_counts(&filtered_countries) - .await - { - Ok(counts) => { - for code in filtered_countries { - if let Some(name) = self.country_codes.get(&code) { - // Get the locality count from the batch result - let count = counts.get(&code).copied().unwrap_or(0); - - if count > 0 { - countries.push(crate::models::country::CountryInfo { - country_code: code.clone(), - country_name: name.clone(), - locality_count: count, - }); - } - } - } - } - Err(_) => { - for code in filtered_countries { - if let Some(name) = self.country_codes.get(&code) { - match db_service.get_country_locality_count(&code).await { - Ok(count) => { - if count > 0 { - countries.push(crate::models::country::CountryInfo { - country_code: code.clone(), - country_name: name.clone(), - locality_count: count, - }); - } - } - Err(_) => { - continue; - } - } - } - } - } - } - - // Sort by country name - countries.sort_by(|a, b| { - a.country_name - .to_lowercase() - .cmp(&b.country_name.to_lowercase()) - }); - - // Apply pagination - let offset = (page - 1) * limit; - let end = std::cmp::min(offset + limit, countries.len() as u32); - let paginated_countries = countries - .into_iter() - .skip(offset as usize) - .take((end - offset) as usize) - .collect(); - - Ok(paginated_countries) - } - - pub async fn get_countries_count( - &self, - db_service: &crate::services::database::DatabaseService, - target_countries: &[String], - query: Option<&str>, - ) -> Result { - let countries_to_process = self.get_countries_to_process(target_countries); - - let filtered_countries = if let Some(q) = query { - countries_to_process - .into_iter() - .filter(|code| { - if let Some(name) = self.country_codes.get(code) { - name.to_lowercase().contains(&q.to_lowercase()) - || code.to_lowercase().contains(&q.to_lowercase()) - } else { - false - } - }) - .collect() - } else { - countries_to_process - }; - - let mut count = 0; - - for code in filtered_countries { - if self.country_codes.contains_key(&code) { - match db_service.get_country_locality_count(&code).await { - Ok(locality_count) => { - if locality_count > 0 { - count += 1; - } - } - Err(_) => { - continue; - } - } - } - } - - Ok(count) - } - fn create_default_country_codes() -> HashMap { let mut codes = HashMap::new(); diff --git a/src/services/database.rs b/src/services/database.rs index 9ea595c..dd7e471 100644 --- a/src/services/database.rs +++ b/src/services/database.rs @@ -1,22 +1,11 @@ use crate::models::locality::Locality; -use crate::utils::file::FileError; use rusqlite::Connection; -use tracing::info; -use std::path::Path; use std::sync::Arc; use thiserror::Error; use tokio::sync::Mutex; #[derive(Error, Debug)] pub enum DatabaseError { - #[error("Database connection failed: {0}")] - ConnectionFailed(String), - #[error("Query failed: {0}")] - QueryFailed(String), - #[error("Database download failed: {0}")] - DownloadFailed(String), - #[error("Database decompression failed: {0}")] - DecompressionFailed(String), #[error("Rusqlite error: {0}")] RusqliteError(#[from] rusqlite::Error), #[error("Tokio join error: {0}")] @@ -24,35 +13,27 @@ pub enum DatabaseError { #[error("IO error: {0}")] IoError(#[from] std::io::Error), #[error("File error: {0}")] - FileError(#[from] FileError), + FileError(#[from] crate::utils::file::FileError), #[error("Command error: {0}")] CmdError(#[from] crate::utils::cmd::CmdError), } pub struct DatabaseService { conn: Arc>, - database_path: String, - whosonfirst_db_url: String, - bzip2_cmd: String, } impl DatabaseService { - pub async fn new( - _database_url: &str, - database_path: &str, - whosonfirst_db_url: &str, - bzip2_cmd: &str, - ) -> Result { + pub async fn new(database_path: &str) -> Result { let conn = Connection::open(database_path)?; let service = Self { conn: Arc::new(Mutex::new(conn)), - database_path: database_path.to_string(), - whosonfirst_db_url: whosonfirst_db_url.to_string(), - bzip2_cmd: bzip2_cmd.to_string(), }; - service.create_optimized_indexes().await?; + // Only create CID table if this is not a WhosOnFirst database + if !database_path.contains("whosonfirst") { + service.create_optimized_indexes().await?; + } Ok(service) } @@ -63,203 +44,32 @@ impl DatabaseService { tokio::task::spawn_blocking(move || { let conn = conn.blocking_lock(); - // Index for countries query - let create_countries_index = r#" - CREATE INDEX IF NOT EXISTS spr_countries_query_idx - ON spr (placetype, is_current, is_deprecated, country) - WHERE placetype = 'locality' AND is_current = 1 AND is_deprecated = 0 - "#; - - // Index for country count query - let create_country_count_index = r#" - CREATE INDEX IF NOT EXISTS spr_country_count_query_idx - ON spr (placetype, is_current, is_deprecated, country) - WHERE placetype = 'locality' AND is_current = 1 AND is_deprecated = 0 - "#; - - // Index for localities pagination queries - let create_pagination_index = r#" - CREATE INDEX IF NOT EXISTS spr_localities_pagination_idx - ON spr (placetype, is_current, is_deprecated, country, name) - WHERE placetype = 'locality' AND is_current = 1 AND is_deprecated = 0 - "#; - - // Index for localities search queries (case-insensitive) - let create_search_index = r#" - CREATE INDEX IF NOT EXISTS spr_localities_search_idx - ON spr (placetype, is_current, is_deprecated, country, name COLLATE NOCASE) - WHERE placetype = 'locality' AND is_current = 1 AND is_deprecated = 0 - "#; - - // Index for localities count queries - let create_count_index = r#" - CREATE INDEX IF NOT EXISTS spr_localities_count_idx - ON spr (placetype, is_current, is_deprecated, country) - WHERE placetype = 'locality' AND is_current = 1 AND is_deprecated = 0 + // Create CID mapping table + let create_cid_table = r#" + CREATE TABLE IF NOT EXISTS locality_cids ( + country_code TEXT NOT NULL, + locality_id INTEGER NOT NULL, + cid TEXT NOT NULL, + upload_time DATETIME DEFAULT CURRENT_TIMESTAMP, + file_size INTEGER, + PRIMARY KEY (country_code, locality_id) + ) "#; - // Index for localities search count queries (case-insensitive) - let create_search_count_index = r#" - CREATE INDEX IF NOT EXISTS spr_localities_search_count_idx - ON spr (placetype, is_current, is_deprecated, country, name COLLATE NOCASE) - WHERE placetype = 'locality' AND is_current = 1 AND is_deprecated = 0 + // Index for fast CID lookups + let create_cid_index = r#" + CREATE INDEX IF NOT EXISTS idx_locality_cids_lookup + ON locality_cids(country_code, locality_id) "#; - conn.execute(create_countries_index, [])?; - conn.execute(create_country_count_index, [])?; - conn.execute(create_pagination_index, [])?; - conn.execute(create_search_index, [])?; - conn.execute(create_count_index, [])?; - conn.execute(create_search_count_index, [])?; + conn.execute(create_cid_table, [])?; + conn.execute(create_cid_index, [])?; Ok::<(), DatabaseError>(()) }) .await? } - pub async fn ensure_database_present(&self) -> Result<(), DatabaseError> { - let path = Path::new(&self.database_path); - - if !path.exists() { - self.download_database().await?; - self.decompress_database().await?; - } - - Ok(()) - } - - pub async fn download_database(&self) -> Result<(), DatabaseError> { - let compressed_path = format!("{}.bz2", self.database_path); - let compressed_path = Path::new(&compressed_path); - - if !compressed_path.exists() { - info!("Downloading WhosOnFirst database..."); - - crate::utils::file::download_file_with_progress( - &self.whosonfirst_db_url, - compressed_path, - ) - .await?; - info!("Database download completed!"); - } - - Ok(()) - } - - pub async fn decompress_database(&self) -> Result<(), DatabaseError> { - let compressed_path = format!("{}.bz2", self.database_path); - let compressed_path = Path::new(&compressed_path); - let database_path = Path::new(&self.database_path); - - if compressed_path.exists() && !database_path.exists() { - info!("Decompressing database..."); - - let output = crate::utils::cmd::run_command( - &self.bzip2_cmd, - &["-dv", &compressed_path.to_string_lossy()], - None, - ) - .await?; - - if !output.stderr.is_empty() { - tracing::error!("Decompression output: {}", output.stderr); - } - - info!("Database decompressed successfully!"); - } - - Ok(()) - } - - pub async fn get_localities_count( - &self, - country_code: &str, - query: Option<&str>, - ) -> Result { - let conn = self.conn.clone(); - let country_code = country_code.to_string(); - let query_param = query.map(|q| format!("{}%", q)); - - tokio::task::spawn_blocking(move || { - let conn = conn.blocking_lock(); - - let conditions = [ - "placetype = 'locality'", - "is_current = 1", - "is_deprecated = 0", - "country = ?1", - ]; - - let where_clause = conditions.join(" AND "); - - let count = if let Some(q) = query_param { - let search_query = format!( - "SELECT COUNT(*) as count FROM spr WHERE {} AND name LIKE ?2 COLLATE NOCASE", - where_clause - ); - conn.query_row(&search_query, [&country_code, &q], |row| { - row.get::<_, i64>(0) - }) - } else { - let query_str = format!("SELECT COUNT(*) as count FROM spr WHERE {}", where_clause); - conn.query_row(&query_str, [&country_code], |row| row.get::<_, i64>(0)) - }; - - Ok(count.map(|c| c as u32)?) - }) - .await? - } - - pub async fn get_localities( - &self, - country_code: &str, - page: u32, - limit: u32, - query: Option<&str>, - ) -> Result, DatabaseError> { - let conn = self.conn.clone(); - let country_code = country_code.to_string(); - let query_param = query.map(|q| format!("{}%", q)); - let offset = (page - 1) * limit; - - tokio::task::spawn_blocking(move || { - let conn = conn.blocking_lock(); - - let conditions = [ - "placetype = 'locality'", - "is_current = 1", - "is_deprecated = 0", - "country = ?1", - ]; - - let where_clause = conditions.join(" AND "); - - let localities = if let Some(q) = query_param { - let search_query = format!( - "SELECT id, name, country, placetype, latitude, longitude, min_longitude, min_latitude, max_longitude, max_latitude FROM spr WHERE {} AND name LIKE ?2 COLLATE NOCASE ORDER BY name COLLATE NOCASE ASC LIMIT ?3 OFFSET ?4", - where_clause - ); - let mut stmt = conn.prepare(&search_query)?; - let rows = stmt.query_map([&country_code, &q, &limit.to_string(), &offset.to_string()], |row| { - Locality::from_row(row) - })?; - rows.collect::, _>>()? - } else { - let paginated_query = format!( - "SELECT id, name, country, placetype, latitude, longitude, min_longitude, min_latitude, max_longitude, max_latitude FROM spr WHERE {} ORDER BY name ASC LIMIT ?2 OFFSET ?3", - where_clause - ); - let mut stmt = conn.prepare(&paginated_query)?; - let rows = stmt.query_map([&country_code, &limit.to_string(), &offset.to_string()], |row| { - Locality::from_row(row) - })?; - rows.collect::, _>>()? - }; - - Ok(localities) - }).await? - } - pub async fn get_country_localities( &self, country_code: &str, @@ -326,56 +136,102 @@ impl DatabaseService { }).await? } - pub async fn get_countries_locality_counts( + /// Get a specific locality by ID + pub async fn get_locality_by_id(&self, locality_id: i64) -> Result, DatabaseError> { + let conn = self.conn.clone(); + + tokio::task::spawn_blocking(move || { + let conn = conn.blocking_lock(); + + let query = r#" + SELECT id, name, country, placetype, latitude, longitude, min_longitude, min_latitude, max_longitude, max_latitude + FROM spr + WHERE id = ?1 AND placetype = 'locality' AND is_current = 1 AND is_deprecated = 0 + "#; + + let mut stmt = conn.prepare(query)?; + let rows = stmt.query_map([&locality_id], |row| { + Locality::from_row(row) + })?; + + // Collect the first result (if any) + let localities: Result, _> = rows.collect(); + match localities { + Ok(locality_vec) => Ok(locality_vec.into_iter().next()), + Err(e) => Err(DatabaseError::RusqliteError(e)), + } + }).await? + } + + /// Batch insert CID mappings + pub async fn batch_insert_cid_mappings( &self, - country_codes: &[String], - ) -> Result, DatabaseError> { - if country_codes.is_empty() { - return Ok(std::collections::HashMap::new()); - } + mappings: &[(String, u32, String, u64)], + ) -> Result<(), DatabaseError> { + let conn = self.conn.clone(); + let mappings = mappings.to_vec(); + + tokio::task::spawn_blocking(move || { + let mut conn = conn.blocking_lock(); + + let tx = conn.transaction()?; + + let query = r#" + INSERT OR REPLACE INTO locality_cids + (country_code, locality_id, cid, file_size, upload_time) + VALUES (?1, ?2, ?3, ?4, CURRENT_TIMESTAMP) + "#; + + for (country_code, locality_id, cid, file_size) in mappings { + tx.execute(query, [&country_code as &dyn rusqlite::ToSql, &locality_id as &dyn rusqlite::ToSql, &cid as &dyn rusqlite::ToSql, &file_size as &dyn rusqlite::ToSql])?; + } + + tx.commit()?; + Ok(()) + }).await? + } + /// Check if a locality already has a CID mapping + pub async fn has_cid_mapping( + &self, + country_code: &str, + locality_id: u32, + ) -> Result { let conn = self.conn.clone(); - let country_codes = country_codes.to_vec(); + let country_code = country_code.to_string(); tokio::task::spawn_blocking(move || { let conn = conn.blocking_lock(); - let conditions = [ - "placetype = 'locality'", - "is_current = 1", - "is_deprecated = 0", - ]; + let query = r#" + SELECT COUNT(*) as count FROM locality_cids + WHERE country_code = ?1 AND locality_id = ?2 + "#; - let base_where_clause = conditions.join(" AND "); + let count = conn.query_row(query, [&country_code as &dyn rusqlite::ToSql, &locality_id as &dyn rusqlite::ToSql], |row| { + row.get::<_, i64>(0) + })?; - let placeholders: Vec<_> = country_codes.iter().map(|_| "?").collect(); - let in_clause = format!("country IN ({})", placeholders.join(",")); + Ok(count > 0) + }).await? + } - let where_clause = format!("{} AND {}", base_where_clause, in_clause); - let query_str = format!( - "SELECT country, COUNT(*) as count FROM spr WHERE {} GROUP BY country", - where_clause - ); + /// Get CID mapping statistics + pub async fn get_cid_mapping_stats(&self) -> Result<(u64, u64), DatabaseError> { + let conn = self.conn.clone(); - let mut stmt = conn.prepare(&query_str)?; + tokio::task::spawn_blocking(move || { + let conn = conn.blocking_lock(); - // Create parameter values - let params: Vec<&dyn rusqlite::ToSql> = country_codes - .iter() - .map(|s| s as &dyn rusqlite::ToSql) - .collect(); + // Get total mappings count + let total_query = "SELECT COUNT(*) as count FROM locality_cids"; + let total_count = conn.query_row(total_query, [], |row| row.get::<_, i64>(0))?; - let rows = stmt.query_map(params.as_slice(), |row| { - Ok((row.get::<_, String>(0)?, row.get::<_, i64>(1)?)) - })?; + // Get unique countries count + let countries_query = "SELECT COUNT(DISTINCT country_code) as count FROM locality_cids"; + let countries_count = conn.query_row(countries_query, [], |row| row.get::<_, i64>(0))?; - let mut counts = std::collections::HashMap::new(); - for row in rows { - let (country, count) = row?; - counts.insert(country, count as u32); - } - - Ok(counts) + Ok((total_count as u64, countries_count as u64)) }).await? } } diff --git a/src/services/extraction.rs b/src/services/extraction.rs index d9b2e92..80f4ae9 100644 --- a/src/services/extraction.rs +++ b/src/services/extraction.rs @@ -1,4 +1,4 @@ -use crate::config::Config; +use crate::config::LocalitySrvConfig; use crate::models::locality::Locality; use crate::utils::cmd::{run_command, CmdError}; use crate::utils::file::{ensure_dir_exists, FileError}; @@ -16,10 +16,6 @@ pub enum ExtractionError { PlanetUrlFailed(String), #[error("Extraction failed: {0}")] ExtractionFailed(String), - #[error("File operation failed: {0}")] - FileOperationFailed(String), - #[error("Command execution failed: {0}")] - CommandFailed(String), #[error("Database error: {0}")] DatabaseError(String), #[error("IO error: {0}")] @@ -32,12 +28,15 @@ pub enum ExtractionError { #[derive(Clone)] pub struct ExtractionService { - config: Arc, + config: Arc, db_service: Arc, } impl ExtractionService { - pub fn new(config: Arc, db_service: Arc) -> Self { + pub fn new( + config: Arc, + db_service: Arc, + ) -> Self { Self { config, db_service } } @@ -56,46 +55,10 @@ impl ExtractionService { } } - // Fall back to fetching the remote URL - info!("Fetching latest planet pmtiles URL..."); - - let response = reqwest::get(&self.config.protomaps_builds_url) - .await - .map_err(|e| { - ExtractionError::PlanetUrlFailed(format!("Failed to fetch builds: {}", e)) - })?; - - if !response.status().is_success() { - return Err(ExtractionError::PlanetUrlFailed(format!( - "Failed to fetch builds: {}", - response.status() - ))); - } - - let builds: Vec = response.json().await.map_err(|e| { - ExtractionError::PlanetUrlFailed(format!("Failed to parse builds: {}", e)) - })?; - - if builds.is_empty() { - return Err(ExtractionError::PlanetUrlFailed( - "No builds found".to_string(), - )); - } - - let latest_build = builds - .iter() - .max_by_key(|build| build.get("uploaded").and_then(|v| v.as_str()).unwrap_or("")) - .ok_or_else(|| ExtractionError::PlanetUrlFailed("No valid builds found".to_string()))?; - - let key = latest_build - .get("key") - .and_then(|v| v.as_str()) - .ok_or_else(|| ExtractionError::PlanetUrlFailed("Build has no key".to_string()))?; - - let url = format!("https://build.protomaps.com/{}", key); - info!("Latest planet pmtiles URL: {}", url); - - Ok(url) + // No HTTP fetching in decentralized mode - require local file + Err(ExtractionError::PlanetUrlFailed( + "No local planet pmtiles file configured. Set PLANET_PMTILES_PATH environment variable.".to_string() + )) } pub async fn extract_locality( @@ -161,7 +124,7 @@ impl ExtractionService { for country_code in country_codes { info!("Processing country: {}", country_code); - let country_dir = self.config.localities_dir().join(country_code); + let country_dir = self.config.localities_dir.join(country_code); ensure_dir_exists(&country_dir)?; let localities = self @@ -269,20 +232,8 @@ impl ExtractionService { Ok(()) } - pub async fn ensure_all_localities_present(&self) -> Result<(), ExtractionError> { - let countries = self.config.target_countries.clone(); - - if countries.is_empty() { - return Err(ExtractionError::ExtractionFailed( - "No target countries specified".to_string(), - )); - } - - self.extract_localities(&countries).await - } - - async fn get_pmtiles_file_count(&self, country_code: &str) -> Result { - let country_dir = self.config.localities_dir().join(country_code); + pub async fn get_pmtiles_file_count(&self, country_code: &str) -> Result { + let country_dir = self.config.localities_dir.join(country_code); if !country_dir.exists() { return Ok(0); diff --git a/src/services/mod.rs b/src/services/mod.rs index edbef91..faaa55d 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -1,4 +1,4 @@ pub mod country; pub mod database; pub mod extraction; -pub mod tor; +pub mod node_ops; diff --git a/src/services/node_ops.rs b/src/services/node_ops.rs new file mode 100644 index 0000000..ee4ea6d --- /dev/null +++ b/src/services/node_ops.rs @@ -0,0 +1,360 @@ +use crate::models::storage::{CompletedUpload, PendingUpload, UploadQueue, UploadStats}; +use crate::node::manager::{CodexNodeManager, NodeManagerError}; +use crate::services::database::{DatabaseError, DatabaseService}; +use codex_bindings::UploadOptions; +use futures::future::join_all; +use std::sync::Arc; +use thiserror::Error; +use tokio::sync::Mutex; +use tracing::{error, info, warn}; + +#[derive(Error, Debug)] +pub enum NodeOpsError { + #[error("Database error: {0}")] + DatabaseError(#[from] DatabaseError), + #[error("Node manager error: {0}")] + NodeManagerError(#[from] NodeManagerError), + #[error("File error: {0}")] + FileError(#[from] std::io::Error), + #[error("Upload queue error: {0}")] + QueueError(String), +} + +pub struct NodeOps { + db_service: Arc, // CID mappings database + whosonfirst_db_service: Arc, // WhosOnFirst database + node_manager: Arc, + upload_queue: Arc>, + stats: Arc>, +} + +impl NodeOps { + pub fn new_with_databases( + cid_db_service: Arc, + whosonfirst_db_service: Arc, + node_manager: Arc, + ) -> Self { + Self { + db_service: cid_db_service, + whosonfirst_db_service, + node_manager, + upload_queue: Arc::new(Mutex::new(UploadQueue::new(10, 100))), + stats: Arc::new(Mutex::new(UploadStats::new())), + } + } + + /// Process all localities by scanning filesystem first + pub async fn process_all_localities(&self) -> Result<(), NodeOpsError> { + info!("Starting to process all localities by scanning filesystem for PMTiles files"); + + // Scan the assets/localities directory for all PMTiles files + let localities_dir = std::path::Path::new("assets/localities"); + if !localities_dir.exists() { + warn!("Localities directory not found: {:?}", localities_dir); + return Ok(()); + } + + let mut total_files = 0; + let mut processed_files = 0; + + // Iterate through all country directories + for country_dir_entry in std::fs::read_dir(localities_dir)? { + let country_dir = country_dir_entry?; + let country_path = country_dir.path(); + + if !country_path.is_dir() { + continue; + } + + let country_code = country_path + .file_name() + .and_then(|name| name.to_str()) + .ok_or_else(|| { + NodeOpsError::QueueError("Invalid country directory name".to_string()) + })?; + + info!("Scanning country directory: {}", country_code); + + // Process all PMTiles files in this country directory + let (country_files, country_processed) = self + .process_country_directory(&country_path, country_code) + .await?; + total_files += country_files; + processed_files += country_processed; + } + + // Process any remaining uploads in the queue + if !self.upload_queue.lock().await.is_empty() { + info!("Processing remaining uploads in queue..."); + self.process_upload_queue().await?; + } + + let stats = self.stats.lock().await; + info!( + "Filesystem scan completed! Total files found: {}, Total processed: {}, Total uploaded: {}, Total failed: {}, Total bytes: {}", + total_files, processed_files, stats.total_uploaded, stats.total_failed, stats.total_bytes_uploaded + ); + + Ok(()) + } + + /// Process all PMTiles files in a country directory + async fn process_country_directory( + &self, + country_path: &std::path::Path, + country_code: &str, + ) -> Result<(usize, usize), NodeOpsError> { + let mut total_files = 0; + let mut processed_files = 0; + + // Iterate through all PMTiles files in the country directory + for file_entry in std::fs::read_dir(country_path)? { + let file_entry = file_entry?; + let file_path = file_entry.path(); + + // Check if it's a PMTiles file + if !file_path.is_file() || file_path.extension().is_none_or(|ext| ext != "pmtiles") { + continue; + } + + total_files += 1; + + // Extract locality ID from filename (without .pmtiles extension) + let filename = file_path + .file_stem() + .and_then(|name| name.to_str()) + .ok_or_else(|| NodeOpsError::QueueError("Invalid filename".to_string()))?; + + let locality_id = filename.parse::().map_err(|_| { + NodeOpsError::QueueError(format!("Invalid locality ID in filename: {}", filename)) + })?; + + // Check if this locality exists in WhosOnFirst database + match self + .whosonfirst_db_service + .get_locality_by_id(locality_id as i64) + .await + { + Ok(Some(_locality)) => { + // Locality exists in database, proceed with upload + if self + .process_file_for_upload(&file_path, country_code, locality_id) + .await? + { + processed_files += 1; + } + } + Ok(None) => { + warn!( + "Locality ID {} found in filesystem but not in database, skipping", + locality_id + ); + } + Err(e) => { + error!("Database error checking locality {}: {}", locality_id, e); + } + } + } + + info!( + "Country {}: {} files found, {} processed", + country_code, total_files, processed_files + ); + Ok((total_files, processed_files)) + } + + /// Process a single file for upload + async fn process_file_for_upload( + &self, + file_path: &std::path::Path, + country_code: &str, + locality_id: u32, + ) -> Result { + // Check if already uploaded + if self + .db_service + .has_cid_mapping(country_code, locality_id) + .await? + { + info!("Locality {} already uploaded, skipping", locality_id); + return Ok(false); + } + + // Create pending upload + let pending_upload = PendingUpload::new( + country_code.to_string(), + locality_id, + file_path.to_path_buf(), + ); + + // Add to queue + { + let mut queue = self.upload_queue.lock().await; + if let Err(e) = queue.add_upload(pending_upload) { + warn!("Failed to add upload to queue: {}", e); + return Ok(false); + } + } + + // Process queue if it's full + if self.upload_queue.lock().await.is_full() { + self.process_upload_queue().await?; + } + + Ok(true) + } + + /// Process the upload queue + async fn process_upload_queue(&self) -> Result<(), NodeOpsError> { + let batch = { + let mut queue = self.upload_queue.lock().await; + queue.take_batch() + }; + + if batch.is_empty() { + return Ok(()); + } + + info!("Processing batch of {} uploads", batch.len()); + + // Upload all files in batch concurrently + let upload_tasks: Vec<_> = batch + .into_iter() + .map(|pending| self.upload_single_file(pending)) + .collect(); + + let results = join_all(upload_tasks).await; + + // Separate successful and failed uploads + let mut successful_uploads = Vec::new(); + let mut failed_count = 0; + + for result in results { + match result { + Ok(upload) => successful_uploads.push(upload), + Err(e) => { + error!("Upload failed: {}", e); + failed_count += 1; + } + } + } + + // Update database with successful uploads + if !successful_uploads.is_empty() { + self.batch_update_cid_mappings(&successful_uploads).await?; + + // Update stats + let mut stats = self.stats.lock().await; + for upload in &successful_uploads { + stats.increment_uploaded(upload.file_size); + } + } + + // Update failed stats + { + let mut stats = self.stats.lock().await; + for _ in 0..failed_count { + stats.increment_failed(); + } + } + + info!( + "Batch completed: {} successful, {} failed", + successful_uploads.len(), + failed_count + ); + + Ok(()) + } + + /// Upload a single file to Codex using the managed node + async fn upload_single_file( + &self, + pending: PendingUpload, + ) -> Result { + let file_path = &pending.file_path; + + // Verify file exists before attempting upload + if !file_path.exists() { + return Err(NodeOpsError::FileError(std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("File not found: {:?}", file_path), + ))); + } + + // Get file size + let file_size = tokio::fs::metadata(file_path).await?.len(); + + info!( + "Uploading locality {} from country {} ({} bytes) using managed node", + pending.locality_id, pending.country_code, file_size + ); + + // Create upload options with progress callback + let locality_id = pending.locality_id; + let country_code = pending.country_code.clone(); + + let upload_options = + UploadOptions::new() + .filepath(file_path) + .on_progress(move |progress| { + let percentage = (progress.percentage * 100.0) as u32; + info!( + "Upload progress for locality {} ({}): {}%", + locality_id, country_code, percentage + ); + }); + + // Use the managed node instead of creating a temporary one + let upload_result = self + .node_manager + .upload_file(upload_options) + .await + .map_err(|e| { + error!("Upload failed for locality {}: {}", pending.locality_id, e); + e + })?; + + let completed_upload = CompletedUpload::new( + pending.country_code.clone(), + pending.locality_id, + upload_result.cid.clone(), + file_size, + ); + + info!( + "Successfully uploaded locality {} with CID: {} using managed node", + pending.locality_id, upload_result.cid + ); + + Ok(completed_upload) + } + + /// Batch update CID mappings in database + async fn batch_update_cid_mappings( + &self, + uploads: &[CompletedUpload], + ) -> Result<(), NodeOpsError> { + let mappings: Vec<_> = uploads + .iter() + .map(|upload| { + ( + upload.country_code.clone(), + upload.locality_id, + upload.cid.clone(), + upload.file_size, + ) + }) + .collect(); + + self.db_service.batch_insert_cid_mappings(&mappings).await?; + + info!("Updated {} CID mappings in database", mappings.len()); + Ok(()) + } + + /// Get current upload statistics + pub async fn get_stats(&self) -> UploadStats { + self.stats.lock().await.clone() + } +} diff --git a/src/services/tor.rs b/src/services/tor.rs deleted file mode 100644 index e07ece5..0000000 --- a/src/services/tor.rs +++ /dev/null @@ -1,191 +0,0 @@ -use crate::AppState; -use anyhow::Result; -use arti_client::{TorClient, TorClientConfig}; -use axum::Router; -use futures::StreamExt; -use hyper::{body::Incoming, Request}; -use hyper_util::rt::{TokioExecutor, TokioIo}; -use hyper_util::server; -use safelog::{sensitive, DisplayRedacted as _}; -use std::sync::Arc; -use tor_cell::relaycell::msg::Connected; -use tor_hsservice::{config::OnionServiceConfigBuilder, StreamRequest}; -use tor_proto::client::stream::IncomingStreamRequest; -use tower::Service; -use tracing::{debug, error, info, warn}; - -pub struct TorServiceManager { - app: Router, - app_state: AppState, - shutdown_signal: Arc, -} - -impl TorServiceManager { - pub fn new( - app: Router, - app_state: AppState, - shutdown_signal: Arc, - ) -> Self { - Self { - app, - app_state, - shutdown_signal, - } - } - - pub async fn run_with_retry(&self, onion_address_tx: tokio::sync::oneshot::Sender) { - let mut retry_count = 0; - let max_retries = 5; - let base_delay = std::time::Duration::from_secs(5); - let mut onion_address_tx = Some(onion_address_tx); - - loop { - let result = self.run_tor_service(onion_address_tx.take()).await; - - match result { - Ok(_) => { - info!("Tor hidden service stopped successfully"); - break; - } - Err(e) if retry_count >= max_retries => { - error!( - "Max retries ({}) reached for Tor hidden service: {}. Giving up.", - max_retries, e - ); - break; - } - Err(e) => { - retry_count += 1; - let delay = base_delay * retry_count; - warn!( - "Tor hidden service failed (attempt {}/{}): {}. Restarting in {:?}...", - retry_count, max_retries, e, delay - ); - - tokio::time::sleep(delay).await; - } - } - } - } - - async fn run_tor_service( - &self, - onion_address_tx: Option>, - ) -> Result<()> { - info!("Starting Tor hidden service..."); - - let config = TorClientConfig::default(); - - info!("Bootstrapping Tor client..."); - let client = TorClient::create_bootstrapped(config).await?; - info!("Tor client bootstrapped successfully"); - - let svc_cfg = OnionServiceConfigBuilder::default() - .nickname("localitysrv".parse().unwrap()) - .build()?; - - info!("Launching onion service..."); - let (service, request_stream) = client.launch_onion_service(svc_cfg)?; - let onion_address = service - .onion_address() - .unwrap() - .display_unredacted() - .to_string(); - - { - let mut config = self.app_state.config.lock().await; - config.onion_address = Some(onion_address.clone()); - } - - info!("Tor hidden service launched at: {}", onion_address); - - if let Some(tx) = onion_address_tx { - let _ = tx.send(onion_address.clone()); - } - - info!("Waiting for Tor hidden service to be fully reachable..."); - let mut status_events = service.status_events(); - - while let Some(status) = status_events.next().await { - if status.state().is_fully_reachable() { - info!( - "✓ Tor hidden service is now fully reachable at http://{}", - onion_address - ); - break; - } - - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - status_events = service.status_events(); - } - - let stream_requests = tor_hsservice::handle_rend_requests(request_stream); - tokio::pin!(stream_requests); - - loop { - tokio::select! { - biased; - _ = self.shutdown_signal.notified() => { - info!("Tor hidden service shutting down..."); - drop(service); - return Ok(()); - } - Some(stream_request) = stream_requests.next() => { - let app_clone = self.app.clone(); - - tokio::spawn(async move { - let request = stream_request.request().clone(); - if let Err(err) = handle_stream_request(stream_request, app_clone).await { - warn!("Error serving connection {:?}: {}. Arti will handle recovery.", sensitive(request), err); - }; - }); - } - } - } - } -} - -async fn handle_stream_request(stream_request: StreamRequest, app: Router) -> Result<()> { - debug!("Handling new stream request"); - - match stream_request.request() { - IncomingStreamRequest::Begin(begin) if begin.port() == 80 => { - debug!("Accepting stream request on port 80"); - - match stream_request.accept(Connected::new_empty()).await { - Ok(onion_service_stream) => { - debug!("Stream accepted successfully"); - let io = TokioIo::new(onion_service_stream); - - let hyper_service = - hyper::service::service_fn(move |request: Request| { - app.clone().call(request) - }); - - match server::conn::auto::Builder::new(TokioExecutor::new()) - .serve_connection(io, hyper_service) - .await - { - Ok(_) => { - debug!("Connection served successfully"); - Ok(()) - } - Err(e) => { - warn!("Connection error: {}. Arti will handle recovery.", e); - Err(anyhow::anyhow!(e)) - } - } - } - Err(e) => { - warn!("Failed to accept stream: {}. Arti will handle recovery.", e); - Err(anyhow::anyhow!(e)) - } - } - } - _ => { - debug!("Rejecting stream request on non-port 80"); - stream_request.shutdown_circuit()?; - Ok(()) - } - } -} diff --git a/src/utils/cmd.rs b/src/utils/cmd.rs index ba58a0e..197a5c4 100644 --- a/src/utils/cmd.rs +++ b/src/utils/cmd.rs @@ -5,8 +5,6 @@ use tokio::process::Command as TokioCommand; #[derive(Error, Debug)] pub enum CmdError { - #[error("Command execution failed: {0}")] - ExecutionFailed(String), #[error("Command not found: {0}")] CommandNotFound(String), #[error("IO error: {0}")] diff --git a/src/utils/file.rs b/src/utils/file.rs index b28ecc1..e7f98c8 100644 --- a/src/utils/file.rs +++ b/src/utils/file.rs @@ -11,8 +11,6 @@ use tracing::info; pub enum FileError { #[error("Download failed: {0}")] DownloadFailed(String), - #[error("File operation failed: {0}")] - FileOperationFailed(String), #[error("IO error: {0}")] IoError(String), #[error("Reqwest error: {0}")] diff --git a/tests/integration_test.rs b/tests/integration_test.rs new file mode 100644 index 0000000..5b7bcba --- /dev/null +++ b/tests/integration_test.rs @@ -0,0 +1,379 @@ +//! Real integration test that demonstrates the complete workflow with actual Codex uploads +//! +//! This test uses real PMTiles files and performs actual Codex network uploads. + +use localitysrv::config::LocalitySrvConfig; +use localitysrv::node::manager::CodexNodeManager; +use localitysrv::services::database::DatabaseService; +use localitysrv::services::node_ops::NodeOps; +use std::fs; +use std::sync::Arc; + +#[tokio::test] +async fn test_real_codex_integration() -> Result<(), Box> { + // Initialize logging to see Codex logs + let _ = env_logger::try_init(); + + println!("Starting real Codex integration test"); + + // Use tests/resources directory for all test data + let test_resources_dir = std::path::Path::new("tests/resources"); + let whosonfirst_db_path = test_resources_dir.join("test_whosonfirst.db"); + let cid_db_path = test_resources_dir.join("test_cid_mappings.db"); + let localities_dir = test_resources_dir.join("localities"); + + // Clean up any existing test data + cleanup_test_data(&test_resources_dir).await?; + + // Create directories if they don't exist + std::fs::create_dir_all(&localities_dir)?; + std::fs::create_dir_all(localities_dir.join("AE"))?; + + // Create localities directory structure + std::fs::create_dir_all(&localities_dir)?; + std::fs::create_dir_all(localities_dir.join("AE"))?; + + // Copy real PMTiles files to test directory + copy_test_pmtiles_files(&localities_dir).await?; + println!("✓ Copied real PMTiles files to test directory"); + + // Create test configuration + let config = create_test_config(&whosonfirst_db_path, &cid_db_path, &localities_dir)?; + + // Initialize WhosOnFirst database service (read-only) + let whosonfirst_db_service = + Arc::new(DatabaseService::new(&whosonfirst_db_path.to_string_lossy()).await?); + + // Initialize CID mappings database service (read-write) + let cid_db_service = Arc::new(DatabaseService::new(&cid_db_path.to_string_lossy()).await?); + + // Create a real node manager + let node_manager = Arc::new(CodexNodeManager::new(config.codex.clone())); + + // Create node operations service with separate databases + let node_ops = NodeOps::new_with_databases( + cid_db_service.clone(), + whosonfirst_db_service.clone(), + node_manager.clone(), + ); + + println!("✓ Setup completed successfully"); + + // Test 1: Verify PMTiles files exist and are real + verify_pmtiles_files(&localities_dir).await?; + println!("✓ PMTiles files verified"); + + // Test 2: Insert test CID mappings into CID database + insert_test_locality_data(&cid_db_service).await?; + println!("✓ Test CID mappings inserted into CID database"); + + // Test 3: Test real Codex uploads + let (real_cid, real_size) = test_real_codex_uploads(&node_ops, &cid_db_service).await?; + println!("✓ Real Codex uploads completed"); + println!(" Real CID: {}", real_cid); + println!(" Real size: {} bytes", real_size); + + // Test 4: Verify CID mappings database contains uploaded CIDs + verify_database_cids(&cid_db_service).await?; + println!("✓ Database CID mappings verified"); + + println!("Real Codex integration test completed successfully!"); + println!("WhosOnFirst database saved at: {:?}", whosonfirst_db_path); + println!("CID mappings database saved at: {:?}", cid_db_path); + println!( + "You can inspect the databases with: sqlite3 {:?} and sqlite3 {:?}", + whosonfirst_db_path, cid_db_path + ); + + // Clean up test data after successful test + cleanup_test_data(&test_resources_dir).await?; + println!("✓ Cleaned up test data after test completion"); + + Ok(()) +} + +async fn copy_test_pmtiles_files( + localities_dir: &std::path::Path, +) -> Result<(), Box> { + let source_dir = std::path::Path::new("assets/localities/AE"); + let target_dir = localities_dir.join("AE"); + + // Copy the real PMTiles files from assets to test resources + let test_files = [ + "421168683.pmtiles", + "421168685.pmtiles", + "421168687.pmtiles", + ]; + + for file in &test_files { + let src = source_dir.join(file); + let dst = target_dir.join(file); + + if src.exists() { + tokio::fs::copy(&src, &dst).await?; + println!(" Copied {} from assets to test directory", file); + } else { + return Err(format!("Source file not found: {:?}", src).into()); + } + } + + Ok(()) +} + +async fn verify_pmtiles_files( + localities_dir: &std::path::Path, +) -> Result<(), Box> { + let ae_dir = localities_dir.join("AE"); + let test_files = [ + "421168683.pmtiles", + "421168685.pmtiles", + "421168687.pmtiles", + ]; + + for file in &test_files { + let file_path = ae_dir.join(file); + + // Check file exists + assert!( + file_path.exists(), + "PMTiles file should exist: {:?}", + file_path + ); + + // Check file size (should be substantial, not empty) + let metadata = tokio::fs::metadata(&file_path).await?; + assert!( + metadata.len() > 1000, + "PMTiles file should be substantial: {} bytes", + metadata.len() + ); + + println!(" Verified {}: {} bytes", file, metadata.len()); + } + + Ok(()) +} + +async fn insert_test_locality_data( + cid_db_service: &Arc, +) -> Result<(), Box> { + println!("Inserting test CID mappings for AE country..."); + + // Insert some test CID mappings to simulate uploaded localities + let test_mappings = vec![ + ( + "AE".to_string(), + 421168683, + "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi421168683".to_string(), + 10965857, // Real file size + ), + ( + "AE".to_string(), + 421168685, + "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi421168685".to_string(), + 1327870, // Real file size + ), + ( + "AE".to_string(), + 421168687, + "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi421168687".to_string(), + 7484944, // Real file size + ), + ]; + + // For testing purposes, we'll insert these as if they were already uploaded + // to test the database operations. In the real upload test, we'll overwrite these. + cid_db_service + .batch_insert_cid_mappings(&test_mappings) + .await?; + + println!(" Inserted {} test CID mappings", test_mappings.len()); + Ok(()) +} + +async fn test_real_codex_uploads( + _node_ops: &NodeOps, + db_service: &Arc, +) -> Result<(String, u64), Box> { + println!("Testing real Codex uploads..."); + + // Test direct Codex upload using the bindings + println!(" Testing direct Codex upload..."); + + let test_file_path = + std::path::PathBuf::from("tests/resources/localities/AE/421168683.pmtiles"); + if !test_file_path.exists() { + return Err("Test PMTiles file not found".into()); + } + + // Create a Codex node using test resources directory + let test_resources_dir = std::path::Path::new("tests/resources"); + let config = codex_bindings::CodexConfig::new() + .log_level(codex_bindings::LogLevel::Info) + .data_dir(test_resources_dir.join(".codex-test-data")) + .storage_quota(100 * 1024 * 1024) // 100MB + .discovery_port(0); // Use random port + + let mut node = codex_bindings::CodexNode::new(config)?; + node.start()?; + + println!(" Started temporary Codex node for upload test"); + + // Perform real upload + let upload_options = codex_bindings::UploadOptions::new() + .filepath(&test_file_path) + .on_progress(|progress| { + let percentage = (progress.percentage * 100.0) as u32; + println!( + " Upload progress: {} bytes ({}%)", + progress.bytes_uploaded, percentage + ); + }); + + let upload_result = codex_bindings::upload_file(&node, upload_options).await?; + + println!(" ✓ Real upload completed!"); + println!(" CID: {}", upload_result.cid); + println!(" Size: {} bytes", upload_result.size); + println!(" Duration: {} ms", upload_result.duration_ms); + + // Verify the uploaded content exists + let exists = codex_bindings::exists(&node, &upload_result.cid).await?; + assert!(exists, "Uploaded content should exist in Codex node"); + println!(" ✓ Upload verification passed - content exists in node"); + + // Test downloading to verify round-trip + let test_resources_dir = std::path::Path::new("tests/resources"); + let download_path = test_resources_dir.join("downloaded.pmtiles"); + let download_options = + codex_bindings::DownloadStreamOptions::new(&upload_result.cid).filepath(&download_path); + + let download_result = + codex_bindings::download_stream(&node, &upload_result.cid, download_options).await?; + + println!(" ✓ Download verification completed!"); + println!(" Downloaded size: {} bytes", download_result.size); + + // Verify file sizes match + let original_size = tokio::fs::metadata(&test_file_path).await?.len(); + let downloaded_size = tokio::fs::metadata(&download_path).await?.len(); + + assert_eq!( + original_size, downloaded_size as u64, + "Downloaded file should match original size" + ); + println!(" ✓ File size verification passed: {} bytes", original_size); + + // Store the real CID in the database + let real_cid_mapping = vec![( + "AE".to_string(), + 421168683, + upload_result.cid.clone(), + upload_result.size as u64, + )]; + + db_service + .batch_insert_cid_mappings(&real_cid_mapping) + .await?; + println!(" ✓ Stored real CID in database: {}", upload_result.cid); + + // Cleanup + node.stop()?; + node.destroy()?; + + println!(" ✓ Real Codex upload test completed successfully"); + + Ok((upload_result.cid, upload_result.size as u64)) +} + +async fn verify_database_cids( + db_service: &Arc, +) -> Result<(), Box> { + println!("Verifying database CID mappings..."); + + // Check if our test mappings exist + let test_locality_ids = [421168683, 421168685, 421168687]; + + for locality_id in &test_locality_ids { + let has_mapping = db_service.has_cid_mapping("AE", *locality_id).await?; + println!(" AE-{} has CID mapping: {}", locality_id, has_mapping); + } + + println!("✓ Database verification completed"); + Ok(()) +} + +fn create_test_config( + whosonfirst_db_path: &std::path::Path, + cid_db_path: &std::path::Path, + localities_dir: &std::path::Path, +) -> Result> { + use codex_bindings::{CodexConfig, LogLevel}; + + let test_resources_dir = std::path::Path::new("tests/resources"); + let codex_data_dir = test_resources_dir.join(".codex-test-data"); + + let codex_config = CodexConfig::new() + .log_level(LogLevel::Error) + .data_dir(&codex_data_dir) + .storage_quota(100 * 1024 * 1024) // 100MB + .discovery_port(8098) // Use different port to avoid conflicts + .listen_addrs(vec!["/ip4/127.0.0.1/tcp/0".to_string()]); + + Ok(LocalitySrvConfig { + codex: codex_config, + data_dir: codex_data_dir, + database_path: whosonfirst_db_path.to_path_buf(), + cid_database_path: cid_db_path.to_path_buf(), // Separate database for CID mappings + localities_dir: localities_dir.to_path_buf(), + pmtiles_cmd: "pmtiles".to_string(), + bzip2_cmd: "bzip2".to_string(), + find_cmd: "find".to_string(), + whosonfirst_db_url: "https://example.com/test.db".to_string(), + planet_pmtiles_path: None, + target_countries: vec!["AE".to_string()], // Only test AE country + max_concurrent_extractions: 1, + }) +} + +async fn cleanup_test_data( + test_resources_dir: &std::path::Path, +) -> Result<(), Box> { + // Remove test databases if they exist + let test_whosonfirst_db_path = test_resources_dir.join("test_whosonfirst.db"); + if test_whosonfirst_db_path.exists() { + fs::remove_file(&test_whosonfirst_db_path)?; + println!( + "✓ Removed existing WhosOnFirst test database: {:?}", + test_whosonfirst_db_path + ); + } + + let test_cid_db_path = test_resources_dir.join("test_cid_mappings.db"); + if test_cid_db_path.exists() { + fs::remove_file(&test_cid_db_path)?; + println!( + "✓ Removed existing CID mappings test database: {:?}", + test_cid_db_path + ); + } + + // Remove .codex-test-data directory if it exists + let codex_test_dir = test_resources_dir.join(".codex-test-data"); + if codex_test_dir.exists() { + fs::remove_dir_all(&codex_test_dir)?; + println!( + "✓ Removed existing test Codex data directory: {:?}", + codex_test_dir + ); + } + + // Remove downloaded test file if it exists + let downloaded_file = test_resources_dir.join("downloaded.pmtiles"); + if downloaded_file.exists() { + fs::remove_file(&downloaded_file)?; + println!("✓ Removed downloaded test file: {:?}", downloaded_file); + } + + Ok(()) +} diff --git a/tests/single_node_usage_test.rs b/tests/single_node_usage_test.rs new file mode 100644 index 0000000..b60d513 --- /dev/null +++ b/tests/single_node_usage_test.rs @@ -0,0 +1,196 @@ +//! Test to verify that localitysrv uses a single node for all operations +//! instead of creating temporary nodes for each upload. + +use codex_bindings::{CodexConfig, LogLevel}; +use localitysrv::models::storage::{PendingUpload, UploadQueue, UploadStats}; +use localitysrv::node::manager::CodexNodeManager; +use localitysrv::services::database::DatabaseService; +use localitysrv::services::node_ops::NodeOps; +use std::path::PathBuf; +use std::sync::Arc; +use tempfile::TempDir; + +#[tokio::test] +async fn test_node_manager_single_instance() -> Result<(), Box> { + // Create a temporary directory for the test + let temp_dir = TempDir::new()?; + let data_dir = temp_dir.path().join("codex_data"); + + // Create codex config + let codex_config = CodexConfig::new() + .log_level(LogLevel::Error) + .data_dir(&data_dir) + .storage_quota(100 * 1024 * 1024) // 100MB + .discovery_port(0); // Use random port + + // Create node manager + let node_manager = Arc::new(CodexNodeManager::new(codex_config)); + + // Start the node + node_manager.start().await?; + + // Test that we can get the node multiple times and it's the same instance + let node1 = node_manager.get_node().await?; + let peer_id1 = tokio::task::spawn_blocking(move || node1.peer_id()).await??; + + let node2 = node_manager.get_node().await?; + let peer_id2 = tokio::task::spawn_blocking(move || node2.peer_id()).await??; + + // Both nodes should have the same peer ID (same instance) + assert_eq!(peer_id1, peer_id2); + + // Stop the node + node_manager.stop().await?; + + // Note: We don't call destroy() here because the Arc still has references + // In a real application, the node would be destroyed when all references are dropped + + Ok(()) +} + +#[tokio::test] +async fn test_node_ops_uses_managed_node() -> Result<(), Box> { + // This test verifies that NodeOps uses the managed node + // We can't test actual uploads without a real Codex network, + // but we can verify the structure is correct + + let temp_dir = TempDir::new()?; + let db_path = temp_dir.path().join("test.db"); + + // Create a mock database service + let db_service = Arc::new(DatabaseService::new(&db_path.to_string_lossy()).await?); + + // Create codex config + let codex_config = CodexConfig::new() + .log_level(LogLevel::Error) + .data_dir(temp_dir.path().join("codex_data")) + .storage_quota(100 * 1024 * 1024) + .discovery_port(0); + + // Create node manager + let node_manager = Arc::new(CodexNodeManager::new(codex_config)); + + // Create NodeOps with the managed node + let node_ops = + NodeOps::new_with_databases(db_service.clone(), db_service, node_manager.clone()); + + // Verify NodeOps was created successfully + let stats = node_ops.get_stats().await; + assert_eq!(stats.total_uploaded, 0); + assert_eq!(stats.total_failed, 0); + + Ok(()) +} + +#[tokio::test] +async fn test_concurrent_uploads_use_same_node() -> Result<(), Box> { + // Test that multiple concurrent uploads would use the same node + let temp_dir = TempDir::new()?; + + // Create codex config + let codex_config = CodexConfig::new() + .log_level(LogLevel::Error) + .data_dir(temp_dir.path().join("codex_data")) + .storage_quota(100 * 1024 * 1024) + .discovery_port(0); + + // Create node manager + let node_manager = Arc::new(CodexNodeManager::new(codex_config)); + + // Start the node + node_manager.start().await?; + + // Get multiple node references concurrently + let mut handles = Vec::new(); + for _ in 0..5 { + let manager = node_manager.clone(); + let handle = tokio::spawn(async move { + let node = manager + .get_node() + .await + .map_err(|e| format!("Failed to get node: {}", e))?; + let peer_id = tokio::task::spawn_blocking(move || { + node.peer_id() + .map_err(|e| format!("Failed to get peer ID: {}", e)) + }) + .await + .map_err(|e| format!("Thread safety error: {}", e))??; + Ok::(peer_id) + }); + handles.push(handle); + } + + // Wait for all tasks to complete + let mut peer_ids = Vec::new(); + for handle in handles { + let peer_id = handle + .await + .map_err(|e| format!("Task join error: {}", e))??; + peer_ids.push(peer_id); + } + + // All peer IDs should be the same (same node instance) + let first_peer_id = &peer_ids[0]; + for peer_id in &peer_ids[1..] { + assert_eq!(first_peer_id, peer_id); + } + + // Stop the node + node_manager.stop().await?; + + Ok(()) +} + +#[test] +fn test_upload_queue_functionality() -> Result<(), Box> { + // Test that upload queue still works correctly after our changes + let mut queue = UploadQueue::new(5, 10); + + // Create test uploads + let upload1 = PendingUpload::new( + "US".to_string(), + 12345, + PathBuf::from("/test/path1.pmtiles"), + ); + + let upload2 = PendingUpload::new( + "CA".to_string(), + 67890, + PathBuf::from("/test/path2.pmtiles"), + ); + + // Add uploads to queue + queue.add_upload(upload1)?; + queue.add_upload(upload2)?; + + assert!(!queue.is_empty()); + assert!(!queue.is_full()); + + // Take batch + let batch = queue.take_batch(); + assert_eq!(batch.len(), 2); + assert!(queue.is_empty()); + + Ok(()) +} + +#[test] +fn test_upload_stats_functionality() -> Result<(), Box> { + // Test that upload stats still work correctly after our changes + let mut stats = UploadStats::new(); + + assert_eq!(stats.total_uploaded, 0); + assert_eq!(stats.total_failed, 0); + assert_eq!(stats.total_bytes_uploaded, 0); + + // Increment stats + stats.increment_uploaded(1024); + stats.increment_uploaded(2048); + stats.increment_failed(); + + assert_eq!(stats.total_uploaded, 2); + assert_eq!(stats.total_failed, 1); + assert_eq!(stats.total_bytes_uploaded, 3072); + + Ok(()) +}