├── .github └── workflows │ └── build.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── README.md ├── _typos.toml ├── cli-utils ├── Cargo.toml └── src │ └── lib.rs ├── deny.toml ├── docs └── images │ ├── screenshot1.png │ ├── screenshot2.png │ └── screenshot3.png ├── requester ├── Cargo.toml ├── fixtures │ ├── diff.yml │ └── req.yml └── src │ ├── diff.rs │ ├── lib.rs │ └── req.rs ├── xdiff ├── Cargo.toml └── src │ └── main.rs └── xreq ├── Cargo.toml └── src └── main.rs /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build-rust: 13 | strategy: 14 | matrix: 15 | platform: [ubuntu-latest] 16 | runs-on: ${{ matrix.platform }} 17 | steps: 18 | - uses: actions/checkout@v3 19 | with: 20 | fetch-depth: 0 21 | # - name: Install stable 22 | # uses: actions-rs/toolchain@v1 23 | # with: 24 | # profile: minimal 25 | # toolchain: stable 26 | # override: true 27 | - name: Install Rust 28 | run: rustup toolchain install stable --component llvm-tools-preview 29 | - name: Install cargo-llvm-cov 30 | uses: taiki-e/install-action@cargo-llvm-cov 31 | - name: install nextest 32 | uses: taiki-e/install-action@nextest 33 | - uses: Swatinem/rust-cache@v1 34 | - name: Check code format 35 | run: cargo fmt -- --check 36 | - name: Check the package for errors 37 | run: cargo check --all 38 | - name: Lint rust sources 39 | run: cargo clippy --all-targets --all-features --tests --benches -- -D warnings 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | fail_fast: false 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v2.3.0 5 | hooks: 6 | - id: check-byte-order-marker 7 | - id: check-case-conflict 8 | - id: check-merge-conflict 9 | - id: check-symlinks 10 | - id: check-yaml 11 | - id: end-of-file-fixer 12 | - id: mixed-line-ending 13 | - id: trailing-whitespace 14 | - repo: https://github.com/psf/black 15 | rev: 19.3b0 16 | hooks: 17 | - id: black 18 | - repo: https://github.com/crate-ci/typos 19 | rev: v1.8.1 20 | hooks: 21 | - id: typos 22 | - repo: local 23 | hooks: 24 | - id: cargo-fmt 25 | name: cargo fmt 26 | description: Format files with rustfmt. 27 | entry: bash -c 'cargo fmt -- --check' 28 | language: rust 29 | files: \.rs$ 30 | args: [] 31 | - id: cargo-deny 32 | name: cargo deny check 33 | description: Check cargo dependencies 34 | entry: bash -c 'cargo deny check' 35 | language: rust 36 | files: \.rs$ 37 | args: [] 38 | - id: cargo-check 39 | name: cargo check 40 | description: Check the package for errors. 41 | entry: bash -c 'cargo check --all' 42 | language: rust 43 | files: \.rs$ 44 | pass_filenames: false 45 | - id: cargo-clippy 46 | name: cargo clippy 47 | description: Lint rust sources 48 | entry: bash -c 'cargo clippy --all-targets --all-features --tests --benches -- -D warnings' 49 | language: rust 50 | files: \.rs$ 51 | pass_filenames: false 52 | - id: cargo-test 53 | name: cargo test 54 | description: unit test for the project 55 | entry: bash -c 'cargo nextest run --all-features' 56 | language: rust 57 | files: \.rs$ 58 | pass_filenames: false 59 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabCompletion": "on", 3 | "diffEditor.codeLens": true 4 | } 5 | -------------------------------------------------------------------------------- /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.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "alloc-no-stdlib" 22 | version = "2.0.4" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 25 | 26 | [[package]] 27 | name = "alloc-stdlib" 28 | version = "0.2.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 31 | dependencies = [ 32 | "alloc-no-stdlib", 33 | ] 34 | 35 | [[package]] 36 | name = "android-tzdata" 37 | version = "0.1.1" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 40 | 41 | [[package]] 42 | name = "android_system_properties" 43 | version = "0.1.5" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 46 | dependencies = [ 47 | "libc", 48 | ] 49 | 50 | [[package]] 51 | name = "anstream" 52 | version = "0.6.18" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 55 | dependencies = [ 56 | "anstyle", 57 | "anstyle-parse", 58 | "anstyle-query", 59 | "anstyle-wincon", 60 | "colorchoice", 61 | "is_terminal_polyfill", 62 | "utf8parse", 63 | ] 64 | 65 | [[package]] 66 | name = "anstyle" 67 | version = "1.0.10" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 70 | 71 | [[package]] 72 | name = "anstyle-parse" 73 | version = "0.2.6" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 76 | dependencies = [ 77 | "utf8parse", 78 | ] 79 | 80 | [[package]] 81 | name = "anstyle-query" 82 | version = "1.1.2" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 85 | dependencies = [ 86 | "windows-sys 0.59.0", 87 | ] 88 | 89 | [[package]] 90 | name = "anstyle-wincon" 91 | version = "3.0.6" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" 94 | dependencies = [ 95 | "anstyle", 96 | "windows-sys 0.59.0", 97 | ] 98 | 99 | [[package]] 100 | name = "anyhow" 101 | version = "1.0.94" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" 104 | 105 | [[package]] 106 | name = "async-compression" 107 | version = "0.4.18" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" 110 | dependencies = [ 111 | "brotli", 112 | "flate2", 113 | "futures-core", 114 | "memchr", 115 | "pin-project-lite", 116 | "tokio", 117 | ] 118 | 119 | [[package]] 120 | name = "atty" 121 | version = "0.2.14" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 124 | dependencies = [ 125 | "hermit-abi", 126 | "libc", 127 | "winapi", 128 | ] 129 | 130 | [[package]] 131 | name = "autocfg" 132 | version = "1.4.0" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 135 | 136 | [[package]] 137 | name = "backtrace" 138 | version = "0.3.74" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 141 | dependencies = [ 142 | "addr2line", 143 | "cfg-if", 144 | "libc", 145 | "miniz_oxide", 146 | "object", 147 | "rustc-demangle", 148 | "windows-targets 0.52.6", 149 | ] 150 | 151 | [[package]] 152 | name = "base64" 153 | version = "0.22.1" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 156 | 157 | [[package]] 158 | name = "bincode" 159 | version = "1.3.3" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 162 | dependencies = [ 163 | "serde", 164 | ] 165 | 166 | [[package]] 167 | name = "bitflags" 168 | version = "1.3.2" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 171 | 172 | [[package]] 173 | name = "bitflags" 174 | version = "2.6.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 177 | 178 | [[package]] 179 | name = "brotli" 180 | version = "7.0.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" 183 | dependencies = [ 184 | "alloc-no-stdlib", 185 | "alloc-stdlib", 186 | "brotli-decompressor", 187 | ] 188 | 189 | [[package]] 190 | name = "brotli-decompressor" 191 | version = "4.0.1" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" 194 | dependencies = [ 195 | "alloc-no-stdlib", 196 | "alloc-stdlib", 197 | ] 198 | 199 | [[package]] 200 | name = "bstr" 201 | version = "1.11.1" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" 204 | dependencies = [ 205 | "memchr", 206 | ] 207 | 208 | [[package]] 209 | name = "bumpalo" 210 | version = "3.16.0" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 213 | 214 | [[package]] 215 | name = "byteorder" 216 | version = "1.5.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 219 | 220 | [[package]] 221 | name = "bytes" 222 | version = "1.9.0" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" 225 | 226 | [[package]] 227 | name = "cc" 228 | version = "1.2.4" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" 231 | dependencies = [ 232 | "shlex", 233 | ] 234 | 235 | [[package]] 236 | name = "cfg-if" 237 | version = "1.0.0" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 240 | 241 | [[package]] 242 | name = "cfg_aliases" 243 | version = "0.2.1" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 246 | 247 | [[package]] 248 | name = "chrono" 249 | version = "0.4.38" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 252 | dependencies = [ 253 | "android-tzdata", 254 | "iana-time-zone", 255 | "num-traits", 256 | "serde", 257 | "windows-targets 0.52.6", 258 | ] 259 | 260 | [[package]] 261 | name = "clap" 262 | version = "4.5.23" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" 265 | dependencies = [ 266 | "clap_builder", 267 | "clap_derive", 268 | ] 269 | 270 | [[package]] 271 | name = "clap_builder" 272 | version = "4.5.23" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" 275 | dependencies = [ 276 | "anstream", 277 | "anstyle", 278 | "clap_lex", 279 | "strsim", 280 | ] 281 | 282 | [[package]] 283 | name = "clap_derive" 284 | version = "4.5.18" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" 287 | dependencies = [ 288 | "heck", 289 | "proc-macro2", 290 | "quote", 291 | "syn", 292 | ] 293 | 294 | [[package]] 295 | name = "clap_lex" 296 | version = "0.7.4" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 299 | 300 | [[package]] 301 | name = "colorchoice" 302 | version = "1.0.3" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 305 | 306 | [[package]] 307 | name = "colored" 308 | version = "2.1.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 311 | dependencies = [ 312 | "lazy_static", 313 | "windows-sys 0.48.0", 314 | ] 315 | 316 | [[package]] 317 | name = "console" 318 | version = "0.15.8" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" 321 | dependencies = [ 322 | "encode_unicode", 323 | "lazy_static", 324 | "libc", 325 | "unicode-width", 326 | "windows-sys 0.52.0", 327 | ] 328 | 329 | [[package]] 330 | name = "core-foundation-sys" 331 | version = "0.8.7" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 334 | 335 | [[package]] 336 | name = "crc32fast" 337 | version = "1.4.2" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 340 | dependencies = [ 341 | "cfg-if", 342 | ] 343 | 344 | [[package]] 345 | name = "darling" 346 | version = "0.20.10" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" 349 | dependencies = [ 350 | "darling_core", 351 | "darling_macro", 352 | ] 353 | 354 | [[package]] 355 | name = "darling_core" 356 | version = "0.20.10" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" 359 | dependencies = [ 360 | "fnv", 361 | "ident_case", 362 | "proc-macro2", 363 | "quote", 364 | "strsim", 365 | "syn", 366 | ] 367 | 368 | [[package]] 369 | name = "darling_macro" 370 | version = "0.20.10" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" 373 | dependencies = [ 374 | "darling_core", 375 | "quote", 376 | "syn", 377 | ] 378 | 379 | [[package]] 380 | name = "deranged" 381 | version = "0.3.11" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 384 | dependencies = [ 385 | "powerfmt", 386 | "serde", 387 | ] 388 | 389 | [[package]] 390 | name = "dialoguer" 391 | version = "0.11.0" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" 394 | dependencies = [ 395 | "console", 396 | "shell-words", 397 | "tempfile", 398 | "thiserror 1.0.69", 399 | "zeroize", 400 | ] 401 | 402 | [[package]] 403 | name = "displaydoc" 404 | version = "0.2.5" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 407 | dependencies = [ 408 | "proc-macro2", 409 | "quote", 410 | "syn", 411 | ] 412 | 413 | [[package]] 414 | name = "encode_unicode" 415 | version = "0.3.6" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 418 | 419 | [[package]] 420 | name = "equivalent" 421 | version = "1.0.1" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 424 | 425 | [[package]] 426 | name = "errno" 427 | version = "0.3.10" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 430 | dependencies = [ 431 | "libc", 432 | "windows-sys 0.59.0", 433 | ] 434 | 435 | [[package]] 436 | name = "fastrand" 437 | version = "2.2.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" 440 | 441 | [[package]] 442 | name = "flate2" 443 | version = "1.0.35" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" 446 | dependencies = [ 447 | "crc32fast", 448 | "miniz_oxide", 449 | ] 450 | 451 | [[package]] 452 | name = "fnv" 453 | version = "1.0.7" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 456 | 457 | [[package]] 458 | name = "form_urlencoded" 459 | version = "1.2.1" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 462 | dependencies = [ 463 | "percent-encoding", 464 | ] 465 | 466 | [[package]] 467 | name = "futures-channel" 468 | version = "0.3.31" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 471 | dependencies = [ 472 | "futures-core", 473 | ] 474 | 475 | [[package]] 476 | name = "futures-core" 477 | version = "0.3.31" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 480 | 481 | [[package]] 482 | name = "futures-sink" 483 | version = "0.3.31" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 486 | 487 | [[package]] 488 | name = "futures-task" 489 | version = "0.3.31" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 492 | 493 | [[package]] 494 | name = "futures-util" 495 | version = "0.3.31" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 498 | dependencies = [ 499 | "futures-core", 500 | "futures-task", 501 | "pin-project-lite", 502 | "pin-utils", 503 | ] 504 | 505 | [[package]] 506 | name = "getrandom" 507 | version = "0.2.15" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 510 | dependencies = [ 511 | "cfg-if", 512 | "js-sys", 513 | "libc", 514 | "wasi", 515 | "wasm-bindgen", 516 | ] 517 | 518 | [[package]] 519 | name = "gimli" 520 | version = "0.31.1" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 523 | 524 | [[package]] 525 | name = "hashbrown" 526 | version = "0.12.3" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 529 | 530 | [[package]] 531 | name = "hashbrown" 532 | version = "0.15.2" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 535 | 536 | [[package]] 537 | name = "heck" 538 | version = "0.5.0" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 541 | 542 | [[package]] 543 | name = "hermit-abi" 544 | version = "0.1.19" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 547 | dependencies = [ 548 | "libc", 549 | ] 550 | 551 | [[package]] 552 | name = "hex" 553 | version = "0.4.3" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 556 | 557 | [[package]] 558 | name = "http" 559 | version = "1.2.0" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" 562 | dependencies = [ 563 | "bytes", 564 | "fnv", 565 | "itoa", 566 | ] 567 | 568 | [[package]] 569 | name = "http-body" 570 | version = "1.0.1" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 573 | dependencies = [ 574 | "bytes", 575 | "http", 576 | ] 577 | 578 | [[package]] 579 | name = "http-body-util" 580 | version = "0.1.2" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" 583 | dependencies = [ 584 | "bytes", 585 | "futures-util", 586 | "http", 587 | "http-body", 588 | "pin-project-lite", 589 | ] 590 | 591 | [[package]] 592 | name = "http-serde" 593 | version = "2.1.1" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd" 596 | dependencies = [ 597 | "http", 598 | "serde", 599 | ] 600 | 601 | [[package]] 602 | name = "httparse" 603 | version = "1.9.5" 604 | source = "registry+https://github.com/rust-lang/crates.io-index" 605 | checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" 606 | 607 | [[package]] 608 | name = "hyper" 609 | version = "1.5.1" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" 612 | dependencies = [ 613 | "bytes", 614 | "futures-channel", 615 | "futures-util", 616 | "http", 617 | "http-body", 618 | "httparse", 619 | "itoa", 620 | "pin-project-lite", 621 | "smallvec", 622 | "tokio", 623 | "want", 624 | ] 625 | 626 | [[package]] 627 | name = "hyper-rustls" 628 | version = "0.27.3" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" 631 | dependencies = [ 632 | "futures-util", 633 | "http", 634 | "hyper", 635 | "hyper-util", 636 | "rustls", 637 | "rustls-pki-types", 638 | "tokio", 639 | "tokio-rustls", 640 | "tower-service", 641 | "webpki-roots", 642 | ] 643 | 644 | [[package]] 645 | name = "hyper-util" 646 | version = "0.1.10" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" 649 | dependencies = [ 650 | "bytes", 651 | "futures-channel", 652 | "futures-util", 653 | "http", 654 | "http-body", 655 | "hyper", 656 | "pin-project-lite", 657 | "socket2", 658 | "tokio", 659 | "tower-service", 660 | "tracing", 661 | ] 662 | 663 | [[package]] 664 | name = "iana-time-zone" 665 | version = "0.1.61" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 668 | dependencies = [ 669 | "android_system_properties", 670 | "core-foundation-sys", 671 | "iana-time-zone-haiku", 672 | "js-sys", 673 | "wasm-bindgen", 674 | "windows-core", 675 | ] 676 | 677 | [[package]] 678 | name = "iana-time-zone-haiku" 679 | version = "0.1.2" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 682 | dependencies = [ 683 | "cc", 684 | ] 685 | 686 | [[package]] 687 | name = "icu_collections" 688 | version = "1.5.0" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 691 | dependencies = [ 692 | "displaydoc", 693 | "yoke", 694 | "zerofrom", 695 | "zerovec", 696 | ] 697 | 698 | [[package]] 699 | name = "icu_locid" 700 | version = "1.5.0" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 703 | dependencies = [ 704 | "displaydoc", 705 | "litemap", 706 | "tinystr", 707 | "writeable", 708 | "zerovec", 709 | ] 710 | 711 | [[package]] 712 | name = "icu_locid_transform" 713 | version = "1.5.0" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 716 | dependencies = [ 717 | "displaydoc", 718 | "icu_locid", 719 | "icu_locid_transform_data", 720 | "icu_provider", 721 | "tinystr", 722 | "zerovec", 723 | ] 724 | 725 | [[package]] 726 | name = "icu_locid_transform_data" 727 | version = "1.5.0" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 730 | 731 | [[package]] 732 | name = "icu_normalizer" 733 | version = "1.5.0" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 736 | dependencies = [ 737 | "displaydoc", 738 | "icu_collections", 739 | "icu_normalizer_data", 740 | "icu_properties", 741 | "icu_provider", 742 | "smallvec", 743 | "utf16_iter", 744 | "utf8_iter", 745 | "write16", 746 | "zerovec", 747 | ] 748 | 749 | [[package]] 750 | name = "icu_normalizer_data" 751 | version = "1.5.0" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 754 | 755 | [[package]] 756 | name = "icu_properties" 757 | version = "1.5.1" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 760 | dependencies = [ 761 | "displaydoc", 762 | "icu_collections", 763 | "icu_locid_transform", 764 | "icu_properties_data", 765 | "icu_provider", 766 | "tinystr", 767 | "zerovec", 768 | ] 769 | 770 | [[package]] 771 | name = "icu_properties_data" 772 | version = "1.5.0" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 775 | 776 | [[package]] 777 | name = "icu_provider" 778 | version = "1.5.0" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 781 | dependencies = [ 782 | "displaydoc", 783 | "icu_locid", 784 | "icu_provider_macros", 785 | "stable_deref_trait", 786 | "tinystr", 787 | "writeable", 788 | "yoke", 789 | "zerofrom", 790 | "zerovec", 791 | ] 792 | 793 | [[package]] 794 | name = "icu_provider_macros" 795 | version = "1.5.0" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 798 | dependencies = [ 799 | "proc-macro2", 800 | "quote", 801 | "syn", 802 | ] 803 | 804 | [[package]] 805 | name = "ident_case" 806 | version = "1.0.1" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 809 | 810 | [[package]] 811 | name = "idna" 812 | version = "1.0.3" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 815 | dependencies = [ 816 | "idna_adapter", 817 | "smallvec", 818 | "utf8_iter", 819 | ] 820 | 821 | [[package]] 822 | name = "idna_adapter" 823 | version = "1.2.0" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 826 | dependencies = [ 827 | "icu_normalizer", 828 | "icu_properties", 829 | ] 830 | 831 | [[package]] 832 | name = "indexmap" 833 | version = "1.9.3" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 836 | dependencies = [ 837 | "autocfg", 838 | "hashbrown 0.12.3", 839 | "serde", 840 | ] 841 | 842 | [[package]] 843 | name = "indexmap" 844 | version = "2.7.0" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" 847 | dependencies = [ 848 | "equivalent", 849 | "hashbrown 0.15.2", 850 | "serde", 851 | ] 852 | 853 | [[package]] 854 | name = "ipnet" 855 | version = "2.10.1" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" 858 | 859 | [[package]] 860 | name = "is_terminal_polyfill" 861 | version = "1.70.1" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 864 | 865 | [[package]] 866 | name = "itoa" 867 | version = "1.0.14" 868 | source = "registry+https://github.com/rust-lang/crates.io-index" 869 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" 870 | 871 | [[package]] 872 | name = "js-sys" 873 | version = "0.3.72" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" 876 | dependencies = [ 877 | "wasm-bindgen", 878 | ] 879 | 880 | [[package]] 881 | name = "lazy_static" 882 | version = "1.5.0" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 885 | 886 | [[package]] 887 | name = "libc" 888 | version = "0.2.168" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" 891 | 892 | [[package]] 893 | name = "linked-hash-map" 894 | version = "0.5.6" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 897 | 898 | [[package]] 899 | name = "linux-raw-sys" 900 | version = "0.4.14" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 903 | 904 | [[package]] 905 | name = "litemap" 906 | version = "0.7.4" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" 909 | 910 | [[package]] 911 | name = "lock_api" 912 | version = "0.4.12" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 915 | dependencies = [ 916 | "autocfg", 917 | "scopeguard", 918 | ] 919 | 920 | [[package]] 921 | name = "log" 922 | version = "0.4.22" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 925 | 926 | [[package]] 927 | name = "memchr" 928 | version = "2.7.4" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 931 | 932 | [[package]] 933 | name = "mime" 934 | version = "0.3.17" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 937 | 938 | [[package]] 939 | name = "miniz_oxide" 940 | version = "0.8.0" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" 943 | dependencies = [ 944 | "adler2", 945 | ] 946 | 947 | [[package]] 948 | name = "mio" 949 | version = "1.0.3" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 952 | dependencies = [ 953 | "libc", 954 | "wasi", 955 | "windows-sys 0.52.0", 956 | ] 957 | 958 | [[package]] 959 | name = "num-conv" 960 | version = "0.1.0" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 963 | 964 | [[package]] 965 | name = "num-traits" 966 | version = "0.2.19" 967 | source = "registry+https://github.com/rust-lang/crates.io-index" 968 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 969 | dependencies = [ 970 | "autocfg", 971 | ] 972 | 973 | [[package]] 974 | name = "object" 975 | version = "0.36.5" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" 978 | dependencies = [ 979 | "memchr", 980 | ] 981 | 982 | [[package]] 983 | name = "once_cell" 984 | version = "1.20.2" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 987 | 988 | [[package]] 989 | name = "onig" 990 | version = "6.4.0" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" 993 | dependencies = [ 994 | "bitflags 1.3.2", 995 | "libc", 996 | "once_cell", 997 | "onig_sys", 998 | ] 999 | 1000 | [[package]] 1001 | name = "onig_sys" 1002 | version = "69.8.1" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" 1005 | dependencies = [ 1006 | "cc", 1007 | "pkg-config", 1008 | ] 1009 | 1010 | [[package]] 1011 | name = "parking_lot" 1012 | version = "0.12.3" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1015 | dependencies = [ 1016 | "lock_api", 1017 | "parking_lot_core", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "parking_lot_core" 1022 | version = "0.9.10" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1025 | dependencies = [ 1026 | "cfg-if", 1027 | "libc", 1028 | "redox_syscall", 1029 | "smallvec", 1030 | "windows-targets 0.52.6", 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "percent-encoding" 1035 | version = "2.3.1" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1038 | 1039 | [[package]] 1040 | name = "pin-project-lite" 1041 | version = "0.2.15" 1042 | source = "registry+https://github.com/rust-lang/crates.io-index" 1043 | checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" 1044 | 1045 | [[package]] 1046 | name = "pin-utils" 1047 | version = "0.1.0" 1048 | source = "registry+https://github.com/rust-lang/crates.io-index" 1049 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1050 | 1051 | [[package]] 1052 | name = "pkg-config" 1053 | version = "0.3.31" 1054 | source = "registry+https://github.com/rust-lang/crates.io-index" 1055 | checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 1056 | 1057 | [[package]] 1058 | name = "plist" 1059 | version = "1.7.0" 1060 | source = "registry+https://github.com/rust-lang/crates.io-index" 1061 | checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" 1062 | dependencies = [ 1063 | "base64", 1064 | "indexmap 2.7.0", 1065 | "quick-xml", 1066 | "serde", 1067 | "time", 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "powerfmt" 1072 | version = "0.2.0" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 1075 | 1076 | [[package]] 1077 | name = "ppv-lite86" 1078 | version = "0.2.20" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 1081 | dependencies = [ 1082 | "zerocopy", 1083 | ] 1084 | 1085 | [[package]] 1086 | name = "proc-macro2" 1087 | version = "1.0.92" 1088 | source = "registry+https://github.com/rust-lang/crates.io-index" 1089 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 1090 | dependencies = [ 1091 | "unicode-ident", 1092 | ] 1093 | 1094 | [[package]] 1095 | name = "quick-xml" 1096 | version = "0.32.0" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" 1099 | dependencies = [ 1100 | "memchr", 1101 | ] 1102 | 1103 | [[package]] 1104 | name = "quinn" 1105 | version = "0.11.6" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" 1108 | dependencies = [ 1109 | "bytes", 1110 | "pin-project-lite", 1111 | "quinn-proto", 1112 | "quinn-udp", 1113 | "rustc-hash", 1114 | "rustls", 1115 | "socket2", 1116 | "thiserror 2.0.7", 1117 | "tokio", 1118 | "tracing", 1119 | ] 1120 | 1121 | [[package]] 1122 | name = "quinn-proto" 1123 | version = "0.11.9" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" 1126 | dependencies = [ 1127 | "bytes", 1128 | "getrandom", 1129 | "rand", 1130 | "ring", 1131 | "rustc-hash", 1132 | "rustls", 1133 | "rustls-pki-types", 1134 | "slab", 1135 | "thiserror 2.0.7", 1136 | "tinyvec", 1137 | "tracing", 1138 | "web-time", 1139 | ] 1140 | 1141 | [[package]] 1142 | name = "quinn-udp" 1143 | version = "0.5.8" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527" 1146 | dependencies = [ 1147 | "cfg_aliases", 1148 | "libc", 1149 | "once_cell", 1150 | "socket2", 1151 | "tracing", 1152 | "windows-sys 0.59.0", 1153 | ] 1154 | 1155 | [[package]] 1156 | name = "quote" 1157 | version = "1.0.37" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 1160 | dependencies = [ 1161 | "proc-macro2", 1162 | ] 1163 | 1164 | [[package]] 1165 | name = "rand" 1166 | version = "0.8.5" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1169 | dependencies = [ 1170 | "libc", 1171 | "rand_chacha", 1172 | "rand_core", 1173 | ] 1174 | 1175 | [[package]] 1176 | name = "rand_chacha" 1177 | version = "0.3.1" 1178 | source = "registry+https://github.com/rust-lang/crates.io-index" 1179 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1180 | dependencies = [ 1181 | "ppv-lite86", 1182 | "rand_core", 1183 | ] 1184 | 1185 | [[package]] 1186 | name = "rand_core" 1187 | version = "0.6.4" 1188 | source = "registry+https://github.com/rust-lang/crates.io-index" 1189 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1190 | dependencies = [ 1191 | "getrandom", 1192 | ] 1193 | 1194 | [[package]] 1195 | name = "redox_syscall" 1196 | version = "0.5.7" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 1199 | dependencies = [ 1200 | "bitflags 2.6.0", 1201 | ] 1202 | 1203 | [[package]] 1204 | name = "regex-syntax" 1205 | version = "0.8.5" 1206 | source = "registry+https://github.com/rust-lang/crates.io-index" 1207 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1208 | 1209 | [[package]] 1210 | name = "reqwest" 1211 | version = "0.12.9" 1212 | source = "registry+https://github.com/rust-lang/crates.io-index" 1213 | checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" 1214 | dependencies = [ 1215 | "async-compression", 1216 | "base64", 1217 | "bytes", 1218 | "futures-core", 1219 | "futures-util", 1220 | "http", 1221 | "http-body", 1222 | "http-body-util", 1223 | "hyper", 1224 | "hyper-rustls", 1225 | "hyper-util", 1226 | "ipnet", 1227 | "js-sys", 1228 | "log", 1229 | "mime", 1230 | "once_cell", 1231 | "percent-encoding", 1232 | "pin-project-lite", 1233 | "quinn", 1234 | "rustls", 1235 | "rustls-pemfile", 1236 | "rustls-pki-types", 1237 | "serde", 1238 | "serde_json", 1239 | "serde_urlencoded", 1240 | "sync_wrapper", 1241 | "tokio", 1242 | "tokio-rustls", 1243 | "tokio-util", 1244 | "tower-service", 1245 | "url", 1246 | "wasm-bindgen", 1247 | "wasm-bindgen-futures", 1248 | "web-sys", 1249 | "webpki-roots", 1250 | "windows-registry", 1251 | ] 1252 | 1253 | [[package]] 1254 | name = "ring" 1255 | version = "0.17.8" 1256 | source = "registry+https://github.com/rust-lang/crates.io-index" 1257 | checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" 1258 | dependencies = [ 1259 | "cc", 1260 | "cfg-if", 1261 | "getrandom", 1262 | "libc", 1263 | "spin", 1264 | "untrusted", 1265 | "windows-sys 0.52.0", 1266 | ] 1267 | 1268 | [[package]] 1269 | name = "rustc-demangle" 1270 | version = "0.1.24" 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" 1272 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1273 | 1274 | [[package]] 1275 | name = "rustc-hash" 1276 | version = "2.1.0" 1277 | source = "registry+https://github.com/rust-lang/crates.io-index" 1278 | checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" 1279 | 1280 | [[package]] 1281 | name = "rustix" 1282 | version = "0.38.41" 1283 | source = "registry+https://github.com/rust-lang/crates.io-index" 1284 | checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" 1285 | dependencies = [ 1286 | "bitflags 2.6.0", 1287 | "errno", 1288 | "libc", 1289 | "linux-raw-sys", 1290 | "windows-sys 0.52.0", 1291 | ] 1292 | 1293 | [[package]] 1294 | name = "rustls" 1295 | version = "0.23.20" 1296 | source = "registry+https://github.com/rust-lang/crates.io-index" 1297 | checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" 1298 | dependencies = [ 1299 | "once_cell", 1300 | "ring", 1301 | "rustls-pki-types", 1302 | "rustls-webpki", 1303 | "subtle", 1304 | "zeroize", 1305 | ] 1306 | 1307 | [[package]] 1308 | name = "rustls-pemfile" 1309 | version = "2.2.0" 1310 | source = "registry+https://github.com/rust-lang/crates.io-index" 1311 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 1312 | dependencies = [ 1313 | "rustls-pki-types", 1314 | ] 1315 | 1316 | [[package]] 1317 | name = "rustls-pki-types" 1318 | version = "1.10.1" 1319 | source = "registry+https://github.com/rust-lang/crates.io-index" 1320 | checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" 1321 | dependencies = [ 1322 | "web-time", 1323 | ] 1324 | 1325 | [[package]] 1326 | name = "rustls-webpki" 1327 | version = "0.102.8" 1328 | source = "registry+https://github.com/rust-lang/crates.io-index" 1329 | checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" 1330 | dependencies = [ 1331 | "ring", 1332 | "rustls-pki-types", 1333 | "untrusted", 1334 | ] 1335 | 1336 | [[package]] 1337 | name = "ryu" 1338 | version = "1.0.18" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 1341 | 1342 | [[package]] 1343 | name = "same-file" 1344 | version = "1.0.6" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1347 | dependencies = [ 1348 | "winapi-util", 1349 | ] 1350 | 1351 | [[package]] 1352 | name = "scopeguard" 1353 | version = "1.2.0" 1354 | source = "registry+https://github.com/rust-lang/crates.io-index" 1355 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1356 | 1357 | [[package]] 1358 | name = "serde" 1359 | version = "1.0.216" 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" 1361 | checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" 1362 | dependencies = [ 1363 | "serde_derive", 1364 | ] 1365 | 1366 | [[package]] 1367 | name = "serde_derive" 1368 | version = "1.0.216" 1369 | source = "registry+https://github.com/rust-lang/crates.io-index" 1370 | checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" 1371 | dependencies = [ 1372 | "proc-macro2", 1373 | "quote", 1374 | "syn", 1375 | ] 1376 | 1377 | [[package]] 1378 | name = "serde_json" 1379 | version = "1.0.133" 1380 | source = "registry+https://github.com/rust-lang/crates.io-index" 1381 | checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" 1382 | dependencies = [ 1383 | "itoa", 1384 | "memchr", 1385 | "ryu", 1386 | "serde", 1387 | ] 1388 | 1389 | [[package]] 1390 | name = "serde_qs" 1391 | version = "0.13.0" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "cd34f36fe4c5ba9654417139a9b3a20d2e1de6012ee678ad14d240c22c78d8d6" 1394 | dependencies = [ 1395 | "percent-encoding", 1396 | "serde", 1397 | "thiserror 1.0.69", 1398 | ] 1399 | 1400 | [[package]] 1401 | name = "serde_urlencoded" 1402 | version = "0.7.1" 1403 | source = "registry+https://github.com/rust-lang/crates.io-index" 1404 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1405 | dependencies = [ 1406 | "form_urlencoded", 1407 | "itoa", 1408 | "ryu", 1409 | "serde", 1410 | ] 1411 | 1412 | [[package]] 1413 | name = "serde_with" 1414 | version = "3.11.0" 1415 | source = "registry+https://github.com/rust-lang/crates.io-index" 1416 | checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" 1417 | dependencies = [ 1418 | "base64", 1419 | "chrono", 1420 | "hex", 1421 | "indexmap 1.9.3", 1422 | "indexmap 2.7.0", 1423 | "serde", 1424 | "serde_derive", 1425 | "serde_json", 1426 | "serde_with_macros", 1427 | "time", 1428 | ] 1429 | 1430 | [[package]] 1431 | name = "serde_with_macros" 1432 | version = "3.11.0" 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" 1434 | checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" 1435 | dependencies = [ 1436 | "darling", 1437 | "proc-macro2", 1438 | "quote", 1439 | "syn", 1440 | ] 1441 | 1442 | [[package]] 1443 | name = "serde_yaml" 1444 | version = "0.9.34+deprecated" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" 1447 | dependencies = [ 1448 | "indexmap 2.7.0", 1449 | "itoa", 1450 | "ryu", 1451 | "serde", 1452 | "unsafe-libyaml", 1453 | ] 1454 | 1455 | [[package]] 1456 | name = "shell-words" 1457 | version = "1.1.0" 1458 | source = "registry+https://github.com/rust-lang/crates.io-index" 1459 | checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" 1460 | 1461 | [[package]] 1462 | name = "shlex" 1463 | version = "1.3.0" 1464 | source = "registry+https://github.com/rust-lang/crates.io-index" 1465 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1466 | 1467 | [[package]] 1468 | name = "signal-hook-registry" 1469 | version = "1.4.2" 1470 | source = "registry+https://github.com/rust-lang/crates.io-index" 1471 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 1472 | dependencies = [ 1473 | "libc", 1474 | ] 1475 | 1476 | [[package]] 1477 | name = "similar" 1478 | version = "2.6.0" 1479 | source = "registry+https://github.com/rust-lang/crates.io-index" 1480 | checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" 1481 | dependencies = [ 1482 | "bstr", 1483 | ] 1484 | 1485 | [[package]] 1486 | name = "slab" 1487 | version = "0.4.9" 1488 | source = "registry+https://github.com/rust-lang/crates.io-index" 1489 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1490 | dependencies = [ 1491 | "autocfg", 1492 | ] 1493 | 1494 | [[package]] 1495 | name = "smallvec" 1496 | version = "1.13.2" 1497 | source = "registry+https://github.com/rust-lang/crates.io-index" 1498 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1499 | 1500 | [[package]] 1501 | name = "socket2" 1502 | version = "0.5.8" 1503 | source = "registry+https://github.com/rust-lang/crates.io-index" 1504 | checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 1505 | dependencies = [ 1506 | "libc", 1507 | "windows-sys 0.52.0", 1508 | ] 1509 | 1510 | [[package]] 1511 | name = "spin" 1512 | version = "0.9.8" 1513 | source = "registry+https://github.com/rust-lang/crates.io-index" 1514 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1515 | 1516 | [[package]] 1517 | name = "stable_deref_trait" 1518 | version = "1.2.0" 1519 | source = "registry+https://github.com/rust-lang/crates.io-index" 1520 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1521 | 1522 | [[package]] 1523 | name = "strsim" 1524 | version = "0.11.1" 1525 | source = "registry+https://github.com/rust-lang/crates.io-index" 1526 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1527 | 1528 | [[package]] 1529 | name = "subtle" 1530 | version = "2.6.1" 1531 | source = "registry+https://github.com/rust-lang/crates.io-index" 1532 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1533 | 1534 | [[package]] 1535 | name = "syn" 1536 | version = "2.0.90" 1537 | source = "registry+https://github.com/rust-lang/crates.io-index" 1538 | checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" 1539 | dependencies = [ 1540 | "proc-macro2", 1541 | "quote", 1542 | "unicode-ident", 1543 | ] 1544 | 1545 | [[package]] 1546 | name = "sync_wrapper" 1547 | version = "1.0.2" 1548 | source = "registry+https://github.com/rust-lang/crates.io-index" 1549 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1550 | dependencies = [ 1551 | "futures-core", 1552 | ] 1553 | 1554 | [[package]] 1555 | name = "synstructure" 1556 | version = "0.13.1" 1557 | source = "registry+https://github.com/rust-lang/crates.io-index" 1558 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 1559 | dependencies = [ 1560 | "proc-macro2", 1561 | "quote", 1562 | "syn", 1563 | ] 1564 | 1565 | [[package]] 1566 | name = "syntect" 1567 | version = "5.2.0" 1568 | source = "registry+https://github.com/rust-lang/crates.io-index" 1569 | checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" 1570 | dependencies = [ 1571 | "bincode", 1572 | "bitflags 1.3.2", 1573 | "flate2", 1574 | "fnv", 1575 | "once_cell", 1576 | "onig", 1577 | "plist", 1578 | "regex-syntax", 1579 | "serde", 1580 | "serde_derive", 1581 | "serde_json", 1582 | "thiserror 1.0.69", 1583 | "walkdir", 1584 | "yaml-rust", 1585 | ] 1586 | 1587 | [[package]] 1588 | name = "tempfile" 1589 | version = "3.14.0" 1590 | source = "registry+https://github.com/rust-lang/crates.io-index" 1591 | checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" 1592 | dependencies = [ 1593 | "cfg-if", 1594 | "fastrand", 1595 | "once_cell", 1596 | "rustix", 1597 | "windows-sys 0.59.0", 1598 | ] 1599 | 1600 | [[package]] 1601 | name = "thiserror" 1602 | version = "1.0.69" 1603 | source = "registry+https://github.com/rust-lang/crates.io-index" 1604 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1605 | dependencies = [ 1606 | "thiserror-impl 1.0.69", 1607 | ] 1608 | 1609 | [[package]] 1610 | name = "thiserror" 1611 | version = "2.0.7" 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" 1613 | checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767" 1614 | dependencies = [ 1615 | "thiserror-impl 2.0.7", 1616 | ] 1617 | 1618 | [[package]] 1619 | name = "thiserror-impl" 1620 | version = "1.0.69" 1621 | source = "registry+https://github.com/rust-lang/crates.io-index" 1622 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1623 | dependencies = [ 1624 | "proc-macro2", 1625 | "quote", 1626 | "syn", 1627 | ] 1628 | 1629 | [[package]] 1630 | name = "thiserror-impl" 1631 | version = "2.0.7" 1632 | source = "registry+https://github.com/rust-lang/crates.io-index" 1633 | checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36" 1634 | dependencies = [ 1635 | "proc-macro2", 1636 | "quote", 1637 | "syn", 1638 | ] 1639 | 1640 | [[package]] 1641 | name = "time" 1642 | version = "0.3.36" 1643 | source = "registry+https://github.com/rust-lang/crates.io-index" 1644 | checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" 1645 | dependencies = [ 1646 | "deranged", 1647 | "itoa", 1648 | "num-conv", 1649 | "powerfmt", 1650 | "serde", 1651 | "time-core", 1652 | "time-macros", 1653 | ] 1654 | 1655 | [[package]] 1656 | name = "time-core" 1657 | version = "0.1.2" 1658 | source = "registry+https://github.com/rust-lang/crates.io-index" 1659 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 1660 | 1661 | [[package]] 1662 | name = "time-macros" 1663 | version = "0.2.18" 1664 | source = "registry+https://github.com/rust-lang/crates.io-index" 1665 | checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" 1666 | dependencies = [ 1667 | "num-conv", 1668 | "time-core", 1669 | ] 1670 | 1671 | [[package]] 1672 | name = "tinystr" 1673 | version = "0.7.6" 1674 | source = "registry+https://github.com/rust-lang/crates.io-index" 1675 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 1676 | dependencies = [ 1677 | "displaydoc", 1678 | "zerovec", 1679 | ] 1680 | 1681 | [[package]] 1682 | name = "tinyvec" 1683 | version = "1.8.0" 1684 | source = "registry+https://github.com/rust-lang/crates.io-index" 1685 | checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 1686 | dependencies = [ 1687 | "tinyvec_macros", 1688 | ] 1689 | 1690 | [[package]] 1691 | name = "tinyvec_macros" 1692 | version = "0.1.1" 1693 | source = "registry+https://github.com/rust-lang/crates.io-index" 1694 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1695 | 1696 | [[package]] 1697 | name = "tokio" 1698 | version = "1.42.0" 1699 | source = "registry+https://github.com/rust-lang/crates.io-index" 1700 | checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" 1701 | dependencies = [ 1702 | "backtrace", 1703 | "bytes", 1704 | "libc", 1705 | "mio", 1706 | "parking_lot", 1707 | "pin-project-lite", 1708 | "signal-hook-registry", 1709 | "socket2", 1710 | "tokio-macros", 1711 | "windows-sys 0.52.0", 1712 | ] 1713 | 1714 | [[package]] 1715 | name = "tokio-macros" 1716 | version = "2.4.0" 1717 | source = "registry+https://github.com/rust-lang/crates.io-index" 1718 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 1719 | dependencies = [ 1720 | "proc-macro2", 1721 | "quote", 1722 | "syn", 1723 | ] 1724 | 1725 | [[package]] 1726 | name = "tokio-rustls" 1727 | version = "0.26.1" 1728 | source = "registry+https://github.com/rust-lang/crates.io-index" 1729 | checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" 1730 | dependencies = [ 1731 | "rustls", 1732 | "tokio", 1733 | ] 1734 | 1735 | [[package]] 1736 | name = "tokio-util" 1737 | version = "0.7.12" 1738 | source = "registry+https://github.com/rust-lang/crates.io-index" 1739 | checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" 1740 | dependencies = [ 1741 | "bytes", 1742 | "futures-core", 1743 | "futures-sink", 1744 | "pin-project-lite", 1745 | "tokio", 1746 | ] 1747 | 1748 | [[package]] 1749 | name = "tower-service" 1750 | version = "0.3.3" 1751 | source = "registry+https://github.com/rust-lang/crates.io-index" 1752 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1753 | 1754 | [[package]] 1755 | name = "tracing" 1756 | version = "0.1.41" 1757 | source = "registry+https://github.com/rust-lang/crates.io-index" 1758 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1759 | dependencies = [ 1760 | "pin-project-lite", 1761 | "tracing-core", 1762 | ] 1763 | 1764 | [[package]] 1765 | name = "tracing-core" 1766 | version = "0.1.33" 1767 | source = "registry+https://github.com/rust-lang/crates.io-index" 1768 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1769 | dependencies = [ 1770 | "once_cell", 1771 | ] 1772 | 1773 | [[package]] 1774 | name = "try-lock" 1775 | version = "0.2.5" 1776 | source = "registry+https://github.com/rust-lang/crates.io-index" 1777 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1778 | 1779 | [[package]] 1780 | name = "unicode-ident" 1781 | version = "1.0.14" 1782 | source = "registry+https://github.com/rust-lang/crates.io-index" 1783 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 1784 | 1785 | [[package]] 1786 | name = "unicode-width" 1787 | version = "0.1.14" 1788 | source = "registry+https://github.com/rust-lang/crates.io-index" 1789 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 1790 | 1791 | [[package]] 1792 | name = "unsafe-libyaml" 1793 | version = "0.2.11" 1794 | source = "registry+https://github.com/rust-lang/crates.io-index" 1795 | checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" 1796 | 1797 | [[package]] 1798 | name = "untrusted" 1799 | version = "0.9.0" 1800 | source = "registry+https://github.com/rust-lang/crates.io-index" 1801 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1802 | 1803 | [[package]] 1804 | name = "url" 1805 | version = "2.5.4" 1806 | source = "registry+https://github.com/rust-lang/crates.io-index" 1807 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1808 | dependencies = [ 1809 | "form_urlencoded", 1810 | "idna", 1811 | "percent-encoding", 1812 | "serde", 1813 | ] 1814 | 1815 | [[package]] 1816 | name = "utf16_iter" 1817 | version = "1.0.5" 1818 | source = "registry+https://github.com/rust-lang/crates.io-index" 1819 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 1820 | 1821 | [[package]] 1822 | name = "utf8_iter" 1823 | version = "1.0.4" 1824 | source = "registry+https://github.com/rust-lang/crates.io-index" 1825 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1826 | 1827 | [[package]] 1828 | name = "utf8parse" 1829 | version = "0.2.2" 1830 | source = "registry+https://github.com/rust-lang/crates.io-index" 1831 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1832 | 1833 | [[package]] 1834 | name = "walkdir" 1835 | version = "2.5.0" 1836 | source = "registry+https://github.com/rust-lang/crates.io-index" 1837 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 1838 | dependencies = [ 1839 | "same-file", 1840 | "winapi-util", 1841 | ] 1842 | 1843 | [[package]] 1844 | name = "want" 1845 | version = "0.3.1" 1846 | source = "registry+https://github.com/rust-lang/crates.io-index" 1847 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1848 | dependencies = [ 1849 | "try-lock", 1850 | ] 1851 | 1852 | [[package]] 1853 | name = "wasi" 1854 | version = "0.11.0+wasi-snapshot-preview1" 1855 | source = "registry+https://github.com/rust-lang/crates.io-index" 1856 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1857 | 1858 | [[package]] 1859 | name = "wasm-bindgen" 1860 | version = "0.2.97" 1861 | source = "registry+https://github.com/rust-lang/crates.io-index" 1862 | checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" 1863 | dependencies = [ 1864 | "cfg-if", 1865 | "once_cell", 1866 | "wasm-bindgen-macro", 1867 | ] 1868 | 1869 | [[package]] 1870 | name = "wasm-bindgen-backend" 1871 | version = "0.2.97" 1872 | source = "registry+https://github.com/rust-lang/crates.io-index" 1873 | checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" 1874 | dependencies = [ 1875 | "bumpalo", 1876 | "log", 1877 | "once_cell", 1878 | "proc-macro2", 1879 | "quote", 1880 | "syn", 1881 | "wasm-bindgen-shared", 1882 | ] 1883 | 1884 | [[package]] 1885 | name = "wasm-bindgen-futures" 1886 | version = "0.4.45" 1887 | source = "registry+https://github.com/rust-lang/crates.io-index" 1888 | checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" 1889 | dependencies = [ 1890 | "cfg-if", 1891 | "js-sys", 1892 | "wasm-bindgen", 1893 | "web-sys", 1894 | ] 1895 | 1896 | [[package]] 1897 | name = "wasm-bindgen-macro" 1898 | version = "0.2.97" 1899 | source = "registry+https://github.com/rust-lang/crates.io-index" 1900 | checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" 1901 | dependencies = [ 1902 | "quote", 1903 | "wasm-bindgen-macro-support", 1904 | ] 1905 | 1906 | [[package]] 1907 | name = "wasm-bindgen-macro-support" 1908 | version = "0.2.97" 1909 | source = "registry+https://github.com/rust-lang/crates.io-index" 1910 | checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" 1911 | dependencies = [ 1912 | "proc-macro2", 1913 | "quote", 1914 | "syn", 1915 | "wasm-bindgen-backend", 1916 | "wasm-bindgen-shared", 1917 | ] 1918 | 1919 | [[package]] 1920 | name = "wasm-bindgen-shared" 1921 | version = "0.2.97" 1922 | source = "registry+https://github.com/rust-lang/crates.io-index" 1923 | checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" 1924 | 1925 | [[package]] 1926 | name = "web-sys" 1927 | version = "0.3.72" 1928 | source = "registry+https://github.com/rust-lang/crates.io-index" 1929 | checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" 1930 | dependencies = [ 1931 | "js-sys", 1932 | "wasm-bindgen", 1933 | ] 1934 | 1935 | [[package]] 1936 | name = "web-time" 1937 | version = "1.1.0" 1938 | source = "registry+https://github.com/rust-lang/crates.io-index" 1939 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 1940 | dependencies = [ 1941 | "js-sys", 1942 | "wasm-bindgen", 1943 | ] 1944 | 1945 | [[package]] 1946 | name = "webpki-roots" 1947 | version = "0.26.7" 1948 | source = "registry+https://github.com/rust-lang/crates.io-index" 1949 | checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" 1950 | dependencies = [ 1951 | "rustls-pki-types", 1952 | ] 1953 | 1954 | [[package]] 1955 | name = "winapi" 1956 | version = "0.3.9" 1957 | source = "registry+https://github.com/rust-lang/crates.io-index" 1958 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1959 | dependencies = [ 1960 | "winapi-i686-pc-windows-gnu", 1961 | "winapi-x86_64-pc-windows-gnu", 1962 | ] 1963 | 1964 | [[package]] 1965 | name = "winapi-i686-pc-windows-gnu" 1966 | version = "0.4.0" 1967 | source = "registry+https://github.com/rust-lang/crates.io-index" 1968 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1969 | 1970 | [[package]] 1971 | name = "winapi-util" 1972 | version = "0.1.9" 1973 | source = "registry+https://github.com/rust-lang/crates.io-index" 1974 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 1975 | dependencies = [ 1976 | "windows-sys 0.59.0", 1977 | ] 1978 | 1979 | [[package]] 1980 | name = "winapi-x86_64-pc-windows-gnu" 1981 | version = "0.4.0" 1982 | source = "registry+https://github.com/rust-lang/crates.io-index" 1983 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1984 | 1985 | [[package]] 1986 | name = "windows-core" 1987 | version = "0.52.0" 1988 | source = "registry+https://github.com/rust-lang/crates.io-index" 1989 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 1990 | dependencies = [ 1991 | "windows-targets 0.52.6", 1992 | ] 1993 | 1994 | [[package]] 1995 | name = "windows-registry" 1996 | version = "0.2.0" 1997 | source = "registry+https://github.com/rust-lang/crates.io-index" 1998 | checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" 1999 | dependencies = [ 2000 | "windows-result", 2001 | "windows-strings", 2002 | "windows-targets 0.52.6", 2003 | ] 2004 | 2005 | [[package]] 2006 | name = "windows-result" 2007 | version = "0.2.0" 2008 | source = "registry+https://github.com/rust-lang/crates.io-index" 2009 | checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" 2010 | dependencies = [ 2011 | "windows-targets 0.52.6", 2012 | ] 2013 | 2014 | [[package]] 2015 | name = "windows-strings" 2016 | version = "0.1.0" 2017 | source = "registry+https://github.com/rust-lang/crates.io-index" 2018 | checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" 2019 | dependencies = [ 2020 | "windows-result", 2021 | "windows-targets 0.52.6", 2022 | ] 2023 | 2024 | [[package]] 2025 | name = "windows-sys" 2026 | version = "0.48.0" 2027 | source = "registry+https://github.com/rust-lang/crates.io-index" 2028 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 2029 | dependencies = [ 2030 | "windows-targets 0.48.5", 2031 | ] 2032 | 2033 | [[package]] 2034 | name = "windows-sys" 2035 | version = "0.52.0" 2036 | source = "registry+https://github.com/rust-lang/crates.io-index" 2037 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2038 | dependencies = [ 2039 | "windows-targets 0.52.6", 2040 | ] 2041 | 2042 | [[package]] 2043 | name = "windows-sys" 2044 | version = "0.59.0" 2045 | source = "registry+https://github.com/rust-lang/crates.io-index" 2046 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2047 | dependencies = [ 2048 | "windows-targets 0.52.6", 2049 | ] 2050 | 2051 | [[package]] 2052 | name = "windows-targets" 2053 | version = "0.48.5" 2054 | source = "registry+https://github.com/rust-lang/crates.io-index" 2055 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2056 | dependencies = [ 2057 | "windows_aarch64_gnullvm 0.48.5", 2058 | "windows_aarch64_msvc 0.48.5", 2059 | "windows_i686_gnu 0.48.5", 2060 | "windows_i686_msvc 0.48.5", 2061 | "windows_x86_64_gnu 0.48.5", 2062 | "windows_x86_64_gnullvm 0.48.5", 2063 | "windows_x86_64_msvc 0.48.5", 2064 | ] 2065 | 2066 | [[package]] 2067 | name = "windows-targets" 2068 | version = "0.52.6" 2069 | source = "registry+https://github.com/rust-lang/crates.io-index" 2070 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2071 | dependencies = [ 2072 | "windows_aarch64_gnullvm 0.52.6", 2073 | "windows_aarch64_msvc 0.52.6", 2074 | "windows_i686_gnu 0.52.6", 2075 | "windows_i686_gnullvm", 2076 | "windows_i686_msvc 0.52.6", 2077 | "windows_x86_64_gnu 0.52.6", 2078 | "windows_x86_64_gnullvm 0.52.6", 2079 | "windows_x86_64_msvc 0.52.6", 2080 | ] 2081 | 2082 | [[package]] 2083 | name = "windows_aarch64_gnullvm" 2084 | version = "0.48.5" 2085 | source = "registry+https://github.com/rust-lang/crates.io-index" 2086 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2087 | 2088 | [[package]] 2089 | name = "windows_aarch64_gnullvm" 2090 | version = "0.52.6" 2091 | source = "registry+https://github.com/rust-lang/crates.io-index" 2092 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2093 | 2094 | [[package]] 2095 | name = "windows_aarch64_msvc" 2096 | version = "0.48.5" 2097 | source = "registry+https://github.com/rust-lang/crates.io-index" 2098 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2099 | 2100 | [[package]] 2101 | name = "windows_aarch64_msvc" 2102 | version = "0.52.6" 2103 | source = "registry+https://github.com/rust-lang/crates.io-index" 2104 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2105 | 2106 | [[package]] 2107 | name = "windows_i686_gnu" 2108 | version = "0.48.5" 2109 | source = "registry+https://github.com/rust-lang/crates.io-index" 2110 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2111 | 2112 | [[package]] 2113 | name = "windows_i686_gnu" 2114 | version = "0.52.6" 2115 | source = "registry+https://github.com/rust-lang/crates.io-index" 2116 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2117 | 2118 | [[package]] 2119 | name = "windows_i686_gnullvm" 2120 | version = "0.52.6" 2121 | source = "registry+https://github.com/rust-lang/crates.io-index" 2122 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2123 | 2124 | [[package]] 2125 | name = "windows_i686_msvc" 2126 | version = "0.48.5" 2127 | source = "registry+https://github.com/rust-lang/crates.io-index" 2128 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2129 | 2130 | [[package]] 2131 | name = "windows_i686_msvc" 2132 | version = "0.52.6" 2133 | source = "registry+https://github.com/rust-lang/crates.io-index" 2134 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2135 | 2136 | [[package]] 2137 | name = "windows_x86_64_gnu" 2138 | version = "0.48.5" 2139 | source = "registry+https://github.com/rust-lang/crates.io-index" 2140 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2141 | 2142 | [[package]] 2143 | name = "windows_x86_64_gnu" 2144 | version = "0.52.6" 2145 | source = "registry+https://github.com/rust-lang/crates.io-index" 2146 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2147 | 2148 | [[package]] 2149 | name = "windows_x86_64_gnullvm" 2150 | version = "0.48.5" 2151 | source = "registry+https://github.com/rust-lang/crates.io-index" 2152 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2153 | 2154 | [[package]] 2155 | name = "windows_x86_64_gnullvm" 2156 | version = "0.52.6" 2157 | source = "registry+https://github.com/rust-lang/crates.io-index" 2158 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2159 | 2160 | [[package]] 2161 | name = "windows_x86_64_msvc" 2162 | version = "0.48.5" 2163 | source = "registry+https://github.com/rust-lang/crates.io-index" 2164 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2165 | 2166 | [[package]] 2167 | name = "windows_x86_64_msvc" 2168 | version = "0.52.6" 2169 | source = "registry+https://github.com/rust-lang/crates.io-index" 2170 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2171 | 2172 | [[package]] 2173 | name = "write16" 2174 | version = "1.0.0" 2175 | source = "registry+https://github.com/rust-lang/crates.io-index" 2176 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 2177 | 2178 | [[package]] 2179 | name = "writeable" 2180 | version = "0.5.5" 2181 | source = "registry+https://github.com/rust-lang/crates.io-index" 2182 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 2183 | 2184 | [[package]] 2185 | name = "xdiff" 2186 | version = "0.4.4" 2187 | dependencies = [ 2188 | "anyhow", 2189 | "atty", 2190 | "clap", 2191 | "dialoguer", 2192 | "serde_json", 2193 | "serde_yaml", 2194 | "tokio", 2195 | "xreq-cli-utils", 2196 | "xreq-lib", 2197 | ] 2198 | 2199 | [[package]] 2200 | name = "xreq" 2201 | version = "0.4.4" 2202 | dependencies = [ 2203 | "anyhow", 2204 | "atty", 2205 | "clap", 2206 | "colored", 2207 | "dialoguer", 2208 | "mime", 2209 | "serde_json", 2210 | "serde_yaml", 2211 | "tokio", 2212 | "xreq-cli-utils", 2213 | "xreq-lib", 2214 | ] 2215 | 2216 | [[package]] 2217 | name = "xreq-cli-utils" 2218 | version = "0.3.2" 2219 | dependencies = [ 2220 | "anyhow", 2221 | "atty", 2222 | "syntect", 2223 | "xreq-lib", 2224 | ] 2225 | 2226 | [[package]] 2227 | name = "xreq-lib" 2228 | version = "0.4.2" 2229 | dependencies = [ 2230 | "anyhow", 2231 | "console", 2232 | "http", 2233 | "http-serde", 2234 | "reqwest", 2235 | "serde", 2236 | "serde_json", 2237 | "serde_qs", 2238 | "serde_with", 2239 | "serde_yaml", 2240 | "similar", 2241 | "tokio", 2242 | "url", 2243 | ] 2244 | 2245 | [[package]] 2246 | name = "yaml-rust" 2247 | version = "0.4.5" 2248 | source = "registry+https://github.com/rust-lang/crates.io-index" 2249 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 2250 | dependencies = [ 2251 | "linked-hash-map", 2252 | ] 2253 | 2254 | [[package]] 2255 | name = "yoke" 2256 | version = "0.7.5" 2257 | source = "registry+https://github.com/rust-lang/crates.io-index" 2258 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 2259 | dependencies = [ 2260 | "serde", 2261 | "stable_deref_trait", 2262 | "yoke-derive", 2263 | "zerofrom", 2264 | ] 2265 | 2266 | [[package]] 2267 | name = "yoke-derive" 2268 | version = "0.7.5" 2269 | source = "registry+https://github.com/rust-lang/crates.io-index" 2270 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 2271 | dependencies = [ 2272 | "proc-macro2", 2273 | "quote", 2274 | "syn", 2275 | "synstructure", 2276 | ] 2277 | 2278 | [[package]] 2279 | name = "zerocopy" 2280 | version = "0.7.35" 2281 | source = "registry+https://github.com/rust-lang/crates.io-index" 2282 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 2283 | dependencies = [ 2284 | "byteorder", 2285 | "zerocopy-derive", 2286 | ] 2287 | 2288 | [[package]] 2289 | name = "zerocopy-derive" 2290 | version = "0.7.35" 2291 | source = "registry+https://github.com/rust-lang/crates.io-index" 2292 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 2293 | dependencies = [ 2294 | "proc-macro2", 2295 | "quote", 2296 | "syn", 2297 | ] 2298 | 2299 | [[package]] 2300 | name = "zerofrom" 2301 | version = "0.1.5" 2302 | source = "registry+https://github.com/rust-lang/crates.io-index" 2303 | checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" 2304 | dependencies = [ 2305 | "zerofrom-derive", 2306 | ] 2307 | 2308 | [[package]] 2309 | name = "zerofrom-derive" 2310 | version = "0.1.5" 2311 | source = "registry+https://github.com/rust-lang/crates.io-index" 2312 | checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" 2313 | dependencies = [ 2314 | "proc-macro2", 2315 | "quote", 2316 | "syn", 2317 | "synstructure", 2318 | ] 2319 | 2320 | [[package]] 2321 | name = "zeroize" 2322 | version = "1.8.1" 2323 | source = "registry+https://github.com/rust-lang/crates.io-index" 2324 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2325 | 2326 | [[package]] 2327 | name = "zerovec" 2328 | version = "0.10.4" 2329 | source = "registry+https://github.com/rust-lang/crates.io-index" 2330 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 2331 | dependencies = [ 2332 | "yoke", 2333 | "zerofrom", 2334 | "zerovec-derive", 2335 | ] 2336 | 2337 | [[package]] 2338 | name = "zerovec-derive" 2339 | version = "0.10.3" 2340 | source = "registry+https://github.com/rust-lang/crates.io-index" 2341 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 2342 | dependencies = [ 2343 | "proc-macro2", 2344 | "quote", 2345 | "syn", 2346 | ] 2347 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "requester", 5 | "cli-utils", 6 | 7 | # cli 8 | "xdiff", 9 | "xreq", 10 | ] 11 | 12 | resolver = "2" 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTTP request and diff tools 2 | 3 | There're two separate CLIs provided: 4 | 5 | - xdiff: A diff tool for comparing HTTP requests. It could be used to compare the difference between production staging or two versions of the same API. 6 | - xreq: A tool to build HTTP requests based on predefined profiles. It could be used to replace curl/httpie for building complicated HTTP requests. 7 | 8 | ## xdiff 9 | 10 | ### Configuration 11 | 12 | You can configure multiple profiles for xdiff. Each profile is identified by a name. Inside a profile you can define the details of the two requests (method, url, query params, request headers, request body), and also what part of the response should be skipped for comparison (currently only headers could be skipped). 13 | 14 | ```yaml 15 | --- 16 | rust: 17 | request1: 18 | method: GET 19 | url: https://www.rust-lang.org/ 20 | headers: 21 | user-agent: Aloha 22 | params: 23 | hello: world 24 | request2: 25 | method: GET 26 | url: https://www.rust-lang.org/ 27 | params: {} 28 | response: 29 | skip_headers: 30 | - set-cookie 31 | - date 32 | - via 33 | - x-amz-cf-id 34 | ``` 35 | 36 | You could put the configuration in `~/.config/xdiff.yml`, or `/etc/xdiff.yml`, or `~/xdiff.yml`. The xdiff CLI will look for configuration from these paths. 37 | 38 | ### How to use xdiff? 39 | 40 | You can use `cargo install xdiff` to install it (need help to [install rust toolchain](https://rustup.rs/)?). Once finished you shall be able to use it. 41 | 42 | ```bash 43 | ➜ xdiff --help 44 | xdiff 0.4.1 45 | A CLI to diff two requests based on predefined profiles. 46 | 47 | USAGE: 48 | xdiff 49 | 50 | OPTIONS: 51 | -h, --help Print help information 52 | -V, --version Print version information 53 | 54 | SUBCOMMANDS: 55 | help Print this message or the help of the given subcommand(s) 56 | parse parse a URL and print the generated diff config 57 | run diff two API responses based on a given profile 58 | 59 | ➜ xdiff run --help 60 | xdiff-run 61 | diff two API responses based on a given profile 62 | 63 | USAGE: 64 | xdiff run [OPTIONS] --profile 65 | 66 | OPTIONS: 67 | -c, --config Path to the config file 68 | -e Extra parameters to pass to the API 69 | -h, --help Print help information 70 | -p, --profile API profile to use 71 | ``` 72 | 73 | An example: 74 | 75 | ```bash 76 | xdiff run -p todo -c requester/fixtures/diff.yml -e a=1 -e b=2 77 | ``` 78 | 79 | This will use the todo profile in the diff.yml defined in `requester/fixtures`, and add extra params for query string with a=1, b=2. Output look like this: 80 | 81 | ![screenshot](docs/images/screenshot1.png) 82 | 83 | If you find writing the config file tedious, you can use the `xdiff parse` subcommand to parse a URL and print the generated config. 84 | 85 | ```bash 86 | ➜ xdiff parse 87 | ✔ Url1 · https://jsonplaceholder.typicode.com/todos/1?a=1 88 | ✔ Url2 · https://jsonplaceholder.typicode.com/todos/2?b=2 89 | ✔ Give this a profile name · todo 90 | ✔ Select response headers to skip · date, x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset, vary, cache-control, expires, etag, via, cf-cache-status, expect-ct, report-to, cf-ray 91 | --- 92 | todo: 93 | request1: 94 | url: https://jsonplaceholder.typicode.com/todos/1 95 | params: 96 | a: '100' 97 | request2: 98 | url: https://jsonplaceholder.typicode.com/todos/2 99 | params: 100 | c: '200' 101 | response: 102 | skip_headers: 103 | - date 104 | - x-ratelimit-limit 105 | - x-ratelimit-remaining 106 | - x-ratelimit-reset 107 | - vary 108 | - cache-control 109 | - expires 110 | - etag 111 | - via 112 | - cf-cache-status 113 | - expect-ct 114 | - report-to 115 | - cf-ray 116 | ``` 117 | 118 | ## xreq 119 | 120 | since xdiff needs to send and format request so this logic was extracted as a separate CLI `xreq`. 121 | 122 | ### Configuration 123 | 124 | You can configure multiple profiles for xreq. Each profile is identified by a name. Inside a profile you can define the details of the request (method, url, query params, request headers, request body). 125 | 126 | ```yaml 127 | --- 128 | rust: 129 | url: https://www.rust-lang.org/ 130 | post: 131 | url: https://jsonplaceholder.typicode.com/comments 132 | params: 133 | postId: 1 134 | ``` 135 | 136 | You could put the configuration in `~/.config/xreq.yml`, or `/etc/xreq.yml`, or `~/xreq.yml`. The xreq CLI will look for configuration from these paths. 137 | 138 | ### How to use xreq? 139 | 140 | You can use `cargo install xreq` to install it. Once finished you shall be able to use it. 141 | 142 | ```bash 143 | ➜ xreq --help 144 | xreq 0.4.1 145 | A CLI to send complicated request based on predefined profiles. 146 | 147 | USAGE: 148 | xreq 149 | 150 | OPTIONS: 151 | -h, --help Print help information 152 | -V, --version Print version information 153 | 154 | SUBCOMMANDS: 155 | help Print this message or the help of the given subcommand(s) 156 | parse parse a URL and print the generated request config 157 | run Send API request based on a given profile 158 | 159 | ➜ xreq run --help 160 | xreq-run 161 | Send API request based on a given profile 162 | 163 | USAGE: 164 | xreq run [OPTIONS] --profile 165 | 166 | OPTIONS: 167 | -c, --config Path to the config file 168 | -e Extra parameters to pass to the API. If no prefix, it will be used 169 | for querystring; If prefix is '@', it will be used for body; If 170 | prefix is '%', it will be used for header 171 | -h, --help Print help information 172 | -p, --profile API profile to use 173 | 174 | ``` 175 | 176 | An example: 177 | 178 | ```bash 179 | xreq run -p post -c requester/fixtures/req.yml -e a=1 -e b=2 180 | ``` 181 | 182 | This will use the todo profile in the req.yml defined in `requester/fixtures`, and add extra params for query string with a=1, b=2. Output look like this: 183 | 184 | ![screenshot](docs/images/screenshot2.png) 185 | 186 | You could also use tools like `jq` to process its output. When xreq detected a pipe, it will skip printing status/headers, and skip the colorized format on http body. For example: 187 | 188 | ```bash 189 | xreq -p post -c requester/fixtures/req.yml -e a=1 -e b=2 | jq ".[] | select (.id < 3)" 190 | ``` 191 | 192 | Output: 193 | 194 | ![screenshot](docs/images/screenshot3.png) 195 | 196 | If you find writing the config file tedious, you can use the `xreq parse` subcommand to parse a URL and print the generated config. 197 | 198 | ```bash 199 | ➜ xreq parse 200 | ✔ Url to parse · https://jsonplaceholder.typicode.com/todos/1?a=1&b=2 201 | ✔ Give this url a profile name · todo 202 | --- 203 | todo: 204 | url: https://jsonplaceholder.typicode.com/todos/1 205 | params: 206 | a: '1' 207 | b: '2' 208 | ``` 209 | -------------------------------------------------------------------------------- /_typos.toml: -------------------------------------------------------------------------------- 1 | [default.extend-words] 2 | flate = "flate" 3 | -------------------------------------------------------------------------------- /cli-utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xreq-cli-utils" 3 | version = "0.3.2" 4 | edition = "2021" 5 | license = "MIT" 6 | documentation = "https://docs.rs/xreq-cli-utils" 7 | repository = "https://github.com/Tubitv/xdiff" 8 | homepage = "https://github.com/Tubitv/xdiff" 9 | description = """ 10 | CLI utility functions. 11 | """ 12 | readme = "../README.md" 13 | keywords = ["cli"] 14 | categories = ["development-tools"] 15 | 16 | 17 | [dependencies] 18 | anyhow = "1.0.94" 19 | atty = "0.2.14" 20 | syntect = "5.2.0" 21 | 22 | xreq-lib = { version = "0.4.0", path = "../requester" } 23 | -------------------------------------------------------------------------------- /cli-utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | 3 | use anyhow::Result; 4 | use syntect::{ 5 | easy::HighlightLines, 6 | highlighting::{Style, ThemeSet}, 7 | parsing::SyntaxSet, 8 | util::{as_24_bit_terminal_escaped, LinesWithEndings}, 9 | }; 10 | use xreq_lib::{KeyVal, KeyValType}; 11 | 12 | /// Parse a single key-value pair 13 | /// - if key has no any prefix, it is for query 14 | /// - if key starts with '%', it is for header 15 | /// - if key starts with '@', it is for body 16 | pub fn parse_key_val(s: &str) -> Result { 17 | let (kv_type, input) = match s.chars().next() { 18 | Some(c) => match c { 19 | '%' => (KeyValType::Header, &s[1..]), 20 | '@' => (KeyValType::Body, &s[1..]), 21 | 'A'..='Z' | 'a'..='z' => (KeyValType::Query, s), 22 | _ => return Err(anyhow::anyhow!("invalid key val pair: {}", s)), 23 | }, 24 | None => return Err(anyhow::anyhow!("empty key-value pair is invalid")), 25 | }; 26 | 27 | let mut parts = input.splitn(2, '='); 28 | let key = parts.next().ok_or_else(|| anyhow::anyhow!("missing key"))?; 29 | let val = parts 30 | .next() 31 | .ok_or_else(|| anyhow::anyhow!("missing value"))?; 32 | Ok(KeyVal::new(kv_type, key, val)) 33 | } 34 | 35 | pub fn get_config_file(s: &str) -> Result { 36 | let path = Path::new(s); 37 | if path.exists() { 38 | Ok(path.to_path_buf()) 39 | } else { 40 | Err(anyhow::anyhow!("config file not found")) 41 | } 42 | } 43 | 44 | pub fn get_default_config(name: &str) -> Result { 45 | let paths = [ 46 | format!("{}/.config/{}", std::env::var("HOME").unwrap(), name), 47 | format!("./{}", name), 48 | format!("/etc/{}", name), 49 | ]; 50 | 51 | for path in paths.iter() { 52 | if Path::new(path).exists() { 53 | return Ok(Path::new(path).to_path_buf()); 54 | } 55 | } 56 | 57 | Err(anyhow::anyhow!("Config file not found. You can either specify it with the --config option or put it in one of the following locations: {}", paths.join(", "))) 58 | } 59 | 60 | pub fn print_syntect(output: &mut Vec, s: String, ext: &str) -> Result<()> { 61 | if atty::isnt(atty::Stream::Stdout) { 62 | output.push(s); 63 | return Ok(()); 64 | } 65 | 66 | // Load these once at the start of your program 67 | let ps = SyntaxSet::load_defaults_newlines(); 68 | let ts = ThemeSet::load_defaults(); 69 | let syntax = ps.find_syntax_by_extension(ext).unwrap(); 70 | let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]); 71 | for line in LinesWithEndings::from(&s) { 72 | let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps)?; 73 | let escaped = as_24_bit_terminal_escaped(&ranges[..], false); 74 | output.push(escaped); 75 | } 76 | 77 | Ok(()) 78 | } 79 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | # This template contains all of the possible sections and their default values 2 | 3 | # Note that all fields that take a lint level have these possible values: 4 | # * deny - An error will be produced and the check will fail 5 | # * warn - A warning will be produced, but the check will not fail 6 | # * allow - No warning or error will be produced, though in some cases a note 7 | # will be 8 | 9 | # The values provided in this template are the default values that will be used 10 | # when any section or field is not specified in your own configuration 11 | 12 | # If 1 or more target triples (and optionally, target_features) are specified, 13 | # only the specified targets will be checked when running `cargo deny check`. 14 | # This means, if a particular package is only ever used as a target specific 15 | # dependency, such as, for example, the `nix` crate only being used via the 16 | # `target_family = "unix"` configuration, that only having windows targets in 17 | # this list would mean the nix crate, as well as any of its exclusive 18 | # dependencies not shared by any other crates, would be ignored, as the target 19 | # list here is effectively saying which targets you are building for. 20 | targets = [ 21 | # The triple can be any string, but only the target triples built in to 22 | # rustc (as of 1.40) can be checked against actual config expressions 23 | #{ triple = "x86_64-unknown-linux-musl" }, 24 | # You can also specify which target_features you promise are enabled for a 25 | # particular target. target_features are currently not validated against 26 | # the actual valid features supported by the target architecture. 27 | #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, 28 | ] 29 | 30 | # This section is considered when running `cargo deny check advisories` 31 | # More documentation for the advisories section can be found here: 32 | # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html 33 | [advisories] 34 | # The path where the advisory database is cloned/fetched into 35 | db-path = "~/.cargo/advisory-db" 36 | # The url(s) of the advisory databases to use 37 | db-urls = ["https://github.com/rustsec/advisory-db"] 38 | # The lint level for security vulnerabilities 39 | vulnerability = "deny" 40 | # The lint level for unmaintained crates 41 | unmaintained = "warn" 42 | # The lint level for crates that have been yanked from their source registry 43 | yanked = "warn" 44 | # The lint level for crates with security notices. Note that as of 45 | # 2019-12-17 there are no security notice advisories in 46 | # https://github.com/rustsec/advisory-db 47 | notice = "warn" 48 | # A list of advisory IDs to ignore. Note that ignored advisories will still 49 | # output a note when they are encountered. 50 | ignore = [ 51 | #"RUSTSEC-0000-0000", 52 | ] 53 | # Threshold for security vulnerabilities, any vulnerability with a CVSS score 54 | # lower than the range specified will be ignored. Note that ignored advisories 55 | # will still output a note when they are encountered. 56 | # * None - CVSS Score 0.0 57 | # * Low - CVSS Score 0.1 - 3.9 58 | # * Medium - CVSS Score 4.0 - 6.9 59 | # * High - CVSS Score 7.0 - 8.9 60 | # * Critical - CVSS Score 9.0 - 10.0 61 | #severity-threshold = 62 | 63 | # This section is considered when running `cargo deny check licenses` 64 | # More documentation for the licenses section can be found here: 65 | # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html 66 | [licenses] 67 | # The lint level for crates which do not have a detectable license 68 | unlicensed = "allow" 69 | # List of explicitly allowed licenses 70 | # See https://spdx.org/licenses/ for list of possible licenses 71 | # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. 72 | allow = [ 73 | "MIT", 74 | "Apache-2.0", 75 | "Apache-2.0 WITH LLVM-exception", 76 | "BSD-3-Clause", 77 | "MPL-2.0", 78 | "ISC", 79 | "Unicode-DFS-2016", 80 | "Unicode-3.0", 81 | ] 82 | # List of explicitly disallowed licenses 83 | # See https://spdx.org/licenses/ for list of possible licenses 84 | # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. 85 | deny = [ 86 | #"Nokia", 87 | ] 88 | # Lint level for licenses considered copyleft 89 | copyleft = "warn" 90 | # Blanket approval or denial for OSI-approved or FSF Free/Libre licenses 91 | # * both - The license will be approved if it is both OSI-approved *AND* FSF 92 | # * either - The license will be approved if it is either OSI-approved *OR* FSF 93 | # * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF 94 | # * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved 95 | # * neither - This predicate is ignored and the default lint level is used 96 | allow-osi-fsf-free = "neither" 97 | # Lint level used when no other predicates are matched 98 | # 1. License isn't in the allow or deny lists 99 | # 2. License isn't copyleft 100 | # 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" 101 | default = "deny" 102 | # The confidence threshold for detecting a license from license text. 103 | # The higher the value, the more closely the license text must be to the 104 | # canonical license text of a valid SPDX license file. 105 | # [possible values: any between 0.0 and 1.0]. 106 | confidence-threshold = 0.8 107 | # Allow 1 or more licenses on a per-crate basis, so that particular licenses 108 | # aren't accepted for every possible crate as with the normal allow list 109 | exceptions = [ 110 | # Each entry is the crate and version constraint, and its specific allow 111 | # list 112 | #{ allow = ["Zlib"], name = "adler32", version = "*" }, 113 | ] 114 | 115 | # Some crates don't have (easily) machine readable licensing information, 116 | # adding a clarification entry for it allows you to manually specify the 117 | # licensing information 118 | #[[licenses.clarify]] 119 | # The name of the crate the clarification applies to 120 | #name = "ring" 121 | # The optional version constraint for the crate 122 | #version = "*" 123 | # The SPDX expression for the license requirements of the crate 124 | #expression = "MIT AND ISC AND OpenSSL" 125 | # One or more files in the crate's source used as the "source of truth" for 126 | # the license expression. If the contents match, the clarification will be used 127 | # when running the license check, otherwise the clarification will be ignored 128 | # and the crate will be checked normally, which may produce warnings or errors 129 | # depending on the rest of your configuration 130 | #license-files = [ 131 | # Each entry is a crate relative path, and the (opaque) hash of its contents 132 | #{ path = "LICENSE", hash = 0xbd0eed23 } 133 | #] 134 | 135 | [licenses.private] 136 | # If true, ignores workspace crates that aren't published, or are only 137 | # published to private registries 138 | ignore = false 139 | # One or more private registries that you might publish crates to, if a crate 140 | # is only published to private registries, and ignore is true, the crate will 141 | # not have its license(s) checked 142 | registries = [ 143 | #"https://sekretz.com/registry 144 | ] 145 | 146 | # This section is considered when running `cargo deny check bans`. 147 | # More documentation about the 'bans' section can be found here: 148 | # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html 149 | [bans] 150 | # Lint level for when multiple versions of the same crate are detected 151 | multiple-versions = "warn" 152 | # Lint level for when a crate version requirement is `*` 153 | wildcards = "allow" 154 | # The graph highlighting used when creating dotgraphs for crates 155 | # with multiple versions 156 | # * lowest-version - The path to the lowest versioned duplicate is highlighted 157 | # * simplest-path - The path to the version with the fewest edges is highlighted 158 | # * all - Both lowest-version and simplest-path are used 159 | highlight = "all" 160 | # List of crates that are allowed. Use with care! 161 | allow = [ 162 | #{ name = "ansi_term", version = "=0.11.0" }, 163 | ] 164 | # List of crates to deny 165 | deny = [ 166 | # Each entry the name of a crate and a version range. If version is 167 | # not specified, all versions will be matched. 168 | #{ name = "ansi_term", version = "=0.11.0" }, 169 | # 170 | # Wrapper crates can optionally be specified to allow the crate when it 171 | # is a direct dependency of the otherwise banned crate 172 | #{ name = "ansi_term", version = "=0.11.0", wrappers = [] }, 173 | ] 174 | # Certain crates/versions that will be skipped when doing duplicate detection. 175 | skip = [ 176 | #{ name = "ansi_term", version = "=0.11.0" }, 177 | ] 178 | # Similarly to `skip` allows you to skip certain crates during duplicate 179 | # detection. Unlike skip, it also includes the entire tree of transitive 180 | # dependencies starting at the specified crate, up to a certain depth, which is 181 | # by default infinite 182 | skip-tree = [ 183 | #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, 184 | ] 185 | 186 | # This section is considered when running `cargo deny check sources`. 187 | # More documentation about the 'sources' section can be found here: 188 | # https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html 189 | [sources] 190 | # Lint level for what to happen when a crate from a crate registry that is not 191 | # in the allow list is encountered 192 | unknown-registry = "warn" 193 | # Lint level for what to happen when a crate from a git repository that is not 194 | # in the allow list is encountered 195 | unknown-git = "warn" 196 | # List of URLs for allowed crate registries. Defaults to the crates.io index 197 | # if not specified. If it is specified but empty, no registries are allowed. 198 | allow-registry = ["https://github.com/rust-lang/crates.io-index"] 199 | # List of URLs for allowed Git repositories 200 | allow-git = [] 201 | 202 | [sources.allow-org] 203 | # 1 or more github.com organizations to allow git sources for 204 | github = [] 205 | # 1 or more gitlab.com organizations to allow git sources for 206 | gitlab = [] 207 | # 1 or more bitbucket.org organizations to allow git sources for 208 | bitbucket = [] 209 | -------------------------------------------------------------------------------- /docs/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tubitv/xdiff/7636debb4b40d5a2bc8090d969344753e3330e64/docs/images/screenshot1.png -------------------------------------------------------------------------------- /docs/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tubitv/xdiff/7636debb4b40d5a2bc8090d969344753e3330e64/docs/images/screenshot2.png -------------------------------------------------------------------------------- /docs/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tubitv/xdiff/7636debb4b40d5a2bc8090d969344753e3330e64/docs/images/screenshot3.png -------------------------------------------------------------------------------- /requester/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xreq-lib" 3 | version = "0.4.2" 4 | edition = "2021" 5 | license = "MIT" 6 | documentation = "https://docs.rs/xreq-lib" 7 | repository = "https://github.com/Tubitv/xdiff" 8 | homepage = "https://github.com/Tubitv/xdiff" 9 | description = """ 10 | xreq/xdiff common library. 11 | """ 12 | readme = "../README.md" 13 | keywords = ["cli", "diff"] 14 | categories = ["development-tools"] 15 | 16 | [dependencies] 17 | anyhow = "1.0.94" 18 | console = "0.15.8" 19 | http = "1" 20 | http-serde = "2" 21 | reqwest = { version = "0.12", features = [ 22 | "rustls-tls", 23 | "gzip", 24 | "brotli", 25 | "deflate", 26 | "json", 27 | ], default-features = false } 28 | serde = { version = "1.0.216", features = ["derive"] } 29 | serde_json = "1.0.133" 30 | serde_qs = "0.13" 31 | serde_with = "3" 32 | serde_yaml = "0.9.34" 33 | similar = { version = "2.6.0", features = ["inline", "bytes"] } 34 | tokio = { version = "1.42.0", features = ["full"] } 35 | url = { version = "2.5.4", features = ["serde"] } 36 | -------------------------------------------------------------------------------- /requester/fixtures/diff.yml: -------------------------------------------------------------------------------- 1 | --- 2 | rust: 3 | request1: 4 | url: https://www.rust-lang.org/ 5 | headers: 6 | user-agent: Aloha 7 | params: 8 | hello: world 9 | request2: 10 | method: GET 11 | url: https://www.rust-lang.org/ 12 | params: {} 13 | response: 14 | skip_headers: 15 | - set-cookie 16 | - date 17 | - via 18 | - x-amz-cf-id 19 | todo: 20 | request1: 21 | url: https://jsonplaceholder.typicode.com/todos/1 22 | params: 23 | a: 100 24 | request2: 25 | url: https://jsonplaceholder.typicode.com/todos/2 26 | params: 27 | c: 200 28 | response: 29 | skip_headers: 30 | - report-to 31 | -------------------------------------------------------------------------------- /requester/fixtures/req.yml: -------------------------------------------------------------------------------- 1 | --- 2 | rust: 3 | url: https://www.rust-lang.org/ 4 | post: 5 | url: https://jsonplaceholder.typicode.com/comments 6 | params: 7 | postId: 1 8 | -------------------------------------------------------------------------------- /requester/src/diff.rs: -------------------------------------------------------------------------------- 1 | use crate::req::RequestContext; 2 | use anyhow::Result; 3 | use console::{style, Style}; 4 | use reqwest::Response; 5 | use serde::{Deserialize, Serialize}; 6 | use serde_json::Value; 7 | use similar::{ChangeTag, TextDiff}; 8 | use std::{collections::HashMap, fmt, io::Write, path::Path}; 9 | use tokio::fs; 10 | 11 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 12 | pub struct DiffConfig { 13 | #[serde(flatten)] 14 | ctxs: HashMap, 15 | } 16 | 17 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 18 | pub struct DiffContext { 19 | pub request1: RequestContext, 20 | pub request2: RequestContext, 21 | #[serde(skip_serializing_if = "is_default_response", default)] 22 | pub response: ResponseContext, 23 | } 24 | 25 | fn is_default_response(r: &ResponseContext) -> bool { 26 | r == &ResponseContext::default() 27 | } 28 | 29 | #[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq)] 30 | pub struct ResponseContext { 31 | #[serde(skip_serializing_if = "Vec::is_empty")] 32 | pub skip_headers: Vec, 33 | } 34 | 35 | #[derive(Debug, PartialEq, Eq)] 36 | pub enum DiffResult { 37 | Equal, 38 | Diff(String), 39 | } 40 | 41 | impl ResponseContext { 42 | pub fn new(skip_headers: Vec) -> Self { 43 | Self { skip_headers } 44 | } 45 | } 46 | 47 | struct Line(Option); 48 | 49 | impl fmt::Display for Line { 50 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 51 | match self.0 { 52 | None => write!(f, " "), 53 | Some(idx) => write!(f, "{:<4}", idx + 1), 54 | } 55 | } 56 | } 57 | 58 | impl DiffConfig { 59 | pub fn new_with_profile( 60 | profile: String, 61 | req1: RequestContext, 62 | req2: RequestContext, 63 | res: ResponseContext, 64 | ) -> Self { 65 | let ctx = DiffContext::new(req1, req2, res); 66 | let mut ctxs = HashMap::new(); 67 | ctxs.insert(profile, ctx); 68 | Self { ctxs } 69 | } 70 | 71 | pub async fn try_load(path: impl AsRef) -> Result { 72 | let file = fs::read_to_string(path).await?; 73 | let config: DiffConfig = serde_yaml::from_str(&file)?; 74 | for (profile, ctx) in config.ctxs.iter() { 75 | if !ctx.request1.params.is_object() || !ctx.request2.params.is_object() { 76 | return Err(anyhow::anyhow!( 77 | "params in request1 or request2 must be an object in profile: {}", 78 | profile 79 | )); 80 | } 81 | } 82 | Ok(config) 83 | } 84 | 85 | pub fn get(&self, profile: &str) -> Result<&DiffContext> { 86 | self.ctxs.get(profile).ok_or_else(|| { 87 | anyhow::anyhow!( 88 | "profile {} not found. Available profiles: {:?}.", 89 | profile, 90 | self.ctxs.keys() 91 | ) 92 | }) 93 | } 94 | 95 | pub async fn diff(&self, profile: &str) -> Result { 96 | let ctx = self.get(profile)?; 97 | 98 | ctx.diff().await 99 | } 100 | } 101 | 102 | impl DiffContext { 103 | pub fn new(req1: RequestContext, req2: RequestContext, resp: ResponseContext) -> Self { 104 | Self { 105 | request1: req1, 106 | request2: req2, 107 | response: resp, 108 | } 109 | } 110 | 111 | pub async fn diff(&self) -> Result { 112 | let res1 = self.request1.send().await?; 113 | let res2 = self.request2.send().await?; 114 | 115 | self.diff_response(res1, res2).await 116 | } 117 | 118 | async fn diff_response(&self, res1: Response, res2: Response) -> Result { 119 | let url1 = res1.url().to_string(); 120 | let url2 = res2.url().to_string(); 121 | 122 | let text1 = self.request_to_string(res1).await?; 123 | let text2 = self.request_to_string(res2).await?; 124 | 125 | if text1 != text2 { 126 | let headers = format!("--- a/{}\n+++ b/{}\n", url1, url2); 127 | return Ok(DiffResult::Diff(build_diff(headers, text1, text2)?)); 128 | } 129 | 130 | Ok(DiffResult::Equal) 131 | } 132 | 133 | async fn request_to_string(&self, res: Response) -> Result { 134 | let mut buf = Vec::new(); 135 | 136 | writeln!(&mut buf, "{}", res.status()).unwrap(); 137 | res.headers().iter().for_each(|(k, v)| { 138 | if self.response.skip_headers.iter().any(|v| v == k.as_str()) { 139 | return; 140 | } 141 | writeln!(&mut buf, "{}: {:?}", k, v).unwrap(); 142 | }); 143 | writeln!(&mut buf).unwrap(); 144 | 145 | let mut body = res.text().await?; 146 | 147 | if let Ok(json) = serde_json::from_str::(&body) { 148 | body = serde_json::to_string_pretty(&json)?; 149 | } 150 | 151 | writeln!(&mut buf, "{}", body).unwrap(); 152 | 153 | Ok(String::from_utf8(buf)?) 154 | } 155 | } 156 | 157 | fn build_diff(headers: String, old: String, new: String) -> Result { 158 | let diff = TextDiff::from_lines(&old, &new); 159 | let mut buf = Vec::with_capacity(4096); 160 | writeln!(&mut buf, "{}", headers).unwrap(); 161 | for (idx, group) in diff.grouped_ops(3).iter().enumerate() { 162 | if idx > 0 { 163 | writeln!(&mut buf, "{:-^1$}", "-", 80)?; 164 | } 165 | for op in group { 166 | for change in diff.iter_inline_changes(op) { 167 | let (sign, s) = match change.tag() { 168 | ChangeTag::Delete => ("-", Style::new().red()), 169 | ChangeTag::Insert => ("+", Style::new().green()), 170 | ChangeTag::Equal => (" ", Style::new().dim()), 171 | }; 172 | write!( 173 | &mut buf, 174 | "{}{} |{}", 175 | style(Line(change.old_index())).dim(), 176 | style(Line(change.new_index())).dim(), 177 | s.apply_to(sign).bold(), 178 | )?; 179 | for (emphasized, value) in change.iter_strings_lossy() { 180 | if emphasized { 181 | write!(&mut buf, "{}", s.apply_to(value).underlined().on_black())?; 182 | } else { 183 | write!(&mut buf, "{}", s.apply_to(value))?; 184 | } 185 | } 186 | if change.missing_newline() { 187 | writeln!(&mut buf)?; 188 | } 189 | } 190 | } 191 | } 192 | Ok(String::from_utf8(buf)?) 193 | } 194 | 195 | #[cfg(test)] 196 | mod tests { 197 | use super::*; 198 | 199 | #[tokio::test] 200 | async fn diff_request_should_work() { 201 | let config = DiffConfig::try_load("fixtures/diff.yml").await.unwrap(); 202 | let result = config.diff("rust").await.unwrap(); 203 | assert_eq!(result, DiffResult::Equal); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /requester/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod diff; 2 | mod req; 3 | 4 | pub use diff::{DiffConfig, DiffContext, DiffResult, ResponseContext}; 5 | pub use req::{RequestConfig, RequestContext}; 6 | 7 | // re-exports 8 | pub use reqwest::Response; 9 | 10 | #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] 11 | pub enum KeyValType { 12 | /// if key has no any prefix, it is for query 13 | #[default] 14 | Query, 15 | /// if key starts with '#', it is for header 16 | Header, 17 | /// if key starts with '@', it is for body 18 | Body, 19 | } 20 | 21 | #[derive(Debug, Clone, PartialEq, Eq)] 22 | pub struct KeyVal { 23 | pub kv_type: KeyValType, 24 | pub key: String, 25 | pub val: String, 26 | } 27 | 28 | impl KeyVal { 29 | pub fn new(kv_type: KeyValType, key: impl Into, val: impl Into) -> Self { 30 | Self { 31 | kv_type, 32 | key: key.into(), 33 | val: val.into(), 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /requester/src/req.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, path::Path, str::FromStr}; 2 | 3 | use anyhow::Result; 4 | use http::{header::HeaderName, HeaderMap, HeaderValue, Method}; 5 | use reqwest::{Client, Response}; 6 | use serde::{Deserialize, Serialize}; 7 | use serde_json::Value; 8 | use tokio::fs; 9 | use url::Url; 10 | 11 | use crate::{KeyVal, KeyValType}; 12 | 13 | const USER_AGENT: &str = "Requester/0.1.0"; 14 | 15 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 16 | pub struct RequestConfig { 17 | #[serde(flatten)] 18 | ctxs: HashMap, 19 | } 20 | 21 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 22 | pub struct RequestContext { 23 | #[serde( 24 | with = "http_serde::method", 25 | skip_serializing_if = "is_default", 26 | default 27 | )] 28 | pub method: Method, 29 | pub url: Url, 30 | #[serde(skip_serializing_if = "is_empty_value", default = "default_params")] 31 | pub params: Value, 32 | #[serde(skip_serializing_if = "HeaderMap::is_empty", default)] 33 | #[serde(with = "http_serde::header_map")] 34 | pub headers: HeaderMap, 35 | #[serde(skip_serializing_if = "Option::is_none", default)] 36 | pub body: Option, 37 | #[serde(skip_serializing_if = "Option::is_none", default)] 38 | pub user_agent: Option, 39 | } 40 | 41 | fn is_default(t: &T) -> bool { 42 | t == &T::default() 43 | } 44 | 45 | fn is_empty_value(v: &Value) -> bool { 46 | v.is_null() || (v.is_object() && v.as_object().unwrap().is_empty()) 47 | } 48 | 49 | fn default_params() -> Value { 50 | serde_json::json!({}) 51 | } 52 | 53 | impl RequestConfig { 54 | pub fn new_with_profile(profile: String, ctx: RequestContext) -> Self { 55 | let mut ctxs = HashMap::new(); 56 | ctxs.insert(profile, ctx); 57 | Self { ctxs } 58 | } 59 | 60 | pub async fn try_load(path: impl AsRef) -> Result { 61 | let file = fs::read_to_string(path).await?; 62 | let config: Self = serde_yaml::from_str(&file)?; 63 | for (profile, ctx) in config.ctxs.iter() { 64 | if !ctx.params.is_object() { 65 | return Err(anyhow::anyhow!( 66 | "params must be an object in profile: {}", 67 | profile 68 | )); 69 | } 70 | } 71 | Ok(config) 72 | } 73 | 74 | pub fn get(&self, profile: &str) -> Result<&RequestContext> { 75 | self.ctxs.get(profile).ok_or_else(|| { 76 | anyhow::anyhow!( 77 | "profile {} not found. Available profiles: {:?}.", 78 | profile, 79 | self.ctxs.keys() 80 | ) 81 | }) 82 | } 83 | 84 | pub async fn send(&self, profile: &str) -> Result { 85 | let ctx = self.get(profile)?; 86 | 87 | ctx.send().await 88 | } 89 | } 90 | 91 | impl RequestContext { 92 | pub fn update(&mut self, values: &[KeyVal]) -> Result<()> { 93 | for v in values { 94 | match v.kv_type { 95 | KeyValType::Query => { 96 | self.params[&v.key] = serde_json::Value::String(v.val.to_owned()); 97 | } 98 | KeyValType::Header => { 99 | self.headers.insert( 100 | HeaderName::from_str(&v.key)?, 101 | HeaderValue::from_str(&v.val)?, 102 | ); 103 | } 104 | KeyValType::Body => { 105 | if let Some(body) = self.body.as_mut() { 106 | body[&v.key] = serde_json::Value::String(v.val.to_owned()) 107 | } 108 | } 109 | } 110 | } 111 | 112 | Ok(()) 113 | } 114 | 115 | pub async fn send(&self) -> Result { 116 | let mut url = self.url.clone(); 117 | let user_agent = self 118 | .user_agent 119 | .clone() 120 | .unwrap_or_else(|| USER_AGENT.to_string()); 121 | match url.scheme() { 122 | "http" | "https" => { 123 | let qs = serde_qs::to_string(&self.params)?; 124 | if !qs.is_empty() { 125 | url.set_query(Some(&qs)); 126 | } 127 | let client = Client::builder().user_agent(user_agent).build()?; 128 | 129 | let mut builder = client 130 | .request(self.method.clone(), url) 131 | .headers(self.headers.clone()); 132 | 133 | if let Some(body) = &self.body { 134 | match self.headers.get(http::header::CONTENT_TYPE) { 135 | Some(content_type) => { 136 | if content_type.to_str().unwrap().contains("application/json") { 137 | builder = builder.json(body); 138 | } else { 139 | return Err(anyhow::anyhow!( 140 | "unsupported content-type: {:?}", 141 | content_type 142 | )); 143 | } 144 | } 145 | None => { 146 | // TODO (tchen): here we just assume the content-type is json 147 | builder = builder.json(body) 148 | } 149 | } 150 | builder = builder.body(serde_json::to_string(body)?); 151 | } 152 | 153 | let res = builder.send().await?; 154 | 155 | Ok(res) 156 | } 157 | _ => Err(anyhow::anyhow!("unsupported scheme")), 158 | } 159 | } 160 | } 161 | 162 | impl FromStr for RequestContext { 163 | type Err = anyhow::Error; 164 | 165 | fn from_str(url: &str) -> std::result::Result { 166 | let mut url = Url::parse(url)?; 167 | let qs = url.query_pairs(); 168 | let mut params = serde_json::Value::Object(Default::default()); 169 | for (k, v) in qs { 170 | let v = serde_json::Value::String(v.to_string()); 171 | match params.get_mut(&*k) { 172 | Some(val) => { 173 | if val.is_string() { 174 | params[&*k] = serde_json::Value::Array(vec![val.clone(), v]); 175 | } else if val.is_array() { 176 | val.as_array_mut().unwrap().push(v); 177 | } else { 178 | panic!("unexpected value: {:?}", val); 179 | } 180 | } 181 | None => { 182 | params[&*k] = v; 183 | } 184 | } 185 | } 186 | 187 | url.set_query(None); 188 | Ok(RequestContext { 189 | method: Method::GET, 190 | url, 191 | params, 192 | headers: HeaderMap::new(), 193 | body: None, 194 | user_agent: None, 195 | }) 196 | } 197 | } 198 | 199 | #[cfg(test)] 200 | mod tests { 201 | use super::*; 202 | 203 | #[tokio::test] 204 | async fn send_request_should_work() { 205 | let config = RequestConfig::try_load("fixtures/req.yml").await.unwrap(); 206 | let result = config.send("rust").await.unwrap(); 207 | assert_eq!(result.status(), 200); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /xdiff/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xdiff" 3 | version = "0.4.4" 4 | edition = "2021" 5 | license = "MIT" 6 | documentation = "https://docs.rs/xdiff" 7 | repository = "https://github.com/Tubitv/xdiff" 8 | homepage = "https://github.com/Tubitv/xdiff" 9 | description = """ 10 | A CLI to diff two requests based on predefined profiles. 11 | """ 12 | readme = "../README.md" 13 | keywords = ["cli", "diff"] 14 | categories = ["development-tools"] 15 | 16 | [dependencies] 17 | anyhow = "1.0.94" 18 | atty = "0.2.14" 19 | clap = { version = "4", features = ["derive"] } 20 | dialoguer = { version = "0.11", features = ["history", "completion"] } 21 | tokio = { version = "1.42.0", features = ["full"] } 22 | serde_json = "1.0.133" 23 | serde_yaml = "0.9.34" 24 | 25 | xreq-cli-utils = { version = "0.3.2", path = "../cli-utils" } 26 | xreq-lib = { version = "0.4.2", path = "../requester" } 27 | -------------------------------------------------------------------------------- /xdiff/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use clap::Parser; 3 | use dialoguer::{theme::ColorfulTheme, Input, MultiSelect}; 4 | use std::{io::Write, path::PathBuf}; 5 | use xreq_cli_utils::{get_config_file, get_default_config, parse_key_val, print_syntect}; 6 | use xreq_lib::{DiffConfig, DiffResult, KeyVal, RequestContext, ResponseContext}; 7 | 8 | /// Diff API response. 9 | #[derive(Parser, Debug)] 10 | #[clap(author, version, about, long_about = None)] 11 | struct Args { 12 | #[clap(subcommand)] 13 | action: Action, 14 | } 15 | 16 | #[derive(clap::Subcommand, Debug, Clone)] 17 | enum Action { 18 | /// parse a URL and print the generated diff config. 19 | Parse, 20 | /// diff two API responses based on a given profile. 21 | Run(RunArgs), 22 | } 23 | 24 | #[derive(Parser, Debug, Clone)] 25 | struct RunArgs { 26 | /// API profile to use. 27 | #[clap(short, long, value_parser)] 28 | profile: String, 29 | 30 | /// Extra parameters to pass to the API. 31 | #[clap(short, value_parser = parse_key_val, number_of_values = 1)] 32 | extra_params: Vec, 33 | 34 | /// Path to the config file. 35 | #[clap(short, long, value_parser = get_config_file)] 36 | config: Option, 37 | } 38 | 39 | #[tokio::main] 40 | async fn main() -> Result<()> { 41 | let args = Args::parse(); 42 | 43 | let mut output: Vec = Vec::new(); 44 | 45 | match args.action { 46 | Action::Parse => parse(&mut output).await?, 47 | Action::Run(args) => run(&mut output, args).await?, 48 | } 49 | 50 | let stdout = std::io::stdout(); 51 | let mut stdout = stdout.lock(); 52 | for line in output { 53 | write!(stdout, "{}", line)?; 54 | } 55 | 56 | Ok(()) 57 | } 58 | 59 | async fn parse(output: &mut Vec) -> Result<()> { 60 | let url1: String = Input::with_theme(&ColorfulTheme::default()) 61 | .with_prompt("Url1") 62 | .interact()?; 63 | let url2: String = Input::with_theme(&ColorfulTheme::default()) 64 | .with_prompt("Url2") 65 | .interact()?; 66 | let profile = Input::with_theme(&ColorfulTheme::default()) 67 | .with_prompt("Give this a profile name") 68 | .default("default".into()) 69 | .interact()?; 70 | 71 | let ctx1: RequestContext = url1.parse()?; 72 | let ctx2: RequestContext = url2.parse()?; 73 | 74 | let response = ctx1.send().await?; 75 | let headers = response 76 | .headers() 77 | .iter() 78 | .map(|(k, _)| k.as_str().to_string()) 79 | .collect::>(); 80 | 81 | let chosen: Vec = MultiSelect::with_theme(&ColorfulTheme::default()) 82 | .with_prompt("Select response headers to skip") 83 | .items(&headers) 84 | .interact()?; 85 | 86 | let skip_headers = chosen 87 | .into_iter() 88 | .map(|i| headers[i].clone()) 89 | .collect::>(); 90 | 91 | let res = ResponseContext::new(skip_headers); 92 | let config = DiffConfig::new_with_profile(profile, ctx1, ctx2, res); 93 | 94 | let result = serde_yaml::to_string(&config)?; 95 | 96 | output.push("---\n".to_string()); 97 | print_syntect(output, result, "yaml")?; 98 | Ok(()) 99 | } 100 | 101 | async fn run(output: &mut Vec, args: RunArgs) -> Result<()> { 102 | let config_file = args.config.unwrap_or(get_default_config("xdiff.yml")?); 103 | let diff_config = DiffConfig::try_load(&config_file).await?; 104 | 105 | let mut config = diff_config.get(&args.profile)?.clone(); 106 | 107 | config.request1.update(&args.extra_params)?; 108 | config.request2.update(&args.extra_params)?; 109 | 110 | let result = config.diff().await?; 111 | 112 | match result { 113 | DiffResult::Equal => { 114 | output.push("API responses are equal".into()); 115 | } 116 | DiffResult::Diff(diff) => { 117 | output.push(diff); 118 | } 119 | } 120 | 121 | Ok(()) 122 | } 123 | -------------------------------------------------------------------------------- /xreq/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xreq" 3 | version = "0.4.4" 4 | edition = "2021" 5 | license = "MIT" 6 | documentation = "https://docs.rs/xreq" 7 | repository = "https://github.com/Tubitv/xdiff" 8 | homepage = "https://github.com/Tubitv/xdiff" 9 | description = """ 10 | A CLI to send complicated request based on predefined profiles. 11 | """ 12 | readme = "../README.md" 13 | keywords = ["cli", "http", "curl"] 14 | categories = ["development-tools"] 15 | 16 | [dependencies] 17 | anyhow = "1.0.94" 18 | atty = "0.2.14" 19 | clap = { version = "4", features = ["derive"] } 20 | colored = "2.1.0" 21 | dialoguer = { version = "0.11", features = ["history", "completion"] } 22 | mime = "0.3.17" 23 | serde_json = "1.0.133" 24 | serde_yaml = "0.9.34" 25 | tokio = { version = "1.42.0", features = ["full"] } 26 | 27 | xreq-cli-utils = { version = "0.3.2", path = "../cli-utils" } 28 | xreq-lib = { version = "0.4.2", path = "../requester" } 29 | -------------------------------------------------------------------------------- /xreq/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use clap::Parser; 3 | use colored::Colorize; 4 | use dialoguer::{theme::ColorfulTheme, Input}; 5 | use mime::Mime; 6 | use serde_json::Value; 7 | use std::{io::Write, path::PathBuf}; 8 | 9 | use xreq_cli_utils::{get_config_file, get_default_config, parse_key_val, print_syntect}; 10 | use xreq_lib::{KeyVal, RequestConfig, RequestContext, Response}; 11 | 12 | /// HTTP request tool just as curl/httpie, but easier to use. 13 | #[derive(Parser, Debug)] 14 | #[clap(author, version, about, long_about = None)] 15 | struct Args { 16 | #[clap(subcommand)] 17 | action: Action, 18 | } 19 | 20 | #[derive(clap::Subcommand, Debug, Clone)] 21 | enum Action { 22 | /// parse a URL and print the generated request config. 23 | Parse(ParseArgs), 24 | /// Send API request based on a given profile. 25 | Run(RunArgs), 26 | } 27 | 28 | #[derive(Parser, Debug, Clone)] 29 | struct ParseArgs { 30 | /// Profile name. Defaults to "default". 31 | #[clap(short, long, value_parser, default_value = "default")] 32 | profile: String, 33 | /// URL to parse. 34 | #[clap(value_parser)] 35 | url: Option, 36 | } 37 | 38 | #[derive(Parser, Debug, Clone)] 39 | struct RunArgs { 40 | /// API profile to use. 41 | #[clap(short, long, value_parser)] 42 | profile: String, 43 | 44 | /// Extra parameters to pass to the API. 45 | /// If no prefix, it will be used for querystring; 46 | /// If prefix is '@', it will be used for body; 47 | /// If prefix is '%', it will be used for header. 48 | #[clap(short, value_parser = parse_key_val, number_of_values = 1)] 49 | extra_params: Vec, 50 | 51 | /// Path to the config file. 52 | #[clap(short, long, value_parser = get_config_file)] 53 | config: Option, 54 | } 55 | 56 | #[tokio::main] 57 | async fn main() -> Result<()> { 58 | let args = Args::parse(); 59 | 60 | let mut output: Vec = Vec::new(); 61 | 62 | match args.action { 63 | Action::Parse(args) => parse(&mut output, args)?, 64 | Action::Run(args) => run(&mut output, args).await?, 65 | } 66 | 67 | let stdout = std::io::stdout(); 68 | let mut stdout = stdout.lock(); 69 | for line in output { 70 | write!(stdout, "{}", line)?; 71 | } 72 | 73 | Ok(()) 74 | } 75 | 76 | fn parse(output: &mut Vec, ParseArgs { profile, url }: ParseArgs) -> Result<()> { 77 | let (profile, url) = match url { 78 | Some(url) => (profile, url), 79 | None => { 80 | let url = Input::with_theme(&ColorfulTheme::default()) 81 | .with_prompt("Url to parse") 82 | .interact()?; 83 | let profile = Input::with_theme(&ColorfulTheme::default()) 84 | .with_prompt("Give this url a profile name") 85 | .default("default".into()) 86 | .interact()?; 87 | (profile, url) 88 | } 89 | }; 90 | 91 | let ctx: RequestContext = url.parse()?; 92 | let config = RequestConfig::new_with_profile(profile, ctx); 93 | 94 | let result = serde_yaml::to_string(&config)?; 95 | 96 | output.push("---\n".to_string()); 97 | print_syntect(output, result, "yaml")?; 98 | Ok(()) 99 | } 100 | 101 | async fn run(output: &mut Vec, args: RunArgs) -> Result<()> { 102 | let config_file = args.config.unwrap_or(get_default_config("xreq.yml")?); 103 | 104 | let request_config = RequestConfig::try_load(&config_file).await?; 105 | 106 | let mut config = request_config.get(&args.profile)?.clone(); 107 | 108 | config.update(&args.extra_params)?; 109 | 110 | let resp = config.send().await?; 111 | 112 | if atty::is(atty::Stream::Stdout) { 113 | output.push(format!("Sending: {}\n\n", resp.url())); 114 | print_status(output, &resp); 115 | print_headers(output, &resp); 116 | } 117 | 118 | let mime = get_content_type(&resp); 119 | let body = resp.text().await?; 120 | 121 | print_body(output, mime, body)?; 122 | 123 | Ok(()) 124 | } 125 | 126 | fn print_status(output: &mut Vec, resp: &Response) { 127 | let status = format!("{:?} {}", resp.version(), resp.status()).blue(); 128 | output.push(format!("{}\n", status)); 129 | } 130 | 131 | fn print_headers(output: &mut Vec, resp: &Response) { 132 | for (name, value) in resp.headers() { 133 | output.push(format!("{}: {:?}\n", name.to_string().green(), value)); 134 | } 135 | 136 | output.push("\n".into()); 137 | } 138 | 139 | fn print_body(output: &mut Vec, m: Option, body: String) -> Result<()> { 140 | match m { 141 | Some(v) if v.essence_str() == mime::APPLICATION_JSON => { 142 | let json: Value = serde_json::from_str(&body).unwrap(); 143 | let body = serde_json::to_string_pretty(&json).unwrap(); 144 | print_syntect(output, body, "json") 145 | } 146 | Some(v) if v == mime::TEXT_HTML => print_syntect(output, body, "html"), 147 | 148 | _ => { 149 | output.push(format!("{}\n", body)); 150 | Ok(()) 151 | } 152 | } 153 | } 154 | 155 | fn get_content_type(resp: &Response) -> Option { 156 | resp.headers() 157 | .get("content-type") 158 | .map(|v| v.to_str().unwrap().parse().unwrap()) 159 | } 160 | --------------------------------------------------------------------------------