grain.social is a photo sharing platform built on atproto.

feat: darkroom service (#6)

* feat: add cascade option to deleteGallery and deletePhoto procedures to remove associated items

* get darkroom service working, testing deploy

* add workflow

* test deploy

* update nix

* use env var for chrome path

* update workflow

* fix /tmp dir

* fix

* try single process

* try this

* use internal address

* add env var for base_url, remove some chrome args

* update flake and chrome args

* use fantoccini and chromedriver

* change port

* ok try this

* fix start script

* use writeShellScript

* update

* try this

* fix: update start script path in Docker configuration

* fix: simplify start script installation in Nix derivation

* oof

* try this

* wait for fonts

* try to fix font rendering

* ok this

* add corefonts pkg

* allow unfree

* try dejavu and libreration fonts

* add fontconfig

* refactor: update darkroom service to use minijinja for HTML generation and remove deprecated scripts

* include templates in nix

authored by chadtmiller.com and committed by GitHub 179c3fef 1baae70f

+76
.github/workflows/darkroom.yml
··· 1 + name: Build and Deploy Darkroom 2 + 3 + on: 4 + pull_request: 5 + branches: [ main ] 6 + paths: [ 'services/darkroom/**' ] 7 + 8 + jobs: 9 + build: 10 + runs-on: ubuntu-latest 11 + defaults: 12 + run: 13 + working-directory: services/darkroom 14 + 15 + steps: 16 + - name: Checkout code 17 + uses: actions/checkout@v4 18 + 19 + - name: Install Nix 20 + uses: DeterminateSystems/nix-installer-action@main 21 + with: 22 + logger: pretty 23 + 24 + - name: Setup Nix cache 25 + uses: DeterminateSystems/magic-nix-cache-action@main 26 + 27 + - name: Check flake 28 + run: nix flake check 29 + 30 + - name: Build Rust binary 31 + run: nix build .#darkroom 32 + 33 + - name: Build Docker image 34 + run: nix build .#darkroomImg 35 + 36 + - name: Load Docker image 37 + run: docker load < result 38 + 39 + - name: Test Docker image 40 + run: | 41 + # Start the container in the background 42 + docker run -d --name darkroom-test -p 8080:8080 darkroom:latest 43 + 44 + # Wait for the service to start 45 + sleep 10 46 + 47 + # Test the health endpoint 48 + curl -f http://localhost:8080/health || exit 1 49 + 50 + # Stop the test container 51 + docker stop darkroom-test 52 + docker rm darkroom-test 53 + 54 + - name: Tag for Fly.io 55 + if: github.ref == 'refs/heads/main' 56 + run: docker tag darkroom:latest registry.fly.io/grain-darkroom:latest 57 + 58 + - name: Setup Fly CLI 59 + if: github.ref == 'refs/heads/main' 60 + uses: superfly/flyctl-actions/setup-flyctl@master 61 + 62 + - name: Login to Fly.io registry 63 + if: github.ref == 'refs/heads/main' 64 + run: flyctl auth docker 65 + env: 66 + FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} 67 + 68 + - name: Push to Fly.io registry 69 + if: github.ref == 'refs/heads/main' 70 + run: docker push registry.fly.io/grain-darkroom:latest 71 + 72 + - name: Deploy to Fly.io 73 + if: github.ref == 'refs/heads/main' 74 + run: flyctl deploy --image registry.fly.io/grain-darkroom:latest 75 + env: 76 + FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+2 -1
.vscode/settings.json
··· 26 26 }, 27 27 "editor.codeActionsOnSave": { 28 28 "source.organizeImports": "explicit" 29 - } 29 + }, 30 + "rust-analyzer.linkedProjects": ["services/darkroom/Cargo.toml"] 30 31 }
+24
lexicons/social/grain/darkroom/getGalleryComposite.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "social.grain.darkroom.getGalleryComposite", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "Returns a composite image for a specified gallery AT-URI.", 8 + "parameters": { 9 + "type": "params", 10 + "required": ["uri"], 11 + "properties": { 12 + "uri": { 13 + "type": "string", 14 + "description": "The AT-URI of the gallery to return a composite for.", 15 + "format": "at-uri" 16 + } 17 + } 18 + }, 19 + "output": { 20 + "encoding": "*/*" 21 + } 22 + } 23 + } 24 + }
+2
services/darkroom/.gitignore
··· 1 + /target 2 + /result
+2388
services/darkroom/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 = "addr2line" 7 + version = "0.24.2" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 + dependencies = [ 11 + "gimli", 12 + ] 13 + 14 + [[package]] 15 + name = "adler2" 16 + version = "2.0.1" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" 19 + 20 + [[package]] 21 + name = "android-tzdata" 22 + version = "0.1.1" 23 + source = "registry+https://github.com/rust-lang/crates.io-index" 24 + checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 25 + 26 + [[package]] 27 + name = "android_system_properties" 28 + version = "0.1.5" 29 + source = "registry+https://github.com/rust-lang/crates.io-index" 30 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 31 + dependencies = [ 32 + "libc", 33 + ] 34 + 35 + [[package]] 36 + name = "anyhow" 37 + version = "1.0.98" 38 + source = "registry+https://github.com/rust-lang/crates.io-index" 39 + checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 40 + 41 + [[package]] 42 + name = "async-trait" 43 + version = "0.1.88" 44 + source = "registry+https://github.com/rust-lang/crates.io-index" 45 + checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" 46 + dependencies = [ 47 + "proc-macro2", 48 + "quote", 49 + "syn", 50 + ] 51 + 52 + [[package]] 53 + name = "atomic-waker" 54 + version = "1.1.2" 55 + source = "registry+https://github.com/rust-lang/crates.io-index" 56 + checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 57 + 58 + [[package]] 59 + name = "autocfg" 60 + version = "1.5.0" 61 + source = "registry+https://github.com/rust-lang/crates.io-index" 62 + checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 63 + 64 + [[package]] 65 + name = "axum" 66 + version = "0.7.9" 67 + source = "registry+https://github.com/rust-lang/crates.io-index" 68 + checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" 69 + dependencies = [ 70 + "async-trait", 71 + "axum-core", 72 + "bytes", 73 + "futures-util", 74 + "http 1.3.1", 75 + "http-body", 76 + "http-body-util", 77 + "hyper", 78 + "hyper-util", 79 + "itoa", 80 + "matchit", 81 + "memchr", 82 + "mime", 83 + "percent-encoding", 84 + "pin-project-lite", 85 + "rustversion", 86 + "serde", 87 + "serde_json", 88 + "serde_path_to_error", 89 + "serde_urlencoded", 90 + "sync_wrapper", 91 + "tokio", 92 + "tower 0.5.2", 93 + "tower-layer", 94 + "tower-service", 95 + "tracing", 96 + ] 97 + 98 + [[package]] 99 + name = "axum-core" 100 + version = "0.4.5" 101 + source = "registry+https://github.com/rust-lang/crates.io-index" 102 + checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" 103 + dependencies = [ 104 + "async-trait", 105 + "bytes", 106 + "futures-util", 107 + "http 1.3.1", 108 + "http-body", 109 + "http-body-util", 110 + "mime", 111 + "pin-project-lite", 112 + "rustversion", 113 + "sync_wrapper", 114 + "tower-layer", 115 + "tower-service", 116 + "tracing", 117 + ] 118 + 119 + [[package]] 120 + name = "backtrace" 121 + version = "0.3.75" 122 + source = "registry+https://github.com/rust-lang/crates.io-index" 123 + checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" 124 + dependencies = [ 125 + "addr2line", 126 + "cfg-if", 127 + "libc", 128 + "miniz_oxide", 129 + "object", 130 + "rustc-demangle", 131 + "windows-targets 0.52.6", 132 + ] 133 + 134 + [[package]] 135 + name = "base64" 136 + version = "0.22.1" 137 + source = "registry+https://github.com/rust-lang/crates.io-index" 138 + checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 139 + 140 + [[package]] 141 + name = "bitflags" 142 + version = "2.9.1" 143 + source = "registry+https://github.com/rust-lang/crates.io-index" 144 + checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 145 + 146 + [[package]] 147 + name = "bumpalo" 148 + version = "3.19.0" 149 + source = "registry+https://github.com/rust-lang/crates.io-index" 150 + checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 151 + 152 + [[package]] 153 + name = "bytes" 154 + version = "1.10.1" 155 + source = "registry+https://github.com/rust-lang/crates.io-index" 156 + checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 157 + 158 + [[package]] 159 + name = "cc" 160 + version = "1.2.30" 161 + source = "registry+https://github.com/rust-lang/crates.io-index" 162 + checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" 163 + dependencies = [ 164 + "shlex", 165 + ] 166 + 167 + [[package]] 168 + name = "cfg-if" 169 + version = "1.0.1" 170 + source = "registry+https://github.com/rust-lang/crates.io-index" 171 + checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 172 + 173 + [[package]] 174 + name = "chrono" 175 + version = "0.4.41" 176 + source = "registry+https://github.com/rust-lang/crates.io-index" 177 + checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" 178 + dependencies = [ 179 + "android-tzdata", 180 + "iana-time-zone", 181 + "js-sys", 182 + "num-traits", 183 + "wasm-bindgen", 184 + "windows-link", 185 + ] 186 + 187 + [[package]] 188 + name = "cookie" 189 + version = "0.16.2" 190 + source = "registry+https://github.com/rust-lang/crates.io-index" 191 + checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" 192 + dependencies = [ 193 + "time", 194 + "version_check", 195 + ] 196 + 197 + [[package]] 198 + name = "cookie" 199 + version = "0.18.1" 200 + source = "registry+https://github.com/rust-lang/crates.io-index" 201 + checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" 202 + dependencies = [ 203 + "percent-encoding", 204 + "time", 205 + "version_check", 206 + ] 207 + 208 + [[package]] 209 + name = "core-foundation" 210 + version = "0.9.4" 211 + source = "registry+https://github.com/rust-lang/crates.io-index" 212 + checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 213 + dependencies = [ 214 + "core-foundation-sys", 215 + "libc", 216 + ] 217 + 218 + [[package]] 219 + name = "core-foundation-sys" 220 + version = "0.8.7" 221 + source = "registry+https://github.com/rust-lang/crates.io-index" 222 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 223 + 224 + [[package]] 225 + name = "core_maths" 226 + version = "0.1.1" 227 + source = "registry+https://github.com/rust-lang/crates.io-index" 228 + checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" 229 + dependencies = [ 230 + "libm", 231 + ] 232 + 233 + [[package]] 234 + name = "darkroom" 235 + version = "0.1.0" 236 + dependencies = [ 237 + "anyhow", 238 + "axum", 239 + "base64", 240 + "chrono", 241 + "fantoccini", 242 + "futures", 243 + "minijinja", 244 + "reqwest", 245 + "serde", 246 + "serde_json", 247 + "thiserror", 248 + "tokio", 249 + "tower 0.4.13", 250 + "tower-http 0.5.2", 251 + "tracing", 252 + "tracing-subscriber", 253 + "urlencoding", 254 + ] 255 + 256 + [[package]] 257 + name = "deranged" 258 + version = "0.4.0" 259 + source = "registry+https://github.com/rust-lang/crates.io-index" 260 + checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" 261 + dependencies = [ 262 + "powerfmt", 263 + ] 264 + 265 + [[package]] 266 + name = "displaydoc" 267 + version = "0.2.5" 268 + source = "registry+https://github.com/rust-lang/crates.io-index" 269 + checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 270 + dependencies = [ 271 + "proc-macro2", 272 + "quote", 273 + "syn", 274 + ] 275 + 276 + [[package]] 277 + name = "encoding_rs" 278 + version = "0.8.35" 279 + source = "registry+https://github.com/rust-lang/crates.io-index" 280 + checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 281 + dependencies = [ 282 + "cfg-if", 283 + ] 284 + 285 + [[package]] 286 + name = "equivalent" 287 + version = "1.0.2" 288 + source = "registry+https://github.com/rust-lang/crates.io-index" 289 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 290 + 291 + [[package]] 292 + name = "errno" 293 + version = "0.3.13" 294 + source = "registry+https://github.com/rust-lang/crates.io-index" 295 + checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" 296 + dependencies = [ 297 + "libc", 298 + "windows-sys 0.60.2", 299 + ] 300 + 301 + [[package]] 302 + name = "fantoccini" 303 + version = "0.22.0" 304 + source = "registry+https://github.com/rust-lang/crates.io-index" 305 + checksum = "2d0086bcd59795408c87a04f94b5a8bd62cba2856cfe656c7e6439061d95b760" 306 + dependencies = [ 307 + "base64", 308 + "cookie 0.18.1", 309 + "futures-util", 310 + "http 1.3.1", 311 + "http-body-util", 312 + "hyper", 313 + "hyper-tls", 314 + "hyper-util", 315 + "mime", 316 + "openssl", 317 + "serde", 318 + "serde_json", 319 + "time", 320 + "tokio", 321 + "url", 322 + "webdriver", 323 + ] 324 + 325 + [[package]] 326 + name = "fastrand" 327 + version = "2.3.0" 328 + source = "registry+https://github.com/rust-lang/crates.io-index" 329 + checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 330 + 331 + [[package]] 332 + name = "fnv" 333 + version = "1.0.7" 334 + source = "registry+https://github.com/rust-lang/crates.io-index" 335 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 336 + 337 + [[package]] 338 + name = "foreign-types" 339 + version = "0.3.2" 340 + source = "registry+https://github.com/rust-lang/crates.io-index" 341 + checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 342 + dependencies = [ 343 + "foreign-types-shared", 344 + ] 345 + 346 + [[package]] 347 + name = "foreign-types-shared" 348 + version = "0.1.1" 349 + source = "registry+https://github.com/rust-lang/crates.io-index" 350 + checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 351 + 352 + [[package]] 353 + name = "form_urlencoded" 354 + version = "1.2.1" 355 + source = "registry+https://github.com/rust-lang/crates.io-index" 356 + checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 357 + dependencies = [ 358 + "percent-encoding", 359 + ] 360 + 361 + [[package]] 362 + name = "futures" 363 + version = "0.3.31" 364 + source = "registry+https://github.com/rust-lang/crates.io-index" 365 + checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 366 + dependencies = [ 367 + "futures-channel", 368 + "futures-core", 369 + "futures-executor", 370 + "futures-io", 371 + "futures-sink", 372 + "futures-task", 373 + "futures-util", 374 + ] 375 + 376 + [[package]] 377 + name = "futures-channel" 378 + version = "0.3.31" 379 + source = "registry+https://github.com/rust-lang/crates.io-index" 380 + checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 381 + dependencies = [ 382 + "futures-core", 383 + "futures-sink", 384 + ] 385 + 386 + [[package]] 387 + name = "futures-core" 388 + version = "0.3.31" 389 + source = "registry+https://github.com/rust-lang/crates.io-index" 390 + checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 391 + 392 + [[package]] 393 + name = "futures-executor" 394 + version = "0.3.31" 395 + source = "registry+https://github.com/rust-lang/crates.io-index" 396 + checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 397 + dependencies = [ 398 + "futures-core", 399 + "futures-task", 400 + "futures-util", 401 + ] 402 + 403 + [[package]] 404 + name = "futures-io" 405 + version = "0.3.31" 406 + source = "registry+https://github.com/rust-lang/crates.io-index" 407 + checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 408 + 409 + [[package]] 410 + name = "futures-macro" 411 + version = "0.3.31" 412 + source = "registry+https://github.com/rust-lang/crates.io-index" 413 + checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 414 + dependencies = [ 415 + "proc-macro2", 416 + "quote", 417 + "syn", 418 + ] 419 + 420 + [[package]] 421 + name = "futures-sink" 422 + version = "0.3.31" 423 + source = "registry+https://github.com/rust-lang/crates.io-index" 424 + checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 425 + 426 + [[package]] 427 + name = "futures-task" 428 + version = "0.3.31" 429 + source = "registry+https://github.com/rust-lang/crates.io-index" 430 + checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 431 + 432 + [[package]] 433 + name = "futures-util" 434 + version = "0.3.31" 435 + source = "registry+https://github.com/rust-lang/crates.io-index" 436 + checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 437 + dependencies = [ 438 + "futures-channel", 439 + "futures-core", 440 + "futures-io", 441 + "futures-macro", 442 + "futures-sink", 443 + "futures-task", 444 + "memchr", 445 + "pin-project-lite", 446 + "pin-utils", 447 + "slab", 448 + ] 449 + 450 + [[package]] 451 + name = "getrandom" 452 + version = "0.2.16" 453 + source = "registry+https://github.com/rust-lang/crates.io-index" 454 + checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 455 + dependencies = [ 456 + "cfg-if", 457 + "libc", 458 + "wasi 0.11.1+wasi-snapshot-preview1", 459 + ] 460 + 461 + [[package]] 462 + name = "getrandom" 463 + version = "0.3.3" 464 + source = "registry+https://github.com/rust-lang/crates.io-index" 465 + checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 466 + dependencies = [ 467 + "cfg-if", 468 + "libc", 469 + "r-efi", 470 + "wasi 0.14.2+wasi-0.2.4", 471 + ] 472 + 473 + [[package]] 474 + name = "gimli" 475 + version = "0.31.1" 476 + source = "registry+https://github.com/rust-lang/crates.io-index" 477 + checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 478 + 479 + [[package]] 480 + name = "h2" 481 + version = "0.4.11" 482 + source = "registry+https://github.com/rust-lang/crates.io-index" 483 + checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" 484 + dependencies = [ 485 + "atomic-waker", 486 + "bytes", 487 + "fnv", 488 + "futures-core", 489 + "futures-sink", 490 + "http 1.3.1", 491 + "indexmap", 492 + "slab", 493 + "tokio", 494 + "tokio-util", 495 + "tracing", 496 + ] 497 + 498 + [[package]] 499 + name = "hashbrown" 500 + version = "0.15.4" 501 + source = "registry+https://github.com/rust-lang/crates.io-index" 502 + checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" 503 + 504 + [[package]] 505 + name = "http" 506 + version = "0.2.12" 507 + source = "registry+https://github.com/rust-lang/crates.io-index" 508 + checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" 509 + dependencies = [ 510 + "bytes", 511 + "fnv", 512 + "itoa", 513 + ] 514 + 515 + [[package]] 516 + name = "http" 517 + version = "1.3.1" 518 + source = "registry+https://github.com/rust-lang/crates.io-index" 519 + checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 520 + dependencies = [ 521 + "bytes", 522 + "fnv", 523 + "itoa", 524 + ] 525 + 526 + [[package]] 527 + name = "http-body" 528 + version = "1.0.1" 529 + source = "registry+https://github.com/rust-lang/crates.io-index" 530 + checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 531 + dependencies = [ 532 + "bytes", 533 + "http 1.3.1", 534 + ] 535 + 536 + [[package]] 537 + name = "http-body-util" 538 + version = "0.1.3" 539 + source = "registry+https://github.com/rust-lang/crates.io-index" 540 + checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 541 + dependencies = [ 542 + "bytes", 543 + "futures-core", 544 + "http 1.3.1", 545 + "http-body", 546 + "pin-project-lite", 547 + ] 548 + 549 + [[package]] 550 + name = "httparse" 551 + version = "1.10.1" 552 + source = "registry+https://github.com/rust-lang/crates.io-index" 553 + checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 554 + 555 + [[package]] 556 + name = "httpdate" 557 + version = "1.0.3" 558 + source = "registry+https://github.com/rust-lang/crates.io-index" 559 + checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 560 + 561 + [[package]] 562 + name = "hyper" 563 + version = "1.6.0" 564 + source = "registry+https://github.com/rust-lang/crates.io-index" 565 + checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 566 + dependencies = [ 567 + "bytes", 568 + "futures-channel", 569 + "futures-util", 570 + "h2", 571 + "http 1.3.1", 572 + "http-body", 573 + "httparse", 574 + "httpdate", 575 + "itoa", 576 + "pin-project-lite", 577 + "smallvec", 578 + "tokio", 579 + "want", 580 + ] 581 + 582 + [[package]] 583 + name = "hyper-rustls" 584 + version = "0.27.7" 585 + source = "registry+https://github.com/rust-lang/crates.io-index" 586 + checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" 587 + dependencies = [ 588 + "http 1.3.1", 589 + "hyper", 590 + "hyper-util", 591 + "rustls", 592 + "rustls-pki-types", 593 + "tokio", 594 + "tokio-rustls", 595 + "tower-service", 596 + ] 597 + 598 + [[package]] 599 + name = "hyper-tls" 600 + version = "0.6.0" 601 + source = "registry+https://github.com/rust-lang/crates.io-index" 602 + checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 603 + dependencies = [ 604 + "bytes", 605 + "http-body-util", 606 + "hyper", 607 + "hyper-util", 608 + "native-tls", 609 + "tokio", 610 + "tokio-native-tls", 611 + "tower-service", 612 + ] 613 + 614 + [[package]] 615 + name = "hyper-util" 616 + version = "0.1.16" 617 + source = "registry+https://github.com/rust-lang/crates.io-index" 618 + checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" 619 + dependencies = [ 620 + "base64", 621 + "bytes", 622 + "futures-channel", 623 + "futures-core", 624 + "futures-util", 625 + "http 1.3.1", 626 + "http-body", 627 + "hyper", 628 + "ipnet", 629 + "libc", 630 + "percent-encoding", 631 + "pin-project-lite", 632 + "socket2", 633 + "system-configuration", 634 + "tokio", 635 + "tower-service", 636 + "tracing", 637 + "windows-registry", 638 + ] 639 + 640 + [[package]] 641 + name = "iana-time-zone" 642 + version = "0.1.63" 643 + source = "registry+https://github.com/rust-lang/crates.io-index" 644 + checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 645 + dependencies = [ 646 + "android_system_properties", 647 + "core-foundation-sys", 648 + "iana-time-zone-haiku", 649 + "js-sys", 650 + "log", 651 + "wasm-bindgen", 652 + "windows-core", 653 + ] 654 + 655 + [[package]] 656 + name = "iana-time-zone-haiku" 657 + version = "0.1.2" 658 + source = "registry+https://github.com/rust-lang/crates.io-index" 659 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 660 + dependencies = [ 661 + "cc", 662 + ] 663 + 664 + [[package]] 665 + name = "icu_collections" 666 + version = "1.5.0" 667 + source = "registry+https://github.com/rust-lang/crates.io-index" 668 + checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 669 + dependencies = [ 670 + "displaydoc", 671 + "yoke 0.7.5", 672 + "zerofrom", 673 + "zerovec 0.10.4", 674 + ] 675 + 676 + [[package]] 677 + name = "icu_collections" 678 + version = "2.0.0" 679 + source = "registry+https://github.com/rust-lang/crates.io-index" 680 + checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 681 + dependencies = [ 682 + "displaydoc", 683 + "potential_utf", 684 + "yoke 0.8.0", 685 + "zerofrom", 686 + "zerovec 0.11.2", 687 + ] 688 + 689 + [[package]] 690 + name = "icu_locale_core" 691 + version = "2.0.0" 692 + source = "registry+https://github.com/rust-lang/crates.io-index" 693 + checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 694 + dependencies = [ 695 + "displaydoc", 696 + "litemap 0.8.0", 697 + "tinystr 0.8.1", 698 + "writeable 0.6.1", 699 + "zerovec 0.11.2", 700 + ] 701 + 702 + [[package]] 703 + name = "icu_locid" 704 + version = "1.5.0" 705 + source = "registry+https://github.com/rust-lang/crates.io-index" 706 + checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 707 + dependencies = [ 708 + "displaydoc", 709 + "litemap 0.7.5", 710 + "tinystr 0.7.6", 711 + "writeable 0.5.5", 712 + ] 713 + 714 + [[package]] 715 + name = "icu_normalizer" 716 + version = "2.0.0" 717 + source = "registry+https://github.com/rust-lang/crates.io-index" 718 + checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 719 + dependencies = [ 720 + "displaydoc", 721 + "icu_collections 2.0.0", 722 + "icu_normalizer_data", 723 + "icu_properties", 724 + "icu_provider 2.0.0", 725 + "smallvec", 726 + "zerovec 0.11.2", 727 + ] 728 + 729 + [[package]] 730 + name = "icu_normalizer_data" 731 + version = "2.0.0" 732 + source = "registry+https://github.com/rust-lang/crates.io-index" 733 + checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 734 + 735 + [[package]] 736 + name = "icu_properties" 737 + version = "2.0.1" 738 + source = "registry+https://github.com/rust-lang/crates.io-index" 739 + checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" 740 + dependencies = [ 741 + "displaydoc", 742 + "icu_collections 2.0.0", 743 + "icu_locale_core", 744 + "icu_properties_data", 745 + "icu_provider 2.0.0", 746 + "potential_utf", 747 + "zerotrie", 748 + "zerovec 0.11.2", 749 + ] 750 + 751 + [[package]] 752 + name = "icu_properties_data" 753 + version = "2.0.1" 754 + source = "registry+https://github.com/rust-lang/crates.io-index" 755 + checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" 756 + 757 + [[package]] 758 + name = "icu_provider" 759 + version = "1.5.0" 760 + source = "registry+https://github.com/rust-lang/crates.io-index" 761 + checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 762 + dependencies = [ 763 + "displaydoc", 764 + "icu_locid", 765 + "icu_provider_macros", 766 + "stable_deref_trait", 767 + "tinystr 0.7.6", 768 + "writeable 0.5.5", 769 + "yoke 0.7.5", 770 + "zerofrom", 771 + "zerovec 0.10.4", 772 + ] 773 + 774 + [[package]] 775 + name = "icu_provider" 776 + version = "2.0.0" 777 + source = "registry+https://github.com/rust-lang/crates.io-index" 778 + checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 779 + dependencies = [ 780 + "displaydoc", 781 + "icu_locale_core", 782 + "stable_deref_trait", 783 + "tinystr 0.8.1", 784 + "writeable 0.6.1", 785 + "yoke 0.8.0", 786 + "zerofrom", 787 + "zerotrie", 788 + "zerovec 0.11.2", 789 + ] 790 + 791 + [[package]] 792 + name = "icu_provider_macros" 793 + version = "1.5.0" 794 + source = "registry+https://github.com/rust-lang/crates.io-index" 795 + checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 796 + dependencies = [ 797 + "proc-macro2", 798 + "quote", 799 + "syn", 800 + ] 801 + 802 + [[package]] 803 + name = "icu_segmenter" 804 + version = "1.5.0" 805 + source = "registry+https://github.com/rust-lang/crates.io-index" 806 + checksum = "a717725612346ffc2d7b42c94b820db6908048f39434504cb130e8b46256b0de" 807 + dependencies = [ 808 + "core_maths", 809 + "displaydoc", 810 + "icu_collections 1.5.0", 811 + "icu_locid", 812 + "icu_provider 1.5.0", 813 + "icu_segmenter_data", 814 + "utf8_iter", 815 + "zerovec 0.10.4", 816 + ] 817 + 818 + [[package]] 819 + name = "icu_segmenter_data" 820 + version = "1.5.1" 821 + source = "registry+https://github.com/rust-lang/crates.io-index" 822 + checksum = "a1e52775179941363cc594e49ce99284d13d6948928d8e72c755f55e98caa1eb" 823 + 824 + [[package]] 825 + name = "idna" 826 + version = "1.0.3" 827 + source = "registry+https://github.com/rust-lang/crates.io-index" 828 + checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 829 + dependencies = [ 830 + "idna_adapter", 831 + "smallvec", 832 + "utf8_iter", 833 + ] 834 + 835 + [[package]] 836 + name = "idna_adapter" 837 + version = "1.2.1" 838 + source = "registry+https://github.com/rust-lang/crates.io-index" 839 + checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 840 + dependencies = [ 841 + "icu_normalizer", 842 + "icu_properties", 843 + ] 844 + 845 + [[package]] 846 + name = "indexmap" 847 + version = "2.10.0" 848 + source = "registry+https://github.com/rust-lang/crates.io-index" 849 + checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" 850 + dependencies = [ 851 + "equivalent", 852 + "hashbrown", 853 + ] 854 + 855 + [[package]] 856 + name = "io-uring" 857 + version = "0.7.9" 858 + source = "registry+https://github.com/rust-lang/crates.io-index" 859 + checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" 860 + dependencies = [ 861 + "bitflags", 862 + "cfg-if", 863 + "libc", 864 + ] 865 + 866 + [[package]] 867 + name = "ipnet" 868 + version = "2.11.0" 869 + source = "registry+https://github.com/rust-lang/crates.io-index" 870 + checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 871 + 872 + [[package]] 873 + name = "iri-string" 874 + version = "0.7.8" 875 + source = "registry+https://github.com/rust-lang/crates.io-index" 876 + checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" 877 + dependencies = [ 878 + "memchr", 879 + "serde", 880 + ] 881 + 882 + [[package]] 883 + name = "itoa" 884 + version = "1.0.15" 885 + source = "registry+https://github.com/rust-lang/crates.io-index" 886 + checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 887 + 888 + [[package]] 889 + name = "js-sys" 890 + version = "0.3.77" 891 + source = "registry+https://github.com/rust-lang/crates.io-index" 892 + checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 893 + dependencies = [ 894 + "once_cell", 895 + "wasm-bindgen", 896 + ] 897 + 898 + [[package]] 899 + name = "lazy_static" 900 + version = "1.5.0" 901 + source = "registry+https://github.com/rust-lang/crates.io-index" 902 + checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 903 + 904 + [[package]] 905 + name = "libc" 906 + version = "0.2.174" 907 + source = "registry+https://github.com/rust-lang/crates.io-index" 908 + checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" 909 + 910 + [[package]] 911 + name = "libm" 912 + version = "0.2.15" 913 + source = "registry+https://github.com/rust-lang/crates.io-index" 914 + checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" 915 + 916 + [[package]] 917 + name = "linux-raw-sys" 918 + version = "0.9.4" 919 + source = "registry+https://github.com/rust-lang/crates.io-index" 920 + checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" 921 + 922 + [[package]] 923 + name = "litemap" 924 + version = "0.7.5" 925 + source = "registry+https://github.com/rust-lang/crates.io-index" 926 + checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" 927 + 928 + [[package]] 929 + name = "litemap" 930 + version = "0.8.0" 931 + source = "registry+https://github.com/rust-lang/crates.io-index" 932 + checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 933 + 934 + [[package]] 935 + name = "lock_api" 936 + version = "0.4.13" 937 + source = "registry+https://github.com/rust-lang/crates.io-index" 938 + checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" 939 + dependencies = [ 940 + "autocfg", 941 + "scopeguard", 942 + ] 943 + 944 + [[package]] 945 + name = "log" 946 + version = "0.4.27" 947 + source = "registry+https://github.com/rust-lang/crates.io-index" 948 + checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 949 + 950 + [[package]] 951 + name = "matchit" 952 + version = "0.7.3" 953 + source = "registry+https://github.com/rust-lang/crates.io-index" 954 + checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" 955 + 956 + [[package]] 957 + name = "memchr" 958 + version = "2.7.5" 959 + source = "registry+https://github.com/rust-lang/crates.io-index" 960 + checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 961 + 962 + [[package]] 963 + name = "mime" 964 + version = "0.3.17" 965 + source = "registry+https://github.com/rust-lang/crates.io-index" 966 + checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 967 + 968 + [[package]] 969 + name = "minijinja" 970 + version = "2.11.0" 971 + source = "registry+https://github.com/rust-lang/crates.io-index" 972 + checksum = "4e60ac08614cc09062820e51d5d94c2fce16b94ea4e5003bb81b99a95f84e876" 973 + dependencies = [ 974 + "serde", 975 + ] 976 + 977 + [[package]] 978 + name = "miniz_oxide" 979 + version = "0.8.9" 980 + source = "registry+https://github.com/rust-lang/crates.io-index" 981 + checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 982 + dependencies = [ 983 + "adler2", 984 + ] 985 + 986 + [[package]] 987 + name = "mio" 988 + version = "1.0.4" 989 + source = "registry+https://github.com/rust-lang/crates.io-index" 990 + checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 991 + dependencies = [ 992 + "libc", 993 + "wasi 0.11.1+wasi-snapshot-preview1", 994 + "windows-sys 0.59.0", 995 + ] 996 + 997 + [[package]] 998 + name = "native-tls" 999 + version = "0.2.14" 1000 + source = "registry+https://github.com/rust-lang/crates.io-index" 1001 + checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" 1002 + dependencies = [ 1003 + "libc", 1004 + "log", 1005 + "openssl", 1006 + "openssl-probe", 1007 + "openssl-sys", 1008 + "schannel", 1009 + "security-framework", 1010 + "security-framework-sys", 1011 + "tempfile", 1012 + ] 1013 + 1014 + [[package]] 1015 + name = "nu-ansi-term" 1016 + version = "0.46.0" 1017 + source = "registry+https://github.com/rust-lang/crates.io-index" 1018 + checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 1019 + dependencies = [ 1020 + "overload", 1021 + "winapi", 1022 + ] 1023 + 1024 + [[package]] 1025 + name = "num-conv" 1026 + version = "0.1.0" 1027 + source = "registry+https://github.com/rust-lang/crates.io-index" 1028 + checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 1029 + 1030 + [[package]] 1031 + name = "num-traits" 1032 + version = "0.2.19" 1033 + source = "registry+https://github.com/rust-lang/crates.io-index" 1034 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1035 + dependencies = [ 1036 + "autocfg", 1037 + ] 1038 + 1039 + [[package]] 1040 + name = "object" 1041 + version = "0.36.7" 1042 + source = "registry+https://github.com/rust-lang/crates.io-index" 1043 + checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 1044 + dependencies = [ 1045 + "memchr", 1046 + ] 1047 + 1048 + [[package]] 1049 + name = "once_cell" 1050 + version = "1.21.3" 1051 + source = "registry+https://github.com/rust-lang/crates.io-index" 1052 + checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 1053 + 1054 + [[package]] 1055 + name = "openssl" 1056 + version = "0.10.73" 1057 + source = "registry+https://github.com/rust-lang/crates.io-index" 1058 + checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" 1059 + dependencies = [ 1060 + "bitflags", 1061 + "cfg-if", 1062 + "foreign-types", 1063 + "libc", 1064 + "once_cell", 1065 + "openssl-macros", 1066 + "openssl-sys", 1067 + ] 1068 + 1069 + [[package]] 1070 + name = "openssl-macros" 1071 + version = "0.1.1" 1072 + source = "registry+https://github.com/rust-lang/crates.io-index" 1073 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 1074 + dependencies = [ 1075 + "proc-macro2", 1076 + "quote", 1077 + "syn", 1078 + ] 1079 + 1080 + [[package]] 1081 + name = "openssl-probe" 1082 + version = "0.1.6" 1083 + source = "registry+https://github.com/rust-lang/crates.io-index" 1084 + checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 1085 + 1086 + [[package]] 1087 + name = "openssl-sys" 1088 + version = "0.9.109" 1089 + source = "registry+https://github.com/rust-lang/crates.io-index" 1090 + checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" 1091 + dependencies = [ 1092 + "cc", 1093 + "libc", 1094 + "pkg-config", 1095 + "vcpkg", 1096 + ] 1097 + 1098 + [[package]] 1099 + name = "overload" 1100 + version = "0.1.1" 1101 + source = "registry+https://github.com/rust-lang/crates.io-index" 1102 + checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 1103 + 1104 + [[package]] 1105 + name = "parking_lot" 1106 + version = "0.12.4" 1107 + source = "registry+https://github.com/rust-lang/crates.io-index" 1108 + checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" 1109 + dependencies = [ 1110 + "lock_api", 1111 + "parking_lot_core", 1112 + ] 1113 + 1114 + [[package]] 1115 + name = "parking_lot_core" 1116 + version = "0.9.11" 1117 + source = "registry+https://github.com/rust-lang/crates.io-index" 1118 + checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" 1119 + dependencies = [ 1120 + "cfg-if", 1121 + "libc", 1122 + "redox_syscall", 1123 + "smallvec", 1124 + "windows-targets 0.52.6", 1125 + ] 1126 + 1127 + [[package]] 1128 + name = "percent-encoding" 1129 + version = "2.3.1" 1130 + source = "registry+https://github.com/rust-lang/crates.io-index" 1131 + checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1132 + 1133 + [[package]] 1134 + name = "pin-project-lite" 1135 + version = "0.2.16" 1136 + source = "registry+https://github.com/rust-lang/crates.io-index" 1137 + checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 1138 + 1139 + [[package]] 1140 + name = "pin-utils" 1141 + version = "0.1.0" 1142 + source = "registry+https://github.com/rust-lang/crates.io-index" 1143 + checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1144 + 1145 + [[package]] 1146 + name = "pkg-config" 1147 + version = "0.3.32" 1148 + source = "registry+https://github.com/rust-lang/crates.io-index" 1149 + checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1150 + 1151 + [[package]] 1152 + name = "potential_utf" 1153 + version = "0.1.2" 1154 + source = "registry+https://github.com/rust-lang/crates.io-index" 1155 + checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" 1156 + dependencies = [ 1157 + "zerovec 0.11.2", 1158 + ] 1159 + 1160 + [[package]] 1161 + name = "powerfmt" 1162 + version = "0.2.0" 1163 + source = "registry+https://github.com/rust-lang/crates.io-index" 1164 + checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 1165 + 1166 + [[package]] 1167 + name = "proc-macro2" 1168 + version = "1.0.95" 1169 + source = "registry+https://github.com/rust-lang/crates.io-index" 1170 + checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 1171 + dependencies = [ 1172 + "unicode-ident", 1173 + ] 1174 + 1175 + [[package]] 1176 + name = "quote" 1177 + version = "1.0.40" 1178 + source = "registry+https://github.com/rust-lang/crates.io-index" 1179 + checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 1180 + dependencies = [ 1181 + "proc-macro2", 1182 + ] 1183 + 1184 + [[package]] 1185 + name = "r-efi" 1186 + version = "5.3.0" 1187 + source = "registry+https://github.com/rust-lang/crates.io-index" 1188 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 1189 + 1190 + [[package]] 1191 + name = "redox_syscall" 1192 + version = "0.5.17" 1193 + source = "registry+https://github.com/rust-lang/crates.io-index" 1194 + checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" 1195 + dependencies = [ 1196 + "bitflags", 1197 + ] 1198 + 1199 + [[package]] 1200 + name = "reqwest" 1201 + version = "0.12.22" 1202 + source = "registry+https://github.com/rust-lang/crates.io-index" 1203 + checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" 1204 + dependencies = [ 1205 + "base64", 1206 + "bytes", 1207 + "encoding_rs", 1208 + "futures-core", 1209 + "h2", 1210 + "http 1.3.1", 1211 + "http-body", 1212 + "http-body-util", 1213 + "hyper", 1214 + "hyper-rustls", 1215 + "hyper-tls", 1216 + "hyper-util", 1217 + "js-sys", 1218 + "log", 1219 + "mime", 1220 + "native-tls", 1221 + "percent-encoding", 1222 + "pin-project-lite", 1223 + "rustls-pki-types", 1224 + "serde", 1225 + "serde_json", 1226 + "serde_urlencoded", 1227 + "sync_wrapper", 1228 + "tokio", 1229 + "tokio-native-tls", 1230 + "tower 0.5.2", 1231 + "tower-http 0.6.6", 1232 + "tower-service", 1233 + "url", 1234 + "wasm-bindgen", 1235 + "wasm-bindgen-futures", 1236 + "web-sys", 1237 + ] 1238 + 1239 + [[package]] 1240 + name = "ring" 1241 + version = "0.17.14" 1242 + source = "registry+https://github.com/rust-lang/crates.io-index" 1243 + checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 1244 + dependencies = [ 1245 + "cc", 1246 + "cfg-if", 1247 + "getrandom 0.2.16", 1248 + "libc", 1249 + "untrusted", 1250 + "windows-sys 0.52.0", 1251 + ] 1252 + 1253 + [[package]] 1254 + name = "rustc-demangle" 1255 + version = "0.1.26" 1256 + source = "registry+https://github.com/rust-lang/crates.io-index" 1257 + checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 1258 + 1259 + [[package]] 1260 + name = "rustix" 1261 + version = "1.0.8" 1262 + source = "registry+https://github.com/rust-lang/crates.io-index" 1263 + checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" 1264 + dependencies = [ 1265 + "bitflags", 1266 + "errno", 1267 + "libc", 1268 + "linux-raw-sys", 1269 + "windows-sys 0.60.2", 1270 + ] 1271 + 1272 + [[package]] 1273 + name = "rustls" 1274 + version = "0.23.31" 1275 + source = "registry+https://github.com/rust-lang/crates.io-index" 1276 + checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" 1277 + dependencies = [ 1278 + "once_cell", 1279 + "rustls-pki-types", 1280 + "rustls-webpki", 1281 + "subtle", 1282 + "zeroize", 1283 + ] 1284 + 1285 + [[package]] 1286 + name = "rustls-pki-types" 1287 + version = "1.12.0" 1288 + source = "registry+https://github.com/rust-lang/crates.io-index" 1289 + checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 1290 + dependencies = [ 1291 + "zeroize", 1292 + ] 1293 + 1294 + [[package]] 1295 + name = "rustls-webpki" 1296 + version = "0.103.4" 1297 + source = "registry+https://github.com/rust-lang/crates.io-index" 1298 + checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" 1299 + dependencies = [ 1300 + "ring", 1301 + "rustls-pki-types", 1302 + "untrusted", 1303 + ] 1304 + 1305 + [[package]] 1306 + name = "rustversion" 1307 + version = "1.0.21" 1308 + source = "registry+https://github.com/rust-lang/crates.io-index" 1309 + checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" 1310 + 1311 + [[package]] 1312 + name = "ryu" 1313 + version = "1.0.20" 1314 + source = "registry+https://github.com/rust-lang/crates.io-index" 1315 + checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 1316 + 1317 + [[package]] 1318 + name = "schannel" 1319 + version = "0.1.27" 1320 + source = "registry+https://github.com/rust-lang/crates.io-index" 1321 + checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 1322 + dependencies = [ 1323 + "windows-sys 0.59.0", 1324 + ] 1325 + 1326 + [[package]] 1327 + name = "scopeguard" 1328 + version = "1.2.0" 1329 + source = "registry+https://github.com/rust-lang/crates.io-index" 1330 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1331 + 1332 + [[package]] 1333 + name = "security-framework" 1334 + version = "2.11.1" 1335 + source = "registry+https://github.com/rust-lang/crates.io-index" 1336 + checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 1337 + dependencies = [ 1338 + "bitflags", 1339 + "core-foundation", 1340 + "core-foundation-sys", 1341 + "libc", 1342 + "security-framework-sys", 1343 + ] 1344 + 1345 + [[package]] 1346 + name = "security-framework-sys" 1347 + version = "2.14.0" 1348 + source = "registry+https://github.com/rust-lang/crates.io-index" 1349 + checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" 1350 + dependencies = [ 1351 + "core-foundation-sys", 1352 + "libc", 1353 + ] 1354 + 1355 + [[package]] 1356 + name = "serde" 1357 + version = "1.0.219" 1358 + source = "registry+https://github.com/rust-lang/crates.io-index" 1359 + checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1360 + dependencies = [ 1361 + "serde_derive", 1362 + ] 1363 + 1364 + [[package]] 1365 + name = "serde_derive" 1366 + version = "1.0.219" 1367 + source = "registry+https://github.com/rust-lang/crates.io-index" 1368 + checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1369 + dependencies = [ 1370 + "proc-macro2", 1371 + "quote", 1372 + "syn", 1373 + ] 1374 + 1375 + [[package]] 1376 + name = "serde_json" 1377 + version = "1.0.141" 1378 + source = "registry+https://github.com/rust-lang/crates.io-index" 1379 + checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" 1380 + dependencies = [ 1381 + "itoa", 1382 + "memchr", 1383 + "ryu", 1384 + "serde", 1385 + ] 1386 + 1387 + [[package]] 1388 + name = "serde_path_to_error" 1389 + version = "0.1.17" 1390 + source = "registry+https://github.com/rust-lang/crates.io-index" 1391 + checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" 1392 + dependencies = [ 1393 + "itoa", 1394 + "serde", 1395 + ] 1396 + 1397 + [[package]] 1398 + name = "serde_urlencoded" 1399 + version = "0.7.1" 1400 + source = "registry+https://github.com/rust-lang/crates.io-index" 1401 + checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1402 + dependencies = [ 1403 + "form_urlencoded", 1404 + "itoa", 1405 + "ryu", 1406 + "serde", 1407 + ] 1408 + 1409 + [[package]] 1410 + name = "sharded-slab" 1411 + version = "0.1.7" 1412 + source = "registry+https://github.com/rust-lang/crates.io-index" 1413 + checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1414 + dependencies = [ 1415 + "lazy_static", 1416 + ] 1417 + 1418 + [[package]] 1419 + name = "shlex" 1420 + version = "1.3.0" 1421 + source = "registry+https://github.com/rust-lang/crates.io-index" 1422 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1423 + 1424 + [[package]] 1425 + name = "signal-hook-registry" 1426 + version = "1.4.5" 1427 + source = "registry+https://github.com/rust-lang/crates.io-index" 1428 + checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" 1429 + dependencies = [ 1430 + "libc", 1431 + ] 1432 + 1433 + [[package]] 1434 + name = "slab" 1435 + version = "0.4.10" 1436 + source = "registry+https://github.com/rust-lang/crates.io-index" 1437 + checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" 1438 + 1439 + [[package]] 1440 + name = "smallvec" 1441 + version = "1.15.1" 1442 + source = "registry+https://github.com/rust-lang/crates.io-index" 1443 + checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 1444 + 1445 + [[package]] 1446 + name = "socket2" 1447 + version = "0.6.0" 1448 + source = "registry+https://github.com/rust-lang/crates.io-index" 1449 + checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" 1450 + dependencies = [ 1451 + "libc", 1452 + "windows-sys 0.59.0", 1453 + ] 1454 + 1455 + [[package]] 1456 + name = "stable_deref_trait" 1457 + version = "1.2.0" 1458 + source = "registry+https://github.com/rust-lang/crates.io-index" 1459 + checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1460 + 1461 + [[package]] 1462 + name = "subtle" 1463 + version = "2.6.1" 1464 + source = "registry+https://github.com/rust-lang/crates.io-index" 1465 + checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1466 + 1467 + [[package]] 1468 + name = "syn" 1469 + version = "2.0.104" 1470 + source = "registry+https://github.com/rust-lang/crates.io-index" 1471 + checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" 1472 + dependencies = [ 1473 + "proc-macro2", 1474 + "quote", 1475 + "unicode-ident", 1476 + ] 1477 + 1478 + [[package]] 1479 + name = "sync_wrapper" 1480 + version = "1.0.2" 1481 + source = "registry+https://github.com/rust-lang/crates.io-index" 1482 + checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1483 + dependencies = [ 1484 + "futures-core", 1485 + ] 1486 + 1487 + [[package]] 1488 + name = "synstructure" 1489 + version = "0.13.2" 1490 + source = "registry+https://github.com/rust-lang/crates.io-index" 1491 + checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 1492 + dependencies = [ 1493 + "proc-macro2", 1494 + "quote", 1495 + "syn", 1496 + ] 1497 + 1498 + [[package]] 1499 + name = "system-configuration" 1500 + version = "0.6.1" 1501 + source = "registry+https://github.com/rust-lang/crates.io-index" 1502 + checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 1503 + dependencies = [ 1504 + "bitflags", 1505 + "core-foundation", 1506 + "system-configuration-sys", 1507 + ] 1508 + 1509 + [[package]] 1510 + name = "system-configuration-sys" 1511 + version = "0.6.0" 1512 + source = "registry+https://github.com/rust-lang/crates.io-index" 1513 + checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 1514 + dependencies = [ 1515 + "core-foundation-sys", 1516 + "libc", 1517 + ] 1518 + 1519 + [[package]] 1520 + name = "tempfile" 1521 + version = "3.20.0" 1522 + source = "registry+https://github.com/rust-lang/crates.io-index" 1523 + checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" 1524 + dependencies = [ 1525 + "fastrand", 1526 + "getrandom 0.3.3", 1527 + "once_cell", 1528 + "rustix", 1529 + "windows-sys 0.59.0", 1530 + ] 1531 + 1532 + [[package]] 1533 + name = "thiserror" 1534 + version = "1.0.69" 1535 + source = "registry+https://github.com/rust-lang/crates.io-index" 1536 + checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1537 + dependencies = [ 1538 + "thiserror-impl", 1539 + ] 1540 + 1541 + [[package]] 1542 + name = "thiserror-impl" 1543 + version = "1.0.69" 1544 + source = "registry+https://github.com/rust-lang/crates.io-index" 1545 + checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1546 + dependencies = [ 1547 + "proc-macro2", 1548 + "quote", 1549 + "syn", 1550 + ] 1551 + 1552 + [[package]] 1553 + name = "thread_local" 1554 + version = "1.1.9" 1555 + source = "registry+https://github.com/rust-lang/crates.io-index" 1556 + checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" 1557 + dependencies = [ 1558 + "cfg-if", 1559 + ] 1560 + 1561 + [[package]] 1562 + name = "time" 1563 + version = "0.3.41" 1564 + source = "registry+https://github.com/rust-lang/crates.io-index" 1565 + checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" 1566 + dependencies = [ 1567 + "deranged", 1568 + "itoa", 1569 + "num-conv", 1570 + "powerfmt", 1571 + "serde", 1572 + "time-core", 1573 + "time-macros", 1574 + ] 1575 + 1576 + [[package]] 1577 + name = "time-core" 1578 + version = "0.1.4" 1579 + source = "registry+https://github.com/rust-lang/crates.io-index" 1580 + checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" 1581 + 1582 + [[package]] 1583 + name = "time-macros" 1584 + version = "0.2.22" 1585 + source = "registry+https://github.com/rust-lang/crates.io-index" 1586 + checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" 1587 + dependencies = [ 1588 + "num-conv", 1589 + "time-core", 1590 + ] 1591 + 1592 + [[package]] 1593 + name = "tinystr" 1594 + version = "0.7.6" 1595 + source = "registry+https://github.com/rust-lang/crates.io-index" 1596 + checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 1597 + dependencies = [ 1598 + "displaydoc", 1599 + ] 1600 + 1601 + [[package]] 1602 + name = "tinystr" 1603 + version = "0.8.1" 1604 + source = "registry+https://github.com/rust-lang/crates.io-index" 1605 + checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 1606 + dependencies = [ 1607 + "displaydoc", 1608 + "zerovec 0.11.2", 1609 + ] 1610 + 1611 + [[package]] 1612 + name = "tokio" 1613 + version = "1.47.0" 1614 + source = "registry+https://github.com/rust-lang/crates.io-index" 1615 + checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35" 1616 + dependencies = [ 1617 + "backtrace", 1618 + "bytes", 1619 + "io-uring", 1620 + "libc", 1621 + "mio", 1622 + "parking_lot", 1623 + "pin-project-lite", 1624 + "signal-hook-registry", 1625 + "slab", 1626 + "socket2", 1627 + "tokio-macros", 1628 + "windows-sys 0.59.0", 1629 + ] 1630 + 1631 + [[package]] 1632 + name = "tokio-macros" 1633 + version = "2.5.0" 1634 + source = "registry+https://github.com/rust-lang/crates.io-index" 1635 + checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1636 + dependencies = [ 1637 + "proc-macro2", 1638 + "quote", 1639 + "syn", 1640 + ] 1641 + 1642 + [[package]] 1643 + name = "tokio-native-tls" 1644 + version = "0.3.1" 1645 + source = "registry+https://github.com/rust-lang/crates.io-index" 1646 + checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 1647 + dependencies = [ 1648 + "native-tls", 1649 + "tokio", 1650 + ] 1651 + 1652 + [[package]] 1653 + name = "tokio-rustls" 1654 + version = "0.26.2" 1655 + source = "registry+https://github.com/rust-lang/crates.io-index" 1656 + checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 1657 + dependencies = [ 1658 + "rustls", 1659 + "tokio", 1660 + ] 1661 + 1662 + [[package]] 1663 + name = "tokio-util" 1664 + version = "0.7.15" 1665 + source = "registry+https://github.com/rust-lang/crates.io-index" 1666 + checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" 1667 + dependencies = [ 1668 + "bytes", 1669 + "futures-core", 1670 + "futures-sink", 1671 + "pin-project-lite", 1672 + "tokio", 1673 + ] 1674 + 1675 + [[package]] 1676 + name = "tower" 1677 + version = "0.4.13" 1678 + source = "registry+https://github.com/rust-lang/crates.io-index" 1679 + checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 1680 + dependencies = [ 1681 + "tower-layer", 1682 + "tower-service", 1683 + "tracing", 1684 + ] 1685 + 1686 + [[package]] 1687 + name = "tower" 1688 + version = "0.5.2" 1689 + source = "registry+https://github.com/rust-lang/crates.io-index" 1690 + checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 1691 + dependencies = [ 1692 + "futures-core", 1693 + "futures-util", 1694 + "pin-project-lite", 1695 + "sync_wrapper", 1696 + "tokio", 1697 + "tower-layer", 1698 + "tower-service", 1699 + "tracing", 1700 + ] 1701 + 1702 + [[package]] 1703 + name = "tower-http" 1704 + version = "0.5.2" 1705 + source = "registry+https://github.com/rust-lang/crates.io-index" 1706 + checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" 1707 + dependencies = [ 1708 + "bitflags", 1709 + "bytes", 1710 + "http 1.3.1", 1711 + "http-body", 1712 + "http-body-util", 1713 + "pin-project-lite", 1714 + "tower-layer", 1715 + "tower-service", 1716 + "tracing", 1717 + ] 1718 + 1719 + [[package]] 1720 + name = "tower-http" 1721 + version = "0.6.6" 1722 + source = "registry+https://github.com/rust-lang/crates.io-index" 1723 + checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" 1724 + dependencies = [ 1725 + "bitflags", 1726 + "bytes", 1727 + "futures-util", 1728 + "http 1.3.1", 1729 + "http-body", 1730 + "iri-string", 1731 + "pin-project-lite", 1732 + "tower 0.5.2", 1733 + "tower-layer", 1734 + "tower-service", 1735 + ] 1736 + 1737 + [[package]] 1738 + name = "tower-layer" 1739 + version = "0.3.3" 1740 + source = "registry+https://github.com/rust-lang/crates.io-index" 1741 + checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1742 + 1743 + [[package]] 1744 + name = "tower-service" 1745 + version = "0.3.3" 1746 + source = "registry+https://github.com/rust-lang/crates.io-index" 1747 + checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1748 + 1749 + [[package]] 1750 + name = "tracing" 1751 + version = "0.1.41" 1752 + source = "registry+https://github.com/rust-lang/crates.io-index" 1753 + checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1754 + dependencies = [ 1755 + "log", 1756 + "pin-project-lite", 1757 + "tracing-attributes", 1758 + "tracing-core", 1759 + ] 1760 + 1761 + [[package]] 1762 + name = "tracing-attributes" 1763 + version = "0.1.30" 1764 + source = "registry+https://github.com/rust-lang/crates.io-index" 1765 + checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" 1766 + dependencies = [ 1767 + "proc-macro2", 1768 + "quote", 1769 + "syn", 1770 + ] 1771 + 1772 + [[package]] 1773 + name = "tracing-core" 1774 + version = "0.1.34" 1775 + source = "registry+https://github.com/rust-lang/crates.io-index" 1776 + checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 1777 + dependencies = [ 1778 + "once_cell", 1779 + "valuable", 1780 + ] 1781 + 1782 + [[package]] 1783 + name = "tracing-log" 1784 + version = "0.2.0" 1785 + source = "registry+https://github.com/rust-lang/crates.io-index" 1786 + checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 1787 + dependencies = [ 1788 + "log", 1789 + "once_cell", 1790 + "tracing-core", 1791 + ] 1792 + 1793 + [[package]] 1794 + name = "tracing-subscriber" 1795 + version = "0.3.19" 1796 + source = "registry+https://github.com/rust-lang/crates.io-index" 1797 + checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 1798 + dependencies = [ 1799 + "nu-ansi-term", 1800 + "sharded-slab", 1801 + "smallvec", 1802 + "thread_local", 1803 + "tracing-core", 1804 + "tracing-log", 1805 + ] 1806 + 1807 + [[package]] 1808 + name = "try-lock" 1809 + version = "0.2.5" 1810 + source = "registry+https://github.com/rust-lang/crates.io-index" 1811 + checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1812 + 1813 + [[package]] 1814 + name = "unicode-ident" 1815 + version = "1.0.18" 1816 + source = "registry+https://github.com/rust-lang/crates.io-index" 1817 + checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1818 + 1819 + [[package]] 1820 + name = "untrusted" 1821 + version = "0.9.0" 1822 + source = "registry+https://github.com/rust-lang/crates.io-index" 1823 + checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1824 + 1825 + [[package]] 1826 + name = "url" 1827 + version = "2.5.4" 1828 + source = "registry+https://github.com/rust-lang/crates.io-index" 1829 + checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1830 + dependencies = [ 1831 + "form_urlencoded", 1832 + "idna", 1833 + "percent-encoding", 1834 + ] 1835 + 1836 + [[package]] 1837 + name = "urlencoding" 1838 + version = "2.1.3" 1839 + source = "registry+https://github.com/rust-lang/crates.io-index" 1840 + checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" 1841 + 1842 + [[package]] 1843 + name = "utf8_iter" 1844 + version = "1.0.4" 1845 + source = "registry+https://github.com/rust-lang/crates.io-index" 1846 + checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1847 + 1848 + [[package]] 1849 + name = "valuable" 1850 + version = "0.1.1" 1851 + source = "registry+https://github.com/rust-lang/crates.io-index" 1852 + checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 1853 + 1854 + [[package]] 1855 + name = "vcpkg" 1856 + version = "0.2.15" 1857 + source = "registry+https://github.com/rust-lang/crates.io-index" 1858 + checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1859 + 1860 + [[package]] 1861 + name = "version_check" 1862 + version = "0.9.5" 1863 + source = "registry+https://github.com/rust-lang/crates.io-index" 1864 + checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1865 + 1866 + [[package]] 1867 + name = "want" 1868 + version = "0.3.1" 1869 + source = "registry+https://github.com/rust-lang/crates.io-index" 1870 + checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1871 + dependencies = [ 1872 + "try-lock", 1873 + ] 1874 + 1875 + [[package]] 1876 + name = "wasi" 1877 + version = "0.11.1+wasi-snapshot-preview1" 1878 + source = "registry+https://github.com/rust-lang/crates.io-index" 1879 + checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 1880 + 1881 + [[package]] 1882 + name = "wasi" 1883 + version = "0.14.2+wasi-0.2.4" 1884 + source = "registry+https://github.com/rust-lang/crates.io-index" 1885 + checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 1886 + dependencies = [ 1887 + "wit-bindgen-rt", 1888 + ] 1889 + 1890 + [[package]] 1891 + name = "wasm-bindgen" 1892 + version = "0.2.100" 1893 + source = "registry+https://github.com/rust-lang/crates.io-index" 1894 + checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1895 + dependencies = [ 1896 + "cfg-if", 1897 + "once_cell", 1898 + "rustversion", 1899 + "wasm-bindgen-macro", 1900 + ] 1901 + 1902 + [[package]] 1903 + name = "wasm-bindgen-backend" 1904 + version = "0.2.100" 1905 + source = "registry+https://github.com/rust-lang/crates.io-index" 1906 + checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1907 + dependencies = [ 1908 + "bumpalo", 1909 + "log", 1910 + "proc-macro2", 1911 + "quote", 1912 + "syn", 1913 + "wasm-bindgen-shared", 1914 + ] 1915 + 1916 + [[package]] 1917 + name = "wasm-bindgen-futures" 1918 + version = "0.4.50" 1919 + source = "registry+https://github.com/rust-lang/crates.io-index" 1920 + checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 1921 + dependencies = [ 1922 + "cfg-if", 1923 + "js-sys", 1924 + "once_cell", 1925 + "wasm-bindgen", 1926 + "web-sys", 1927 + ] 1928 + 1929 + [[package]] 1930 + name = "wasm-bindgen-macro" 1931 + version = "0.2.100" 1932 + source = "registry+https://github.com/rust-lang/crates.io-index" 1933 + checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1934 + dependencies = [ 1935 + "quote", 1936 + "wasm-bindgen-macro-support", 1937 + ] 1938 + 1939 + [[package]] 1940 + name = "wasm-bindgen-macro-support" 1941 + version = "0.2.100" 1942 + source = "registry+https://github.com/rust-lang/crates.io-index" 1943 + checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1944 + dependencies = [ 1945 + "proc-macro2", 1946 + "quote", 1947 + "syn", 1948 + "wasm-bindgen-backend", 1949 + "wasm-bindgen-shared", 1950 + ] 1951 + 1952 + [[package]] 1953 + name = "wasm-bindgen-shared" 1954 + version = "0.2.100" 1955 + source = "registry+https://github.com/rust-lang/crates.io-index" 1956 + checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1957 + dependencies = [ 1958 + "unicode-ident", 1959 + ] 1960 + 1961 + [[package]] 1962 + name = "web-sys" 1963 + version = "0.3.77" 1964 + source = "registry+https://github.com/rust-lang/crates.io-index" 1965 + checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 1966 + dependencies = [ 1967 + "js-sys", 1968 + "wasm-bindgen", 1969 + ] 1970 + 1971 + [[package]] 1972 + name = "webdriver" 1973 + version = "0.53.0" 1974 + source = "registry+https://github.com/rust-lang/crates.io-index" 1975 + checksum = "91d53921e1bef27512fa358179c9a22428d55778d2c2ae3c5c37a52b82ce6e92" 1976 + dependencies = [ 1977 + "base64", 1978 + "bytes", 1979 + "cookie 0.16.2", 1980 + "http 0.2.12", 1981 + "icu_segmenter", 1982 + "log", 1983 + "serde", 1984 + "serde_derive", 1985 + "serde_json", 1986 + "thiserror", 1987 + "time", 1988 + "url", 1989 + ] 1990 + 1991 + [[package]] 1992 + name = "winapi" 1993 + version = "0.3.9" 1994 + source = "registry+https://github.com/rust-lang/crates.io-index" 1995 + checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1996 + dependencies = [ 1997 + "winapi-i686-pc-windows-gnu", 1998 + "winapi-x86_64-pc-windows-gnu", 1999 + ] 2000 + 2001 + [[package]] 2002 + name = "winapi-i686-pc-windows-gnu" 2003 + version = "0.4.0" 2004 + source = "registry+https://github.com/rust-lang/crates.io-index" 2005 + checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2006 + 2007 + [[package]] 2008 + name = "winapi-x86_64-pc-windows-gnu" 2009 + version = "0.4.0" 2010 + source = "registry+https://github.com/rust-lang/crates.io-index" 2011 + checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2012 + 2013 + [[package]] 2014 + name = "windows-core" 2015 + version = "0.61.2" 2016 + source = "registry+https://github.com/rust-lang/crates.io-index" 2017 + checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 2018 + dependencies = [ 2019 + "windows-implement", 2020 + "windows-interface", 2021 + "windows-link", 2022 + "windows-result", 2023 + "windows-strings", 2024 + ] 2025 + 2026 + [[package]] 2027 + name = "windows-implement" 2028 + version = "0.60.0" 2029 + source = "registry+https://github.com/rust-lang/crates.io-index" 2030 + checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 2031 + dependencies = [ 2032 + "proc-macro2", 2033 + "quote", 2034 + "syn", 2035 + ] 2036 + 2037 + [[package]] 2038 + name = "windows-interface" 2039 + version = "0.59.1" 2040 + source = "registry+https://github.com/rust-lang/crates.io-index" 2041 + checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 2042 + dependencies = [ 2043 + "proc-macro2", 2044 + "quote", 2045 + "syn", 2046 + ] 2047 + 2048 + [[package]] 2049 + name = "windows-link" 2050 + version = "0.1.3" 2051 + source = "registry+https://github.com/rust-lang/crates.io-index" 2052 + checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 2053 + 2054 + [[package]] 2055 + name = "windows-registry" 2056 + version = "0.5.3" 2057 + source = "registry+https://github.com/rust-lang/crates.io-index" 2058 + checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" 2059 + dependencies = [ 2060 + "windows-link", 2061 + "windows-result", 2062 + "windows-strings", 2063 + ] 2064 + 2065 + [[package]] 2066 + name = "windows-result" 2067 + version = "0.3.4" 2068 + source = "registry+https://github.com/rust-lang/crates.io-index" 2069 + checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 2070 + dependencies = [ 2071 + "windows-link", 2072 + ] 2073 + 2074 + [[package]] 2075 + name = "windows-strings" 2076 + version = "0.4.2" 2077 + source = "registry+https://github.com/rust-lang/crates.io-index" 2078 + checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 2079 + dependencies = [ 2080 + "windows-link", 2081 + ] 2082 + 2083 + [[package]] 2084 + name = "windows-sys" 2085 + version = "0.52.0" 2086 + source = "registry+https://github.com/rust-lang/crates.io-index" 2087 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2088 + dependencies = [ 2089 + "windows-targets 0.52.6", 2090 + ] 2091 + 2092 + [[package]] 2093 + name = "windows-sys" 2094 + version = "0.59.0" 2095 + source = "registry+https://github.com/rust-lang/crates.io-index" 2096 + checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2097 + dependencies = [ 2098 + "windows-targets 0.52.6", 2099 + ] 2100 + 2101 + [[package]] 2102 + name = "windows-sys" 2103 + version = "0.60.2" 2104 + source = "registry+https://github.com/rust-lang/crates.io-index" 2105 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 2106 + dependencies = [ 2107 + "windows-targets 0.53.3", 2108 + ] 2109 + 2110 + [[package]] 2111 + name = "windows-targets" 2112 + version = "0.52.6" 2113 + source = "registry+https://github.com/rust-lang/crates.io-index" 2114 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2115 + dependencies = [ 2116 + "windows_aarch64_gnullvm 0.52.6", 2117 + "windows_aarch64_msvc 0.52.6", 2118 + "windows_i686_gnu 0.52.6", 2119 + "windows_i686_gnullvm 0.52.6", 2120 + "windows_i686_msvc 0.52.6", 2121 + "windows_x86_64_gnu 0.52.6", 2122 + "windows_x86_64_gnullvm 0.52.6", 2123 + "windows_x86_64_msvc 0.52.6", 2124 + ] 2125 + 2126 + [[package]] 2127 + name = "windows-targets" 2128 + version = "0.53.3" 2129 + source = "registry+https://github.com/rust-lang/crates.io-index" 2130 + checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" 2131 + dependencies = [ 2132 + "windows-link", 2133 + "windows_aarch64_gnullvm 0.53.0", 2134 + "windows_aarch64_msvc 0.53.0", 2135 + "windows_i686_gnu 0.53.0", 2136 + "windows_i686_gnullvm 0.53.0", 2137 + "windows_i686_msvc 0.53.0", 2138 + "windows_x86_64_gnu 0.53.0", 2139 + "windows_x86_64_gnullvm 0.53.0", 2140 + "windows_x86_64_msvc 0.53.0", 2141 + ] 2142 + 2143 + [[package]] 2144 + name = "windows_aarch64_gnullvm" 2145 + version = "0.52.6" 2146 + source = "registry+https://github.com/rust-lang/crates.io-index" 2147 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2148 + 2149 + [[package]] 2150 + name = "windows_aarch64_gnullvm" 2151 + version = "0.53.0" 2152 + source = "registry+https://github.com/rust-lang/crates.io-index" 2153 + checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 2154 + 2155 + [[package]] 2156 + name = "windows_aarch64_msvc" 2157 + version = "0.52.6" 2158 + source = "registry+https://github.com/rust-lang/crates.io-index" 2159 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2160 + 2161 + [[package]] 2162 + name = "windows_aarch64_msvc" 2163 + version = "0.53.0" 2164 + source = "registry+https://github.com/rust-lang/crates.io-index" 2165 + checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 2166 + 2167 + [[package]] 2168 + name = "windows_i686_gnu" 2169 + version = "0.52.6" 2170 + source = "registry+https://github.com/rust-lang/crates.io-index" 2171 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2172 + 2173 + [[package]] 2174 + name = "windows_i686_gnu" 2175 + version = "0.53.0" 2176 + source = "registry+https://github.com/rust-lang/crates.io-index" 2177 + checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 2178 + 2179 + [[package]] 2180 + name = "windows_i686_gnullvm" 2181 + version = "0.52.6" 2182 + source = "registry+https://github.com/rust-lang/crates.io-index" 2183 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2184 + 2185 + [[package]] 2186 + name = "windows_i686_gnullvm" 2187 + version = "0.53.0" 2188 + source = "registry+https://github.com/rust-lang/crates.io-index" 2189 + checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 2190 + 2191 + [[package]] 2192 + name = "windows_i686_msvc" 2193 + version = "0.52.6" 2194 + source = "registry+https://github.com/rust-lang/crates.io-index" 2195 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2196 + 2197 + [[package]] 2198 + name = "windows_i686_msvc" 2199 + version = "0.53.0" 2200 + source = "registry+https://github.com/rust-lang/crates.io-index" 2201 + checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 2202 + 2203 + [[package]] 2204 + name = "windows_x86_64_gnu" 2205 + version = "0.52.6" 2206 + source = "registry+https://github.com/rust-lang/crates.io-index" 2207 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2208 + 2209 + [[package]] 2210 + name = "windows_x86_64_gnu" 2211 + version = "0.53.0" 2212 + source = "registry+https://github.com/rust-lang/crates.io-index" 2213 + checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 2214 + 2215 + [[package]] 2216 + name = "windows_x86_64_gnullvm" 2217 + version = "0.52.6" 2218 + source = "registry+https://github.com/rust-lang/crates.io-index" 2219 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2220 + 2221 + [[package]] 2222 + name = "windows_x86_64_gnullvm" 2223 + version = "0.53.0" 2224 + source = "registry+https://github.com/rust-lang/crates.io-index" 2225 + checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 2226 + 2227 + [[package]] 2228 + name = "windows_x86_64_msvc" 2229 + version = "0.52.6" 2230 + source = "registry+https://github.com/rust-lang/crates.io-index" 2231 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2232 + 2233 + [[package]] 2234 + name = "windows_x86_64_msvc" 2235 + version = "0.53.0" 2236 + source = "registry+https://github.com/rust-lang/crates.io-index" 2237 + checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 2238 + 2239 + [[package]] 2240 + name = "wit-bindgen-rt" 2241 + version = "0.39.0" 2242 + source = "registry+https://github.com/rust-lang/crates.io-index" 2243 + checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 2244 + dependencies = [ 2245 + "bitflags", 2246 + ] 2247 + 2248 + [[package]] 2249 + name = "writeable" 2250 + version = "0.5.5" 2251 + source = "registry+https://github.com/rust-lang/crates.io-index" 2252 + checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 2253 + 2254 + [[package]] 2255 + name = "writeable" 2256 + version = "0.6.1" 2257 + source = "registry+https://github.com/rust-lang/crates.io-index" 2258 + checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 2259 + 2260 + [[package]] 2261 + name = "yoke" 2262 + version = "0.7.5" 2263 + source = "registry+https://github.com/rust-lang/crates.io-index" 2264 + checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 2265 + dependencies = [ 2266 + "serde", 2267 + "stable_deref_trait", 2268 + "yoke-derive 0.7.5", 2269 + "zerofrom", 2270 + ] 2271 + 2272 + [[package]] 2273 + name = "yoke" 2274 + version = "0.8.0" 2275 + source = "registry+https://github.com/rust-lang/crates.io-index" 2276 + checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 2277 + dependencies = [ 2278 + "serde", 2279 + "stable_deref_trait", 2280 + "yoke-derive 0.8.0", 2281 + "zerofrom", 2282 + ] 2283 + 2284 + [[package]] 2285 + name = "yoke-derive" 2286 + version = "0.7.5" 2287 + source = "registry+https://github.com/rust-lang/crates.io-index" 2288 + checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 2289 + dependencies = [ 2290 + "proc-macro2", 2291 + "quote", 2292 + "syn", 2293 + "synstructure", 2294 + ] 2295 + 2296 + [[package]] 2297 + name = "yoke-derive" 2298 + version = "0.8.0" 2299 + source = "registry+https://github.com/rust-lang/crates.io-index" 2300 + checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 2301 + dependencies = [ 2302 + "proc-macro2", 2303 + "quote", 2304 + "syn", 2305 + "synstructure", 2306 + ] 2307 + 2308 + [[package]] 2309 + name = "zerofrom" 2310 + version = "0.1.6" 2311 + source = "registry+https://github.com/rust-lang/crates.io-index" 2312 + checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 2313 + dependencies = [ 2314 + "zerofrom-derive", 2315 + ] 2316 + 2317 + [[package]] 2318 + name = "zerofrom-derive" 2319 + version = "0.1.6" 2320 + source = "registry+https://github.com/rust-lang/crates.io-index" 2321 + checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 2322 + dependencies = [ 2323 + "proc-macro2", 2324 + "quote", 2325 + "syn", 2326 + "synstructure", 2327 + ] 2328 + 2329 + [[package]] 2330 + name = "zeroize" 2331 + version = "1.8.1" 2332 + source = "registry+https://github.com/rust-lang/crates.io-index" 2333 + checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2334 + 2335 + [[package]] 2336 + name = "zerotrie" 2337 + version = "0.2.2" 2338 + source = "registry+https://github.com/rust-lang/crates.io-index" 2339 + checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 2340 + dependencies = [ 2341 + "displaydoc", 2342 + "yoke 0.8.0", 2343 + "zerofrom", 2344 + ] 2345 + 2346 + [[package]] 2347 + name = "zerovec" 2348 + version = "0.10.4" 2349 + source = "registry+https://github.com/rust-lang/crates.io-index" 2350 + checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 2351 + dependencies = [ 2352 + "yoke 0.7.5", 2353 + "zerofrom", 2354 + "zerovec-derive 0.10.3", 2355 + ] 2356 + 2357 + [[package]] 2358 + name = "zerovec" 2359 + version = "0.11.2" 2360 + source = "registry+https://github.com/rust-lang/crates.io-index" 2361 + checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" 2362 + dependencies = [ 2363 + "yoke 0.8.0", 2364 + "zerofrom", 2365 + "zerovec-derive 0.11.1", 2366 + ] 2367 + 2368 + [[package]] 2369 + name = "zerovec-derive" 2370 + version = "0.10.3" 2371 + source = "registry+https://github.com/rust-lang/crates.io-index" 2372 + checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 2373 + dependencies = [ 2374 + "proc-macro2", 2375 + "quote", 2376 + "syn", 2377 + ] 2378 + 2379 + [[package]] 2380 + name = "zerovec-derive" 2381 + version = "0.11.1" 2382 + source = "registry+https://github.com/rust-lang/crates.io-index" 2383 + checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 2384 + dependencies = [ 2385 + "proc-macro2", 2386 + "quote", 2387 + "syn", 2388 + ]
+29
services/darkroom/Cargo.toml
··· 1 + [package] 2 + name = "darkroom" 3 + version = "0.1.0" 4 + edition = "2024" 5 + 6 + [dependencies] 7 + tokio = { version = "1.0", features = ["full"] } 8 + axum = "0.7" 9 + serde = { version = "1.0", features = ["derive"] } 10 + serde_json = "1.0" 11 + reqwest = { version = "0.12", features = ["json"] } 12 + anyhow = "1.0" 13 + minijinja = "2.0" 14 + tracing = "0.1" 15 + tracing-subscriber = "0.3" 16 + urlencoding = "2.1" 17 + fantoccini = "0.22" 18 + tower = "0.4" 19 + tower-http = { version = "0.5", features = ["cors", "trace"] } 20 + thiserror = "1.0" 21 + base64 = "0.22" 22 + futures = "0.3" 23 + chrono = "0.4.41" 24 + 25 + [profile.release] 26 + opt-level = 3 27 + lto = true 28 + codegen-units = 1 29 + panic = "abort"
+57
services/darkroom/README.md
··· 1 + # Grain Darkroom 2 + 3 + A Rust-based service for generating composite images from photo galleries. 4 + 5 + ### API Endpoints 6 + 7 + #### `GET /composite-preview` 8 + 9 + Generates an HTML preview of a photo grid layout. 10 + 11 + **Query Parameters:** 12 + 13 + - `thumbs` - Comma-separated list of thumbnail URLs 14 + - `title` - Gallery title (optional) 15 + - `handle` - Creator handle (optional) 16 + 17 + #### `GET /xrpc/social.grain.darkroom.getGalleryComposite` 18 + 19 + Creates a composite image from a gallery URI. 20 + 21 + **Query Parameters:** 22 + 23 + - `uri` - URI of the gallery to process 24 + 25 + **Returns:** JPEG image 26 + 27 + ### Dependencies 28 + 29 + - Rust 1.88+ 30 + - Chrome/Chromium for screenshot capture 31 + - Alpine Linux (for container deployment) 32 + 33 + ### Development 34 + 35 + ```bash 36 + # Run in development mode 37 + cargo run 38 + 39 + # Build release version 40 + cargo build --release 41 + 42 + # Run with Docker 43 + docker build -t darkroom . 44 + docker run -p 8080:8080 darkroom 45 + ``` 46 + 47 + The service will start on port 8080. 48 + 49 + ### Architecture 50 + 51 + The service is built using: 52 + 53 + - **Axum** - Web framework 54 + - **Tokio** - Async runtime 55 + - **headless_chrome** - Browser automation for screenshots 56 + - **Reqwest** - HTTP client for API calls 57 + - **Serde** - JSON serialization/deserialization
+65
services/darkroom/flake.lock
··· 1 + { 2 + "nodes": { 3 + "crane": { 4 + "locked": { 5 + "lastModified": 1753316655, 6 + "narHash": "sha256-tzWa2kmTEN69OEMhxFy+J2oWSvZP5QhEgXp3TROOzl0=", 7 + "owner": "ipetkov", 8 + "repo": "crane", 9 + "rev": "f35a3372d070c9e9ccb63ba7ce347f0634ddf3d2", 10 + "type": "github" 11 + }, 12 + "original": { 13 + "owner": "ipetkov", 14 + "repo": "crane", 15 + "type": "github" 16 + } 17 + }, 18 + "fenix": { 19 + "inputs": { 20 + "nixpkgs": [ 21 + "nixpkgs" 22 + ], 23 + "rust-analyzer-src": [] 24 + }, 25 + "locked": { 26 + "lastModified": 1753858083, 27 + "narHash": "sha256-9eNLBxVBaOLGTOC1QkwrzRtnb1x9MB/3PYLb+CiALZY=", 28 + "owner": "nix-community", 29 + "repo": "fenix", 30 + "rev": "2c5508b7563b9138a00cd82e213febfc9cbbb36c", 31 + "type": "github" 32 + }, 33 + "original": { 34 + "owner": "nix-community", 35 + "repo": "fenix", 36 + "type": "github" 37 + } 38 + }, 39 + "nixpkgs": { 40 + "locked": { 41 + "lastModified": 1753694789, 42 + "narHash": "sha256-cKgvtz6fKuK1Xr5LQW/zOUiAC0oSQoA9nOISB0pJZqM=", 43 + "owner": "NixOS", 44 + "repo": "nixpkgs", 45 + "rev": "dc9637876d0dcc8c9e5e22986b857632effeb727", 46 + "type": "github" 47 + }, 48 + "original": { 49 + "owner": "NixOS", 50 + "ref": "nixos-unstable", 51 + "repo": "nixpkgs", 52 + "type": "github" 53 + } 54 + }, 55 + "root": { 56 + "inputs": { 57 + "crane": "crane", 58 + "fenix": "fenix", 59 + "nixpkgs": "nixpkgs" 60 + } 61 + } 62 + }, 63 + "root": "root", 64 + "version": 7 65 + }
+144
services/darkroom/flake.nix
··· 1 + { 2 + description = "Darkroom service for Grain"; 3 + 4 + inputs = { 5 + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 + crane.url = "github:ipetkov/crane"; 7 + fenix = { 8 + url = "github:nix-community/fenix"; 9 + inputs.nixpkgs.follows = "nixpkgs"; 10 + inputs.rust-analyzer-src.follows = ""; 11 + }; 12 + }; 13 + 14 + outputs = { self, nixpkgs, crane, fenix }: 15 + let 16 + systems = [ "x86_64-linux" ]; 17 + forAllSystems = nixpkgs.lib.genAttrs systems; 18 + 19 + mkPackagesForSystem = system: 20 + let 21 + pkgs = import nixpkgs { 22 + inherit system; 23 + config = { allowUnfree = true; }; 24 + }; 25 + 26 + # Configure crane with stable Rust toolchain 27 + craneLib = (crane.mkLib pkgs).overrideToolchain 28 + fenix.packages.${system}.stable.toolchain; 29 + 30 + # Project source for crane 31 + src = pkgs.lib.cleanSourceWith { 32 + src = ./.; 33 + filter = path: type: 34 + (craneLib.filterCargoSources path type) || 35 + # Include templates and static directories for include_str! macros 36 + (pkgs.lib.hasInfix "/templates/" path) || 37 + (pkgs.lib.hasInfix "/static/" path) || 38 + (pkgs.lib.hasSuffix "/templates" path) || 39 + (pkgs.lib.hasSuffix "/static" path); 40 + }; 41 + 42 + commonArgs = { 43 + inherit src; 44 + version = "0.1.0"; 45 + strictDeps = true; 46 + pname = "darkroom"; 47 + name = "darkroom"; 48 + buildInputs = with pkgs; [ 49 + openssl 50 + pkg-config 51 + ]; 52 + nativeBuildInputs = with pkgs; [ 53 + pkg-config 54 + openssl.dev 55 + ]; 56 + 57 + # Environment variables for OpenSSL 58 + OPENSSL_NO_VENDOR = 1; 59 + PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig"; 60 + }; 61 + 62 + cargoArtifacts = craneLib.buildDepsOnly commonArgs; 63 + 64 + darkroom = craneLib.buildPackage (commonArgs // { 65 + inherit cargoArtifacts; 66 + doCheck = false; 67 + CARGO_PROFILE = "release"; 68 + }); 69 + 70 + # Docker image for deployment 71 + darkroomImg = let 72 + fontDirs = [ 73 + "${pkgs.corefonts}/share/fonts" 74 + "${pkgs.dejavu_fonts}/share/fonts" 75 + "${pkgs.liberation_ttf}/share/fonts" 76 + ]; 77 + fontsConf = pkgs.makeFontsConf { fontDirectories = fontDirs; }; 78 + in pkgs.dockerTools.buildImage { 79 + name = "darkroom"; 80 + tag = "latest"; 81 + contents = [ 82 + darkroom 83 + pkgs.chromium 84 + pkgs.chromedriver 85 + pkgs.cacert 86 + pkgs.corefonts 87 + pkgs.dejavu_fonts 88 + pkgs.liberation_ttf 89 + ]; 90 + 91 + runAsRoot = '' 92 + #!${pkgs.runtimeShell} 93 + mkdir -p /tmp /app/chrome-profile 94 + chmod 1777 /tmp 95 + chmod 755 /app/chrome-profile 96 + ''; 97 + 98 + config = { 99 + Cmd = [ "/bin/darkroom" ]; 100 + Env = [ 101 + "RUST_BACKTRACE=1" 102 + "RUST_LOG=debug" 103 + "CHROME_PATH=${pkgs.chromium}/bin/chromium" 104 + "CHROMEDRIVER_PATH=${pkgs.chromedriver}/bin/chromedriver" 105 + "BASE_URL=http://grain-darkroom.internal:8080" 106 + "FONTCONFIG_FILE=${fontsConf}" 107 + ]; 108 + ExposedPorts = { 109 + "8080/tcp" = {}; 110 + }; 111 + }; 112 + }; 113 + in 114 + { 115 + inherit darkroom darkroomImg; 116 + default = darkroom; 117 + }; 118 + in 119 + { 120 + packages = forAllSystems mkPackagesForSystem; 121 + 122 + devShells = forAllSystems (system: 123 + let 124 + pkgs = import nixpkgs { inherit system; }; 125 + craneLib = (crane.mkLib pkgs).overrideToolchain 126 + fenix.packages.${system}.stable.toolchain; 127 + in 128 + { 129 + default = craneLib.devShell { 130 + packages = with pkgs; [ 131 + nixpkgs-fmt 132 + nil 133 + dive 134 + flyctl 135 + chromium 136 + ]; 137 + 138 + # Set up environment for development 139 + RUST_LOG = "debug"; 140 + CHROME_PATH = "${pkgs.chromium}/bin/chromium"; 141 + }; 142 + }); 143 + }; 144 + }
+30
services/darkroom/fly.toml
··· 1 + # fly.toml app configuration file generated for grain-darkroom on 2025-07-29T07:55:42-07:00 2 + # 3 + # See https://fly.io/docs/reference/configuration/ for information about how to use this file. 4 + # 5 + 6 + app = 'grain-darkroom' 7 + primary_region = 'sea' 8 + 9 + [build] 10 + image = 'registry.fly.io/grain-darkroom:latest' 11 + 12 + [env] 13 + BASE_URL = 'http://grain-darkroom.internal:8080' 14 + 15 + [[services]] 16 + protocol = 'tcp' 17 + internal_port = 8080 18 + auto_stop_machines = 'off' 19 + auto_start_machines = true 20 + min_machines_running = 1 21 + processes = ['app'] 22 + 23 + [[services.ports]] 24 + port = 443 25 + handlers = ['tls', 'http'] 26 + 27 + [[vm]] 28 + memory = '2gb' 29 + cpu_kind = 'shared' 30 + cpus = 1
+60
services/darkroom/src/composite_handler.rs
··· 1 + use crate::gallery_service::{extract_thumbnails, fetch_gallery_data}; 2 + use crate::screenshot_service::{build_preview_url, capture_screenshot}; 3 + use anyhow::{anyhow, Result}; 4 + use axum::body::Body; 5 + use axum::http::{HeaderMap, HeaderValue}; 6 + use axum::response::Response; 7 + use std::collections::HashMap; 8 + use tracing::info; 9 + 10 + pub async fn handle_composite_api(params: HashMap<String, String>) -> Result<Response<Body>> { 11 + let gallery_uri = params 12 + .get("uri") 13 + .ok_or_else(|| anyhow!("Missing uri parameter"))?; 14 + 15 + // Fetch gallery data 16 + let gallery_data = fetch_gallery_data(gallery_uri).await?; 17 + 18 + // Extract thumbnail URLs 19 + let thumb_urls = extract_thumbnails(&gallery_data, 9)?; 20 + 21 + info!("Extracted thumbnails: {:?}", thumb_urls); 22 + info!( 23 + "Building preview URL with title: {:?}", 24 + gallery_data.title.as_deref().unwrap_or("") 25 + ); 26 + 27 + // Build preview URL for screenshot 28 + let base_url = std::env::var("BASE_URL") 29 + .unwrap_or_else(|_| "http://[::]:8080".to_string()); 30 + 31 + let preview_url = build_preview_url( 32 + &base_url, 33 + &thumb_urls, 34 + gallery_data.title.as_deref().unwrap_or(""), 35 + gallery_data 36 + .creator 37 + .as_ref() 38 + .and_then(|c| c.handle.as_deref()) 39 + .unwrap_or(""), 40 + ); 41 + 42 + info!("Capturing screenshot for preview URL: {}", preview_url); 43 + 44 + // Capture screenshot 45 + let screenshot = capture_screenshot(&preview_url).await?; 46 + 47 + info!("Screenshot captured successfully"); 48 + 49 + let mut headers = HeaderMap::new(); 50 + headers.insert("Content-Type", HeaderValue::from_static("image/jpeg")); 51 + headers.insert( 52 + "Cache-Control", 53 + HeaderValue::from_static("public, max-age=3600"), 54 + ); 55 + 56 + let mut response = Response::new(Body::from(screenshot)); 57 + *response.headers_mut() = headers; 58 + 59 + Ok(response) 60 + }
+48
services/darkroom/src/gallery_service.rs
··· 1 + use crate::types::GalleryResponse; 2 + use anyhow::{anyhow, Result}; 3 + use reqwest; 4 + use tracing::info; 5 + 6 + pub async fn fetch_gallery_data(gallery_uri: &str) -> Result<GalleryResponse> { 7 + let gallery_url = format!( 8 + "https://grain.social/xrpc/social.grain.gallery.getGallery?uri={}", 9 + urlencoding::encode(gallery_uri) 10 + ); 11 + 12 + info!("Fetching gallery data from: {}", gallery_url); 13 + 14 + let response = reqwest::get(&gallery_url).await?; 15 + 16 + if !response.status().is_success() { 17 + return Err(anyhow!("Failed to fetch gallery: {}", response.status())); 18 + } 19 + 20 + let data: GalleryResponse = response.json().await?; 21 + 22 + if data.items.is_empty() { 23 + return Err(anyhow!("No items found in gallery")); 24 + } 25 + 26 + Ok(data) 27 + } 28 + 29 + pub fn extract_thumbnails(gallery_data: &GalleryResponse, max_count: usize) -> Result<Vec<String>> { 30 + let thumb_urls: Vec<String> = gallery_data 31 + .items 32 + .iter() 33 + .filter_map(|item| { 34 + if !item.thumb.is_empty() { 35 + Some(item.thumb.clone()) 36 + } else { 37 + None 38 + } 39 + }) 40 + .take(max_count) 41 + .collect(); 42 + 43 + if thumb_urls.is_empty() { 44 + return Err(anyhow!("No thumbnail images found")); 45 + } 46 + 47 + Ok(thumb_urls) 48 + }
+71
services/darkroom/src/html_generator.rs
··· 1 + use crate::types::{CompositeOptions, GalleryItem}; 2 + use minijinja::{Environment, context}; 3 + use std::include_str; 4 + 5 + fn is_portrait(item: &GalleryItem) -> bool { 6 + item.aspect_ratio.height > item.aspect_ratio.width 7 + } 8 + 9 + fn create_template_env() -> Environment<'static> { 10 + let mut env = Environment::new(); 11 + 12 + // Add templates as strings (embedded at compile time) 13 + env.add_template( 14 + "single_image.html", 15 + include_str!("../templates/single_image.html"), 16 + ) 17 + .unwrap(); 18 + env.add_template( 19 + "multi_image.html", 20 + include_str!("../templates/multi_image.html"), 21 + ) 22 + .unwrap(); 23 + 24 + env 25 + } 26 + 27 + pub fn generate_grid_html(options: CompositeOptions) -> String { 28 + let CompositeOptions { 29 + items, 30 + title, 31 + handle, 32 + } = options; 33 + 34 + let env = create_template_env(); 35 + 36 + match items.len() { 37 + 1 => { 38 + let is_portrait = is_portrait(&items[0]); 39 + let template = env.get_template("single_image.html").unwrap(); 40 + template 41 + .render(context! { 42 + item => &items[0], 43 + title => title, 44 + handle => handle, 45 + is_portrait => is_portrait 46 + }) 47 + .unwrap() 48 + } 49 + 2..=9 => { 50 + let template = env.get_template("multi_image.html").unwrap(); 51 + template 52 + .render(context! { 53 + items => &items, 54 + title => title, 55 + handle => handle 56 + }) 57 + .unwrap() 58 + } 59 + _ => { 60 + // Fallback for more than 9 images - use first 9 61 + let template = env.get_template("multi_image.html").unwrap(); 62 + template 63 + .render(context! { 64 + items => &items[..9], 65 + title => title, 66 + handle => handle 67 + }) 68 + .unwrap() 69 + } 70 + } 71 + }
+88
services/darkroom/src/main.rs
··· 1 + use axum::{ 2 + body::Body, 3 + extract::Query, 4 + http::{StatusCode, header}, 5 + response::{Html, Response}, 6 + routing::get, 7 + Json, Router, 8 + }; 9 + use serde_json::json; 10 + use std::collections::HashMap; 11 + use tokio::net::TcpListener; 12 + use tower_http::{cors::CorsLayer, trace::TraceLayer}; 13 + use tracing::{info, warn}; 14 + 15 + mod composite_handler; 16 + mod gallery_service; 17 + mod html_generator; 18 + mod preview_handler; 19 + mod screenshot_service; 20 + mod types; 21 + 22 + use composite_handler::handle_composite_api; 23 + use preview_handler::handle_composite_preview; 24 + 25 + #[tokio::main] 26 + async fn main() -> anyhow::Result<()> { 27 + // Initialize tracing 28 + tracing_subscriber::fmt::init(); 29 + 30 + let app = Router::new() 31 + .route("/health", get(health_check)) 32 + .route("/composite-preview", get(preview_route)) 33 + .route("/xrpc/social.grain.darkroom.getGalleryComposite", get(api_route)) 34 + .route("/static/css/base.css", get(serve_css)) 35 + .layer(CorsLayer::permissive()) 36 + .layer(TraceLayer::new_for_http()) 37 + .fallback(not_found); 38 + 39 + let listener = TcpListener::bind("[::]:8080").await?; 40 + info!("Darkroom service listening on http://localhost:8080"); 41 + 42 + axum::serve(listener, app).await?; 43 + Ok(()) 44 + } 45 + 46 + async fn preview_route(Query(params): Query<HashMap<String, String>>) -> Result<Html<String>, StatusCode> { 47 + match handle_composite_preview(params) { 48 + Ok(html) => Ok(Html(html)), 49 + Err(e) => { 50 + warn!("Preview error: {}", e); 51 + Err(StatusCode::INTERNAL_SERVER_ERROR) 52 + } 53 + } 54 + } 55 + 56 + async fn api_route(Query(params): Query<HashMap<String, String>>) -> Result<Response<Body>, StatusCode> { 57 + match handle_composite_api(params).await { 58 + Ok(response) => Ok(response), 59 + Err(e) => { 60 + warn!("API error: {}", e); 61 + Err(StatusCode::INTERNAL_SERVER_ERROR) 62 + } 63 + } 64 + } 65 + 66 + async fn not_found() -> (StatusCode, Html<&'static str>) { 67 + ( 68 + StatusCode::NOT_FOUND, 69 + Html("<h1>404 - Page Not Found</h1>"), 70 + ) 71 + } 72 + 73 + async fn serve_css() -> Response<Body> { 74 + let css_content = include_str!("../static/css/base.css"); 75 + Response::builder() 76 + .status(StatusCode::OK) 77 + .header(header::CONTENT_TYPE, "text/css") 78 + .body(Body::from(css_content)) 79 + .unwrap() 80 + } 81 + 82 + async fn health_check() -> Json<serde_json::Value> { 83 + Json(json!({ 84 + "status": "healthy", 85 + "service": "darkroom", 86 + "timestamp": chrono::Utc::now().to_rfc3339() 87 + })) 88 + }
+39
services/darkroom/src/preview_handler.rs
··· 1 + use crate::html_generator::generate_grid_html; 2 + use crate::types::{AspectRatio, CompositeOptions, GalleryItem}; 3 + use anyhow::Result; 4 + use std::collections::HashMap; 5 + 6 + pub fn handle_composite_preview(params: HashMap<String, String>) -> Result<String> { 7 + let thumbs_param = params.get("thumbs").unwrap_or(&String::new()).clone(); 8 + let thumb_urls: Vec<String> = if !thumbs_param.is_empty() { 9 + thumbs_param.split(',').map(|s| s.to_string()).collect() 10 + } else { 11 + Vec::new() 12 + }; 13 + 14 + // Convert URLs to GalleryItem objects with default aspect ratios 15 + // Since we don't have actual aspect ratio data from URL params, use a default square ratio 16 + let items: Vec<GalleryItem> = thumb_urls 17 + .into_iter() 18 + .map(|thumb| GalleryItem { 19 + thumb, 20 + aspect_ratio: AspectRatio { 21 + width: 1.0, 22 + height: 1.0, 23 + }, 24 + extra: serde_json::Value::Null, 25 + }) 26 + .collect(); 27 + 28 + let title = params.get("title").unwrap_or(&String::new()).clone(); 29 + let handle = params.get("handle").unwrap_or(&String::new()).clone(); 30 + 31 + let options = CompositeOptions { 32 + items, 33 + title, 34 + handle, 35 + }; 36 + 37 + let html = generate_grid_html(options); 38 + Ok(html) 39 + }
+125
services/darkroom/src/screenshot_service.rs
··· 1 + use anyhow::{Result, anyhow}; 2 + use fantoccini::ClientBuilder; 3 + use std::net::TcpStream; 4 + use std::process::{Child, Command}; 5 + use std::{thread, time}; 6 + use tracing::info; 7 + 8 + pub async fn capture_screenshot(preview_url: &str) -> Result<Vec<u8>> { 9 + info!("Starting screenshot capture for: {}", preview_url); 10 + 11 + // Check if ChromeDriver is running on port 9515 12 + let chromedriver_addr = "127.0.0.1:9515"; 13 + let mut chromedriver_child: Option<Child> = None; 14 + let chromedriver_running = TcpStream::connect(chromedriver_addr).is_ok(); 15 + if !chromedriver_running { 16 + let chromedriver_path = std::env::var("CHROMEDRIVER_PATH") 17 + .unwrap_or_else(|_| "/usr/bin/chromedriver".to_string()); 18 + info!("Starting ChromeDriver at {}...", chromedriver_path); 19 + let child = Command::new(chromedriver_path) 20 + .arg("--port=9515") 21 + .spawn() 22 + .map_err(|e| anyhow!("Failed to start ChromeDriver: {}", e))?; 23 + chromedriver_child = Some(child); 24 + // Wait for ChromeDriver to become available 25 + let max_tries = 10; 26 + let delay = time::Duration::from_millis(300); 27 + let mut started = false; 28 + for _ in 0..max_tries { 29 + if TcpStream::connect(chromedriver_addr).is_ok() { 30 + started = true; 31 + break; 32 + } 33 + thread::sleep(delay); 34 + } 35 + if !started { 36 + return Err(anyhow!("ChromeDriver did not start on port 9515")); 37 + } 38 + } 39 + 40 + let mut caps = serde_json::map::Map::new(); 41 + let opts = serde_json::json!({ 42 + "binary": std::env::var("CHROME_PATH") 43 + .unwrap_or_else(|_| "/usr/bin/chromium".to_string()), 44 + "args": [ 45 + "--headless", 46 + "--no-sandbox", 47 + "--disable-gpu", 48 + "--disable-dev-shm-usage", 49 + "--window-size=1500,2139", 50 + "--font-render-hinting=medium", 51 + "--enable-font-antialiasing", 52 + ], 53 + }); 54 + caps.insert("goog:chromeOptions".to_string(), opts); 55 + 56 + // Create WebDriver client 57 + let client = ClientBuilder::native() 58 + .capabilities(caps) 59 + .connect("http://localhost:9515") 60 + .await 61 + .map_err(|e| anyhow!("Failed to connect to ChromeDriver: {}", e))?; 62 + 63 + let result = async { 64 + info!("Navigating to URL: {}", preview_url); 65 + client 66 + .goto(preview_url) 67 + .await 68 + .map_err(|e| anyhow!("Failed to navigate to {}: {}", preview_url, e))?; 69 + 70 + // Wait for body to be present 71 + use fantoccini::wd::Locator; 72 + client.wait().for_element(Locator::Css("body")).await?; 73 + 74 + // Wait for all web fonts to be loaded 75 + client 76 + .execute( 77 + "return document.fonts.status === 'loaded' ? true : await document.fonts.ready.then(() => true);", 78 + vec![], 79 + ) 80 + .await 81 + .map_err(|e| anyhow!("Failed to wait for fonts: {}", e))?; 82 + 83 + // Add a small delay to ensure rendering 84 + tokio::time::sleep(std::time::Duration::from_millis(300)).await; 85 + 86 + info!("Taking screenshot..."); 87 + let screenshot_data = client 88 + .screenshot() 89 + .await 90 + .map_err(|e| anyhow!("Failed to capture screenshot: {}", e))?; 91 + 92 + info!( 93 + "Screenshot captured successfully, size: {} bytes", 94 + screenshot_data.len() 95 + ); 96 + Ok(screenshot_data) 97 + } 98 + .await; 99 + 100 + // Clean up 101 + client.close().await.ok(); 102 + 103 + // If we started ChromeDriver, kill it 104 + if let Some(mut child) = chromedriver_child { 105 + let _ = child.kill(); 106 + } 107 + 108 + result 109 + } 110 + 111 + pub fn build_preview_url( 112 + base_url: &str, 113 + thumb_urls: &[String], 114 + title: &str, 115 + handle: &str, 116 + ) -> String { 117 + let thumbs_param = thumb_urls.join(","); 118 + format!( 119 + "{}/composite-preview?thumbs={}&title={}&handle={}", 120 + base_url, 121 + urlencoding::encode(&thumbs_param), 122 + urlencoding::encode(title), 123 + urlencoding::encode(handle) 124 + ) 125 + }
+37
services/darkroom/src/types.rs
··· 1 + use serde::{Deserialize, Serialize}; 2 + 3 + #[derive(Debug, Clone, Deserialize, Serialize)] 4 + pub struct AspectRatio { 5 + pub width: f32, 6 + pub height: f32, 7 + } 8 + 9 + #[derive(Debug, Clone, Deserialize, Serialize)] 10 + pub struct GalleryItem { 11 + pub thumb: String, 12 + #[serde(rename = "aspectRatio")] 13 + pub aspect_ratio: AspectRatio, 14 + #[serde(flatten)] 15 + pub extra: serde_json::Value, 16 + } 17 + 18 + #[derive(Debug, Deserialize, Serialize)] 19 + pub struct GalleryCreator { 20 + pub handle: Option<String>, 21 + } 22 + 23 + #[derive(Debug, Deserialize, Serialize)] 24 + pub struct GalleryResponse { 25 + pub items: Vec<GalleryItem>, 26 + pub title: Option<String>, 27 + pub creator: Option<GalleryCreator>, 28 + #[serde(flatten)] 29 + pub extra: serde_json::Value, 30 + } 31 + 32 + #[derive(Debug, Clone)] 33 + pub struct CompositeOptions { 34 + pub items: Vec<GalleryItem>, 35 + pub title: String, 36 + pub handle: String, 37 + }
+365
services/darkroom/static/css/base.css
··· 1 + body { 2 + background: #fff; 3 + margin: 0; 4 + padding: 0; 5 + width: 1500px; 6 + height: 2000px; 7 + font-family: "DejaVu Sans", "Liberation Sans", Arial, sans-serif; 8 + position: relative; 9 + } 10 + 11 + /* Content area - absolute positioned */ 12 + .content { 13 + position: absolute; 14 + top: 20px; 15 + left: 20px; 16 + width: 1460px; /* 1500px - 40px padding */ 17 + height: 1760px; /* 2000px - 40px padding - 200px footer */ 18 + } 19 + 20 + /* Footer fixed at bottom */ 21 + .footer { 22 + position: absolute; 23 + bottom: 0; 24 + left: 0; 25 + width: 1500px; 26 + height: 220px; /* 200px + 20px to center between grid end and bottom */ 27 + display: flex; 28 + align-items: center; 29 + justify-content: space-between; 30 + padding: 0 20px; 31 + box-sizing: border-box; 32 + } 33 + 34 + .title { 35 + font-weight: 400; 36 + color: #212529; 37 + line-height: 1.1; 38 + max-width: 800px; /* Reserve space for handle/grain on right */ 39 + font-size: clamp(32px, 6vw, 72px); /* Scales from 32px to 72px based on container width */ 40 + word-wrap: break-word; 41 + overflow-wrap: break-word; 42 + } 43 + 44 + .handle { 45 + font-size: 38px; 46 + font-weight: bold; 47 + color: #212529; 48 + line-height: 1.1; 49 + } 50 + 51 + .grain { 52 + font-size: 32px; 53 + font-weight: bold; 54 + color: #6c757d; 55 + line-height: 1.1; 56 + margin-top: 8px; 57 + } 58 + 59 + /* Single image - centered */ 60 + .single-image { 61 + width: 100%; 62 + height: 100%; 63 + display: flex; 64 + align-items: center; 65 + justify-content: center; 66 + } 67 + 68 + .single-image img { 69 + max-width: 100%; 70 + max-height: 100%; 71 + object-fit: cover; 72 + display: block; 73 + } 74 + 75 + /* Multi-image layouts using absolute positioning */ 76 + .multi-grid { 77 + position: relative; 78 + width: 100%; 79 + height: 100%; 80 + } 81 + 82 + .multi-grid img { 83 + position: absolute; 84 + object-fit: cover; 85 + } 86 + 87 + /* Two images: stacked vertically */ 88 + .grid-2 img:nth-child(1) { 89 + top: 0; 90 + left: 0; 91 + width: 1460px; 92 + height: 877px; 93 + } 94 + .grid-2 img:nth-child(2) { 95 + top: 882px; 96 + left: 0; 97 + width: 1460px; 98 + height: 878px; 99 + } 100 + 101 + /* Three images: 2 top, 1 bottom spanning full width */ 102 + .grid-3 img:nth-child(1) { 103 + top: 0; 104 + left: 0; 105 + width: 727px; 106 + height: 877px; 107 + } 108 + .grid-3 img:nth-child(2) { 109 + top: 0; 110 + left: 732px; 111 + width: 728px; 112 + height: 877px; 113 + } 114 + .grid-3 img:nth-child(3) { 115 + top: 882px; 116 + left: 0; 117 + width: 1460px; 118 + height: 878px; 119 + } 120 + 121 + /* Four images: 2x2 grid */ 122 + .grid-4 img:nth-child(1) { 123 + top: 0; 124 + left: 0; 125 + width: 727px; 126 + height: 877px; 127 + } 128 + .grid-4 img:nth-child(2) { 129 + top: 0; 130 + left: 732px; 131 + width: 728px; 132 + height: 877px; 133 + } 134 + .grid-4 img:nth-child(3) { 135 + top: 882px; 136 + left: 0; 137 + width: 727px; 138 + height: 878px; 139 + } 140 + .grid-4 img:nth-child(4) { 141 + top: 882px; 142 + left: 732px; 143 + width: 728px; 144 + height: 878px; 145 + } 146 + 147 + /* Five images: 2x2 + 1 bottom spanning full */ 148 + .grid-5 img:nth-child(1) { 149 + top: 0; 150 + left: 0; 151 + width: 727px; 152 + height: 583px; 153 + } 154 + .grid-5 img:nth-child(2) { 155 + top: 0; 156 + left: 732px; 157 + width: 728px; 158 + height: 583px; 159 + } 160 + .grid-5 img:nth-child(3) { 161 + top: 588px; 162 + left: 0; 163 + width: 727px; 164 + height: 583px; 165 + } 166 + .grid-5 img:nth-child(4) { 167 + top: 588px; 168 + left: 732px; 169 + width: 728px; 170 + height: 583px; 171 + } 172 + .grid-5 img:nth-child(5) { 173 + top: 1176px; 174 + left: 0; 175 + width: 1460px; 176 + height: 584px; 177 + } 178 + 179 + /* Six images: 2x3 grid */ 180 + .grid-6 img:nth-child(1) { 181 + top: 0; 182 + left: 0; 183 + width: 727px; 184 + height: 583px; 185 + } 186 + .grid-6 img:nth-child(2) { 187 + top: 0; 188 + left: 732px; 189 + width: 728px; 190 + height: 583px; 191 + } 192 + .grid-6 img:nth-child(3) { 193 + top: 588px; 194 + left: 0; 195 + width: 727px; 196 + height: 583px; 197 + } 198 + .grid-6 img:nth-child(4) { 199 + top: 588px; 200 + left: 732px; 201 + width: 728px; 202 + height: 583px; 203 + } 204 + .grid-6 img:nth-child(5) { 205 + top: 1176px; 206 + left: 0; 207 + width: 727px; 208 + height: 584px; 209 + } 210 + .grid-6 img:nth-child(6) { 211 + top: 1176px; 212 + left: 732px; 213 + width: 728px; 214 + height: 584px; 215 + } 216 + 217 + /* Seven images: 3x3 with last row having 1 image */ 218 + .grid-7 img:nth-child(1) { 219 + top: 0; 220 + left: 0; 221 + width: 483px; 222 + height: 583px; 223 + } 224 + .grid-7 img:nth-child(2) { 225 + top: 0; 226 + left: 488px; 227 + width: 484px; 228 + height: 583px; 229 + } 230 + .grid-7 img:nth-child(3) { 231 + top: 0; 232 + left: 977px; 233 + width: 483px; 234 + height: 583px; 235 + } 236 + .grid-7 img:nth-child(4) { 237 + top: 588px; 238 + left: 0; 239 + width: 483px; 240 + height: 583px; 241 + } 242 + .grid-7 img:nth-child(5) { 243 + top: 588px; 244 + left: 488px; 245 + width: 484px; 246 + height: 583px; 247 + } 248 + .grid-7 img:nth-child(6) { 249 + top: 588px; 250 + left: 977px; 251 + width: 483px; 252 + height: 583px; 253 + } 254 + .grid-7 img:nth-child(7) { 255 + top: 1176px; 256 + left: 0; 257 + width: 1460px; 258 + height: 584px; 259 + } 260 + 261 + /* Eight images: 3x3 with last row having 2 images */ 262 + .grid-8 img:nth-child(1) { 263 + top: 0; 264 + left: 0; 265 + width: 483px; 266 + height: 583px; 267 + } 268 + .grid-8 img:nth-child(2) { 269 + top: 0; 270 + left: 488px; 271 + width: 484px; 272 + height: 583px; 273 + } 274 + .grid-8 img:nth-child(3) { 275 + top: 0; 276 + left: 977px; 277 + width: 483px; 278 + height: 583px; 279 + } 280 + .grid-8 img:nth-child(4) { 281 + top: 588px; 282 + left: 0; 283 + width: 483px; 284 + height: 583px; 285 + } 286 + .grid-8 img:nth-child(5) { 287 + top: 588px; 288 + left: 488px; 289 + width: 484px; 290 + height: 583px; 291 + } 292 + .grid-8 img:nth-child(6) { 293 + top: 588px; 294 + left: 977px; 295 + width: 483px; 296 + height: 583px; 297 + } 298 + .grid-8 img:nth-child(7) { 299 + top: 1176px; 300 + left: 0; 301 + width: 727px; 302 + height: 584px; 303 + } 304 + .grid-8 img:nth-child(8) { 305 + top: 1176px; 306 + left: 732px; 307 + width: 728px; 308 + height: 584px; 309 + } 310 + 311 + /* Nine images: 3x3 grid */ 312 + .grid-9 img:nth-child(1) { 313 + top: 0; 314 + left: 0; 315 + width: 483px; 316 + height: 583px; 317 + } 318 + .grid-9 img:nth-child(2) { 319 + top: 0; 320 + left: 488px; 321 + width: 484px; 322 + height: 583px; 323 + } 324 + .grid-9 img:nth-child(3) { 325 + top: 0; 326 + left: 977px; 327 + width: 483px; 328 + height: 583px; 329 + } 330 + .grid-9 img:nth-child(4) { 331 + top: 588px; 332 + left: 0; 333 + width: 483px; 334 + height: 583px; 335 + } 336 + .grid-9 img:nth-child(5) { 337 + top: 588px; 338 + left: 488px; 339 + width: 484px; 340 + height: 583px; 341 + } 342 + .grid-9 img:nth-child(6) { 343 + top: 588px; 344 + left: 977px; 345 + width: 483px; 346 + height: 583px; 347 + } 348 + .grid-9 img:nth-child(7) { 349 + top: 1176px; 350 + left: 0; 351 + width: 483px; 352 + height: 584px; 353 + } 354 + .grid-9 img:nth-child(8) { 355 + top: 1176px; 356 + left: 488px; 357 + width: 484px; 358 + height: 584px; 359 + } 360 + .grid-9 img:nth-child(9) { 361 + top: 1176px; 362 + left: 977px; 363 + width: 483px; 364 + height: 584px; 365 + }
+29
services/darkroom/templates/multi_image.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <title>Composite Preview</title> 6 + <link rel="stylesheet" href="/static/css/base.css"> 7 + </head> 8 + <body> 9 + <div class="content"> 10 + <div class="multi-grid grid-{{ items|length }}"> 11 + {% for item in items %} 12 + <img src="{{ item.thumb }}" /> 13 + {% endfor %} 14 + </div> 15 + </div> 16 + 17 + {% if title or handle %} 18 + <div class="footer"> 19 + <span class="title">{{ title }}</span> 20 + <div style="text-align: right;"> 21 + {% if handle %} 22 + <div class="handle">@{{ handle }}</div> 23 + {% endif %} 24 + <div class="grain">grain.social</div> 25 + </div> 26 + </div> 27 + {% endif %} 28 + </body> 29 + </html>
+27
services/darkroom/templates/single_image.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <title>Composite Preview</title> 6 + <link rel="stylesheet" href="/static/css/base.css"> 7 + </head> 8 + <body> 9 + <div class="content"> 10 + <div class="single-image"> 11 + <img src="{{ item.thumb }}" /> 12 + </div> 13 + </div> 14 + 15 + {% if title or handle %} 16 + <div class="footer"> 17 + <span class="title">{{ title }}</span> 18 + <div style="text-align: right;"> 19 + {% if handle %} 20 + <div class="handle">@{{ handle }}</div> 21 + {% endif %} 22 + <div class="grain">grain.social</div> 23 + </div> 24 + </div> 25 + {% endif %} 26 + </body> 27 + </html>
+11
services/nginx/nginx.conf
··· 16 16 default http://atphoto.internal:8080; 17 17 } 18 18 19 + map $http_host $darkroom { 20 + default http://grain-darkroom.internal:8080; 21 + } 22 + 19 23 map $http_upgrade $connection_upgrade { 20 24 default upgrade; 21 25 '' close; ··· 27 31 28 32 if ($http_user_agent ~* "(AI2Bot|Ai2Bot\-Dolma|aiHitBot|Amazonbot|Andibot|anthropic\-ai|Applebot|Applebot\-Extended|bedrockbot|Brightbot\ 1\.0|Bytespider|CCBot|ChatGPT\-User|Claude\-SearchBot|Claude\-User|Claude\-Web|ClaudeBot|cohere\-ai|cohere\-training\-data\-crawler|Cotoyogi|Crawlspace|Diffbot|DuckAssistBot|EchoboxBot|FacebookBot|facebookexternalhit|Factset_spyderbot|FirecrawlAgent|FriendlyCrawler|Google\-CloudVertexBot|Google\-Extended|GoogleOther|GoogleOther\-Image|GoogleOther\-Video|GPTBot|iaskspider/2\.0|ICC\-Crawler|ImagesiftBot|img2dataset|ISSCyberRiskCrawler|Kangaroo\ Bot|meta\-externalagent|Meta\-ExternalAgent|meta\-externalfetcher|Meta\-ExternalFetcher|MistralAI\-User/1\.0|MyCentralAIScraperBot|NovaAct|OAI\-SearchBot|omgili|omgilibot|Operator|PanguBot|Panscient|panscient\.com|Perplexity\-User|PerplexityBot|PetalBot|PhindBot|Poseidon\ Research\ Crawler|QualifiedBot|QuillBot|quillbot\.com|SBIntuitionsBot|Scrapy|SemrushBot|SemrushBot\-BA|SemrushBot\-CT|SemrushBot\-OCOB|SemrushBot\-SI|SemrushBot\-SWA|Sidetrade\ indexer\ bot|TikTokSpider|Timpibot|VelenPublicWebCrawler|Webzio\-Extended|wpbot|YandexAdditional|YandexAdditionalBot|YouBot)") { 29 33 return 403; 34 + } 35 + 36 + location ~ ^/xrpc/social\.grain\.darkroom\..* { 37 + proxy_pass $darkroom; 38 + proxy_set_header Host $host; 39 + proxy_set_header Upgrade $http_upgrade; 40 + proxy_set_header Connection $connection_upgrade; 30 41 } 31 42 32 43 location ~ ^/xrpc/social\.grain\..* {