Restore a rocks database from object storage
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

hello. eat rocks.

phil c04ac04e

+6170
+2
.gitignore
··· 1 + /target 2 + /local
+3
.gitmodules
··· 1 + [submodule "rocksdb"] 2 + path = rocksdb 3 + url = https://github.com/facebook/rocksdb.git
+2683
Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "aho-corasick" 7 + version = "1.1.4" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" 10 + dependencies = [ 11 + "memchr", 12 + ] 13 + 14 + [[package]] 15 + name = "android_system_properties" 16 + version = "0.1.5" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 19 + dependencies = [ 20 + "libc", 21 + ] 22 + 23 + [[package]] 24 + name = "anstream" 25 + version = "1.0.0" 26 + source = "registry+https://github.com/rust-lang/crates.io-index" 27 + checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" 28 + dependencies = [ 29 + "anstyle", 30 + "anstyle-parse", 31 + "anstyle-query", 32 + "anstyle-wincon", 33 + "colorchoice", 34 + "is_terminal_polyfill", 35 + "utf8parse", 36 + ] 37 + 38 + [[package]] 39 + name = "anstyle" 40 + version = "1.0.14" 41 + source = "registry+https://github.com/rust-lang/crates.io-index" 42 + checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" 43 + 44 + [[package]] 45 + name = "anstyle-parse" 46 + version = "1.0.0" 47 + source = "registry+https://github.com/rust-lang/crates.io-index" 48 + checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" 49 + dependencies = [ 50 + "utf8parse", 51 + ] 52 + 53 + [[package]] 54 + name = "anstyle-query" 55 + version = "1.1.5" 56 + source = "registry+https://github.com/rust-lang/crates.io-index" 57 + checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" 58 + dependencies = [ 59 + "windows-sys 0.61.2", 60 + ] 61 + 62 + [[package]] 63 + name = "anstyle-wincon" 64 + version = "3.0.11" 65 + source = "registry+https://github.com/rust-lang/crates.io-index" 66 + checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" 67 + dependencies = [ 68 + "anstyle", 69 + "once_cell_polyfill", 70 + "windows-sys 0.61.2", 71 + ] 72 + 73 + [[package]] 74 + name = "anyhow" 75 + version = "1.0.102" 76 + source = "registry+https://github.com/rust-lang/crates.io-index" 77 + checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" 78 + 79 + [[package]] 80 + name = "async-trait" 81 + version = "0.1.89" 82 + source = "registry+https://github.com/rust-lang/crates.io-index" 83 + checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" 84 + dependencies = [ 85 + "proc-macro2", 86 + "quote", 87 + "syn", 88 + ] 89 + 90 + [[package]] 91 + name = "atomic-waker" 92 + version = "1.1.2" 93 + source = "registry+https://github.com/rust-lang/crates.io-index" 94 + checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 95 + 96 + [[package]] 97 + name = "autocfg" 98 + version = "1.5.0" 99 + source = "registry+https://github.com/rust-lang/crates.io-index" 100 + checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 101 + 102 + [[package]] 103 + name = "base64" 104 + version = "0.22.1" 105 + source = "registry+https://github.com/rust-lang/crates.io-index" 106 + checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 107 + 108 + [[package]] 109 + name = "bindgen" 110 + version = "0.69.5" 111 + source = "registry+https://github.com/rust-lang/crates.io-index" 112 + checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" 113 + dependencies = [ 114 + "bitflags", 115 + "cexpr", 116 + "clang-sys", 117 + "itertools 0.12.1", 118 + "lazy_static", 119 + "lazycell", 120 + "proc-macro2", 121 + "quote", 122 + "regex", 123 + "rustc-hash 1.1.0", 124 + "shlex", 125 + "syn", 126 + ] 127 + 128 + [[package]] 129 + name = "bindgen" 130 + version = "0.72.1" 131 + source = "registry+https://github.com/rust-lang/crates.io-index" 132 + checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" 133 + dependencies = [ 134 + "bitflags", 135 + "cexpr", 136 + "clang-sys", 137 + "itertools 0.13.0", 138 + "proc-macro2", 139 + "quote", 140 + "regex", 141 + "rustc-hash 2.1.2", 142 + "shlex", 143 + "syn", 144 + ] 145 + 146 + [[package]] 147 + name = "bitflags" 148 + version = "2.11.0" 149 + source = "registry+https://github.com/rust-lang/crates.io-index" 150 + checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" 151 + 152 + [[package]] 153 + name = "block-buffer" 154 + version = "0.10.4" 155 + source = "registry+https://github.com/rust-lang/crates.io-index" 156 + checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 157 + dependencies = [ 158 + "generic-array", 159 + ] 160 + 161 + [[package]] 162 + name = "bumpalo" 163 + version = "3.20.2" 164 + source = "registry+https://github.com/rust-lang/crates.io-index" 165 + checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" 166 + 167 + [[package]] 168 + name = "bytes" 169 + version = "1.11.1" 170 + source = "registry+https://github.com/rust-lang/crates.io-index" 171 + checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" 172 + 173 + [[package]] 174 + name = "bzip2-sys" 175 + version = "0.1.13+1.0.8" 176 + source = "registry+https://github.com/rust-lang/crates.io-index" 177 + checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" 178 + dependencies = [ 179 + "cc", 180 + "pkg-config", 181 + ] 182 + 183 + [[package]] 184 + name = "cc" 185 + version = "1.2.59" 186 + source = "registry+https://github.com/rust-lang/crates.io-index" 187 + checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" 188 + dependencies = [ 189 + "find-msvc-tools", 190 + "jobserver", 191 + "libc", 192 + "shlex", 193 + ] 194 + 195 + [[package]] 196 + name = "cexpr" 197 + version = "0.6.0" 198 + source = "registry+https://github.com/rust-lang/crates.io-index" 199 + checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 200 + dependencies = [ 201 + "nom", 202 + ] 203 + 204 + [[package]] 205 + name = "cfg-if" 206 + version = "1.0.4" 207 + source = "registry+https://github.com/rust-lang/crates.io-index" 208 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 209 + 210 + [[package]] 211 + name = "cfg_aliases" 212 + version = "0.2.1" 213 + source = "registry+https://github.com/rust-lang/crates.io-index" 214 + checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 215 + 216 + [[package]] 217 + name = "chacha20" 218 + version = "0.10.0" 219 + source = "registry+https://github.com/rust-lang/crates.io-index" 220 + checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" 221 + dependencies = [ 222 + "cfg-if", 223 + "cpufeatures", 224 + "rand_core 0.10.0", 225 + ] 226 + 227 + [[package]] 228 + name = "chrono" 229 + version = "0.4.44" 230 + source = "registry+https://github.com/rust-lang/crates.io-index" 231 + checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" 232 + dependencies = [ 233 + "iana-time-zone", 234 + "num-traits", 235 + "serde", 236 + "windows-link", 237 + ] 238 + 239 + [[package]] 240 + name = "clang-sys" 241 + version = "1.8.1" 242 + source = "registry+https://github.com/rust-lang/crates.io-index" 243 + checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" 244 + dependencies = [ 245 + "glob", 246 + "libc", 247 + "libloading", 248 + ] 249 + 250 + [[package]] 251 + name = "clap" 252 + version = "4.6.0" 253 + source = "registry+https://github.com/rust-lang/crates.io-index" 254 + checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" 255 + dependencies = [ 256 + "clap_builder", 257 + "clap_derive", 258 + ] 259 + 260 + [[package]] 261 + name = "clap_builder" 262 + version = "4.6.0" 263 + source = "registry+https://github.com/rust-lang/crates.io-index" 264 + checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" 265 + dependencies = [ 266 + "anstream", 267 + "anstyle", 268 + "clap_lex", 269 + "strsim", 270 + ] 271 + 272 + [[package]] 273 + name = "clap_derive" 274 + version = "4.6.0" 275 + source = "registry+https://github.com/rust-lang/crates.io-index" 276 + checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" 277 + dependencies = [ 278 + "heck", 279 + "proc-macro2", 280 + "quote", 281 + "syn", 282 + ] 283 + 284 + [[package]] 285 + name = "clap_lex" 286 + version = "1.1.0" 287 + source = "registry+https://github.com/rust-lang/crates.io-index" 288 + checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" 289 + 290 + [[package]] 291 + name = "colorchoice" 292 + version = "1.0.5" 293 + source = "registry+https://github.com/rust-lang/crates.io-index" 294 + checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" 295 + 296 + [[package]] 297 + name = "core-foundation" 298 + version = "0.10.1" 299 + source = "registry+https://github.com/rust-lang/crates.io-index" 300 + checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" 301 + dependencies = [ 302 + "core-foundation-sys", 303 + "libc", 304 + ] 305 + 306 + [[package]] 307 + name = "core-foundation-sys" 308 + version = "0.8.7" 309 + source = "registry+https://github.com/rust-lang/crates.io-index" 310 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 311 + 312 + [[package]] 313 + name = "cpufeatures" 314 + version = "0.3.0" 315 + source = "registry+https://github.com/rust-lang/crates.io-index" 316 + checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" 317 + dependencies = [ 318 + "libc", 319 + ] 320 + 321 + [[package]] 322 + name = "crc32c" 323 + version = "0.6.8" 324 + source = "registry+https://github.com/rust-lang/crates.io-index" 325 + checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" 326 + dependencies = [ 327 + "rustc_version", 328 + ] 329 + 330 + [[package]] 331 + name = "crypto-common" 332 + version = "0.1.7" 333 + source = "registry+https://github.com/rust-lang/crates.io-index" 334 + checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" 335 + dependencies = [ 336 + "generic-array", 337 + "typenum", 338 + ] 339 + 340 + [[package]] 341 + name = "digest" 342 + version = "0.10.7" 343 + source = "registry+https://github.com/rust-lang/crates.io-index" 344 + checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 345 + dependencies = [ 346 + "block-buffer", 347 + "crypto-common", 348 + ] 349 + 350 + [[package]] 351 + name = "displaydoc" 352 + version = "0.2.5" 353 + source = "registry+https://github.com/rust-lang/crates.io-index" 354 + checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 355 + dependencies = [ 356 + "proc-macro2", 357 + "quote", 358 + "syn", 359 + ] 360 + 361 + [[package]] 362 + name = "eat-rocks" 363 + version = "0.1.0" 364 + dependencies = [ 365 + "clap", 366 + "crc32c", 367 + "futures", 368 + "object_store", 369 + "rocksdb", 370 + "tempfile", 371 + "thiserror", 372 + "tokio", 373 + "tracing", 374 + "tracing-subscriber", 375 + ] 376 + 377 + [[package]] 378 + name = "either" 379 + version = "1.15.0" 380 + source = "registry+https://github.com/rust-lang/crates.io-index" 381 + checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 382 + 383 + [[package]] 384 + name = "equivalent" 385 + version = "1.0.2" 386 + source = "registry+https://github.com/rust-lang/crates.io-index" 387 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 388 + 389 + [[package]] 390 + name = "errno" 391 + version = "0.3.14" 392 + source = "registry+https://github.com/rust-lang/crates.io-index" 393 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 394 + dependencies = [ 395 + "libc", 396 + "windows-sys 0.61.2", 397 + ] 398 + 399 + [[package]] 400 + name = "fastrand" 401 + version = "2.4.1" 402 + source = "registry+https://github.com/rust-lang/crates.io-index" 403 + checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" 404 + 405 + [[package]] 406 + name = "find-msvc-tools" 407 + version = "0.1.9" 408 + source = "registry+https://github.com/rust-lang/crates.io-index" 409 + checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" 410 + 411 + [[package]] 412 + name = "fnv" 413 + version = "1.0.7" 414 + source = "registry+https://github.com/rust-lang/crates.io-index" 415 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 416 + 417 + [[package]] 418 + name = "foldhash" 419 + version = "0.1.5" 420 + source = "registry+https://github.com/rust-lang/crates.io-index" 421 + checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 422 + 423 + [[package]] 424 + name = "form_urlencoded" 425 + version = "1.2.2" 426 + source = "registry+https://github.com/rust-lang/crates.io-index" 427 + checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 428 + dependencies = [ 429 + "percent-encoding", 430 + ] 431 + 432 + [[package]] 433 + name = "futures" 434 + version = "0.3.32" 435 + source = "registry+https://github.com/rust-lang/crates.io-index" 436 + checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" 437 + dependencies = [ 438 + "futures-channel", 439 + "futures-core", 440 + "futures-executor", 441 + "futures-io", 442 + "futures-sink", 443 + "futures-task", 444 + "futures-util", 445 + ] 446 + 447 + [[package]] 448 + name = "futures-channel" 449 + version = "0.3.32" 450 + source = "registry+https://github.com/rust-lang/crates.io-index" 451 + checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" 452 + dependencies = [ 453 + "futures-core", 454 + "futures-sink", 455 + ] 456 + 457 + [[package]] 458 + name = "futures-core" 459 + version = "0.3.32" 460 + source = "registry+https://github.com/rust-lang/crates.io-index" 461 + checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" 462 + 463 + [[package]] 464 + name = "futures-executor" 465 + version = "0.3.32" 466 + source = "registry+https://github.com/rust-lang/crates.io-index" 467 + checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" 468 + dependencies = [ 469 + "futures-core", 470 + "futures-task", 471 + "futures-util", 472 + ] 473 + 474 + [[package]] 475 + name = "futures-io" 476 + version = "0.3.32" 477 + source = "registry+https://github.com/rust-lang/crates.io-index" 478 + checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" 479 + 480 + [[package]] 481 + name = "futures-macro" 482 + version = "0.3.32" 483 + source = "registry+https://github.com/rust-lang/crates.io-index" 484 + checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" 485 + dependencies = [ 486 + "proc-macro2", 487 + "quote", 488 + "syn", 489 + ] 490 + 491 + [[package]] 492 + name = "futures-sink" 493 + version = "0.3.32" 494 + source = "registry+https://github.com/rust-lang/crates.io-index" 495 + checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" 496 + 497 + [[package]] 498 + name = "futures-task" 499 + version = "0.3.32" 500 + source = "registry+https://github.com/rust-lang/crates.io-index" 501 + checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" 502 + 503 + [[package]] 504 + name = "futures-util" 505 + version = "0.3.32" 506 + source = "registry+https://github.com/rust-lang/crates.io-index" 507 + checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" 508 + dependencies = [ 509 + "futures-channel", 510 + "futures-core", 511 + "futures-io", 512 + "futures-macro", 513 + "futures-sink", 514 + "futures-task", 515 + "memchr", 516 + "pin-project-lite", 517 + "slab", 518 + ] 519 + 520 + [[package]] 521 + name = "generic-array" 522 + version = "0.14.7" 523 + source = "registry+https://github.com/rust-lang/crates.io-index" 524 + checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 525 + dependencies = [ 526 + "typenum", 527 + "version_check", 528 + ] 529 + 530 + [[package]] 531 + name = "getrandom" 532 + version = "0.2.17" 533 + source = "registry+https://github.com/rust-lang/crates.io-index" 534 + checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" 535 + dependencies = [ 536 + "cfg-if", 537 + "js-sys", 538 + "libc", 539 + "wasi", 540 + "wasm-bindgen", 541 + ] 542 + 543 + [[package]] 544 + name = "getrandom" 545 + version = "0.3.4" 546 + source = "registry+https://github.com/rust-lang/crates.io-index" 547 + checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 548 + dependencies = [ 549 + "cfg-if", 550 + "js-sys", 551 + "libc", 552 + "r-efi 5.3.0", 553 + "wasip2", 554 + "wasm-bindgen", 555 + ] 556 + 557 + [[package]] 558 + name = "getrandom" 559 + version = "0.4.2" 560 + source = "registry+https://github.com/rust-lang/crates.io-index" 561 + checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" 562 + dependencies = [ 563 + "cfg-if", 564 + "libc", 565 + "r-efi 6.0.0", 566 + "rand_core 0.10.0", 567 + "wasip2", 568 + "wasip3", 569 + ] 570 + 571 + [[package]] 572 + name = "glob" 573 + version = "0.3.3" 574 + source = "registry+https://github.com/rust-lang/crates.io-index" 575 + checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" 576 + 577 + [[package]] 578 + name = "h2" 579 + version = "0.4.13" 580 + source = "registry+https://github.com/rust-lang/crates.io-index" 581 + checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" 582 + dependencies = [ 583 + "atomic-waker", 584 + "bytes", 585 + "fnv", 586 + "futures-core", 587 + "futures-sink", 588 + "http", 589 + "indexmap", 590 + "slab", 591 + "tokio", 592 + "tokio-util", 593 + "tracing", 594 + ] 595 + 596 + [[package]] 597 + name = "hashbrown" 598 + version = "0.15.5" 599 + source = "registry+https://github.com/rust-lang/crates.io-index" 600 + checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 601 + dependencies = [ 602 + "foldhash", 603 + ] 604 + 605 + [[package]] 606 + name = "hashbrown" 607 + version = "0.16.1" 608 + source = "registry+https://github.com/rust-lang/crates.io-index" 609 + checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" 610 + 611 + [[package]] 612 + name = "heck" 613 + version = "0.5.0" 614 + source = "registry+https://github.com/rust-lang/crates.io-index" 615 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 616 + 617 + [[package]] 618 + name = "http" 619 + version = "1.4.0" 620 + source = "registry+https://github.com/rust-lang/crates.io-index" 621 + checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" 622 + dependencies = [ 623 + "bytes", 624 + "itoa", 625 + ] 626 + 627 + [[package]] 628 + name = "http-body" 629 + version = "1.0.1" 630 + source = "registry+https://github.com/rust-lang/crates.io-index" 631 + checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 632 + dependencies = [ 633 + "bytes", 634 + "http", 635 + ] 636 + 637 + [[package]] 638 + name = "http-body-util" 639 + version = "0.1.3" 640 + source = "registry+https://github.com/rust-lang/crates.io-index" 641 + checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 642 + dependencies = [ 643 + "bytes", 644 + "futures-core", 645 + "http", 646 + "http-body", 647 + "pin-project-lite", 648 + ] 649 + 650 + [[package]] 651 + name = "httparse" 652 + version = "1.10.1" 653 + source = "registry+https://github.com/rust-lang/crates.io-index" 654 + checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 655 + 656 + [[package]] 657 + name = "humantime" 658 + version = "2.3.0" 659 + source = "registry+https://github.com/rust-lang/crates.io-index" 660 + checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" 661 + 662 + [[package]] 663 + name = "hyper" 664 + version = "1.9.0" 665 + source = "registry+https://github.com/rust-lang/crates.io-index" 666 + checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" 667 + dependencies = [ 668 + "atomic-waker", 669 + "bytes", 670 + "futures-channel", 671 + "futures-core", 672 + "h2", 673 + "http", 674 + "http-body", 675 + "httparse", 676 + "itoa", 677 + "pin-project-lite", 678 + "smallvec", 679 + "tokio", 680 + "want", 681 + ] 682 + 683 + [[package]] 684 + name = "hyper-rustls" 685 + version = "0.27.7" 686 + source = "registry+https://github.com/rust-lang/crates.io-index" 687 + checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" 688 + dependencies = [ 689 + "http", 690 + "hyper", 691 + "hyper-util", 692 + "rustls", 693 + "rustls-native-certs", 694 + "rustls-pki-types", 695 + "tokio", 696 + "tokio-rustls", 697 + "tower-service", 698 + ] 699 + 700 + [[package]] 701 + name = "hyper-util" 702 + version = "0.1.20" 703 + source = "registry+https://github.com/rust-lang/crates.io-index" 704 + checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" 705 + dependencies = [ 706 + "base64", 707 + "bytes", 708 + "futures-channel", 709 + "futures-util", 710 + "http", 711 + "http-body", 712 + "hyper", 713 + "ipnet", 714 + "libc", 715 + "percent-encoding", 716 + "pin-project-lite", 717 + "socket2", 718 + "tokio", 719 + "tower-service", 720 + "tracing", 721 + ] 722 + 723 + [[package]] 724 + name = "iana-time-zone" 725 + version = "0.1.65" 726 + source = "registry+https://github.com/rust-lang/crates.io-index" 727 + checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" 728 + dependencies = [ 729 + "android_system_properties", 730 + "core-foundation-sys", 731 + "iana-time-zone-haiku", 732 + "js-sys", 733 + "log", 734 + "wasm-bindgen", 735 + "windows-core", 736 + ] 737 + 738 + [[package]] 739 + name = "iana-time-zone-haiku" 740 + version = "0.1.2" 741 + source = "registry+https://github.com/rust-lang/crates.io-index" 742 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 743 + dependencies = [ 744 + "cc", 745 + ] 746 + 747 + [[package]] 748 + name = "icu_collections" 749 + version = "2.2.0" 750 + source = "registry+https://github.com/rust-lang/crates.io-index" 751 + checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" 752 + dependencies = [ 753 + "displaydoc", 754 + "potential_utf", 755 + "utf8_iter", 756 + "yoke", 757 + "zerofrom", 758 + "zerovec", 759 + ] 760 + 761 + [[package]] 762 + name = "icu_locale_core" 763 + version = "2.2.0" 764 + source = "registry+https://github.com/rust-lang/crates.io-index" 765 + checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" 766 + dependencies = [ 767 + "displaydoc", 768 + "litemap", 769 + "tinystr", 770 + "writeable", 771 + "zerovec", 772 + ] 773 + 774 + [[package]] 775 + name = "icu_normalizer" 776 + version = "2.2.0" 777 + source = "registry+https://github.com/rust-lang/crates.io-index" 778 + checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" 779 + dependencies = [ 780 + "icu_collections", 781 + "icu_normalizer_data", 782 + "icu_properties", 783 + "icu_provider", 784 + "smallvec", 785 + "zerovec", 786 + ] 787 + 788 + [[package]] 789 + name = "icu_normalizer_data" 790 + version = "2.2.0" 791 + source = "registry+https://github.com/rust-lang/crates.io-index" 792 + checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" 793 + 794 + [[package]] 795 + name = "icu_properties" 796 + version = "2.2.0" 797 + source = "registry+https://github.com/rust-lang/crates.io-index" 798 + checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" 799 + dependencies = [ 800 + "icu_collections", 801 + "icu_locale_core", 802 + "icu_properties_data", 803 + "icu_provider", 804 + "zerotrie", 805 + "zerovec", 806 + ] 807 + 808 + [[package]] 809 + name = "icu_properties_data" 810 + version = "2.2.0" 811 + source = "registry+https://github.com/rust-lang/crates.io-index" 812 + checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" 813 + 814 + [[package]] 815 + name = "icu_provider" 816 + version = "2.2.0" 817 + source = "registry+https://github.com/rust-lang/crates.io-index" 818 + checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" 819 + dependencies = [ 820 + "displaydoc", 821 + "icu_locale_core", 822 + "writeable", 823 + "yoke", 824 + "zerofrom", 825 + "zerotrie", 826 + "zerovec", 827 + ] 828 + 829 + [[package]] 830 + name = "id-arena" 831 + version = "2.3.0" 832 + source = "registry+https://github.com/rust-lang/crates.io-index" 833 + checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" 834 + 835 + [[package]] 836 + name = "idna" 837 + version = "1.1.0" 838 + source = "registry+https://github.com/rust-lang/crates.io-index" 839 + checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 840 + dependencies = [ 841 + "idna_adapter", 842 + "smallvec", 843 + "utf8_iter", 844 + ] 845 + 846 + [[package]] 847 + name = "idna_adapter" 848 + version = "1.2.1" 849 + source = "registry+https://github.com/rust-lang/crates.io-index" 850 + checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 851 + dependencies = [ 852 + "icu_normalizer", 853 + "icu_properties", 854 + ] 855 + 856 + [[package]] 857 + name = "indexmap" 858 + version = "2.13.1" 859 + source = "registry+https://github.com/rust-lang/crates.io-index" 860 + checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" 861 + dependencies = [ 862 + "equivalent", 863 + "hashbrown 0.16.1", 864 + "serde", 865 + "serde_core", 866 + ] 867 + 868 + [[package]] 869 + name = "ipnet" 870 + version = "2.12.0" 871 + source = "registry+https://github.com/rust-lang/crates.io-index" 872 + checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" 873 + 874 + [[package]] 875 + name = "iri-string" 876 + version = "0.7.12" 877 + source = "registry+https://github.com/rust-lang/crates.io-index" 878 + checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" 879 + dependencies = [ 880 + "memchr", 881 + "serde", 882 + ] 883 + 884 + [[package]] 885 + name = "is_terminal_polyfill" 886 + version = "1.70.2" 887 + source = "registry+https://github.com/rust-lang/crates.io-index" 888 + checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" 889 + 890 + [[package]] 891 + name = "itertools" 892 + version = "0.12.1" 893 + source = "registry+https://github.com/rust-lang/crates.io-index" 894 + checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 895 + dependencies = [ 896 + "either", 897 + ] 898 + 899 + [[package]] 900 + name = "itertools" 901 + version = "0.13.0" 902 + source = "registry+https://github.com/rust-lang/crates.io-index" 903 + checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 904 + dependencies = [ 905 + "either", 906 + ] 907 + 908 + [[package]] 909 + name = "itertools" 910 + version = "0.14.0" 911 + source = "registry+https://github.com/rust-lang/crates.io-index" 912 + checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" 913 + dependencies = [ 914 + "either", 915 + ] 916 + 917 + [[package]] 918 + name = "itoa" 919 + version = "1.0.18" 920 + source = "registry+https://github.com/rust-lang/crates.io-index" 921 + checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" 922 + 923 + [[package]] 924 + name = "jobserver" 925 + version = "0.1.34" 926 + source = "registry+https://github.com/rust-lang/crates.io-index" 927 + checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" 928 + dependencies = [ 929 + "getrandom 0.3.4", 930 + "libc", 931 + ] 932 + 933 + [[package]] 934 + name = "js-sys" 935 + version = "0.3.94" 936 + source = "registry+https://github.com/rust-lang/crates.io-index" 937 + checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" 938 + dependencies = [ 939 + "cfg-if", 940 + "futures-util", 941 + "once_cell", 942 + "wasm-bindgen", 943 + ] 944 + 945 + [[package]] 946 + name = "lazy_static" 947 + version = "1.5.0" 948 + source = "registry+https://github.com/rust-lang/crates.io-index" 949 + checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 950 + 951 + [[package]] 952 + name = "lazycell" 953 + version = "1.3.0" 954 + source = "registry+https://github.com/rust-lang/crates.io-index" 955 + checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 956 + 957 + [[package]] 958 + name = "leb128fmt" 959 + version = "0.1.0" 960 + source = "registry+https://github.com/rust-lang/crates.io-index" 961 + checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" 962 + 963 + [[package]] 964 + name = "libc" 965 + version = "0.2.184" 966 + source = "registry+https://github.com/rust-lang/crates.io-index" 967 + checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" 968 + 969 + [[package]] 970 + name = "libloading" 971 + version = "0.8.9" 972 + source = "registry+https://github.com/rust-lang/crates.io-index" 973 + checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" 974 + dependencies = [ 975 + "cfg-if", 976 + "windows-link", 977 + ] 978 + 979 + [[package]] 980 + name = "librocksdb-sys" 981 + version = "0.16.0+8.10.0" 982 + source = "registry+https://github.com/rust-lang/crates.io-index" 983 + checksum = "ce3d60bc059831dc1c83903fb45c103f75db65c5a7bf22272764d9cc683e348c" 984 + dependencies = [ 985 + "bindgen 0.69.5", 986 + "bzip2-sys", 987 + "cc", 988 + "glob", 989 + "libc", 990 + "libz-sys", 991 + "lz4-sys", 992 + "zstd-sys", 993 + ] 994 + 995 + [[package]] 996 + name = "libz-sys" 997 + version = "1.1.28" 998 + source = "registry+https://github.com/rust-lang/crates.io-index" 999 + checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22" 1000 + dependencies = [ 1001 + "cc", 1002 + "pkg-config", 1003 + "vcpkg", 1004 + ] 1005 + 1006 + [[package]] 1007 + name = "linux-raw-sys" 1008 + version = "0.12.1" 1009 + source = "registry+https://github.com/rust-lang/crates.io-index" 1010 + checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" 1011 + 1012 + [[package]] 1013 + name = "litemap" 1014 + version = "0.8.2" 1015 + source = "registry+https://github.com/rust-lang/crates.io-index" 1016 + checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" 1017 + 1018 + [[package]] 1019 + name = "lock_api" 1020 + version = "0.4.14" 1021 + source = "registry+https://github.com/rust-lang/crates.io-index" 1022 + checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 1023 + dependencies = [ 1024 + "scopeguard", 1025 + ] 1026 + 1027 + [[package]] 1028 + name = "log" 1029 + version = "0.4.29" 1030 + source = "registry+https://github.com/rust-lang/crates.io-index" 1031 + checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 1032 + 1033 + [[package]] 1034 + name = "lru-slab" 1035 + version = "0.1.2" 1036 + source = "registry+https://github.com/rust-lang/crates.io-index" 1037 + checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" 1038 + 1039 + [[package]] 1040 + name = "lz4-sys" 1041 + version = "1.11.1+lz4-1.10.0" 1042 + source = "registry+https://github.com/rust-lang/crates.io-index" 1043 + checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" 1044 + dependencies = [ 1045 + "cc", 1046 + "libc", 1047 + ] 1048 + 1049 + [[package]] 1050 + name = "matchers" 1051 + version = "0.2.0" 1052 + source = "registry+https://github.com/rust-lang/crates.io-index" 1053 + checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" 1054 + dependencies = [ 1055 + "regex-automata", 1056 + ] 1057 + 1058 + [[package]] 1059 + name = "md-5" 1060 + version = "0.10.6" 1061 + source = "registry+https://github.com/rust-lang/crates.io-index" 1062 + checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" 1063 + dependencies = [ 1064 + "cfg-if", 1065 + "digest", 1066 + ] 1067 + 1068 + [[package]] 1069 + name = "memchr" 1070 + version = "2.8.0" 1071 + source = "registry+https://github.com/rust-lang/crates.io-index" 1072 + checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" 1073 + 1074 + [[package]] 1075 + name = "minimal-lexical" 1076 + version = "0.2.1" 1077 + source = "registry+https://github.com/rust-lang/crates.io-index" 1078 + checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 1079 + 1080 + [[package]] 1081 + name = "mio" 1082 + version = "1.2.0" 1083 + source = "registry+https://github.com/rust-lang/crates.io-index" 1084 + checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" 1085 + dependencies = [ 1086 + "libc", 1087 + "wasi", 1088 + "windows-sys 0.61.2", 1089 + ] 1090 + 1091 + [[package]] 1092 + name = "nom" 1093 + version = "7.1.3" 1094 + source = "registry+https://github.com/rust-lang/crates.io-index" 1095 + checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 1096 + dependencies = [ 1097 + "memchr", 1098 + "minimal-lexical", 1099 + ] 1100 + 1101 + [[package]] 1102 + name = "nu-ansi-term" 1103 + version = "0.50.3" 1104 + source = "registry+https://github.com/rust-lang/crates.io-index" 1105 + checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" 1106 + dependencies = [ 1107 + "windows-sys 0.61.2", 1108 + ] 1109 + 1110 + [[package]] 1111 + name = "num-traits" 1112 + version = "0.2.19" 1113 + source = "registry+https://github.com/rust-lang/crates.io-index" 1114 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1115 + dependencies = [ 1116 + "autocfg", 1117 + ] 1118 + 1119 + [[package]] 1120 + name = "object_store" 1121 + version = "0.13.2" 1122 + source = "registry+https://github.com/rust-lang/crates.io-index" 1123 + checksum = "622acbc9100d3c10e2ee15804b0caa40e55c933d5aa53814cd520805b7958a49" 1124 + dependencies = [ 1125 + "async-trait", 1126 + "base64", 1127 + "bytes", 1128 + "chrono", 1129 + "form_urlencoded", 1130 + "futures-channel", 1131 + "futures-core", 1132 + "futures-util", 1133 + "http", 1134 + "http-body-util", 1135 + "humantime", 1136 + "hyper", 1137 + "itertools 0.14.0", 1138 + "md-5", 1139 + "parking_lot", 1140 + "percent-encoding", 1141 + "quick-xml", 1142 + "rand 0.10.0", 1143 + "reqwest", 1144 + "ring", 1145 + "serde", 1146 + "serde_json", 1147 + "serde_urlencoded", 1148 + "thiserror", 1149 + "tokio", 1150 + "tracing", 1151 + "url", 1152 + "walkdir", 1153 + "wasm-bindgen-futures", 1154 + "web-time", 1155 + ] 1156 + 1157 + [[package]] 1158 + name = "once_cell" 1159 + version = "1.21.4" 1160 + source = "registry+https://github.com/rust-lang/crates.io-index" 1161 + checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" 1162 + 1163 + [[package]] 1164 + name = "once_cell_polyfill" 1165 + version = "1.70.2" 1166 + source = "registry+https://github.com/rust-lang/crates.io-index" 1167 + checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" 1168 + 1169 + [[package]] 1170 + name = "openssl-probe" 1171 + version = "0.2.1" 1172 + source = "registry+https://github.com/rust-lang/crates.io-index" 1173 + checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" 1174 + 1175 + [[package]] 1176 + name = "parking_lot" 1177 + version = "0.12.5" 1178 + source = "registry+https://github.com/rust-lang/crates.io-index" 1179 + checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 1180 + dependencies = [ 1181 + "lock_api", 1182 + "parking_lot_core", 1183 + ] 1184 + 1185 + [[package]] 1186 + name = "parking_lot_core" 1187 + version = "0.9.12" 1188 + source = "registry+https://github.com/rust-lang/crates.io-index" 1189 + checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 1190 + dependencies = [ 1191 + "cfg-if", 1192 + "libc", 1193 + "redox_syscall", 1194 + "smallvec", 1195 + "windows-link", 1196 + ] 1197 + 1198 + [[package]] 1199 + name = "percent-encoding" 1200 + version = "2.3.2" 1201 + source = "registry+https://github.com/rust-lang/crates.io-index" 1202 + checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 1203 + 1204 + [[package]] 1205 + name = "pin-project-lite" 1206 + version = "0.2.17" 1207 + source = "registry+https://github.com/rust-lang/crates.io-index" 1208 + checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" 1209 + 1210 + [[package]] 1211 + name = "pkg-config" 1212 + version = "0.3.32" 1213 + source = "registry+https://github.com/rust-lang/crates.io-index" 1214 + checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1215 + 1216 + [[package]] 1217 + name = "potential_utf" 1218 + version = "0.1.5" 1219 + source = "registry+https://github.com/rust-lang/crates.io-index" 1220 + checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" 1221 + dependencies = [ 1222 + "zerovec", 1223 + ] 1224 + 1225 + [[package]] 1226 + name = "ppv-lite86" 1227 + version = "0.2.21" 1228 + source = "registry+https://github.com/rust-lang/crates.io-index" 1229 + checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 1230 + dependencies = [ 1231 + "zerocopy", 1232 + ] 1233 + 1234 + [[package]] 1235 + name = "prettyplease" 1236 + version = "0.2.37" 1237 + source = "registry+https://github.com/rust-lang/crates.io-index" 1238 + checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 1239 + dependencies = [ 1240 + "proc-macro2", 1241 + "syn", 1242 + ] 1243 + 1244 + [[package]] 1245 + name = "proc-macro2" 1246 + version = "1.0.106" 1247 + source = "registry+https://github.com/rust-lang/crates.io-index" 1248 + checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" 1249 + dependencies = [ 1250 + "unicode-ident", 1251 + ] 1252 + 1253 + [[package]] 1254 + name = "quick-xml" 1255 + version = "0.39.2" 1256 + source = "registry+https://github.com/rust-lang/crates.io-index" 1257 + checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" 1258 + dependencies = [ 1259 + "memchr", 1260 + "serde", 1261 + ] 1262 + 1263 + [[package]] 1264 + name = "quinn" 1265 + version = "0.11.9" 1266 + source = "registry+https://github.com/rust-lang/crates.io-index" 1267 + checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" 1268 + dependencies = [ 1269 + "bytes", 1270 + "cfg_aliases", 1271 + "pin-project-lite", 1272 + "quinn-proto", 1273 + "quinn-udp", 1274 + "rustc-hash 2.1.2", 1275 + "rustls", 1276 + "socket2", 1277 + "thiserror", 1278 + "tokio", 1279 + "tracing", 1280 + "web-time", 1281 + ] 1282 + 1283 + [[package]] 1284 + name = "quinn-proto" 1285 + version = "0.11.14" 1286 + source = "registry+https://github.com/rust-lang/crates.io-index" 1287 + checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" 1288 + dependencies = [ 1289 + "bytes", 1290 + "getrandom 0.3.4", 1291 + "lru-slab", 1292 + "rand 0.9.2", 1293 + "ring", 1294 + "rustc-hash 2.1.2", 1295 + "rustls", 1296 + "rustls-pki-types", 1297 + "slab", 1298 + "thiserror", 1299 + "tinyvec", 1300 + "tracing", 1301 + "web-time", 1302 + ] 1303 + 1304 + [[package]] 1305 + name = "quinn-udp" 1306 + version = "0.5.14" 1307 + source = "registry+https://github.com/rust-lang/crates.io-index" 1308 + checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" 1309 + dependencies = [ 1310 + "cfg_aliases", 1311 + "libc", 1312 + "once_cell", 1313 + "socket2", 1314 + "tracing", 1315 + "windows-sys 0.60.2", 1316 + ] 1317 + 1318 + [[package]] 1319 + name = "quote" 1320 + version = "1.0.45" 1321 + source = "registry+https://github.com/rust-lang/crates.io-index" 1322 + checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" 1323 + dependencies = [ 1324 + "proc-macro2", 1325 + ] 1326 + 1327 + [[package]] 1328 + name = "r-efi" 1329 + version = "5.3.0" 1330 + source = "registry+https://github.com/rust-lang/crates.io-index" 1331 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 1332 + 1333 + [[package]] 1334 + name = "r-efi" 1335 + version = "6.0.0" 1336 + source = "registry+https://github.com/rust-lang/crates.io-index" 1337 + checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" 1338 + 1339 + [[package]] 1340 + name = "rand" 1341 + version = "0.9.2" 1342 + source = "registry+https://github.com/rust-lang/crates.io-index" 1343 + checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 1344 + dependencies = [ 1345 + "rand_chacha", 1346 + "rand_core 0.9.5", 1347 + ] 1348 + 1349 + [[package]] 1350 + name = "rand" 1351 + version = "0.10.0" 1352 + source = "registry+https://github.com/rust-lang/crates.io-index" 1353 + checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" 1354 + dependencies = [ 1355 + "chacha20", 1356 + "getrandom 0.4.2", 1357 + "rand_core 0.10.0", 1358 + ] 1359 + 1360 + [[package]] 1361 + name = "rand_chacha" 1362 + version = "0.9.0" 1363 + source = "registry+https://github.com/rust-lang/crates.io-index" 1364 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 1365 + dependencies = [ 1366 + "ppv-lite86", 1367 + "rand_core 0.9.5", 1368 + ] 1369 + 1370 + [[package]] 1371 + name = "rand_core" 1372 + version = "0.9.5" 1373 + source = "registry+https://github.com/rust-lang/crates.io-index" 1374 + checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" 1375 + dependencies = [ 1376 + "getrandom 0.3.4", 1377 + ] 1378 + 1379 + [[package]] 1380 + name = "rand_core" 1381 + version = "0.10.0" 1382 + source = "registry+https://github.com/rust-lang/crates.io-index" 1383 + checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" 1384 + 1385 + [[package]] 1386 + name = "redox_syscall" 1387 + version = "0.5.18" 1388 + source = "registry+https://github.com/rust-lang/crates.io-index" 1389 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 1390 + dependencies = [ 1391 + "bitflags", 1392 + ] 1393 + 1394 + [[package]] 1395 + name = "regex" 1396 + version = "1.12.3" 1397 + source = "registry+https://github.com/rust-lang/crates.io-index" 1398 + checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" 1399 + dependencies = [ 1400 + "aho-corasick", 1401 + "memchr", 1402 + "regex-automata", 1403 + "regex-syntax", 1404 + ] 1405 + 1406 + [[package]] 1407 + name = "regex-automata" 1408 + version = "0.4.14" 1409 + source = "registry+https://github.com/rust-lang/crates.io-index" 1410 + checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" 1411 + dependencies = [ 1412 + "aho-corasick", 1413 + "memchr", 1414 + "regex-syntax", 1415 + ] 1416 + 1417 + [[package]] 1418 + name = "regex-syntax" 1419 + version = "0.8.10" 1420 + source = "registry+https://github.com/rust-lang/crates.io-index" 1421 + checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" 1422 + 1423 + [[package]] 1424 + name = "reqwest" 1425 + version = "0.12.28" 1426 + source = "registry+https://github.com/rust-lang/crates.io-index" 1427 + checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" 1428 + dependencies = [ 1429 + "base64", 1430 + "bytes", 1431 + "futures-core", 1432 + "futures-util", 1433 + "h2", 1434 + "http", 1435 + "http-body", 1436 + "http-body-util", 1437 + "hyper", 1438 + "hyper-rustls", 1439 + "hyper-util", 1440 + "js-sys", 1441 + "log", 1442 + "percent-encoding", 1443 + "pin-project-lite", 1444 + "quinn", 1445 + "rustls", 1446 + "rustls-native-certs", 1447 + "rustls-pki-types", 1448 + "serde", 1449 + "serde_json", 1450 + "serde_urlencoded", 1451 + "sync_wrapper", 1452 + "tokio", 1453 + "tokio-rustls", 1454 + "tokio-util", 1455 + "tower", 1456 + "tower-http", 1457 + "tower-service", 1458 + "url", 1459 + "wasm-bindgen", 1460 + "wasm-bindgen-futures", 1461 + "wasm-streams", 1462 + "web-sys", 1463 + ] 1464 + 1465 + [[package]] 1466 + name = "ring" 1467 + version = "0.17.14" 1468 + source = "registry+https://github.com/rust-lang/crates.io-index" 1469 + checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 1470 + dependencies = [ 1471 + "cc", 1472 + "cfg-if", 1473 + "getrandom 0.2.17", 1474 + "libc", 1475 + "untrusted", 1476 + "windows-sys 0.52.0", 1477 + ] 1478 + 1479 + [[package]] 1480 + name = "rocksdb" 1481 + version = "0.22.0" 1482 + source = "registry+https://github.com/rust-lang/crates.io-index" 1483 + checksum = "6bd13e55d6d7b8cd0ea569161127567cd587676c99f4472f779a0279aa60a7a7" 1484 + dependencies = [ 1485 + "libc", 1486 + "librocksdb-sys", 1487 + ] 1488 + 1489 + [[package]] 1490 + name = "rustc-hash" 1491 + version = "1.1.0" 1492 + source = "registry+https://github.com/rust-lang/crates.io-index" 1493 + checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1494 + 1495 + [[package]] 1496 + name = "rustc-hash" 1497 + version = "2.1.2" 1498 + source = "registry+https://github.com/rust-lang/crates.io-index" 1499 + checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" 1500 + 1501 + [[package]] 1502 + name = "rustc_version" 1503 + version = "0.4.1" 1504 + source = "registry+https://github.com/rust-lang/crates.io-index" 1505 + checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 1506 + dependencies = [ 1507 + "semver", 1508 + ] 1509 + 1510 + [[package]] 1511 + name = "rustix" 1512 + version = "1.1.4" 1513 + source = "registry+https://github.com/rust-lang/crates.io-index" 1514 + checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" 1515 + dependencies = [ 1516 + "bitflags", 1517 + "errno", 1518 + "libc", 1519 + "linux-raw-sys", 1520 + "windows-sys 0.61.2", 1521 + ] 1522 + 1523 + [[package]] 1524 + name = "rustls" 1525 + version = "0.23.37" 1526 + source = "registry+https://github.com/rust-lang/crates.io-index" 1527 + checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" 1528 + dependencies = [ 1529 + "once_cell", 1530 + "ring", 1531 + "rustls-pki-types", 1532 + "rustls-webpki", 1533 + "subtle", 1534 + "zeroize", 1535 + ] 1536 + 1537 + [[package]] 1538 + name = "rustls-native-certs" 1539 + version = "0.8.3" 1540 + source = "registry+https://github.com/rust-lang/crates.io-index" 1541 + checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" 1542 + dependencies = [ 1543 + "openssl-probe", 1544 + "rustls-pki-types", 1545 + "schannel", 1546 + "security-framework", 1547 + ] 1548 + 1549 + [[package]] 1550 + name = "rustls-pki-types" 1551 + version = "1.14.0" 1552 + source = "registry+https://github.com/rust-lang/crates.io-index" 1553 + checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" 1554 + dependencies = [ 1555 + "web-time", 1556 + "zeroize", 1557 + ] 1558 + 1559 + [[package]] 1560 + name = "rustls-webpki" 1561 + version = "0.103.10" 1562 + source = "registry+https://github.com/rust-lang/crates.io-index" 1563 + checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" 1564 + dependencies = [ 1565 + "ring", 1566 + "rustls-pki-types", 1567 + "untrusted", 1568 + ] 1569 + 1570 + [[package]] 1571 + name = "rustversion" 1572 + version = "1.0.22" 1573 + source = "registry+https://github.com/rust-lang/crates.io-index" 1574 + checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 1575 + 1576 + [[package]] 1577 + name = "ryu" 1578 + version = "1.0.23" 1579 + source = "registry+https://github.com/rust-lang/crates.io-index" 1580 + checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" 1581 + 1582 + [[package]] 1583 + name = "same-file" 1584 + version = "1.0.6" 1585 + source = "registry+https://github.com/rust-lang/crates.io-index" 1586 + checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1587 + dependencies = [ 1588 + "winapi-util", 1589 + ] 1590 + 1591 + [[package]] 1592 + name = "schannel" 1593 + version = "0.1.29" 1594 + source = "registry+https://github.com/rust-lang/crates.io-index" 1595 + checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" 1596 + dependencies = [ 1597 + "windows-sys 0.61.2", 1598 + ] 1599 + 1600 + [[package]] 1601 + name = "scopeguard" 1602 + version = "1.2.0" 1603 + source = "registry+https://github.com/rust-lang/crates.io-index" 1604 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1605 + 1606 + [[package]] 1607 + name = "security-framework" 1608 + version = "3.7.0" 1609 + source = "registry+https://github.com/rust-lang/crates.io-index" 1610 + checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" 1611 + dependencies = [ 1612 + "bitflags", 1613 + "core-foundation", 1614 + "core-foundation-sys", 1615 + "libc", 1616 + "security-framework-sys", 1617 + ] 1618 + 1619 + [[package]] 1620 + name = "security-framework-sys" 1621 + version = "2.17.0" 1622 + source = "registry+https://github.com/rust-lang/crates.io-index" 1623 + checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" 1624 + dependencies = [ 1625 + "core-foundation-sys", 1626 + "libc", 1627 + ] 1628 + 1629 + [[package]] 1630 + name = "semver" 1631 + version = "1.0.28" 1632 + source = "registry+https://github.com/rust-lang/crates.io-index" 1633 + checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" 1634 + 1635 + [[package]] 1636 + name = "serde" 1637 + version = "1.0.228" 1638 + source = "registry+https://github.com/rust-lang/crates.io-index" 1639 + checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 1640 + dependencies = [ 1641 + "serde_core", 1642 + "serde_derive", 1643 + ] 1644 + 1645 + [[package]] 1646 + name = "serde_core" 1647 + version = "1.0.228" 1648 + source = "registry+https://github.com/rust-lang/crates.io-index" 1649 + checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 1650 + dependencies = [ 1651 + "serde_derive", 1652 + ] 1653 + 1654 + [[package]] 1655 + name = "serde_derive" 1656 + version = "1.0.228" 1657 + source = "registry+https://github.com/rust-lang/crates.io-index" 1658 + checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 1659 + dependencies = [ 1660 + "proc-macro2", 1661 + "quote", 1662 + "syn", 1663 + ] 1664 + 1665 + [[package]] 1666 + name = "serde_json" 1667 + version = "1.0.149" 1668 + source = "registry+https://github.com/rust-lang/crates.io-index" 1669 + checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" 1670 + dependencies = [ 1671 + "itoa", 1672 + "memchr", 1673 + "serde", 1674 + "serde_core", 1675 + "zmij", 1676 + ] 1677 + 1678 + [[package]] 1679 + name = "serde_urlencoded" 1680 + version = "0.7.1" 1681 + source = "registry+https://github.com/rust-lang/crates.io-index" 1682 + checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1683 + dependencies = [ 1684 + "form_urlencoded", 1685 + "itoa", 1686 + "ryu", 1687 + "serde", 1688 + ] 1689 + 1690 + [[package]] 1691 + name = "sharded-slab" 1692 + version = "0.1.7" 1693 + source = "registry+https://github.com/rust-lang/crates.io-index" 1694 + checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1695 + dependencies = [ 1696 + "lazy_static", 1697 + ] 1698 + 1699 + [[package]] 1700 + name = "shlex" 1701 + version = "1.3.0" 1702 + source = "registry+https://github.com/rust-lang/crates.io-index" 1703 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1704 + 1705 + [[package]] 1706 + name = "signal-hook-registry" 1707 + version = "1.4.8" 1708 + source = "registry+https://github.com/rust-lang/crates.io-index" 1709 + checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" 1710 + dependencies = [ 1711 + "errno", 1712 + "libc", 1713 + ] 1714 + 1715 + [[package]] 1716 + name = "slab" 1717 + version = "0.4.12" 1718 + source = "registry+https://github.com/rust-lang/crates.io-index" 1719 + checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" 1720 + 1721 + [[package]] 1722 + name = "smallvec" 1723 + version = "1.15.1" 1724 + source = "registry+https://github.com/rust-lang/crates.io-index" 1725 + checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 1726 + 1727 + [[package]] 1728 + name = "socket2" 1729 + version = "0.6.3" 1730 + source = "registry+https://github.com/rust-lang/crates.io-index" 1731 + checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" 1732 + dependencies = [ 1733 + "libc", 1734 + "windows-sys 0.61.2", 1735 + ] 1736 + 1737 + [[package]] 1738 + name = "stable_deref_trait" 1739 + version = "1.2.1" 1740 + source = "registry+https://github.com/rust-lang/crates.io-index" 1741 + checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 1742 + 1743 + [[package]] 1744 + name = "strsim" 1745 + version = "0.11.1" 1746 + source = "registry+https://github.com/rust-lang/crates.io-index" 1747 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1748 + 1749 + [[package]] 1750 + name = "subtle" 1751 + version = "2.6.1" 1752 + source = "registry+https://github.com/rust-lang/crates.io-index" 1753 + checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1754 + 1755 + [[package]] 1756 + name = "syn" 1757 + version = "2.0.117" 1758 + source = "registry+https://github.com/rust-lang/crates.io-index" 1759 + checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" 1760 + dependencies = [ 1761 + "proc-macro2", 1762 + "quote", 1763 + "unicode-ident", 1764 + ] 1765 + 1766 + [[package]] 1767 + name = "sync_wrapper" 1768 + version = "1.0.2" 1769 + source = "registry+https://github.com/rust-lang/crates.io-index" 1770 + checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1771 + dependencies = [ 1772 + "futures-core", 1773 + ] 1774 + 1775 + [[package]] 1776 + name = "synstructure" 1777 + version = "0.13.2" 1778 + source = "registry+https://github.com/rust-lang/crates.io-index" 1779 + checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 1780 + dependencies = [ 1781 + "proc-macro2", 1782 + "quote", 1783 + "syn", 1784 + ] 1785 + 1786 + [[package]] 1787 + name = "tempfile" 1788 + version = "3.27.0" 1789 + source = "registry+https://github.com/rust-lang/crates.io-index" 1790 + checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" 1791 + dependencies = [ 1792 + "fastrand", 1793 + "getrandom 0.4.2", 1794 + "once_cell", 1795 + "rustix", 1796 + "windows-sys 0.61.2", 1797 + ] 1798 + 1799 + [[package]] 1800 + name = "thiserror" 1801 + version = "2.0.18" 1802 + source = "registry+https://github.com/rust-lang/crates.io-index" 1803 + checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" 1804 + dependencies = [ 1805 + "thiserror-impl", 1806 + ] 1807 + 1808 + [[package]] 1809 + name = "thiserror-impl" 1810 + version = "2.0.18" 1811 + source = "registry+https://github.com/rust-lang/crates.io-index" 1812 + checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" 1813 + dependencies = [ 1814 + "proc-macro2", 1815 + "quote", 1816 + "syn", 1817 + ] 1818 + 1819 + [[package]] 1820 + name = "thread_local" 1821 + version = "1.1.9" 1822 + source = "registry+https://github.com/rust-lang/crates.io-index" 1823 + checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" 1824 + dependencies = [ 1825 + "cfg-if", 1826 + ] 1827 + 1828 + [[package]] 1829 + name = "tinystr" 1830 + version = "0.8.3" 1831 + source = "registry+https://github.com/rust-lang/crates.io-index" 1832 + checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" 1833 + dependencies = [ 1834 + "displaydoc", 1835 + "zerovec", 1836 + ] 1837 + 1838 + [[package]] 1839 + name = "tinyvec" 1840 + version = "1.11.0" 1841 + source = "registry+https://github.com/rust-lang/crates.io-index" 1842 + checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" 1843 + dependencies = [ 1844 + "tinyvec_macros", 1845 + ] 1846 + 1847 + [[package]] 1848 + name = "tinyvec_macros" 1849 + version = "0.1.1" 1850 + source = "registry+https://github.com/rust-lang/crates.io-index" 1851 + checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1852 + 1853 + [[package]] 1854 + name = "tokio" 1855 + version = "1.51.1" 1856 + source = "registry+https://github.com/rust-lang/crates.io-index" 1857 + checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" 1858 + dependencies = [ 1859 + "bytes", 1860 + "libc", 1861 + "mio", 1862 + "parking_lot", 1863 + "pin-project-lite", 1864 + "signal-hook-registry", 1865 + "socket2", 1866 + "tokio-macros", 1867 + "windows-sys 0.61.2", 1868 + ] 1869 + 1870 + [[package]] 1871 + name = "tokio-macros" 1872 + version = "2.7.0" 1873 + source = "registry+https://github.com/rust-lang/crates.io-index" 1874 + checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" 1875 + dependencies = [ 1876 + "proc-macro2", 1877 + "quote", 1878 + "syn", 1879 + ] 1880 + 1881 + [[package]] 1882 + name = "tokio-rustls" 1883 + version = "0.26.4" 1884 + source = "registry+https://github.com/rust-lang/crates.io-index" 1885 + checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" 1886 + dependencies = [ 1887 + "rustls", 1888 + "tokio", 1889 + ] 1890 + 1891 + [[package]] 1892 + name = "tokio-util" 1893 + version = "0.7.18" 1894 + source = "registry+https://github.com/rust-lang/crates.io-index" 1895 + checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" 1896 + dependencies = [ 1897 + "bytes", 1898 + "futures-core", 1899 + "futures-sink", 1900 + "pin-project-lite", 1901 + "tokio", 1902 + ] 1903 + 1904 + [[package]] 1905 + name = "tower" 1906 + version = "0.5.3" 1907 + source = "registry+https://github.com/rust-lang/crates.io-index" 1908 + checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" 1909 + dependencies = [ 1910 + "futures-core", 1911 + "futures-util", 1912 + "pin-project-lite", 1913 + "sync_wrapper", 1914 + "tokio", 1915 + "tower-layer", 1916 + "tower-service", 1917 + ] 1918 + 1919 + [[package]] 1920 + name = "tower-http" 1921 + version = "0.6.8" 1922 + source = "registry+https://github.com/rust-lang/crates.io-index" 1923 + checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" 1924 + dependencies = [ 1925 + "bitflags", 1926 + "bytes", 1927 + "futures-util", 1928 + "http", 1929 + "http-body", 1930 + "iri-string", 1931 + "pin-project-lite", 1932 + "tower", 1933 + "tower-layer", 1934 + "tower-service", 1935 + ] 1936 + 1937 + [[package]] 1938 + name = "tower-layer" 1939 + version = "0.3.3" 1940 + source = "registry+https://github.com/rust-lang/crates.io-index" 1941 + checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1942 + 1943 + [[package]] 1944 + name = "tower-service" 1945 + version = "0.3.3" 1946 + source = "registry+https://github.com/rust-lang/crates.io-index" 1947 + checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1948 + 1949 + [[package]] 1950 + name = "tracing" 1951 + version = "0.1.44" 1952 + source = "registry+https://github.com/rust-lang/crates.io-index" 1953 + checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" 1954 + dependencies = [ 1955 + "pin-project-lite", 1956 + "tracing-attributes", 1957 + "tracing-core", 1958 + ] 1959 + 1960 + [[package]] 1961 + name = "tracing-attributes" 1962 + version = "0.1.31" 1963 + source = "registry+https://github.com/rust-lang/crates.io-index" 1964 + checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" 1965 + dependencies = [ 1966 + "proc-macro2", 1967 + "quote", 1968 + "syn", 1969 + ] 1970 + 1971 + [[package]] 1972 + name = "tracing-core" 1973 + version = "0.1.36" 1974 + source = "registry+https://github.com/rust-lang/crates.io-index" 1975 + checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" 1976 + dependencies = [ 1977 + "once_cell", 1978 + "valuable", 1979 + ] 1980 + 1981 + [[package]] 1982 + name = "tracing-log" 1983 + version = "0.2.0" 1984 + source = "registry+https://github.com/rust-lang/crates.io-index" 1985 + checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 1986 + dependencies = [ 1987 + "log", 1988 + "once_cell", 1989 + "tracing-core", 1990 + ] 1991 + 1992 + [[package]] 1993 + name = "tracing-subscriber" 1994 + version = "0.3.23" 1995 + source = "registry+https://github.com/rust-lang/crates.io-index" 1996 + checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" 1997 + dependencies = [ 1998 + "matchers", 1999 + "nu-ansi-term", 2000 + "once_cell", 2001 + "regex-automata", 2002 + "sharded-slab", 2003 + "smallvec", 2004 + "thread_local", 2005 + "tracing", 2006 + "tracing-core", 2007 + "tracing-log", 2008 + ] 2009 + 2010 + [[package]] 2011 + name = "try-lock" 2012 + version = "0.2.5" 2013 + source = "registry+https://github.com/rust-lang/crates.io-index" 2014 + checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 2015 + 2016 + [[package]] 2017 + name = "typenum" 2018 + version = "1.19.0" 2019 + source = "registry+https://github.com/rust-lang/crates.io-index" 2020 + checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 2021 + 2022 + [[package]] 2023 + name = "unicode-ident" 2024 + version = "1.0.24" 2025 + source = "registry+https://github.com/rust-lang/crates.io-index" 2026 + checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" 2027 + 2028 + [[package]] 2029 + name = "unicode-xid" 2030 + version = "0.2.6" 2031 + source = "registry+https://github.com/rust-lang/crates.io-index" 2032 + checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 2033 + 2034 + [[package]] 2035 + name = "untrusted" 2036 + version = "0.9.0" 2037 + source = "registry+https://github.com/rust-lang/crates.io-index" 2038 + checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 2039 + 2040 + [[package]] 2041 + name = "url" 2042 + version = "2.5.8" 2043 + source = "registry+https://github.com/rust-lang/crates.io-index" 2044 + checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" 2045 + dependencies = [ 2046 + "form_urlencoded", 2047 + "idna", 2048 + "percent-encoding", 2049 + "serde", 2050 + ] 2051 + 2052 + [[package]] 2053 + name = "utf8_iter" 2054 + version = "1.0.4" 2055 + source = "registry+https://github.com/rust-lang/crates.io-index" 2056 + checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 2057 + 2058 + [[package]] 2059 + name = "utf8parse" 2060 + version = "0.2.2" 2061 + source = "registry+https://github.com/rust-lang/crates.io-index" 2062 + checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 2063 + 2064 + [[package]] 2065 + name = "valuable" 2066 + version = "0.1.1" 2067 + source = "registry+https://github.com/rust-lang/crates.io-index" 2068 + checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 2069 + 2070 + [[package]] 2071 + name = "vcpkg" 2072 + version = "0.2.15" 2073 + source = "registry+https://github.com/rust-lang/crates.io-index" 2074 + checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2075 + 2076 + [[package]] 2077 + name = "version_check" 2078 + version = "0.9.5" 2079 + source = "registry+https://github.com/rust-lang/crates.io-index" 2080 + checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2081 + 2082 + [[package]] 2083 + name = "walkdir" 2084 + version = "2.5.0" 2085 + source = "registry+https://github.com/rust-lang/crates.io-index" 2086 + checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 2087 + dependencies = [ 2088 + "same-file", 2089 + "winapi-util", 2090 + ] 2091 + 2092 + [[package]] 2093 + name = "want" 2094 + version = "0.3.1" 2095 + source = "registry+https://github.com/rust-lang/crates.io-index" 2096 + checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 2097 + dependencies = [ 2098 + "try-lock", 2099 + ] 2100 + 2101 + [[package]] 2102 + name = "wasi" 2103 + version = "0.11.1+wasi-snapshot-preview1" 2104 + source = "registry+https://github.com/rust-lang/crates.io-index" 2105 + checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 2106 + 2107 + [[package]] 2108 + name = "wasip2" 2109 + version = "1.0.2+wasi-0.2.9" 2110 + source = "registry+https://github.com/rust-lang/crates.io-index" 2111 + checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" 2112 + dependencies = [ 2113 + "wit-bindgen", 2114 + ] 2115 + 2116 + [[package]] 2117 + name = "wasip3" 2118 + version = "0.4.0+wasi-0.3.0-rc-2026-01-06" 2119 + source = "registry+https://github.com/rust-lang/crates.io-index" 2120 + checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" 2121 + dependencies = [ 2122 + "wit-bindgen", 2123 + ] 2124 + 2125 + [[package]] 2126 + name = "wasm-bindgen" 2127 + version = "0.2.117" 2128 + source = "registry+https://github.com/rust-lang/crates.io-index" 2129 + checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" 2130 + dependencies = [ 2131 + "cfg-if", 2132 + "once_cell", 2133 + "rustversion", 2134 + "wasm-bindgen-macro", 2135 + "wasm-bindgen-shared", 2136 + ] 2137 + 2138 + [[package]] 2139 + name = "wasm-bindgen-futures" 2140 + version = "0.4.67" 2141 + source = "registry+https://github.com/rust-lang/crates.io-index" 2142 + checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" 2143 + dependencies = [ 2144 + "js-sys", 2145 + "wasm-bindgen", 2146 + ] 2147 + 2148 + [[package]] 2149 + name = "wasm-bindgen-macro" 2150 + version = "0.2.117" 2151 + source = "registry+https://github.com/rust-lang/crates.io-index" 2152 + checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" 2153 + dependencies = [ 2154 + "quote", 2155 + "wasm-bindgen-macro-support", 2156 + ] 2157 + 2158 + [[package]] 2159 + name = "wasm-bindgen-macro-support" 2160 + version = "0.2.117" 2161 + source = "registry+https://github.com/rust-lang/crates.io-index" 2162 + checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" 2163 + dependencies = [ 2164 + "bumpalo", 2165 + "proc-macro2", 2166 + "quote", 2167 + "syn", 2168 + "wasm-bindgen-shared", 2169 + ] 2170 + 2171 + [[package]] 2172 + name = "wasm-bindgen-shared" 2173 + version = "0.2.117" 2174 + source = "registry+https://github.com/rust-lang/crates.io-index" 2175 + checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" 2176 + dependencies = [ 2177 + "unicode-ident", 2178 + ] 2179 + 2180 + [[package]] 2181 + name = "wasm-encoder" 2182 + version = "0.244.0" 2183 + source = "registry+https://github.com/rust-lang/crates.io-index" 2184 + checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" 2185 + dependencies = [ 2186 + "leb128fmt", 2187 + "wasmparser", 2188 + ] 2189 + 2190 + [[package]] 2191 + name = "wasm-metadata" 2192 + version = "0.244.0" 2193 + source = "registry+https://github.com/rust-lang/crates.io-index" 2194 + checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" 2195 + dependencies = [ 2196 + "anyhow", 2197 + "indexmap", 2198 + "wasm-encoder", 2199 + "wasmparser", 2200 + ] 2201 + 2202 + [[package]] 2203 + name = "wasm-streams" 2204 + version = "0.4.2" 2205 + source = "registry+https://github.com/rust-lang/crates.io-index" 2206 + checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" 2207 + dependencies = [ 2208 + "futures-util", 2209 + "js-sys", 2210 + "wasm-bindgen", 2211 + "wasm-bindgen-futures", 2212 + "web-sys", 2213 + ] 2214 + 2215 + [[package]] 2216 + name = "wasmparser" 2217 + version = "0.244.0" 2218 + source = "registry+https://github.com/rust-lang/crates.io-index" 2219 + checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" 2220 + dependencies = [ 2221 + "bitflags", 2222 + "hashbrown 0.15.5", 2223 + "indexmap", 2224 + "semver", 2225 + ] 2226 + 2227 + [[package]] 2228 + name = "web-sys" 2229 + version = "0.3.94" 2230 + source = "registry+https://github.com/rust-lang/crates.io-index" 2231 + checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" 2232 + dependencies = [ 2233 + "js-sys", 2234 + "wasm-bindgen", 2235 + ] 2236 + 2237 + [[package]] 2238 + name = "web-time" 2239 + version = "1.1.0" 2240 + source = "registry+https://github.com/rust-lang/crates.io-index" 2241 + checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 2242 + dependencies = [ 2243 + "js-sys", 2244 + "wasm-bindgen", 2245 + ] 2246 + 2247 + [[package]] 2248 + name = "winapi-util" 2249 + version = "0.1.11" 2250 + source = "registry+https://github.com/rust-lang/crates.io-index" 2251 + checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" 2252 + dependencies = [ 2253 + "windows-sys 0.61.2", 2254 + ] 2255 + 2256 + [[package]] 2257 + name = "windows-core" 2258 + version = "0.62.2" 2259 + source = "registry+https://github.com/rust-lang/crates.io-index" 2260 + checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" 2261 + dependencies = [ 2262 + "windows-implement", 2263 + "windows-interface", 2264 + "windows-link", 2265 + "windows-result", 2266 + "windows-strings", 2267 + ] 2268 + 2269 + [[package]] 2270 + name = "windows-implement" 2271 + version = "0.60.2" 2272 + source = "registry+https://github.com/rust-lang/crates.io-index" 2273 + checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" 2274 + dependencies = [ 2275 + "proc-macro2", 2276 + "quote", 2277 + "syn", 2278 + ] 2279 + 2280 + [[package]] 2281 + name = "windows-interface" 2282 + version = "0.59.3" 2283 + source = "registry+https://github.com/rust-lang/crates.io-index" 2284 + checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" 2285 + dependencies = [ 2286 + "proc-macro2", 2287 + "quote", 2288 + "syn", 2289 + ] 2290 + 2291 + [[package]] 2292 + name = "windows-link" 2293 + version = "0.2.1" 2294 + source = "registry+https://github.com/rust-lang/crates.io-index" 2295 + checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 2296 + 2297 + [[package]] 2298 + name = "windows-result" 2299 + version = "0.4.1" 2300 + source = "registry+https://github.com/rust-lang/crates.io-index" 2301 + checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" 2302 + dependencies = [ 2303 + "windows-link", 2304 + ] 2305 + 2306 + [[package]] 2307 + name = "windows-strings" 2308 + version = "0.5.1" 2309 + source = "registry+https://github.com/rust-lang/crates.io-index" 2310 + checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" 2311 + dependencies = [ 2312 + "windows-link", 2313 + ] 2314 + 2315 + [[package]] 2316 + name = "windows-sys" 2317 + version = "0.52.0" 2318 + source = "registry+https://github.com/rust-lang/crates.io-index" 2319 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2320 + dependencies = [ 2321 + "windows-targets 0.52.6", 2322 + ] 2323 + 2324 + [[package]] 2325 + name = "windows-sys" 2326 + version = "0.60.2" 2327 + source = "registry+https://github.com/rust-lang/crates.io-index" 2328 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 2329 + dependencies = [ 2330 + "windows-targets 0.53.5", 2331 + ] 2332 + 2333 + [[package]] 2334 + name = "windows-sys" 2335 + version = "0.61.2" 2336 + source = "registry+https://github.com/rust-lang/crates.io-index" 2337 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 2338 + dependencies = [ 2339 + "windows-link", 2340 + ] 2341 + 2342 + [[package]] 2343 + name = "windows-targets" 2344 + version = "0.52.6" 2345 + source = "registry+https://github.com/rust-lang/crates.io-index" 2346 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2347 + dependencies = [ 2348 + "windows_aarch64_gnullvm 0.52.6", 2349 + "windows_aarch64_msvc 0.52.6", 2350 + "windows_i686_gnu 0.52.6", 2351 + "windows_i686_gnullvm 0.52.6", 2352 + "windows_i686_msvc 0.52.6", 2353 + "windows_x86_64_gnu 0.52.6", 2354 + "windows_x86_64_gnullvm 0.52.6", 2355 + "windows_x86_64_msvc 0.52.6", 2356 + ] 2357 + 2358 + [[package]] 2359 + name = "windows-targets" 2360 + version = "0.53.5" 2361 + source = "registry+https://github.com/rust-lang/crates.io-index" 2362 + checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" 2363 + dependencies = [ 2364 + "windows-link", 2365 + "windows_aarch64_gnullvm 0.53.1", 2366 + "windows_aarch64_msvc 0.53.1", 2367 + "windows_i686_gnu 0.53.1", 2368 + "windows_i686_gnullvm 0.53.1", 2369 + "windows_i686_msvc 0.53.1", 2370 + "windows_x86_64_gnu 0.53.1", 2371 + "windows_x86_64_gnullvm 0.53.1", 2372 + "windows_x86_64_msvc 0.53.1", 2373 + ] 2374 + 2375 + [[package]] 2376 + name = "windows_aarch64_gnullvm" 2377 + version = "0.52.6" 2378 + source = "registry+https://github.com/rust-lang/crates.io-index" 2379 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2380 + 2381 + [[package]] 2382 + name = "windows_aarch64_gnullvm" 2383 + version = "0.53.1" 2384 + source = "registry+https://github.com/rust-lang/crates.io-index" 2385 + checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" 2386 + 2387 + [[package]] 2388 + name = "windows_aarch64_msvc" 2389 + version = "0.52.6" 2390 + source = "registry+https://github.com/rust-lang/crates.io-index" 2391 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2392 + 2393 + [[package]] 2394 + name = "windows_aarch64_msvc" 2395 + version = "0.53.1" 2396 + source = "registry+https://github.com/rust-lang/crates.io-index" 2397 + checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" 2398 + 2399 + [[package]] 2400 + name = "windows_i686_gnu" 2401 + version = "0.52.6" 2402 + source = "registry+https://github.com/rust-lang/crates.io-index" 2403 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2404 + 2405 + [[package]] 2406 + name = "windows_i686_gnu" 2407 + version = "0.53.1" 2408 + source = "registry+https://github.com/rust-lang/crates.io-index" 2409 + checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" 2410 + 2411 + [[package]] 2412 + name = "windows_i686_gnullvm" 2413 + version = "0.52.6" 2414 + source = "registry+https://github.com/rust-lang/crates.io-index" 2415 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2416 + 2417 + [[package]] 2418 + name = "windows_i686_gnullvm" 2419 + version = "0.53.1" 2420 + source = "registry+https://github.com/rust-lang/crates.io-index" 2421 + checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" 2422 + 2423 + [[package]] 2424 + name = "windows_i686_msvc" 2425 + version = "0.52.6" 2426 + source = "registry+https://github.com/rust-lang/crates.io-index" 2427 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2428 + 2429 + [[package]] 2430 + name = "windows_i686_msvc" 2431 + version = "0.53.1" 2432 + source = "registry+https://github.com/rust-lang/crates.io-index" 2433 + checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" 2434 + 2435 + [[package]] 2436 + name = "windows_x86_64_gnu" 2437 + version = "0.52.6" 2438 + source = "registry+https://github.com/rust-lang/crates.io-index" 2439 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2440 + 2441 + [[package]] 2442 + name = "windows_x86_64_gnu" 2443 + version = "0.53.1" 2444 + source = "registry+https://github.com/rust-lang/crates.io-index" 2445 + checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" 2446 + 2447 + [[package]] 2448 + name = "windows_x86_64_gnullvm" 2449 + version = "0.52.6" 2450 + source = "registry+https://github.com/rust-lang/crates.io-index" 2451 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2452 + 2453 + [[package]] 2454 + name = "windows_x86_64_gnullvm" 2455 + version = "0.53.1" 2456 + source = "registry+https://github.com/rust-lang/crates.io-index" 2457 + checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" 2458 + 2459 + [[package]] 2460 + name = "windows_x86_64_msvc" 2461 + version = "0.52.6" 2462 + source = "registry+https://github.com/rust-lang/crates.io-index" 2463 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2464 + 2465 + [[package]] 2466 + name = "windows_x86_64_msvc" 2467 + version = "0.53.1" 2468 + source = "registry+https://github.com/rust-lang/crates.io-index" 2469 + checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" 2470 + 2471 + [[package]] 2472 + name = "wit-bindgen" 2473 + version = "0.51.0" 2474 + source = "registry+https://github.com/rust-lang/crates.io-index" 2475 + checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" 2476 + dependencies = [ 2477 + "wit-bindgen-rust-macro", 2478 + ] 2479 + 2480 + [[package]] 2481 + name = "wit-bindgen-core" 2482 + version = "0.51.0" 2483 + source = "registry+https://github.com/rust-lang/crates.io-index" 2484 + checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" 2485 + dependencies = [ 2486 + "anyhow", 2487 + "heck", 2488 + "wit-parser", 2489 + ] 2490 + 2491 + [[package]] 2492 + name = "wit-bindgen-rust" 2493 + version = "0.51.0" 2494 + source = "registry+https://github.com/rust-lang/crates.io-index" 2495 + checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" 2496 + dependencies = [ 2497 + "anyhow", 2498 + "heck", 2499 + "indexmap", 2500 + "prettyplease", 2501 + "syn", 2502 + "wasm-metadata", 2503 + "wit-bindgen-core", 2504 + "wit-component", 2505 + ] 2506 + 2507 + [[package]] 2508 + name = "wit-bindgen-rust-macro" 2509 + version = "0.51.0" 2510 + source = "registry+https://github.com/rust-lang/crates.io-index" 2511 + checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" 2512 + dependencies = [ 2513 + "anyhow", 2514 + "prettyplease", 2515 + "proc-macro2", 2516 + "quote", 2517 + "syn", 2518 + "wit-bindgen-core", 2519 + "wit-bindgen-rust", 2520 + ] 2521 + 2522 + [[package]] 2523 + name = "wit-component" 2524 + version = "0.244.0" 2525 + source = "registry+https://github.com/rust-lang/crates.io-index" 2526 + checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" 2527 + dependencies = [ 2528 + "anyhow", 2529 + "bitflags", 2530 + "indexmap", 2531 + "log", 2532 + "serde", 2533 + "serde_derive", 2534 + "serde_json", 2535 + "wasm-encoder", 2536 + "wasm-metadata", 2537 + "wasmparser", 2538 + "wit-parser", 2539 + ] 2540 + 2541 + [[package]] 2542 + name = "wit-parser" 2543 + version = "0.244.0" 2544 + source = "registry+https://github.com/rust-lang/crates.io-index" 2545 + checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" 2546 + dependencies = [ 2547 + "anyhow", 2548 + "id-arena", 2549 + "indexmap", 2550 + "log", 2551 + "semver", 2552 + "serde", 2553 + "serde_derive", 2554 + "serde_json", 2555 + "unicode-xid", 2556 + "wasmparser", 2557 + ] 2558 + 2559 + [[package]] 2560 + name = "writeable" 2561 + version = "0.6.3" 2562 + source = "registry+https://github.com/rust-lang/crates.io-index" 2563 + checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" 2564 + 2565 + [[package]] 2566 + name = "yoke" 2567 + version = "0.8.2" 2568 + source = "registry+https://github.com/rust-lang/crates.io-index" 2569 + checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" 2570 + dependencies = [ 2571 + "stable_deref_trait", 2572 + "yoke-derive", 2573 + "zerofrom", 2574 + ] 2575 + 2576 + [[package]] 2577 + name = "yoke-derive" 2578 + version = "0.8.2" 2579 + source = "registry+https://github.com/rust-lang/crates.io-index" 2580 + checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" 2581 + dependencies = [ 2582 + "proc-macro2", 2583 + "quote", 2584 + "syn", 2585 + "synstructure", 2586 + ] 2587 + 2588 + [[package]] 2589 + name = "zerocopy" 2590 + version = "0.8.48" 2591 + source = "registry+https://github.com/rust-lang/crates.io-index" 2592 + checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" 2593 + dependencies = [ 2594 + "zerocopy-derive", 2595 + ] 2596 + 2597 + [[package]] 2598 + name = "zerocopy-derive" 2599 + version = "0.8.48" 2600 + source = "registry+https://github.com/rust-lang/crates.io-index" 2601 + checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" 2602 + dependencies = [ 2603 + "proc-macro2", 2604 + "quote", 2605 + "syn", 2606 + ] 2607 + 2608 + [[package]] 2609 + name = "zerofrom" 2610 + version = "0.1.7" 2611 + source = "registry+https://github.com/rust-lang/crates.io-index" 2612 + checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" 2613 + dependencies = [ 2614 + "zerofrom-derive", 2615 + ] 2616 + 2617 + [[package]] 2618 + name = "zerofrom-derive" 2619 + version = "0.1.7" 2620 + source = "registry+https://github.com/rust-lang/crates.io-index" 2621 + checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" 2622 + dependencies = [ 2623 + "proc-macro2", 2624 + "quote", 2625 + "syn", 2626 + "synstructure", 2627 + ] 2628 + 2629 + [[package]] 2630 + name = "zeroize" 2631 + version = "1.8.2" 2632 + source = "registry+https://github.com/rust-lang/crates.io-index" 2633 + checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" 2634 + 2635 + [[package]] 2636 + name = "zerotrie" 2637 + version = "0.2.4" 2638 + source = "registry+https://github.com/rust-lang/crates.io-index" 2639 + checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" 2640 + dependencies = [ 2641 + "displaydoc", 2642 + "yoke", 2643 + "zerofrom", 2644 + ] 2645 + 2646 + [[package]] 2647 + name = "zerovec" 2648 + version = "0.11.6" 2649 + source = "registry+https://github.com/rust-lang/crates.io-index" 2650 + checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" 2651 + dependencies = [ 2652 + "yoke", 2653 + "zerofrom", 2654 + "zerovec-derive", 2655 + ] 2656 + 2657 + [[package]] 2658 + name = "zerovec-derive" 2659 + version = "0.11.3" 2660 + source = "registry+https://github.com/rust-lang/crates.io-index" 2661 + checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" 2662 + dependencies = [ 2663 + "proc-macro2", 2664 + "quote", 2665 + "syn", 2666 + ] 2667 + 2668 + [[package]] 2669 + name = "zmij" 2670 + version = "1.0.21" 2671 + source = "registry+https://github.com/rust-lang/crates.io-index" 2672 + checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" 2673 + 2674 + [[package]] 2675 + name = "zstd-sys" 2676 + version = "2.0.16+zstd.1.5.7" 2677 + source = "registry+https://github.com/rust-lang/crates.io-index" 2678 + checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" 2679 + dependencies = [ 2680 + "bindgen 0.72.1", 2681 + "cc", 2682 + "pkg-config", 2683 + ]
+29
Cargo.toml
··· 1 + [package] 2 + name = "eat-rocks" 3 + description = "Restore a rocks database from public object storage" 4 + license = "MIT OR Apache-2.0" 5 + version = "0.1.0" 6 + edition = "2024" 7 + 8 + [features] 9 + default = ["easy"] 10 + easy = ["object_store/aws"] 11 + cli = ["dep:clap", "dep:tracing-subscriber", "easy"] 12 + 13 + [[bin]] 14 + name = "eat-rocks" 15 + required-features = ["cli"] 16 + 17 + [dependencies] 18 + clap = { version = "4", features = ["derive", "env"], optional = true } 19 + crc32c = "0.6" 20 + futures = "0.3" 21 + object_store = "0.13" 22 + thiserror = "2" 23 + tokio = { version = "1", features = ["full"] } 24 + tracing = "0.1" 25 + tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true } 26 + 27 + [dev-dependencies] 28 + rocksdb = "0.22" 29 + tempfile = "3"
+190
LICENSE.Apache-2.0
··· 1 + Apache License 2 + Version 2.0, January 2004 3 + http://www.apache.org/licenses/ 4 + 5 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 + 7 + 1. Definitions. 8 + 9 + "License" shall mean the terms and conditions for use, reproduction, 10 + and distribution as defined by Sections 1 through 9 of this document. 11 + 12 + "Licensor" shall mean the copyright owner or entity authorized by 13 + the copyright owner that is granting the License. 14 + 15 + "Legal Entity" shall mean the union of the acting entity and all 16 + other entities that control, are controlled by, or are under common 17 + control with that entity. For the purposes of this definition, 18 + "control" means (i) the power, direct or indirect, to cause the 19 + direction or management of such entity, whether by contract or 20 + otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 + outstanding shares, or (iii) beneficial ownership of such entity. 22 + 23 + "You" (or "Your") shall mean an individual or Legal Entity 24 + exercising permissions granted by this License. 25 + 26 + "Source" form shall mean the preferred form for making modifications, 27 + including but not limited to software source code, documentation 28 + source, and configuration files. 29 + 30 + "Object" form shall mean any form resulting from mechanical 31 + transformation or translation of a Source form, including but 32 + not limited to compiled object code, generated documentation, 33 + and conversions to other media types. 34 + 35 + "Work" shall mean the work of authorship, whether in Source or 36 + Object form, made available under the License, as indicated by a 37 + copyright notice that is included in or attached to the work 38 + (an example is provided in the Appendix below). 39 + 40 + "Derivative Works" shall mean any work, whether in Source or Object 41 + form, that is based on (or derived from) the Work and for which the 42 + editorial revisions, annotations, elaborations, or other modifications 43 + represent, as a whole, an original work of authorship. For the purposes 44 + of this License, Derivative Works shall not include works that remain 45 + separable from, or merely link (or bind by name) to the interfaces of, 46 + the Work and Derivative Works thereof. 47 + 48 + "Contribution" shall mean any work of authorship, including 49 + the original version of the Work and any modifications or additions 50 + to that Work or Derivative Works thereof, that is intentionally 51 + submitted to Licensor for inclusion in the Work by the copyright owner 52 + or by an individual or Legal Entity authorized to submit on behalf of 53 + the copyright owner. For the purposes of this definition, "submitted" 54 + means any form of electronic, verbal, or written communication sent 55 + to the Licensor or its representatives, including but not limited to 56 + communication on electronic mailing lists, source code control systems, 57 + and issue tracking systems that are managed by, or on behalf of, the 58 + Licensor for the purpose of discussing and improving the Work, but 59 + excluding communication that is conspicuously marked or otherwise 60 + designated in writing by the copyright owner as "Not a Contribution." 61 + 62 + "Contributor" shall mean Licensor and any individual or Legal Entity 63 + on behalf of whom a Contribution has been received by Licensor and 64 + subsequently incorporated within the Work. 65 + 66 + 2. Grant of Copyright License. Subject to the terms and conditions of 67 + this License, each Contributor hereby grants to You a perpetual, 68 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 + copyright license to reproduce, prepare Derivative Works of, 70 + publicly display, publicly perform, sublicense, and distribute the 71 + Work and such Derivative Works in Source or Object form. 72 + 73 + 3. Grant of Patent License. Subject to the terms and conditions of 74 + this License, each Contributor hereby grants to You a perpetual, 75 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 + (except as stated in this section) patent license to make, have made, 77 + use, offer to sell, sell, import, and otherwise transfer the Work, 78 + where such license applies only to those patent claims licensable 79 + by such Contributor that are necessarily infringed by their 80 + Contribution(s) alone or by combination of their Contribution(s) 81 + with the Work to which such Contribution(s) was submitted. If You 82 + institute patent litigation against any entity (including a 83 + cross-claim or counterclaim in a lawsuit) alleging that the Work 84 + or a Contribution incorporated within the Work constitutes direct 85 + or contributory patent infringement, then any patent licenses 86 + granted to You under this License for that Work shall terminate 87 + as of the date such litigation is filed. 88 + 89 + 4. Redistribution. You may reproduce and distribute copies of the 90 + Work or Derivative Works thereof in any medium, with or without 91 + modifications, and in Source or Object form, provided that You 92 + meet the following conditions: 93 + 94 + (a) You must give any other recipients of the Work or 95 + Derivative Works a copy of this License; and 96 + 97 + (b) You must cause any modified files to carry prominent notices 98 + stating that You changed the files; and 99 + 100 + (c) You must retain, in the Source form of any Derivative Works 101 + that You distribute, all copyright, patent, trademark, and 102 + attribution notices from the Source form of the Work, 103 + excluding those notices that do not pertain to any part of 104 + the Derivative Works; and 105 + 106 + (d) If the Work includes a "NOTICE" text file as part of its 107 + distribution, then any Derivative Works that You distribute must 108 + include a readable copy of the attribution notices contained 109 + within such NOTICE file, excluding those notices that do not 110 + pertain to any part of the Derivative Works, in at least one 111 + of the following places: within a NOTICE text file distributed 112 + as part of the Derivative Works; within the Source form or 113 + documentation, if provided along with the Derivative Works; or, 114 + within a display generated by the Derivative Works, if and 115 + wherever such third-party notices normally appear. The contents 116 + of the NOTICE file are for informational purposes only and 117 + do not modify the License. You may add Your own attribution 118 + notices within Derivative Works that You distribute, alongside 119 + or as an addendum to the NOTICE text from the Work, provided 120 + that such additional attribution notices cannot be construed 121 + as modifying the License. 122 + 123 + You may add Your own copyright statement to Your modifications and 124 + may provide additional or different license terms and conditions 125 + for use, reproduction, or distribution of Your modifications, or 126 + for any such Derivative Works as a whole, provided Your use, 127 + reproduction, and distribution of the Work otherwise complies with 128 + the conditions stated in this License. 129 + 130 + 5. Submission of Contributions. Unless You explicitly state otherwise, 131 + any Contribution intentionally submitted for inclusion in the Work 132 + by You to the Licensor shall be under the terms and conditions of 133 + this License, without any additional terms or conditions. 134 + Notwithstanding the above, nothing herein shall supersede or modify 135 + the terms of any separate license agreement you may have executed 136 + with Licensor regarding such Contributions. 137 + 138 + 6. Trademarks. This License does not grant permission to use the trade 139 + names, trademarks, service marks, or product names of the Licensor, 140 + except as required for reasonable and customary use in describing the 141 + origin of the Work and reproducing the content of the NOTICE file. 142 + 143 + 7. Disclaimer of Warranty. Unless required by applicable law or 144 + agreed to in writing, Licensor provides the Work (and each 145 + Contributor provides its Contributions) on an "AS IS" BASIS, 146 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 + implied, including, without limitation, any warranties or conditions 148 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 + PARTICULAR PURPOSE. You are solely responsible for determining the 150 + appropriateness of using or redistributing the Work and assume any 151 + risks associated with Your exercise of permissions under this License. 152 + 153 + 8. Limitation of Liability. In no event and under no legal theory, 154 + whether in tort (including negligence), contract, or otherwise, 155 + unless required by applicable law (such as deliberate and grossly 156 + negligent acts) or agreed to in writing, shall any Contributor be 157 + liable to You for damages, including any direct, indirect, special, 158 + incidental, or consequential damages of any character arising as a 159 + result of this License or out of the use or inability to use the 160 + Work (including but not limited to damages for loss of goodwill, 161 + work stoppage, computer failure or malfunction, or any and all 162 + other commercial damages or losses), even if such Contributor 163 + has been advised of the possibility of such damages. 164 + 165 + 9. Accepting Warranty or Additional Liability. While redistributing 166 + the Work or Derivative Works thereof, You may choose to offer, 167 + and charge a fee for, acceptance of support, warranty, indemnity, 168 + or other liability obligations and/or rights consistent with this 169 + License. However, in accepting such obligations, You may act only 170 + on Your own behalf and on Your sole responsibility, not on behalf 171 + of any other Contributor, and only if You agree to indemnify, 172 + defend, and hold each Contributor harmless for any liability 173 + incurred by, or claims asserted against, such Contributor by reason 174 + of your accepting any such warranty or additional liability. 175 + 176 + END OF TERMS AND CONDITIONS 177 + 178 + Copyright 2025 microcosm 179 + 180 + Licensed under the Apache License, Version 2.0 (the "License"); 181 + you may not use this file except in compliance with the License. 182 + You may obtain a copy of the License at 183 + 184 + http://www.apache.org/licenses/LICENSE-2.0 185 + 186 + Unless required by applicable law or agreed to in writing, software 187 + distributed under the License is distributed on an "AS IS" BASIS, 188 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 + See the License for the specific language governing permissions and 190 + limitations under the License.
+21
LICENSE.MIT
··· 1 + MIT License 2 + 3 + Copyright (c) 2025 microcosm 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy 6 + of this software and associated documentation files (the "Software"), to deal 7 + in the Software without restriction, including without limitation the rights 8 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 + copies of the Software, and to permit persons to whom the Software is 10 + furnished to do so, subject to the following conditions: 11 + 12 + The above copyright notice and this permission notice shall be included in all 13 + copies or substantial portions of the Software. 14 + 15 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 + SOFTWARE.
+4
fuzz/.gitignore
··· 1 + target 2 + corpus 3 + artifacts 4 + coverage
+1111
fuzz/Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "android_system_properties" 7 + version = "0.1.5" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 10 + dependencies = [ 11 + "libc", 12 + ] 13 + 14 + [[package]] 15 + name = "arbitrary" 16 + version = "1.4.2" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" 19 + 20 + [[package]] 21 + name = "async-trait" 22 + version = "0.1.89" 23 + source = "registry+https://github.com/rust-lang/crates.io-index" 24 + checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" 25 + dependencies = [ 26 + "proc-macro2", 27 + "quote", 28 + "syn", 29 + ] 30 + 31 + [[package]] 32 + name = "autocfg" 33 + version = "1.5.0" 34 + source = "registry+https://github.com/rust-lang/crates.io-index" 35 + checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 36 + 37 + [[package]] 38 + name = "bitflags" 39 + version = "2.11.0" 40 + source = "registry+https://github.com/rust-lang/crates.io-index" 41 + checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" 42 + 43 + [[package]] 44 + name = "bumpalo" 45 + version = "3.20.2" 46 + source = "registry+https://github.com/rust-lang/crates.io-index" 47 + checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" 48 + 49 + [[package]] 50 + name = "bytes" 51 + version = "1.11.1" 52 + source = "registry+https://github.com/rust-lang/crates.io-index" 53 + checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" 54 + 55 + [[package]] 56 + name = "cc" 57 + version = "1.2.59" 58 + source = "registry+https://github.com/rust-lang/crates.io-index" 59 + checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" 60 + dependencies = [ 61 + "find-msvc-tools", 62 + "jobserver", 63 + "libc", 64 + "shlex", 65 + ] 66 + 67 + [[package]] 68 + name = "cfg-if" 69 + version = "1.0.4" 70 + source = "registry+https://github.com/rust-lang/crates.io-index" 71 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 72 + 73 + [[package]] 74 + name = "chrono" 75 + version = "0.4.44" 76 + source = "registry+https://github.com/rust-lang/crates.io-index" 77 + checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" 78 + dependencies = [ 79 + "iana-time-zone", 80 + "num-traits", 81 + "windows-link", 82 + ] 83 + 84 + [[package]] 85 + name = "core-foundation-sys" 86 + version = "0.8.7" 87 + source = "registry+https://github.com/rust-lang/crates.io-index" 88 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 89 + 90 + [[package]] 91 + name = "crc32c" 92 + version = "0.6.8" 93 + source = "registry+https://github.com/rust-lang/crates.io-index" 94 + checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" 95 + dependencies = [ 96 + "rustc_version", 97 + ] 98 + 99 + [[package]] 100 + name = "displaydoc" 101 + version = "0.2.5" 102 + source = "registry+https://github.com/rust-lang/crates.io-index" 103 + checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 104 + dependencies = [ 105 + "proc-macro2", 106 + "quote", 107 + "syn", 108 + ] 109 + 110 + [[package]] 111 + name = "eat-rocks" 112 + version = "0.1.0" 113 + dependencies = [ 114 + "crc32c", 115 + "futures", 116 + "object_store", 117 + "thiserror", 118 + "tokio", 119 + "tracing", 120 + ] 121 + 122 + [[package]] 123 + name = "eat-rocks-fuzz" 124 + version = "0.0.0" 125 + dependencies = [ 126 + "eat-rocks", 127 + "libfuzzer-sys", 128 + ] 129 + 130 + [[package]] 131 + name = "either" 132 + version = "1.15.0" 133 + source = "registry+https://github.com/rust-lang/crates.io-index" 134 + checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 135 + 136 + [[package]] 137 + name = "errno" 138 + version = "0.3.14" 139 + source = "registry+https://github.com/rust-lang/crates.io-index" 140 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 141 + dependencies = [ 142 + "libc", 143 + "windows-sys", 144 + ] 145 + 146 + [[package]] 147 + name = "find-msvc-tools" 148 + version = "0.1.9" 149 + source = "registry+https://github.com/rust-lang/crates.io-index" 150 + checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" 151 + 152 + [[package]] 153 + name = "form_urlencoded" 154 + version = "1.2.2" 155 + source = "registry+https://github.com/rust-lang/crates.io-index" 156 + checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 157 + dependencies = [ 158 + "percent-encoding", 159 + ] 160 + 161 + [[package]] 162 + name = "futures" 163 + version = "0.3.32" 164 + source = "registry+https://github.com/rust-lang/crates.io-index" 165 + checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" 166 + dependencies = [ 167 + "futures-channel", 168 + "futures-core", 169 + "futures-executor", 170 + "futures-io", 171 + "futures-sink", 172 + "futures-task", 173 + "futures-util", 174 + ] 175 + 176 + [[package]] 177 + name = "futures-channel" 178 + version = "0.3.32" 179 + source = "registry+https://github.com/rust-lang/crates.io-index" 180 + checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" 181 + dependencies = [ 182 + "futures-core", 183 + "futures-sink", 184 + ] 185 + 186 + [[package]] 187 + name = "futures-core" 188 + version = "0.3.32" 189 + source = "registry+https://github.com/rust-lang/crates.io-index" 190 + checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" 191 + 192 + [[package]] 193 + name = "futures-executor" 194 + version = "0.3.32" 195 + source = "registry+https://github.com/rust-lang/crates.io-index" 196 + checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" 197 + dependencies = [ 198 + "futures-core", 199 + "futures-task", 200 + "futures-util", 201 + ] 202 + 203 + [[package]] 204 + name = "futures-io" 205 + version = "0.3.32" 206 + source = "registry+https://github.com/rust-lang/crates.io-index" 207 + checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" 208 + 209 + [[package]] 210 + name = "futures-macro" 211 + version = "0.3.32" 212 + source = "registry+https://github.com/rust-lang/crates.io-index" 213 + checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" 214 + dependencies = [ 215 + "proc-macro2", 216 + "quote", 217 + "syn", 218 + ] 219 + 220 + [[package]] 221 + name = "futures-sink" 222 + version = "0.3.32" 223 + source = "registry+https://github.com/rust-lang/crates.io-index" 224 + checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" 225 + 226 + [[package]] 227 + name = "futures-task" 228 + version = "0.3.32" 229 + source = "registry+https://github.com/rust-lang/crates.io-index" 230 + checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" 231 + 232 + [[package]] 233 + name = "futures-util" 234 + version = "0.3.32" 235 + source = "registry+https://github.com/rust-lang/crates.io-index" 236 + checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" 237 + dependencies = [ 238 + "futures-channel", 239 + "futures-core", 240 + "futures-io", 241 + "futures-macro", 242 + "futures-sink", 243 + "futures-task", 244 + "memchr", 245 + "pin-project-lite", 246 + "slab", 247 + ] 248 + 249 + [[package]] 250 + name = "getrandom" 251 + version = "0.3.4" 252 + source = "registry+https://github.com/rust-lang/crates.io-index" 253 + checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 254 + dependencies = [ 255 + "cfg-if", 256 + "libc", 257 + "r-efi", 258 + "wasip2", 259 + ] 260 + 261 + [[package]] 262 + name = "http" 263 + version = "1.4.0" 264 + source = "registry+https://github.com/rust-lang/crates.io-index" 265 + checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" 266 + dependencies = [ 267 + "bytes", 268 + "itoa", 269 + ] 270 + 271 + [[package]] 272 + name = "humantime" 273 + version = "2.3.0" 274 + source = "registry+https://github.com/rust-lang/crates.io-index" 275 + checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" 276 + 277 + [[package]] 278 + name = "iana-time-zone" 279 + version = "0.1.65" 280 + source = "registry+https://github.com/rust-lang/crates.io-index" 281 + checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" 282 + dependencies = [ 283 + "android_system_properties", 284 + "core-foundation-sys", 285 + "iana-time-zone-haiku", 286 + "js-sys", 287 + "log", 288 + "wasm-bindgen", 289 + "windows-core", 290 + ] 291 + 292 + [[package]] 293 + name = "iana-time-zone-haiku" 294 + version = "0.1.2" 295 + source = "registry+https://github.com/rust-lang/crates.io-index" 296 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 297 + dependencies = [ 298 + "cc", 299 + ] 300 + 301 + [[package]] 302 + name = "icu_collections" 303 + version = "2.2.0" 304 + source = "registry+https://github.com/rust-lang/crates.io-index" 305 + checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" 306 + dependencies = [ 307 + "displaydoc", 308 + "potential_utf", 309 + "utf8_iter", 310 + "yoke", 311 + "zerofrom", 312 + "zerovec", 313 + ] 314 + 315 + [[package]] 316 + name = "icu_locale_core" 317 + version = "2.2.0" 318 + source = "registry+https://github.com/rust-lang/crates.io-index" 319 + checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" 320 + dependencies = [ 321 + "displaydoc", 322 + "litemap", 323 + "tinystr", 324 + "writeable", 325 + "zerovec", 326 + ] 327 + 328 + [[package]] 329 + name = "icu_normalizer" 330 + version = "2.2.0" 331 + source = "registry+https://github.com/rust-lang/crates.io-index" 332 + checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" 333 + dependencies = [ 334 + "icu_collections", 335 + "icu_normalizer_data", 336 + "icu_properties", 337 + "icu_provider", 338 + "smallvec", 339 + "zerovec", 340 + ] 341 + 342 + [[package]] 343 + name = "icu_normalizer_data" 344 + version = "2.2.0" 345 + source = "registry+https://github.com/rust-lang/crates.io-index" 346 + checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" 347 + 348 + [[package]] 349 + name = "icu_properties" 350 + version = "2.2.0" 351 + source = "registry+https://github.com/rust-lang/crates.io-index" 352 + checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" 353 + dependencies = [ 354 + "icu_collections", 355 + "icu_locale_core", 356 + "icu_properties_data", 357 + "icu_provider", 358 + "zerotrie", 359 + "zerovec", 360 + ] 361 + 362 + [[package]] 363 + name = "icu_properties_data" 364 + version = "2.2.0" 365 + source = "registry+https://github.com/rust-lang/crates.io-index" 366 + checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" 367 + 368 + [[package]] 369 + name = "icu_provider" 370 + version = "2.2.0" 371 + source = "registry+https://github.com/rust-lang/crates.io-index" 372 + checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" 373 + dependencies = [ 374 + "displaydoc", 375 + "icu_locale_core", 376 + "writeable", 377 + "yoke", 378 + "zerofrom", 379 + "zerotrie", 380 + "zerovec", 381 + ] 382 + 383 + [[package]] 384 + name = "idna" 385 + version = "1.1.0" 386 + source = "registry+https://github.com/rust-lang/crates.io-index" 387 + checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 388 + dependencies = [ 389 + "idna_adapter", 390 + "smallvec", 391 + "utf8_iter", 392 + ] 393 + 394 + [[package]] 395 + name = "idna_adapter" 396 + version = "1.2.1" 397 + source = "registry+https://github.com/rust-lang/crates.io-index" 398 + checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 399 + dependencies = [ 400 + "icu_normalizer", 401 + "icu_properties", 402 + ] 403 + 404 + [[package]] 405 + name = "itertools" 406 + version = "0.14.0" 407 + source = "registry+https://github.com/rust-lang/crates.io-index" 408 + checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" 409 + dependencies = [ 410 + "either", 411 + ] 412 + 413 + [[package]] 414 + name = "itoa" 415 + version = "1.0.18" 416 + source = "registry+https://github.com/rust-lang/crates.io-index" 417 + checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" 418 + 419 + [[package]] 420 + name = "jobserver" 421 + version = "0.1.34" 422 + source = "registry+https://github.com/rust-lang/crates.io-index" 423 + checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" 424 + dependencies = [ 425 + "getrandom", 426 + "libc", 427 + ] 428 + 429 + [[package]] 430 + name = "js-sys" 431 + version = "0.3.94" 432 + source = "registry+https://github.com/rust-lang/crates.io-index" 433 + checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" 434 + dependencies = [ 435 + "cfg-if", 436 + "futures-util", 437 + "once_cell", 438 + "wasm-bindgen", 439 + ] 440 + 441 + [[package]] 442 + name = "libc" 443 + version = "0.2.184" 444 + source = "registry+https://github.com/rust-lang/crates.io-index" 445 + checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" 446 + 447 + [[package]] 448 + name = "libfuzzer-sys" 449 + version = "0.4.12" 450 + source = "registry+https://github.com/rust-lang/crates.io-index" 451 + checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" 452 + dependencies = [ 453 + "arbitrary", 454 + "cc", 455 + ] 456 + 457 + [[package]] 458 + name = "litemap" 459 + version = "0.8.2" 460 + source = "registry+https://github.com/rust-lang/crates.io-index" 461 + checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" 462 + 463 + [[package]] 464 + name = "lock_api" 465 + version = "0.4.14" 466 + source = "registry+https://github.com/rust-lang/crates.io-index" 467 + checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 468 + dependencies = [ 469 + "scopeguard", 470 + ] 471 + 472 + [[package]] 473 + name = "log" 474 + version = "0.4.29" 475 + source = "registry+https://github.com/rust-lang/crates.io-index" 476 + checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 477 + 478 + [[package]] 479 + name = "memchr" 480 + version = "2.8.0" 481 + source = "registry+https://github.com/rust-lang/crates.io-index" 482 + checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" 483 + 484 + [[package]] 485 + name = "mio" 486 + version = "1.2.0" 487 + source = "registry+https://github.com/rust-lang/crates.io-index" 488 + checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" 489 + dependencies = [ 490 + "libc", 491 + "wasi", 492 + "windows-sys", 493 + ] 494 + 495 + [[package]] 496 + name = "num-traits" 497 + version = "0.2.19" 498 + source = "registry+https://github.com/rust-lang/crates.io-index" 499 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 500 + dependencies = [ 501 + "autocfg", 502 + ] 503 + 504 + [[package]] 505 + name = "object_store" 506 + version = "0.13.2" 507 + source = "registry+https://github.com/rust-lang/crates.io-index" 508 + checksum = "622acbc9100d3c10e2ee15804b0caa40e55c933d5aa53814cd520805b7958a49" 509 + dependencies = [ 510 + "async-trait", 511 + "bytes", 512 + "chrono", 513 + "futures-channel", 514 + "futures-core", 515 + "futures-util", 516 + "http", 517 + "humantime", 518 + "itertools", 519 + "parking_lot", 520 + "percent-encoding", 521 + "thiserror", 522 + "tokio", 523 + "tracing", 524 + "url", 525 + "walkdir", 526 + "wasm-bindgen-futures", 527 + "web-time", 528 + ] 529 + 530 + [[package]] 531 + name = "once_cell" 532 + version = "1.21.4" 533 + source = "registry+https://github.com/rust-lang/crates.io-index" 534 + checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" 535 + 536 + [[package]] 537 + name = "parking_lot" 538 + version = "0.12.5" 539 + source = "registry+https://github.com/rust-lang/crates.io-index" 540 + checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 541 + dependencies = [ 542 + "lock_api", 543 + "parking_lot_core", 544 + ] 545 + 546 + [[package]] 547 + name = "parking_lot_core" 548 + version = "0.9.12" 549 + source = "registry+https://github.com/rust-lang/crates.io-index" 550 + checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 551 + dependencies = [ 552 + "cfg-if", 553 + "libc", 554 + "redox_syscall", 555 + "smallvec", 556 + "windows-link", 557 + ] 558 + 559 + [[package]] 560 + name = "percent-encoding" 561 + version = "2.3.2" 562 + source = "registry+https://github.com/rust-lang/crates.io-index" 563 + checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 564 + 565 + [[package]] 566 + name = "pin-project-lite" 567 + version = "0.2.17" 568 + source = "registry+https://github.com/rust-lang/crates.io-index" 569 + checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" 570 + 571 + [[package]] 572 + name = "potential_utf" 573 + version = "0.1.5" 574 + source = "registry+https://github.com/rust-lang/crates.io-index" 575 + checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" 576 + dependencies = [ 577 + "zerovec", 578 + ] 579 + 580 + [[package]] 581 + name = "proc-macro2" 582 + version = "1.0.106" 583 + source = "registry+https://github.com/rust-lang/crates.io-index" 584 + checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" 585 + dependencies = [ 586 + "unicode-ident", 587 + ] 588 + 589 + [[package]] 590 + name = "quote" 591 + version = "1.0.45" 592 + source = "registry+https://github.com/rust-lang/crates.io-index" 593 + checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" 594 + dependencies = [ 595 + "proc-macro2", 596 + ] 597 + 598 + [[package]] 599 + name = "r-efi" 600 + version = "5.3.0" 601 + source = "registry+https://github.com/rust-lang/crates.io-index" 602 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 603 + 604 + [[package]] 605 + name = "redox_syscall" 606 + version = "0.5.18" 607 + source = "registry+https://github.com/rust-lang/crates.io-index" 608 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 609 + dependencies = [ 610 + "bitflags", 611 + ] 612 + 613 + [[package]] 614 + name = "rustc_version" 615 + version = "0.4.1" 616 + source = "registry+https://github.com/rust-lang/crates.io-index" 617 + checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 618 + dependencies = [ 619 + "semver", 620 + ] 621 + 622 + [[package]] 623 + name = "rustversion" 624 + version = "1.0.22" 625 + source = "registry+https://github.com/rust-lang/crates.io-index" 626 + checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 627 + 628 + [[package]] 629 + name = "same-file" 630 + version = "1.0.6" 631 + source = "registry+https://github.com/rust-lang/crates.io-index" 632 + checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 633 + dependencies = [ 634 + "winapi-util", 635 + ] 636 + 637 + [[package]] 638 + name = "scopeguard" 639 + version = "1.2.0" 640 + source = "registry+https://github.com/rust-lang/crates.io-index" 641 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 642 + 643 + [[package]] 644 + name = "semver" 645 + version = "1.0.28" 646 + source = "registry+https://github.com/rust-lang/crates.io-index" 647 + checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" 648 + 649 + [[package]] 650 + name = "serde" 651 + version = "1.0.228" 652 + source = "registry+https://github.com/rust-lang/crates.io-index" 653 + checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 654 + dependencies = [ 655 + "serde_core", 656 + ] 657 + 658 + [[package]] 659 + name = "serde_core" 660 + version = "1.0.228" 661 + source = "registry+https://github.com/rust-lang/crates.io-index" 662 + checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 663 + dependencies = [ 664 + "serde_derive", 665 + ] 666 + 667 + [[package]] 668 + name = "serde_derive" 669 + version = "1.0.228" 670 + source = "registry+https://github.com/rust-lang/crates.io-index" 671 + checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 672 + dependencies = [ 673 + "proc-macro2", 674 + "quote", 675 + "syn", 676 + ] 677 + 678 + [[package]] 679 + name = "shlex" 680 + version = "1.3.0" 681 + source = "registry+https://github.com/rust-lang/crates.io-index" 682 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 683 + 684 + [[package]] 685 + name = "signal-hook-registry" 686 + version = "1.4.8" 687 + source = "registry+https://github.com/rust-lang/crates.io-index" 688 + checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" 689 + dependencies = [ 690 + "errno", 691 + "libc", 692 + ] 693 + 694 + [[package]] 695 + name = "slab" 696 + version = "0.4.12" 697 + source = "registry+https://github.com/rust-lang/crates.io-index" 698 + checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" 699 + 700 + [[package]] 701 + name = "smallvec" 702 + version = "1.15.1" 703 + source = "registry+https://github.com/rust-lang/crates.io-index" 704 + checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 705 + 706 + [[package]] 707 + name = "socket2" 708 + version = "0.6.3" 709 + source = "registry+https://github.com/rust-lang/crates.io-index" 710 + checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" 711 + dependencies = [ 712 + "libc", 713 + "windows-sys", 714 + ] 715 + 716 + [[package]] 717 + name = "stable_deref_trait" 718 + version = "1.2.1" 719 + source = "registry+https://github.com/rust-lang/crates.io-index" 720 + checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 721 + 722 + [[package]] 723 + name = "syn" 724 + version = "2.0.117" 725 + source = "registry+https://github.com/rust-lang/crates.io-index" 726 + checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" 727 + dependencies = [ 728 + "proc-macro2", 729 + "quote", 730 + "unicode-ident", 731 + ] 732 + 733 + [[package]] 734 + name = "synstructure" 735 + version = "0.13.2" 736 + source = "registry+https://github.com/rust-lang/crates.io-index" 737 + checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 738 + dependencies = [ 739 + "proc-macro2", 740 + "quote", 741 + "syn", 742 + ] 743 + 744 + [[package]] 745 + name = "thiserror" 746 + version = "2.0.18" 747 + source = "registry+https://github.com/rust-lang/crates.io-index" 748 + checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" 749 + dependencies = [ 750 + "thiserror-impl", 751 + ] 752 + 753 + [[package]] 754 + name = "thiserror-impl" 755 + version = "2.0.18" 756 + source = "registry+https://github.com/rust-lang/crates.io-index" 757 + checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" 758 + dependencies = [ 759 + "proc-macro2", 760 + "quote", 761 + "syn", 762 + ] 763 + 764 + [[package]] 765 + name = "tinystr" 766 + version = "0.8.3" 767 + source = "registry+https://github.com/rust-lang/crates.io-index" 768 + checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" 769 + dependencies = [ 770 + "displaydoc", 771 + "zerovec", 772 + ] 773 + 774 + [[package]] 775 + name = "tokio" 776 + version = "1.51.1" 777 + source = "registry+https://github.com/rust-lang/crates.io-index" 778 + checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" 779 + dependencies = [ 780 + "bytes", 781 + "libc", 782 + "mio", 783 + "parking_lot", 784 + "pin-project-lite", 785 + "signal-hook-registry", 786 + "socket2", 787 + "tokio-macros", 788 + "windows-sys", 789 + ] 790 + 791 + [[package]] 792 + name = "tokio-macros" 793 + version = "2.7.0" 794 + source = "registry+https://github.com/rust-lang/crates.io-index" 795 + checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" 796 + dependencies = [ 797 + "proc-macro2", 798 + "quote", 799 + "syn", 800 + ] 801 + 802 + [[package]] 803 + name = "tracing" 804 + version = "0.1.44" 805 + source = "registry+https://github.com/rust-lang/crates.io-index" 806 + checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" 807 + dependencies = [ 808 + "pin-project-lite", 809 + "tracing-attributes", 810 + "tracing-core", 811 + ] 812 + 813 + [[package]] 814 + name = "tracing-attributes" 815 + version = "0.1.31" 816 + source = "registry+https://github.com/rust-lang/crates.io-index" 817 + checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" 818 + dependencies = [ 819 + "proc-macro2", 820 + "quote", 821 + "syn", 822 + ] 823 + 824 + [[package]] 825 + name = "tracing-core" 826 + version = "0.1.36" 827 + source = "registry+https://github.com/rust-lang/crates.io-index" 828 + checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" 829 + dependencies = [ 830 + "once_cell", 831 + ] 832 + 833 + [[package]] 834 + name = "unicode-ident" 835 + version = "1.0.24" 836 + source = "registry+https://github.com/rust-lang/crates.io-index" 837 + checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" 838 + 839 + [[package]] 840 + name = "url" 841 + version = "2.5.8" 842 + source = "registry+https://github.com/rust-lang/crates.io-index" 843 + checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" 844 + dependencies = [ 845 + "form_urlencoded", 846 + "idna", 847 + "percent-encoding", 848 + "serde", 849 + ] 850 + 851 + [[package]] 852 + name = "utf8_iter" 853 + version = "1.0.4" 854 + source = "registry+https://github.com/rust-lang/crates.io-index" 855 + checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 856 + 857 + [[package]] 858 + name = "walkdir" 859 + version = "2.5.0" 860 + source = "registry+https://github.com/rust-lang/crates.io-index" 861 + checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 862 + dependencies = [ 863 + "same-file", 864 + "winapi-util", 865 + ] 866 + 867 + [[package]] 868 + name = "wasi" 869 + version = "0.11.1+wasi-snapshot-preview1" 870 + source = "registry+https://github.com/rust-lang/crates.io-index" 871 + checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 872 + 873 + [[package]] 874 + name = "wasip2" 875 + version = "1.0.2+wasi-0.2.9" 876 + source = "registry+https://github.com/rust-lang/crates.io-index" 877 + checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" 878 + dependencies = [ 879 + "wit-bindgen", 880 + ] 881 + 882 + [[package]] 883 + name = "wasm-bindgen" 884 + version = "0.2.117" 885 + source = "registry+https://github.com/rust-lang/crates.io-index" 886 + checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" 887 + dependencies = [ 888 + "cfg-if", 889 + "once_cell", 890 + "rustversion", 891 + "wasm-bindgen-macro", 892 + "wasm-bindgen-shared", 893 + ] 894 + 895 + [[package]] 896 + name = "wasm-bindgen-futures" 897 + version = "0.4.67" 898 + source = "registry+https://github.com/rust-lang/crates.io-index" 899 + checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" 900 + dependencies = [ 901 + "js-sys", 902 + "wasm-bindgen", 903 + ] 904 + 905 + [[package]] 906 + name = "wasm-bindgen-macro" 907 + version = "0.2.117" 908 + source = "registry+https://github.com/rust-lang/crates.io-index" 909 + checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" 910 + dependencies = [ 911 + "quote", 912 + "wasm-bindgen-macro-support", 913 + ] 914 + 915 + [[package]] 916 + name = "wasm-bindgen-macro-support" 917 + version = "0.2.117" 918 + source = "registry+https://github.com/rust-lang/crates.io-index" 919 + checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" 920 + dependencies = [ 921 + "bumpalo", 922 + "proc-macro2", 923 + "quote", 924 + "syn", 925 + "wasm-bindgen-shared", 926 + ] 927 + 928 + [[package]] 929 + name = "wasm-bindgen-shared" 930 + version = "0.2.117" 931 + source = "registry+https://github.com/rust-lang/crates.io-index" 932 + checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" 933 + dependencies = [ 934 + "unicode-ident", 935 + ] 936 + 937 + [[package]] 938 + name = "web-time" 939 + version = "1.1.0" 940 + source = "registry+https://github.com/rust-lang/crates.io-index" 941 + checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 942 + dependencies = [ 943 + "js-sys", 944 + "wasm-bindgen", 945 + ] 946 + 947 + [[package]] 948 + name = "winapi-util" 949 + version = "0.1.11" 950 + source = "registry+https://github.com/rust-lang/crates.io-index" 951 + checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" 952 + dependencies = [ 953 + "windows-sys", 954 + ] 955 + 956 + [[package]] 957 + name = "windows-core" 958 + version = "0.62.2" 959 + source = "registry+https://github.com/rust-lang/crates.io-index" 960 + checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" 961 + dependencies = [ 962 + "windows-implement", 963 + "windows-interface", 964 + "windows-link", 965 + "windows-result", 966 + "windows-strings", 967 + ] 968 + 969 + [[package]] 970 + name = "windows-implement" 971 + version = "0.60.2" 972 + source = "registry+https://github.com/rust-lang/crates.io-index" 973 + checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" 974 + dependencies = [ 975 + "proc-macro2", 976 + "quote", 977 + "syn", 978 + ] 979 + 980 + [[package]] 981 + name = "windows-interface" 982 + version = "0.59.3" 983 + source = "registry+https://github.com/rust-lang/crates.io-index" 984 + checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" 985 + dependencies = [ 986 + "proc-macro2", 987 + "quote", 988 + "syn", 989 + ] 990 + 991 + [[package]] 992 + name = "windows-link" 993 + version = "0.2.1" 994 + source = "registry+https://github.com/rust-lang/crates.io-index" 995 + checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 996 + 997 + [[package]] 998 + name = "windows-result" 999 + version = "0.4.1" 1000 + source = "registry+https://github.com/rust-lang/crates.io-index" 1001 + checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" 1002 + dependencies = [ 1003 + "windows-link", 1004 + ] 1005 + 1006 + [[package]] 1007 + name = "windows-strings" 1008 + version = "0.5.1" 1009 + source = "registry+https://github.com/rust-lang/crates.io-index" 1010 + checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" 1011 + dependencies = [ 1012 + "windows-link", 1013 + ] 1014 + 1015 + [[package]] 1016 + name = "windows-sys" 1017 + version = "0.61.2" 1018 + source = "registry+https://github.com/rust-lang/crates.io-index" 1019 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 1020 + dependencies = [ 1021 + "windows-link", 1022 + ] 1023 + 1024 + [[package]] 1025 + name = "wit-bindgen" 1026 + version = "0.51.0" 1027 + source = "registry+https://github.com/rust-lang/crates.io-index" 1028 + checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" 1029 + 1030 + [[package]] 1031 + name = "writeable" 1032 + version = "0.6.3" 1033 + source = "registry+https://github.com/rust-lang/crates.io-index" 1034 + checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" 1035 + 1036 + [[package]] 1037 + name = "yoke" 1038 + version = "0.8.2" 1039 + source = "registry+https://github.com/rust-lang/crates.io-index" 1040 + checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" 1041 + dependencies = [ 1042 + "stable_deref_trait", 1043 + "yoke-derive", 1044 + "zerofrom", 1045 + ] 1046 + 1047 + [[package]] 1048 + name = "yoke-derive" 1049 + version = "0.8.2" 1050 + source = "registry+https://github.com/rust-lang/crates.io-index" 1051 + checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" 1052 + dependencies = [ 1053 + "proc-macro2", 1054 + "quote", 1055 + "syn", 1056 + "synstructure", 1057 + ] 1058 + 1059 + [[package]] 1060 + name = "zerofrom" 1061 + version = "0.1.7" 1062 + source = "registry+https://github.com/rust-lang/crates.io-index" 1063 + checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" 1064 + dependencies = [ 1065 + "zerofrom-derive", 1066 + ] 1067 + 1068 + [[package]] 1069 + name = "zerofrom-derive" 1070 + version = "0.1.7" 1071 + source = "registry+https://github.com/rust-lang/crates.io-index" 1072 + checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" 1073 + dependencies = [ 1074 + "proc-macro2", 1075 + "quote", 1076 + "syn", 1077 + "synstructure", 1078 + ] 1079 + 1080 + [[package]] 1081 + name = "zerotrie" 1082 + version = "0.2.4" 1083 + source = "registry+https://github.com/rust-lang/crates.io-index" 1084 + checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" 1085 + dependencies = [ 1086 + "displaydoc", 1087 + "yoke", 1088 + "zerofrom", 1089 + ] 1090 + 1091 + [[package]] 1092 + name = "zerovec" 1093 + version = "0.11.6" 1094 + source = "registry+https://github.com/rust-lang/crates.io-index" 1095 + checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" 1096 + dependencies = [ 1097 + "yoke", 1098 + "zerofrom", 1099 + "zerovec-derive", 1100 + ] 1101 + 1102 + [[package]] 1103 + name = "zerovec-derive" 1104 + version = "0.11.3" 1105 + source = "registry+https://github.com/rust-lang/crates.io-index" 1106 + checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" 1107 + dependencies = [ 1108 + "proc-macro2", 1109 + "quote", 1110 + "syn", 1111 + ]
+17
fuzz/Cargo.toml
··· 1 + [package] 2 + name = "eat-rocks-fuzz" 3 + version = "0.0.0" 4 + publish = false 5 + edition = "2024" 6 + 7 + [package.metadata] 8 + cargo-fuzz = true 9 + 10 + [dependencies] 11 + libfuzzer-sys = "0.4" 12 + eat-rocks = { path = "..", default-features = false } 13 + 14 + [[bin]] 15 + name = "parse_meta" 16 + path = "fuzz_targets/parse_meta.rs" 17 + doc = false
+8
fuzz/fuzz_targets/parse_meta.rs
··· 1 + #![no_main] 2 + 3 + use libfuzzer_sys::fuzz_target; 4 + 5 + fuzz_target!(|data: &str| { 6 + // parser must never panic (ok or err both fine) 7 + let _ = eat_rocks::BackupMeta::parse(data); 8 + });
+99
hacking.md
··· 1 + implementation, format stuff, etc. 2 + 3 + in general when there's a question of how to handle something: try to match the rocksdb behaviour. 4 + 5 + 6 + ## backup files 7 + 8 + see the rocksdb [wiki](https://github.com/facebook/rocksdb/wiki/How-to-backup-RocksDB) and the authoritative rocks backup implementation: [`utilities/backup/backup_engine.cc`](https://github.com/facebook/rocksdb/blob/main/utilities/backup/backup_engine.cc). 9 + 10 + The format is line-oriented text. yay. 11 + 12 + The incremental backup files are stored like this: 13 + 14 + ``` 15 + meta/ 16 + 1 # meta file for backup 1 17 + 2 # meta file for backup 2 18 + private/ 19 + 1/ # per-backup files (CURRENT, MANIFEST, OPTIONS, WALs) 20 + 2/ 21 + shared_checksum/ 22 + 000007_2894567812_590.sst # SSTs shared across backups, funny names 23 + ``` 24 + 25 + The `meta` files contain everything you need to restore one backup: namely, a list of files to copy. 26 + It's all not too complicated. 27 + 28 + Schema v2 added a `schema_version 2.N` on the first line but is otherwise backwards compatible with v1 (which starts with the timestamp line), so both are supported: 29 + 30 + ``` 31 + [schema_version 2.n] # absent for v1 32 + <timestamp> 33 + <sequence_number> 34 + [metadata <hex>] # optional app metadata 35 + [<field> <value>]* # unknown fields, skippable unless ni:: prefixed 36 + <file_count> 37 + <path> [field value]* # many lines like this (<file_count> of them) 38 + ``` 39 + 40 + File fields include `crc32` (actually crc32c), `size`, `temp`, and `ni::excluded`. 41 + Fields starting with `ni::` (non-ignorable) are meant to fail to parse unless they are specifically recognized by the parser. yay forward compat. 42 + 43 + To avoid file name collisions, rocks puts `_<checksum>_<size>` suffixes on files in the `shared_checksum/` folder. 44 + These are just for uniqueness. 45 + During restore you pop them out and just write to `<name before underscore>.<ext>`, without any interpretation. 46 + 47 + 48 + ### exclusion zone 49 + 50 + rocks has fancy weird multi-backup advanced stuff where you can reference files living in one backup from another, to avoid redundant copies, and these get marked `excluded`. 51 + 52 + these are outside the scope of `eat-rocks` and we error if any files in the meta are `excluded`, since we wouldn't know where to look for them. 53 + 54 + 55 + ### get `CURRENT` 56 + 57 + rocks itself uses a file called `CURRENT` as its entrypoint to the db. 58 + when restoring, we write all other files first, then atomically rename the new `CURRENT` into place, so a partial restore won't corrupt things. (just following rocks here) 59 + 60 + 61 + ## with integrity 62 + 63 + rocksdb (accidentally?) doesn't emit a `size` value in the meta for files, for... some reason. 64 + so despite implementing an unconditional validation check for it, it's really not actually checked. 65 + 66 + we do reliably get the crc32c from rocks though, and `eat-rocks` will check it by default. 67 + passing `--no-verify` (or setting `RestoreOptions::verify` to false) disables the check. 68 + i'm not sure why you'd want to though -- unlike a restore from the local filesystem, downloading from object storage means the full contents get streamed through us, so checking the crc is basically free. 69 + 70 + The meta file field is called `crc32` but the values are actually crc32c. 71 + grep for "WART" in rocksdb source about it. `crc32c` with `crc32c_append` for streaming works for us. 72 + 73 + 74 + ## all together (concurrency) 75 + 76 + the concurrency limit is applied in two places -- idk if this was a great idea but hey. 77 + `futures::stream::buffer_unordered` limits how many files we're asking `object_store` to work on at any given time, and `object_store::limit::LimitStore` wraps the actual `ObjectStore` backend to apply the same limit at a lower level. 78 + 79 + 80 + ## plz don't ignore 81 + 82 + non-ignorable fields (`ni::` prefix) cause hard failures in both header and file parsing if they're not recognized. 83 + Unknown ignorable fields are silently skipped. 84 + `ni::excluded` is the only recognized `ni::` field currently. 85 + 86 + 87 + ## don't test me 88 + 89 + unit tests go in the modules of things they test (normal rust) 90 + 91 + `object_store::memory::InMemory` is great for stubbing out object storage space with arbitrary contents. 92 + 93 + there are some neat end-to-end tests in `tests/e2e.rs` which hopefully validate the whole thing going on here, down to actually generating real backups from rocksdb (via the `rocksdb` rust crate) and restoring them with both our implementation and rocks'. 94 + 95 + the meta file parser is pretty simple/small but hey why not fuzz it -- try: 96 + 97 + ```bash 98 + rustup run nightly cargo fuzz run parse_meta 99 + ```
+80
reame.md
··· 1 + # eat-rocks 2 + 3 + [![docs.rs](https://docs.rs/eat-rocks/badge.svg)](https://docs.rs/eat-rocks) 4 + 5 + restore a rocks backup from s3-compatible object storage 6 + 7 + rocks has built-in backup/restore, but its restore function expects a local filesystem. 8 + bridging object storage to a filesystem *works*, but it's really annoying. 9 + 10 + eat-rocks talks to object storage directly (and with high default concurrency) so it can be pretty fast at getting your database back. 11 + 12 + 13 + ## cli 14 + 15 + ```bash 16 + # restore latest from a public bucket (subdomain style) 17 + eat-rocks --endpoint https://constellation.t3.storage.dev restore /data/rocksdb 18 + 19 + # list available backups 20 + eat-rocks --endpoint https://constellation.t3.storage.dev list 21 + 22 + # restore a specific backup 23 + eat-rocks --endpoint https://constellation.t3.storage.dev restore \ 24 + --backup-id 3 /data/rocksdb 25 + 26 + # authenticated access (or set AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY) 27 + eat-rocks \ 28 + --endpoint https://constellation.t3.storage.dev \ 29 + --access-key-id AKIA... \ 30 + --secret-access-key wJal... \ 31 + restore /data/rocksdb 32 + 33 + # path-style (minio, localstack) 34 + eat-rocks \ 35 + --endpoint http://localhost:9000 \ 36 + --bucket mybucket \ 37 + restore /data/rocksdb 38 + 39 + # limit concurrency (poor connection, etc) 40 + eat-rocks --endpoint https://constellation.t3.storage.dev \ 41 + restore --concurrency 8 /data/rocksdb 42 + ``` 43 + 44 + ## lib 45 + 46 + ```rust 47 + use eat_rocks::{public_bucket, restore}; 48 + 49 + let store = public_bucket("https://constellation.t3.storage.dev")?; 50 + restore(store, "", "/data/rocksdb".as_ref(), Default::default()).await?; 51 + ``` 52 + 53 + or bring your own [`ObjectStore`](https://docs.rs/object_store/latest/object_store/) implementation (S3, GCS, Azure, local filesystem, ...): 54 + 55 + ```rust 56 + let store: Arc<dyn ObjectStore> = /* store: all you */; 57 + eat_rocks::restore(store, "", "/data/rocksdb".as_ref(), Default::default()).await?; 58 + ``` 59 + 60 + 61 + ## featureful 62 + 63 + - `cli`: enable deps to build the binary 64 + - `easy` (default): `public_bucket()` convenience function with `aws` store backend 65 + 66 + 67 + ### cli build 68 + 69 + the `cli` feature flag is required to build the cli 70 + 71 + ```bash 72 + cargo build --release --features cli 73 + ``` 74 + 75 + 76 + ## license 77 + 78 + Dual-licensed under MIT and Apache 2.0. 79 + 80 + `SPDX-License-Identifier: MIT OR Apache-2.0`
+138
src/lib.rs
··· 1 + //! Restore a [rocks backup](https://github.com/facebook/rocksdb/wiki/How-to-backup-RocksDB) 2 + //! from object storage 3 + //! 4 + //! ```rust,no_run 5 + //! use eat_rocks::{public_bucket, restore}; 6 + //! # async fn example() -> Result<(), eat_rocks::Error> { 7 + //! let store = public_bucket("https://constellation.t3.storage.dev")?; 8 + //! restore(store, "", "/tmp/constellation-db", Default::default()).await?; 9 + //! # Ok(()) 10 + //! # } 11 + //! ``` 12 + //! 13 + //! the `public_bucket` function (`easy` feature, enabled by default) works with 14 + //! s3-compatible stores (like tigris) which are configured for public read access. 15 + //! 16 + //! you can use any [`ObjectStore`](object_store::ObjectStore) to restore from pretty much any backend (S3, GCS, Azure, local filesystem, ...) 17 + 18 + pub mod meta; 19 + pub mod restore; 20 + 21 + pub use meta::{BackupFile, BackupMeta}; 22 + pub use restore::{DEFAULT_CONCURRENCY, RestoreOptions, fetch_meta, list_backup_ids, restore}; 23 + 24 + use std::io; 25 + use std::path::PathBuf; 26 + use std::string::FromUtf8Error; 27 + 28 + use object_store::path::Path as StorePath; 29 + 30 + /// Build an unsigned s3-compatible object store 31 + /// 32 + /// `endpoint` should include the bucket as a subdomain for virtual-hosted style: 33 + /// 34 + /// ```text 35 + /// https://constellation.t3.storage.dev 36 + /// ``` 37 + /// 38 + /// you can construct your own [`object_store::ObjectStore`] implementation directly for more control. 39 + #[cfg(feature = "easy")] 40 + pub fn public_bucket( 41 + endpoint: &str, 42 + ) -> Result<std::sync::Arc<dyn object_store::ObjectStore>, Error> { 43 + use object_store::aws::AmazonS3Builder; 44 + use std::sync::Arc; 45 + 46 + let store = AmazonS3Builder::new() 47 + .with_endpoint(endpoint) 48 + .with_bucket_name("_") 49 + .with_skip_signature(true) 50 + .with_allow_http(true) 51 + .with_virtual_hosted_style_request(true) 52 + .build() 53 + .map_err(|e| Error::ObjectStoreBuild { 54 + endpoint: endpoint.to_string(), 55 + source: e, 56 + })?; 57 + 58 + Ok(Arc::new(store)) 59 + } 60 + 61 + /// Errors that can occur during backup discovery, metadata parsing, or restore. 62 + #[derive(Debug, thiserror::Error)] 63 + pub enum Error { 64 + #[cfg(feature = "easy")] 65 + #[error("failed to build object store")] 66 + ObjectStoreBuild { 67 + endpoint: String, 68 + #[source] 69 + source: object_store::Error, 70 + }, 71 + 72 + #[error("failed to list objects under {prefix:?}")] 73 + List { 74 + prefix: StorePath, 75 + #[source] 76 + source: object_store::Error, 77 + }, 78 + 79 + #[error("failed to fetch {key:?}")] 80 + Fetch { 81 + key: StorePath, 82 + #[source] 83 + source: object_store::Error, 84 + }, 85 + 86 + #[error("meta file for backup {backup_id} is not valid UTF-8")] 87 + MetaEncoding { 88 + backup_id: u64, 89 + #[source] 90 + source: FromUtf8Error, 91 + }, 92 + 93 + #[error("failed to parse meta file for backup {backup_id}")] 94 + MetaParse { 95 + backup_id: u64, 96 + #[source] 97 + source: meta::ParseError, 98 + }, 99 + 100 + #[error("no backups found")] 101 + NoBackups, 102 + 103 + #[error("backup contains {count} excluded file(s) (unsupported)")] 104 + ExcludedFiles { count: usize }, 105 + 106 + #[error("backup path has unrecognized prefix: {0:?}")] 107 + UnrecognizedPathPrefix(String), 108 + 109 + #[error("shared_checksum filename missing underscore: {0:?}")] 110 + SharedChecksumNoUnderscore(String), 111 + 112 + #[error("shared_checksum filename missing extension: {0:?}")] 113 + SharedChecksumNoExtension(String), 114 + 115 + #[error("private/ path has too few components: {0:?}")] 116 + PrivatePathTooShort(String), 117 + 118 + #[error("size mismatch on {path}: expected {expected} bytes, got {actual}")] 119 + SizeMismatch { 120 + path: String, 121 + expected: u64, 122 + actual: u64, 123 + }, 124 + 125 + #[error("crc32c mismatch on {path}: expected {expected:#010x}, got {actual:#010x}")] 126 + ChecksumMismatch { 127 + path: String, 128 + expected: u32, 129 + actual: u32, 130 + }, 131 + 132 + #[error("{}: {source}", path.display())] 133 + Io { 134 + path: PathBuf, 135 + #[source] 136 + source: io::Error, 137 + }, 138 + }
+220
src/main.rs
··· 1 + use std::path::PathBuf; 2 + use std::sync::Arc; 3 + 4 + use clap::{Parser, Subcommand}; 5 + use object_store::{ObjectStore, aws::AmazonS3Builder}; 6 + use tracing::warn; 7 + 8 + #[derive(Debug, thiserror::Error)] 9 + enum CliError { 10 + #[error("failed to initialize S3 store for {endpoint:?}")] 11 + StoreInit { 12 + endpoint: String, 13 + #[source] 14 + source: object_store::Error, 15 + }, 16 + 17 + #[error("failed to list backups at prefix {prefix:?}")] 18 + List { 19 + prefix: String, 20 + #[source] 21 + source: eat_rocks::Error, 22 + }, 23 + 24 + #[error("restore to {} failed", target.display())] 25 + Restore { 26 + target: PathBuf, 27 + #[source] 28 + source: eat_rocks::Error, 29 + }, 30 + } 31 + 32 + #[derive(Parser)] 33 + #[command(version, about)] 34 + struct Cli { 35 + /// s3-compatible endpoint url 36 + /// 37 + /// for subdomain style (aka virtual-hosted) like tigris, include the bucket 38 + /// in the hostname, like `--endpoint https://constellation.t3.storage.dev`. 39 + #[arg(long)] 40 + endpoint: String, 41 + 42 + /// bucket name 43 + /// 44 + /// only for path-style buckets (minio, localstack,). see `--endpoint`. 45 + #[arg(long)] 46 + bucket: Option<String>, 47 + 48 + /// Prefix within the bucket (path to backup root) 49 + #[arg(long, default_value = "")] 50 + prefix: String, 51 + 52 + /// access key ID, omit for public access 53 + #[arg(long, env = "AWS_ACCESS_KEY_ID", requires = "secret_access_key")] 54 + access_key_id: Option<String>, 55 + 56 + /// secret access key, omit for public access 57 + #[arg(long, env = "AWS_SECRET_ACCESS_KEY", requires = "access_key_id")] 58 + secret_access_key: Option<String>, 59 + 60 + #[command(subcommand)] 61 + command: Command, 62 + } 63 + 64 + #[derive(Subcommand)] 65 + enum Command { 66 + /// show available backups 67 + List, 68 + /// restore a backup to a local directory 69 + Restore { 70 + /// backup to restore 71 + /// 72 + /// default: latest. use `list` to see available backups. 73 + #[arg(long)] 74 + backup_id: Option<u64>, 75 + 76 + /// WAL directory (default: same as target) 77 + #[arg(long)] 78 + wal_dir: Option<PathBuf>, 79 + 80 + /// max concurrent actions against object storage 81 + #[arg(long, default_value_t = eat_rocks::DEFAULT_CONCURRENCY)] 82 + concurrency: usize, 83 + 84 + /// skip verifying crc32c checksums 85 + /// 86 + /// these are almost free (they're computed as the file is streamed), 87 + /// and the restore should typically be i/o-bound, so i'm not sure when/ 88 + /// why turning this off would be useful. 89 + #[arg(long)] 90 + no_verify: bool, 91 + 92 + /// target directory for restored database 93 + target: PathBuf, 94 + }, 95 + } 96 + 97 + impl Cli { 98 + fn build_store(&self) -> Result<Arc<dyn ObjectStore>, Box<CliError>> { 99 + // if `--bucket` is passed, then we get path-style buckets in the URL. 100 + // if not, the caller is responsible for putting the bucket in the endpoint 101 + // url, but we still need to set it, hence the `_` placeholder. 102 + let bucket = self.bucket.as_deref().unwrap_or("_"); 103 + 104 + let mut builder = AmazonS3Builder::new() 105 + .with_endpoint(&self.endpoint) 106 + .with_bucket_name(bucket) 107 + .with_allow_http(true) 108 + .with_virtual_hosted_style_request(self.bucket.is_none()); 109 + 110 + builder = match (&self.access_key_id, &self.secret_access_key) { 111 + (Some(key_id), Some(secret)) => builder 112 + .with_access_key_id(key_id) 113 + .with_secret_access_key(secret), 114 + (None, None) => builder.with_skip_signature(true), 115 + _ => unreachable!("clap `requires` ensures both or neither are present"), 116 + }; 117 + 118 + let store = builder.build().map_err(|source| CliError::StoreInit { 119 + endpoint: self.endpoint.clone(), 120 + source, 121 + })?; 122 + Ok(Arc::new(store)) 123 + } 124 + } 125 + 126 + // --------------------------------------------------------------------------- 127 + 128 + #[tokio::main] 129 + async fn main() { 130 + tracing_subscriber::fmt() 131 + .with_env_filter( 132 + tracing_subscriber::EnvFilter::try_from_default_env() 133 + .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")), 134 + ) 135 + .with_target(false) 136 + .init(); 137 + 138 + let cli = Cli::parse(); 139 + let result = match &cli.command { 140 + Command::List => list(&cli).await, 141 + Command::Restore { .. } => cmd_restore(&cli).await, 142 + }; 143 + 144 + if let Err(e) = result { 145 + eprintln!("error: {e}"); 146 + let mut err: &dyn std::error::Error = &e; 147 + while let Some(source) = err.source() { 148 + eprintln!(" caused by: {source}"); 149 + err = source; 150 + } 151 + std::process::exit(1); 152 + } 153 + } 154 + 155 + async fn list(cli: &Cli) -> Result<(), Box<CliError>> { 156 + let store = cli.build_store()?; 157 + let prefix = &cli.prefix; 158 + 159 + let ids = eat_rocks::list_backup_ids(&store, prefix) 160 + .await 161 + .map_err(|source| CliError::List { 162 + prefix: prefix.clone(), 163 + source, 164 + })?; 165 + 166 + if ids.is_empty() { 167 + warn!("no backups found"); 168 + return Ok(()); 169 + } 170 + 171 + for id in &ids { 172 + match eat_rocks::fetch_meta(&store, prefix, *id).await { 173 + Ok(meta) => { 174 + println!( 175 + "backup {id:>4} | seq {:>12} | ts {} | {} files", 176 + meta.sequence_number, 177 + meta.timestamp, 178 + meta.files.len(), 179 + ); 180 + } 181 + Err(e) => warn!(backup_id = id, error = %e, "failed to read backup metadata"), 182 + } 183 + } 184 + 185 + Ok(()) 186 + } 187 + 188 + async fn cmd_restore(cli: &Cli) -> Result<(), Box<CliError>> { 189 + let Command::Restore { 190 + backup_id, 191 + target, 192 + wal_dir, 193 + concurrency, 194 + no_verify, 195 + } = &cli.command 196 + else { 197 + unreachable!() 198 + }; 199 + 200 + let store = cli.build_store()?; 201 + 202 + eat_rocks::restore( 203 + store, 204 + &cli.prefix, 205 + target, 206 + eat_rocks::RestoreOptions { 207 + backup_id: *backup_id, 208 + concurrency: *concurrency, 209 + verify: !no_verify, 210 + wal_dir: wal_dir.clone(), 211 + }, 212 + ) 213 + .await 214 + .map_err(|source| CliError::Restore { 215 + target: target.clone(), 216 + source, 217 + })?; 218 + 219 + Ok(()) 220 + }
+373
src/meta.rs
··· 1 + /// rocks backup meta file parse errors 2 + #[derive(Debug, PartialEq, thiserror::Error)] 3 + pub enum ParseError { 4 + #[error("empty meta file")] 5 + Empty, 6 + #[error("could not parse schema version from {0:?} (v1 and v2 supported)")] 7 + InvalidSchemaVersion(String), 8 + #[error("missing timestamp line")] 9 + MissingTimestamp, 10 + #[error("invalid timestamp: {0:?}")] 11 + InvalidTimestamp(String), 12 + #[error("missing sequence number line")] 13 + MissingSequenceNumber, 14 + #[error("invalid sequence number: {0:?}")] 15 + InvalidSequenceNumber(String), 16 + #[error("unexpected end of meta file while looking for file count")] 17 + UnexpectedEndBeforeFileCount, 18 + #[error("invalid file count: {0:?}")] 19 + InvalidFileCount(String), 20 + #[error("expected {expected} file entries but file ended after {actual}")] 21 + FileCountMismatch { expected: usize, actual: usize }, 22 + #[error("empty file entry at position {0}")] 23 + EmptyFileEntry(usize), 24 + #[error("field {0:?} missing its value")] 25 + MissingFieldValue(String), 26 + #[error("invalid crc32 value: {0:?}")] 27 + InvalidCrc32(String), 28 + #[error("invalid size value: {0:?}")] 29 + InvalidSize(String), 30 + #[error("unrecognized non-ignorable field: {0:?}")] 31 + NonIgnorableField(String), 32 + #[error("unrecognized value {value:?} for field {field:?}")] 33 + UnrecognizedFieldValue { field: String, value: String }, 34 + } 35 + 36 + /// Parsed contents of a rocks backup meta file (`meta/<id>`). 37 + /// 38 + /// See [`BackupMeta::parse`] and the 39 + /// [backup format docs](https://github.com/facebook/rocksdb/wiki/How-to-backup-RocksDB). 40 + #[derive(Debug, PartialEq)] 41 + pub struct BackupMeta { 42 + pub timestamp: u64, 43 + pub sequence_number: u64, 44 + pub metadata: Option<String>, 45 + pub files: Vec<BackupFile>, 46 + } 47 + 48 + /// Each file entry in a rocks backup meta file 49 + #[derive(Debug, PartialEq)] 50 + pub struct BackupFile { 51 + /// path is relative to backup root, eg. `shared_checksum/000007_123_456.sst` 52 + pub path: String, 53 + pub crc32c: Option<u32>, 54 + pub size: Option<u64>, 55 + pub excluded: bool, 56 + } 57 + 58 + impl BackupMeta { 59 + /// Parse a RocksDB backup meta file. 60 + /// 61 + /// works with schema v1 (implicit) and v2. 62 + pub fn parse(content: &str) -> Result<Self, ParseError> { 63 + let mut lines = content.lines(); 64 + 65 + let first = lines.next().ok_or(ParseError::Empty)?; 66 + 67 + // schema v1: first line is the timestamp. 68 + // schema v2 starts with "schema_version 2.x". 69 + let ts_line = if let Some(ver_str) = first.strip_prefix("schema_version ") { 70 + let Some("2") = ver_str.split('.').next() else { 71 + return Err(ParseError::InvalidSchemaVersion(ver_str.to_string())); 72 + }; 73 + lines.next().ok_or(ParseError::MissingTimestamp)? 74 + } else { 75 + first 76 + }; 77 + 78 + let timestamp: u64 = ts_line 79 + .parse() 80 + .map_err(|_| ParseError::InvalidTimestamp(ts_line.to_string()))?; 81 + 82 + let seq_line = lines.next().ok_or(ParseError::MissingSequenceNumber)?; 83 + let sequence_number: u64 = seq_line 84 + .parse() 85 + .map_err(|_| ParseError::InvalidSequenceNumber(seq_line.to_string()))?; 86 + 87 + // optional metadata lines before file count (first purely numeric line) 88 + let mut metadata = None; 89 + let num_files: usize = loop { 90 + let line = lines 91 + .next() 92 + .ok_or(ParseError::UnexpectedEndBeforeFileCount)?; 93 + if let Ok(n) = line.parse::<usize>() { 94 + break n; 95 + } 96 + if let Some(hex) = line.strip_prefix("metadata ") { 97 + metadata = Some(hex.to_string()); 98 + } else if line.starts_with("ni::") { 99 + let field = line.split_whitespace().next().unwrap_or(line); 100 + return Err(ParseError::NonIgnorableField(field.to_string())); 101 + } 102 + // safe to skip unknown ignorable (non-"ni::") fields 103 + }; 104 + 105 + // file entries, finally 106 + let mut files = Vec::new(); 107 + for i in 0..num_files { 108 + let line = lines.next().ok_or(ParseError::FileCountMismatch { 109 + expected: num_files, 110 + actual: i, // 0-indexed so it's last round's count (what we actually did) 111 + })?; 112 + files.push(parse_file_line(line, i)?); 113 + } 114 + 115 + Ok(BackupMeta { 116 + timestamp, 117 + sequence_number, 118 + metadata, 119 + files, 120 + }) 121 + } 122 + } 123 + 124 + fn parse_file_line(line: &str, position: usize) -> Result<BackupFile, ParseError> { 125 + let mut parts = line.split_whitespace(); 126 + let path = parts 127 + .next() 128 + .ok_or(ParseError::EmptyFileEntry(position))? 129 + .to_string(); 130 + 131 + let mut crc32c = None; 132 + let mut size = None; 133 + let mut excluded = false; 134 + 135 + while let Some(field_name) = parts.next() { 136 + let field_value = parts 137 + .next() 138 + .ok_or_else(|| ParseError::MissingFieldValue(field_name.to_string()))?; 139 + 140 + match field_name { 141 + "crc32" => { 142 + crc32c = Some( 143 + field_value 144 + .parse() 145 + .map_err(|_| ParseError::InvalidCrc32(field_value.to_string()))?, 146 + ); 147 + } 148 + "size" => { 149 + size = Some( 150 + field_value 151 + .parse() 152 + .map_err(|_| ParseError::InvalidSize(field_value.to_string()))?, 153 + ); 154 + } 155 + "ni::excluded" => match field_value { 156 + "true" => excluded = true, 157 + "false" => excluded = false, 158 + _ => { 159 + return Err(ParseError::UnrecognizedFieldValue { 160 + field: field_name.to_string(), 161 + value: field_value.to_string(), 162 + }); 163 + } 164 + }, 165 + "temp" => {} 166 + other if other.starts_with("ni::") => { 167 + return Err(ParseError::NonIgnorableField(other.to_string())); 168 + } 169 + _ => {} // unknown non-"ni" fields are safe to ignore 170 + } 171 + } 172 + 173 + Ok(BackupFile { 174 + path, 175 + crc32c, 176 + size, 177 + excluded, 178 + }) 179 + } 180 + 181 + #[cfg(test)] 182 + mod tests { 183 + use super::*; 184 + 185 + #[test] 186 + fn parse_schema_v1() { 187 + let content = "\ 188 + 1498774076 189 + 590 190 + 3 191 + private/1/CURRENT crc32 123456 192 + private/1/MANIFEST-000008 crc32 789012 193 + shared_checksum/000007_1498774076_590.sst crc32 345678"; 194 + 195 + let meta = BackupMeta::parse(content).unwrap(); 196 + assert_eq!(meta.timestamp, 1498774076); 197 + assert_eq!(meta.sequence_number, 590); 198 + assert_eq!(meta.files.len(), 3); 199 + assert_eq!(meta.files[0].path, "private/1/CURRENT"); 200 + assert_eq!(meta.files[0].crc32c, Some(123456)); 201 + } 202 + 203 + #[test] 204 + fn parse_schema_v2() { 205 + let content = "\ 206 + schema_version 2.1 207 + 1498774076 208 + 590 209 + metadata 48656c6c6f 210 + 3 211 + private/1/CURRENT crc32 123456 size 16 212 + private/1/MANIFEST-000008 crc32 789012 size 1024 213 + shared_checksum/000007_1498774076_590.sst crc32 345678 size 65536 temp kCold"; 214 + 215 + let meta = BackupMeta::parse(content).unwrap(); 216 + assert_eq!(meta.timestamp, 1498774076); 217 + assert_eq!(meta.sequence_number, 590); 218 + assert_eq!(meta.metadata.as_deref(), Some("48656c6c6f")); 219 + assert_eq!(meta.files.len(), 3); 220 + assert_eq!(meta.files[2].size, Some(65536)); 221 + } 222 + 223 + #[test] 224 + fn parse_empty() { 225 + assert!(matches!(BackupMeta::parse(""), Err(ParseError::Empty))); 226 + } 227 + 228 + #[test] 229 + fn parse_unsupported_schema() { 230 + let content = "schema_version 3.0\n0\n0\n0\n"; 231 + assert_eq!( 232 + BackupMeta::parse(content), 233 + Err(ParseError::InvalidSchemaVersion("3.0".to_string())) 234 + ); 235 + } 236 + 237 + #[test] 238 + fn non_ignorable_field_rejected() { 239 + let content = "\ 240 + 1498774076 241 + 590 242 + 1 243 + private/1/CURRENT ni::unknown_field true"; 244 + 245 + assert!(matches!( 246 + BackupMeta::parse(content), 247 + Err(ParseError::NonIgnorableField(_)) 248 + )); 249 + } 250 + 251 + #[test] 252 + fn bogus_file_count_does_not_allocate() { 253 + // Regression: "2\n2\n64406400" claimed 64M files with 0 lines remaining, 254 + // causing a multi-GB Vec::with_capacity before the loop could fail. 255 + let content = "2\n2\n64406400"; 256 + assert!(matches!( 257 + BackupMeta::parse(content), 258 + Err(ParseError::FileCountMismatch { 259 + expected: 64406400, 260 + actual: 0 261 + }) 262 + )); 263 + } 264 + 265 + #[test] 266 + fn explicit_schema_v1_rejected() { 267 + let content = "schema_version 1.0\n1000\n100\n0\n"; 268 + assert_eq!( 269 + BackupMeta::parse(content), 270 + Err(ParseError::InvalidSchemaVersion("1.0".to_string())) 271 + ); 272 + } 273 + 274 + #[test] 275 + fn non_ignorable_header_field_rejected() { 276 + let content = "\ 277 + schema_version 2.1 278 + 1498774076 279 + 590 280 + ni::future_breaking_field something 281 + 0"; 282 + assert!(matches!( 283 + BackupMeta::parse(content), 284 + Err(ParseError::NonIgnorableField(_)) 285 + )); 286 + } 287 + 288 + #[test] 289 + fn unknown_ignorable_header_field_skipped() { 290 + let content = "\ 291 + schema_version 2.1 292 + 1498774076 293 + 590 294 + some_future_field data 295 + 0"; 296 + let meta = BackupMeta::parse(content).unwrap(); 297 + assert_eq!(meta.timestamp, 1498774076); 298 + assert_eq!(meta.files.len(), 0); 299 + } 300 + 301 + #[test] 302 + fn duplicate_metadata_uses_last() { 303 + let content = "\ 304 + schema_version 2.1 305 + 1498774076 306 + 590 307 + metadata aaa 308 + metadata bbb 309 + 0"; 310 + let meta = BackupMeta::parse(content).unwrap(); 311 + assert_eq!(meta.metadata.as_deref(), Some("bbb")); 312 + } 313 + 314 + #[test] 315 + fn file_fields_in_any_order() { 316 + let content = "\ 317 + 1498774076 318 + 590 319 + 1 320 + private/1/CURRENT size 16 crc32 123456 temp kHot"; 321 + let meta = BackupMeta::parse(content).unwrap(); 322 + assert_eq!(meta.files[0].crc32c, Some(123456)); 323 + assert_eq!(meta.files[0].size, Some(16)); 324 + } 325 + 326 + #[test] 327 + fn duplicate_file_fields_use_last() { 328 + let content = "\ 329 + 1498774076 330 + 590 331 + 1 332 + private/1/CURRENT crc32 111 crc32 222"; 333 + let meta = BackupMeta::parse(content).unwrap(); 334 + assert_eq!(meta.files[0].crc32c, Some(222)); 335 + } 336 + 337 + #[test] 338 + fn unknown_ignorable_file_field_skipped() { 339 + let content = "\ 340 + 1498774076 341 + 590 342 + 1 343 + private/1/CURRENT crc32 123 future_field value123"; 344 + let meta = BackupMeta::parse(content).unwrap(); 345 + assert_eq!(meta.files[0].crc32c, Some(123)); 346 + } 347 + 348 + #[test] 349 + fn excluded_bad_value_rejected() { 350 + let content = "\ 351 + 1498774076 352 + 590 353 + 1 354 + private/1/CURRENT ni::excluded banana"; 355 + assert!(matches!( 356 + BackupMeta::parse(content), 357 + Err(ParseError::UnrecognizedFieldValue { .. }) 358 + )); 359 + } 360 + 361 + #[test] 362 + fn excluded_file_parsed() { 363 + let content = "\ 364 + schema_version 2.1 365 + 1498774076 366 + 590 367 + 1 368 + shared_checksum/000007_123_456.sst crc32 999 ni::excluded true"; 369 + 370 + let meta = BackupMeta::parse(content).unwrap(); 371 + assert!(meta.files[0].excluded); 372 + } 373 + }
+728
src/restore.rs
··· 1 + use std::path::{Path, PathBuf}; 2 + use std::sync::Arc; 3 + use std::time::Instant; 4 + 5 + use futures::StreamExt; 6 + use object_store::limit::LimitStore; 7 + use object_store::path::Path as StorePath; 8 + use object_store::{ObjectStore, ObjectStoreExt}; 9 + use tokio::io::{AsyncWriteExt, BufWriter}; 10 + use tracing::{debug, info}; 11 + 12 + use crate::{BackupFile, Error, meta::BackupMeta}; 13 + 14 + /// List available backups from the `meta/` directory under `prefix` 15 + /// 16 + /// returns a sorted vec of backups ids. 17 + /// 18 + /// in-progress backups (`.tmp` files) are excluded. 19 + pub async fn list_backup_ids(store: &dyn ObjectStore, prefix: &str) -> Result<Vec<u64>, Error> { 20 + let meta_prefix = StorePath::from(prefix).join("meta"); 21 + let mut ids = Vec::new(); 22 + 23 + let mut stream = store.list(Some(&meta_prefix)); 24 + while let Some(item) = stream.next().await { 25 + let item = item.map_err(|e| Error::List { 26 + prefix: meta_prefix.clone(), 27 + source: e, 28 + })?; 29 + if let Some(name) = item.location.filename() 30 + && !name.ends_with(".tmp") 31 + { 32 + if let Ok(id) = name.parse::<u64>() { 33 + ids.push(id); 34 + } else { 35 + debug!(name, "ignoring invalid backup id"); 36 + } 37 + } 38 + } 39 + 40 + ids.sort(); 41 + Ok(ids) 42 + } 43 + 44 + /// default max concurrent object store operations 45 + pub const DEFAULT_CONCURRENCY: usize = 64; 46 + 47 + /// configure how a backup-restore happens 48 + #[derive(Debug)] 49 + pub struct RestoreOptions { 50 + /// backup to restore, `None` for latest 51 + /// 52 + /// default: [`None`] 53 + pub backup_id: Option<u64>, 54 + /// max concurrent object store operations 55 + /// 56 + /// default: [`DEFAULT_CONCURRENCY`] 57 + pub concurrency: usize, 58 + /// set false to disable streaming crc32c checksum verification 59 + /// 60 + /// default: is true 61 + /// 62 + /// file sizes are always verified if present in the meta file (which rocks 63 + /// actually does not include currently), regardless of this setting. 64 + pub verify: bool, 65 + /// specify to place WAL files somewhere non-default 66 + pub wal_dir: Option<PathBuf>, 67 + } 68 + 69 + impl Default for RestoreOptions { 70 + fn default() -> Self { 71 + Self { 72 + backup_id: None, 73 + concurrency: DEFAULT_CONCURRENCY, 74 + verify: true, 75 + wal_dir: None, 76 + } 77 + } 78 + } 79 + 80 + async fn download_file( 81 + store: Arc<dyn ObjectStore>, 82 + store_prefix: StorePath, 83 + file: &BackupFile, 84 + target: PathBuf, 85 + wal_dir: PathBuf, 86 + verify: bool, 87 + ) -> Result<u64, Error> { 88 + let name = db_filename(&file.path)?; 89 + 90 + let mut key = store_prefix; 91 + key.extend(&StorePath::from(file.path.as_str())); 92 + 93 + // write CURRENT to a temp file (we atomically rename once other files are ready) 94 + let dest = if name.as_os_str() == "CURRENT" { 95 + target.join("CURRENT.tmp") 96 + } else if name.extension().is_some_and(|ext| ext == "log") { 97 + wal_dir.join(&name) 98 + } else { 99 + target.join(&name) 100 + }; 101 + 102 + let result = store.get(&key).await.map_err(|e| Error::Fetch { 103 + key: key.clone(), 104 + source: e, 105 + })?; 106 + 107 + let mut stream = result.into_stream(); 108 + 109 + let f = tokio::fs::File::create(&dest) 110 + .await 111 + .map_err(|e| Error::Io { 112 + path: dest.clone(), 113 + source: e, 114 + })?; 115 + let mut out = BufWriter::new(f); 116 + 117 + let mut total_size = 0u64; 118 + let mut crc = 0u32; 119 + let do_crc = verify && file.crc32c.is_some(); 120 + 121 + while let Some(chunk) = stream.next().await { 122 + let chunk = chunk.map_err(|e| Error::Fetch { 123 + key: key.clone(), 124 + source: e, 125 + })?; 126 + total_size += chunk.len() as u64; 127 + if do_crc { 128 + crc = crc32c::crc32c_append(crc, &chunk); 129 + } 130 + out.write_all(&chunk).await.map_err(|e| Error::Io { 131 + path: dest.clone(), 132 + source: e, 133 + })?; 134 + } 135 + 136 + out.shutdown().await.map_err(|e| Error::Io { 137 + path: dest.clone(), 138 + source: e, 139 + })?; 140 + 141 + if let Some(expected) = file.size 142 + && total_size != expected 143 + { 144 + return Err(Error::SizeMismatch { 145 + path: file.path.clone(), 146 + expected, 147 + actual: total_size, 148 + }); 149 + } 150 + 151 + if let Some(expected) = file.crc32c.filter(|_| verify) 152 + && crc != expected 153 + { 154 + return Err(Error::ChecksumMismatch { 155 + path: file.path.clone(), 156 + expected, 157 + actual: crc, 158 + }); 159 + } 160 + 161 + Ok(total_size) 162 + } 163 + 164 + /// Restore a rocks backup to a `target` directory. 165 + /// 166 + /// streams files from the backup's meta file to disk. see [`RestoreOptions`] 167 + /// for the main behaviours. 168 + /// 169 + /// backups with [excluded files](https://github.com/facebook/rocksdb/wiki/How-to-backup-RocksDB) 170 + /// (references to other backup directories) reject with [`Error::ExcludedFiles`]. 171 + pub async fn restore( 172 + store: Arc<dyn ObjectStore>, 173 + prefix: &str, 174 + target: impl AsRef<Path>, 175 + opts: RestoreOptions, 176 + ) -> Result<(), Error> { 177 + let target = target.as_ref(); 178 + let RestoreOptions { 179 + backup_id, 180 + concurrency, 181 + verify, 182 + wal_dir, 183 + } = opts; 184 + 185 + let wal_dir = wal_dir.as_deref().unwrap_or(target); 186 + 187 + // object_store's built-in upstream concurrency limit 188 + let store: Arc<dyn ObjectStore> = Arc::new(LimitStore::new(store, concurrency)); 189 + 190 + // resolve latest backup if unspecified 191 + let id = match backup_id { 192 + Some(id) => id, 193 + None => { 194 + let ids = list_backup_ids(&*store, prefix).await?; 195 + *ids.last().ok_or(Error::NoBackups)? 196 + } 197 + }; 198 + 199 + let meta = fetch_meta(&*store, prefix, id).await?; 200 + let excluded_count = meta.files.iter().filter(|f| f.excluded).count(); 201 + if excluded_count > 0 { 202 + return Err(Error::ExcludedFiles { 203 + count: excluded_count, 204 + }); 205 + } 206 + info!( 207 + backup_id = id, 208 + file_count = meta.files.len(), 209 + sequence_number = meta.sequence_number, 210 + "restoring backup" 211 + ); 212 + 213 + tokio::fs::create_dir_all(target) 214 + .await 215 + .map_err(|e| Error::Io { 216 + path: target.to_path_buf(), 217 + source: e, 218 + })?; 219 + if wal_dir != target { 220 + tokio::fs::create_dir_all(wal_dir) 221 + .await 222 + .map_err(|e| Error::Io { 223 + path: wal_dir.to_path_buf(), 224 + source: e, 225 + })?; 226 + } 227 + 228 + let started = Instant::now(); 229 + let store_prefix = StorePath::from(prefix); 230 + let target = target.to_path_buf(); 231 + let wal_dir = wal_dir.to_path_buf(); 232 + 233 + // build concurrent download tasks 234 + let tasks = meta.files.iter().map(|f| { 235 + download_file( 236 + Arc::clone(&store), 237 + store_prefix.clone(), 238 + f, 239 + target.clone(), 240 + wal_dir.clone(), 241 + verify, 242 + ) 243 + }); 244 + 245 + let total = tasks.len(); 246 + let mut completed = 0usize; 247 + let mut total_bytes = 0u64; 248 + 249 + // buffered_unordered keeps a kind of window over a limited number of polling tasks 250 + // (kind of because "unordered" means slow early tasks don't stop it advancing) 251 + let mut stream = futures::stream::iter(tasks).buffer_unordered(concurrency); 252 + while let Some(result) = stream.next().await { 253 + let bytes = result?; // fail if any download fails 254 + completed += 1; 255 + total_bytes += bytes; 256 + if completed.is_multiple_of(100) || completed == total { 257 + let elapsed_secs = started.elapsed().as_secs_f64(); 258 + let mb = total_bytes as f64 / 2_f64.powf(20.); 259 + let rate_mb_s = if elapsed_secs > 0.0 { 260 + mb / elapsed_secs 261 + } else { 262 + 0.0 263 + }; 264 + info!( 265 + completed, 266 + total, 267 + downloaded_mb = format_args!("{mb:.1}"), 268 + elapsed_secs = format_args!("{elapsed_secs:.1}"), 269 + rate_mb_s = format_args!("{rate_mb_s:.1}"), 270 + "progress" 271 + ); 272 + } 273 + } 274 + 275 + // atomically do the `CURRENT` thing, finally 276 + let current_tmp = target.join("CURRENT.tmp"); 277 + let current_final = target.join("CURRENT"); 278 + tokio::fs::rename(&current_tmp, &current_final) 279 + .await 280 + .map_err(|e| Error::Io { 281 + path: current_final, 282 + source: e, 283 + })?; 284 + 285 + let elapsed_secs = started.elapsed().as_secs_f64(); 286 + let mb = total_bytes as f64 / 2_f64.powf(20.); 287 + let rate_mb_s = if elapsed_secs > 0.0 { 288 + mb / elapsed_secs 289 + } else { 290 + 0.0 291 + }; 292 + info!( 293 + total_files = total, 294 + total_mb = format_args!("{mb:.1}"), 295 + elapsed_secs = format_args!("{elapsed_secs:.1}"), 296 + rate_mb_s = format_args!("{rate_mb_s:.1}"), 297 + "restore complete" 298 + ); 299 + 300 + Ok(()) 301 + } 302 + 303 + /// Fetch and parse the rocks meta file for a backup 304 + pub async fn fetch_meta( 305 + store: &dyn ObjectStore, 306 + prefix: &str, 307 + id: u64, 308 + ) -> Result<BackupMeta, Error> { 309 + let key = StorePath::from(prefix).join("meta").join(id.to_string()); 310 + 311 + let data = store 312 + .get(&key) 313 + .await 314 + .map_err(|e| Error::Fetch { 315 + key: key.clone(), 316 + source: e, 317 + })? 318 + .bytes() 319 + .await 320 + .map_err(|e| Error::Fetch { key, source: e })?; 321 + 322 + let text = String::from_utf8(data.to_vec()).map_err(|e| Error::MetaEncoding { 323 + backup_id: id, 324 + source: e, 325 + })?; 326 + 327 + BackupMeta::parse(&text).map_err(|e| Error::MetaParse { 328 + backup_id: id, 329 + source: e, 330 + }) 331 + } 332 + 333 + /// Convert a backup-relative path to the DB filename it restores as. 334 + /// 335 + /// `shared_checksum/000007_2894567812_590.sst` -> `000007.sst` 336 + /// `private/1/MANIFEST-000008` -> `MANIFEST-000008` 337 + /// `shared/000007.sst` -> `000007.sst` 338 + pub(crate) fn db_filename(backup_path: &str) -> Result<PathBuf, Error> { 339 + let sp = StorePath::from(backup_path); 340 + let parts: Vec<_> = sp.parts().collect(); 341 + 342 + match parts.first().map(|p| p.as_ref()) { 343 + Some("shared_checksum") => { 344 + let filename = parts 345 + .last() 346 + .ok_or_else(|| Error::SharedChecksumNoExtension(backup_path.to_string()))?; 347 + unmangle_shared_checksum(filename.as_ref()) 348 + } 349 + Some("private") => { 350 + // private/<id>/<filename> 351 + parts 352 + .get(2) 353 + .map(|p| PathBuf::from(p.as_ref())) 354 + .ok_or_else(|| Error::PrivatePathTooShort(backup_path.to_string())) 355 + } 356 + Some("shared") => parts 357 + .last() 358 + .map(|p| PathBuf::from(p.as_ref())) 359 + .ok_or_else(|| Error::UnrecognizedPathPrefix(backup_path.to_string())), 360 + _ => Err(Error::UnrecognizedPathPrefix(backup_path.to_string())), 361 + } 362 + } 363 + 364 + /// Unmangle a `shared_checksum` filename back to its original DB name. 365 + /// 366 + /// `000007_2894567812_590.sst` -> `000007.sst` 367 + fn unmangle_shared_checksum(mangled: &str) -> Result<PathBuf, Error> { 368 + let p = Path::new(mangled); 369 + let ext = p 370 + .extension() 371 + .and_then(|e| e.to_str()) 372 + .ok_or_else(|| Error::SharedChecksumNoExtension(mangled.to_string()))?; 373 + let stem = p 374 + .file_stem() 375 + .and_then(|s| s.to_str()) 376 + .ok_or_else(|| Error::SharedChecksumNoExtension(mangled.to_string()))?; 377 + let underscore = stem 378 + .find('_') 379 + .ok_or_else(|| Error::SharedChecksumNoUnderscore(mangled.to_string()))?; 380 + Ok(PathBuf::from(format!("{}.{ext}", &stem[..underscore]))) 381 + } 382 + 383 + #[cfg(test)] 384 + mod tests { 385 + use super::*; 386 + use object_store::{PutPayload, memory::InMemory}; 387 + 388 + async fn put(store: &InMemory, path: &str, data: &[u8]) { 389 + use object_store::ObjectStoreExt; 390 + store 391 + .put( 392 + &StorePath::from(path), 393 + PutPayload::from_iter(data.iter().copied()), 394 + ) 395 + .await 396 + .unwrap(); 397 + } 398 + 399 + fn build_meta(timestamp: u64, seq: u64, files: &[(&str, &[u8])]) -> String { 400 + let mut lines = vec![ 401 + timestamp.to_string(), 402 + seq.to_string(), 403 + files.len().to_string(), 404 + ]; 405 + for (path, data) in files { 406 + let crc = crc32c::crc32c(data); 407 + lines.push(format!("{path} crc32 {crc}")); 408 + } 409 + lines.join("\n") 410 + } 411 + 412 + async fn add_backup( 413 + store: &InMemory, 414 + id: u64, 415 + timestamp: u64, 416 + seq: u64, 417 + files: &[(&str, &[u8])], 418 + ) { 419 + let meta = build_meta(timestamp, seq, files); 420 + put(store, &format!("meta/{id}"), meta.as_bytes()).await; 421 + for (path, data) in files { 422 + put(store, path, data).await; 423 + } 424 + } 425 + 426 + #[tokio::test] 427 + async fn list_discovers_backups() { 428 + let store = InMemory::new(); 429 + add_backup(&store, 1, 1000, 100, &[("private/1/CURRENT", b"M-1\n")]).await; 430 + add_backup(&store, 3, 3000, 300, &[("private/3/CURRENT", b"M-3\n")]).await; 431 + put(&store, "meta/5.tmp", b"in progress").await; 432 + 433 + let ids = list_backup_ids(&store, "").await.unwrap(); 434 + assert_eq!(ids, vec![1, 3]); 435 + } 436 + 437 + #[tokio::test] 438 + async fn list_with_prefix() { 439 + let store = InMemory::new(); 440 + let meta = build_meta(1000, 100, &[("pfx/private/1/CURRENT", b"M\n")]); 441 + put(&store, "pfx/meta/1", meta.as_bytes()).await; 442 + put(&store, "pfx/private/1/CURRENT", b"M\n").await; 443 + 444 + let ids = list_backup_ids(&store, "pfx").await.unwrap(); 445 + assert_eq!(ids, vec![1]); 446 + } 447 + 448 + #[tokio::test] 449 + async fn list_empty() { 450 + let store = InMemory::new(); 451 + let ids = list_backup_ids(&store, "").await.unwrap(); 452 + assert!(ids.is_empty()); 453 + } 454 + 455 + #[tokio::test] 456 + async fn places_files_correctly() { 457 + let store = InMemory::new(); 458 + let current = b"MANIFEST-000008\n"; 459 + let manifest = b"manifest-data-here"; 460 + let options = b"options-data-here"; 461 + let sst = b"sst-file-contents!"; 462 + 463 + add_backup( 464 + &store, 465 + 1, 466 + 1000, 467 + 100, 468 + &[ 469 + ("private/1/CURRENT", current), 470 + ("private/1/MANIFEST-000008", manifest), 471 + ("private/1/OPTIONS-000009", options), 472 + ("shared_checksum/000007_123_456.sst", sst), 473 + ], 474 + ) 475 + .await; 476 + 477 + let target = tempfile::tempdir().unwrap(); 478 + let tp = target.path(); 479 + 480 + restore( 481 + Arc::new(store), 482 + "", 483 + tp, 484 + RestoreOptions { 485 + backup_id: Some(1), 486 + ..Default::default() 487 + }, 488 + ) 489 + .await 490 + .unwrap(); 491 + 492 + assert_eq!(std::fs::read(tp.join("CURRENT")).unwrap(), current); 493 + assert_eq!(std::fs::read(tp.join("MANIFEST-000008")).unwrap(), manifest); 494 + assert_eq!(std::fs::read(tp.join("OPTIONS-000009")).unwrap(), options); 495 + assert_eq!(std::fs::read(tp.join("000007.sst")).unwrap(), sst); 496 + } 497 + 498 + #[tokio::test] 499 + async fn routes_wal_to_wal_dir() { 500 + let store = InMemory::new(); 501 + add_backup( 502 + &store, 503 + 1, 504 + 1000, 505 + 100, 506 + &[ 507 + ("private/1/CURRENT", b"M-1\n"), 508 + ("private/1/000003.log", b"wal-data"), 509 + ], 510 + ) 511 + .await; 512 + 513 + let target = tempfile::tempdir().unwrap(); 514 + let wal = tempfile::tempdir().unwrap(); 515 + 516 + restore( 517 + Arc::new(store), 518 + "", 519 + target.path(), 520 + RestoreOptions { 521 + backup_id: Some(1), 522 + wal_dir: Some(wal.path().to_path_buf()), 523 + ..Default::default() 524 + }, 525 + ) 526 + .await 527 + .unwrap(); 528 + 529 + assert!(wal.path().join("000003.log").exists()); 530 + assert!(!target.path().join("000003.log").exists()); 531 + assert!(target.path().join("CURRENT").exists()); 532 + } 533 + 534 + #[tokio::test] 535 + async fn defaults_to_latest() { 536 + let store = InMemory::new(); 537 + add_backup(&store, 1, 1000, 100, &[("private/1/CURRENT", b"old\n")]).await; 538 + add_backup(&store, 5, 5000, 500, &[("private/5/CURRENT", b"new\n")]).await; 539 + 540 + let target = tempfile::tempdir().unwrap(); 541 + 542 + restore(Arc::new(store), "", target.path(), Default::default()) 543 + .await 544 + .unwrap(); 545 + 546 + assert_eq!( 547 + std::fs::read(target.path().join("CURRENT")).unwrap(), 548 + b"new\n" 549 + ); 550 + } 551 + 552 + #[tokio::test] 553 + async fn rejects_excluded_files() { 554 + let store = InMemory::new(); 555 + let current = b"M-1\n"; 556 + let crc = crc32c::crc32c(current); 557 + let meta = format!( 558 + "schema_version 2.1\n1000\n100\n2\n\ 559 + private/1/CURRENT crc32 {crc}\n\ 560 + shared_checksum/000099_123_456.sst crc32 999 ni::excluded true" 561 + ); 562 + put(&store, "meta/1", meta.as_bytes()).await; 563 + put(&store, "private/1/CURRENT", current).await; 564 + 565 + let target = tempfile::tempdir().unwrap(); 566 + 567 + let result = restore( 568 + Arc::new(store), 569 + "", 570 + target.path(), 571 + RestoreOptions { 572 + backup_id: Some(1), 573 + ..Default::default() 574 + }, 575 + ) 576 + .await; 577 + assert!(matches!(result, Err(Error::ExcludedFiles { count: 1 }))); 578 + } 579 + 580 + #[tokio::test] 581 + async fn verify_passes_with_correct_crc() { 582 + let store = InMemory::new(); 583 + add_backup(&store, 1, 1000, 100, &[("private/1/CURRENT", b"data\n")]).await; 584 + 585 + let target = tempfile::tempdir().unwrap(); 586 + 587 + restore( 588 + Arc::new(store), 589 + "", 590 + target.path(), 591 + RestoreOptions { 592 + backup_id: Some(1), 593 + verify: true, 594 + ..Default::default() 595 + }, 596 + ) 597 + .await 598 + .unwrap(); 599 + } 600 + 601 + #[tokio::test] 602 + async fn size_mismatch_detected() { 603 + let store = InMemory::new(); 604 + let content = b"hello"; 605 + let crc = crc32c::crc32c(content); 606 + // Meta claims size 999 but actual content is 5 bytes 607 + let meta = format!( 608 + "schema_version 2.1\n1000\n100\n1\n\ 609 + private/1/CURRENT crc32 {crc} size 999" 610 + ); 611 + put(&store, "meta/1", meta.as_bytes()).await; 612 + put(&store, "private/1/CURRENT", content).await; 613 + 614 + let target = tempfile::tempdir().unwrap(); 615 + 616 + let result = restore( 617 + Arc::new(store), 618 + "", 619 + target.path(), 620 + RestoreOptions { 621 + backup_id: Some(1), 622 + ..Default::default() 623 + }, 624 + ) 625 + .await; 626 + assert!(matches!( 627 + result, 628 + Err(Error::SizeMismatch { 629 + expected: 999, 630 + actual: 5, 631 + .. 632 + }) 633 + )); 634 + } 635 + 636 + #[tokio::test] 637 + async fn verify_catches_mismatch() { 638 + let store = InMemory::new(); 639 + let meta = build_meta(1000, 100, &[("private/1/CURRENT", b"correct data")]); 640 + put(&store, "meta/1", meta.as_bytes()).await; 641 + put(&store, "private/1/CURRENT", b"tampered data").await; 642 + 643 + let target = tempfile::tempdir().unwrap(); 644 + 645 + let result = restore( 646 + Arc::new(store), 647 + "", 648 + target.path(), 649 + RestoreOptions { 650 + backup_id: Some(1), 651 + verify: true, 652 + ..Default::default() 653 + }, 654 + ) 655 + .await; 656 + assert!(matches!(result, Err(Error::ChecksumMismatch { .. }))); 657 + } 658 + 659 + #[tokio::test] 660 + async fn no_backups() { 661 + let store = InMemory::new(); 662 + let target = tempfile::tempdir().unwrap(); 663 + 664 + let result = restore(Arc::new(store), "", target.path(), Default::default()).await; 665 + assert!(matches!(result, Err(Error::NoBackups))); 666 + } 667 + 668 + #[tokio::test] 669 + async fn fetch_meta_parses() { 670 + let store = InMemory::new(); 671 + add_backup( 672 + &store, 673 + 1, 674 + 1000, 675 + 100, 676 + &[ 677 + ("private/1/CURRENT", b"MANIFEST-1\n"), 678 + ("shared_checksum/000007_123_456.sst", b"sst-data"), 679 + ], 680 + ) 681 + .await; 682 + 683 + let meta = fetch_meta(&store, "", 1).await.unwrap(); 684 + assert_eq!(meta.timestamp, 1000); 685 + assert_eq!(meta.sequence_number, 100); 686 + assert_eq!(meta.files.len(), 2); 687 + } 688 + 689 + #[test] 690 + fn shared_checksum() { 691 + assert_eq!( 692 + db_filename("shared_checksum/000007_2894567812_590.sst").unwrap(), 693 + Path::new("000007.sst") 694 + ); 695 + } 696 + 697 + #[test] 698 + fn private() { 699 + assert_eq!( 700 + db_filename("private/1/MANIFEST-000008").unwrap(), 701 + Path::new("MANIFEST-000008") 702 + ); 703 + } 704 + 705 + #[test] 706 + fn shared() { 707 + assert_eq!( 708 + db_filename("shared/000007.sst").unwrap(), 709 + Path::new("000007.sst") 710 + ); 711 + } 712 + 713 + #[test] 714 + fn unrecognized_prefix() { 715 + assert!(matches!( 716 + db_filename("unknown/file.sst"), 717 + Err(Error::UnrecognizedPathPrefix(_)) 718 + )); 719 + } 720 + 721 + #[test] 722 + fn private_too_short() { 723 + assert!(matches!( 724 + db_filename("private/1"), 725 + Err(Error::PrivatePathTooShort(_)) 726 + )); 727 + } 728 + }
+464
tests/e2e.rs
··· 1 + use std::collections::BTreeMap; 2 + use std::path::Path; 3 + use std::sync::Arc; 4 + 5 + use object_store::memory::InMemory; 6 + use object_store::path::Path as StorePath; 7 + use object_store::{ObjectStoreExt, PutPayload}; 8 + 9 + use eat_rocks::Error; 10 + 11 + // --------------------------------------------------------------------------- 12 + // Test helpers 13 + // --------------------------------------------------------------------------- 14 + 15 + async fn put(store: &InMemory, path: &str, data: &[u8]) { 16 + store 17 + .put( 18 + &StorePath::from(path), 19 + PutPayload::from_iter(data.iter().copied()), 20 + ) 21 + .await 22 + .unwrap(); 23 + } 24 + 25 + /// Recursively collect every file under `dir`, returning (path-relative-to-base, contents). 26 + fn collect_files(base: &Path, dir: &Path) -> Vec<(String, Vec<u8>)> { 27 + let mut out = Vec::new(); 28 + for entry in std::fs::read_dir(dir).unwrap() { 29 + let entry = entry.unwrap(); 30 + let path = entry.path(); 31 + if path.is_dir() { 32 + out.extend(collect_files(base, &path)); 33 + } else if path.is_file() { 34 + let rel = path 35 + .strip_prefix(base) 36 + .unwrap() 37 + .to_str() 38 + .unwrap() 39 + .to_string(); 40 + out.push((rel, std::fs::read(&path).unwrap())); 41 + } 42 + } 43 + out 44 + } 45 + 46 + /// Load every file in a directory tree into an InMemory object store. 47 + async fn load_dir_into_store(store: &InMemory, dir: &Path) { 48 + for (rel, data) in collect_files(dir, dir) { 49 + put(store, &rel, &data).await; 50 + } 51 + } 52 + 53 + /// Restore via RocksDB's own BackupEngine. 54 + fn rocksdb_restore( 55 + backup_dir: &Path, 56 + db_dir: &Path, 57 + wal_dir: &Path, 58 + backup_id: u32, 59 + ) -> Result<(), rocksdb::Error> { 60 + let opts = rocksdb::backup::BackupEngineOptions::new(backup_dir)?; 61 + let env = rocksdb::Env::new()?; 62 + let mut engine = rocksdb::backup::BackupEngine::open(&opts, &env)?; 63 + engine.restore_from_backup( 64 + db_dir, 65 + wal_dir, 66 + &rocksdb::backup::RestoreOptions::default(), 67 + backup_id, 68 + ) 69 + } 70 + 71 + /// Restore via our library. 72 + async fn our_restore( 73 + backup_dir: &Path, 74 + db_dir: &Path, 75 + wal_dir: &Path, 76 + backup_id: u64, 77 + ) -> Result<(), Error> { 78 + let store = InMemory::new(); 79 + load_dir_into_store(&store, backup_dir).await; 80 + eat_rocks::restore( 81 + Arc::new(store), 82 + "", 83 + db_dir, 84 + eat_rocks::RestoreOptions { 85 + backup_id: Some(backup_id), 86 + verify: true, 87 + wal_dir: Some(wal_dir.to_path_buf()), 88 + ..Default::default() 89 + }, 90 + ) 91 + .await 92 + } 93 + 94 + /// Assert two directory trees contain the exact same files with identical contents. 95 + fn assert_dirs_equal(label: &str, dir_a: &Path, dir_b: &Path) { 96 + let files_a: BTreeMap<String, Vec<u8>> = collect_files(dir_a, dir_a).into_iter().collect(); 97 + let files_b: BTreeMap<String, Vec<u8>> = collect_files(dir_b, dir_b).into_iter().collect(); 98 + 99 + let keys_a: Vec<_> = files_a.keys().collect(); 100 + let keys_b: Vec<_> = files_b.keys().collect(); 101 + assert_eq!( 102 + keys_a, keys_b, 103 + "{label}: file sets differ\n left: {keys_a:?}\n right: {keys_b:?}" 104 + ); 105 + 106 + for (name, content_a) in &files_a { 107 + assert_eq!( 108 + content_a, 109 + &files_b[name], 110 + "{label}: contents differ for {name} ({} vs {} bytes)", 111 + content_a.len(), 112 + files_b[name].len(), 113 + ); 114 + } 115 + } 116 + 117 + /// Create a DB, populate it, back it up. Returns (db_dir, backup_dir) tempdirs. 118 + fn make_backup(key_range: std::ops::Range<u32>) -> (tempfile::TempDir, tempfile::TempDir) { 119 + let db_dir = tempfile::tempdir().unwrap(); 120 + let backup_dir = tempfile::tempdir().unwrap(); 121 + { 122 + let db = rocksdb::DB::open_default(db_dir.path()).unwrap(); 123 + for i in key_range { 124 + db.put(format!("k{i:04}").as_bytes(), format!("v{i}").as_bytes()) 125 + .unwrap(); 126 + } 127 + let opts = rocksdb::backup::BackupEngineOptions::new(backup_dir.path()).unwrap(); 128 + let env = rocksdb::Env::new().unwrap(); 129 + let mut engine = rocksdb::backup::BackupEngine::open(&opts, &env).unwrap(); 130 + engine.create_new_backup_flush(&db, true).unwrap(); 131 + } 132 + (db_dir, backup_dir) 133 + } 134 + 135 + // --------------------------------------------------------------------------- 136 + // E2E: create real backup -> restore via library -> verify DB 137 + // --------------------------------------------------------------------------- 138 + 139 + #[tokio::test] 140 + async fn e2e_single_backup() { 141 + let db_dir = tempfile::tempdir().unwrap(); 142 + let backup_dir = tempfile::tempdir().unwrap(); 143 + 144 + { 145 + let db = rocksdb::DB::open_default(db_dir.path()).unwrap(); 146 + for i in 0..100u32 { 147 + db.put( 148 + format!("key_{i:04}").as_bytes(), 149 + format!("value_{i}").as_bytes(), 150 + ) 151 + .unwrap(); 152 + } 153 + let opts = rocksdb::backup::BackupEngineOptions::new(backup_dir.path()).unwrap(); 154 + let env = rocksdb::Env::new().unwrap(); 155 + let mut engine = rocksdb::backup::BackupEngine::open(&opts, &env).unwrap(); 156 + engine.create_new_backup_flush(&db, true).unwrap(); 157 + } 158 + 159 + let store = InMemory::new(); 160 + load_dir_into_store(&store, backup_dir.path()).await; 161 + 162 + let restore_dir = tempfile::tempdir().unwrap(); 163 + eat_rocks::restore( 164 + Arc::new(store), 165 + "", 166 + restore_dir.path(), 167 + eat_rocks::RestoreOptions { 168 + backup_id: Some(1), 169 + verify: true, 170 + ..Default::default() 171 + }, 172 + ) 173 + .await 174 + .unwrap(); 175 + 176 + { 177 + let db = rocksdb::DB::open_for_read_only( 178 + &rocksdb::Options::default(), 179 + restore_dir.path(), 180 + false, 181 + ) 182 + .unwrap(); 183 + for i in 0..100u32 { 184 + let val = db 185 + .get(format!("key_{i:04}").as_bytes()) 186 + .unwrap() 187 + .expect("key missing after restore"); 188 + assert_eq!(val, format!("value_{i}").as_bytes()); 189 + } 190 + } 191 + } 192 + 193 + #[tokio::test] 194 + async fn e2e_multiple_backups_shared_checksum() { 195 + let db_dir = tempfile::tempdir().unwrap(); 196 + let backup_dir = tempfile::tempdir().unwrap(); 197 + 198 + { 199 + let db = rocksdb::DB::open_default(db_dir.path()).unwrap(); 200 + let opts = rocksdb::backup::BackupEngineOptions::new(backup_dir.path()).unwrap(); 201 + let env = rocksdb::Env::new().unwrap(); 202 + let mut engine = rocksdb::backup::BackupEngine::open(&opts, &env).unwrap(); 203 + 204 + for i in 0..50u32 { 205 + db.put( 206 + format!("key_{i:04}").as_bytes(), 207 + format!("v1_{i}").as_bytes(), 208 + ) 209 + .unwrap(); 210 + } 211 + engine.create_new_backup_flush(&db, true).unwrap(); 212 + 213 + for i in 50..100u32 { 214 + db.put( 215 + format!("key_{i:04}").as_bytes(), 216 + format!("v2_{i}").as_bytes(), 217 + ) 218 + .unwrap(); 219 + } 220 + engine.create_new_backup_flush(&db, true).unwrap(); 221 + } 222 + 223 + let store = InMemory::new(); 224 + load_dir_into_store(&store, backup_dir.path()).await; 225 + 226 + // Restore backup 1 — should only have keys 0-49. 227 + let dir1 = tempfile::tempdir().unwrap(); 228 + eat_rocks::restore( 229 + Arc::new(store.fork()), 230 + "", 231 + dir1.path(), 232 + eat_rocks::RestoreOptions { 233 + backup_id: Some(1), 234 + verify: true, 235 + ..Default::default() 236 + }, 237 + ) 238 + .await 239 + .unwrap(); 240 + { 241 + let db = rocksdb::DB::open_for_read_only(&rocksdb::Options::default(), dir1.path(), false) 242 + .unwrap(); 243 + for i in 0..50u32 { 244 + assert!( 245 + db.get(format!("key_{i:04}").as_bytes()).unwrap().is_some(), 246 + "backup 1 missing key_{i:04}" 247 + ); 248 + } 249 + for i in 50..100u32 { 250 + assert!( 251 + db.get(format!("key_{i:04}").as_bytes()).unwrap().is_none(), 252 + "backup 1 should not have key_{i:04}" 253 + ); 254 + } 255 + } 256 + 257 + // Restore backup 2 — should have all 100 keys. 258 + let dir2 = tempfile::tempdir().unwrap(); 259 + eat_rocks::restore( 260 + Arc::new(store.fork()), 261 + "", 262 + dir2.path(), 263 + eat_rocks::RestoreOptions { 264 + backup_id: Some(2), 265 + verify: true, 266 + ..Default::default() 267 + }, 268 + ) 269 + .await 270 + .unwrap(); 271 + { 272 + let db = rocksdb::DB::open_for_read_only(&rocksdb::Options::default(), dir2.path(), false) 273 + .unwrap(); 274 + for i in 0..100u32 { 275 + assert!( 276 + db.get(format!("key_{i:04}").as_bytes()).unwrap().is_some(), 277 + "backup 2 missing key_{i:04}" 278 + ); 279 + } 280 + } 281 + } 282 + 283 + // --------------------------------------------------------------------------- 284 + // Oracle: compare our output with rocksdb's own restore 285 + // --------------------------------------------------------------------------- 286 + 287 + #[tokio::test] 288 + async fn oracle_single_backup() { 289 + let (_db, backup_dir) = make_backup(0..200); 290 + 291 + let oracle = tempfile::tempdir().unwrap(); 292 + rocksdb_restore(backup_dir.path(), oracle.path(), oracle.path(), 1).unwrap(); 293 + 294 + let ours = tempfile::tempdir().unwrap(); 295 + our_restore(backup_dir.path(), ours.path(), ours.path(), 1) 296 + .await 297 + .unwrap(); 298 + 299 + assert_dirs_equal("single backup", oracle.path(), ours.path()); 300 + } 301 + 302 + #[tokio::test] 303 + async fn oracle_incremental_backups() { 304 + let db_dir = tempfile::tempdir().unwrap(); 305 + let backup_dir = tempfile::tempdir().unwrap(); 306 + 307 + { 308 + let db = rocksdb::DB::open_default(db_dir.path()).unwrap(); 309 + let opts = rocksdb::backup::BackupEngineOptions::new(backup_dir.path()).unwrap(); 310 + let env = rocksdb::Env::new().unwrap(); 311 + let mut engine = rocksdb::backup::BackupEngine::open(&opts, &env).unwrap(); 312 + 313 + for i in 0..100u32 { 314 + db.put(format!("k{i:04}").as_bytes(), b"batch1").unwrap(); 315 + } 316 + engine.create_new_backup_flush(&db, true).unwrap(); 317 + 318 + for i in 100..250u32 { 319 + db.put(format!("k{i:04}").as_bytes(), b"batch2").unwrap(); 320 + } 321 + engine.create_new_backup_flush(&db, true).unwrap(); 322 + } 323 + 324 + let o1 = tempfile::tempdir().unwrap(); 325 + rocksdb_restore(backup_dir.path(), o1.path(), o1.path(), 1).unwrap(); 326 + let u1 = tempfile::tempdir().unwrap(); 327 + our_restore(backup_dir.path(), u1.path(), u1.path(), 1) 328 + .await 329 + .unwrap(); 330 + assert_dirs_equal("backup 1", o1.path(), u1.path()); 331 + 332 + let o2 = tempfile::tempdir().unwrap(); 333 + rocksdb_restore(backup_dir.path(), o2.path(), o2.path(), 2).unwrap(); 334 + let u2 = tempfile::tempdir().unwrap(); 335 + our_restore(backup_dir.path(), u2.path(), u2.path(), 2) 336 + .await 337 + .unwrap(); 338 + assert_dirs_equal("backup 2", o2.path(), u2.path()); 339 + } 340 + 341 + #[tokio::test] 342 + async fn oracle_separate_wal_dir() { 343 + let db_dir = tempfile::tempdir().unwrap(); 344 + let backup_dir = tempfile::tempdir().unwrap(); 345 + 346 + { 347 + let db = rocksdb::DB::open_default(db_dir.path()).unwrap(); 348 + for i in 0..100u32 { 349 + db.put(format!("k{i:04}").as_bytes(), b"v").unwrap(); 350 + } 351 + let opts = rocksdb::backup::BackupEngineOptions::new(backup_dir.path()).unwrap(); 352 + let env = rocksdb::Env::new().unwrap(); 353 + let mut engine = rocksdb::backup::BackupEngine::open(&opts, &env).unwrap(); 354 + // No flush — WAL should contain unflushed data. 355 + engine.create_new_backup(&db).unwrap(); 356 + } 357 + 358 + let o_db = tempfile::tempdir().unwrap(); 359 + let o_wal = tempfile::tempdir().unwrap(); 360 + rocksdb_restore(backup_dir.path(), o_db.path(), o_wal.path(), 1).unwrap(); 361 + 362 + let u_db = tempfile::tempdir().unwrap(); 363 + let u_wal = tempfile::tempdir().unwrap(); 364 + our_restore(backup_dir.path(), u_db.path(), u_wal.path(), 1) 365 + .await 366 + .unwrap(); 367 + 368 + assert_dirs_equal("db_dir", o_db.path(), u_db.path()); 369 + assert_dirs_equal("wal_dir", o_wal.path(), u_wal.path()); 370 + } 371 + 372 + #[tokio::test] 373 + async fn oracle_both_reject_nonexistent_backup() { 374 + let (_db, backup_dir) = make_backup(0..10); 375 + 376 + let o = tempfile::tempdir().unwrap(); 377 + assert!( 378 + rocksdb_restore(backup_dir.path(), o.path(), o.path(), 99).is_err(), 379 + "rocksdb should reject backup 99" 380 + ); 381 + 382 + let u = tempfile::tempdir().unwrap(); 383 + assert!( 384 + our_restore(backup_dir.path(), u.path(), u.path(), 99) 385 + .await 386 + .is_err(), 387 + "our restore should reject backup 99" 388 + ); 389 + } 390 + 391 + // --------------------------------------------------------------------------- 392 + // Failure cases 393 + // --------------------------------------------------------------------------- 394 + 395 + #[tokio::test] 396 + async fn failure_missing_data_file() { 397 + let (_db, backup_dir) = make_backup(0..100); 398 + 399 + let store = InMemory::new(); 400 + load_dir_into_store(&store, backup_dir.path()).await; 401 + 402 + // Find and delete a data file from the store. 403 + let meta = eat_rocks::fetch_meta(&store, "", 1).await.unwrap(); 404 + let victim = meta 405 + .files 406 + .iter() 407 + .find(|f| f.path.contains("shared_checksum") || f.path.contains("MANIFEST")) 408 + .expect("no deletable file found"); 409 + store 410 + .delete(&StorePath::from(victim.path.as_str())) 411 + .await 412 + .unwrap(); 413 + 414 + let target = tempfile::tempdir().unwrap(); 415 + let result = eat_rocks::restore( 416 + Arc::new(store), 417 + "", 418 + target.path(), 419 + eat_rocks::RestoreOptions { 420 + backup_id: Some(1), 421 + ..Default::default() 422 + }, 423 + ) 424 + .await; 425 + assert!( 426 + matches!(result, Err(Error::Fetch { .. })), 427 + "expected Fetch error, got {result:?}" 428 + ); 429 + } 430 + 431 + #[tokio::test] 432 + async fn failure_corrupt_crc_detected() { 433 + let (_db, backup_dir) = make_backup(0..50); 434 + 435 + let store = InMemory::new(); 436 + load_dir_into_store(&store, backup_dir.path()).await; 437 + 438 + // Corrupt a data file in the store by overwriting it. 439 + let meta = eat_rocks::fetch_meta(&store, "", 1).await.unwrap(); 440 + let victim = meta 441 + .files 442 + .iter() 443 + .find(|f| f.path.starts_with("shared_checksum/")) 444 + .expect("no shared_checksum file"); 445 + put(&store, &victim.path, b"corrupted contents!").await; 446 + 447 + let target = tempfile::tempdir().unwrap(); 448 + let result = eat_rocks::restore( 449 + Arc::new(store), 450 + "", 451 + target.path(), 452 + eat_rocks::RestoreOptions { 453 + backup_id: Some(1), 454 + verify: true, 455 + concurrency: 1, 456 + ..Default::default() 457 + }, 458 + ) 459 + .await; 460 + assert!( 461 + matches!(result, Err(Error::ChecksumMismatch { .. })), 462 + "expected ChecksumMismatch, got {result:?}" 463 + ); 464 + }