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

feat: add beginnings of a rust-based cli tool

+138
.github/workflows/cli.yml
··· 1 + name: Release CLI Binaries 2 + 3 + on: 4 + push: 5 + tags: 6 + - 'cli@*' 7 + workflow_dispatch: 8 + inputs: 9 + tag: 10 + description: 'Tag to release (e.g. cli@1.0.0)' 11 + required: true 12 + type: string 13 + 14 + jobs: 15 + build: 16 + name: Build CLI Binaries 17 + runs-on: ubuntu-latest 18 + defaults: 19 + run: 20 + working-directory: cli 21 + steps: 22 + - name: Checkout 23 + uses: actions/checkout@v4 24 + 25 + - name: Install Nix 26 + uses: DeterminateSystems/nix-installer-action@main 27 + with: 28 + logger: pretty 29 + 30 + - name: Setup Nix cache 31 + uses: DeterminateSystems/magic-nix-cache-action@main 32 + 33 + - name: Check flake 34 + run: nix flake check 35 + 36 + - name: Build Darwin aarch64 only (for testing) 37 + run: | 38 + echo "Building for macOS aarch64 only..." 39 + mkdir -p releases 40 + nix build .#grain-macos-aarch64 -o result-macos-aarch64 41 + cp result-macos-aarch64/bin/grain releases/grain-darwin-aarch64 42 + 43 + - name: Prepare release assets 44 + run: | 45 + mkdir -p release-assets 46 + cp releases/grain-darwin-aarch64 release-assets/ 47 + cp install.sh release-assets/ 48 + 49 + # Create checksums 50 + cd release-assets 51 + sha256sum grain-darwin-aarch64 > checksums.txt 52 + 53 + # List files for verification 54 + ls -la 55 + 56 + - name: Get tag name 57 + id: tag 58 + run: | 59 + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then 60 + echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT 61 + else 62 + echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT 63 + fi 64 + 65 + - name: Create Release 66 + uses: softprops/action-gh-release@v1 67 + with: 68 + tag_name: ${{ steps.tag.outputs.tag }} 69 + name: Grain CLI ${{ steps.tag.outputs.tag }} 70 + draft: false 71 + prerelease: false 72 + files: | 73 + cli/release-assets/* 74 + body: | 75 + ## Grain CLI ${{ steps.tag.outputs.tag }} (Test Build) 76 + 77 + **Note: This is a test build with only macOS Apple Silicon support.** 78 + 79 + ### Installation 80 + 81 + Quick install: 82 + ```bash 83 + curl -L https://github.com/grainsocial/grain/releases/download/${{ steps.tag.outputs.tag }}/install.sh | bash 84 + ``` 85 + 86 + Or download manually: 87 + 88 + - **macOS Apple Silicon**: `grain-darwin-aarch64` 89 + 90 + ### Usage 91 + 92 + ```bash 93 + grain login 94 + grain gallery list 95 + grain gallery create 96 + ``` 97 + 98 + ### Checksums 99 + 100 + See `checksums.txt` for SHA256 checksums. 101 + env: 102 + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 103 + 104 + test-install: 105 + name: Test Installation 106 + needs: build 107 + strategy: 108 + matrix: 109 + os: [ubuntu-latest, macos-latest] 110 + runs-on: ${{ matrix.os }} 111 + steps: 112 + - name: Checkout 113 + uses: actions/checkout@v4 114 + 115 + - name: Get tag name 116 + id: tag 117 + run: | 118 + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then 119 + echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT 120 + else 121 + echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT 122 + fi 123 + 124 + - name: Test install script 125 + run: | 126 + # Wait for release to be available 127 + sleep 30 128 + 129 + # Test the install script 130 + curl -L https://github.com/grainsocial/grain/releases/download/${{ steps.tag.outputs.tag }}/grain-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m) -o grain-test || exit 0 131 + 132 + if [ -f grain-test ]; then 133 + chmod +x grain-test 134 + ./grain-test --help 135 + echo "✅ Binary works correctly" 136 + else 137 + echo "⚠️ Binary not available yet for this platform" 138 + fi
+9
README.md
··· 50 50 - **Tech Stack**: Deno, TypeScript 51 51 - **Features**: Content labeling, moderation tools 52 52 53 + ### 💻 [CLI](/cli) 54 + 55 + Command-line interface for interacting with Grain Social from the terminal. 56 + Provides tools for gallery management, photo uploads, and platform interaction. 57 + 58 + - **Tech Stack**: Rust 59 + - **Features**: Gallery creation/management, photo uploads, authentication 60 + - **Installation**: Download from [releases](https://github.com/grainsocial/grain/releases) 61 + 53 62 ### 🌐 [Infrastructure](/nginx) 54 63 55 64 Nginx reverse proxy configuration for routing requests between services.
+1
cli/.gitignore
··· 1 + target
+2900
cli/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 = "aligned-vec" 22 + version = "0.6.4" 23 + source = "registry+https://github.com/rust-lang/crates.io-index" 24 + checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" 25 + dependencies = [ 26 + "equator", 27 + ] 28 + 29 + [[package]] 30 + name = "android-tzdata" 31 + version = "0.1.1" 32 + source = "registry+https://github.com/rust-lang/crates.io-index" 33 + checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 34 + 35 + [[package]] 36 + name = "android_system_properties" 37 + version = "0.1.5" 38 + source = "registry+https://github.com/rust-lang/crates.io-index" 39 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 40 + dependencies = [ 41 + "libc", 42 + ] 43 + 44 + [[package]] 45 + name = "anstream" 46 + version = "0.6.20" 47 + source = "registry+https://github.com/rust-lang/crates.io-index" 48 + checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" 49 + dependencies = [ 50 + "anstyle", 51 + "anstyle-parse", 52 + "anstyle-query", 53 + "anstyle-wincon", 54 + "colorchoice", 55 + "is_terminal_polyfill", 56 + "utf8parse", 57 + ] 58 + 59 + [[package]] 60 + name = "anstyle" 61 + version = "1.0.11" 62 + source = "registry+https://github.com/rust-lang/crates.io-index" 63 + checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" 64 + 65 + [[package]] 66 + name = "anstyle-parse" 67 + version = "0.2.7" 68 + source = "registry+https://github.com/rust-lang/crates.io-index" 69 + checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 70 + dependencies = [ 71 + "utf8parse", 72 + ] 73 + 74 + [[package]] 75 + name = "anstyle-query" 76 + version = "1.1.4" 77 + source = "registry+https://github.com/rust-lang/crates.io-index" 78 + checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" 79 + dependencies = [ 80 + "windows-sys 0.60.2", 81 + ] 82 + 83 + [[package]] 84 + name = "anstyle-wincon" 85 + version = "3.0.10" 86 + source = "registry+https://github.com/rust-lang/crates.io-index" 87 + checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" 88 + dependencies = [ 89 + "anstyle", 90 + "once_cell_polyfill", 91 + "windows-sys 0.60.2", 92 + ] 93 + 94 + [[package]] 95 + name = "anyhow" 96 + version = "1.0.98" 97 + source = "registry+https://github.com/rust-lang/crates.io-index" 98 + checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 99 + 100 + [[package]] 101 + name = "arbitrary" 102 + version = "1.4.1" 103 + source = "registry+https://github.com/rust-lang/crates.io-index" 104 + checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" 105 + 106 + [[package]] 107 + name = "arg_enum_proc_macro" 108 + version = "0.3.4" 109 + source = "registry+https://github.com/rust-lang/crates.io-index" 110 + checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" 111 + dependencies = [ 112 + "proc-macro2", 113 + "quote", 114 + "syn", 115 + ] 116 + 117 + [[package]] 118 + name = "arrayvec" 119 + version = "0.7.6" 120 + source = "registry+https://github.com/rust-lang/crates.io-index" 121 + checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 122 + 123 + [[package]] 124 + name = "atomic-waker" 125 + version = "1.1.2" 126 + source = "registry+https://github.com/rust-lang/crates.io-index" 127 + checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 128 + 129 + [[package]] 130 + name = "autocfg" 131 + version = "1.5.0" 132 + source = "registry+https://github.com/rust-lang/crates.io-index" 133 + checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 134 + 135 + [[package]] 136 + name = "av1-grain" 137 + version = "0.2.4" 138 + source = "registry+https://github.com/rust-lang/crates.io-index" 139 + checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" 140 + dependencies = [ 141 + "anyhow", 142 + "arrayvec", 143 + "log", 144 + "nom", 145 + "num-rational", 146 + "v_frame", 147 + ] 148 + 149 + [[package]] 150 + name = "avif-serialize" 151 + version = "0.8.5" 152 + source = "registry+https://github.com/rust-lang/crates.io-index" 153 + checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42" 154 + dependencies = [ 155 + "arrayvec", 156 + ] 157 + 158 + [[package]] 159 + name = "backtrace" 160 + version = "0.3.75" 161 + source = "registry+https://github.com/rust-lang/crates.io-index" 162 + checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" 163 + dependencies = [ 164 + "addr2line", 165 + "cfg-if", 166 + "libc", 167 + "miniz_oxide", 168 + "object", 169 + "rustc-demangle", 170 + "windows-targets 0.52.6", 171 + ] 172 + 173 + [[package]] 174 + name = "base64" 175 + version = "0.22.1" 176 + source = "registry+https://github.com/rust-lang/crates.io-index" 177 + checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 178 + 179 + [[package]] 180 + name = "bit_field" 181 + version = "0.10.2" 182 + source = "registry+https://github.com/rust-lang/crates.io-index" 183 + checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" 184 + 185 + [[package]] 186 + name = "bitflags" 187 + version = "1.3.2" 188 + source = "registry+https://github.com/rust-lang/crates.io-index" 189 + checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 190 + 191 + [[package]] 192 + name = "bitflags" 193 + version = "2.9.1" 194 + source = "registry+https://github.com/rust-lang/crates.io-index" 195 + checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 196 + 197 + [[package]] 198 + name = "bitstream-io" 199 + version = "2.6.0" 200 + source = "registry+https://github.com/rust-lang/crates.io-index" 201 + checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" 202 + 203 + [[package]] 204 + name = "built" 205 + version = "0.7.7" 206 + source = "registry+https://github.com/rust-lang/crates.io-index" 207 + checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" 208 + 209 + [[package]] 210 + name = "bumpalo" 211 + version = "3.19.0" 212 + source = "registry+https://github.com/rust-lang/crates.io-index" 213 + checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 214 + 215 + [[package]] 216 + name = "bytemuck" 217 + version = "1.23.1" 218 + source = "registry+https://github.com/rust-lang/crates.io-index" 219 + checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" 220 + 221 + [[package]] 222 + name = "byteorder-lite" 223 + version = "0.1.0" 224 + source = "registry+https://github.com/rust-lang/crates.io-index" 225 + checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" 226 + 227 + [[package]] 228 + name = "bytes" 229 + version = "1.10.1" 230 + source = "registry+https://github.com/rust-lang/crates.io-index" 231 + checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 232 + 233 + [[package]] 234 + name = "cc" 235 + version = "1.2.31" 236 + source = "registry+https://github.com/rust-lang/crates.io-index" 237 + checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" 238 + dependencies = [ 239 + "jobserver", 240 + "libc", 241 + "shlex", 242 + ] 243 + 244 + [[package]] 245 + name = "cfg-expr" 246 + version = "0.15.8" 247 + source = "registry+https://github.com/rust-lang/crates.io-index" 248 + checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" 249 + dependencies = [ 250 + "smallvec", 251 + "target-lexicon", 252 + ] 253 + 254 + [[package]] 255 + name = "cfg-if" 256 + version = "1.0.1" 257 + source = "registry+https://github.com/rust-lang/crates.io-index" 258 + checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 259 + 260 + [[package]] 261 + name = "chrono" 262 + version = "0.4.41" 263 + source = "registry+https://github.com/rust-lang/crates.io-index" 264 + checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" 265 + dependencies = [ 266 + "android-tzdata", 267 + "iana-time-zone", 268 + "js-sys", 269 + "num-traits", 270 + "serde", 271 + "wasm-bindgen", 272 + "windows-link", 273 + ] 274 + 275 + [[package]] 276 + name = "clap" 277 + version = "4.5.42" 278 + source = "registry+https://github.com/rust-lang/crates.io-index" 279 + checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" 280 + dependencies = [ 281 + "clap_builder", 282 + "clap_derive", 283 + ] 284 + 285 + [[package]] 286 + name = "clap_builder" 287 + version = "4.5.42" 288 + source = "registry+https://github.com/rust-lang/crates.io-index" 289 + checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" 290 + dependencies = [ 291 + "anstream", 292 + "anstyle", 293 + "clap_lex", 294 + "strsim", 295 + ] 296 + 297 + [[package]] 298 + name = "clap_derive" 299 + version = "4.5.41" 300 + source = "registry+https://github.com/rust-lang/crates.io-index" 301 + checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" 302 + dependencies = [ 303 + "heck", 304 + "proc-macro2", 305 + "quote", 306 + "syn", 307 + ] 308 + 309 + [[package]] 310 + name = "clap_lex" 311 + version = "0.7.5" 312 + source = "registry+https://github.com/rust-lang/crates.io-index" 313 + checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 314 + 315 + [[package]] 316 + name = "color_quant" 317 + version = "1.1.0" 318 + source = "registry+https://github.com/rust-lang/crates.io-index" 319 + checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" 320 + 321 + [[package]] 322 + name = "colorchoice" 323 + version = "1.0.4" 324 + source = "registry+https://github.com/rust-lang/crates.io-index" 325 + checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 326 + 327 + [[package]] 328 + name = "console" 329 + version = "0.15.11" 330 + source = "registry+https://github.com/rust-lang/crates.io-index" 331 + checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" 332 + dependencies = [ 333 + "encode_unicode", 334 + "libc", 335 + "once_cell", 336 + "unicode-width", 337 + "windows-sys 0.59.0", 338 + ] 339 + 340 + [[package]] 341 + name = "console" 342 + version = "0.16.0" 343 + source = "registry+https://github.com/rust-lang/crates.io-index" 344 + checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" 345 + dependencies = [ 346 + "encode_unicode", 347 + "libc", 348 + "once_cell", 349 + "unicode-width", 350 + "windows-sys 0.60.2", 351 + ] 352 + 353 + [[package]] 354 + name = "core-foundation" 355 + version = "0.9.4" 356 + source = "registry+https://github.com/rust-lang/crates.io-index" 357 + checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 358 + dependencies = [ 359 + "core-foundation-sys", 360 + "libc", 361 + ] 362 + 363 + [[package]] 364 + name = "core-foundation-sys" 365 + version = "0.8.7" 366 + source = "registry+https://github.com/rust-lang/crates.io-index" 367 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 368 + 369 + [[package]] 370 + name = "crc32fast" 371 + version = "1.5.0" 372 + source = "registry+https://github.com/rust-lang/crates.io-index" 373 + checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" 374 + dependencies = [ 375 + "cfg-if", 376 + ] 377 + 378 + [[package]] 379 + name = "crossbeam-deque" 380 + version = "0.8.6" 381 + source = "registry+https://github.com/rust-lang/crates.io-index" 382 + checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 383 + dependencies = [ 384 + "crossbeam-epoch", 385 + "crossbeam-utils", 386 + ] 387 + 388 + [[package]] 389 + name = "crossbeam-epoch" 390 + version = "0.9.18" 391 + source = "registry+https://github.com/rust-lang/crates.io-index" 392 + checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 393 + dependencies = [ 394 + "crossbeam-utils", 395 + ] 396 + 397 + [[package]] 398 + name = "crossbeam-utils" 399 + version = "0.8.21" 400 + source = "registry+https://github.com/rust-lang/crates.io-index" 401 + checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 402 + 403 + [[package]] 404 + name = "crunchy" 405 + version = "0.2.4" 406 + source = "registry+https://github.com/rust-lang/crates.io-index" 407 + checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" 408 + 409 + [[package]] 410 + name = "dialoguer" 411 + version = "0.11.0" 412 + source = "registry+https://github.com/rust-lang/crates.io-index" 413 + checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" 414 + dependencies = [ 415 + "console 0.15.11", 416 + "shell-words", 417 + "tempfile", 418 + "thiserror 1.0.69", 419 + "zeroize", 420 + ] 421 + 422 + [[package]] 423 + name = "dirs" 424 + version = "6.0.0" 425 + source = "registry+https://github.com/rust-lang/crates.io-index" 426 + checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" 427 + dependencies = [ 428 + "dirs-sys", 429 + ] 430 + 431 + [[package]] 432 + name = "dirs-sys" 433 + version = "0.5.0" 434 + source = "registry+https://github.com/rust-lang/crates.io-index" 435 + checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" 436 + dependencies = [ 437 + "libc", 438 + "option-ext", 439 + "redox_users", 440 + "windows-sys 0.60.2", 441 + ] 442 + 443 + [[package]] 444 + name = "displaydoc" 445 + version = "0.2.5" 446 + source = "registry+https://github.com/rust-lang/crates.io-index" 447 + checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 448 + dependencies = [ 449 + "proc-macro2", 450 + "quote", 451 + "syn", 452 + ] 453 + 454 + [[package]] 455 + name = "either" 456 + version = "1.15.0" 457 + source = "registry+https://github.com/rust-lang/crates.io-index" 458 + checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 459 + 460 + [[package]] 461 + name = "encode_unicode" 462 + version = "1.0.0" 463 + source = "registry+https://github.com/rust-lang/crates.io-index" 464 + checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" 465 + 466 + [[package]] 467 + name = "encoding_rs" 468 + version = "0.8.35" 469 + source = "registry+https://github.com/rust-lang/crates.io-index" 470 + checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 471 + dependencies = [ 472 + "cfg-if", 473 + ] 474 + 475 + [[package]] 476 + name = "equator" 477 + version = "0.4.2" 478 + source = "registry+https://github.com/rust-lang/crates.io-index" 479 + checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" 480 + dependencies = [ 481 + "equator-macro", 482 + ] 483 + 484 + [[package]] 485 + name = "equator-macro" 486 + version = "0.4.2" 487 + source = "registry+https://github.com/rust-lang/crates.io-index" 488 + checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" 489 + dependencies = [ 490 + "proc-macro2", 491 + "quote", 492 + "syn", 493 + ] 494 + 495 + [[package]] 496 + name = "equivalent" 497 + version = "1.0.2" 498 + source = "registry+https://github.com/rust-lang/crates.io-index" 499 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 500 + 501 + [[package]] 502 + name = "errno" 503 + version = "0.3.13" 504 + source = "registry+https://github.com/rust-lang/crates.io-index" 505 + checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" 506 + dependencies = [ 507 + "libc", 508 + "windows-sys 0.60.2", 509 + ] 510 + 511 + [[package]] 512 + name = "exr" 513 + version = "1.73.0" 514 + source = "registry+https://github.com/rust-lang/crates.io-index" 515 + checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" 516 + dependencies = [ 517 + "bit_field", 518 + "half", 519 + "lebe", 520 + "miniz_oxide", 521 + "rayon-core", 522 + "smallvec", 523 + "zune-inflate", 524 + ] 525 + 526 + [[package]] 527 + name = "fastrand" 528 + version = "2.3.0" 529 + source = "registry+https://github.com/rust-lang/crates.io-index" 530 + checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 531 + 532 + [[package]] 533 + name = "fdeflate" 534 + version = "0.3.7" 535 + source = "registry+https://github.com/rust-lang/crates.io-index" 536 + checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" 537 + dependencies = [ 538 + "simd-adler32", 539 + ] 540 + 541 + [[package]] 542 + name = "flate2" 543 + version = "1.1.2" 544 + source = "registry+https://github.com/rust-lang/crates.io-index" 545 + checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" 546 + dependencies = [ 547 + "crc32fast", 548 + "miniz_oxide", 549 + ] 550 + 551 + [[package]] 552 + name = "fnv" 553 + version = "1.0.7" 554 + source = "registry+https://github.com/rust-lang/crates.io-index" 555 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 556 + 557 + [[package]] 558 + name = "foreign-types" 559 + version = "0.3.2" 560 + source = "registry+https://github.com/rust-lang/crates.io-index" 561 + checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 562 + dependencies = [ 563 + "foreign-types-shared", 564 + ] 565 + 566 + [[package]] 567 + name = "foreign-types-shared" 568 + version = "0.1.1" 569 + source = "registry+https://github.com/rust-lang/crates.io-index" 570 + checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 571 + 572 + [[package]] 573 + name = "form_urlencoded" 574 + version = "1.2.1" 575 + source = "registry+https://github.com/rust-lang/crates.io-index" 576 + checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 577 + dependencies = [ 578 + "percent-encoding", 579 + ] 580 + 581 + [[package]] 582 + name = "futures-channel" 583 + version = "0.3.31" 584 + source = "registry+https://github.com/rust-lang/crates.io-index" 585 + checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 586 + dependencies = [ 587 + "futures-core", 588 + ] 589 + 590 + [[package]] 591 + name = "futures-core" 592 + version = "0.3.31" 593 + source = "registry+https://github.com/rust-lang/crates.io-index" 594 + checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 595 + 596 + [[package]] 597 + name = "futures-macro" 598 + version = "0.3.31" 599 + source = "registry+https://github.com/rust-lang/crates.io-index" 600 + checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 601 + dependencies = [ 602 + "proc-macro2", 603 + "quote", 604 + "syn", 605 + ] 606 + 607 + [[package]] 608 + name = "futures-sink" 609 + version = "0.3.31" 610 + source = "registry+https://github.com/rust-lang/crates.io-index" 611 + checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 612 + 613 + [[package]] 614 + name = "futures-task" 615 + version = "0.3.31" 616 + source = "registry+https://github.com/rust-lang/crates.io-index" 617 + checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 618 + 619 + [[package]] 620 + name = "futures-util" 621 + version = "0.3.31" 622 + source = "registry+https://github.com/rust-lang/crates.io-index" 623 + checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 624 + dependencies = [ 625 + "futures-core", 626 + "futures-macro", 627 + "futures-task", 628 + "pin-project-lite", 629 + "pin-utils", 630 + "slab", 631 + ] 632 + 633 + [[package]] 634 + name = "getrandom" 635 + version = "0.2.16" 636 + source = "registry+https://github.com/rust-lang/crates.io-index" 637 + checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 638 + dependencies = [ 639 + "cfg-if", 640 + "libc", 641 + "wasi 0.11.1+wasi-snapshot-preview1", 642 + ] 643 + 644 + [[package]] 645 + name = "getrandom" 646 + version = "0.3.3" 647 + source = "registry+https://github.com/rust-lang/crates.io-index" 648 + checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 649 + dependencies = [ 650 + "cfg-if", 651 + "libc", 652 + "r-efi", 653 + "wasi 0.14.2+wasi-0.2.4", 654 + ] 655 + 656 + [[package]] 657 + name = "gif" 658 + version = "0.13.3" 659 + source = "registry+https://github.com/rust-lang/crates.io-index" 660 + checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" 661 + dependencies = [ 662 + "color_quant", 663 + "weezl", 664 + ] 665 + 666 + [[package]] 667 + name = "gimli" 668 + version = "0.31.1" 669 + source = "registry+https://github.com/rust-lang/crates.io-index" 670 + checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 671 + 672 + [[package]] 673 + name = "grain-cli" 674 + version = "0.1.0" 675 + dependencies = [ 676 + "anyhow", 677 + "chrono", 678 + "clap", 679 + "dialoguer", 680 + "dirs", 681 + "futures-util", 682 + "http-body-util", 683 + "hyper", 684 + "hyper-util", 685 + "image", 686 + "indicatif", 687 + "open", 688 + "reqwest", 689 + "serde", 690 + "serde_json", 691 + "tokio", 692 + "tokio-util", 693 + "url", 694 + "urlencoding", 695 + ] 696 + 697 + [[package]] 698 + name = "h2" 699 + version = "0.4.11" 700 + source = "registry+https://github.com/rust-lang/crates.io-index" 701 + checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" 702 + dependencies = [ 703 + "atomic-waker", 704 + "bytes", 705 + "fnv", 706 + "futures-core", 707 + "futures-sink", 708 + "http", 709 + "indexmap", 710 + "slab", 711 + "tokio", 712 + "tokio-util", 713 + "tracing", 714 + ] 715 + 716 + [[package]] 717 + name = "half" 718 + version = "2.6.0" 719 + source = "registry+https://github.com/rust-lang/crates.io-index" 720 + checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" 721 + dependencies = [ 722 + "cfg-if", 723 + "crunchy", 724 + ] 725 + 726 + [[package]] 727 + name = "hashbrown" 728 + version = "0.15.4" 729 + source = "registry+https://github.com/rust-lang/crates.io-index" 730 + checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" 731 + 732 + [[package]] 733 + name = "heck" 734 + version = "0.5.0" 735 + source = "registry+https://github.com/rust-lang/crates.io-index" 736 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 737 + 738 + [[package]] 739 + name = "http" 740 + version = "1.3.1" 741 + source = "registry+https://github.com/rust-lang/crates.io-index" 742 + checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 743 + dependencies = [ 744 + "bytes", 745 + "fnv", 746 + "itoa", 747 + ] 748 + 749 + [[package]] 750 + name = "http-body" 751 + version = "1.0.1" 752 + source = "registry+https://github.com/rust-lang/crates.io-index" 753 + checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 754 + dependencies = [ 755 + "bytes", 756 + "http", 757 + ] 758 + 759 + [[package]] 760 + name = "http-body-util" 761 + version = "0.1.3" 762 + source = "registry+https://github.com/rust-lang/crates.io-index" 763 + checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 764 + dependencies = [ 765 + "bytes", 766 + "futures-core", 767 + "http", 768 + "http-body", 769 + "pin-project-lite", 770 + ] 771 + 772 + [[package]] 773 + name = "httparse" 774 + version = "1.10.1" 775 + source = "registry+https://github.com/rust-lang/crates.io-index" 776 + checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 777 + 778 + [[package]] 779 + name = "httpdate" 780 + version = "1.0.3" 781 + source = "registry+https://github.com/rust-lang/crates.io-index" 782 + checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 783 + 784 + [[package]] 785 + name = "hyper" 786 + version = "1.6.0" 787 + source = "registry+https://github.com/rust-lang/crates.io-index" 788 + checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 789 + dependencies = [ 790 + "bytes", 791 + "futures-channel", 792 + "futures-util", 793 + "h2", 794 + "http", 795 + "http-body", 796 + "httparse", 797 + "httpdate", 798 + "itoa", 799 + "pin-project-lite", 800 + "smallvec", 801 + "tokio", 802 + "want", 803 + ] 804 + 805 + [[package]] 806 + name = "hyper-rustls" 807 + version = "0.27.7" 808 + source = "registry+https://github.com/rust-lang/crates.io-index" 809 + checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" 810 + dependencies = [ 811 + "http", 812 + "hyper", 813 + "hyper-util", 814 + "rustls", 815 + "rustls-pki-types", 816 + "tokio", 817 + "tokio-rustls", 818 + "tower-service", 819 + ] 820 + 821 + [[package]] 822 + name = "hyper-tls" 823 + version = "0.6.0" 824 + source = "registry+https://github.com/rust-lang/crates.io-index" 825 + checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 826 + dependencies = [ 827 + "bytes", 828 + "http-body-util", 829 + "hyper", 830 + "hyper-util", 831 + "native-tls", 832 + "tokio", 833 + "tokio-native-tls", 834 + "tower-service", 835 + ] 836 + 837 + [[package]] 838 + name = "hyper-util" 839 + version = "0.1.16" 840 + source = "registry+https://github.com/rust-lang/crates.io-index" 841 + checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" 842 + dependencies = [ 843 + "base64", 844 + "bytes", 845 + "futures-channel", 846 + "futures-core", 847 + "futures-util", 848 + "http", 849 + "http-body", 850 + "hyper", 851 + "ipnet", 852 + "libc", 853 + "percent-encoding", 854 + "pin-project-lite", 855 + "socket2", 856 + "system-configuration", 857 + "tokio", 858 + "tower-service", 859 + "tracing", 860 + "windows-registry", 861 + ] 862 + 863 + [[package]] 864 + name = "iana-time-zone" 865 + version = "0.1.63" 866 + source = "registry+https://github.com/rust-lang/crates.io-index" 867 + checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 868 + dependencies = [ 869 + "android_system_properties", 870 + "core-foundation-sys", 871 + "iana-time-zone-haiku", 872 + "js-sys", 873 + "log", 874 + "wasm-bindgen", 875 + "windows-core", 876 + ] 877 + 878 + [[package]] 879 + name = "iana-time-zone-haiku" 880 + version = "0.1.2" 881 + source = "registry+https://github.com/rust-lang/crates.io-index" 882 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 883 + dependencies = [ 884 + "cc", 885 + ] 886 + 887 + [[package]] 888 + name = "icu_collections" 889 + version = "2.0.0" 890 + source = "registry+https://github.com/rust-lang/crates.io-index" 891 + checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 892 + dependencies = [ 893 + "displaydoc", 894 + "potential_utf", 895 + "yoke", 896 + "zerofrom", 897 + "zerovec", 898 + ] 899 + 900 + [[package]] 901 + name = "icu_locale_core" 902 + version = "2.0.0" 903 + source = "registry+https://github.com/rust-lang/crates.io-index" 904 + checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 905 + dependencies = [ 906 + "displaydoc", 907 + "litemap", 908 + "tinystr", 909 + "writeable", 910 + "zerovec", 911 + ] 912 + 913 + [[package]] 914 + name = "icu_normalizer" 915 + version = "2.0.0" 916 + source = "registry+https://github.com/rust-lang/crates.io-index" 917 + checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 918 + dependencies = [ 919 + "displaydoc", 920 + "icu_collections", 921 + "icu_normalizer_data", 922 + "icu_properties", 923 + "icu_provider", 924 + "smallvec", 925 + "zerovec", 926 + ] 927 + 928 + [[package]] 929 + name = "icu_normalizer_data" 930 + version = "2.0.0" 931 + source = "registry+https://github.com/rust-lang/crates.io-index" 932 + checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 933 + 934 + [[package]] 935 + name = "icu_properties" 936 + version = "2.0.1" 937 + source = "registry+https://github.com/rust-lang/crates.io-index" 938 + checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" 939 + dependencies = [ 940 + "displaydoc", 941 + "icu_collections", 942 + "icu_locale_core", 943 + "icu_properties_data", 944 + "icu_provider", 945 + "potential_utf", 946 + "zerotrie", 947 + "zerovec", 948 + ] 949 + 950 + [[package]] 951 + name = "icu_properties_data" 952 + version = "2.0.1" 953 + source = "registry+https://github.com/rust-lang/crates.io-index" 954 + checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" 955 + 956 + [[package]] 957 + name = "icu_provider" 958 + version = "2.0.0" 959 + source = "registry+https://github.com/rust-lang/crates.io-index" 960 + checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 961 + dependencies = [ 962 + "displaydoc", 963 + "icu_locale_core", 964 + "stable_deref_trait", 965 + "tinystr", 966 + "writeable", 967 + "yoke", 968 + "zerofrom", 969 + "zerotrie", 970 + "zerovec", 971 + ] 972 + 973 + [[package]] 974 + name = "idna" 975 + version = "1.0.3" 976 + source = "registry+https://github.com/rust-lang/crates.io-index" 977 + checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 978 + dependencies = [ 979 + "idna_adapter", 980 + "smallvec", 981 + "utf8_iter", 982 + ] 983 + 984 + [[package]] 985 + name = "idna_adapter" 986 + version = "1.2.1" 987 + source = "registry+https://github.com/rust-lang/crates.io-index" 988 + checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 989 + dependencies = [ 990 + "icu_normalizer", 991 + "icu_properties", 992 + ] 993 + 994 + [[package]] 995 + name = "image" 996 + version = "0.25.6" 997 + source = "registry+https://github.com/rust-lang/crates.io-index" 998 + checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" 999 + dependencies = [ 1000 + "bytemuck", 1001 + "byteorder-lite", 1002 + "color_quant", 1003 + "exr", 1004 + "gif", 1005 + "image-webp", 1006 + "num-traits", 1007 + "png", 1008 + "qoi", 1009 + "ravif", 1010 + "rayon", 1011 + "rgb", 1012 + "tiff", 1013 + "zune-core", 1014 + "zune-jpeg", 1015 + ] 1016 + 1017 + [[package]] 1018 + name = "image-webp" 1019 + version = "0.2.3" 1020 + source = "registry+https://github.com/rust-lang/crates.io-index" 1021 + checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b" 1022 + dependencies = [ 1023 + "byteorder-lite", 1024 + "quick-error", 1025 + ] 1026 + 1027 + [[package]] 1028 + name = "imgref" 1029 + version = "1.11.0" 1030 + source = "registry+https://github.com/rust-lang/crates.io-index" 1031 + checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" 1032 + 1033 + [[package]] 1034 + name = "indexmap" 1035 + version = "2.10.0" 1036 + source = "registry+https://github.com/rust-lang/crates.io-index" 1037 + checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" 1038 + dependencies = [ 1039 + "equivalent", 1040 + "hashbrown", 1041 + ] 1042 + 1043 + [[package]] 1044 + name = "indicatif" 1045 + version = "0.18.0" 1046 + source = "registry+https://github.com/rust-lang/crates.io-index" 1047 + checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" 1048 + dependencies = [ 1049 + "console 0.16.0", 1050 + "portable-atomic", 1051 + "unicode-width", 1052 + "unit-prefix", 1053 + "web-time", 1054 + ] 1055 + 1056 + [[package]] 1057 + name = "interpolate_name" 1058 + version = "0.2.4" 1059 + source = "registry+https://github.com/rust-lang/crates.io-index" 1060 + checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" 1061 + dependencies = [ 1062 + "proc-macro2", 1063 + "quote", 1064 + "syn", 1065 + ] 1066 + 1067 + [[package]] 1068 + name = "io-uring" 1069 + version = "0.7.9" 1070 + source = "registry+https://github.com/rust-lang/crates.io-index" 1071 + checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" 1072 + dependencies = [ 1073 + "bitflags 2.9.1", 1074 + "cfg-if", 1075 + "libc", 1076 + ] 1077 + 1078 + [[package]] 1079 + name = "ipnet" 1080 + version = "2.11.0" 1081 + source = "registry+https://github.com/rust-lang/crates.io-index" 1082 + checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 1083 + 1084 + [[package]] 1085 + name = "iri-string" 1086 + version = "0.7.8" 1087 + source = "registry+https://github.com/rust-lang/crates.io-index" 1088 + checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" 1089 + dependencies = [ 1090 + "memchr", 1091 + "serde", 1092 + ] 1093 + 1094 + [[package]] 1095 + name = "is-docker" 1096 + version = "0.2.0" 1097 + source = "registry+https://github.com/rust-lang/crates.io-index" 1098 + checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" 1099 + dependencies = [ 1100 + "once_cell", 1101 + ] 1102 + 1103 + [[package]] 1104 + name = "is-wsl" 1105 + version = "0.4.0" 1106 + source = "registry+https://github.com/rust-lang/crates.io-index" 1107 + checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" 1108 + dependencies = [ 1109 + "is-docker", 1110 + "once_cell", 1111 + ] 1112 + 1113 + [[package]] 1114 + name = "is_terminal_polyfill" 1115 + version = "1.70.1" 1116 + source = "registry+https://github.com/rust-lang/crates.io-index" 1117 + checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 1118 + 1119 + [[package]] 1120 + name = "itertools" 1121 + version = "0.12.1" 1122 + source = "registry+https://github.com/rust-lang/crates.io-index" 1123 + checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 1124 + dependencies = [ 1125 + "either", 1126 + ] 1127 + 1128 + [[package]] 1129 + name = "itoa" 1130 + version = "1.0.15" 1131 + source = "registry+https://github.com/rust-lang/crates.io-index" 1132 + checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 1133 + 1134 + [[package]] 1135 + name = "jobserver" 1136 + version = "0.1.33" 1137 + source = "registry+https://github.com/rust-lang/crates.io-index" 1138 + checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" 1139 + dependencies = [ 1140 + "getrandom 0.3.3", 1141 + "libc", 1142 + ] 1143 + 1144 + [[package]] 1145 + name = "jpeg-decoder" 1146 + version = "0.3.2" 1147 + source = "registry+https://github.com/rust-lang/crates.io-index" 1148 + checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" 1149 + 1150 + [[package]] 1151 + name = "js-sys" 1152 + version = "0.3.77" 1153 + source = "registry+https://github.com/rust-lang/crates.io-index" 1154 + checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 1155 + dependencies = [ 1156 + "once_cell", 1157 + "wasm-bindgen", 1158 + ] 1159 + 1160 + [[package]] 1161 + name = "lebe" 1162 + version = "0.5.2" 1163 + source = "registry+https://github.com/rust-lang/crates.io-index" 1164 + checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" 1165 + 1166 + [[package]] 1167 + name = "libc" 1168 + version = "0.2.174" 1169 + source = "registry+https://github.com/rust-lang/crates.io-index" 1170 + checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" 1171 + 1172 + [[package]] 1173 + name = "libfuzzer-sys" 1174 + version = "0.4.10" 1175 + source = "registry+https://github.com/rust-lang/crates.io-index" 1176 + checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" 1177 + dependencies = [ 1178 + "arbitrary", 1179 + "cc", 1180 + ] 1181 + 1182 + [[package]] 1183 + name = "libredox" 1184 + version = "0.1.9" 1185 + source = "registry+https://github.com/rust-lang/crates.io-index" 1186 + checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" 1187 + dependencies = [ 1188 + "bitflags 2.9.1", 1189 + "libc", 1190 + ] 1191 + 1192 + [[package]] 1193 + name = "linux-raw-sys" 1194 + version = "0.9.4" 1195 + source = "registry+https://github.com/rust-lang/crates.io-index" 1196 + checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" 1197 + 1198 + [[package]] 1199 + name = "litemap" 1200 + version = "0.8.0" 1201 + source = "registry+https://github.com/rust-lang/crates.io-index" 1202 + checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 1203 + 1204 + [[package]] 1205 + name = "lock_api" 1206 + version = "0.4.13" 1207 + source = "registry+https://github.com/rust-lang/crates.io-index" 1208 + checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" 1209 + dependencies = [ 1210 + "autocfg", 1211 + "scopeguard", 1212 + ] 1213 + 1214 + [[package]] 1215 + name = "log" 1216 + version = "0.4.27" 1217 + source = "registry+https://github.com/rust-lang/crates.io-index" 1218 + checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 1219 + 1220 + [[package]] 1221 + name = "loop9" 1222 + version = "0.1.5" 1223 + source = "registry+https://github.com/rust-lang/crates.io-index" 1224 + checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" 1225 + dependencies = [ 1226 + "imgref", 1227 + ] 1228 + 1229 + [[package]] 1230 + name = "maybe-rayon" 1231 + version = "0.1.1" 1232 + source = "registry+https://github.com/rust-lang/crates.io-index" 1233 + checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" 1234 + dependencies = [ 1235 + "cfg-if", 1236 + "rayon", 1237 + ] 1238 + 1239 + [[package]] 1240 + name = "memchr" 1241 + version = "2.7.5" 1242 + source = "registry+https://github.com/rust-lang/crates.io-index" 1243 + checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 1244 + 1245 + [[package]] 1246 + name = "mime" 1247 + version = "0.3.17" 1248 + source = "registry+https://github.com/rust-lang/crates.io-index" 1249 + checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1250 + 1251 + [[package]] 1252 + name = "minimal-lexical" 1253 + version = "0.2.1" 1254 + source = "registry+https://github.com/rust-lang/crates.io-index" 1255 + checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 1256 + 1257 + [[package]] 1258 + name = "miniz_oxide" 1259 + version = "0.8.9" 1260 + source = "registry+https://github.com/rust-lang/crates.io-index" 1261 + checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 1262 + dependencies = [ 1263 + "adler2", 1264 + "simd-adler32", 1265 + ] 1266 + 1267 + [[package]] 1268 + name = "mio" 1269 + version = "1.0.4" 1270 + source = "registry+https://github.com/rust-lang/crates.io-index" 1271 + checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 1272 + dependencies = [ 1273 + "libc", 1274 + "wasi 0.11.1+wasi-snapshot-preview1", 1275 + "windows-sys 0.59.0", 1276 + ] 1277 + 1278 + [[package]] 1279 + name = "native-tls" 1280 + version = "0.2.14" 1281 + source = "registry+https://github.com/rust-lang/crates.io-index" 1282 + checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" 1283 + dependencies = [ 1284 + "libc", 1285 + "log", 1286 + "openssl", 1287 + "openssl-probe", 1288 + "openssl-sys", 1289 + "schannel", 1290 + "security-framework", 1291 + "security-framework-sys", 1292 + "tempfile", 1293 + ] 1294 + 1295 + [[package]] 1296 + name = "new_debug_unreachable" 1297 + version = "1.0.6" 1298 + source = "registry+https://github.com/rust-lang/crates.io-index" 1299 + checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" 1300 + 1301 + [[package]] 1302 + name = "nom" 1303 + version = "7.1.3" 1304 + source = "registry+https://github.com/rust-lang/crates.io-index" 1305 + checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 1306 + dependencies = [ 1307 + "memchr", 1308 + "minimal-lexical", 1309 + ] 1310 + 1311 + [[package]] 1312 + name = "noop_proc_macro" 1313 + version = "0.3.0" 1314 + source = "registry+https://github.com/rust-lang/crates.io-index" 1315 + checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" 1316 + 1317 + [[package]] 1318 + name = "num-bigint" 1319 + version = "0.4.6" 1320 + source = "registry+https://github.com/rust-lang/crates.io-index" 1321 + checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 1322 + dependencies = [ 1323 + "num-integer", 1324 + "num-traits", 1325 + ] 1326 + 1327 + [[package]] 1328 + name = "num-derive" 1329 + version = "0.4.2" 1330 + source = "registry+https://github.com/rust-lang/crates.io-index" 1331 + checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" 1332 + dependencies = [ 1333 + "proc-macro2", 1334 + "quote", 1335 + "syn", 1336 + ] 1337 + 1338 + [[package]] 1339 + name = "num-integer" 1340 + version = "0.1.46" 1341 + source = "registry+https://github.com/rust-lang/crates.io-index" 1342 + checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 1343 + dependencies = [ 1344 + "num-traits", 1345 + ] 1346 + 1347 + [[package]] 1348 + name = "num-rational" 1349 + version = "0.4.2" 1350 + source = "registry+https://github.com/rust-lang/crates.io-index" 1351 + checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" 1352 + dependencies = [ 1353 + "num-bigint", 1354 + "num-integer", 1355 + "num-traits", 1356 + ] 1357 + 1358 + [[package]] 1359 + name = "num-traits" 1360 + version = "0.2.19" 1361 + source = "registry+https://github.com/rust-lang/crates.io-index" 1362 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1363 + dependencies = [ 1364 + "autocfg", 1365 + ] 1366 + 1367 + [[package]] 1368 + name = "object" 1369 + version = "0.36.7" 1370 + source = "registry+https://github.com/rust-lang/crates.io-index" 1371 + checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 1372 + dependencies = [ 1373 + "memchr", 1374 + ] 1375 + 1376 + [[package]] 1377 + name = "once_cell" 1378 + version = "1.21.3" 1379 + source = "registry+https://github.com/rust-lang/crates.io-index" 1380 + checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 1381 + 1382 + [[package]] 1383 + name = "once_cell_polyfill" 1384 + version = "1.70.1" 1385 + source = "registry+https://github.com/rust-lang/crates.io-index" 1386 + checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 1387 + 1388 + [[package]] 1389 + name = "open" 1390 + version = "5.3.2" 1391 + source = "registry+https://github.com/rust-lang/crates.io-index" 1392 + checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" 1393 + dependencies = [ 1394 + "is-wsl", 1395 + "libc", 1396 + "pathdiff", 1397 + ] 1398 + 1399 + [[package]] 1400 + name = "openssl" 1401 + version = "0.10.73" 1402 + source = "registry+https://github.com/rust-lang/crates.io-index" 1403 + checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" 1404 + dependencies = [ 1405 + "bitflags 2.9.1", 1406 + "cfg-if", 1407 + "foreign-types", 1408 + "libc", 1409 + "once_cell", 1410 + "openssl-macros", 1411 + "openssl-sys", 1412 + ] 1413 + 1414 + [[package]] 1415 + name = "openssl-macros" 1416 + version = "0.1.1" 1417 + source = "registry+https://github.com/rust-lang/crates.io-index" 1418 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 1419 + dependencies = [ 1420 + "proc-macro2", 1421 + "quote", 1422 + "syn", 1423 + ] 1424 + 1425 + [[package]] 1426 + name = "openssl-probe" 1427 + version = "0.1.6" 1428 + source = "registry+https://github.com/rust-lang/crates.io-index" 1429 + checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 1430 + 1431 + [[package]] 1432 + name = "openssl-sys" 1433 + version = "0.9.109" 1434 + source = "registry+https://github.com/rust-lang/crates.io-index" 1435 + checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" 1436 + dependencies = [ 1437 + "cc", 1438 + "libc", 1439 + "pkg-config", 1440 + "vcpkg", 1441 + ] 1442 + 1443 + [[package]] 1444 + name = "option-ext" 1445 + version = "0.2.0" 1446 + source = "registry+https://github.com/rust-lang/crates.io-index" 1447 + checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" 1448 + 1449 + [[package]] 1450 + name = "parking_lot" 1451 + version = "0.12.4" 1452 + source = "registry+https://github.com/rust-lang/crates.io-index" 1453 + checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" 1454 + dependencies = [ 1455 + "lock_api", 1456 + "parking_lot_core", 1457 + ] 1458 + 1459 + [[package]] 1460 + name = "parking_lot_core" 1461 + version = "0.9.11" 1462 + source = "registry+https://github.com/rust-lang/crates.io-index" 1463 + checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" 1464 + dependencies = [ 1465 + "cfg-if", 1466 + "libc", 1467 + "redox_syscall", 1468 + "smallvec", 1469 + "windows-targets 0.52.6", 1470 + ] 1471 + 1472 + [[package]] 1473 + name = "paste" 1474 + version = "1.0.15" 1475 + source = "registry+https://github.com/rust-lang/crates.io-index" 1476 + checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1477 + 1478 + [[package]] 1479 + name = "pathdiff" 1480 + version = "0.2.3" 1481 + source = "registry+https://github.com/rust-lang/crates.io-index" 1482 + checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" 1483 + 1484 + [[package]] 1485 + name = "percent-encoding" 1486 + version = "2.3.1" 1487 + source = "registry+https://github.com/rust-lang/crates.io-index" 1488 + checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1489 + 1490 + [[package]] 1491 + name = "pin-project-lite" 1492 + version = "0.2.16" 1493 + source = "registry+https://github.com/rust-lang/crates.io-index" 1494 + checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 1495 + 1496 + [[package]] 1497 + name = "pin-utils" 1498 + version = "0.1.0" 1499 + source = "registry+https://github.com/rust-lang/crates.io-index" 1500 + checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1501 + 1502 + [[package]] 1503 + name = "pkg-config" 1504 + version = "0.3.32" 1505 + source = "registry+https://github.com/rust-lang/crates.io-index" 1506 + checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1507 + 1508 + [[package]] 1509 + name = "png" 1510 + version = "0.17.16" 1511 + source = "registry+https://github.com/rust-lang/crates.io-index" 1512 + checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" 1513 + dependencies = [ 1514 + "bitflags 1.3.2", 1515 + "crc32fast", 1516 + "fdeflate", 1517 + "flate2", 1518 + "miniz_oxide", 1519 + ] 1520 + 1521 + [[package]] 1522 + name = "portable-atomic" 1523 + version = "1.11.1" 1524 + source = "registry+https://github.com/rust-lang/crates.io-index" 1525 + checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 1526 + 1527 + [[package]] 1528 + name = "potential_utf" 1529 + version = "0.1.2" 1530 + source = "registry+https://github.com/rust-lang/crates.io-index" 1531 + checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" 1532 + dependencies = [ 1533 + "zerovec", 1534 + ] 1535 + 1536 + [[package]] 1537 + name = "ppv-lite86" 1538 + version = "0.2.21" 1539 + source = "registry+https://github.com/rust-lang/crates.io-index" 1540 + checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 1541 + dependencies = [ 1542 + "zerocopy", 1543 + ] 1544 + 1545 + [[package]] 1546 + name = "proc-macro2" 1547 + version = "1.0.95" 1548 + source = "registry+https://github.com/rust-lang/crates.io-index" 1549 + checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 1550 + dependencies = [ 1551 + "unicode-ident", 1552 + ] 1553 + 1554 + [[package]] 1555 + name = "profiling" 1556 + version = "1.0.17" 1557 + source = "registry+https://github.com/rust-lang/crates.io-index" 1558 + checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" 1559 + dependencies = [ 1560 + "profiling-procmacros", 1561 + ] 1562 + 1563 + [[package]] 1564 + name = "profiling-procmacros" 1565 + version = "1.0.17" 1566 + source = "registry+https://github.com/rust-lang/crates.io-index" 1567 + checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" 1568 + dependencies = [ 1569 + "quote", 1570 + "syn", 1571 + ] 1572 + 1573 + [[package]] 1574 + name = "qoi" 1575 + version = "0.4.1" 1576 + source = "registry+https://github.com/rust-lang/crates.io-index" 1577 + checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" 1578 + dependencies = [ 1579 + "bytemuck", 1580 + ] 1581 + 1582 + [[package]] 1583 + name = "quick-error" 1584 + version = "2.0.1" 1585 + source = "registry+https://github.com/rust-lang/crates.io-index" 1586 + checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" 1587 + 1588 + [[package]] 1589 + name = "quote" 1590 + version = "1.0.40" 1591 + source = "registry+https://github.com/rust-lang/crates.io-index" 1592 + checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 1593 + dependencies = [ 1594 + "proc-macro2", 1595 + ] 1596 + 1597 + [[package]] 1598 + name = "r-efi" 1599 + version = "5.3.0" 1600 + source = "registry+https://github.com/rust-lang/crates.io-index" 1601 + checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 1602 + 1603 + [[package]] 1604 + name = "rand" 1605 + version = "0.8.5" 1606 + source = "registry+https://github.com/rust-lang/crates.io-index" 1607 + checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1608 + dependencies = [ 1609 + "libc", 1610 + "rand_chacha", 1611 + "rand_core", 1612 + ] 1613 + 1614 + [[package]] 1615 + name = "rand_chacha" 1616 + version = "0.3.1" 1617 + source = "registry+https://github.com/rust-lang/crates.io-index" 1618 + checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1619 + dependencies = [ 1620 + "ppv-lite86", 1621 + "rand_core", 1622 + ] 1623 + 1624 + [[package]] 1625 + name = "rand_core" 1626 + version = "0.6.4" 1627 + source = "registry+https://github.com/rust-lang/crates.io-index" 1628 + checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1629 + dependencies = [ 1630 + "getrandom 0.2.16", 1631 + ] 1632 + 1633 + [[package]] 1634 + name = "rav1e" 1635 + version = "0.7.1" 1636 + source = "registry+https://github.com/rust-lang/crates.io-index" 1637 + checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" 1638 + dependencies = [ 1639 + "arbitrary", 1640 + "arg_enum_proc_macro", 1641 + "arrayvec", 1642 + "av1-grain", 1643 + "bitstream-io", 1644 + "built", 1645 + "cfg-if", 1646 + "interpolate_name", 1647 + "itertools", 1648 + "libc", 1649 + "libfuzzer-sys", 1650 + "log", 1651 + "maybe-rayon", 1652 + "new_debug_unreachable", 1653 + "noop_proc_macro", 1654 + "num-derive", 1655 + "num-traits", 1656 + "once_cell", 1657 + "paste", 1658 + "profiling", 1659 + "rand", 1660 + "rand_chacha", 1661 + "simd_helpers", 1662 + "system-deps", 1663 + "thiserror 1.0.69", 1664 + "v_frame", 1665 + "wasm-bindgen", 1666 + ] 1667 + 1668 + [[package]] 1669 + name = "ravif" 1670 + version = "0.11.20" 1671 + source = "registry+https://github.com/rust-lang/crates.io-index" 1672 + checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" 1673 + dependencies = [ 1674 + "avif-serialize", 1675 + "imgref", 1676 + "loop9", 1677 + "quick-error", 1678 + "rav1e", 1679 + "rayon", 1680 + "rgb", 1681 + ] 1682 + 1683 + [[package]] 1684 + name = "rayon" 1685 + version = "1.10.0" 1686 + source = "registry+https://github.com/rust-lang/crates.io-index" 1687 + checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 1688 + dependencies = [ 1689 + "either", 1690 + "rayon-core", 1691 + ] 1692 + 1693 + [[package]] 1694 + name = "rayon-core" 1695 + version = "1.12.1" 1696 + source = "registry+https://github.com/rust-lang/crates.io-index" 1697 + checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 1698 + dependencies = [ 1699 + "crossbeam-deque", 1700 + "crossbeam-utils", 1701 + ] 1702 + 1703 + [[package]] 1704 + name = "redox_syscall" 1705 + version = "0.5.17" 1706 + source = "registry+https://github.com/rust-lang/crates.io-index" 1707 + checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" 1708 + dependencies = [ 1709 + "bitflags 2.9.1", 1710 + ] 1711 + 1712 + [[package]] 1713 + name = "redox_users" 1714 + version = "0.5.2" 1715 + source = "registry+https://github.com/rust-lang/crates.io-index" 1716 + checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" 1717 + dependencies = [ 1718 + "getrandom 0.2.16", 1719 + "libredox", 1720 + "thiserror 2.0.12", 1721 + ] 1722 + 1723 + [[package]] 1724 + name = "reqwest" 1725 + version = "0.12.22" 1726 + source = "registry+https://github.com/rust-lang/crates.io-index" 1727 + checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" 1728 + dependencies = [ 1729 + "base64", 1730 + "bytes", 1731 + "encoding_rs", 1732 + "futures-core", 1733 + "h2", 1734 + "http", 1735 + "http-body", 1736 + "http-body-util", 1737 + "hyper", 1738 + "hyper-rustls", 1739 + "hyper-tls", 1740 + "hyper-util", 1741 + "js-sys", 1742 + "log", 1743 + "mime", 1744 + "native-tls", 1745 + "percent-encoding", 1746 + "pin-project-lite", 1747 + "rustls-pki-types", 1748 + "serde", 1749 + "serde_json", 1750 + "serde_urlencoded", 1751 + "sync_wrapper", 1752 + "tokio", 1753 + "tokio-native-tls", 1754 + "tower", 1755 + "tower-http", 1756 + "tower-service", 1757 + "url", 1758 + "wasm-bindgen", 1759 + "wasm-bindgen-futures", 1760 + "web-sys", 1761 + ] 1762 + 1763 + [[package]] 1764 + name = "rgb" 1765 + version = "0.8.52" 1766 + source = "registry+https://github.com/rust-lang/crates.io-index" 1767 + checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" 1768 + 1769 + [[package]] 1770 + name = "ring" 1771 + version = "0.17.14" 1772 + source = "registry+https://github.com/rust-lang/crates.io-index" 1773 + checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 1774 + dependencies = [ 1775 + "cc", 1776 + "cfg-if", 1777 + "getrandom 0.2.16", 1778 + "libc", 1779 + "untrusted", 1780 + "windows-sys 0.52.0", 1781 + ] 1782 + 1783 + [[package]] 1784 + name = "rustc-demangle" 1785 + version = "0.1.26" 1786 + source = "registry+https://github.com/rust-lang/crates.io-index" 1787 + checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 1788 + 1789 + [[package]] 1790 + name = "rustix" 1791 + version = "1.0.8" 1792 + source = "registry+https://github.com/rust-lang/crates.io-index" 1793 + checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" 1794 + dependencies = [ 1795 + "bitflags 2.9.1", 1796 + "errno", 1797 + "libc", 1798 + "linux-raw-sys", 1799 + "windows-sys 0.60.2", 1800 + ] 1801 + 1802 + [[package]] 1803 + name = "rustls" 1804 + version = "0.23.31" 1805 + source = "registry+https://github.com/rust-lang/crates.io-index" 1806 + checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" 1807 + dependencies = [ 1808 + "once_cell", 1809 + "rustls-pki-types", 1810 + "rustls-webpki", 1811 + "subtle", 1812 + "zeroize", 1813 + ] 1814 + 1815 + [[package]] 1816 + name = "rustls-pki-types" 1817 + version = "1.12.0" 1818 + source = "registry+https://github.com/rust-lang/crates.io-index" 1819 + checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 1820 + dependencies = [ 1821 + "zeroize", 1822 + ] 1823 + 1824 + [[package]] 1825 + name = "rustls-webpki" 1826 + version = "0.103.4" 1827 + source = "registry+https://github.com/rust-lang/crates.io-index" 1828 + checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" 1829 + dependencies = [ 1830 + "ring", 1831 + "rustls-pki-types", 1832 + "untrusted", 1833 + ] 1834 + 1835 + [[package]] 1836 + name = "rustversion" 1837 + version = "1.0.21" 1838 + source = "registry+https://github.com/rust-lang/crates.io-index" 1839 + checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" 1840 + 1841 + [[package]] 1842 + name = "ryu" 1843 + version = "1.0.20" 1844 + source = "registry+https://github.com/rust-lang/crates.io-index" 1845 + checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 1846 + 1847 + [[package]] 1848 + name = "schannel" 1849 + version = "0.1.27" 1850 + source = "registry+https://github.com/rust-lang/crates.io-index" 1851 + checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 1852 + dependencies = [ 1853 + "windows-sys 0.59.0", 1854 + ] 1855 + 1856 + [[package]] 1857 + name = "scopeguard" 1858 + version = "1.2.0" 1859 + source = "registry+https://github.com/rust-lang/crates.io-index" 1860 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1861 + 1862 + [[package]] 1863 + name = "security-framework" 1864 + version = "2.11.1" 1865 + source = "registry+https://github.com/rust-lang/crates.io-index" 1866 + checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 1867 + dependencies = [ 1868 + "bitflags 2.9.1", 1869 + "core-foundation", 1870 + "core-foundation-sys", 1871 + "libc", 1872 + "security-framework-sys", 1873 + ] 1874 + 1875 + [[package]] 1876 + name = "security-framework-sys" 1877 + version = "2.14.0" 1878 + source = "registry+https://github.com/rust-lang/crates.io-index" 1879 + checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" 1880 + dependencies = [ 1881 + "core-foundation-sys", 1882 + "libc", 1883 + ] 1884 + 1885 + [[package]] 1886 + name = "serde" 1887 + version = "1.0.219" 1888 + source = "registry+https://github.com/rust-lang/crates.io-index" 1889 + checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1890 + dependencies = [ 1891 + "serde_derive", 1892 + ] 1893 + 1894 + [[package]] 1895 + name = "serde_derive" 1896 + version = "1.0.219" 1897 + source = "registry+https://github.com/rust-lang/crates.io-index" 1898 + checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1899 + dependencies = [ 1900 + "proc-macro2", 1901 + "quote", 1902 + "syn", 1903 + ] 1904 + 1905 + [[package]] 1906 + name = "serde_json" 1907 + version = "1.0.142" 1908 + source = "registry+https://github.com/rust-lang/crates.io-index" 1909 + checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" 1910 + dependencies = [ 1911 + "itoa", 1912 + "memchr", 1913 + "ryu", 1914 + "serde", 1915 + ] 1916 + 1917 + [[package]] 1918 + name = "serde_spanned" 1919 + version = "0.6.9" 1920 + source = "registry+https://github.com/rust-lang/crates.io-index" 1921 + checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" 1922 + dependencies = [ 1923 + "serde", 1924 + ] 1925 + 1926 + [[package]] 1927 + name = "serde_urlencoded" 1928 + version = "0.7.1" 1929 + source = "registry+https://github.com/rust-lang/crates.io-index" 1930 + checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1931 + dependencies = [ 1932 + "form_urlencoded", 1933 + "itoa", 1934 + "ryu", 1935 + "serde", 1936 + ] 1937 + 1938 + [[package]] 1939 + name = "shell-words" 1940 + version = "1.1.0" 1941 + source = "registry+https://github.com/rust-lang/crates.io-index" 1942 + checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" 1943 + 1944 + [[package]] 1945 + name = "shlex" 1946 + version = "1.3.0" 1947 + source = "registry+https://github.com/rust-lang/crates.io-index" 1948 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1949 + 1950 + [[package]] 1951 + name = "signal-hook-registry" 1952 + version = "1.4.6" 1953 + source = "registry+https://github.com/rust-lang/crates.io-index" 1954 + checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" 1955 + dependencies = [ 1956 + "libc", 1957 + ] 1958 + 1959 + [[package]] 1960 + name = "simd-adler32" 1961 + version = "0.3.7" 1962 + source = "registry+https://github.com/rust-lang/crates.io-index" 1963 + checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 1964 + 1965 + [[package]] 1966 + name = "simd_helpers" 1967 + version = "0.1.0" 1968 + source = "registry+https://github.com/rust-lang/crates.io-index" 1969 + checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" 1970 + dependencies = [ 1971 + "quote", 1972 + ] 1973 + 1974 + [[package]] 1975 + name = "slab" 1976 + version = "0.4.10" 1977 + source = "registry+https://github.com/rust-lang/crates.io-index" 1978 + checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" 1979 + 1980 + [[package]] 1981 + name = "smallvec" 1982 + version = "1.15.1" 1983 + source = "registry+https://github.com/rust-lang/crates.io-index" 1984 + checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 1985 + 1986 + [[package]] 1987 + name = "socket2" 1988 + version = "0.6.0" 1989 + source = "registry+https://github.com/rust-lang/crates.io-index" 1990 + checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" 1991 + dependencies = [ 1992 + "libc", 1993 + "windows-sys 0.59.0", 1994 + ] 1995 + 1996 + [[package]] 1997 + name = "stable_deref_trait" 1998 + version = "1.2.0" 1999 + source = "registry+https://github.com/rust-lang/crates.io-index" 2000 + checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 2001 + 2002 + [[package]] 2003 + name = "strsim" 2004 + version = "0.11.1" 2005 + source = "registry+https://github.com/rust-lang/crates.io-index" 2006 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 2007 + 2008 + [[package]] 2009 + name = "subtle" 2010 + version = "2.6.1" 2011 + source = "registry+https://github.com/rust-lang/crates.io-index" 2012 + checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 2013 + 2014 + [[package]] 2015 + name = "syn" 2016 + version = "2.0.104" 2017 + source = "registry+https://github.com/rust-lang/crates.io-index" 2018 + checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" 2019 + dependencies = [ 2020 + "proc-macro2", 2021 + "quote", 2022 + "unicode-ident", 2023 + ] 2024 + 2025 + [[package]] 2026 + name = "sync_wrapper" 2027 + version = "1.0.2" 2028 + source = "registry+https://github.com/rust-lang/crates.io-index" 2029 + checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 2030 + dependencies = [ 2031 + "futures-core", 2032 + ] 2033 + 2034 + [[package]] 2035 + name = "synstructure" 2036 + version = "0.13.2" 2037 + source = "registry+https://github.com/rust-lang/crates.io-index" 2038 + checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 2039 + dependencies = [ 2040 + "proc-macro2", 2041 + "quote", 2042 + "syn", 2043 + ] 2044 + 2045 + [[package]] 2046 + name = "system-configuration" 2047 + version = "0.6.1" 2048 + source = "registry+https://github.com/rust-lang/crates.io-index" 2049 + checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 2050 + dependencies = [ 2051 + "bitflags 2.9.1", 2052 + "core-foundation", 2053 + "system-configuration-sys", 2054 + ] 2055 + 2056 + [[package]] 2057 + name = "system-configuration-sys" 2058 + version = "0.6.0" 2059 + source = "registry+https://github.com/rust-lang/crates.io-index" 2060 + checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 2061 + dependencies = [ 2062 + "core-foundation-sys", 2063 + "libc", 2064 + ] 2065 + 2066 + [[package]] 2067 + name = "system-deps" 2068 + version = "6.2.2" 2069 + source = "registry+https://github.com/rust-lang/crates.io-index" 2070 + checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" 2071 + dependencies = [ 2072 + "cfg-expr", 2073 + "heck", 2074 + "pkg-config", 2075 + "toml", 2076 + "version-compare", 2077 + ] 2078 + 2079 + [[package]] 2080 + name = "target-lexicon" 2081 + version = "0.12.16" 2082 + source = "registry+https://github.com/rust-lang/crates.io-index" 2083 + checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" 2084 + 2085 + [[package]] 2086 + name = "tempfile" 2087 + version = "3.20.0" 2088 + source = "registry+https://github.com/rust-lang/crates.io-index" 2089 + checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" 2090 + dependencies = [ 2091 + "fastrand", 2092 + "getrandom 0.3.3", 2093 + "once_cell", 2094 + "rustix", 2095 + "windows-sys 0.59.0", 2096 + ] 2097 + 2098 + [[package]] 2099 + name = "thiserror" 2100 + version = "1.0.69" 2101 + source = "registry+https://github.com/rust-lang/crates.io-index" 2102 + checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 2103 + dependencies = [ 2104 + "thiserror-impl 1.0.69", 2105 + ] 2106 + 2107 + [[package]] 2108 + name = "thiserror" 2109 + version = "2.0.12" 2110 + source = "registry+https://github.com/rust-lang/crates.io-index" 2111 + checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 2112 + dependencies = [ 2113 + "thiserror-impl 2.0.12", 2114 + ] 2115 + 2116 + [[package]] 2117 + name = "thiserror-impl" 2118 + version = "1.0.69" 2119 + source = "registry+https://github.com/rust-lang/crates.io-index" 2120 + checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 2121 + dependencies = [ 2122 + "proc-macro2", 2123 + "quote", 2124 + "syn", 2125 + ] 2126 + 2127 + [[package]] 2128 + name = "thiserror-impl" 2129 + version = "2.0.12" 2130 + source = "registry+https://github.com/rust-lang/crates.io-index" 2131 + checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 2132 + dependencies = [ 2133 + "proc-macro2", 2134 + "quote", 2135 + "syn", 2136 + ] 2137 + 2138 + [[package]] 2139 + name = "tiff" 2140 + version = "0.9.1" 2141 + source = "registry+https://github.com/rust-lang/crates.io-index" 2142 + checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" 2143 + dependencies = [ 2144 + "flate2", 2145 + "jpeg-decoder", 2146 + "weezl", 2147 + ] 2148 + 2149 + [[package]] 2150 + name = "tinystr" 2151 + version = "0.8.1" 2152 + source = "registry+https://github.com/rust-lang/crates.io-index" 2153 + checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 2154 + dependencies = [ 2155 + "displaydoc", 2156 + "zerovec", 2157 + ] 2158 + 2159 + [[package]] 2160 + name = "tokio" 2161 + version = "1.47.1" 2162 + source = "registry+https://github.com/rust-lang/crates.io-index" 2163 + checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" 2164 + dependencies = [ 2165 + "backtrace", 2166 + "bytes", 2167 + "io-uring", 2168 + "libc", 2169 + "mio", 2170 + "parking_lot", 2171 + "pin-project-lite", 2172 + "signal-hook-registry", 2173 + "slab", 2174 + "socket2", 2175 + "tokio-macros", 2176 + "windows-sys 0.59.0", 2177 + ] 2178 + 2179 + [[package]] 2180 + name = "tokio-macros" 2181 + version = "2.5.0" 2182 + source = "registry+https://github.com/rust-lang/crates.io-index" 2183 + checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 2184 + dependencies = [ 2185 + "proc-macro2", 2186 + "quote", 2187 + "syn", 2188 + ] 2189 + 2190 + [[package]] 2191 + name = "tokio-native-tls" 2192 + version = "0.3.1" 2193 + source = "registry+https://github.com/rust-lang/crates.io-index" 2194 + checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 2195 + dependencies = [ 2196 + "native-tls", 2197 + "tokio", 2198 + ] 2199 + 2200 + [[package]] 2201 + name = "tokio-rustls" 2202 + version = "0.26.2" 2203 + source = "registry+https://github.com/rust-lang/crates.io-index" 2204 + checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 2205 + dependencies = [ 2206 + "rustls", 2207 + "tokio", 2208 + ] 2209 + 2210 + [[package]] 2211 + name = "tokio-util" 2212 + version = "0.7.16" 2213 + source = "registry+https://github.com/rust-lang/crates.io-index" 2214 + checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" 2215 + dependencies = [ 2216 + "bytes", 2217 + "futures-core", 2218 + "futures-sink", 2219 + "pin-project-lite", 2220 + "tokio", 2221 + ] 2222 + 2223 + [[package]] 2224 + name = "toml" 2225 + version = "0.8.23" 2226 + source = "registry+https://github.com/rust-lang/crates.io-index" 2227 + checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" 2228 + dependencies = [ 2229 + "serde", 2230 + "serde_spanned", 2231 + "toml_datetime", 2232 + "toml_edit", 2233 + ] 2234 + 2235 + [[package]] 2236 + name = "toml_datetime" 2237 + version = "0.6.11" 2238 + source = "registry+https://github.com/rust-lang/crates.io-index" 2239 + checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" 2240 + dependencies = [ 2241 + "serde", 2242 + ] 2243 + 2244 + [[package]] 2245 + name = "toml_edit" 2246 + version = "0.22.27" 2247 + source = "registry+https://github.com/rust-lang/crates.io-index" 2248 + checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" 2249 + dependencies = [ 2250 + "indexmap", 2251 + "serde", 2252 + "serde_spanned", 2253 + "toml_datetime", 2254 + "winnow", 2255 + ] 2256 + 2257 + [[package]] 2258 + name = "tower" 2259 + version = "0.5.2" 2260 + source = "registry+https://github.com/rust-lang/crates.io-index" 2261 + checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 2262 + dependencies = [ 2263 + "futures-core", 2264 + "futures-util", 2265 + "pin-project-lite", 2266 + "sync_wrapper", 2267 + "tokio", 2268 + "tower-layer", 2269 + "tower-service", 2270 + ] 2271 + 2272 + [[package]] 2273 + name = "tower-http" 2274 + version = "0.6.6" 2275 + source = "registry+https://github.com/rust-lang/crates.io-index" 2276 + checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" 2277 + dependencies = [ 2278 + "bitflags 2.9.1", 2279 + "bytes", 2280 + "futures-util", 2281 + "http", 2282 + "http-body", 2283 + "iri-string", 2284 + "pin-project-lite", 2285 + "tower", 2286 + "tower-layer", 2287 + "tower-service", 2288 + ] 2289 + 2290 + [[package]] 2291 + name = "tower-layer" 2292 + version = "0.3.3" 2293 + source = "registry+https://github.com/rust-lang/crates.io-index" 2294 + checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 2295 + 2296 + [[package]] 2297 + name = "tower-service" 2298 + version = "0.3.3" 2299 + source = "registry+https://github.com/rust-lang/crates.io-index" 2300 + checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 2301 + 2302 + [[package]] 2303 + name = "tracing" 2304 + version = "0.1.41" 2305 + source = "registry+https://github.com/rust-lang/crates.io-index" 2306 + checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 2307 + dependencies = [ 2308 + "pin-project-lite", 2309 + "tracing-core", 2310 + ] 2311 + 2312 + [[package]] 2313 + name = "tracing-core" 2314 + version = "0.1.34" 2315 + source = "registry+https://github.com/rust-lang/crates.io-index" 2316 + checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 2317 + dependencies = [ 2318 + "once_cell", 2319 + ] 2320 + 2321 + [[package]] 2322 + name = "try-lock" 2323 + version = "0.2.5" 2324 + source = "registry+https://github.com/rust-lang/crates.io-index" 2325 + checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 2326 + 2327 + [[package]] 2328 + name = "unicode-ident" 2329 + version = "1.0.18" 2330 + source = "registry+https://github.com/rust-lang/crates.io-index" 2331 + checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 2332 + 2333 + [[package]] 2334 + name = "unicode-width" 2335 + version = "0.2.1" 2336 + source = "registry+https://github.com/rust-lang/crates.io-index" 2337 + checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" 2338 + 2339 + [[package]] 2340 + name = "unit-prefix" 2341 + version = "0.5.1" 2342 + source = "registry+https://github.com/rust-lang/crates.io-index" 2343 + checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" 2344 + 2345 + [[package]] 2346 + name = "untrusted" 2347 + version = "0.9.0" 2348 + source = "registry+https://github.com/rust-lang/crates.io-index" 2349 + checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 2350 + 2351 + [[package]] 2352 + name = "url" 2353 + version = "2.5.4" 2354 + source = "registry+https://github.com/rust-lang/crates.io-index" 2355 + checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 2356 + dependencies = [ 2357 + "form_urlencoded", 2358 + "idna", 2359 + "percent-encoding", 2360 + ] 2361 + 2362 + [[package]] 2363 + name = "urlencoding" 2364 + version = "2.1.3" 2365 + source = "registry+https://github.com/rust-lang/crates.io-index" 2366 + checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" 2367 + 2368 + [[package]] 2369 + name = "utf8_iter" 2370 + version = "1.0.4" 2371 + source = "registry+https://github.com/rust-lang/crates.io-index" 2372 + checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 2373 + 2374 + [[package]] 2375 + name = "utf8parse" 2376 + version = "0.2.2" 2377 + source = "registry+https://github.com/rust-lang/crates.io-index" 2378 + checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 2379 + 2380 + [[package]] 2381 + name = "v_frame" 2382 + version = "0.3.9" 2383 + source = "registry+https://github.com/rust-lang/crates.io-index" 2384 + checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" 2385 + dependencies = [ 2386 + "aligned-vec", 2387 + "num-traits", 2388 + "wasm-bindgen", 2389 + ] 2390 + 2391 + [[package]] 2392 + name = "vcpkg" 2393 + version = "0.2.15" 2394 + source = "registry+https://github.com/rust-lang/crates.io-index" 2395 + checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2396 + 2397 + [[package]] 2398 + name = "version-compare" 2399 + version = "0.2.0" 2400 + source = "registry+https://github.com/rust-lang/crates.io-index" 2401 + checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" 2402 + 2403 + [[package]] 2404 + name = "want" 2405 + version = "0.3.1" 2406 + source = "registry+https://github.com/rust-lang/crates.io-index" 2407 + checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 2408 + dependencies = [ 2409 + "try-lock", 2410 + ] 2411 + 2412 + [[package]] 2413 + name = "wasi" 2414 + version = "0.11.1+wasi-snapshot-preview1" 2415 + source = "registry+https://github.com/rust-lang/crates.io-index" 2416 + checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 2417 + 2418 + [[package]] 2419 + name = "wasi" 2420 + version = "0.14.2+wasi-0.2.4" 2421 + source = "registry+https://github.com/rust-lang/crates.io-index" 2422 + checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 2423 + dependencies = [ 2424 + "wit-bindgen-rt", 2425 + ] 2426 + 2427 + [[package]] 2428 + name = "wasm-bindgen" 2429 + version = "0.2.100" 2430 + source = "registry+https://github.com/rust-lang/crates.io-index" 2431 + checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 2432 + dependencies = [ 2433 + "cfg-if", 2434 + "once_cell", 2435 + "rustversion", 2436 + "wasm-bindgen-macro", 2437 + ] 2438 + 2439 + [[package]] 2440 + name = "wasm-bindgen-backend" 2441 + version = "0.2.100" 2442 + source = "registry+https://github.com/rust-lang/crates.io-index" 2443 + checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 2444 + dependencies = [ 2445 + "bumpalo", 2446 + "log", 2447 + "proc-macro2", 2448 + "quote", 2449 + "syn", 2450 + "wasm-bindgen-shared", 2451 + ] 2452 + 2453 + [[package]] 2454 + name = "wasm-bindgen-futures" 2455 + version = "0.4.50" 2456 + source = "registry+https://github.com/rust-lang/crates.io-index" 2457 + checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 2458 + dependencies = [ 2459 + "cfg-if", 2460 + "js-sys", 2461 + "once_cell", 2462 + "wasm-bindgen", 2463 + "web-sys", 2464 + ] 2465 + 2466 + [[package]] 2467 + name = "wasm-bindgen-macro" 2468 + version = "0.2.100" 2469 + source = "registry+https://github.com/rust-lang/crates.io-index" 2470 + checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 2471 + dependencies = [ 2472 + "quote", 2473 + "wasm-bindgen-macro-support", 2474 + ] 2475 + 2476 + [[package]] 2477 + name = "wasm-bindgen-macro-support" 2478 + version = "0.2.100" 2479 + source = "registry+https://github.com/rust-lang/crates.io-index" 2480 + checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 2481 + dependencies = [ 2482 + "proc-macro2", 2483 + "quote", 2484 + "syn", 2485 + "wasm-bindgen-backend", 2486 + "wasm-bindgen-shared", 2487 + ] 2488 + 2489 + [[package]] 2490 + name = "wasm-bindgen-shared" 2491 + version = "0.2.100" 2492 + source = "registry+https://github.com/rust-lang/crates.io-index" 2493 + checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 2494 + dependencies = [ 2495 + "unicode-ident", 2496 + ] 2497 + 2498 + [[package]] 2499 + name = "web-sys" 2500 + version = "0.3.77" 2501 + source = "registry+https://github.com/rust-lang/crates.io-index" 2502 + checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 2503 + dependencies = [ 2504 + "js-sys", 2505 + "wasm-bindgen", 2506 + ] 2507 + 2508 + [[package]] 2509 + name = "web-time" 2510 + version = "1.1.0" 2511 + source = "registry+https://github.com/rust-lang/crates.io-index" 2512 + checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 2513 + dependencies = [ 2514 + "js-sys", 2515 + "wasm-bindgen", 2516 + ] 2517 + 2518 + [[package]] 2519 + name = "weezl" 2520 + version = "0.1.10" 2521 + source = "registry+https://github.com/rust-lang/crates.io-index" 2522 + checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" 2523 + 2524 + [[package]] 2525 + name = "windows-core" 2526 + version = "0.61.2" 2527 + source = "registry+https://github.com/rust-lang/crates.io-index" 2528 + checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 2529 + dependencies = [ 2530 + "windows-implement", 2531 + "windows-interface", 2532 + "windows-link", 2533 + "windows-result", 2534 + "windows-strings", 2535 + ] 2536 + 2537 + [[package]] 2538 + name = "windows-implement" 2539 + version = "0.60.0" 2540 + source = "registry+https://github.com/rust-lang/crates.io-index" 2541 + checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 2542 + dependencies = [ 2543 + "proc-macro2", 2544 + "quote", 2545 + "syn", 2546 + ] 2547 + 2548 + [[package]] 2549 + name = "windows-interface" 2550 + version = "0.59.1" 2551 + source = "registry+https://github.com/rust-lang/crates.io-index" 2552 + checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 2553 + dependencies = [ 2554 + "proc-macro2", 2555 + "quote", 2556 + "syn", 2557 + ] 2558 + 2559 + [[package]] 2560 + name = "windows-link" 2561 + version = "0.1.3" 2562 + source = "registry+https://github.com/rust-lang/crates.io-index" 2563 + checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 2564 + 2565 + [[package]] 2566 + name = "windows-registry" 2567 + version = "0.5.3" 2568 + source = "registry+https://github.com/rust-lang/crates.io-index" 2569 + checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" 2570 + dependencies = [ 2571 + "windows-link", 2572 + "windows-result", 2573 + "windows-strings", 2574 + ] 2575 + 2576 + [[package]] 2577 + name = "windows-result" 2578 + version = "0.3.4" 2579 + source = "registry+https://github.com/rust-lang/crates.io-index" 2580 + checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 2581 + dependencies = [ 2582 + "windows-link", 2583 + ] 2584 + 2585 + [[package]] 2586 + name = "windows-strings" 2587 + version = "0.4.2" 2588 + source = "registry+https://github.com/rust-lang/crates.io-index" 2589 + checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 2590 + dependencies = [ 2591 + "windows-link", 2592 + ] 2593 + 2594 + [[package]] 2595 + name = "windows-sys" 2596 + version = "0.52.0" 2597 + source = "registry+https://github.com/rust-lang/crates.io-index" 2598 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2599 + dependencies = [ 2600 + "windows-targets 0.52.6", 2601 + ] 2602 + 2603 + [[package]] 2604 + name = "windows-sys" 2605 + version = "0.59.0" 2606 + source = "registry+https://github.com/rust-lang/crates.io-index" 2607 + checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2608 + dependencies = [ 2609 + "windows-targets 0.52.6", 2610 + ] 2611 + 2612 + [[package]] 2613 + name = "windows-sys" 2614 + version = "0.60.2" 2615 + source = "registry+https://github.com/rust-lang/crates.io-index" 2616 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 2617 + dependencies = [ 2618 + "windows-targets 0.53.3", 2619 + ] 2620 + 2621 + [[package]] 2622 + name = "windows-targets" 2623 + version = "0.52.6" 2624 + source = "registry+https://github.com/rust-lang/crates.io-index" 2625 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2626 + dependencies = [ 2627 + "windows_aarch64_gnullvm 0.52.6", 2628 + "windows_aarch64_msvc 0.52.6", 2629 + "windows_i686_gnu 0.52.6", 2630 + "windows_i686_gnullvm 0.52.6", 2631 + "windows_i686_msvc 0.52.6", 2632 + "windows_x86_64_gnu 0.52.6", 2633 + "windows_x86_64_gnullvm 0.52.6", 2634 + "windows_x86_64_msvc 0.52.6", 2635 + ] 2636 + 2637 + [[package]] 2638 + name = "windows-targets" 2639 + version = "0.53.3" 2640 + source = "registry+https://github.com/rust-lang/crates.io-index" 2641 + checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" 2642 + dependencies = [ 2643 + "windows-link", 2644 + "windows_aarch64_gnullvm 0.53.0", 2645 + "windows_aarch64_msvc 0.53.0", 2646 + "windows_i686_gnu 0.53.0", 2647 + "windows_i686_gnullvm 0.53.0", 2648 + "windows_i686_msvc 0.53.0", 2649 + "windows_x86_64_gnu 0.53.0", 2650 + "windows_x86_64_gnullvm 0.53.0", 2651 + "windows_x86_64_msvc 0.53.0", 2652 + ] 2653 + 2654 + [[package]] 2655 + name = "windows_aarch64_gnullvm" 2656 + version = "0.52.6" 2657 + source = "registry+https://github.com/rust-lang/crates.io-index" 2658 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2659 + 2660 + [[package]] 2661 + name = "windows_aarch64_gnullvm" 2662 + version = "0.53.0" 2663 + source = "registry+https://github.com/rust-lang/crates.io-index" 2664 + checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 2665 + 2666 + [[package]] 2667 + name = "windows_aarch64_msvc" 2668 + version = "0.52.6" 2669 + source = "registry+https://github.com/rust-lang/crates.io-index" 2670 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2671 + 2672 + [[package]] 2673 + name = "windows_aarch64_msvc" 2674 + version = "0.53.0" 2675 + source = "registry+https://github.com/rust-lang/crates.io-index" 2676 + checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 2677 + 2678 + [[package]] 2679 + name = "windows_i686_gnu" 2680 + version = "0.52.6" 2681 + source = "registry+https://github.com/rust-lang/crates.io-index" 2682 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2683 + 2684 + [[package]] 2685 + name = "windows_i686_gnu" 2686 + version = "0.53.0" 2687 + source = "registry+https://github.com/rust-lang/crates.io-index" 2688 + checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 2689 + 2690 + [[package]] 2691 + name = "windows_i686_gnullvm" 2692 + version = "0.52.6" 2693 + source = "registry+https://github.com/rust-lang/crates.io-index" 2694 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2695 + 2696 + [[package]] 2697 + name = "windows_i686_gnullvm" 2698 + version = "0.53.0" 2699 + source = "registry+https://github.com/rust-lang/crates.io-index" 2700 + checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 2701 + 2702 + [[package]] 2703 + name = "windows_i686_msvc" 2704 + version = "0.52.6" 2705 + source = "registry+https://github.com/rust-lang/crates.io-index" 2706 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2707 + 2708 + [[package]] 2709 + name = "windows_i686_msvc" 2710 + version = "0.53.0" 2711 + source = "registry+https://github.com/rust-lang/crates.io-index" 2712 + checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 2713 + 2714 + [[package]] 2715 + name = "windows_x86_64_gnu" 2716 + version = "0.52.6" 2717 + source = "registry+https://github.com/rust-lang/crates.io-index" 2718 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2719 + 2720 + [[package]] 2721 + name = "windows_x86_64_gnu" 2722 + version = "0.53.0" 2723 + source = "registry+https://github.com/rust-lang/crates.io-index" 2724 + checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 2725 + 2726 + [[package]] 2727 + name = "windows_x86_64_gnullvm" 2728 + version = "0.52.6" 2729 + source = "registry+https://github.com/rust-lang/crates.io-index" 2730 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2731 + 2732 + [[package]] 2733 + name = "windows_x86_64_gnullvm" 2734 + version = "0.53.0" 2735 + source = "registry+https://github.com/rust-lang/crates.io-index" 2736 + checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 2737 + 2738 + [[package]] 2739 + name = "windows_x86_64_msvc" 2740 + version = "0.52.6" 2741 + source = "registry+https://github.com/rust-lang/crates.io-index" 2742 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2743 + 2744 + [[package]] 2745 + name = "windows_x86_64_msvc" 2746 + version = "0.53.0" 2747 + source = "registry+https://github.com/rust-lang/crates.io-index" 2748 + checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 2749 + 2750 + [[package]] 2751 + name = "winnow" 2752 + version = "0.7.12" 2753 + source = "registry+https://github.com/rust-lang/crates.io-index" 2754 + checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" 2755 + dependencies = [ 2756 + "memchr", 2757 + ] 2758 + 2759 + [[package]] 2760 + name = "wit-bindgen-rt" 2761 + version = "0.39.0" 2762 + source = "registry+https://github.com/rust-lang/crates.io-index" 2763 + checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 2764 + dependencies = [ 2765 + "bitflags 2.9.1", 2766 + ] 2767 + 2768 + [[package]] 2769 + name = "writeable" 2770 + version = "0.6.1" 2771 + source = "registry+https://github.com/rust-lang/crates.io-index" 2772 + checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 2773 + 2774 + [[package]] 2775 + name = "yoke" 2776 + version = "0.8.0" 2777 + source = "registry+https://github.com/rust-lang/crates.io-index" 2778 + checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 2779 + dependencies = [ 2780 + "serde", 2781 + "stable_deref_trait", 2782 + "yoke-derive", 2783 + "zerofrom", 2784 + ] 2785 + 2786 + [[package]] 2787 + name = "yoke-derive" 2788 + version = "0.8.0" 2789 + source = "registry+https://github.com/rust-lang/crates.io-index" 2790 + checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 2791 + dependencies = [ 2792 + "proc-macro2", 2793 + "quote", 2794 + "syn", 2795 + "synstructure", 2796 + ] 2797 + 2798 + [[package]] 2799 + name = "zerocopy" 2800 + version = "0.8.26" 2801 + source = "registry+https://github.com/rust-lang/crates.io-index" 2802 + checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" 2803 + dependencies = [ 2804 + "zerocopy-derive", 2805 + ] 2806 + 2807 + [[package]] 2808 + name = "zerocopy-derive" 2809 + version = "0.8.26" 2810 + source = "registry+https://github.com/rust-lang/crates.io-index" 2811 + checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" 2812 + dependencies = [ 2813 + "proc-macro2", 2814 + "quote", 2815 + "syn", 2816 + ] 2817 + 2818 + [[package]] 2819 + name = "zerofrom" 2820 + version = "0.1.6" 2821 + source = "registry+https://github.com/rust-lang/crates.io-index" 2822 + checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 2823 + dependencies = [ 2824 + "zerofrom-derive", 2825 + ] 2826 + 2827 + [[package]] 2828 + name = "zerofrom-derive" 2829 + version = "0.1.6" 2830 + source = "registry+https://github.com/rust-lang/crates.io-index" 2831 + checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 2832 + dependencies = [ 2833 + "proc-macro2", 2834 + "quote", 2835 + "syn", 2836 + "synstructure", 2837 + ] 2838 + 2839 + [[package]] 2840 + name = "zeroize" 2841 + version = "1.8.1" 2842 + source = "registry+https://github.com/rust-lang/crates.io-index" 2843 + checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2844 + 2845 + [[package]] 2846 + name = "zerotrie" 2847 + version = "0.2.2" 2848 + source = "registry+https://github.com/rust-lang/crates.io-index" 2849 + checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 2850 + dependencies = [ 2851 + "displaydoc", 2852 + "yoke", 2853 + "zerofrom", 2854 + ] 2855 + 2856 + [[package]] 2857 + name = "zerovec" 2858 + version = "0.11.3" 2859 + source = "registry+https://github.com/rust-lang/crates.io-index" 2860 + checksum = "bdbb9122ea75b11bf96e7492afb723e8a7fbe12c67417aa95e7e3d18144d37cd" 2861 + dependencies = [ 2862 + "yoke", 2863 + "zerofrom", 2864 + "zerovec-derive", 2865 + ] 2866 + 2867 + [[package]] 2868 + name = "zerovec-derive" 2869 + version = "0.11.1" 2870 + source = "registry+https://github.com/rust-lang/crates.io-index" 2871 + checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 2872 + dependencies = [ 2873 + "proc-macro2", 2874 + "quote", 2875 + "syn", 2876 + ] 2877 + 2878 + [[package]] 2879 + name = "zune-core" 2880 + version = "0.4.12" 2881 + source = "registry+https://github.com/rust-lang/crates.io-index" 2882 + checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" 2883 + 2884 + [[package]] 2885 + name = "zune-inflate" 2886 + version = "0.2.54" 2887 + source = "registry+https://github.com/rust-lang/crates.io-index" 2888 + checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" 2889 + dependencies = [ 2890 + "simd-adler32", 2891 + ] 2892 + 2893 + [[package]] 2894 + name = "zune-jpeg" 2895 + version = "0.4.20" 2896 + source = "registry+https://github.com/rust-lang/crates.io-index" 2897 + checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089" 2898 + dependencies = [ 2899 + "zune-core", 2900 + ]
+29
cli/Cargo.toml
··· 1 + [package] 2 + name = "grain-cli" 3 + version = "0.1.0" 4 + edition = "2021" 5 + 6 + [[bin]] 7 + name = "grain" 8 + path = "src/main.rs" 9 + 10 + [dependencies] 11 + tokio = { version = "1.0", features = ["full"] } 12 + reqwest = { version = "0.12", features = ["json"] } 13 + serde = { version = "1.0", features = ["derive"] } 14 + serde_json = "1.0" 15 + clap = { version = "4.0", features = ["derive"] } 16 + dialoguer = "0.11" 17 + image = "0.25" 18 + anyhow = "1.0" 19 + open = "5.0" 20 + hyper = { version = "1.0", features = ["full"] } 21 + hyper-util = { version = "0.1", features = ["full"] } 22 + http-body-util = "0.1" 23 + tokio-util = "0.7" 24 + futures-util = "0.3" 25 + chrono = { version = "0.4", features = ["serde"] } 26 + urlencoding = "2.1" 27 + url = "2.5" 28 + indicatif = "0.18" 29 + dirs = "6.0"
+72
cli/README.md
··· 1 + # Grain CLI 2 + 3 + ⚠️ **Work in Progress**: This CLI is currently under development and is not 4 + ready for use yet. 5 + 6 + A command-line interface for [grain.social](https://grain.social) - a platform 7 + for sharing photo galleries. 8 + 9 + ## Features 10 + 11 + - **Authentication**: OAuth login flow with grain.social 12 + - **Gallery Management**: List and create photo galleries 13 + - **Image Upload**: Bulk upload images from local folders with automatic 14 + resizing 15 + 16 + ## Requirements 17 + 18 + - [Rust](https://rustup.rs/) installed 19 + - AT Protocol account ([Bluesky](https://bsky.app)) 20 + 21 + ## Installation 22 + 23 + Build from source: 24 + 25 + ```bash 26 + cargo build --release 27 + ``` 28 + 29 + The binary will be available at `target/release/grain`. 30 + 31 + ## Usage 32 + 33 + ### Authentication 34 + 35 + First, authenticate with your grain.social account: 36 + 37 + ```bash 38 + grain login 39 + ``` 40 + 41 + This will open your browser for OAuth authentication. 42 + 43 + ### Gallery Commands 44 + 45 + List your existing galleries: 46 + 47 + ```bash 48 + grain gallery list 49 + ``` 50 + 51 + Create a new gallery from a folder of images: 52 + 53 + ```bash 54 + grain gallery create 55 + ``` 56 + 57 + You'll be prompted for: 58 + 59 + - Gallery title 60 + - Gallery description (optional) 61 + - Path to folder containing images 62 + 63 + The CLI will automatically resize images and upload them to your new gallery. 64 + 65 + ### Verbose Output 66 + 67 + Add `--verbose` to any command for detailed output: 68 + 69 + ```bash 70 + grain login --verbose 71 + grain gallery create --verbose 72 + ```
+128
cli/flake.nix
··· 1 + { 2 + description = "Grain CLI - A command-line interface for grain.social"; 3 + 4 + inputs = { 5 + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 + flake-utils.url = "github:numtide/flake-utils"; 7 + rust-overlay = { 8 + url = "github:oxalica/rust-overlay"; 9 + inputs.nixpkgs.follows = "nixpkgs"; 10 + }; 11 + }; 12 + 13 + outputs = { self, nixpkgs, flake-utils, rust-overlay }: 14 + flake-utils.lib.eachDefaultSystem (system: 15 + let 16 + overlays = [ (import rust-overlay) ]; 17 + pkgs = import nixpkgs { inherit system overlays; }; 18 + 19 + rustToolchain = pkgs.rust-bin.stable.latest.default.override { 20 + extensions = [ "rust-src" ]; 21 + targets = [ 22 + "x86_64-unknown-linux-gnu" 23 + "x86_64-pc-windows-gnu" 24 + "x86_64-apple-darwin" 25 + "aarch64-apple-darwin" 26 + "aarch64-unknown-linux-gnu" 27 + ]; 28 + }; 29 + 30 + # Build function for different targets 31 + buildGrainCLI = target: pkgs.stdenv.mkDerivation rec { 32 + pname = "grain-cli-${target}"; 33 + version = "0.1.0"; 34 + 35 + src = ./.; 36 + 37 + nativeBuildInputs = with pkgs; [ 38 + rustToolchain 39 + pkg-config 40 + ] ++ pkgs.lib.optionals (target == "x86_64-pc-windows-gnu") [ 41 + pkgs.pkgsCross.mingwW64.stdenv.cc 42 + ]; 43 + 44 + buildInputs = with pkgs; [ 45 + openssl 46 + ] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ 47 + pkgs.darwin.apple_sdk.frameworks.Security 48 + pkgs.darwin.apple_sdk.frameworks.SystemConfiguration 49 + ]; 50 + 51 + buildPhase = '' 52 + export CARGO_HOME=$TMPDIR/cargo 53 + export OPENSSL_NO_VENDOR=1 54 + export PKG_CONFIG_PATH="${pkgs.openssl.dev}/lib/pkgconfig:$PKG_CONFIG_PATH" 55 + 56 + ${if target == "x86_64-pc-windows-gnu" then '' 57 + export CC_x86_64_pc_windows_gnu="${pkgs.pkgsCross.mingwW64.stdenv.cc}/bin/x86_64-w64-mingw32-gcc" 58 + export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER="${pkgs.pkgsCross.mingwW64.stdenv.cc}/bin/x86_64-w64-mingw32-gcc" 59 + export PKG_CONFIG_ALLOW_CROSS=1 60 + '' else ""} 61 + 62 + cargo build --release --target ${target} 63 + ''; 64 + 65 + installPhase = '' 66 + mkdir -p $out/bin 67 + cp target/${target}/release/grain${if target == "x86_64-pc-windows-gnu" then ".exe" else ""} $out/bin/ 68 + ''; 69 + }; 70 + 71 + in { 72 + packages = { 73 + default = buildGrainCLI system; 74 + 75 + # Cross-compilation targets 76 + grain-linux-x86_64 = buildGrainCLI "x86_64-unknown-linux-gnu"; 77 + grain-linux-aarch64 = buildGrainCLI "aarch64-unknown-linux-gnu"; 78 + grain-macos-x86_64 = buildGrainCLI "x86_64-apple-darwin"; 79 + grain-macos-aarch64 = buildGrainCLI "aarch64-apple-darwin"; 80 + grain-windows-x86_64 = buildGrainCLI "x86_64-pc-windows-gnu"; 81 + }; 82 + 83 + devShells.default = pkgs.mkShell { 84 + inputsFrom = [ self.packages.${system}.default ]; 85 + packages = with pkgs; [ 86 + rustToolchain 87 + pkg-config 88 + openssl 89 + cargo-cross 90 + ]; 91 + }; 92 + 93 + # Helper script to build all targets 94 + apps.build-all = flake-utils.lib.mkApp { 95 + drv = pkgs.writeShellScriptBin "build-all" '' 96 + set -e 97 + 98 + echo "Building Grain CLI for all platforms..." 99 + 100 + mkdir -p releases 101 + 102 + # Build for each platform 103 + echo "Building for Linux x86_64..." 104 + nix build .#grain-linux-x86_64 -o result-linux-x86_64 105 + cp result-linux-x86_64/bin/grain releases/grain-linux-x86_64 106 + 107 + echo "Building for Linux aarch64..." 108 + nix build .#grain-linux-aarch64 -o result-linux-aarch64 109 + cp result-linux-aarch64/bin/grain releases/grain-linux-aarch64 110 + 111 + echo "Building for macOS x86_64..." 112 + nix build .#grain-macos-x86_64 -o result-macos-x86_64 113 + cp result-macos-x86_64/bin/grain releases/grain-darwin-x86_64 114 + 115 + echo "Building for macOS aarch64..." 116 + nix build .#grain-macos-aarch64 -o result-macos-aarch64 117 + cp result-macos-aarch64/bin/grain releases/grain-darwin-aarch64 118 + 119 + echo "Building for Windows x86_64..." 120 + nix build .#grain-windows-x86_64 -o result-windows-x86_64 121 + cp result-windows-x86_64/bin/grain.exe releases/grain-windows-x86_64.exe 122 + 123 + echo "All builds complete! Binaries are in releases/" 124 + ls -la releases/ 125 + ''; 126 + }; 127 + }); 128 + }
+48
cli/install.sh
··· 1 + #!/bin/bash 2 + 3 + set -e 4 + 5 + echo "🌾 Installing Grain CLI..." 6 + 7 + # Detect OS and architecture 8 + OS=$(uname -s | tr '[:upper:]' '[:lower:]') 9 + ARCH=$(uname -m) 10 + 11 + # Map architecture names 12 + case $ARCH in 13 + x86_64) 14 + ARCH="x86_64" 15 + ;; 16 + arm64|aarch64) 17 + ARCH="aarch64" 18 + ;; 19 + *) 20 + echo "❌ Unsupported architecture: $ARCH" 21 + exit 1 22 + ;; 23 + esac 24 + 25 + # Set GitHub repository (replace with your actual repo) 26 + REPO="grainsocial/grain" 27 + BINARY_NAME="grain-${OS}-${ARCH}" 28 + 29 + echo "📦 Downloading Grain CLI for ${OS}-${ARCH}..." 30 + 31 + # Download pre-built binary 32 + if ! curl -L "https://github.com/${REPO}/releases/latest/download/${BINARY_NAME}" -o grain; then 33 + echo "❌ Pre-built binary not available for ${OS}-${ARCH}" 34 + echo "Please build from source instead:" 35 + echo " cargo build --release" 36 + exit 1 37 + fi 38 + 39 + # Make executable 40 + chmod +x grain 41 + 42 + # Install to system 43 + echo "🔗 Installing to /usr/local/bin (requires sudo)..." 44 + sudo mv grain /usr/local/bin/ 45 + 46 + echo "✅ Grain CLI installed successfully!" 47 + echo "📖 Run 'grain --help' to get started" 48 + echo "🔐 Run 'grain login' to authenticate"
+763
cli/src/main.rs
··· 1 + use anyhow::{Context, Result}; 2 + use clap::{Parser, Subcommand}; 3 + use dialoguer::{Confirm, Input}; 4 + use reqwest::{header::HeaderMap, Client, Method}; 5 + use serde::{Deserialize, Serialize}; 6 + use serde_json::Value; 7 + use std::collections::HashMap; 8 + use std::sync::{Arc, Mutex}; 9 + use std::fs; 10 + use std::path::Path; 11 + use std::process; 12 + use std::time::Duration; 13 + use hyper::server::conn::http1; 14 + use hyper::service::service_fn; 15 + use hyper::{body::Incoming as IncomingBody, Request, Response, StatusCode}; 16 + use hyper_util::rt::TokioIo; 17 + use http_body_util::Full; 18 + use tokio::net::TcpListener; 19 + use indicatif::{ProgressBar, ProgressStyle}; 20 + use dirs; 21 + 22 + mod photo_manip; 23 + use photo_manip::{do_resize, ResizeOptions}; 24 + 25 + const API_BASE: &str = "http://localhost:8080"; 26 + const OAUTH_PORT: u16 = 8787; 27 + const OAUTH_PATH: &str = "/callback"; 28 + const OAUTH_TIMEOUT: Duration = Duration::from_secs(300); // 5 minutes 29 + 30 + #[derive(Parser)] 31 + #[command(name = "grain")] 32 + #[command(about = "A CLI for grain.social")] 33 + struct Cli { 34 + #[command(subcommand)] 35 + command: Option<Commands>, 36 + 37 + #[arg(short, long, global = true, help = "Enable verbose output")] 38 + verbose: bool, 39 + } 40 + 41 + #[derive(Subcommand)] 42 + enum Commands { 43 + #[command(about = "Authenticate with grain.social")] 44 + Login, 45 + #[command(about = "Manage your galleries")] 46 + Gallery { 47 + #[command(subcommand)] 48 + action: GalleryAction, 49 + }, 50 + } 51 + 52 + #[derive(Subcommand)] 53 + enum GalleryAction { 54 + #[command(about = "List your galleries")] 55 + List, 56 + #[command(about = "Create a new gallery from a folder of images")] 57 + Create, 58 + #[command(about = "Delete a gallery")] 59 + Delete, 60 + #[command(about = "Open a gallery in the browser")] 61 + Show, 62 + } 63 + 64 + #[derive(Serialize, Deserialize)] 65 + struct AuthData { 66 + did: String, 67 + token: String, 68 + #[serde(rename = "expiresAt")] 69 + expires_at: Option<String>, 70 + } 71 + 72 + #[derive(Deserialize)] 73 + struct GalleryItem { 74 + title: Option<String>, 75 + items: Option<Vec<Value>>, 76 + uri: String, 77 + } 78 + 79 + #[derive(Debug, Deserialize)] 80 + struct LoginResponse { 81 + url: Option<String>, 82 + } 83 + 84 + #[derive(Deserialize)] 85 + struct GalleriesResponse { 86 + items: Option<Vec<GalleryItem>>, 87 + } 88 + 89 + #[derive(Debug, Deserialize)] 90 + struct CreateGalleryResponse { 91 + #[serde(rename = "galleryUri")] 92 + gallery_uri: String, 93 + } 94 + 95 + #[derive(Deserialize)] 96 + struct UploadPhotoResponse { 97 + #[serde(rename = "photoUri")] 98 + photo_uri: String, 99 + } 100 + 101 + fn exit_with_error(message: &str, code: i32) -> ! { 102 + eprintln!("{}", message); 103 + process::exit(code); 104 + } 105 + 106 + async fn make_request<T>( 107 + client: &Client, 108 + url: &str, 109 + method: Method, 110 + body: Option<Vec<u8>>, 111 + token: Option<&str>, 112 + content_type: Option<&str>, 113 + ) -> Result<T> 114 + where 115 + T: for<'de> Deserialize<'de>, 116 + { 117 + let mut headers = HeaderMap::new(); 118 + headers.insert("Accept", "application/json".parse()?); 119 + 120 + if let Some(token) = token { 121 + headers.insert("Authorization", format!("Bearer {}", token).parse()?); 122 + } 123 + 124 + if let Some(ct) = content_type { 125 + headers.insert("Content-Type", ct.parse()?); 126 + } 127 + 128 + let mut request = client.request(method, url).headers(headers); 129 + 130 + if let Some(body) = body { 131 + request = request.body(body); 132 + } 133 + 134 + let response = request.send().await?; 135 + 136 + let status = response.status(); 137 + if !status.is_success() { 138 + let text = response.text().await?; 139 + return Err(anyhow::anyhow!("HTTP {}: {}", status, text)); 140 + } 141 + 142 + let content_type = response.headers() 143 + .get("content-type") 144 + .and_then(|v| v.to_str().ok()) 145 + .unwrap_or(""); 146 + 147 + if !content_type.contains("application/json") { 148 + return Err(anyhow::anyhow!("Expected JSON response")); 149 + } 150 + 151 + let data: T = response.json().await?; 152 + Ok(data) 153 + } 154 + 155 + fn get_auth_file_path() -> Result<std::path::PathBuf> { 156 + let config_dir = dirs::config_dir() 157 + .ok_or_else(|| anyhow::anyhow!("Unable to determine config directory"))?; 158 + let grain_config_dir = config_dir.join("grain"); 159 + fs::create_dir_all(&grain_config_dir)?; 160 + Ok(grain_config_dir.join("auth.json")) 161 + } 162 + 163 + async fn load_auth() -> Result<AuthData> { 164 + let auth_file = get_auth_file_path()?; 165 + let auth_text = fs::read_to_string(&auth_file) 166 + .with_context(|| "Please run 'login' first")?; 167 + 168 + let auth: AuthData = serde_json::from_str(&auth_text) 169 + .with_context(|| "Invalid auth file format")?; 170 + 171 + if auth.did.is_empty() || auth.token.is_empty() { 172 + exit_with_error("Please re-authenticate.", 1); 173 + } 174 + 175 + if let Some(expires_at) = &auth.expires_at { 176 + if let Ok(expires) = chrono::DateTime::parse_from_rfc3339(expires_at) { 177 + if chrono::Utc::now() >= expires { 178 + exit_with_error("Authentication expired. Please re-authenticate.", 1); 179 + } 180 + } 181 + } 182 + 183 + Ok(auth) 184 + } 185 + 186 + 187 + async fn handle_login(client: &Client, verbose: bool) -> Result<()> { 188 + let handle: String = Input::new() 189 + .with_prompt("Enter your handle") 190 + .default("ansel.grain.social".to_string()) 191 + .interact()?; 192 + 193 + let login_url = format!("{}/oauth/login?handle={}&client=cli", API_BASE, urlencoding::encode(&handle)); 194 + 195 + let data: LoginResponse = make_request( 196 + client, 197 + &login_url, 198 + Method::POST, 199 + None, 200 + None, 201 + None, 202 + ).await?; 203 + 204 + if verbose { 205 + println!("Login response: {:?}", data); 206 + } 207 + 208 + if let Some(url) = data.url { 209 + let result = Arc::new(Mutex::new(None::<HashMap<String, String>>)); 210 + let (tx, mut rx) = tokio::sync::oneshot::channel::<()>(); 211 + let tx = Arc::new(Mutex::new(Some(tx))); 212 + 213 + if verbose { 214 + println!("Waiting for OAuth redirect on http://localhost:{}{}...", OAUTH_PORT, OAUTH_PATH); 215 + } 216 + 217 + // Open browser 218 + open::that(&url)?; 219 + if verbose { 220 + println!("Opened browser for: {}", url); 221 + } 222 + 223 + // Start OAuth server with timeout 224 + let listener = TcpListener::bind((std::net::Ipv4Addr::new(127, 0, 0, 1), OAUTH_PORT)).await?; 225 + 226 + let result_for_task = result.clone(); 227 + let tx_for_task = tx.clone(); 228 + 229 + let server_task = async move { 230 + if verbose { 231 + println!("OAuth server listening on port {}...", OAUTH_PORT); 232 + } 233 + 234 + match listener.accept().await { 235 + Ok((stream, addr)) => { 236 + if verbose { 237 + println!("Received connection from: {}", addr); 238 + } 239 + 240 + let io = TokioIo::new(stream); 241 + let result_clone = result_for_task.clone(); 242 + let tx_clone = tx_for_task.clone(); 243 + 244 + let service = service_fn(move |req: Request<IncomingBody>| { 245 + let result = result_clone.clone(); 246 + let tx = tx_clone.clone(); 247 + async move { 248 + let uri = req.uri(); 249 + if verbose { 250 + println!("Received request: {} {}", req.method(), uri); 251 + } 252 + 253 + if uri.path() == OAUTH_PATH { 254 + if verbose { 255 + println!("Matched OAuth callback path: {}", OAUTH_PATH); 256 + } 257 + 258 + if let Some(query) = uri.query() { 259 + if verbose { 260 + println!("Query string: {}", query); 261 + } 262 + 263 + let params: HashMap<String, String> = url::form_urlencoded::parse(query.as_bytes()) 264 + .into_owned() 265 + .collect(); 266 + 267 + if verbose { 268 + println!("Parsed parameters: {:?}", params); 269 + } 270 + 271 + if let Ok(mut r) = result.lock() { 272 + *r = Some(params); 273 + if verbose { 274 + println!("Successfully stored OAuth parameters"); 275 + } 276 + 277 + // Signal that we have the parameters 278 + if let Ok(mut tx_guard) = tx.lock() { 279 + if let Some(sender) = tx_guard.take() { 280 + let _ = sender.send(()); 281 + if verbose { 282 + println!("Sent completion signal"); 283 + } 284 + } 285 + } 286 + } else if verbose { 287 + println!("Failed to lock result mutex"); 288 + } 289 + } else if verbose { 290 + println!("No query string found in OAuth callback"); 291 + } 292 + 293 + let html = r#" 294 + <!DOCTYPE html> 295 + <html lang="en"> 296 + <head> 297 + <meta charset="UTF-8" /> 298 + <title>Authentication Complete</title> 299 + <style> 300 + body { font-family: sans-serif; text-align: center; margin-top: 10vh; } 301 + .box { display: inline-block; padding: 2em; border: 1px solid #ccc; border-radius: 8px; background: #fafafa; } 302 + </style> 303 + </head> 304 + <body> 305 + <div class="box"> 306 + <h1>✅ Authentication Complete</h1> 307 + <p>You may now return to your terminal.</p> 308 + <p>You can close this tab.</p> 309 + </div> 310 + </body> 311 + </html> 312 + "#; 313 + 314 + Ok::<_, anyhow::Error>(Response::builder() 315 + .status(StatusCode::OK) 316 + .header("content-type", "text/html; charset=utf-8") 317 + .body(Full::new(hyper::body::Bytes::from(html)))?) 318 + } else { 319 + if verbose { 320 + println!("Path '{}' does not match OAuth callback path '{}'", uri.path(), OAUTH_PATH); 321 + } 322 + 323 + Ok(Response::builder() 324 + .status(StatusCode::NOT_FOUND) 325 + .body(Full::new(hyper::body::Bytes::from("Not found")))?) 326 + } 327 + } 328 + }); 329 + 330 + // Handle the connection with timeout 331 + if verbose { 332 + println!("Serving HTTP connection..."); 333 + } 334 + 335 + let connection = http1::Builder::new() 336 + .serve_connection(io, service); 337 + 338 + match tokio::time::timeout(Duration::from_secs(10), connection).await { 339 + Ok(Ok(_)) => { 340 + if verbose { 341 + println!("Connection served successfully"); 342 + } 343 + } 344 + Ok(Err(e)) => { 345 + if verbose { 346 + println!("Connection error: {}", e); 347 + } 348 + } 349 + Err(_) => { 350 + if verbose { 351 + println!("Connection handling timed out"); 352 + } 353 + } 354 + } 355 + } 356 + Err(e) => { 357 + if verbose { 358 + println!("Failed to accept connection: {}", e); 359 + } 360 + } 361 + } 362 + 363 + if verbose { 364 + println!("Server task ending"); 365 + } 366 + }; 367 + 368 + // Start the server task in the background 369 + tokio::spawn(server_task); 370 + 371 + // Wait for either the OAuth callback or timeout 372 + tokio::select! { 373 + _ = &mut rx => { 374 + if verbose { 375 + println!("OAuth callback received, proceeding..."); 376 + } 377 + }, 378 + _ = tokio::time::sleep(OAUTH_TIMEOUT) => { 379 + eprintln!("Timed out waiting for OAuth redirect."); 380 + } 381 + } 382 + 383 + let params_result = { 384 + let guard = result.lock(); 385 + match guard { 386 + Ok(p) => p.clone(), 387 + Err(_) => None, 388 + } 389 + }; 390 + 391 + if let Some(params) = params_result { 392 + if verbose { 393 + println!("Received redirect with params: {:?}", params); 394 + } 395 + save_auth_params(&params).await?; 396 + println!("Login successful! You can now use other commands."); 397 + } else { 398 + eprintln!("No redirect received."); 399 + } 400 + } 401 + 402 + Ok(()) 403 + } 404 + 405 + async fn save_auth_params(params: &HashMap<String, String>) -> Result<()> { 406 + let auth_file = get_auth_file_path()?; 407 + let json = serde_json::to_string_pretty(params)?; 408 + fs::write(&auth_file, json)?; 409 + println!("Saved config data to {}", auth_file.display()); 410 + Ok(()) 411 + } 412 + 413 + async fn fetch_galleries(client: &Client) -> Result<Vec<GalleryItem>> { 414 + let auth = load_auth().await?; 415 + let galleries_url = format!( 416 + "{}/xrpc/social.grain.gallery.getActorGalleries?actor={}", 417 + API_BASE, 418 + urlencoding::encode(&auth.did) 419 + ); 420 + 421 + let data: GalleriesResponse = make_request( 422 + client, 423 + &galleries_url, 424 + Method::GET, 425 + None, 426 + Some(&auth.token), 427 + None, 428 + ).await?; 429 + 430 + Ok(data.items.unwrap_or_default()) 431 + } 432 + 433 + async fn handle_galleries_list(client: &Client) -> Result<()> { 434 + let items = fetch_galleries(client).await?; 435 + 436 + if items.is_empty() { 437 + println!("No galleries found."); 438 + } else { 439 + for item in items { 440 + let count = item.items.as_ref().map(|i| i.len()).unwrap_or(0); 441 + let title = item.title.as_deref().unwrap_or("Untitled"); 442 + println!("{} ({})", title, count); 443 + } 444 + } 445 + 446 + Ok(()) 447 + } 448 + 449 + async fn delete_gallery(client: &Client, gallery_uri: &str) -> Result<()> { 450 + let auth = load_auth().await?; 451 + let delete_url = format!("{}/xrpc/social.grain.gallery.deleteGallery", API_BASE); 452 + 453 + let payload = serde_json::json!({ 454 + "uri": gallery_uri 455 + }); 456 + 457 + let _response: Value = make_request( 458 + client, 459 + &delete_url, 460 + Method::POST, 461 + Some(payload.to_string().into_bytes()), 462 + Some(&auth.token), 463 + Some("application/json"), 464 + ).await?; 465 + 466 + Ok(()) 467 + } 468 + 469 + async fn handle_gallery_delete(client: &Client) -> Result<()> { 470 + let galleries = fetch_galleries(client).await?; 471 + 472 + if galleries.is_empty() { 473 + println!("No galleries found to delete."); 474 + return Ok(()); 475 + } 476 + 477 + // Create selection options for dialoguer 478 + let gallery_options: Vec<String> = galleries 479 + .iter() 480 + .map(|item| { 481 + let count = item.items.as_ref().map(|i| i.len()).unwrap_or(0); 482 + let title = item.title.as_deref().unwrap_or("Untitled"); 483 + format!("{} ({} items)", title, count) 484 + }) 485 + .collect(); 486 + 487 + let selection = dialoguer::Select::new() 488 + .with_prompt("Select a gallery to delete") 489 + .items(&gallery_options) 490 + .default(0) 491 + .interact()?; 492 + 493 + let selected_gallery = &galleries[selection]; 494 + let title = selected_gallery.title.as_deref().unwrap_or("Untitled"); 495 + 496 + let confirm = dialoguer::Confirm::new() 497 + .with_prompt(format!("Are you sure you want to delete gallery '{}'? This action cannot be undone.", title)) 498 + .default(false) 499 + .interact()?; 500 + 501 + if confirm { 502 + delete_gallery(client, &selected_gallery.uri).await?; 503 + println!("Gallery '{}' deleted successfully.", title); 504 + } else { 505 + println!("Gallery deletion cancelled."); 506 + } 507 + 508 + Ok(()) 509 + } 510 + 511 + async fn handle_gallery_show(client: &Client) -> Result<()> { 512 + let galleries = fetch_galleries(client).await?; 513 + 514 + if galleries.is_empty() { 515 + println!("No galleries found to show."); 516 + return Ok(()); 517 + } 518 + 519 + // Create selection options for dialoguer 520 + let gallery_options: Vec<String> = galleries 521 + .iter() 522 + .map(|item| { 523 + let count = item.items.as_ref().map(|i| i.len()).unwrap_or(0); 524 + let title = item.title.as_deref().unwrap_or("Untitled"); 525 + format!("{} ({} items)", title, count) 526 + }) 527 + .collect(); 528 + 529 + let selection = dialoguer::Select::new() 530 + .with_prompt("Select a gallery to open") 531 + .items(&gallery_options) 532 + .default(0) 533 + .interact()?; 534 + 535 + let selected_gallery = &galleries[selection]; 536 + let web_url = selected_gallery.uri.strip_prefix("at://").unwrap_or(&selected_gallery.uri); 537 + let formatted_url = format!("https://grain.social/{}", web_url); 538 + 539 + println!("Opening gallery in browser: {}", formatted_url); 540 + open::that(&formatted_url)?; 541 + 542 + Ok(()) 543 + } 544 + 545 + async fn create_gallery(client: &Client, title: &str, description: &str) -> Result<String> { 546 + let auth = load_auth().await?; 547 + let create_url = format!("{}/xrpc/social.grain.gallery.createGallery", API_BASE); 548 + 549 + let payload = serde_json::json!({ 550 + "title": title, 551 + "description": description 552 + }); 553 + 554 + let response: CreateGalleryResponse = make_request( 555 + client, 556 + &create_url, 557 + Method::POST, 558 + Some(payload.to_string().into_bytes()), 559 + Some(&auth.token), 560 + Some("application/json"), 561 + ).await?; 562 + 563 + Ok(response.gallery_uri) 564 + } 565 + 566 + async fn upload_photo(client: &Client, image_buffer: &[u8]) -> Result<String> { 567 + let auth = load_auth().await?; 568 + let upload_url = format!("{}/xrpc/social.grain.photo.uploadPhoto", API_BASE); 569 + 570 + let response: UploadPhotoResponse = make_request( 571 + client, 572 + &upload_url, 573 + Method::POST, 574 + Some(image_buffer.to_vec()), 575 + Some(&auth.token), 576 + Some("image/jpeg"), 577 + ).await?; 578 + 579 + println!("Photo uploaded successfully: {}", response.photo_uri); 580 + Ok(response.photo_uri) 581 + } 582 + 583 + async fn create_gallery_item( 584 + client: &Client, 585 + gallery_uri: &str, 586 + photo_uri: &str, 587 + position: u32, 588 + ) -> Result<()> { 589 + let auth = load_auth().await?; 590 + let create_url = format!("{}/xrpc/social.grain.gallery.createItem", API_BASE); 591 + 592 + let payload = serde_json::json!({ 593 + "galleryUri": gallery_uri, 594 + "photoUri": photo_uri, 595 + "position": position 596 + }); 597 + 598 + let _response: Value = make_request( 599 + client, 600 + &create_url, 601 + Method::POST, 602 + Some(payload.to_string().into_bytes()), 603 + Some(&auth.token), 604 + Some("application/json"), 605 + ).await?; 606 + 607 + Ok(()) 608 + } 609 + 610 + async fn handle_gallery_create(client: &Client, verbose: bool) -> Result<()> { 611 + let title: String = Input::new() 612 + .with_prompt("Gallery title") 613 + .interact()?; 614 + 615 + let description: String = Input::new() 616 + .with_prompt("Gallery description (optional)") 617 + .allow_empty(true) 618 + .interact()?; 619 + 620 + let folder_path: String = Input::new() 621 + .with_prompt("Path to folder of image files to upload") 622 + .validate_with(|input: &String| -> Result<(), &str> { 623 + let path = Path::new(input); 624 + if !path.exists() { 625 + return Err("Directory does not exist"); 626 + } 627 + if !path.is_dir() { 628 + return Err("Path is not a directory"); 629 + } 630 + Ok(()) 631 + }) 632 + .interact()?; 633 + 634 + // List image files in the folder 635 + let image_extensions = [".jpg", ".jpeg"]; 636 + let mut image_files = Vec::new(); 637 + 638 + let entries = fs::read_dir(&folder_path)?; 639 + for entry in entries { 640 + let entry = entry?; 641 + if entry.file_type()?.is_file() { 642 + let file_name = entry.file_name(); 643 + let file_name_str = file_name.to_string_lossy().to_lowercase(); 644 + if image_extensions.iter().any(|ext| file_name_str.ends_with(ext)) { 645 + image_files.push(entry.file_name().to_string_lossy().to_string()); 646 + } 647 + } 648 + } 649 + 650 + if image_files.is_empty() { 651 + exit_with_error("No image files found in the selected folder.", 1); 652 + } 653 + 654 + println!("Found {} image files in '{}':", image_files.len(), folder_path); 655 + for file in &image_files { 656 + println!(" - {}", file); 657 + } 658 + 659 + let confirm = Confirm::new() 660 + .with_prompt(format!("Are you sure you want to upload these {} images?", image_files.len())) 661 + .default(true) 662 + .interact()?; 663 + 664 + if !confirm { 665 + println!("Aborted by user."); 666 + return Ok(()); 667 + } 668 + 669 + let gallery_uri = create_gallery(client, &title, &description).await?; 670 + 671 + let pb = ProgressBar::new(image_files.len() as u64); 672 + pb.set_style( 673 + ProgressStyle::default_bar() 674 + .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} {msg}") 675 + .unwrap() 676 + .progress_chars("#>-") 677 + ); 678 + 679 + let mut position = 0; 680 + 681 + for file_name in image_files { 682 + pb.set_message(format!("Processing {}", file_name)); 683 + 684 + let file_path = format!("{}/{}", folder_path, file_name); 685 + let file_data = fs::read(&file_path)?; 686 + 687 + let resized = do_resize(&file_data, ResizeOptions { 688 + width: 2000, 689 + height: 2000, 690 + max_size: 1000 * 1000, // 1MB 691 + mode: "inside".to_string(), 692 + verbose, 693 + })?; 694 + 695 + let photo_uri = upload_photo(client, &resized.buffer).await?; 696 + 697 + create_gallery_item(client, &gallery_uri, &photo_uri, position).await?; 698 + position += 1; 699 + pb.inc(1); 700 + } 701 + 702 + pb.finish_with_message("All images uploaded successfully!"); 703 + 704 + let web_url = gallery_uri.strip_prefix("at://").unwrap_or(&gallery_uri); 705 + let formatted_url = format!("https://grain.social/{}", web_url); 706 + println!("Here's a link to the gallery: {}", formatted_url); 707 + 708 + Ok(()) 709 + } 710 + 711 + #[tokio::main] 712 + async fn main() -> Result<()> { 713 + let cli = Cli::parse(); 714 + let client = Client::new(); 715 + 716 + match cli.command { 717 + None => { 718 + println!(r#" 719 + ██████╗ ██████╗ █████╗ ██╗███╗ ██╗ 720 + ██╔════╝ ██╔══██╗██╔══██╗██║████╗ ██║ 721 + ██║ ███╗██████╔╝███████║██║██╔██╗ ██║ 722 + ██║ ██║██╔══██╗██╔══██║██║██║╚██╗██║ 723 + ╚██████╔╝██║ ██║██║ ██║██║██║ ╚████║ 724 + ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ 725 + 726 + https://grain.social CLI 727 + 728 + Use --help to see available commands. 729 + "#); 730 + } 731 + Some(Commands::Login) => { 732 + if let Err(e) = handle_login(&client, cli.verbose).await { 733 + eprintln!("Login failed: {}", e); 734 + } 735 + } 736 + Some(Commands::Gallery { action }) => { 737 + match action { 738 + GalleryAction::List => { 739 + if let Err(e) = handle_galleries_list(&client).await { 740 + exit_with_error(&format!("Failed to fetch galleries: {}", e), 1); 741 + } 742 + } 743 + GalleryAction::Create => { 744 + if let Err(e) = handle_gallery_create(&client, cli.verbose).await { 745 + exit_with_error(&format!("Failed to create gallery: {}", e), 1); 746 + } 747 + } 748 + GalleryAction::Delete => { 749 + if let Err(e) = handle_gallery_delete(&client).await { 750 + exit_with_error(&format!("Failed to delete gallery: {}", e), 1); 751 + } 752 + } 753 + GalleryAction::Show => { 754 + if let Err(e) = handle_gallery_show(&client).await { 755 + exit_with_error(&format!("Failed to show gallery: {}", e), 1); 756 + } 757 + } 758 + } 759 + } 760 + } 761 + 762 + Ok(()) 763 + }
+129
cli/src/photo_manip.rs
··· 1 + use anyhow::Result; 2 + use image::codecs::jpeg::JpegEncoder; 3 + use image::ExtendedColorType; 4 + use std::io::Cursor; 5 + use std::time::Instant; 6 + 7 + pub struct ResizeOptions { 8 + pub width: u32, 9 + pub height: u32, 10 + pub max_size: usize, // in bytes 11 + pub mode: String, 12 + pub verbose: bool, 13 + } 14 + 15 + pub struct ResizeResult { 16 + pub buffer: Vec<u8>, 17 + } 18 + 19 + pub fn do_resize(input: &[u8], opts: ResizeOptions) -> Result<ResizeResult> { 20 + let start_time = Instant::now(); 21 + if opts.verbose { 22 + eprintln!("Starting image resize - input size: {} bytes, target: {}x{}, max_size: {} bytes", 23 + input.len(), opts.width, opts.height, opts.max_size); 24 + } 25 + 26 + let img_load_start = Instant::now(); 27 + let img = image::load_from_memory(input)?; 28 + if opts.verbose { 29 + eprintln!("Image loaded in {:?} - dimensions: {}x{}", img_load_start.elapsed(), img.width(), img.height()); 30 + } 31 + 32 + let resize_start = Instant::now(); 33 + let resized = match opts.mode.as_str() { 34 + "inside" => img.resize(opts.width, opts.height, image::imageops::FilterType::Triangle), 35 + "cover" => img.resize_to_fill(opts.width, opts.height, image::imageops::FilterType::Triangle), 36 + _ => img.resize(opts.width, opts.height, image::imageops::FilterType::Triangle), 37 + }; 38 + if opts.verbose { 39 + eprintln!("Image resized in {:?} - new dimensions: {}x{}", resize_start.elapsed(), resized.width(), resized.height()); 40 + } 41 + 42 + // Binary search for the best quality that fits under max_size 43 + let mut best_result: Option<ResizeResult> = None; 44 + let mut min_quality = 1u8; 45 + let mut max_quality = 100u8; 46 + let mut iteration = 0; 47 + 48 + let search_start = Instant::now(); 49 + if opts.verbose { 50 + eprintln!("Starting binary search for optimal quality"); 51 + } 52 + 53 + while max_quality > min_quality + 1 { 54 + iteration += 1; 55 + let quality = (min_quality + max_quality) / 2; 56 + let encode_start = Instant::now(); 57 + 58 + let mut buffer = Vec::new(); 59 + { 60 + let mut cursor = Cursor::new(&mut buffer); 61 + let mut encoder = JpegEncoder::new_with_quality(&mut cursor, quality); 62 + encoder.encode( 63 + resized.as_bytes(), 64 + resized.width(), 65 + resized.height(), 66 + ExtendedColorType::Rgb8, 67 + )?; 68 + } 69 + 70 + let size = buffer.len(); 71 + if opts.verbose { 72 + eprintln!("Iteration {}: quality={}, size={} bytes, encoded in {:?}", 73 + iteration, quality, size, encode_start.elapsed()); 74 + } 75 + 76 + if size <= opts.max_size { 77 + min_quality = quality; 78 + best_result = Some(ResizeResult { 79 + buffer, 80 + }); 81 + if opts.verbose { 82 + eprintln!(" -> Fits! New min_quality: {}", min_quality); 83 + } 84 + } else { 85 + max_quality = quality; 86 + if opts.verbose { 87 + eprintln!(" -> Too large! New max_quality: {}", max_quality); 88 + } 89 + } 90 + } 91 + 92 + if opts.verbose { 93 + eprintln!("Binary search completed in {:?} after {} iterations", search_start.elapsed(), iteration); 94 + } 95 + 96 + // Try with minimum quality if no result yet 97 + if best_result.is_none() { 98 + if opts.verbose { 99 + eprintln!("No result found, trying minimum quality: {}", min_quality); 100 + } 101 + let final_encode_start = Instant::now(); 102 + 103 + let mut buffer = Vec::new(); 104 + { 105 + let mut cursor = Cursor::new(&mut buffer); 106 + let mut encoder = JpegEncoder::new_with_quality(&mut cursor, min_quality); 107 + encoder.encode( 108 + resized.as_bytes(), 109 + resized.width(), 110 + resized.height(), 111 + ExtendedColorType::Rgb8, 112 + )?; 113 + } 114 + 115 + if opts.verbose { 116 + eprintln!("Final encode with quality {} completed in {:?}, size: {} bytes", 117 + min_quality, final_encode_start.elapsed(), buffer.len()); 118 + } 119 + 120 + best_result = Some(ResizeResult { 121 + buffer, 122 + }); 123 + } 124 + 125 + if opts.verbose { 126 + eprintln!("Total resize operation completed in {:?}", start_time.elapsed()); 127 + } 128 + best_result.ok_or_else(|| anyhow::anyhow!("Failed to compress image")) 129 + }
cli/test_photos/R0090101.JPG

This is a binary file and will not be displayed.

cli/test_photos/R0090102.JPG

This is a binary file and will not be displayed.

cli/test_photos/R0090104.JPG

This is a binary file and will not be displayed.