Homebrew RSS reader server
0
fork

Configure Feed

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

repo: inital commit of slurp

Tim Chmielecki c50a2391

+4034
+17
.claude/settings.local.json
··· 1 + { 2 + "permissions": { 3 + "allow": [ 4 + "WebSearch", 5 + "WebFetch(domain:crates.io)", 6 + "WebFetch(domain:freshrss.github.io)", 7 + "WebFetch(domain:raw.githubusercontent.com)", 8 + "WebFetch(domain:jocmp.com)", 9 + "WebFetch(domain:docs.rs)", 10 + "WebFetch(domain:miniflux.app)", 11 + "Bash(rustc:*)", 12 + "Bash(cargo:*)", 13 + "WebFetch(domain:github.com)", 14 + "Bash(./target/debug/slurp auth:*)" 15 + ] 16 + } 17 + }
+4
.gitignore
··· 1 + /target 2 + *.db 3 + *.db-wal 4 + *.db-shm
+2908
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 = "allocator-api2" 16 + version = "0.2.21" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 19 + 20 + [[package]] 21 + name = "android_system_properties" 22 + version = "0.1.5" 23 + source = "registry+https://github.com/rust-lang/crates.io-index" 24 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 25 + dependencies = [ 26 + "libc", 27 + ] 28 + 29 + [[package]] 30 + name = "anstream" 31 + version = "0.6.21" 32 + source = "registry+https://github.com/rust-lang/crates.io-index" 33 + checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" 34 + dependencies = [ 35 + "anstyle", 36 + "anstyle-parse", 37 + "anstyle-query", 38 + "anstyle-wincon", 39 + "colorchoice", 40 + "is_terminal_polyfill", 41 + "utf8parse", 42 + ] 43 + 44 + [[package]] 45 + name = "anstyle" 46 + version = "1.0.13" 47 + source = "registry+https://github.com/rust-lang/crates.io-index" 48 + checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" 49 + 50 + [[package]] 51 + name = "anstyle-parse" 52 + version = "0.2.7" 53 + source = "registry+https://github.com/rust-lang/crates.io-index" 54 + checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 55 + dependencies = [ 56 + "utf8parse", 57 + ] 58 + 59 + [[package]] 60 + name = "anstyle-query" 61 + version = "1.1.5" 62 + source = "registry+https://github.com/rust-lang/crates.io-index" 63 + checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" 64 + dependencies = [ 65 + "windows-sys 0.61.2", 66 + ] 67 + 68 + [[package]] 69 + name = "anstyle-wincon" 70 + version = "3.0.11" 71 + source = "registry+https://github.com/rust-lang/crates.io-index" 72 + checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" 73 + dependencies = [ 74 + "anstyle", 75 + "once_cell_polyfill", 76 + "windows-sys 0.61.2", 77 + ] 78 + 79 + [[package]] 80 + name = "anyhow" 81 + version = "1.0.100" 82 + source = "registry+https://github.com/rust-lang/crates.io-index" 83 + checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 84 + 85 + [[package]] 86 + name = "atoi" 87 + version = "2.0.0" 88 + source = "registry+https://github.com/rust-lang/crates.io-index" 89 + checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" 90 + dependencies = [ 91 + "num-traits", 92 + ] 93 + 94 + [[package]] 95 + name = "atomic-waker" 96 + version = "1.1.2" 97 + source = "registry+https://github.com/rust-lang/crates.io-index" 98 + checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 99 + 100 + [[package]] 101 + name = "autocfg" 102 + version = "1.5.0" 103 + source = "registry+https://github.com/rust-lang/crates.io-index" 104 + checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 105 + 106 + [[package]] 107 + name = "axum" 108 + version = "0.8.8" 109 + source = "registry+https://github.com/rust-lang/crates.io-index" 110 + checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" 111 + dependencies = [ 112 + "axum-core", 113 + "bytes", 114 + "form_urlencoded", 115 + "futures-util", 116 + "http", 117 + "http-body", 118 + "http-body-util", 119 + "hyper", 120 + "hyper-util", 121 + "itoa", 122 + "matchit", 123 + "memchr", 124 + "mime", 125 + "percent-encoding", 126 + "pin-project-lite", 127 + "serde_core", 128 + "serde_json", 129 + "serde_path_to_error", 130 + "serde_urlencoded", 131 + "sync_wrapper", 132 + "tokio", 133 + "tower", 134 + "tower-layer", 135 + "tower-service", 136 + "tracing", 137 + ] 138 + 139 + [[package]] 140 + name = "axum-core" 141 + version = "0.5.6" 142 + source = "registry+https://github.com/rust-lang/crates.io-index" 143 + checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" 144 + dependencies = [ 145 + "bytes", 146 + "futures-core", 147 + "http", 148 + "http-body", 149 + "http-body-util", 150 + "mime", 151 + "pin-project-lite", 152 + "sync_wrapper", 153 + "tower-layer", 154 + "tower-service", 155 + "tracing", 156 + ] 157 + 158 + [[package]] 159 + name = "base64" 160 + version = "0.22.1" 161 + source = "registry+https://github.com/rust-lang/crates.io-index" 162 + checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 163 + 164 + [[package]] 165 + name = "base64ct" 166 + version = "1.8.3" 167 + source = "registry+https://github.com/rust-lang/crates.io-index" 168 + checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" 169 + 170 + [[package]] 171 + name = "bitflags" 172 + version = "2.10.0" 173 + source = "registry+https://github.com/rust-lang/crates.io-index" 174 + checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 175 + dependencies = [ 176 + "serde_core", 177 + ] 178 + 179 + [[package]] 180 + name = "block-buffer" 181 + version = "0.10.4" 182 + source = "registry+https://github.com/rust-lang/crates.io-index" 183 + checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 184 + dependencies = [ 185 + "generic-array", 186 + ] 187 + 188 + [[package]] 189 + name = "bumpalo" 190 + version = "3.19.1" 191 + source = "registry+https://github.com/rust-lang/crates.io-index" 192 + checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" 193 + 194 + [[package]] 195 + name = "byteorder" 196 + version = "1.5.0" 197 + source = "registry+https://github.com/rust-lang/crates.io-index" 198 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 199 + 200 + [[package]] 201 + name = "bytes" 202 + version = "1.11.0" 203 + source = "registry+https://github.com/rust-lang/crates.io-index" 204 + checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" 205 + 206 + [[package]] 207 + name = "cc" 208 + version = "1.2.54" 209 + source = "registry+https://github.com/rust-lang/crates.io-index" 210 + checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" 211 + dependencies = [ 212 + "find-msvc-tools", 213 + "shlex", 214 + ] 215 + 216 + [[package]] 217 + name = "cfg-if" 218 + version = "1.0.4" 219 + source = "registry+https://github.com/rust-lang/crates.io-index" 220 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 221 + 222 + [[package]] 223 + name = "cfg_aliases" 224 + version = "0.2.1" 225 + source = "registry+https://github.com/rust-lang/crates.io-index" 226 + checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 227 + 228 + [[package]] 229 + name = "chrono" 230 + version = "0.4.43" 231 + source = "registry+https://github.com/rust-lang/crates.io-index" 232 + checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" 233 + dependencies = [ 234 + "iana-time-zone", 235 + "js-sys", 236 + "num-traits", 237 + "serde", 238 + "wasm-bindgen", 239 + "windows-link", 240 + ] 241 + 242 + [[package]] 243 + name = "clap" 244 + version = "4.5.55" 245 + source = "registry+https://github.com/rust-lang/crates.io-index" 246 + checksum = "3e34525d5bbbd55da2bb745d34b36121baac88d07619a9a09cfcf4a6c0832785" 247 + dependencies = [ 248 + "clap_builder", 249 + "clap_derive", 250 + ] 251 + 252 + [[package]] 253 + name = "clap_builder" 254 + version = "4.5.55" 255 + source = "registry+https://github.com/rust-lang/crates.io-index" 256 + checksum = "59a20016a20a3da95bef50ec7238dbd09baeef4311dcdd38ec15aba69812fb61" 257 + dependencies = [ 258 + "anstream", 259 + "anstyle", 260 + "clap_lex", 261 + "strsim", 262 + ] 263 + 264 + [[package]] 265 + name = "clap_derive" 266 + version = "4.5.55" 267 + source = "registry+https://github.com/rust-lang/crates.io-index" 268 + checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" 269 + dependencies = [ 270 + "heck", 271 + "proc-macro2", 272 + "quote", 273 + "syn", 274 + ] 275 + 276 + [[package]] 277 + name = "clap_lex" 278 + version = "0.7.7" 279 + source = "registry+https://github.com/rust-lang/crates.io-index" 280 + checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" 281 + 282 + [[package]] 283 + name = "colorchoice" 284 + version = "1.0.4" 285 + source = "registry+https://github.com/rust-lang/crates.io-index" 286 + checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 287 + 288 + [[package]] 289 + name = "concurrent-queue" 290 + version = "2.5.0" 291 + source = "registry+https://github.com/rust-lang/crates.io-index" 292 + checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 293 + dependencies = [ 294 + "crossbeam-utils", 295 + ] 296 + 297 + [[package]] 298 + name = "const-oid" 299 + version = "0.9.6" 300 + source = "registry+https://github.com/rust-lang/crates.io-index" 301 + checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" 302 + 303 + [[package]] 304 + name = "core-foundation-sys" 305 + version = "0.8.7" 306 + source = "registry+https://github.com/rust-lang/crates.io-index" 307 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 308 + 309 + [[package]] 310 + name = "cpufeatures" 311 + version = "0.2.17" 312 + source = "registry+https://github.com/rust-lang/crates.io-index" 313 + checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 314 + dependencies = [ 315 + "libc", 316 + ] 317 + 318 + [[package]] 319 + name = "crc" 320 + version = "3.4.0" 321 + source = "registry+https://github.com/rust-lang/crates.io-index" 322 + checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" 323 + dependencies = [ 324 + "crc-catalog", 325 + ] 326 + 327 + [[package]] 328 + name = "crc-catalog" 329 + version = "2.4.0" 330 + source = "registry+https://github.com/rust-lang/crates.io-index" 331 + checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" 332 + 333 + [[package]] 334 + name = "crossbeam-queue" 335 + version = "0.3.12" 336 + source = "registry+https://github.com/rust-lang/crates.io-index" 337 + checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" 338 + dependencies = [ 339 + "crossbeam-utils", 340 + ] 341 + 342 + [[package]] 343 + name = "crossbeam-utils" 344 + version = "0.8.21" 345 + source = "registry+https://github.com/rust-lang/crates.io-index" 346 + checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 347 + 348 + [[package]] 349 + name = "crypto-common" 350 + version = "0.1.7" 351 + source = "registry+https://github.com/rust-lang/crates.io-index" 352 + checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" 353 + dependencies = [ 354 + "generic-array", 355 + "typenum", 356 + ] 357 + 358 + [[package]] 359 + name = "der" 360 + version = "0.7.10" 361 + source = "registry+https://github.com/rust-lang/crates.io-index" 362 + checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" 363 + dependencies = [ 364 + "const-oid", 365 + "pem-rfc7468", 366 + "zeroize", 367 + ] 368 + 369 + [[package]] 370 + name = "digest" 371 + version = "0.10.7" 372 + source = "registry+https://github.com/rust-lang/crates.io-index" 373 + checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 374 + dependencies = [ 375 + "block-buffer", 376 + "const-oid", 377 + "crypto-common", 378 + "subtle", 379 + ] 380 + 381 + [[package]] 382 + name = "displaydoc" 383 + version = "0.2.5" 384 + source = "registry+https://github.com/rust-lang/crates.io-index" 385 + checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 386 + dependencies = [ 387 + "proc-macro2", 388 + "quote", 389 + "syn", 390 + ] 391 + 392 + [[package]] 393 + name = "dotenvy" 394 + version = "0.15.7" 395 + source = "registry+https://github.com/rust-lang/crates.io-index" 396 + checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" 397 + 398 + [[package]] 399 + name = "either" 400 + version = "1.15.0" 401 + source = "registry+https://github.com/rust-lang/crates.io-index" 402 + checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 403 + dependencies = [ 404 + "serde", 405 + ] 406 + 407 + [[package]] 408 + name = "encoding_rs" 409 + version = "0.8.35" 410 + source = "registry+https://github.com/rust-lang/crates.io-index" 411 + checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 412 + dependencies = [ 413 + "cfg-if", 414 + ] 415 + 416 + [[package]] 417 + name = "equivalent" 418 + version = "1.0.2" 419 + source = "registry+https://github.com/rust-lang/crates.io-index" 420 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 421 + 422 + [[package]] 423 + name = "errno" 424 + version = "0.3.14" 425 + source = "registry+https://github.com/rust-lang/crates.io-index" 426 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 427 + dependencies = [ 428 + "libc", 429 + "windows-sys 0.61.2", 430 + ] 431 + 432 + [[package]] 433 + name = "etcetera" 434 + version = "0.8.0" 435 + source = "registry+https://github.com/rust-lang/crates.io-index" 436 + checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" 437 + dependencies = [ 438 + "cfg-if", 439 + "home", 440 + "windows-sys 0.48.0", 441 + ] 442 + 443 + [[package]] 444 + name = "event-listener" 445 + version = "5.4.1" 446 + source = "registry+https://github.com/rust-lang/crates.io-index" 447 + checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" 448 + dependencies = [ 449 + "concurrent-queue", 450 + "parking", 451 + "pin-project-lite", 452 + ] 453 + 454 + [[package]] 455 + name = "feed-rs" 456 + version = "2.3.1" 457 + source = "registry+https://github.com/rust-lang/crates.io-index" 458 + checksum = "e4c0591d23efd0d595099af69a31863ac1823046b1b021e3b06ba3aae7e00991" 459 + dependencies = [ 460 + "chrono", 461 + "mediatype", 462 + "quick-xml", 463 + "regex", 464 + "serde", 465 + "serde_json", 466 + "siphasher", 467 + "url", 468 + "uuid", 469 + ] 470 + 471 + [[package]] 472 + name = "find-msvc-tools" 473 + version = "0.1.8" 474 + source = "registry+https://github.com/rust-lang/crates.io-index" 475 + checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" 476 + 477 + [[package]] 478 + name = "flume" 479 + version = "0.11.1" 480 + source = "registry+https://github.com/rust-lang/crates.io-index" 481 + checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" 482 + dependencies = [ 483 + "futures-core", 484 + "futures-sink", 485 + "spin", 486 + ] 487 + 488 + [[package]] 489 + name = "foldhash" 490 + version = "0.1.5" 491 + source = "registry+https://github.com/rust-lang/crates.io-index" 492 + checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 493 + 494 + [[package]] 495 + name = "form_urlencoded" 496 + version = "1.2.2" 497 + source = "registry+https://github.com/rust-lang/crates.io-index" 498 + checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 499 + dependencies = [ 500 + "percent-encoding", 501 + ] 502 + 503 + [[package]] 504 + name = "futures-channel" 505 + version = "0.3.31" 506 + source = "registry+https://github.com/rust-lang/crates.io-index" 507 + checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 508 + dependencies = [ 509 + "futures-core", 510 + "futures-sink", 511 + ] 512 + 513 + [[package]] 514 + name = "futures-core" 515 + version = "0.3.31" 516 + source = "registry+https://github.com/rust-lang/crates.io-index" 517 + checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 518 + 519 + [[package]] 520 + name = "futures-executor" 521 + version = "0.3.31" 522 + source = "registry+https://github.com/rust-lang/crates.io-index" 523 + checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 524 + dependencies = [ 525 + "futures-core", 526 + "futures-task", 527 + "futures-util", 528 + ] 529 + 530 + [[package]] 531 + name = "futures-intrusive" 532 + version = "0.5.0" 533 + source = "registry+https://github.com/rust-lang/crates.io-index" 534 + checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" 535 + dependencies = [ 536 + "futures-core", 537 + "lock_api", 538 + "parking_lot", 539 + ] 540 + 541 + [[package]] 542 + name = "futures-io" 543 + version = "0.3.31" 544 + source = "registry+https://github.com/rust-lang/crates.io-index" 545 + checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 546 + 547 + [[package]] 548 + name = "futures-sink" 549 + version = "0.3.31" 550 + source = "registry+https://github.com/rust-lang/crates.io-index" 551 + checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 552 + 553 + [[package]] 554 + name = "futures-task" 555 + version = "0.3.31" 556 + source = "registry+https://github.com/rust-lang/crates.io-index" 557 + checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 558 + 559 + [[package]] 560 + name = "futures-util" 561 + version = "0.3.31" 562 + source = "registry+https://github.com/rust-lang/crates.io-index" 563 + checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 564 + dependencies = [ 565 + "futures-core", 566 + "futures-io", 567 + "futures-sink", 568 + "futures-task", 569 + "memchr", 570 + "pin-project-lite", 571 + "pin-utils", 572 + "slab", 573 + ] 574 + 575 + [[package]] 576 + name = "generic-array" 577 + version = "0.14.7" 578 + source = "registry+https://github.com/rust-lang/crates.io-index" 579 + checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 580 + dependencies = [ 581 + "typenum", 582 + "version_check", 583 + ] 584 + 585 + [[package]] 586 + name = "getrandom" 587 + version = "0.2.17" 588 + source = "registry+https://github.com/rust-lang/crates.io-index" 589 + checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" 590 + dependencies = [ 591 + "cfg-if", 592 + "js-sys", 593 + "libc", 594 + "wasi", 595 + "wasm-bindgen", 596 + ] 597 + 598 + [[package]] 599 + name = "getrandom" 600 + version = "0.3.4" 601 + source = "registry+https://github.com/rust-lang/crates.io-index" 602 + checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 603 + dependencies = [ 604 + "cfg-if", 605 + "js-sys", 606 + "libc", 607 + "r-efi", 608 + "wasip2", 609 + "wasm-bindgen", 610 + ] 611 + 612 + [[package]] 613 + name = "hashbrown" 614 + version = "0.15.5" 615 + source = "registry+https://github.com/rust-lang/crates.io-index" 616 + checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 617 + dependencies = [ 618 + "allocator-api2", 619 + "equivalent", 620 + "foldhash", 621 + ] 622 + 623 + [[package]] 624 + name = "hashbrown" 625 + version = "0.16.1" 626 + source = "registry+https://github.com/rust-lang/crates.io-index" 627 + checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" 628 + 629 + [[package]] 630 + name = "hashlink" 631 + version = "0.10.0" 632 + source = "registry+https://github.com/rust-lang/crates.io-index" 633 + checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" 634 + dependencies = [ 635 + "hashbrown 0.15.5", 636 + ] 637 + 638 + [[package]] 639 + name = "heck" 640 + version = "0.5.0" 641 + source = "registry+https://github.com/rust-lang/crates.io-index" 642 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 643 + 644 + [[package]] 645 + name = "hex" 646 + version = "0.4.3" 647 + source = "registry+https://github.com/rust-lang/crates.io-index" 648 + checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 649 + 650 + [[package]] 651 + name = "hkdf" 652 + version = "0.12.4" 653 + source = "registry+https://github.com/rust-lang/crates.io-index" 654 + checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" 655 + dependencies = [ 656 + "hmac", 657 + ] 658 + 659 + [[package]] 660 + name = "hmac" 661 + version = "0.12.1" 662 + source = "registry+https://github.com/rust-lang/crates.io-index" 663 + checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 664 + dependencies = [ 665 + "digest", 666 + ] 667 + 668 + [[package]] 669 + name = "home" 670 + version = "0.5.12" 671 + source = "registry+https://github.com/rust-lang/crates.io-index" 672 + checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" 673 + dependencies = [ 674 + "windows-sys 0.61.2", 675 + ] 676 + 677 + [[package]] 678 + name = "http" 679 + version = "1.4.0" 680 + source = "registry+https://github.com/rust-lang/crates.io-index" 681 + checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" 682 + dependencies = [ 683 + "bytes", 684 + "itoa", 685 + ] 686 + 687 + [[package]] 688 + name = "http-body" 689 + version = "1.0.1" 690 + source = "registry+https://github.com/rust-lang/crates.io-index" 691 + checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 692 + dependencies = [ 693 + "bytes", 694 + "http", 695 + ] 696 + 697 + [[package]] 698 + name = "http-body-util" 699 + version = "0.1.3" 700 + source = "registry+https://github.com/rust-lang/crates.io-index" 701 + checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 702 + dependencies = [ 703 + "bytes", 704 + "futures-core", 705 + "http", 706 + "http-body", 707 + "pin-project-lite", 708 + ] 709 + 710 + [[package]] 711 + name = "httparse" 712 + version = "1.10.1" 713 + source = "registry+https://github.com/rust-lang/crates.io-index" 714 + checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 715 + 716 + [[package]] 717 + name = "httpdate" 718 + version = "1.0.3" 719 + source = "registry+https://github.com/rust-lang/crates.io-index" 720 + checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 721 + 722 + [[package]] 723 + name = "hyper" 724 + version = "1.8.1" 725 + source = "registry+https://github.com/rust-lang/crates.io-index" 726 + checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" 727 + dependencies = [ 728 + "atomic-waker", 729 + "bytes", 730 + "futures-channel", 731 + "futures-core", 732 + "http", 733 + "http-body", 734 + "httparse", 735 + "httpdate", 736 + "itoa", 737 + "pin-project-lite", 738 + "pin-utils", 739 + "smallvec", 740 + "tokio", 741 + "want", 742 + ] 743 + 744 + [[package]] 745 + name = "hyper-rustls" 746 + version = "0.27.7" 747 + source = "registry+https://github.com/rust-lang/crates.io-index" 748 + checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" 749 + dependencies = [ 750 + "http", 751 + "hyper", 752 + "hyper-util", 753 + "rustls", 754 + "rustls-pki-types", 755 + "tokio", 756 + "tokio-rustls", 757 + "tower-service", 758 + "webpki-roots", 759 + ] 760 + 761 + [[package]] 762 + name = "hyper-util" 763 + version = "0.1.19" 764 + source = "registry+https://github.com/rust-lang/crates.io-index" 765 + checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" 766 + dependencies = [ 767 + "base64", 768 + "bytes", 769 + "futures-channel", 770 + "futures-core", 771 + "futures-util", 772 + "http", 773 + "http-body", 774 + "hyper", 775 + "ipnet", 776 + "libc", 777 + "percent-encoding", 778 + "pin-project-lite", 779 + "socket2", 780 + "tokio", 781 + "tower-service", 782 + "tracing", 783 + ] 784 + 785 + [[package]] 786 + name = "iana-time-zone" 787 + version = "0.1.65" 788 + source = "registry+https://github.com/rust-lang/crates.io-index" 789 + checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" 790 + dependencies = [ 791 + "android_system_properties", 792 + "core-foundation-sys", 793 + "iana-time-zone-haiku", 794 + "js-sys", 795 + "log", 796 + "wasm-bindgen", 797 + "windows-core", 798 + ] 799 + 800 + [[package]] 801 + name = "iana-time-zone-haiku" 802 + version = "0.1.2" 803 + source = "registry+https://github.com/rust-lang/crates.io-index" 804 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 805 + dependencies = [ 806 + "cc", 807 + ] 808 + 809 + [[package]] 810 + name = "icu_collections" 811 + version = "2.1.1" 812 + source = "registry+https://github.com/rust-lang/crates.io-index" 813 + checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" 814 + dependencies = [ 815 + "displaydoc", 816 + "potential_utf", 817 + "yoke", 818 + "zerofrom", 819 + "zerovec", 820 + ] 821 + 822 + [[package]] 823 + name = "icu_locale_core" 824 + version = "2.1.1" 825 + source = "registry+https://github.com/rust-lang/crates.io-index" 826 + checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" 827 + dependencies = [ 828 + "displaydoc", 829 + "litemap", 830 + "tinystr", 831 + "writeable", 832 + "zerovec", 833 + ] 834 + 835 + [[package]] 836 + name = "icu_normalizer" 837 + version = "2.1.1" 838 + source = "registry+https://github.com/rust-lang/crates.io-index" 839 + checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" 840 + dependencies = [ 841 + "icu_collections", 842 + "icu_normalizer_data", 843 + "icu_properties", 844 + "icu_provider", 845 + "smallvec", 846 + "zerovec", 847 + ] 848 + 849 + [[package]] 850 + name = "icu_normalizer_data" 851 + version = "2.1.1" 852 + source = "registry+https://github.com/rust-lang/crates.io-index" 853 + checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" 854 + 855 + [[package]] 856 + name = "icu_properties" 857 + version = "2.1.2" 858 + source = "registry+https://github.com/rust-lang/crates.io-index" 859 + checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" 860 + dependencies = [ 861 + "icu_collections", 862 + "icu_locale_core", 863 + "icu_properties_data", 864 + "icu_provider", 865 + "zerotrie", 866 + "zerovec", 867 + ] 868 + 869 + [[package]] 870 + name = "icu_properties_data" 871 + version = "2.1.2" 872 + source = "registry+https://github.com/rust-lang/crates.io-index" 873 + checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" 874 + 875 + [[package]] 876 + name = "icu_provider" 877 + version = "2.1.1" 878 + source = "registry+https://github.com/rust-lang/crates.io-index" 879 + checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" 880 + dependencies = [ 881 + "displaydoc", 882 + "icu_locale_core", 883 + "writeable", 884 + "yoke", 885 + "zerofrom", 886 + "zerotrie", 887 + "zerovec", 888 + ] 889 + 890 + [[package]] 891 + name = "idna" 892 + version = "1.1.0" 893 + source = "registry+https://github.com/rust-lang/crates.io-index" 894 + checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 895 + dependencies = [ 896 + "idna_adapter", 897 + "smallvec", 898 + "utf8_iter", 899 + ] 900 + 901 + [[package]] 902 + name = "idna_adapter" 903 + version = "1.2.1" 904 + source = "registry+https://github.com/rust-lang/crates.io-index" 905 + checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 906 + dependencies = [ 907 + "icu_normalizer", 908 + "icu_properties", 909 + ] 910 + 911 + [[package]] 912 + name = "indexmap" 913 + version = "2.13.0" 914 + source = "registry+https://github.com/rust-lang/crates.io-index" 915 + checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" 916 + dependencies = [ 917 + "equivalent", 918 + "hashbrown 0.16.1", 919 + ] 920 + 921 + [[package]] 922 + name = "ipnet" 923 + version = "2.11.0" 924 + source = "registry+https://github.com/rust-lang/crates.io-index" 925 + checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 926 + 927 + [[package]] 928 + name = "iri-string" 929 + version = "0.7.10" 930 + source = "registry+https://github.com/rust-lang/crates.io-index" 931 + checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" 932 + dependencies = [ 933 + "memchr", 934 + "serde", 935 + ] 936 + 937 + [[package]] 938 + name = "is_terminal_polyfill" 939 + version = "1.70.2" 940 + source = "registry+https://github.com/rust-lang/crates.io-index" 941 + checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" 942 + 943 + [[package]] 944 + name = "itoa" 945 + version = "1.0.17" 946 + source = "registry+https://github.com/rust-lang/crates.io-index" 947 + checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" 948 + 949 + [[package]] 950 + name = "js-sys" 951 + version = "0.3.85" 952 + source = "registry+https://github.com/rust-lang/crates.io-index" 953 + checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" 954 + dependencies = [ 955 + "once_cell", 956 + "wasm-bindgen", 957 + ] 958 + 959 + [[package]] 960 + name = "lazy_static" 961 + version = "1.5.0" 962 + source = "registry+https://github.com/rust-lang/crates.io-index" 963 + checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 964 + dependencies = [ 965 + "spin", 966 + ] 967 + 968 + [[package]] 969 + name = "libc" 970 + version = "0.2.180" 971 + source = "registry+https://github.com/rust-lang/crates.io-index" 972 + checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" 973 + 974 + [[package]] 975 + name = "libm" 976 + version = "0.2.16" 977 + source = "registry+https://github.com/rust-lang/crates.io-index" 978 + checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" 979 + 980 + [[package]] 981 + name = "libredox" 982 + version = "0.1.12" 983 + source = "registry+https://github.com/rust-lang/crates.io-index" 984 + checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" 985 + dependencies = [ 986 + "bitflags", 987 + "libc", 988 + "redox_syscall 0.7.0", 989 + ] 990 + 991 + [[package]] 992 + name = "libsqlite3-sys" 993 + version = "0.30.1" 994 + source = "registry+https://github.com/rust-lang/crates.io-index" 995 + checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" 996 + dependencies = [ 997 + "cc", 998 + "pkg-config", 999 + "vcpkg", 1000 + ] 1001 + 1002 + [[package]] 1003 + name = "litemap" 1004 + version = "0.8.1" 1005 + source = "registry+https://github.com/rust-lang/crates.io-index" 1006 + checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" 1007 + 1008 + [[package]] 1009 + name = "lock_api" 1010 + version = "0.4.14" 1011 + source = "registry+https://github.com/rust-lang/crates.io-index" 1012 + checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 1013 + dependencies = [ 1014 + "scopeguard", 1015 + ] 1016 + 1017 + [[package]] 1018 + name = "log" 1019 + version = "0.4.29" 1020 + source = "registry+https://github.com/rust-lang/crates.io-index" 1021 + checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 1022 + 1023 + [[package]] 1024 + name = "lru-slab" 1025 + version = "0.1.2" 1026 + source = "registry+https://github.com/rust-lang/crates.io-index" 1027 + checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" 1028 + 1029 + [[package]] 1030 + name = "matchit" 1031 + version = "0.8.4" 1032 + source = "registry+https://github.com/rust-lang/crates.io-index" 1033 + checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" 1034 + 1035 + [[package]] 1036 + name = "md-5" 1037 + version = "0.10.6" 1038 + source = "registry+https://github.com/rust-lang/crates.io-index" 1039 + checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" 1040 + dependencies = [ 1041 + "cfg-if", 1042 + "digest", 1043 + ] 1044 + 1045 + [[package]] 1046 + name = "mediatype" 1047 + version = "0.19.20" 1048 + source = "registry+https://github.com/rust-lang/crates.io-index" 1049 + checksum = "33746aadcb41349ec291e7f2f0a3aa6834d1d7c58066fb4b01f68efc4c4b7631" 1050 + dependencies = [ 1051 + "serde", 1052 + ] 1053 + 1054 + [[package]] 1055 + name = "memchr" 1056 + version = "2.7.6" 1057 + source = "registry+https://github.com/rust-lang/crates.io-index" 1058 + checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 1059 + 1060 + [[package]] 1061 + name = "mime" 1062 + version = "0.3.17" 1063 + source = "registry+https://github.com/rust-lang/crates.io-index" 1064 + checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1065 + 1066 + [[package]] 1067 + name = "mio" 1068 + version = "1.1.1" 1069 + source = "registry+https://github.com/rust-lang/crates.io-index" 1070 + checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" 1071 + dependencies = [ 1072 + "libc", 1073 + "wasi", 1074 + "windows-sys 0.61.2", 1075 + ] 1076 + 1077 + [[package]] 1078 + name = "nu-ansi-term" 1079 + version = "0.50.3" 1080 + source = "registry+https://github.com/rust-lang/crates.io-index" 1081 + checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" 1082 + dependencies = [ 1083 + "windows-sys 0.61.2", 1084 + ] 1085 + 1086 + [[package]] 1087 + name = "num-bigint-dig" 1088 + version = "0.8.6" 1089 + source = "registry+https://github.com/rust-lang/crates.io-index" 1090 + checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" 1091 + dependencies = [ 1092 + "lazy_static", 1093 + "libm", 1094 + "num-integer", 1095 + "num-iter", 1096 + "num-traits", 1097 + "rand 0.8.5", 1098 + "smallvec", 1099 + "zeroize", 1100 + ] 1101 + 1102 + [[package]] 1103 + name = "num-integer" 1104 + version = "0.1.46" 1105 + source = "registry+https://github.com/rust-lang/crates.io-index" 1106 + checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 1107 + dependencies = [ 1108 + "num-traits", 1109 + ] 1110 + 1111 + [[package]] 1112 + name = "num-iter" 1113 + version = "0.1.45" 1114 + source = "registry+https://github.com/rust-lang/crates.io-index" 1115 + checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 1116 + dependencies = [ 1117 + "autocfg", 1118 + "num-integer", 1119 + "num-traits", 1120 + ] 1121 + 1122 + [[package]] 1123 + name = "num-traits" 1124 + version = "0.2.19" 1125 + source = "registry+https://github.com/rust-lang/crates.io-index" 1126 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1127 + dependencies = [ 1128 + "autocfg", 1129 + "libm", 1130 + ] 1131 + 1132 + [[package]] 1133 + name = "once_cell" 1134 + version = "1.21.3" 1135 + source = "registry+https://github.com/rust-lang/crates.io-index" 1136 + checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 1137 + 1138 + [[package]] 1139 + name = "once_cell_polyfill" 1140 + version = "1.70.2" 1141 + source = "registry+https://github.com/rust-lang/crates.io-index" 1142 + checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" 1143 + 1144 + [[package]] 1145 + name = "parking" 1146 + version = "2.2.1" 1147 + source = "registry+https://github.com/rust-lang/crates.io-index" 1148 + checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 1149 + 1150 + [[package]] 1151 + name = "parking_lot" 1152 + version = "0.12.5" 1153 + source = "registry+https://github.com/rust-lang/crates.io-index" 1154 + checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 1155 + dependencies = [ 1156 + "lock_api", 1157 + "parking_lot_core", 1158 + ] 1159 + 1160 + [[package]] 1161 + name = "parking_lot_core" 1162 + version = "0.9.12" 1163 + source = "registry+https://github.com/rust-lang/crates.io-index" 1164 + checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 1165 + dependencies = [ 1166 + "cfg-if", 1167 + "libc", 1168 + "redox_syscall 0.5.18", 1169 + "smallvec", 1170 + "windows-link", 1171 + ] 1172 + 1173 + [[package]] 1174 + name = "pem-rfc7468" 1175 + version = "0.7.0" 1176 + source = "registry+https://github.com/rust-lang/crates.io-index" 1177 + checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" 1178 + dependencies = [ 1179 + "base64ct", 1180 + ] 1181 + 1182 + [[package]] 1183 + name = "percent-encoding" 1184 + version = "2.3.2" 1185 + source = "registry+https://github.com/rust-lang/crates.io-index" 1186 + checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 1187 + 1188 + [[package]] 1189 + name = "pin-project-lite" 1190 + version = "0.2.16" 1191 + source = "registry+https://github.com/rust-lang/crates.io-index" 1192 + checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 1193 + 1194 + [[package]] 1195 + name = "pin-utils" 1196 + version = "0.1.0" 1197 + source = "registry+https://github.com/rust-lang/crates.io-index" 1198 + checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1199 + 1200 + [[package]] 1201 + name = "pkcs1" 1202 + version = "0.7.5" 1203 + source = "registry+https://github.com/rust-lang/crates.io-index" 1204 + checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" 1205 + dependencies = [ 1206 + "der", 1207 + "pkcs8", 1208 + "spki", 1209 + ] 1210 + 1211 + [[package]] 1212 + name = "pkcs8" 1213 + version = "0.10.2" 1214 + source = "registry+https://github.com/rust-lang/crates.io-index" 1215 + checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" 1216 + dependencies = [ 1217 + "der", 1218 + "spki", 1219 + ] 1220 + 1221 + [[package]] 1222 + name = "pkg-config" 1223 + version = "0.3.32" 1224 + source = "registry+https://github.com/rust-lang/crates.io-index" 1225 + checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1226 + 1227 + [[package]] 1228 + name = "potential_utf" 1229 + version = "0.1.4" 1230 + source = "registry+https://github.com/rust-lang/crates.io-index" 1231 + checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" 1232 + dependencies = [ 1233 + "zerovec", 1234 + ] 1235 + 1236 + [[package]] 1237 + name = "ppv-lite86" 1238 + version = "0.2.21" 1239 + source = "registry+https://github.com/rust-lang/crates.io-index" 1240 + checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 1241 + dependencies = [ 1242 + "zerocopy", 1243 + ] 1244 + 1245 + [[package]] 1246 + name = "proc-macro2" 1247 + version = "1.0.106" 1248 + source = "registry+https://github.com/rust-lang/crates.io-index" 1249 + checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" 1250 + dependencies = [ 1251 + "unicode-ident", 1252 + ] 1253 + 1254 + [[package]] 1255 + name = "quick-xml" 1256 + version = "0.37.5" 1257 + source = "registry+https://github.com/rust-lang/crates.io-index" 1258 + checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" 1259 + dependencies = [ 1260 + "encoding_rs", 1261 + "memchr", 1262 + ] 1263 + 1264 + [[package]] 1265 + name = "quinn" 1266 + version = "0.11.9" 1267 + source = "registry+https://github.com/rust-lang/crates.io-index" 1268 + checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" 1269 + dependencies = [ 1270 + "bytes", 1271 + "cfg_aliases", 1272 + "pin-project-lite", 1273 + "quinn-proto", 1274 + "quinn-udp", 1275 + "rustc-hash", 1276 + "rustls", 1277 + "socket2", 1278 + "thiserror", 1279 + "tokio", 1280 + "tracing", 1281 + "web-time", 1282 + ] 1283 + 1284 + [[package]] 1285 + name = "quinn-proto" 1286 + version = "0.11.13" 1287 + source = "registry+https://github.com/rust-lang/crates.io-index" 1288 + checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" 1289 + dependencies = [ 1290 + "bytes", 1291 + "getrandom 0.3.4", 1292 + "lru-slab", 1293 + "rand 0.9.2", 1294 + "ring", 1295 + "rustc-hash", 1296 + "rustls", 1297 + "rustls-pki-types", 1298 + "slab", 1299 + "thiserror", 1300 + "tinyvec", 1301 + "tracing", 1302 + "web-time", 1303 + ] 1304 + 1305 + [[package]] 1306 + name = "quinn-udp" 1307 + version = "0.5.14" 1308 + source = "registry+https://github.com/rust-lang/crates.io-index" 1309 + checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" 1310 + dependencies = [ 1311 + "cfg_aliases", 1312 + "libc", 1313 + "once_cell", 1314 + "socket2", 1315 + "tracing", 1316 + "windows-sys 0.60.2", 1317 + ] 1318 + 1319 + [[package]] 1320 + name = "quote" 1321 + version = "1.0.44" 1322 + source = "registry+https://github.com/rust-lang/crates.io-index" 1323 + checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" 1324 + dependencies = [ 1325 + "proc-macro2", 1326 + ] 1327 + 1328 + [[package]] 1329 + name = "r-efi" 1330 + version = "5.3.0" 1331 + source = "registry+https://github.com/rust-lang/crates.io-index" 1332 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 1333 + 1334 + [[package]] 1335 + name = "rand" 1336 + version = "0.8.5" 1337 + source = "registry+https://github.com/rust-lang/crates.io-index" 1338 + checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1339 + dependencies = [ 1340 + "libc", 1341 + "rand_chacha 0.3.1", 1342 + "rand_core 0.6.4", 1343 + ] 1344 + 1345 + [[package]] 1346 + name = "rand" 1347 + version = "0.9.2" 1348 + source = "registry+https://github.com/rust-lang/crates.io-index" 1349 + checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 1350 + dependencies = [ 1351 + "rand_chacha 0.9.0", 1352 + "rand_core 0.9.5", 1353 + ] 1354 + 1355 + [[package]] 1356 + name = "rand_chacha" 1357 + version = "0.3.1" 1358 + source = "registry+https://github.com/rust-lang/crates.io-index" 1359 + checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1360 + dependencies = [ 1361 + "ppv-lite86", 1362 + "rand_core 0.6.4", 1363 + ] 1364 + 1365 + [[package]] 1366 + name = "rand_chacha" 1367 + version = "0.9.0" 1368 + source = "registry+https://github.com/rust-lang/crates.io-index" 1369 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 1370 + dependencies = [ 1371 + "ppv-lite86", 1372 + "rand_core 0.9.5", 1373 + ] 1374 + 1375 + [[package]] 1376 + name = "rand_core" 1377 + version = "0.6.4" 1378 + source = "registry+https://github.com/rust-lang/crates.io-index" 1379 + checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1380 + dependencies = [ 1381 + "getrandom 0.2.17", 1382 + ] 1383 + 1384 + [[package]] 1385 + name = "rand_core" 1386 + version = "0.9.5" 1387 + source = "registry+https://github.com/rust-lang/crates.io-index" 1388 + checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" 1389 + dependencies = [ 1390 + "getrandom 0.3.4", 1391 + ] 1392 + 1393 + [[package]] 1394 + name = "redox_syscall" 1395 + version = "0.5.18" 1396 + source = "registry+https://github.com/rust-lang/crates.io-index" 1397 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 1398 + dependencies = [ 1399 + "bitflags", 1400 + ] 1401 + 1402 + [[package]] 1403 + name = "redox_syscall" 1404 + version = "0.7.0" 1405 + source = "registry+https://github.com/rust-lang/crates.io-index" 1406 + checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" 1407 + dependencies = [ 1408 + "bitflags", 1409 + ] 1410 + 1411 + [[package]] 1412 + name = "regex" 1413 + version = "1.12.2" 1414 + source = "registry+https://github.com/rust-lang/crates.io-index" 1415 + checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" 1416 + dependencies = [ 1417 + "aho-corasick", 1418 + "memchr", 1419 + "regex-automata", 1420 + "regex-syntax", 1421 + ] 1422 + 1423 + [[package]] 1424 + name = "regex-automata" 1425 + version = "0.4.13" 1426 + source = "registry+https://github.com/rust-lang/crates.io-index" 1427 + checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" 1428 + dependencies = [ 1429 + "aho-corasick", 1430 + "memchr", 1431 + "regex-syntax", 1432 + ] 1433 + 1434 + [[package]] 1435 + name = "regex-syntax" 1436 + version = "0.8.8" 1437 + source = "registry+https://github.com/rust-lang/crates.io-index" 1438 + checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" 1439 + 1440 + [[package]] 1441 + name = "reqwest" 1442 + version = "0.12.28" 1443 + source = "registry+https://github.com/rust-lang/crates.io-index" 1444 + checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" 1445 + dependencies = [ 1446 + "base64", 1447 + "bytes", 1448 + "futures-core", 1449 + "http", 1450 + "http-body", 1451 + "http-body-util", 1452 + "hyper", 1453 + "hyper-rustls", 1454 + "hyper-util", 1455 + "js-sys", 1456 + "log", 1457 + "percent-encoding", 1458 + "pin-project-lite", 1459 + "quinn", 1460 + "rustls", 1461 + "rustls-pki-types", 1462 + "serde", 1463 + "serde_json", 1464 + "serde_urlencoded", 1465 + "sync_wrapper", 1466 + "tokio", 1467 + "tokio-rustls", 1468 + "tower", 1469 + "tower-http", 1470 + "tower-service", 1471 + "url", 1472 + "wasm-bindgen", 1473 + "wasm-bindgen-futures", 1474 + "web-sys", 1475 + "webpki-roots", 1476 + ] 1477 + 1478 + [[package]] 1479 + name = "ring" 1480 + version = "0.17.14" 1481 + source = "registry+https://github.com/rust-lang/crates.io-index" 1482 + checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 1483 + dependencies = [ 1484 + "cc", 1485 + "cfg-if", 1486 + "getrandom 0.2.17", 1487 + "libc", 1488 + "untrusted", 1489 + "windows-sys 0.52.0", 1490 + ] 1491 + 1492 + [[package]] 1493 + name = "rsa" 1494 + version = "0.9.10" 1495 + source = "registry+https://github.com/rust-lang/crates.io-index" 1496 + checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" 1497 + dependencies = [ 1498 + "const-oid", 1499 + "digest", 1500 + "num-bigint-dig", 1501 + "num-integer", 1502 + "num-traits", 1503 + "pkcs1", 1504 + "pkcs8", 1505 + "rand_core 0.6.4", 1506 + "signature", 1507 + "spki", 1508 + "subtle", 1509 + "zeroize", 1510 + ] 1511 + 1512 + [[package]] 1513 + name = "rustc-hash" 1514 + version = "2.1.1" 1515 + source = "registry+https://github.com/rust-lang/crates.io-index" 1516 + checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 1517 + 1518 + [[package]] 1519 + name = "rustls" 1520 + version = "0.23.36" 1521 + source = "registry+https://github.com/rust-lang/crates.io-index" 1522 + checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" 1523 + dependencies = [ 1524 + "once_cell", 1525 + "ring", 1526 + "rustls-pki-types", 1527 + "rustls-webpki", 1528 + "subtle", 1529 + "zeroize", 1530 + ] 1531 + 1532 + [[package]] 1533 + name = "rustls-pki-types" 1534 + version = "1.14.0" 1535 + source = "registry+https://github.com/rust-lang/crates.io-index" 1536 + checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" 1537 + dependencies = [ 1538 + "web-time", 1539 + "zeroize", 1540 + ] 1541 + 1542 + [[package]] 1543 + name = "rustls-webpki" 1544 + version = "0.103.9" 1545 + source = "registry+https://github.com/rust-lang/crates.io-index" 1546 + checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" 1547 + dependencies = [ 1548 + "ring", 1549 + "rustls-pki-types", 1550 + "untrusted", 1551 + ] 1552 + 1553 + [[package]] 1554 + name = "rustversion" 1555 + version = "1.0.22" 1556 + source = "registry+https://github.com/rust-lang/crates.io-index" 1557 + checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 1558 + 1559 + [[package]] 1560 + name = "ryu" 1561 + version = "1.0.22" 1562 + source = "registry+https://github.com/rust-lang/crates.io-index" 1563 + checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" 1564 + 1565 + [[package]] 1566 + name = "scopeguard" 1567 + version = "1.2.0" 1568 + source = "registry+https://github.com/rust-lang/crates.io-index" 1569 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1570 + 1571 + [[package]] 1572 + name = "serde" 1573 + version = "1.0.228" 1574 + source = "registry+https://github.com/rust-lang/crates.io-index" 1575 + checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 1576 + dependencies = [ 1577 + "serde_core", 1578 + "serde_derive", 1579 + ] 1580 + 1581 + [[package]] 1582 + name = "serde_core" 1583 + version = "1.0.228" 1584 + source = "registry+https://github.com/rust-lang/crates.io-index" 1585 + checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 1586 + dependencies = [ 1587 + "serde_derive", 1588 + ] 1589 + 1590 + [[package]] 1591 + name = "serde_derive" 1592 + version = "1.0.228" 1593 + source = "registry+https://github.com/rust-lang/crates.io-index" 1594 + checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 1595 + dependencies = [ 1596 + "proc-macro2", 1597 + "quote", 1598 + "syn", 1599 + ] 1600 + 1601 + [[package]] 1602 + name = "serde_json" 1603 + version = "1.0.149" 1604 + source = "registry+https://github.com/rust-lang/crates.io-index" 1605 + checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" 1606 + dependencies = [ 1607 + "itoa", 1608 + "memchr", 1609 + "serde", 1610 + "serde_core", 1611 + "zmij", 1612 + ] 1613 + 1614 + [[package]] 1615 + name = "serde_path_to_error" 1616 + version = "0.1.20" 1617 + source = "registry+https://github.com/rust-lang/crates.io-index" 1618 + checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" 1619 + dependencies = [ 1620 + "itoa", 1621 + "serde", 1622 + "serde_core", 1623 + ] 1624 + 1625 + [[package]] 1626 + name = "serde_spanned" 1627 + version = "0.6.9" 1628 + source = "registry+https://github.com/rust-lang/crates.io-index" 1629 + checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" 1630 + dependencies = [ 1631 + "serde", 1632 + ] 1633 + 1634 + [[package]] 1635 + name = "serde_urlencoded" 1636 + version = "0.7.1" 1637 + source = "registry+https://github.com/rust-lang/crates.io-index" 1638 + checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1639 + dependencies = [ 1640 + "form_urlencoded", 1641 + "itoa", 1642 + "ryu", 1643 + "serde", 1644 + ] 1645 + 1646 + [[package]] 1647 + name = "sha1" 1648 + version = "0.10.6" 1649 + source = "registry+https://github.com/rust-lang/crates.io-index" 1650 + checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1651 + dependencies = [ 1652 + "cfg-if", 1653 + "cpufeatures", 1654 + "digest", 1655 + ] 1656 + 1657 + [[package]] 1658 + name = "sha2" 1659 + version = "0.10.9" 1660 + source = "registry+https://github.com/rust-lang/crates.io-index" 1661 + checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 1662 + dependencies = [ 1663 + "cfg-if", 1664 + "cpufeatures", 1665 + "digest", 1666 + ] 1667 + 1668 + [[package]] 1669 + name = "sharded-slab" 1670 + version = "0.1.7" 1671 + source = "registry+https://github.com/rust-lang/crates.io-index" 1672 + checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1673 + dependencies = [ 1674 + "lazy_static", 1675 + ] 1676 + 1677 + [[package]] 1678 + name = "shlex" 1679 + version = "1.3.0" 1680 + source = "registry+https://github.com/rust-lang/crates.io-index" 1681 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1682 + 1683 + [[package]] 1684 + name = "signal-hook-registry" 1685 + version = "1.4.8" 1686 + source = "registry+https://github.com/rust-lang/crates.io-index" 1687 + checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" 1688 + dependencies = [ 1689 + "errno", 1690 + "libc", 1691 + ] 1692 + 1693 + [[package]] 1694 + name = "signature" 1695 + version = "2.2.0" 1696 + source = "registry+https://github.com/rust-lang/crates.io-index" 1697 + checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" 1698 + dependencies = [ 1699 + "digest", 1700 + "rand_core 0.6.4", 1701 + ] 1702 + 1703 + [[package]] 1704 + name = "siphasher" 1705 + version = "1.0.2" 1706 + source = "registry+https://github.com/rust-lang/crates.io-index" 1707 + checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" 1708 + 1709 + [[package]] 1710 + name = "slab" 1711 + version = "0.4.11" 1712 + source = "registry+https://github.com/rust-lang/crates.io-index" 1713 + checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" 1714 + 1715 + [[package]] 1716 + name = "slurp" 1717 + version = "0.1.0" 1718 + dependencies = [ 1719 + "anyhow", 1720 + "axum", 1721 + "base64", 1722 + "chrono", 1723 + "clap", 1724 + "feed-rs", 1725 + "md-5", 1726 + "reqwest", 1727 + "serde", 1728 + "serde_json", 1729 + "serde_urlencoded", 1730 + "sqlx", 1731 + "tokio", 1732 + "toml", 1733 + "toml_edit", 1734 + "tracing", 1735 + "tracing-subscriber", 1736 + ] 1737 + 1738 + [[package]] 1739 + name = "smallvec" 1740 + version = "1.15.1" 1741 + source = "registry+https://github.com/rust-lang/crates.io-index" 1742 + checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 1743 + dependencies = [ 1744 + "serde", 1745 + ] 1746 + 1747 + [[package]] 1748 + name = "socket2" 1749 + version = "0.6.2" 1750 + source = "registry+https://github.com/rust-lang/crates.io-index" 1751 + checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" 1752 + dependencies = [ 1753 + "libc", 1754 + "windows-sys 0.60.2", 1755 + ] 1756 + 1757 + [[package]] 1758 + name = "spin" 1759 + version = "0.9.8" 1760 + source = "registry+https://github.com/rust-lang/crates.io-index" 1761 + checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1762 + dependencies = [ 1763 + "lock_api", 1764 + ] 1765 + 1766 + [[package]] 1767 + name = "spki" 1768 + version = "0.7.3" 1769 + source = "registry+https://github.com/rust-lang/crates.io-index" 1770 + checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" 1771 + dependencies = [ 1772 + "base64ct", 1773 + "der", 1774 + ] 1775 + 1776 + [[package]] 1777 + name = "sqlx" 1778 + version = "0.8.6" 1779 + source = "registry+https://github.com/rust-lang/crates.io-index" 1780 + checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" 1781 + dependencies = [ 1782 + "sqlx-core", 1783 + "sqlx-macros", 1784 + "sqlx-mysql", 1785 + "sqlx-postgres", 1786 + "sqlx-sqlite", 1787 + ] 1788 + 1789 + [[package]] 1790 + name = "sqlx-core" 1791 + version = "0.8.6" 1792 + source = "registry+https://github.com/rust-lang/crates.io-index" 1793 + checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" 1794 + dependencies = [ 1795 + "base64", 1796 + "bytes", 1797 + "crc", 1798 + "crossbeam-queue", 1799 + "either", 1800 + "event-listener", 1801 + "futures-core", 1802 + "futures-intrusive", 1803 + "futures-io", 1804 + "futures-util", 1805 + "hashbrown 0.15.5", 1806 + "hashlink", 1807 + "indexmap", 1808 + "log", 1809 + "memchr", 1810 + "once_cell", 1811 + "percent-encoding", 1812 + "serde", 1813 + "serde_json", 1814 + "sha2", 1815 + "smallvec", 1816 + "thiserror", 1817 + "tokio", 1818 + "tokio-stream", 1819 + "tracing", 1820 + "url", 1821 + ] 1822 + 1823 + [[package]] 1824 + name = "sqlx-macros" 1825 + version = "0.8.6" 1826 + source = "registry+https://github.com/rust-lang/crates.io-index" 1827 + checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" 1828 + dependencies = [ 1829 + "proc-macro2", 1830 + "quote", 1831 + "sqlx-core", 1832 + "sqlx-macros-core", 1833 + "syn", 1834 + ] 1835 + 1836 + [[package]] 1837 + name = "sqlx-macros-core" 1838 + version = "0.8.6" 1839 + source = "registry+https://github.com/rust-lang/crates.io-index" 1840 + checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" 1841 + dependencies = [ 1842 + "dotenvy", 1843 + "either", 1844 + "heck", 1845 + "hex", 1846 + "once_cell", 1847 + "proc-macro2", 1848 + "quote", 1849 + "serde", 1850 + "serde_json", 1851 + "sha2", 1852 + "sqlx-core", 1853 + "sqlx-mysql", 1854 + "sqlx-postgres", 1855 + "sqlx-sqlite", 1856 + "syn", 1857 + "tokio", 1858 + "url", 1859 + ] 1860 + 1861 + [[package]] 1862 + name = "sqlx-mysql" 1863 + version = "0.8.6" 1864 + source = "registry+https://github.com/rust-lang/crates.io-index" 1865 + checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" 1866 + dependencies = [ 1867 + "atoi", 1868 + "base64", 1869 + "bitflags", 1870 + "byteorder", 1871 + "bytes", 1872 + "crc", 1873 + "digest", 1874 + "dotenvy", 1875 + "either", 1876 + "futures-channel", 1877 + "futures-core", 1878 + "futures-io", 1879 + "futures-util", 1880 + "generic-array", 1881 + "hex", 1882 + "hkdf", 1883 + "hmac", 1884 + "itoa", 1885 + "log", 1886 + "md-5", 1887 + "memchr", 1888 + "once_cell", 1889 + "percent-encoding", 1890 + "rand 0.8.5", 1891 + "rsa", 1892 + "serde", 1893 + "sha1", 1894 + "sha2", 1895 + "smallvec", 1896 + "sqlx-core", 1897 + "stringprep", 1898 + "thiserror", 1899 + "tracing", 1900 + "whoami", 1901 + ] 1902 + 1903 + [[package]] 1904 + name = "sqlx-postgres" 1905 + version = "0.8.6" 1906 + source = "registry+https://github.com/rust-lang/crates.io-index" 1907 + checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" 1908 + dependencies = [ 1909 + "atoi", 1910 + "base64", 1911 + "bitflags", 1912 + "byteorder", 1913 + "crc", 1914 + "dotenvy", 1915 + "etcetera", 1916 + "futures-channel", 1917 + "futures-core", 1918 + "futures-util", 1919 + "hex", 1920 + "hkdf", 1921 + "hmac", 1922 + "home", 1923 + "itoa", 1924 + "log", 1925 + "md-5", 1926 + "memchr", 1927 + "once_cell", 1928 + "rand 0.8.5", 1929 + "serde", 1930 + "serde_json", 1931 + "sha2", 1932 + "smallvec", 1933 + "sqlx-core", 1934 + "stringprep", 1935 + "thiserror", 1936 + "tracing", 1937 + "whoami", 1938 + ] 1939 + 1940 + [[package]] 1941 + name = "sqlx-sqlite" 1942 + version = "0.8.6" 1943 + source = "registry+https://github.com/rust-lang/crates.io-index" 1944 + checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" 1945 + dependencies = [ 1946 + "atoi", 1947 + "flume", 1948 + "futures-channel", 1949 + "futures-core", 1950 + "futures-executor", 1951 + "futures-intrusive", 1952 + "futures-util", 1953 + "libsqlite3-sys", 1954 + "log", 1955 + "percent-encoding", 1956 + "serde", 1957 + "serde_urlencoded", 1958 + "sqlx-core", 1959 + "thiserror", 1960 + "tracing", 1961 + "url", 1962 + ] 1963 + 1964 + [[package]] 1965 + name = "stable_deref_trait" 1966 + version = "1.2.1" 1967 + source = "registry+https://github.com/rust-lang/crates.io-index" 1968 + checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 1969 + 1970 + [[package]] 1971 + name = "stringprep" 1972 + version = "0.1.5" 1973 + source = "registry+https://github.com/rust-lang/crates.io-index" 1974 + checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" 1975 + dependencies = [ 1976 + "unicode-bidi", 1977 + "unicode-normalization", 1978 + "unicode-properties", 1979 + ] 1980 + 1981 + [[package]] 1982 + name = "strsim" 1983 + version = "0.11.1" 1984 + source = "registry+https://github.com/rust-lang/crates.io-index" 1985 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1986 + 1987 + [[package]] 1988 + name = "subtle" 1989 + version = "2.6.1" 1990 + source = "registry+https://github.com/rust-lang/crates.io-index" 1991 + checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1992 + 1993 + [[package]] 1994 + name = "syn" 1995 + version = "2.0.114" 1996 + source = "registry+https://github.com/rust-lang/crates.io-index" 1997 + checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" 1998 + dependencies = [ 1999 + "proc-macro2", 2000 + "quote", 2001 + "unicode-ident", 2002 + ] 2003 + 2004 + [[package]] 2005 + name = "sync_wrapper" 2006 + version = "1.0.2" 2007 + source = "registry+https://github.com/rust-lang/crates.io-index" 2008 + checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 2009 + dependencies = [ 2010 + "futures-core", 2011 + ] 2012 + 2013 + [[package]] 2014 + name = "synstructure" 2015 + version = "0.13.2" 2016 + source = "registry+https://github.com/rust-lang/crates.io-index" 2017 + checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 2018 + dependencies = [ 2019 + "proc-macro2", 2020 + "quote", 2021 + "syn", 2022 + ] 2023 + 2024 + [[package]] 2025 + name = "thiserror" 2026 + version = "2.0.18" 2027 + source = "registry+https://github.com/rust-lang/crates.io-index" 2028 + checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" 2029 + dependencies = [ 2030 + "thiserror-impl", 2031 + ] 2032 + 2033 + [[package]] 2034 + name = "thiserror-impl" 2035 + version = "2.0.18" 2036 + source = "registry+https://github.com/rust-lang/crates.io-index" 2037 + checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" 2038 + dependencies = [ 2039 + "proc-macro2", 2040 + "quote", 2041 + "syn", 2042 + ] 2043 + 2044 + [[package]] 2045 + name = "thread_local" 2046 + version = "1.1.9" 2047 + source = "registry+https://github.com/rust-lang/crates.io-index" 2048 + checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" 2049 + dependencies = [ 2050 + "cfg-if", 2051 + ] 2052 + 2053 + [[package]] 2054 + name = "tinystr" 2055 + version = "0.8.2" 2056 + source = "registry+https://github.com/rust-lang/crates.io-index" 2057 + checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" 2058 + dependencies = [ 2059 + "displaydoc", 2060 + "zerovec", 2061 + ] 2062 + 2063 + [[package]] 2064 + name = "tinyvec" 2065 + version = "1.10.0" 2066 + source = "registry+https://github.com/rust-lang/crates.io-index" 2067 + checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" 2068 + dependencies = [ 2069 + "tinyvec_macros", 2070 + ] 2071 + 2072 + [[package]] 2073 + name = "tinyvec_macros" 2074 + version = "0.1.1" 2075 + source = "registry+https://github.com/rust-lang/crates.io-index" 2076 + checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 2077 + 2078 + [[package]] 2079 + name = "tokio" 2080 + version = "1.49.0" 2081 + source = "registry+https://github.com/rust-lang/crates.io-index" 2082 + checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" 2083 + dependencies = [ 2084 + "bytes", 2085 + "libc", 2086 + "mio", 2087 + "parking_lot", 2088 + "pin-project-lite", 2089 + "signal-hook-registry", 2090 + "socket2", 2091 + "tokio-macros", 2092 + "windows-sys 0.61.2", 2093 + ] 2094 + 2095 + [[package]] 2096 + name = "tokio-macros" 2097 + version = "2.6.0" 2098 + source = "registry+https://github.com/rust-lang/crates.io-index" 2099 + checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" 2100 + dependencies = [ 2101 + "proc-macro2", 2102 + "quote", 2103 + "syn", 2104 + ] 2105 + 2106 + [[package]] 2107 + name = "tokio-rustls" 2108 + version = "0.26.4" 2109 + source = "registry+https://github.com/rust-lang/crates.io-index" 2110 + checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" 2111 + dependencies = [ 2112 + "rustls", 2113 + "tokio", 2114 + ] 2115 + 2116 + [[package]] 2117 + name = "tokio-stream" 2118 + version = "0.1.18" 2119 + source = "registry+https://github.com/rust-lang/crates.io-index" 2120 + checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" 2121 + dependencies = [ 2122 + "futures-core", 2123 + "pin-project-lite", 2124 + "tokio", 2125 + ] 2126 + 2127 + [[package]] 2128 + name = "toml" 2129 + version = "0.8.23" 2130 + source = "registry+https://github.com/rust-lang/crates.io-index" 2131 + checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" 2132 + dependencies = [ 2133 + "serde", 2134 + "serde_spanned", 2135 + "toml_datetime", 2136 + "toml_edit", 2137 + ] 2138 + 2139 + [[package]] 2140 + name = "toml_datetime" 2141 + version = "0.6.11" 2142 + source = "registry+https://github.com/rust-lang/crates.io-index" 2143 + checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" 2144 + dependencies = [ 2145 + "serde", 2146 + ] 2147 + 2148 + [[package]] 2149 + name = "toml_edit" 2150 + version = "0.22.27" 2151 + source = "registry+https://github.com/rust-lang/crates.io-index" 2152 + checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" 2153 + dependencies = [ 2154 + "indexmap", 2155 + "serde", 2156 + "serde_spanned", 2157 + "toml_datetime", 2158 + "toml_write", 2159 + "winnow", 2160 + ] 2161 + 2162 + [[package]] 2163 + name = "toml_write" 2164 + version = "0.1.2" 2165 + source = "registry+https://github.com/rust-lang/crates.io-index" 2166 + checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" 2167 + 2168 + [[package]] 2169 + name = "tower" 2170 + version = "0.5.3" 2171 + source = "registry+https://github.com/rust-lang/crates.io-index" 2172 + checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" 2173 + dependencies = [ 2174 + "futures-core", 2175 + "futures-util", 2176 + "pin-project-lite", 2177 + "sync_wrapper", 2178 + "tokio", 2179 + "tower-layer", 2180 + "tower-service", 2181 + "tracing", 2182 + ] 2183 + 2184 + [[package]] 2185 + name = "tower-http" 2186 + version = "0.6.8" 2187 + source = "registry+https://github.com/rust-lang/crates.io-index" 2188 + checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" 2189 + dependencies = [ 2190 + "bitflags", 2191 + "bytes", 2192 + "futures-util", 2193 + "http", 2194 + "http-body", 2195 + "iri-string", 2196 + "pin-project-lite", 2197 + "tower", 2198 + "tower-layer", 2199 + "tower-service", 2200 + ] 2201 + 2202 + [[package]] 2203 + name = "tower-layer" 2204 + version = "0.3.3" 2205 + source = "registry+https://github.com/rust-lang/crates.io-index" 2206 + checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 2207 + 2208 + [[package]] 2209 + name = "tower-service" 2210 + version = "0.3.3" 2211 + source = "registry+https://github.com/rust-lang/crates.io-index" 2212 + checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 2213 + 2214 + [[package]] 2215 + name = "tracing" 2216 + version = "0.1.44" 2217 + source = "registry+https://github.com/rust-lang/crates.io-index" 2218 + checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" 2219 + dependencies = [ 2220 + "log", 2221 + "pin-project-lite", 2222 + "tracing-attributes", 2223 + "tracing-core", 2224 + ] 2225 + 2226 + [[package]] 2227 + name = "tracing-attributes" 2228 + version = "0.1.31" 2229 + source = "registry+https://github.com/rust-lang/crates.io-index" 2230 + checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" 2231 + dependencies = [ 2232 + "proc-macro2", 2233 + "quote", 2234 + "syn", 2235 + ] 2236 + 2237 + [[package]] 2238 + name = "tracing-core" 2239 + version = "0.1.36" 2240 + source = "registry+https://github.com/rust-lang/crates.io-index" 2241 + checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" 2242 + dependencies = [ 2243 + "once_cell", 2244 + "valuable", 2245 + ] 2246 + 2247 + [[package]] 2248 + name = "tracing-log" 2249 + version = "0.2.0" 2250 + source = "registry+https://github.com/rust-lang/crates.io-index" 2251 + checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 2252 + dependencies = [ 2253 + "log", 2254 + "once_cell", 2255 + "tracing-core", 2256 + ] 2257 + 2258 + [[package]] 2259 + name = "tracing-subscriber" 2260 + version = "0.3.22" 2261 + source = "registry+https://github.com/rust-lang/crates.io-index" 2262 + checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" 2263 + dependencies = [ 2264 + "nu-ansi-term", 2265 + "sharded-slab", 2266 + "smallvec", 2267 + "thread_local", 2268 + "tracing-core", 2269 + "tracing-log", 2270 + ] 2271 + 2272 + [[package]] 2273 + name = "try-lock" 2274 + version = "0.2.5" 2275 + source = "registry+https://github.com/rust-lang/crates.io-index" 2276 + checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 2277 + 2278 + [[package]] 2279 + name = "typenum" 2280 + version = "1.19.0" 2281 + source = "registry+https://github.com/rust-lang/crates.io-index" 2282 + checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 2283 + 2284 + [[package]] 2285 + name = "unicode-bidi" 2286 + version = "0.3.18" 2287 + source = "registry+https://github.com/rust-lang/crates.io-index" 2288 + checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" 2289 + 2290 + [[package]] 2291 + name = "unicode-ident" 2292 + version = "1.0.22" 2293 + source = "registry+https://github.com/rust-lang/crates.io-index" 2294 + checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 2295 + 2296 + [[package]] 2297 + name = "unicode-normalization" 2298 + version = "0.1.25" 2299 + source = "registry+https://github.com/rust-lang/crates.io-index" 2300 + checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" 2301 + dependencies = [ 2302 + "tinyvec", 2303 + ] 2304 + 2305 + [[package]] 2306 + name = "unicode-properties" 2307 + version = "0.1.4" 2308 + source = "registry+https://github.com/rust-lang/crates.io-index" 2309 + checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" 2310 + 2311 + [[package]] 2312 + name = "untrusted" 2313 + version = "0.9.0" 2314 + source = "registry+https://github.com/rust-lang/crates.io-index" 2315 + checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 2316 + 2317 + [[package]] 2318 + name = "url" 2319 + version = "2.5.8" 2320 + source = "registry+https://github.com/rust-lang/crates.io-index" 2321 + checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" 2322 + dependencies = [ 2323 + "form_urlencoded", 2324 + "idna", 2325 + "percent-encoding", 2326 + "serde", 2327 + "serde_derive", 2328 + ] 2329 + 2330 + [[package]] 2331 + name = "utf8_iter" 2332 + version = "1.0.4" 2333 + source = "registry+https://github.com/rust-lang/crates.io-index" 2334 + checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 2335 + 2336 + [[package]] 2337 + name = "utf8parse" 2338 + version = "0.2.2" 2339 + source = "registry+https://github.com/rust-lang/crates.io-index" 2340 + checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 2341 + 2342 + [[package]] 2343 + name = "uuid" 2344 + version = "1.20.0" 2345 + source = "registry+https://github.com/rust-lang/crates.io-index" 2346 + checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" 2347 + dependencies = [ 2348 + "getrandom 0.3.4", 2349 + "js-sys", 2350 + "wasm-bindgen", 2351 + ] 2352 + 2353 + [[package]] 2354 + name = "valuable" 2355 + version = "0.1.1" 2356 + source = "registry+https://github.com/rust-lang/crates.io-index" 2357 + checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 2358 + 2359 + [[package]] 2360 + name = "vcpkg" 2361 + version = "0.2.15" 2362 + source = "registry+https://github.com/rust-lang/crates.io-index" 2363 + checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2364 + 2365 + [[package]] 2366 + name = "version_check" 2367 + version = "0.9.5" 2368 + source = "registry+https://github.com/rust-lang/crates.io-index" 2369 + checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2370 + 2371 + [[package]] 2372 + name = "want" 2373 + version = "0.3.1" 2374 + source = "registry+https://github.com/rust-lang/crates.io-index" 2375 + checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 2376 + dependencies = [ 2377 + "try-lock", 2378 + ] 2379 + 2380 + [[package]] 2381 + name = "wasi" 2382 + version = "0.11.1+wasi-snapshot-preview1" 2383 + source = "registry+https://github.com/rust-lang/crates.io-index" 2384 + checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 2385 + 2386 + [[package]] 2387 + name = "wasip2" 2388 + version = "1.0.2+wasi-0.2.9" 2389 + source = "registry+https://github.com/rust-lang/crates.io-index" 2390 + checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" 2391 + dependencies = [ 2392 + "wit-bindgen", 2393 + ] 2394 + 2395 + [[package]] 2396 + name = "wasite" 2397 + version = "0.1.0" 2398 + source = "registry+https://github.com/rust-lang/crates.io-index" 2399 + checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" 2400 + 2401 + [[package]] 2402 + name = "wasm-bindgen" 2403 + version = "0.2.108" 2404 + source = "registry+https://github.com/rust-lang/crates.io-index" 2405 + checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" 2406 + dependencies = [ 2407 + "cfg-if", 2408 + "once_cell", 2409 + "rustversion", 2410 + "wasm-bindgen-macro", 2411 + "wasm-bindgen-shared", 2412 + ] 2413 + 2414 + [[package]] 2415 + name = "wasm-bindgen-futures" 2416 + version = "0.4.58" 2417 + source = "registry+https://github.com/rust-lang/crates.io-index" 2418 + checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" 2419 + dependencies = [ 2420 + "cfg-if", 2421 + "futures-util", 2422 + "js-sys", 2423 + "once_cell", 2424 + "wasm-bindgen", 2425 + "web-sys", 2426 + ] 2427 + 2428 + [[package]] 2429 + name = "wasm-bindgen-macro" 2430 + version = "0.2.108" 2431 + source = "registry+https://github.com/rust-lang/crates.io-index" 2432 + checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" 2433 + dependencies = [ 2434 + "quote", 2435 + "wasm-bindgen-macro-support", 2436 + ] 2437 + 2438 + [[package]] 2439 + name = "wasm-bindgen-macro-support" 2440 + version = "0.2.108" 2441 + source = "registry+https://github.com/rust-lang/crates.io-index" 2442 + checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" 2443 + dependencies = [ 2444 + "bumpalo", 2445 + "proc-macro2", 2446 + "quote", 2447 + "syn", 2448 + "wasm-bindgen-shared", 2449 + ] 2450 + 2451 + [[package]] 2452 + name = "wasm-bindgen-shared" 2453 + version = "0.2.108" 2454 + source = "registry+https://github.com/rust-lang/crates.io-index" 2455 + checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" 2456 + dependencies = [ 2457 + "unicode-ident", 2458 + ] 2459 + 2460 + [[package]] 2461 + name = "web-sys" 2462 + version = "0.3.85" 2463 + source = "registry+https://github.com/rust-lang/crates.io-index" 2464 + checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" 2465 + dependencies = [ 2466 + "js-sys", 2467 + "wasm-bindgen", 2468 + ] 2469 + 2470 + [[package]] 2471 + name = "web-time" 2472 + version = "1.1.0" 2473 + source = "registry+https://github.com/rust-lang/crates.io-index" 2474 + checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 2475 + dependencies = [ 2476 + "js-sys", 2477 + "wasm-bindgen", 2478 + ] 2479 + 2480 + [[package]] 2481 + name = "webpki-roots" 2482 + version = "1.0.5" 2483 + source = "registry+https://github.com/rust-lang/crates.io-index" 2484 + checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" 2485 + dependencies = [ 2486 + "rustls-pki-types", 2487 + ] 2488 + 2489 + [[package]] 2490 + name = "whoami" 2491 + version = "1.6.1" 2492 + source = "registry+https://github.com/rust-lang/crates.io-index" 2493 + checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" 2494 + dependencies = [ 2495 + "libredox", 2496 + "wasite", 2497 + ] 2498 + 2499 + [[package]] 2500 + name = "windows-core" 2501 + version = "0.62.2" 2502 + source = "registry+https://github.com/rust-lang/crates.io-index" 2503 + checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" 2504 + dependencies = [ 2505 + "windows-implement", 2506 + "windows-interface", 2507 + "windows-link", 2508 + "windows-result", 2509 + "windows-strings", 2510 + ] 2511 + 2512 + [[package]] 2513 + name = "windows-implement" 2514 + version = "0.60.2" 2515 + source = "registry+https://github.com/rust-lang/crates.io-index" 2516 + checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" 2517 + dependencies = [ 2518 + "proc-macro2", 2519 + "quote", 2520 + "syn", 2521 + ] 2522 + 2523 + [[package]] 2524 + name = "windows-interface" 2525 + version = "0.59.3" 2526 + source = "registry+https://github.com/rust-lang/crates.io-index" 2527 + checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" 2528 + dependencies = [ 2529 + "proc-macro2", 2530 + "quote", 2531 + "syn", 2532 + ] 2533 + 2534 + [[package]] 2535 + name = "windows-link" 2536 + version = "0.2.1" 2537 + source = "registry+https://github.com/rust-lang/crates.io-index" 2538 + checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 2539 + 2540 + [[package]] 2541 + name = "windows-result" 2542 + version = "0.4.1" 2543 + source = "registry+https://github.com/rust-lang/crates.io-index" 2544 + checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" 2545 + dependencies = [ 2546 + "windows-link", 2547 + ] 2548 + 2549 + [[package]] 2550 + name = "windows-strings" 2551 + version = "0.5.1" 2552 + source = "registry+https://github.com/rust-lang/crates.io-index" 2553 + checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" 2554 + dependencies = [ 2555 + "windows-link", 2556 + ] 2557 + 2558 + [[package]] 2559 + name = "windows-sys" 2560 + version = "0.48.0" 2561 + source = "registry+https://github.com/rust-lang/crates.io-index" 2562 + checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 2563 + dependencies = [ 2564 + "windows-targets 0.48.5", 2565 + ] 2566 + 2567 + [[package]] 2568 + name = "windows-sys" 2569 + version = "0.52.0" 2570 + source = "registry+https://github.com/rust-lang/crates.io-index" 2571 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2572 + dependencies = [ 2573 + "windows-targets 0.52.6", 2574 + ] 2575 + 2576 + [[package]] 2577 + name = "windows-sys" 2578 + version = "0.60.2" 2579 + source = "registry+https://github.com/rust-lang/crates.io-index" 2580 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 2581 + dependencies = [ 2582 + "windows-targets 0.53.5", 2583 + ] 2584 + 2585 + [[package]] 2586 + name = "windows-sys" 2587 + version = "0.61.2" 2588 + source = "registry+https://github.com/rust-lang/crates.io-index" 2589 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 2590 + dependencies = [ 2591 + "windows-link", 2592 + ] 2593 + 2594 + [[package]] 2595 + name = "windows-targets" 2596 + version = "0.48.5" 2597 + source = "registry+https://github.com/rust-lang/crates.io-index" 2598 + checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2599 + dependencies = [ 2600 + "windows_aarch64_gnullvm 0.48.5", 2601 + "windows_aarch64_msvc 0.48.5", 2602 + "windows_i686_gnu 0.48.5", 2603 + "windows_i686_msvc 0.48.5", 2604 + "windows_x86_64_gnu 0.48.5", 2605 + "windows_x86_64_gnullvm 0.48.5", 2606 + "windows_x86_64_msvc 0.48.5", 2607 + ] 2608 + 2609 + [[package]] 2610 + name = "windows-targets" 2611 + version = "0.52.6" 2612 + source = "registry+https://github.com/rust-lang/crates.io-index" 2613 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2614 + dependencies = [ 2615 + "windows_aarch64_gnullvm 0.52.6", 2616 + "windows_aarch64_msvc 0.52.6", 2617 + "windows_i686_gnu 0.52.6", 2618 + "windows_i686_gnullvm 0.52.6", 2619 + "windows_i686_msvc 0.52.6", 2620 + "windows_x86_64_gnu 0.52.6", 2621 + "windows_x86_64_gnullvm 0.52.6", 2622 + "windows_x86_64_msvc 0.52.6", 2623 + ] 2624 + 2625 + [[package]] 2626 + name = "windows-targets" 2627 + version = "0.53.5" 2628 + source = "registry+https://github.com/rust-lang/crates.io-index" 2629 + checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" 2630 + dependencies = [ 2631 + "windows-link", 2632 + "windows_aarch64_gnullvm 0.53.1", 2633 + "windows_aarch64_msvc 0.53.1", 2634 + "windows_i686_gnu 0.53.1", 2635 + "windows_i686_gnullvm 0.53.1", 2636 + "windows_i686_msvc 0.53.1", 2637 + "windows_x86_64_gnu 0.53.1", 2638 + "windows_x86_64_gnullvm 0.53.1", 2639 + "windows_x86_64_msvc 0.53.1", 2640 + ] 2641 + 2642 + [[package]] 2643 + name = "windows_aarch64_gnullvm" 2644 + version = "0.48.5" 2645 + source = "registry+https://github.com/rust-lang/crates.io-index" 2646 + checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2647 + 2648 + [[package]] 2649 + name = "windows_aarch64_gnullvm" 2650 + version = "0.52.6" 2651 + source = "registry+https://github.com/rust-lang/crates.io-index" 2652 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2653 + 2654 + [[package]] 2655 + name = "windows_aarch64_gnullvm" 2656 + version = "0.53.1" 2657 + source = "registry+https://github.com/rust-lang/crates.io-index" 2658 + checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" 2659 + 2660 + [[package]] 2661 + name = "windows_aarch64_msvc" 2662 + version = "0.48.5" 2663 + source = "registry+https://github.com/rust-lang/crates.io-index" 2664 + checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2665 + 2666 + [[package]] 2667 + name = "windows_aarch64_msvc" 2668 + version = "0.52.6" 2669 + source = "registry+https://github.com/rust-lang/crates.io-index" 2670 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2671 + 2672 + [[package]] 2673 + name = "windows_aarch64_msvc" 2674 + version = "0.53.1" 2675 + source = "registry+https://github.com/rust-lang/crates.io-index" 2676 + checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" 2677 + 2678 + [[package]] 2679 + name = "windows_i686_gnu" 2680 + version = "0.48.5" 2681 + source = "registry+https://github.com/rust-lang/crates.io-index" 2682 + checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2683 + 2684 + [[package]] 2685 + name = "windows_i686_gnu" 2686 + version = "0.52.6" 2687 + source = "registry+https://github.com/rust-lang/crates.io-index" 2688 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2689 + 2690 + [[package]] 2691 + name = "windows_i686_gnu" 2692 + version = "0.53.1" 2693 + source = "registry+https://github.com/rust-lang/crates.io-index" 2694 + checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" 2695 + 2696 + [[package]] 2697 + name = "windows_i686_gnullvm" 2698 + version = "0.52.6" 2699 + source = "registry+https://github.com/rust-lang/crates.io-index" 2700 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2701 + 2702 + [[package]] 2703 + name = "windows_i686_gnullvm" 2704 + version = "0.53.1" 2705 + source = "registry+https://github.com/rust-lang/crates.io-index" 2706 + checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" 2707 + 2708 + [[package]] 2709 + name = "windows_i686_msvc" 2710 + version = "0.48.5" 2711 + source = "registry+https://github.com/rust-lang/crates.io-index" 2712 + checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2713 + 2714 + [[package]] 2715 + name = "windows_i686_msvc" 2716 + version = "0.52.6" 2717 + source = "registry+https://github.com/rust-lang/crates.io-index" 2718 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2719 + 2720 + [[package]] 2721 + name = "windows_i686_msvc" 2722 + version = "0.53.1" 2723 + source = "registry+https://github.com/rust-lang/crates.io-index" 2724 + checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" 2725 + 2726 + [[package]] 2727 + name = "windows_x86_64_gnu" 2728 + version = "0.48.5" 2729 + source = "registry+https://github.com/rust-lang/crates.io-index" 2730 + checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2731 + 2732 + [[package]] 2733 + name = "windows_x86_64_gnu" 2734 + version = "0.52.6" 2735 + source = "registry+https://github.com/rust-lang/crates.io-index" 2736 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2737 + 2738 + [[package]] 2739 + name = "windows_x86_64_gnu" 2740 + version = "0.53.1" 2741 + source = "registry+https://github.com/rust-lang/crates.io-index" 2742 + checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" 2743 + 2744 + [[package]] 2745 + name = "windows_x86_64_gnullvm" 2746 + version = "0.48.5" 2747 + source = "registry+https://github.com/rust-lang/crates.io-index" 2748 + checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2749 + 2750 + [[package]] 2751 + name = "windows_x86_64_gnullvm" 2752 + version = "0.52.6" 2753 + source = "registry+https://github.com/rust-lang/crates.io-index" 2754 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2755 + 2756 + [[package]] 2757 + name = "windows_x86_64_gnullvm" 2758 + version = "0.53.1" 2759 + source = "registry+https://github.com/rust-lang/crates.io-index" 2760 + checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" 2761 + 2762 + [[package]] 2763 + name = "windows_x86_64_msvc" 2764 + version = "0.48.5" 2765 + source = "registry+https://github.com/rust-lang/crates.io-index" 2766 + checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2767 + 2768 + [[package]] 2769 + name = "windows_x86_64_msvc" 2770 + version = "0.52.6" 2771 + source = "registry+https://github.com/rust-lang/crates.io-index" 2772 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2773 + 2774 + [[package]] 2775 + name = "windows_x86_64_msvc" 2776 + version = "0.53.1" 2777 + source = "registry+https://github.com/rust-lang/crates.io-index" 2778 + checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" 2779 + 2780 + [[package]] 2781 + name = "winnow" 2782 + version = "0.7.14" 2783 + source = "registry+https://github.com/rust-lang/crates.io-index" 2784 + checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" 2785 + dependencies = [ 2786 + "memchr", 2787 + ] 2788 + 2789 + [[package]] 2790 + name = "wit-bindgen" 2791 + version = "0.51.0" 2792 + source = "registry+https://github.com/rust-lang/crates.io-index" 2793 + checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" 2794 + 2795 + [[package]] 2796 + name = "writeable" 2797 + version = "0.6.2" 2798 + source = "registry+https://github.com/rust-lang/crates.io-index" 2799 + checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" 2800 + 2801 + [[package]] 2802 + name = "yoke" 2803 + version = "0.8.1" 2804 + source = "registry+https://github.com/rust-lang/crates.io-index" 2805 + checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" 2806 + dependencies = [ 2807 + "stable_deref_trait", 2808 + "yoke-derive", 2809 + "zerofrom", 2810 + ] 2811 + 2812 + [[package]] 2813 + name = "yoke-derive" 2814 + version = "0.8.1" 2815 + source = "registry+https://github.com/rust-lang/crates.io-index" 2816 + checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" 2817 + dependencies = [ 2818 + "proc-macro2", 2819 + "quote", 2820 + "syn", 2821 + "synstructure", 2822 + ] 2823 + 2824 + [[package]] 2825 + name = "zerocopy" 2826 + version = "0.8.35" 2827 + source = "registry+https://github.com/rust-lang/crates.io-index" 2828 + checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572" 2829 + dependencies = [ 2830 + "zerocopy-derive", 2831 + ] 2832 + 2833 + [[package]] 2834 + name = "zerocopy-derive" 2835 + version = "0.8.35" 2836 + source = "registry+https://github.com/rust-lang/crates.io-index" 2837 + checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22" 2838 + dependencies = [ 2839 + "proc-macro2", 2840 + "quote", 2841 + "syn", 2842 + ] 2843 + 2844 + [[package]] 2845 + name = "zerofrom" 2846 + version = "0.1.6" 2847 + source = "registry+https://github.com/rust-lang/crates.io-index" 2848 + checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 2849 + dependencies = [ 2850 + "zerofrom-derive", 2851 + ] 2852 + 2853 + [[package]] 2854 + name = "zerofrom-derive" 2855 + version = "0.1.6" 2856 + source = "registry+https://github.com/rust-lang/crates.io-index" 2857 + checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 2858 + dependencies = [ 2859 + "proc-macro2", 2860 + "quote", 2861 + "syn", 2862 + "synstructure", 2863 + ] 2864 + 2865 + [[package]] 2866 + name = "zeroize" 2867 + version = "1.8.2" 2868 + source = "registry+https://github.com/rust-lang/crates.io-index" 2869 + checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" 2870 + 2871 + [[package]] 2872 + name = "zerotrie" 2873 + version = "0.2.3" 2874 + source = "registry+https://github.com/rust-lang/crates.io-index" 2875 + checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" 2876 + dependencies = [ 2877 + "displaydoc", 2878 + "yoke", 2879 + "zerofrom", 2880 + ] 2881 + 2882 + [[package]] 2883 + name = "zerovec" 2884 + version = "0.11.5" 2885 + source = "registry+https://github.com/rust-lang/crates.io-index" 2886 + checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" 2887 + dependencies = [ 2888 + "yoke", 2889 + "zerofrom", 2890 + "zerovec-derive", 2891 + ] 2892 + 2893 + [[package]] 2894 + name = "zerovec-derive" 2895 + version = "0.11.2" 2896 + source = "registry+https://github.com/rust-lang/crates.io-index" 2897 + checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" 2898 + dependencies = [ 2899 + "proc-macro2", 2900 + "quote", 2901 + "syn", 2902 + ] 2903 + 2904 + [[package]] 2905 + name = "zmij" 2906 + version = "1.0.17" 2907 + source = "registry+https://github.com/rust-lang/crates.io-index" 2908 + checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439"
+23
Cargo.toml
··· 1 + [package] 2 + name = "slurp" 3 + version = "0.1.0" 4 + edition = "2024" 5 + 6 + [dependencies] 7 + axum = "0.8" 8 + tokio = { version = "1", features = ["full"] } 9 + reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] } 10 + sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite", "migrate"] } 11 + feed-rs = "2" 12 + clap = { version = "4.5", features = ["derive"] } 13 + serde = { version = "1", features = ["derive"] } 14 + serde_json = "1" 15 + serde_urlencoded = "0.7" 16 + toml = "0.8" 17 + toml_edit = "0.22" 18 + tracing = "0.1" 19 + tracing-subscriber = "0.3" 20 + md-5 = "0.10" 21 + base64 = "0.22" 22 + chrono = "0.4" 23 + anyhow = "1"
+251
SUMMARY.md
··· 1 + ## Project Summary: Custom RSS Reader 2 + 3 + ### Goals 4 + 5 + Build a lightweight, self-hosted RSS aggregator in Rust that serves feeds via the Fever API to external RSS readers (ReadKit on iOS). No web interface needed - the server is headless and solely provides API endpoints for feed consumption. 6 + 7 + ### Core Requirements 8 + 9 + **Feed Aggregation:** 10 + 11 + - Add RSS/Atom feeds by URL 12 + - Automatic polling and fetching (15min intervals) 13 + - Deduplication and storage in SQLite 14 + - Serve items chronologically 15 + 16 + **Email-to-RSS:** 17 + 18 + - Generate unique email addresses per newsletter "feed" 19 + - Accept incoming emails via SMTP or webhook 20 + - Parse newsletter content and extract links 21 + - Store as RSS items alongside regular feeds 22 + 23 + **API Compatibility:** 24 + 25 + - Implement Fever API (ReadKit supports this) 26 + - JSON responses with groups, feeds, items 27 + - Authentication via API key 28 + - No read/unread state tracking (client handles this) 29 + 30 + **Deployment:** 31 + 32 + - NixOS service on bigchungus (Minisforum UM790) 33 + - SQLite database in `/data/rss` 34 + - Exposed via Caddy reverse proxy 35 + - No web UI - purely API-driven 36 + 37 + ### Non-Requirements 38 + 39 + - Web interface for reading feeds 40 + - User accounts or multi-user support (single user) 41 + - Read/unread state synchronization 42 + - Mobile push notifications (ReadKit handles this) 43 + - Feed discovery or search features 44 + - Complex filtering or tagging 45 + 46 + ### Success Criteria 47 + 48 + ReadKit can connect, fetch all feeds (RSS + newsletters), and display items - all without touching the server's terminal. Client maintains its own read state locally. 49 + 50 + ## Architecture overview 51 + 52 + Looking at ReadKit's supported services, **Fever API** is the simplest to implement - it's a straightforward JSON API that many RSS readers support. 53 + 54 + ## Implementation Plan 55 + 56 + ### Architecture 57 + 58 + ``` 59 + ┌─────────────┐ 60 + │ ReadKit │──────HTTP───────┐ 61 + │ (iOS) │ │ 62 + └─────────────┘ ▼ 63 + ┌──────────────┐ 64 + │ Rust Server │ 65 + │ (axum) │ 66 + └──────┬───────┘ 67 + 68 + ┌───────────┼───────────┐ 69 + ▼ ▼ ▼ 70 + ┌─────────┐ ┌─────────┐ ┌─────────┐ 71 + │ SQLite │ │ Fetcher│ │ Email │ 72 + │ DB │ │ (cron) │ │ Handler │ 73 + └─────────┘ └─────────┘ └─────────┘ 74 + ``` 75 + 76 + ### Tech Stack 77 + 78 + | Component | Choice | Why | 79 + | ------------- | ------------------------ | ---------------------------------------- | 80 + | Web framework | **axum** | Ergonomic, Tower ecosystem, tokio-native | 81 + | DB | **SQLite** | Simple, NixOS-friendly, plenty for RSS | 82 + | RSS parsing | **feed-rs** | Best maintained Rust RSS/Atom parser | 83 + | HTTP client | **reqwest** | Standard choice | 84 + | Email parsing | **mail-parser** | Clean MIME parsing | 85 + | Scheduler | **tokio-cron-scheduler** | Background feed fetching | 86 + 87 + ### Phase 1: Core RSS Aggregator (1-2 days) 88 + 89 + **DB Schema:** 90 + 91 + ```sql 92 + CREATE TABLE feeds ( 93 + id INTEGER PRIMARY KEY, 94 + url TEXT UNIQUE NOT NULL, 95 + title TEXT, 96 + last_fetched INTEGER 97 + ); 98 + 99 + CREATE TABLE items ( 100 + id INTEGER PRIMARY KEY, 101 + feed_id INTEGER, 102 + guid TEXT UNIQUE NOT NULL, 103 + title TEXT, 104 + url TEXT, 105 + content TEXT, 106 + published INTEGER, 107 + is_read BOOLEAN DEFAULT 0 108 + ); 109 + ``` 110 + 111 + **Fever API endpoints to implement:** 112 + 113 + - `GET /fever/?api` - Returns groups, feeds, items 114 + - `POST /fever/?api&mark=item&as=read&id=X` - Mark as read 115 + - Auth via `?api_key=<hash>` 116 + 117 + **Tasks:** 118 + 119 + 1. Set up axum server with Fever routes 120 + 2. RSS fetcher that polls feeds every 15min 121 + 3. Basic dedup (by GUID) 122 + 4. Serve items via Fever JSON format 123 + 124 + ### Phase 2: Email-to-RSS (1 day) 125 + 126 + **Approach:** Generate unique email addresses per "feed" 127 + 128 + ``` 129 + newsletter-<uuid>@yourdomain.com 130 + ``` 131 + 132 + **Flow:** 133 + 134 + 1. User creates a newsletter "feed" → get email address 135 + 2. Forward newsletters to that address (via email forwarding rules or MX records) 136 + 3. Server receives email via SMTP → parse → store as RSS item 137 + 4. ReadKit sees it as another feed 138 + 139 + **Email handler:** 140 + 141 + ```rust 142 + // Parse incoming email 143 + let parsed = mail_parser::Message::parse(&raw_email)?; 144 + 145 + // Extract links from HTML body 146 + let html = parsed.html_body()?; 147 + let links = extract_links(html); // scraper crate 148 + 149 + // Create RSS item 150 + let item = Item { 151 + title: parsed.subject(), 152 + content: format_links_as_html(links), 153 + url: links[0], // first link as canonical 154 + published: parsed.date(), 155 + ... 156 + }; 157 + ``` 158 + 159 + ### Phase 3: NixOS Module (half day) 160 + 161 + ```nix 162 + # /etc/nixos/rss-reader.nix 163 + { config, pkgs, ... }: 164 + 165 + { 166 + systemd.services.rss-reader = { 167 + description = "Custom RSS Reader"; 168 + after = [ "network.target" ]; 169 + wantedBy = [ "multi-user.target" ]; 170 + 171 + serviceConfig = { 172 + ExecStart = "${pkgs.rss-reader}/bin/rss-reader"; 173 + User = "rss"; 174 + WorkingDirectory = "/data/rss"; 175 + Restart = "always"; 176 + }; 177 + }; 178 + 179 + users.users.rss = { 180 + isSystemUser = true; 181 + group = "rss"; 182 + home = "/data/rss"; 183 + }; 184 + 185 + networking.firewall.allowedTCPPorts = [ 8080 ]; 186 + } 187 + ``` 188 + 189 + ### Directory Structure 190 + 191 + ``` 192 + rss-reader/ 193 + ├── Cargo.toml 194 + ├── src/ 195 + │ ├── main.rs 196 + │ ├── api/ 197 + │ │ ├── fever.rs # Fever API implementation 198 + │ │ └── mod.rs 199 + │ ├── db/ 200 + │ │ ├── models.rs 201 + │ │ └── mod.rs 202 + │ ├── fetcher/ 203 + │ │ ├── rss.rs # RSS/Atom fetching 204 + │ │ └── email.rs # Email parsing 205 + │ └── lib.rs 206 + ├── migrations/ 207 + │ └── 001_initial.sql 208 + └── default.nix # Nix package definition 209 + ``` 210 + 211 + ### MVP Feature Checklist 212 + 213 + **Feed Management:** 214 + 215 + - [ ] Add RSS/Atom feed by URL 216 + - [ ] Auto-fetch every 15min 217 + - [ ] Dedupe by GUID 218 + 219 + **Reading:** 220 + 221 + - [ ] Fever API `/fever/?api` endpoint 222 + - [ ] Mark as read/unread 223 + - [ ] Basic auth (api_key in config) 224 + 225 + **Newsletter:** 226 + 227 + - [ ] Generate unique email per feed 228 + - [ ] SMTP server (or webhook receiver) 229 + - [ ] Parse HTML → extract links 230 + - [ ] Store as RSS item 231 + 232 + **NixOS:** 233 + 234 + - [ ] systemd service 235 + - [ ] SQLite in `/data/rss` 236 + - [ ] Reverse proxy via Caddy (already have this) 237 + 238 + ### Quick Start Commands 239 + 240 + ```nix 241 + # In your NixOS config 242 + virtualisation.oci-containers.containers.rss-reader = { 243 + image = "ghcr.io/yourusername/rss-reader:latest"; 244 + ports = [ "127.0.0.1:8080:8080" ]; 245 + volumes = [ "/data/rss:/data" ]; 246 + }; 247 + ``` 248 + 249 + Then point ReadKit to `https://rss.tymek.me/fever/` with your API key. 250 + 251 + Want me to start with a basic `Cargo.toml` and skeleton code for phase 1?
+34
migrations/001_initial.sql
··· 1 + CREATE TABLE IF NOT EXISTS groups ( 2 + id INTEGER PRIMARY KEY AUTOINCREMENT, 3 + name TEXT NOT NULL UNIQUE 4 + ); 5 + 6 + CREATE TABLE IF NOT EXISTS favicons ( 7 + id INTEGER PRIMARY KEY AUTOINCREMENT, 8 + data TEXT NOT NULL 9 + ); 10 + 11 + CREATE TABLE IF NOT EXISTS feeds ( 12 + id INTEGER PRIMARY KEY AUTOINCREMENT, 13 + url TEXT NOT NULL UNIQUE, 14 + title TEXT NOT NULL DEFAULT '', 15 + site_url TEXT NOT NULL DEFAULT '', 16 + favicon_id INTEGER REFERENCES favicons(id), 17 + group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE CASCADE, 18 + last_fetched_at INTEGER 19 + ); 20 + 21 + CREATE TABLE IF NOT EXISTS items ( 22 + id INTEGER PRIMARY KEY AUTOINCREMENT, 23 + feed_id INTEGER NOT NULL REFERENCES feeds(id) ON DELETE CASCADE, 24 + guid TEXT NOT NULL UNIQUE, 25 + title TEXT NOT NULL DEFAULT '', 26 + author TEXT NOT NULL DEFAULT '', 27 + url TEXT NOT NULL DEFAULT '', 28 + content TEXT NOT NULL DEFAULT '', 29 + published_at INTEGER, 30 + created_at INTEGER NOT NULL DEFAULT (unixepoch()) 31 + ); 32 + 33 + CREATE INDEX IF NOT EXISTS idx_items_feed_id ON items(feed_id); 34 + CREATE INDEX IF NOT EXISTS idx_items_guid ON items(guid);
+16
slurp.toml
··· 1 + [server] 2 + bind = "127.0.0.1:8080" 3 + api_key = "83137f7a8fd8a7cae994f09bec2115e1" 4 + 5 + [database] 6 + path = "slurp.db" 7 + 8 + [fetcher] 9 + interval_minutes = 30 10 + 11 + [[groups]] 12 + name = "tech" 13 + 14 + [[feeds]] 15 + url = "https://simonwillison.net/atom/everything/" 16 + group = "tech"
+86
src/api/fever.rs
··· 1 + use axum::body::Bytes; 2 + use axum::extract::{Query, State}; 3 + use axum::response::Json; 4 + use serde_json::{json, Value}; 5 + use std::collections::HashMap; 6 + 7 + use crate::db; 8 + use crate::server::AppState; 9 + 10 + pub async fn handler( 11 + State(state): State<AppState>, 12 + Query(params): Query<HashMap<String, String>>, 13 + body: Bytes, 14 + ) -> Json<Value> { 15 + // parse form body (may be empty for GET requests) 16 + let body_params: HashMap<String, String> = 17 + serde_urlencoded::from_bytes(&body).unwrap_or_default(); 18 + 19 + let api_key = body_params.get("api_key").cloned().unwrap_or_default(); 20 + let authed = api_key == state.api_key; 21 + 22 + let now = chrono::Utc::now().timestamp(); 23 + let mut response = json!({ 24 + "api_version": 3, 25 + "auth": if authed { 1 } else { 0 }, 26 + "last_refreshed_on_time": now, 27 + }); 28 + 29 + if !authed { 30 + return Json(response); 31 + } 32 + 33 + let pool = &state.pool; 34 + 35 + if params.contains_key("groups") { 36 + if let Ok(groups) = db::get_groups(pool).await { 37 + response["groups"] = json!(groups); 38 + } 39 + if let Ok(fg) = db::get_feeds_groups(pool).await { 40 + response["feeds_groups"] = json!(fg); 41 + } 42 + } 43 + 44 + if params.contains_key("feeds") { 45 + if let Ok(feeds) = db::get_feeds(pool).await { 46 + response["feeds"] = json!(feeds); 47 + } 48 + if let Ok(fg) = db::get_feeds_groups(pool).await { 49 + response["feeds_groups"] = json!(fg); 50 + } 51 + } 52 + 53 + if params.contains_key("items") { 54 + let since_id = params.get("since_id").and_then(|v| v.parse().ok()); 55 + let max_id = params.get("max_id").and_then(|v| v.parse().ok()); 56 + let with_ids: Option<Vec<i64>> = params.get("with_ids").map(|v| { 57 + v.split(',') 58 + .filter_map(|s| s.trim().parse().ok()) 59 + .collect() 60 + }); 61 + 62 + if let Ok(items) = db::get_items(pool, since_id, max_id, with_ids.as_deref()).await { 63 + response["items"] = json!(items); 64 + } 65 + } 66 + 67 + if params.contains_key("favicons") { 68 + if let Ok(favicons) = db::get_favicons(pool).await { 69 + response["favicons"] = json!(favicons); 70 + } 71 + } 72 + 73 + if params.contains_key("unread_item_ids") { 74 + if let Ok(ids) = db::get_unread_item_ids(pool).await { 75 + response["unread_item_ids"] = json!(ids); 76 + } 77 + } 78 + 79 + if params.contains_key("saved_item_ids") { 80 + response["saved_item_ids"] = json!(""); 81 + } 82 + 83 + // POST write ops (mark read/saved) accepted silently as no-ops 84 + 85 + Json(response) 86 + }
+1
src/api/mod.rs
··· 1 + pub mod fever;
+73
src/config.rs
··· 1 + use anyhow::{Context, Result, bail}; 2 + use serde::Deserialize; 3 + use std::collections::HashSet; 4 + use std::path::Path; 5 + 6 + #[derive(Debug, Deserialize)] 7 + pub struct Config { 8 + pub server: ServerConfig, 9 + pub database: DatabaseConfig, 10 + pub fetcher: FetcherConfig, 11 + #[serde(default)] 12 + pub groups: Vec<GroupConfig>, 13 + #[serde(default)] 14 + pub feeds: Vec<FeedConfig>, 15 + } 16 + 17 + #[derive(Debug, Deserialize)] 18 + pub struct ServerConfig { 19 + pub bind: String, 20 + pub api_key: String, 21 + } 22 + 23 + #[derive(Debug, Deserialize)] 24 + pub struct DatabaseConfig { 25 + pub path: String, 26 + } 27 + 28 + #[derive(Debug, Deserialize)] 29 + pub struct FetcherConfig { 30 + pub interval_minutes: u64, 31 + } 32 + 33 + #[derive(Debug, Deserialize)] 34 + pub struct GroupConfig { 35 + pub name: String, 36 + } 37 + 38 + #[derive(Debug, Deserialize)] 39 + pub struct FeedConfig { 40 + pub url: String, 41 + pub group: String, 42 + } 43 + 44 + impl Config { 45 + pub fn load(path: &Path) -> Result<Self> { 46 + let content = std::fs::read_to_string(path) 47 + .with_context(|| format!("reading config from {}", path.display()))?; 48 + let config: Config = toml::from_str(&content) 49 + .with_context(|| format!("parsing config from {}", path.display()))?; 50 + config.validate()?; 51 + Ok(config) 52 + } 53 + 54 + fn validate(&self) -> Result<()> { 55 + if self.server.api_key.is_empty() { 56 + bail!("server.api_key must not be empty"); 57 + } 58 + 59 + let group_names: HashSet<&str> = self.groups.iter().map(|g| g.name.as_str()).collect(); 60 + 61 + for feed in &self.feeds { 62 + if !group_names.contains(feed.group.as_str()) { 63 + bail!( 64 + "feed '{}' references unknown group '{}'", 65 + feed.url, 66 + feed.group 67 + ); 68 + } 69 + } 70 + 71 + Ok(()) 72 + } 73 + }
+342
src/db.rs
··· 1 + use anyhow::{Context, Result}; 2 + use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions}; 3 + use sqlx::{Row, SqlitePool}; 4 + use std::str::FromStr; 5 + 6 + use crate::config::Config; 7 + 8 + pub async fn init_pool(db_path: &str) -> Result<SqlitePool> { 9 + let options = SqliteConnectOptions::from_str(&format!("sqlite:{db_path}"))? 10 + .create_if_missing(true) 11 + .journal_mode(SqliteJournalMode::Wal) 12 + .foreign_keys(true); 13 + 14 + let pool = SqlitePoolOptions::new() 15 + .max_connections(5) 16 + .connect_with(options) 17 + .await 18 + .context("connecting to database")?; 19 + 20 + sqlx::migrate!("./migrations") 21 + .run(&pool) 22 + .await 23 + .context("running migrations")?; 24 + 25 + Ok(pool) 26 + } 27 + 28 + pub async fn sync_config(pool: &SqlitePool, config: &Config) -> Result<()> { 29 + let config_group_names: Vec<&str> = config.groups.iter().map(|g| g.name.as_str()).collect(); 30 + 31 + // remove groups not in config (CASCADE deletes their feeds and items) 32 + let existing_groups: Vec<(i64, String)> = sqlx::query_as("SELECT id, name FROM groups") 33 + .fetch_all(pool) 34 + .await?; 35 + 36 + for (id, name) in &existing_groups { 37 + if !config_group_names.contains(&name.as_str()) { 38 + sqlx::query("DELETE FROM groups WHERE id = ?") 39 + .bind(id) 40 + .execute(pool) 41 + .await?; 42 + } 43 + } 44 + 45 + // insert missing groups 46 + for group in &config.groups { 47 + sqlx::query("INSERT OR IGNORE INTO groups (name) VALUES (?)") 48 + .bind(&group.name) 49 + .execute(pool) 50 + .await?; 51 + } 52 + 53 + // remove feeds not in config 54 + let config_feed_urls: Vec<&str> = config.feeds.iter().map(|f| f.url.as_str()).collect(); 55 + 56 + let existing_feeds: Vec<(i64, String)> = sqlx::query_as("SELECT id, url FROM feeds") 57 + .fetch_all(pool) 58 + .await?; 59 + 60 + for (id, url) in &existing_feeds { 61 + if !config_feed_urls.contains(&url.as_str()) { 62 + sqlx::query("DELETE FROM feeds WHERE id = ?") 63 + .bind(id) 64 + .execute(pool) 65 + .await?; 66 + } 67 + } 68 + 69 + // insert missing feeds, updating group_id if feed already exists 70 + for feed in &config.feeds { 71 + let group_id: (i64,) = sqlx::query_as("SELECT id FROM groups WHERE name = ?") 72 + .bind(&feed.group) 73 + .fetch_one(pool) 74 + .await?; 75 + 76 + sqlx::query( 77 + "INSERT INTO feeds (url, group_id) VALUES (?, ?) ON CONFLICT(url) DO UPDATE SET group_id = excluded.group_id", 78 + ) 79 + .bind(&feed.url) 80 + .bind(group_id.0) 81 + .execute(pool) 82 + .await?; 83 + } 84 + 85 + Ok(()) 86 + } 87 + 88 + // -- Fever API query types -- 89 + 90 + #[derive(Debug, serde::Serialize)] 91 + pub struct Group { 92 + pub id: i64, 93 + pub title: String, 94 + } 95 + 96 + #[derive(Debug, serde::Serialize)] 97 + pub struct FeedGroup { 98 + pub group_id: i64, 99 + pub feed_ids: String, 100 + } 101 + 102 + #[derive(Debug, serde::Serialize)] 103 + pub struct Feed { 104 + pub id: i64, 105 + pub favicon_id: i64, 106 + pub title: String, 107 + pub url: String, 108 + pub site_url: String, 109 + pub is_spark: i32, 110 + pub last_updated_on_time: i64, 111 + } 112 + 113 + #[derive(Debug, serde::Serialize)] 114 + pub struct Item { 115 + pub id: i64, 116 + pub feed_id: i64, 117 + pub title: String, 118 + pub author: String, 119 + pub html: String, 120 + pub url: String, 121 + pub is_saved: i32, 122 + pub is_read: i32, 123 + pub created_on_time: i64, 124 + } 125 + 126 + #[derive(Debug, serde::Serialize)] 127 + pub struct Favicon { 128 + pub id: i64, 129 + pub data: String, 130 + } 131 + 132 + pub async fn get_groups(pool: &SqlitePool) -> Result<Vec<Group>> { 133 + let rows: Vec<(i64, String)> = sqlx::query_as("SELECT id, name FROM groups") 134 + .fetch_all(pool) 135 + .await?; 136 + Ok(rows 137 + .into_iter() 138 + .map(|(id, name)| Group { id, title: name }) 139 + .collect()) 140 + } 141 + 142 + pub async fn get_feeds_groups(pool: &SqlitePool) -> Result<Vec<FeedGroup>> { 143 + let rows = sqlx::query( 144 + "SELECT group_id, GROUP_CONCAT(id) as feed_ids FROM feeds GROUP BY group_id", 145 + ) 146 + .fetch_all(pool) 147 + .await?; 148 + 149 + Ok(rows 150 + .iter() 151 + .map(|row| FeedGroup { 152 + group_id: row.get("group_id"), 153 + feed_ids: row.get::<String, _>("feed_ids"), 154 + }) 155 + .collect()) 156 + } 157 + 158 + pub async fn get_feeds(pool: &SqlitePool) -> Result<Vec<Feed>> { 159 + let rows = sqlx::query( 160 + "SELECT id, COALESCE(favicon_id, 0) as favicon_id, title, url, site_url, \ 161 + COALESCE(last_fetched_at, 0) as last_fetched_at FROM feeds", 162 + ) 163 + .fetch_all(pool) 164 + .await?; 165 + 166 + Ok(rows 167 + .iter() 168 + .map(|row| Feed { 169 + id: row.get("id"), 170 + favicon_id: row.get("favicon_id"), 171 + title: row.get("title"), 172 + url: row.get("url"), 173 + site_url: row.get("site_url"), 174 + is_spark: 0, 175 + last_updated_on_time: row.get("last_fetched_at"), 176 + }) 177 + .collect()) 178 + } 179 + 180 + pub async fn get_items( 181 + pool: &SqlitePool, 182 + since_id: Option<i64>, 183 + max_id: Option<i64>, 184 + with_ids: Option<&[i64]>, 185 + ) -> Result<Vec<Item>> { 186 + let rows = if let Some(ids) = with_ids { 187 + let placeholders: Vec<&str> = ids.iter().map(|_| "?").collect(); 188 + let sql = format!( 189 + "SELECT id, feed_id, title, author, content, url, \ 190 + COALESCE(published_at, created_at) as ts \ 191 + FROM items WHERE id IN ({}) ORDER BY id DESC", 192 + placeholders.join(",") 193 + ); 194 + let mut query = sqlx::query(&sql); 195 + for id in ids { 196 + query = query.bind(id); 197 + } 198 + query.fetch_all(pool).await? 199 + } else if let Some(since) = since_id { 200 + sqlx::query( 201 + "SELECT id, feed_id, title, author, content, url, \ 202 + COALESCE(published_at, created_at) as ts \ 203 + FROM items WHERE id > ? ORDER BY id ASC LIMIT 50", 204 + ) 205 + .bind(since) 206 + .fetch_all(pool) 207 + .await? 208 + } else if let Some(max) = max_id { 209 + sqlx::query( 210 + "SELECT id, feed_id, title, author, content, url, \ 211 + COALESCE(published_at, created_at) as ts \ 212 + FROM items WHERE id < ? ORDER BY id DESC LIMIT 50", 213 + ) 214 + .bind(max) 215 + .fetch_all(pool) 216 + .await? 217 + } else { 218 + sqlx::query( 219 + "SELECT id, feed_id, title, author, content, url, \ 220 + COALESCE(published_at, created_at) as ts \ 221 + FROM items ORDER BY id DESC LIMIT 50", 222 + ) 223 + .fetch_all(pool) 224 + .await? 225 + }; 226 + 227 + Ok(rows 228 + .iter() 229 + .map(|row| Item { 230 + id: row.get("id"), 231 + feed_id: row.get("feed_id"), 232 + title: row.get("title"), 233 + author: row.get("author"), 234 + html: row.get("content"), 235 + url: row.get("url"), 236 + is_saved: 0, 237 + is_read: 0, 238 + created_on_time: row.get("ts"), 239 + }) 240 + .collect()) 241 + } 242 + 243 + pub async fn get_unread_item_ids(pool: &SqlitePool) -> Result<String> { 244 + let rows: Vec<(i64,)> = sqlx::query_as("SELECT id FROM items ORDER BY id") 245 + .fetch_all(pool) 246 + .await?; 247 + let ids: Vec<String> = rows.iter().map(|(id,)| id.to_string()).collect(); 248 + Ok(ids.join(",")) 249 + } 250 + 251 + pub async fn get_favicons(pool: &SqlitePool) -> Result<Vec<Favicon>> { 252 + let rows: Vec<(i64, String)> = sqlx::query_as("SELECT id, data FROM favicons") 253 + .fetch_all(pool) 254 + .await?; 255 + Ok(rows 256 + .into_iter() 257 + .map(|(id, data)| Favicon { id, data }) 258 + .collect()) 259 + } 260 + 261 + // -- Fetcher queries -- 262 + 263 + pub struct FeedRow { 264 + pub id: i64, 265 + pub url: String, 266 + pub favicon_id: Option<i64>, 267 + } 268 + 269 + pub async fn get_all_feeds(pool: &SqlitePool) -> Result<Vec<FeedRow>> { 270 + let rows: Vec<(i64, String, Option<i64>)> = 271 + sqlx::query_as("SELECT id, url, favicon_id FROM feeds") 272 + .fetch_all(pool) 273 + .await?; 274 + Ok(rows 275 + .into_iter() 276 + .map(|(id, url, favicon_id)| FeedRow { 277 + id, 278 + url, 279 + favicon_id, 280 + }) 281 + .collect()) 282 + } 283 + 284 + pub async fn upsert_feed_metadata( 285 + pool: &SqlitePool, 286 + feed_id: i64, 287 + title: &str, 288 + site_url: &str, 289 + ) -> Result<()> { 290 + sqlx::query( 291 + "UPDATE feeds SET title = ?, site_url = ?, last_fetched_at = unixepoch() WHERE id = ?", 292 + ) 293 + .bind(title) 294 + .bind(site_url) 295 + .bind(feed_id) 296 + .execute(pool) 297 + .await?; 298 + Ok(()) 299 + } 300 + 301 + pub async fn insert_favicon(pool: &SqlitePool, data: &str) -> Result<i64> { 302 + let result = sqlx::query("INSERT INTO favicons (data) VALUES (?)") 303 + .bind(data) 304 + .execute(pool) 305 + .await?; 306 + Ok(result.last_insert_rowid()) 307 + } 308 + 309 + pub async fn set_feed_favicon(pool: &SqlitePool, feed_id: i64, favicon_id: i64) -> Result<()> { 310 + sqlx::query("UPDATE feeds SET favicon_id = ? WHERE id = ?") 311 + .bind(favicon_id) 312 + .bind(feed_id) 313 + .execute(pool) 314 + .await?; 315 + Ok(()) 316 + } 317 + 318 + pub async fn insert_item( 319 + pool: &SqlitePool, 320 + feed_id: i64, 321 + guid: &str, 322 + title: &str, 323 + author: &str, 324 + url: &str, 325 + content: &str, 326 + published_at: Option<i64>, 327 + ) -> Result<()> { 328 + sqlx::query( 329 + "INSERT OR IGNORE INTO items (feed_id, guid, title, author, url, content, published_at) \ 330 + VALUES (?, ?, ?, ?, ?, ?, ?)", 331 + ) 332 + .bind(feed_id) 333 + .bind(guid) 334 + .bind(title) 335 + .bind(author) 336 + .bind(url) 337 + .bind(content) 338 + .bind(published_at) 339 + .execute(pool) 340 + .await?; 341 + Ok(()) 342 + }
+137
src/fetcher.rs
··· 1 + use anyhow::Result; 2 + use base64::Engine; 3 + use sqlx::SqlitePool; 4 + use std::time::Duration; 5 + use tracing::{info, warn}; 6 + 7 + use crate::db; 8 + 9 + pub async fn run(pool: SqlitePool, interval_minutes: u64) { 10 + let mut interval = tokio::time::interval(Duration::from_secs(interval_minutes * 60)); 11 + 12 + loop { 13 + interval.tick().await; 14 + info!("starting feed fetch cycle"); 15 + if let Err(e) = fetch_all(&pool).await { 16 + warn!("feed fetch cycle failed: {e:#}"); 17 + } 18 + } 19 + } 20 + 21 + async fn fetch_all(pool: &SqlitePool) -> Result<()> { 22 + let feeds = db::get_all_feeds(pool).await?; 23 + let client = reqwest::Client::builder() 24 + .timeout(Duration::from_secs(30)) 25 + .build()?; 26 + 27 + for feed in feeds { 28 + if let Err(e) = fetch_feed(pool, &client, &feed).await { 29 + warn!(feed_id = feed.id, url = %feed.url, "failed to fetch: {e:#}"); 30 + } 31 + } 32 + 33 + Ok(()) 34 + } 35 + 36 + async fn fetch_feed( 37 + pool: &SqlitePool, 38 + client: &reqwest::Client, 39 + feed: &db::FeedRow, 40 + ) -> Result<()> { 41 + info!(feed_id = feed.id, url = %feed.url, "fetching"); 42 + 43 + let response = client.get(&feed.url).send().await?.bytes().await?; 44 + let parsed = feed_rs::parser::parse(&response[..])?; 45 + 46 + let title = parsed.title.map(|t| t.content).unwrap_or_default(); 47 + let site_url = parsed 48 + .links 49 + .first() 50 + .map(|l| l.href.clone()) 51 + .unwrap_or_default(); 52 + 53 + db::upsert_feed_metadata(pool, feed.id, &title, &site_url).await?; 54 + 55 + // fetch favicon if missing 56 + if feed.favicon_id.is_none() { 57 + let icon_url = parsed 58 + .icon 59 + .map(|i| i.uri) 60 + .or_else(|| parsed.logo.map(|l| l.uri)) 61 + .or_else(|| { 62 + if site_url.is_empty() { 63 + None 64 + } else { 65 + let base = site_url.trim_end_matches('/'); 66 + Some(format!("{base}/favicon.ico")) 67 + } 68 + }); 69 + 70 + if let Some(url) = icon_url { 71 + match fetch_favicon(client, &url).await { 72 + Ok(data) => { 73 + let favicon_id = db::insert_favicon(pool, &data).await?; 74 + db::set_feed_favicon(pool, feed.id, favicon_id).await?; 75 + } 76 + Err(e) => warn!(feed_id = feed.id, "favicon fetch failed: {e:#}"), 77 + } 78 + } 79 + } 80 + 81 + for entry in parsed.entries { 82 + let guid = entry.id; 83 + let entry_title = entry.title.map(|t| t.content).unwrap_or_default(); 84 + let author = entry 85 + .authors 86 + .first() 87 + .map(|a| a.name.clone()) 88 + .unwrap_or_default(); 89 + let url = entry 90 + .links 91 + .first() 92 + .map(|l| l.href.clone()) 93 + .unwrap_or_default(); 94 + let content = entry 95 + .content 96 + .and_then(|c| c.body) 97 + .or_else(|| entry.summary.map(|s| s.content)) 98 + .unwrap_or_default(); 99 + let published_at = entry 100 + .published 101 + .or(entry.updated) 102 + .map(|dt| dt.timestamp()); 103 + 104 + db::insert_item( 105 + pool, 106 + feed.id, 107 + &guid, 108 + &entry_title, 109 + &author, 110 + &url, 111 + &content, 112 + published_at, 113 + ) 114 + .await?; 115 + } 116 + 117 + info!(feed_id = feed.id, "done"); 118 + Ok(()) 119 + } 120 + 121 + async fn fetch_favicon(client: &reqwest::Client, url: &str) -> Result<String> { 122 + let response = client.get(url).send().await?; 123 + let content_type = response 124 + .headers() 125 + .get("content-type") 126 + .and_then(|v| v.to_str().ok()) 127 + .unwrap_or("image/x-icon") 128 + .to_string(); 129 + let mime = content_type 130 + .split(';') 131 + .next() 132 + .unwrap_or("image/x-icon") 133 + .trim(); 134 + let bytes = response.bytes().await?; 135 + let encoded = base64::engine::general_purpose::STANDARD.encode(&bytes); 136 + Ok(format!("{mime};base64,{encoded}")) 137 + }
+117
src/main.rs
··· 1 + mod api; 2 + mod config; 3 + mod db; 4 + mod fetcher; 5 + mod server; 6 + 7 + use anyhow::Result; 8 + use clap::{Parser, Subcommand}; 9 + use md5::{Digest, Md5}; 10 + use std::path::PathBuf; 11 + use toml_edit::{DocumentMut, Item, Table}; 12 + 13 + #[derive(Parser)] 14 + #[command(name = "slurp", about = "Headless RSS aggregator")] 15 + struct Cli { 16 + #[command(subcommand)] 17 + command: Command, 18 + } 19 + 20 + #[derive(Subcommand)] 21 + enum Command { 22 + /// Start the server and fetcher 23 + Serve { 24 + #[arg(short, long, default_value = "slurp.toml")] 25 + config: PathBuf, 26 + }, 27 + /// Generate API key hash from email and password 28 + Auth { email: String, password: String }, 29 + /// Add a feed to the config file 30 + Add { 31 + url: String, 32 + group: String, 33 + #[arg(short, long, default_value = "slurp.toml")] 34 + config: PathBuf, 35 + }, 36 + } 37 + 38 + #[tokio::main] 39 + async fn main() -> Result<()> { 40 + tracing_subscriber::fmt::init(); 41 + 42 + let cli = Cli::parse(); 43 + 44 + match cli.command { 45 + Command::Serve { config: path } => { 46 + let config = config::Config::load(&path)?; 47 + let pool = db::init_pool(&config.database.path).await?; 48 + db::sync_config(&pool, &config).await?; 49 + 50 + let state = server::AppState { 51 + pool: pool.clone(), 52 + api_key: config.server.api_key.clone(), 53 + }; 54 + 55 + let app = server::router(state); 56 + let listener = tokio::net::TcpListener::bind(&config.server.bind).await?; 57 + tracing::info!("listening on {}", config.server.bind); 58 + 59 + tokio::spawn(fetcher::run(pool, config.fetcher.interval_minutes)); 60 + 61 + axum::serve(listener, app).await?; 62 + } 63 + Command::Auth { email, password } => { 64 + let input = format!("{email}:{password}"); 65 + let hash = format!("{:x}", Md5::digest(input.as_bytes())); 66 + println!("{hash}"); 67 + } 68 + Command::Add { url, group, config } => { 69 + let content = if config.exists() { 70 + std::fs::read_to_string(&config)? 71 + } else { 72 + // create minimal config skeleton 73 + r#"[server] 74 + bind = "127.0.0.1:8080" 75 + api_key = "CHANGE_ME" 76 + 77 + [database] 78 + path = "slurp.db" 79 + 80 + [fetcher] 81 + interval_minutes = 30 82 + "# 83 + .to_string() 84 + }; 85 + let mut doc: DocumentMut = content.parse()?; 86 + 87 + // ensure [[groups]] array exists and contains the group 88 + if !doc.contains_key("groups") { 89 + doc["groups"] = Item::ArrayOfTables(toml_edit::ArrayOfTables::new()); 90 + } 91 + let groups = doc["groups"].as_array_of_tables_mut().unwrap(); 92 + let group_exists = groups 93 + .iter() 94 + .any(|t| t.get("name").and_then(|v| v.as_str()) == Some(&group)); 95 + if !group_exists { 96 + let mut new_group = Table::new(); 97 + new_group["name"] = toml_edit::value(&group); 98 + groups.push(new_group); 99 + } 100 + 101 + // ensure [[feeds]] array exists and add the feed 102 + if !doc.contains_key("feeds") { 103 + doc["feeds"] = Item::ArrayOfTables(toml_edit::ArrayOfTables::new()); 104 + } 105 + let feeds = doc["feeds"].as_array_of_tables_mut().unwrap(); 106 + let mut new_feed = Table::new(); 107 + new_feed["url"] = toml_edit::value(&url); 108 + new_feed["group"] = toml_edit::value(&group); 109 + feeds.push(new_feed); 110 + 111 + std::fs::write(&config, doc.to_string())?; 112 + println!("Added feed {} to group {}", url, group); 113 + } 114 + } 115 + 116 + Ok(()) 117 + }
+25
src/server.rs
··· 1 + use axum::routing::get; 2 + use axum::Router; 3 + use sqlx::SqlitePool; 4 + 5 + use crate::api; 6 + 7 + #[derive(Clone)] 8 + pub struct AppState { 9 + pub pool: SqlitePool, 10 + pub api_key: String, 11 + } 12 + 13 + pub fn router(state: AppState) -> Router { 14 + Router::new() 15 + .route("/", get(api::fever::handler).post(api::fever::handler)) 16 + .route( 17 + "/fever", 18 + get(api::fever::handler).post(api::fever::handler), 19 + ) 20 + .route( 21 + "/fever/", 22 + get(api::fever::handler).post(api::fever::handler), 23 + ) 24 + .with_state(state) 25 + }