├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── library ├── Cargo.toml ├── README.md └── src │ ├── availability.rs │ ├── cache.rs │ ├── downloader.rs │ ├── lib.rs │ ├── manifest.rs │ ├── skip_errors.rs │ ├── source.rs │ ├── table.rs │ └── types.rs ├── prepare-pages.sh ├── template.html └── web ├── Cargo.toml └── src ├── config_gen.rs ├── main.rs ├── opts.rs └── tiers_table.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | schedule: 10 | # Runs at 2:00 UTC every day (to give the release process 2h to complete). 11 | - cron: '0 2 * * *' 12 | 13 | env: 14 | CARGO_TERM_COLOR: always 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | with: 24 | fetch-depth: 0 25 | - name: Prepare pages 26 | run: ./prepare-pages.sh 27 | - name: Deploy GitHub Pages 28 | if: ${{ github.event_name != 'pull_request' }} 29 | run: | 30 | git worktree add gh-pages gh-pages 31 | git config user.name "Deploy from CI" 32 | git config user.email "" 33 | cd gh-pages 34 | # Delete the ref to avoid keeping history. 35 | git update-ref -d refs/heads/gh-pages 36 | rm -rf * 37 | mv ../output/* . 38 | git add . 39 | git commit -m "Deploy $GITHUB_SHA to gh-pages" 40 | git push --force 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | */target 3 | **/*.rs.bk 4 | /output 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.20" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "android_system_properties" 16 | version = "0.1.5" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 19 | dependencies = [ 20 | "libc", 21 | ] 22 | 23 | [[package]] 24 | name = "ansi_term" 25 | version = "0.12.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 28 | dependencies = [ 29 | "winapi", 30 | ] 31 | 32 | [[package]] 33 | name = "anyhow" 34 | version = "1.0.68" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" 37 | 38 | [[package]] 39 | name = "atty" 40 | version = "0.2.14" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 43 | dependencies = [ 44 | "hermit-abi 0.1.19", 45 | "libc", 46 | "winapi", 47 | ] 48 | 49 | [[package]] 50 | name = "autocfg" 51 | version = "1.1.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 54 | 55 | [[package]] 56 | name = "base64" 57 | version = "0.21.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" 60 | 61 | [[package]] 62 | name = "bitflags" 63 | version = "1.3.2" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 66 | 67 | [[package]] 68 | name = "block-buffer" 69 | version = "0.10.3" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" 72 | dependencies = [ 73 | "generic-array", 74 | ] 75 | 76 | [[package]] 77 | name = "bumpalo" 78 | version = "3.12.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" 81 | 82 | [[package]] 83 | name = "bytes" 84 | version = "1.4.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 87 | 88 | [[package]] 89 | name = "cc" 90 | version = "1.0.79" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 93 | 94 | [[package]] 95 | name = "cfg-if" 96 | version = "1.0.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 99 | 100 | [[package]] 101 | name = "chrono" 102 | version = "0.4.23" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" 105 | dependencies = [ 106 | "iana-time-zone", 107 | "js-sys", 108 | "num-integer", 109 | "num-traits", 110 | "serde", 111 | "time", 112 | "wasm-bindgen", 113 | "winapi", 114 | ] 115 | 116 | [[package]] 117 | name = "clap" 118 | version = "2.34.0" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 121 | dependencies = [ 122 | "ansi_term", 123 | "atty", 124 | "bitflags", 125 | "strsim", 126 | "textwrap", 127 | "unicode-width", 128 | "vec_map", 129 | ] 130 | 131 | [[package]] 132 | name = "codespan-reporting" 133 | version = "0.11.1" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 136 | dependencies = [ 137 | "termcolor", 138 | "unicode-width", 139 | ] 140 | 141 | [[package]] 142 | name = "core-foundation" 143 | version = "0.9.3" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 146 | dependencies = [ 147 | "core-foundation-sys", 148 | "libc", 149 | ] 150 | 151 | [[package]] 152 | name = "core-foundation-sys" 153 | version = "0.8.3" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 156 | 157 | [[package]] 158 | name = "cpufeatures" 159 | version = "0.2.5" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" 162 | dependencies = [ 163 | "libc", 164 | ] 165 | 166 | [[package]] 167 | name = "crypto-common" 168 | version = "0.1.6" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 171 | dependencies = [ 172 | "generic-array", 173 | "typenum", 174 | ] 175 | 176 | [[package]] 177 | name = "cxx" 178 | version = "1.0.91" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" 181 | dependencies = [ 182 | "cc", 183 | "cxxbridge-flags", 184 | "cxxbridge-macro", 185 | "link-cplusplus", 186 | ] 187 | 188 | [[package]] 189 | name = "cxx-build" 190 | version = "1.0.91" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" 193 | dependencies = [ 194 | "cc", 195 | "codespan-reporting", 196 | "once_cell", 197 | "proc-macro2", 198 | "quote", 199 | "scratch", 200 | "syn", 201 | ] 202 | 203 | [[package]] 204 | name = "cxxbridge-flags" 205 | version = "1.0.91" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" 208 | 209 | [[package]] 210 | name = "cxxbridge-macro" 211 | version = "1.0.91" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" 214 | dependencies = [ 215 | "proc-macro2", 216 | "quote", 217 | "syn", 218 | ] 219 | 220 | [[package]] 221 | name = "digest" 222 | version = "0.10.6" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" 225 | dependencies = [ 226 | "block-buffer", 227 | "crypto-common", 228 | ] 229 | 230 | [[package]] 231 | name = "encoding_rs" 232 | version = "0.8.32" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" 235 | dependencies = [ 236 | "cfg-if", 237 | ] 238 | 239 | [[package]] 240 | name = "env_logger" 241 | version = "0.10.0" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" 244 | dependencies = [ 245 | "humantime", 246 | "is-terminal", 247 | "log", 248 | "regex", 249 | "termcolor", 250 | ] 251 | 252 | [[package]] 253 | name = "errno" 254 | version = "0.2.8" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" 257 | dependencies = [ 258 | "errno-dragonfly", 259 | "libc", 260 | "winapi", 261 | ] 262 | 263 | [[package]] 264 | name = "errno-dragonfly" 265 | version = "0.1.2" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 268 | dependencies = [ 269 | "cc", 270 | "libc", 271 | ] 272 | 273 | [[package]] 274 | name = "fastrand" 275 | version = "1.9.0" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" 278 | dependencies = [ 279 | "instant", 280 | ] 281 | 282 | [[package]] 283 | name = "fnv" 284 | version = "1.0.7" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 287 | 288 | [[package]] 289 | name = "foreign-types" 290 | version = "0.3.2" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 293 | dependencies = [ 294 | "foreign-types-shared", 295 | ] 296 | 297 | [[package]] 298 | name = "foreign-types-shared" 299 | version = "0.1.1" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 302 | 303 | [[package]] 304 | name = "form_urlencoded" 305 | version = "1.1.0" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 308 | dependencies = [ 309 | "percent-encoding", 310 | ] 311 | 312 | [[package]] 313 | name = "futures-channel" 314 | version = "0.3.26" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" 317 | dependencies = [ 318 | "futures-core", 319 | ] 320 | 321 | [[package]] 322 | name = "futures-core" 323 | version = "0.3.26" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" 326 | 327 | [[package]] 328 | name = "futures-io" 329 | version = "0.3.26" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" 332 | 333 | [[package]] 334 | name = "futures-sink" 335 | version = "0.3.26" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" 338 | 339 | [[package]] 340 | name = "futures-task" 341 | version = "0.3.26" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" 344 | 345 | [[package]] 346 | name = "futures-util" 347 | version = "0.3.26" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" 350 | dependencies = [ 351 | "futures-core", 352 | "futures-io", 353 | "futures-task", 354 | "memchr", 355 | "pin-project-lite", 356 | "pin-utils", 357 | "slab", 358 | ] 359 | 360 | [[package]] 361 | name = "generic-array" 362 | version = "0.14.6" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" 365 | dependencies = [ 366 | "typenum", 367 | "version_check", 368 | ] 369 | 370 | [[package]] 371 | name = "h2" 372 | version = "0.3.15" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" 375 | dependencies = [ 376 | "bytes", 377 | "fnv", 378 | "futures-core", 379 | "futures-sink", 380 | "futures-util", 381 | "http", 382 | "indexmap", 383 | "slab", 384 | "tokio", 385 | "tokio-util", 386 | "tracing", 387 | ] 388 | 389 | [[package]] 390 | name = "handlebars" 391 | version = "4.3.6" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" 394 | dependencies = [ 395 | "log", 396 | "pest", 397 | "pest_derive", 398 | "serde", 399 | "serde_json", 400 | "thiserror", 401 | ] 402 | 403 | [[package]] 404 | name = "hashbrown" 405 | version = "0.12.3" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 408 | 409 | [[package]] 410 | name = "heck" 411 | version = "0.3.3" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 414 | dependencies = [ 415 | "unicode-segmentation", 416 | ] 417 | 418 | [[package]] 419 | name = "heck" 420 | version = "0.4.1" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 423 | 424 | [[package]] 425 | name = "hermit-abi" 426 | version = "0.1.19" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 429 | dependencies = [ 430 | "libc", 431 | ] 432 | 433 | [[package]] 434 | name = "hermit-abi" 435 | version = "0.2.6" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 438 | dependencies = [ 439 | "libc", 440 | ] 441 | 442 | [[package]] 443 | name = "hermit-abi" 444 | version = "0.3.1" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 447 | 448 | [[package]] 449 | name = "http" 450 | version = "0.2.9" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" 453 | dependencies = [ 454 | "bytes", 455 | "fnv", 456 | "itoa", 457 | ] 458 | 459 | [[package]] 460 | name = "http-body" 461 | version = "0.4.5" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 464 | dependencies = [ 465 | "bytes", 466 | "http", 467 | "pin-project-lite", 468 | ] 469 | 470 | [[package]] 471 | name = "httparse" 472 | version = "1.8.0" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 475 | 476 | [[package]] 477 | name = "httpdate" 478 | version = "1.0.2" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 481 | 482 | [[package]] 483 | name = "humantime" 484 | version = "2.1.0" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 487 | 488 | [[package]] 489 | name = "hyper" 490 | version = "0.14.24" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" 493 | dependencies = [ 494 | "bytes", 495 | "futures-channel", 496 | "futures-core", 497 | "futures-util", 498 | "h2", 499 | "http", 500 | "http-body", 501 | "httparse", 502 | "httpdate", 503 | "itoa", 504 | "pin-project-lite", 505 | "socket2", 506 | "tokio", 507 | "tower-service", 508 | "tracing", 509 | "want", 510 | ] 511 | 512 | [[package]] 513 | name = "hyper-tls" 514 | version = "0.5.0" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" 517 | dependencies = [ 518 | "bytes", 519 | "hyper", 520 | "native-tls", 521 | "tokio", 522 | "tokio-native-tls", 523 | ] 524 | 525 | [[package]] 526 | name = "iana-time-zone" 527 | version = "0.1.53" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" 530 | dependencies = [ 531 | "android_system_properties", 532 | "core-foundation-sys", 533 | "iana-time-zone-haiku", 534 | "js-sys", 535 | "wasm-bindgen", 536 | "winapi", 537 | ] 538 | 539 | [[package]] 540 | name = "iana-time-zone-haiku" 541 | version = "0.1.1" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" 544 | dependencies = [ 545 | "cxx", 546 | "cxx-build", 547 | ] 548 | 549 | [[package]] 550 | name = "idna" 551 | version = "0.3.0" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" 554 | dependencies = [ 555 | "unicode-bidi", 556 | "unicode-normalization", 557 | ] 558 | 559 | [[package]] 560 | name = "indexmap" 561 | version = "1.9.2" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" 564 | dependencies = [ 565 | "autocfg", 566 | "hashbrown", 567 | ] 568 | 569 | [[package]] 570 | name = "instant" 571 | version = "0.1.12" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 574 | dependencies = [ 575 | "cfg-if", 576 | ] 577 | 578 | [[package]] 579 | name = "io-lifetimes" 580 | version = "1.0.5" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" 583 | dependencies = [ 584 | "libc", 585 | "windows-sys 0.45.0", 586 | ] 587 | 588 | [[package]] 589 | name = "ipnet" 590 | version = "2.7.1" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" 593 | 594 | [[package]] 595 | name = "is-terminal" 596 | version = "0.4.4" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" 599 | dependencies = [ 600 | "hermit-abi 0.3.1", 601 | "io-lifetimes", 602 | "rustix", 603 | "windows-sys 0.45.0", 604 | ] 605 | 606 | [[package]] 607 | name = "itoa" 608 | version = "1.0.5" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" 611 | 612 | [[package]] 613 | name = "js-sys" 614 | version = "0.3.61" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" 617 | dependencies = [ 618 | "wasm-bindgen", 619 | ] 620 | 621 | [[package]] 622 | name = "lazy_static" 623 | version = "1.4.0" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 626 | 627 | [[package]] 628 | name = "libc" 629 | version = "0.2.139" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" 632 | 633 | [[package]] 634 | name = "link-cplusplus" 635 | version = "1.0.8" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" 638 | dependencies = [ 639 | "cc", 640 | ] 641 | 642 | [[package]] 643 | name = "linux-raw-sys" 644 | version = "0.1.4" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" 647 | 648 | [[package]] 649 | name = "log" 650 | version = "0.4.17" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 653 | dependencies = [ 654 | "cfg-if", 655 | "serde", 656 | ] 657 | 658 | [[package]] 659 | name = "memchr" 660 | version = "2.5.0" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 663 | 664 | [[package]] 665 | name = "mime" 666 | version = "0.3.16" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 669 | 670 | [[package]] 671 | name = "mio" 672 | version = "0.8.6" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" 675 | dependencies = [ 676 | "libc", 677 | "log", 678 | "wasi 0.11.0+wasi-snapshot-preview1", 679 | "windows-sys 0.45.0", 680 | ] 681 | 682 | [[package]] 683 | name = "native-tls" 684 | version = "0.2.11" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" 687 | dependencies = [ 688 | "lazy_static", 689 | "libc", 690 | "log", 691 | "openssl", 692 | "openssl-probe", 693 | "openssl-sys", 694 | "schannel", 695 | "security-framework", 696 | "security-framework-sys", 697 | "tempfile", 698 | ] 699 | 700 | [[package]] 701 | name = "num-integer" 702 | version = "0.1.45" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 705 | dependencies = [ 706 | "autocfg", 707 | "num-traits", 708 | ] 709 | 710 | [[package]] 711 | name = "num-traits" 712 | version = "0.2.15" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 715 | dependencies = [ 716 | "autocfg", 717 | ] 718 | 719 | [[package]] 720 | name = "num_cpus" 721 | version = "1.15.0" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 724 | dependencies = [ 725 | "hermit-abi 0.2.6", 726 | "libc", 727 | ] 728 | 729 | [[package]] 730 | name = "once_cell" 731 | version = "1.17.1" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 734 | 735 | [[package]] 736 | name = "openssl" 737 | version = "0.10.45" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" 740 | dependencies = [ 741 | "bitflags", 742 | "cfg-if", 743 | "foreign-types", 744 | "libc", 745 | "once_cell", 746 | "openssl-macros", 747 | "openssl-sys", 748 | ] 749 | 750 | [[package]] 751 | name = "openssl-macros" 752 | version = "0.1.0" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" 755 | dependencies = [ 756 | "proc-macro2", 757 | "quote", 758 | "syn", 759 | ] 760 | 761 | [[package]] 762 | name = "openssl-probe" 763 | version = "0.1.5" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 766 | 767 | [[package]] 768 | name = "openssl-sys" 769 | version = "0.9.80" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" 772 | dependencies = [ 773 | "autocfg", 774 | "cc", 775 | "libc", 776 | "pkg-config", 777 | "vcpkg", 778 | ] 779 | 780 | [[package]] 781 | name = "percent-encoding" 782 | version = "2.2.0" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 785 | 786 | [[package]] 787 | name = "pest" 788 | version = "2.5.1" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0" 791 | dependencies = [ 792 | "thiserror", 793 | "ucd-trie", 794 | ] 795 | 796 | [[package]] 797 | name = "pest_derive" 798 | version = "2.5.1" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "cdc078600d06ff90d4ed238f0119d84ab5d43dbaad278b0e33a8820293b32344" 801 | dependencies = [ 802 | "pest", 803 | "pest_generator", 804 | ] 805 | 806 | [[package]] 807 | name = "pest_generator" 808 | version = "2.5.1" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "28a1af60b1c4148bb269006a750cff8e2ea36aff34d2d96cf7be0b14d1bed23c" 811 | dependencies = [ 812 | "pest", 813 | "pest_meta", 814 | "proc-macro2", 815 | "quote", 816 | "syn", 817 | ] 818 | 819 | [[package]] 820 | name = "pest_meta" 821 | version = "2.5.1" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "fec8605d59fc2ae0c6c1aefc0c7c7a9769732017c0ce07f7a9cfffa7b4404f20" 824 | dependencies = [ 825 | "once_cell", 826 | "pest", 827 | "sha1", 828 | ] 829 | 830 | [[package]] 831 | name = "pin-project-lite" 832 | version = "0.2.9" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 835 | 836 | [[package]] 837 | name = "pin-utils" 838 | version = "0.1.0" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 841 | 842 | [[package]] 843 | name = "pkg-config" 844 | version = "0.3.26" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" 847 | 848 | [[package]] 849 | name = "proc-macro-error" 850 | version = "1.0.4" 851 | source = "registry+https://github.com/rust-lang/crates.io-index" 852 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 853 | dependencies = [ 854 | "proc-macro-error-attr", 855 | "proc-macro2", 856 | "quote", 857 | "syn", 858 | "version_check", 859 | ] 860 | 861 | [[package]] 862 | name = "proc-macro-error-attr" 863 | version = "1.0.4" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 866 | dependencies = [ 867 | "proc-macro2", 868 | "quote", 869 | "version_check", 870 | ] 871 | 872 | [[package]] 873 | name = "proc-macro2" 874 | version = "1.0.51" 875 | source = "registry+https://github.com/rust-lang/crates.io-index" 876 | checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" 877 | dependencies = [ 878 | "unicode-ident", 879 | ] 880 | 881 | [[package]] 882 | name = "quote" 883 | version = "1.0.23" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" 886 | dependencies = [ 887 | "proc-macro2", 888 | ] 889 | 890 | [[package]] 891 | name = "redox_syscall" 892 | version = "0.2.16" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 895 | dependencies = [ 896 | "bitflags", 897 | ] 898 | 899 | [[package]] 900 | name = "regex" 901 | version = "1.7.1" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" 904 | dependencies = [ 905 | "aho-corasick", 906 | "memchr", 907 | "regex-syntax", 908 | ] 909 | 910 | [[package]] 911 | name = "regex-syntax" 912 | version = "0.6.28" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 915 | 916 | [[package]] 917 | name = "reqwest" 918 | version = "0.11.14" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" 921 | dependencies = [ 922 | "base64", 923 | "bytes", 924 | "encoding_rs", 925 | "futures-core", 926 | "futures-util", 927 | "h2", 928 | "http", 929 | "http-body", 930 | "hyper", 931 | "hyper-tls", 932 | "ipnet", 933 | "js-sys", 934 | "log", 935 | "mime", 936 | "native-tls", 937 | "once_cell", 938 | "percent-encoding", 939 | "pin-project-lite", 940 | "serde", 941 | "serde_json", 942 | "serde_urlencoded", 943 | "tokio", 944 | "tokio-native-tls", 945 | "tower-service", 946 | "url", 947 | "wasm-bindgen", 948 | "wasm-bindgen-futures", 949 | "web-sys", 950 | "winreg", 951 | ] 952 | 953 | [[package]] 954 | name = "rustix" 955 | version = "0.36.8" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" 958 | dependencies = [ 959 | "bitflags", 960 | "errno", 961 | "io-lifetimes", 962 | "libc", 963 | "linux-raw-sys", 964 | "windows-sys 0.45.0", 965 | ] 966 | 967 | [[package]] 968 | name = "rustup-available-packages" 969 | version = "0.2.1" 970 | dependencies = [ 971 | "chrono", 972 | "log", 973 | "reqwest", 974 | "serde", 975 | "thiserror", 976 | "toml", 977 | ] 978 | 979 | [[package]] 980 | name = "rustup-available-packages-web" 981 | version = "0.1.3" 982 | dependencies = [ 983 | "anyhow", 984 | "chrono", 985 | "env_logger", 986 | "handlebars", 987 | "log", 988 | "reqwest", 989 | "rustup-available-packages", 990 | "serde", 991 | "serde_json", 992 | "serde_yaml", 993 | "structopt", 994 | "strum", 995 | "tl", 996 | ] 997 | 998 | [[package]] 999 | name = "rustversion" 1000 | version = "1.0.11" 1001 | source = "registry+https://github.com/rust-lang/crates.io-index" 1002 | checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" 1003 | 1004 | [[package]] 1005 | name = "ryu" 1006 | version = "1.0.12" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" 1009 | 1010 | [[package]] 1011 | name = "schannel" 1012 | version = "0.1.21" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" 1015 | dependencies = [ 1016 | "windows-sys 0.42.0", 1017 | ] 1018 | 1019 | [[package]] 1020 | name = "scratch" 1021 | version = "1.0.3" 1022 | source = "registry+https://github.com/rust-lang/crates.io-index" 1023 | checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" 1024 | 1025 | [[package]] 1026 | name = "security-framework" 1027 | version = "2.8.2" 1028 | source = "registry+https://github.com/rust-lang/crates.io-index" 1029 | checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" 1030 | dependencies = [ 1031 | "bitflags", 1032 | "core-foundation", 1033 | "core-foundation-sys", 1034 | "libc", 1035 | "security-framework-sys", 1036 | ] 1037 | 1038 | [[package]] 1039 | name = "security-framework-sys" 1040 | version = "2.8.0" 1041 | source = "registry+https://github.com/rust-lang/crates.io-index" 1042 | checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" 1043 | dependencies = [ 1044 | "core-foundation-sys", 1045 | "libc", 1046 | ] 1047 | 1048 | [[package]] 1049 | name = "serde" 1050 | version = "1.0.152" 1051 | source = "registry+https://github.com/rust-lang/crates.io-index" 1052 | checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" 1053 | dependencies = [ 1054 | "serde_derive", 1055 | ] 1056 | 1057 | [[package]] 1058 | name = "serde_derive" 1059 | version = "1.0.152" 1060 | source = "registry+https://github.com/rust-lang/crates.io-index" 1061 | checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" 1062 | dependencies = [ 1063 | "proc-macro2", 1064 | "quote", 1065 | "syn", 1066 | ] 1067 | 1068 | [[package]] 1069 | name = "serde_json" 1070 | version = "1.0.93" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" 1073 | dependencies = [ 1074 | "itoa", 1075 | "ryu", 1076 | "serde", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "serde_urlencoded" 1081 | version = "0.7.1" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1084 | dependencies = [ 1085 | "form_urlencoded", 1086 | "itoa", 1087 | "ryu", 1088 | "serde", 1089 | ] 1090 | 1091 | [[package]] 1092 | name = "serde_yaml" 1093 | version = "0.9.17" 1094 | source = "registry+https://github.com/rust-lang/crates.io-index" 1095 | checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567" 1096 | dependencies = [ 1097 | "indexmap", 1098 | "itoa", 1099 | "ryu", 1100 | "serde", 1101 | "unsafe-libyaml", 1102 | ] 1103 | 1104 | [[package]] 1105 | name = "sha1" 1106 | version = "0.10.5" 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" 1108 | checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" 1109 | dependencies = [ 1110 | "cfg-if", 1111 | "cpufeatures", 1112 | "digest", 1113 | ] 1114 | 1115 | [[package]] 1116 | name = "slab" 1117 | version = "0.4.8" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" 1120 | dependencies = [ 1121 | "autocfg", 1122 | ] 1123 | 1124 | [[package]] 1125 | name = "socket2" 1126 | version = "0.4.7" 1127 | source = "registry+https://github.com/rust-lang/crates.io-index" 1128 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 1129 | dependencies = [ 1130 | "libc", 1131 | "winapi", 1132 | ] 1133 | 1134 | [[package]] 1135 | name = "strsim" 1136 | version = "0.8.0" 1137 | source = "registry+https://github.com/rust-lang/crates.io-index" 1138 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 1139 | 1140 | [[package]] 1141 | name = "structopt" 1142 | version = "0.3.26" 1143 | source = "registry+https://github.com/rust-lang/crates.io-index" 1144 | checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" 1145 | dependencies = [ 1146 | "clap", 1147 | "lazy_static", 1148 | "structopt-derive", 1149 | ] 1150 | 1151 | [[package]] 1152 | name = "structopt-derive" 1153 | version = "0.4.18" 1154 | source = "registry+https://github.com/rust-lang/crates.io-index" 1155 | checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" 1156 | dependencies = [ 1157 | "heck 0.3.3", 1158 | "proc-macro-error", 1159 | "proc-macro2", 1160 | "quote", 1161 | "syn", 1162 | ] 1163 | 1164 | [[package]] 1165 | name = "strum" 1166 | version = "0.24.1" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" 1169 | dependencies = [ 1170 | "strum_macros", 1171 | ] 1172 | 1173 | [[package]] 1174 | name = "strum_macros" 1175 | version = "0.24.3" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" 1178 | dependencies = [ 1179 | "heck 0.4.1", 1180 | "proc-macro2", 1181 | "quote", 1182 | "rustversion", 1183 | "syn", 1184 | ] 1185 | 1186 | [[package]] 1187 | name = "syn" 1188 | version = "1.0.109" 1189 | source = "registry+https://github.com/rust-lang/crates.io-index" 1190 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1191 | dependencies = [ 1192 | "proc-macro2", 1193 | "quote", 1194 | "unicode-ident", 1195 | ] 1196 | 1197 | [[package]] 1198 | name = "tempfile" 1199 | version = "3.4.0" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" 1202 | dependencies = [ 1203 | "cfg-if", 1204 | "fastrand", 1205 | "redox_syscall", 1206 | "rustix", 1207 | "windows-sys 0.42.0", 1208 | ] 1209 | 1210 | [[package]] 1211 | name = "termcolor" 1212 | version = "1.2.0" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" 1215 | dependencies = [ 1216 | "winapi-util", 1217 | ] 1218 | 1219 | [[package]] 1220 | name = "textwrap" 1221 | version = "0.11.0" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 1224 | dependencies = [ 1225 | "unicode-width", 1226 | ] 1227 | 1228 | [[package]] 1229 | name = "thiserror" 1230 | version = "1.0.38" 1231 | source = "registry+https://github.com/rust-lang/crates.io-index" 1232 | checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" 1233 | dependencies = [ 1234 | "thiserror-impl", 1235 | ] 1236 | 1237 | [[package]] 1238 | name = "thiserror-impl" 1239 | version = "1.0.38" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" 1242 | dependencies = [ 1243 | "proc-macro2", 1244 | "quote", 1245 | "syn", 1246 | ] 1247 | 1248 | [[package]] 1249 | name = "time" 1250 | version = "0.1.45" 1251 | source = "registry+https://github.com/rust-lang/crates.io-index" 1252 | checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" 1253 | dependencies = [ 1254 | "libc", 1255 | "wasi 0.10.0+wasi-snapshot-preview1", 1256 | "winapi", 1257 | ] 1258 | 1259 | [[package]] 1260 | name = "tinyvec" 1261 | version = "1.6.0" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 1264 | dependencies = [ 1265 | "tinyvec_macros", 1266 | ] 1267 | 1268 | [[package]] 1269 | name = "tinyvec_macros" 1270 | version = "0.1.1" 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" 1272 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1273 | 1274 | [[package]] 1275 | name = "tl" 1276 | version = "0.7.7" 1277 | source = "registry+https://github.com/rust-lang/crates.io-index" 1278 | checksum = "d5e993a1c7c32fdf90a308cec4d457f507b2573acc909bd6e7a092321664fdb3" 1279 | 1280 | [[package]] 1281 | name = "tokio" 1282 | version = "1.25.0" 1283 | source = "registry+https://github.com/rust-lang/crates.io-index" 1284 | checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" 1285 | dependencies = [ 1286 | "autocfg", 1287 | "bytes", 1288 | "libc", 1289 | "memchr", 1290 | "mio", 1291 | "num_cpus", 1292 | "pin-project-lite", 1293 | "socket2", 1294 | "windows-sys 0.42.0", 1295 | ] 1296 | 1297 | [[package]] 1298 | name = "tokio-native-tls" 1299 | version = "0.3.1" 1300 | source = "registry+https://github.com/rust-lang/crates.io-index" 1301 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 1302 | dependencies = [ 1303 | "native-tls", 1304 | "tokio", 1305 | ] 1306 | 1307 | [[package]] 1308 | name = "tokio-util" 1309 | version = "0.7.7" 1310 | source = "registry+https://github.com/rust-lang/crates.io-index" 1311 | checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" 1312 | dependencies = [ 1313 | "bytes", 1314 | "futures-core", 1315 | "futures-sink", 1316 | "pin-project-lite", 1317 | "tokio", 1318 | "tracing", 1319 | ] 1320 | 1321 | [[package]] 1322 | name = "toml" 1323 | version = "0.5.11" 1324 | source = "registry+https://github.com/rust-lang/crates.io-index" 1325 | checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" 1326 | dependencies = [ 1327 | "serde", 1328 | ] 1329 | 1330 | [[package]] 1331 | name = "tower-service" 1332 | version = "0.3.2" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 1335 | 1336 | [[package]] 1337 | name = "tracing" 1338 | version = "0.1.37" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 1341 | dependencies = [ 1342 | "cfg-if", 1343 | "pin-project-lite", 1344 | "tracing-core", 1345 | ] 1346 | 1347 | [[package]] 1348 | name = "tracing-core" 1349 | version = "0.1.30" 1350 | source = "registry+https://github.com/rust-lang/crates.io-index" 1351 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 1352 | dependencies = [ 1353 | "once_cell", 1354 | ] 1355 | 1356 | [[package]] 1357 | name = "try-lock" 1358 | version = "0.2.4" 1359 | source = "registry+https://github.com/rust-lang/crates.io-index" 1360 | checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" 1361 | 1362 | [[package]] 1363 | name = "typenum" 1364 | version = "1.16.0" 1365 | source = "registry+https://github.com/rust-lang/crates.io-index" 1366 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" 1367 | 1368 | [[package]] 1369 | name = "ucd-trie" 1370 | version = "0.1.5" 1371 | source = "registry+https://github.com/rust-lang/crates.io-index" 1372 | checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" 1373 | 1374 | [[package]] 1375 | name = "unicode-bidi" 1376 | version = "0.3.10" 1377 | source = "registry+https://github.com/rust-lang/crates.io-index" 1378 | checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" 1379 | 1380 | [[package]] 1381 | name = "unicode-ident" 1382 | version = "1.0.6" 1383 | source = "registry+https://github.com/rust-lang/crates.io-index" 1384 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" 1385 | 1386 | [[package]] 1387 | name = "unicode-normalization" 1388 | version = "0.1.22" 1389 | source = "registry+https://github.com/rust-lang/crates.io-index" 1390 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 1391 | dependencies = [ 1392 | "tinyvec", 1393 | ] 1394 | 1395 | [[package]] 1396 | name = "unicode-segmentation" 1397 | version = "1.10.0" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" 1400 | 1401 | [[package]] 1402 | name = "unicode-width" 1403 | version = "0.1.10" 1404 | source = "registry+https://github.com/rust-lang/crates.io-index" 1405 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 1406 | 1407 | [[package]] 1408 | name = "unsafe-libyaml" 1409 | version = "0.2.5" 1410 | source = "registry+https://github.com/rust-lang/crates.io-index" 1411 | checksum = "bc7ed8ba44ca06be78ea1ad2c3682a43349126c8818054231ee6f4748012aed2" 1412 | 1413 | [[package]] 1414 | name = "url" 1415 | version = "2.3.1" 1416 | source = "registry+https://github.com/rust-lang/crates.io-index" 1417 | checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" 1418 | dependencies = [ 1419 | "form_urlencoded", 1420 | "idna", 1421 | "percent-encoding", 1422 | ] 1423 | 1424 | [[package]] 1425 | name = "vcpkg" 1426 | version = "0.2.15" 1427 | source = "registry+https://github.com/rust-lang/crates.io-index" 1428 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1429 | 1430 | [[package]] 1431 | name = "vec_map" 1432 | version = "0.8.2" 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" 1434 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 1435 | 1436 | [[package]] 1437 | name = "version_check" 1438 | version = "0.9.4" 1439 | source = "registry+https://github.com/rust-lang/crates.io-index" 1440 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1441 | 1442 | [[package]] 1443 | name = "want" 1444 | version = "0.3.0" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1447 | dependencies = [ 1448 | "log", 1449 | "try-lock", 1450 | ] 1451 | 1452 | [[package]] 1453 | name = "wasi" 1454 | version = "0.10.0+wasi-snapshot-preview1" 1455 | source = "registry+https://github.com/rust-lang/crates.io-index" 1456 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1457 | 1458 | [[package]] 1459 | name = "wasi" 1460 | version = "0.11.0+wasi-snapshot-preview1" 1461 | source = "registry+https://github.com/rust-lang/crates.io-index" 1462 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1463 | 1464 | [[package]] 1465 | name = "wasm-bindgen" 1466 | version = "0.2.84" 1467 | source = "registry+https://github.com/rust-lang/crates.io-index" 1468 | checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" 1469 | dependencies = [ 1470 | "cfg-if", 1471 | "wasm-bindgen-macro", 1472 | ] 1473 | 1474 | [[package]] 1475 | name = "wasm-bindgen-backend" 1476 | version = "0.2.84" 1477 | source = "registry+https://github.com/rust-lang/crates.io-index" 1478 | checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" 1479 | dependencies = [ 1480 | "bumpalo", 1481 | "log", 1482 | "once_cell", 1483 | "proc-macro2", 1484 | "quote", 1485 | "syn", 1486 | "wasm-bindgen-shared", 1487 | ] 1488 | 1489 | [[package]] 1490 | name = "wasm-bindgen-futures" 1491 | version = "0.4.34" 1492 | source = "registry+https://github.com/rust-lang/crates.io-index" 1493 | checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" 1494 | dependencies = [ 1495 | "cfg-if", 1496 | "js-sys", 1497 | "wasm-bindgen", 1498 | "web-sys", 1499 | ] 1500 | 1501 | [[package]] 1502 | name = "wasm-bindgen-macro" 1503 | version = "0.2.84" 1504 | source = "registry+https://github.com/rust-lang/crates.io-index" 1505 | checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" 1506 | dependencies = [ 1507 | "quote", 1508 | "wasm-bindgen-macro-support", 1509 | ] 1510 | 1511 | [[package]] 1512 | name = "wasm-bindgen-macro-support" 1513 | version = "0.2.84" 1514 | source = "registry+https://github.com/rust-lang/crates.io-index" 1515 | checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" 1516 | dependencies = [ 1517 | "proc-macro2", 1518 | "quote", 1519 | "syn", 1520 | "wasm-bindgen-backend", 1521 | "wasm-bindgen-shared", 1522 | ] 1523 | 1524 | [[package]] 1525 | name = "wasm-bindgen-shared" 1526 | version = "0.2.84" 1527 | source = "registry+https://github.com/rust-lang/crates.io-index" 1528 | checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" 1529 | 1530 | [[package]] 1531 | name = "web-sys" 1532 | version = "0.3.61" 1533 | source = "registry+https://github.com/rust-lang/crates.io-index" 1534 | checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" 1535 | dependencies = [ 1536 | "js-sys", 1537 | "wasm-bindgen", 1538 | ] 1539 | 1540 | [[package]] 1541 | name = "winapi" 1542 | version = "0.3.9" 1543 | source = "registry+https://github.com/rust-lang/crates.io-index" 1544 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1545 | dependencies = [ 1546 | "winapi-i686-pc-windows-gnu", 1547 | "winapi-x86_64-pc-windows-gnu", 1548 | ] 1549 | 1550 | [[package]] 1551 | name = "winapi-i686-pc-windows-gnu" 1552 | version = "0.4.0" 1553 | source = "registry+https://github.com/rust-lang/crates.io-index" 1554 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1555 | 1556 | [[package]] 1557 | name = "winapi-util" 1558 | version = "0.1.5" 1559 | source = "registry+https://github.com/rust-lang/crates.io-index" 1560 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1561 | dependencies = [ 1562 | "winapi", 1563 | ] 1564 | 1565 | [[package]] 1566 | name = "winapi-x86_64-pc-windows-gnu" 1567 | version = "0.4.0" 1568 | source = "registry+https://github.com/rust-lang/crates.io-index" 1569 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1570 | 1571 | [[package]] 1572 | name = "windows-sys" 1573 | version = "0.42.0" 1574 | source = "registry+https://github.com/rust-lang/crates.io-index" 1575 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 1576 | dependencies = [ 1577 | "windows_aarch64_gnullvm", 1578 | "windows_aarch64_msvc", 1579 | "windows_i686_gnu", 1580 | "windows_i686_msvc", 1581 | "windows_x86_64_gnu", 1582 | "windows_x86_64_gnullvm", 1583 | "windows_x86_64_msvc", 1584 | ] 1585 | 1586 | [[package]] 1587 | name = "windows-sys" 1588 | version = "0.45.0" 1589 | source = "registry+https://github.com/rust-lang/crates.io-index" 1590 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 1591 | dependencies = [ 1592 | "windows-targets", 1593 | ] 1594 | 1595 | [[package]] 1596 | name = "windows-targets" 1597 | version = "0.42.1" 1598 | source = "registry+https://github.com/rust-lang/crates.io-index" 1599 | checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" 1600 | dependencies = [ 1601 | "windows_aarch64_gnullvm", 1602 | "windows_aarch64_msvc", 1603 | "windows_i686_gnu", 1604 | "windows_i686_msvc", 1605 | "windows_x86_64_gnu", 1606 | "windows_x86_64_gnullvm", 1607 | "windows_x86_64_msvc", 1608 | ] 1609 | 1610 | [[package]] 1611 | name = "windows_aarch64_gnullvm" 1612 | version = "0.42.1" 1613 | source = "registry+https://github.com/rust-lang/crates.io-index" 1614 | checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" 1615 | 1616 | [[package]] 1617 | name = "windows_aarch64_msvc" 1618 | version = "0.42.1" 1619 | source = "registry+https://github.com/rust-lang/crates.io-index" 1620 | checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" 1621 | 1622 | [[package]] 1623 | name = "windows_i686_gnu" 1624 | version = "0.42.1" 1625 | source = "registry+https://github.com/rust-lang/crates.io-index" 1626 | checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" 1627 | 1628 | [[package]] 1629 | name = "windows_i686_msvc" 1630 | version = "0.42.1" 1631 | source = "registry+https://github.com/rust-lang/crates.io-index" 1632 | checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" 1633 | 1634 | [[package]] 1635 | name = "windows_x86_64_gnu" 1636 | version = "0.42.1" 1637 | source = "registry+https://github.com/rust-lang/crates.io-index" 1638 | checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" 1639 | 1640 | [[package]] 1641 | name = "windows_x86_64_gnullvm" 1642 | version = "0.42.1" 1643 | source = "registry+https://github.com/rust-lang/crates.io-index" 1644 | checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" 1645 | 1646 | [[package]] 1647 | name = "windows_x86_64_msvc" 1648 | version = "0.42.1" 1649 | source = "registry+https://github.com/rust-lang/crates.io-index" 1650 | checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" 1651 | 1652 | [[package]] 1653 | name = "winreg" 1654 | version = "0.10.1" 1655 | source = "registry+https://github.com/rust-lang/crates.io-index" 1656 | checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" 1657 | dependencies = [ 1658 | "winapi", 1659 | ] 1660 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "library", 4 | "web", 5 | ] 6 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2018] [mexus ] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rustup components availability tool 2 | 3 | A library and some binaries to monitor rustup components availability history 4 | on different platforms. 5 | 6 | ## The library part 7 | 8 | Please refer to [docs.rs](https://docs.rs/rustup-available-packages) for more info on 9 | the library, and to the source code of the binary crate for usage hints. 10 | 11 | ## The Web part 12 | 13 | Under the `web` directory you will find a binary crate that's capable of 14 | producing web-pages like 15 | [https://rust-lang.github.io/rustup-components-history/](https://rust-lang.github.io/rustup-components-history/). 16 | 17 | Machine-readable information on the latest availability can be fetched on a 18 | *per-component-per-target* basis, i.e. 19 | `https://rust-lang.github.io/rustup-components-history/$target/$package` where `$target` stands for 20 | a target host architecture, like `x86_64-unknown-linux-gnu`, and `$package` stands for a package 21 | name, like `rls` or `rust-src`. For example, getting the date when `miri` was available for the last 22 | time on `x86_64-apple-darwin` is as simple as running the following command: 23 | 24 | ``` 25 | $ curl https://rust-lang.github.io/rustup-components-history/x86_64-apple-darwin/miri 26 | 2019-06-08 27 | ``` 28 | 29 | More information (in a JSON format) can be found at a similar location with a `.json` suffix. This 30 | data will include at least the last date the package was available (if it ever was) and whether the 31 | package was available over a configurable range of dates. E.g., 32 | 33 | ``` 34 | $ curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri.json 35 | {"2019-06-13":true,"2019-06-12":true,"2019-06-11":true,"2019-06-10":false,"2019-06-09":true,"2019-06-08":true,"2019-06-07":true,"last_available":"2019-06-13"} 36 | ``` 37 | 38 | Run the binary with a `--help` flag to see available options. 39 | 40 | More info is coming :) 41 | 42 | ### License 43 | 44 | Licensed under either of 45 | 46 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 47 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 48 | 49 | at your option. 50 | 51 | #### Contribution 52 | 53 | Unless you explicitly state otherwise, any contribution intentionally submitted 54 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 55 | additional terms or conditions. 56 | 57 | License: MIT/Apache-2.0 58 | -------------------------------------------------------------------------------- /library/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustup-available-packages" 3 | version = "0.2.1" 4 | description = "Rustup tools state info" 5 | license = "MIT/Apache-2.0" 6 | keywords = ["rustup"] 7 | repository = "https://github.com/rust-lang/rustup-components-history" 8 | documentation = "https://docs.rs/rustup-available-packages/" 9 | readme = "README.md" 10 | edition = "2018" 11 | 12 | [dependencies] 13 | chrono = { version = "0.4.6", features = [ "serde" ] } 14 | thiserror = "1" 15 | log = "0.4.5" 16 | reqwest = { version = "0.11.2", features = ["blocking"] } 17 | serde = { version = "1", features = [ "derive" ] } 18 | toml = "0.5" 19 | -------------------------------------------------------------------------------- /library/README.md: -------------------------------------------------------------------------------- 1 | # rustup-available-packages 2 | 3 | [![Travis CI badge](https://api.travis-ci.com/rust-lang/rustup-components-history.svg?branch=master)](https://travis-ci.com/github/rust-lang/rustup-components-history) 4 | [![crates.io](https://img.shields.io/crates/v/rustup-available-packages.svg)](https://crates.io/crates/rustup-available-packages) 5 | [![docs.rs](https://docs.rs/rustup-available-packages/badge.svg)](https://docs.rs/rustup-available-packages) 6 | 7 | [[Release docs]](https://docs.rs/rustup-available-packages/) 8 | 9 | A library that helps you to find out which packages are available in your **rustup** tool for 10 | specific dates and targets. 11 | 12 | Suggestions and critiques are welcome! 13 | 14 | License: MIT/Apache-2.0 15 | -------------------------------------------------------------------------------- /library/src/availability.rs: -------------------------------------------------------------------------------- 1 | //! Availability evaluation tools. 2 | 3 | use crate::manifest::Manifest; 4 | use chrono::NaiveDate; 5 | use std::{ 6 | borrow::Borrow, 7 | collections::{HashMap, HashSet}, 8 | }; 9 | 10 | type PackageName = String; 11 | type TargetTriple = String; 12 | type DatesSet = HashSet; 13 | type PackagesAvailability = HashMap; 14 | 15 | /// Data about packages availability in rust builds. 16 | #[derive(Debug, Default)] 17 | pub struct AvailabilityData { 18 | data: HashMap, 19 | } 20 | 21 | /// A single row in an availability table. 22 | #[derive(Debug, serde::Serialize)] 23 | pub struct AvailabilityRow<'a> { 24 | /// Name of the package. 25 | pub package_name: &'a str, 26 | /// List of "availabilities". 27 | pub availability_list: Vec, 28 | /// Date when the component has been available for the last time. 29 | pub last_available: Option, 30 | /// A hidden field to improve compatibility. 31 | _hidden: (), 32 | } 33 | 34 | impl AvailabilityData { 35 | /// Adds an availability data from a given [`Manifest`]. 36 | pub fn add_manifest(&mut self, manifest: Manifest) { 37 | let reverse_renames: HashMap<_, _> = manifest 38 | .renames 39 | .iter() 40 | .map(|(key, value)| (&value.to, key)) 41 | .collect(); 42 | for (package_name, info) in manifest.packages { 43 | let package_name = reverse_renames 44 | .get(&package_name) 45 | .map(|name| String::clone(name)) 46 | .unwrap_or(package_name); 47 | for (target_triple, target_info) in info.targets { 48 | if target_info.available { 49 | self.data 50 | .entry(target_triple.clone()) 51 | .or_default() 52 | .entry(package_name.clone()) 53 | .or_default() 54 | .insert(manifest.date); 55 | } 56 | } 57 | } 58 | } 59 | 60 | /// Adds multiple [`Manifest`]s at once. 61 | pub fn add_manifests(&mut self, manifests: impl IntoIterator) { 62 | manifests 63 | .into_iter() 64 | .for_each(|manifest| self.add_manifest(manifest)); 65 | } 66 | 67 | /// Gets a list of targets that have been extracted from manifest files except for the '*' 68 | /// target. 69 | pub fn get_available_targets(&self) -> HashSet<&'_ str> { 70 | self.data 71 | .keys() 72 | .filter(|target| target != &"*") 73 | .map(AsRef::as_ref) 74 | .collect() 75 | } 76 | 77 | /// Returns all available packages throughout all the targets and all the times. 78 | pub fn get_available_packages<'a>(&'a self) -> HashSet<&'a str> { 79 | self.data 80 | .iter() 81 | .flat_map(|(_, per_target)| per_target.keys()) 82 | .map(AsRef::as_ref) 83 | .collect() 84 | } 85 | 86 | /// Makes an iterator that maps given dates to `true` or `false`, depending on whether or not the 87 | /// given package is available on a given moment. 88 | /// 89 | /// Availability is checked against the specified target and against the `*` target. 90 | pub fn get_availability_row<'a, I>( 91 | &self, 92 | target: &str, 93 | pkg: &'a str, 94 | dates: I, 95 | ) -> Option> 96 | where 97 | I: IntoIterator, 98 | I::Item: Borrow, 99 | { 100 | if self.data.get(target).and_then(|t| t.get(pkg)).is_none() { 101 | return None; 102 | } 103 | let available_dates = self.available_dates(target, pkg); 104 | let availability_list = dates 105 | .into_iter() 106 | .map(|date| available_dates.contains(date.borrow())) 107 | .collect(); 108 | Some(AvailabilityRow { 109 | package_name: pkg, 110 | availability_list, 111 | last_available: available_dates.into_iter().max(), 112 | _hidden: (), 113 | }) 114 | } 115 | 116 | /// Retrieves a set of all the dates when a given package was available on a given target. 117 | fn available_dates(&self, target: &str, pkg: &str) -> HashSet { 118 | let available_on_target = self.data.get(target).and_then(|packages| packages.get(pkg)); 119 | let available_on_wildcard = self.data.get("*").and_then(|packages| packages.get(pkg)); 120 | // FIXME cloned -> copied when 1.36 releases. 121 | match (available_on_target, available_on_wildcard) { 122 | (Some(x), Some(y)) => x.union(y).cloned().collect(), 123 | (Some(x), None) | (None, Some(x)) => x.iter().cloned().collect(), 124 | (None, None) => HashSet::new(), 125 | } 126 | } 127 | 128 | /// Finds when a given package was last available on a given target. 129 | pub fn last_available(&self, target: &str, pkg: &str) -> Option { 130 | self.available_dates(target, pkg).into_iter().max() 131 | } 132 | } 133 | 134 | #[cfg(test)] 135 | mod tests { 136 | use super::*; 137 | use crate::manifest::Manifest; 138 | 139 | #[test] 140 | fn check() { 141 | let data = r#"date = "2018-09-03" 142 | [pkg.rust-src.target."*"] 143 | available = true 144 | [pkg.ahaha.target.lol] 145 | available = true 146 | "#; 147 | let parsed_manifest: Manifest = toml::from_str(data).unwrap(); 148 | let mut availability: AvailabilityData = Default::default(); 149 | availability.add_manifest(parsed_manifest); 150 | let all_packages = availability.get_available_packages(); 151 | assert_eq!(2, all_packages.len()); 152 | assert!(all_packages.contains("rust-src")); 153 | assert!(all_packages.contains("ahaha")); 154 | 155 | let all_targets = availability.get_available_targets(); 156 | // The *wildcard* target is ignored here. 157 | assert_eq!(1, all_targets.len()); 158 | assert!(all_targets.contains("lol")); 159 | 160 | let package_exists = availability 161 | .get_availability_row("*", "rust-src", vec![NaiveDate::from_ymd(2018, 9, 3)]) 162 | .unwrap(); 163 | assert_eq!("rust-src", package_exists.package_name); 164 | assert_eq!(vec!(true), package_exists.availability_list); 165 | let package_exists = availability.get_availability_row( 166 | "lol", 167 | "rust-src", 168 | vec![NaiveDate::from_ymd(2018, 9, 3)], 169 | ); 170 | // rust-src is not present in lol target 171 | assert!(package_exists.is_none()); 172 | let package_exists = availability 173 | .get_availability_row("lol", "ahaha", vec![NaiveDate::from_ymd(2018, 9, 3)]) 174 | .unwrap(); 175 | assert_eq!("ahaha", package_exists.package_name); 176 | assert_eq!(vec!(true), package_exists.availability_list); 177 | } 178 | 179 | #[test] 180 | fn check_rename() { 181 | let data = r#"date = "2018-09-03" 182 | [pkg.ahaha.target.lol] 183 | available = true 184 | [renames.kek] 185 | to = "ahaha" 186 | "#; 187 | let parsed_manifest: Manifest = toml::from_str(data).unwrap(); 188 | let mut availability: AvailabilityData = Default::default(); 189 | availability.add_manifest(parsed_manifest); 190 | let all_packages = availability.get_available_packages(); 191 | assert_eq!(1, all_packages.len()); 192 | assert!(all_packages.contains("kek")); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /library/src/cache.rs: -------------------------------------------------------------------------------- 1 | //! Cache downloaded manifests. 2 | 3 | use crate::{manifest::Manifest, Error}; 4 | use chrono::NaiveDate; 5 | use std::{ 6 | fs, 7 | path::{Path, PathBuf}, 8 | }; 9 | 10 | /// A cache that stores manifests on a file system. 11 | pub struct FsCache { 12 | storage_path: Option, 13 | } 14 | 15 | impl FsCache { 16 | /// Initializes a cache with a given path. 17 | /// 18 | /// The path is created if it doesn't exist. 19 | pub fn new(path: impl AsRef) -> Result { 20 | let path = path.as_ref(); 21 | if !path.exists() { 22 | fs::create_dir_all(path) 23 | .map_err(|e| Error::Io(e, format!("creating path {:?}", path)))?; 24 | } 25 | Ok(FsCache { 26 | storage_path: Some(path.into()), 27 | }) 28 | } 29 | 30 | /// Initializes a no-op cache. 31 | pub fn noop() -> Self { 32 | FsCache { storage_path: None } 33 | } 34 | 35 | fn make_file_name(&self, day: NaiveDate) -> PathBuf { 36 | self.storage_path 37 | .as_ref() 38 | .unwrap() 39 | .join(day.format("%Y-%m-%d.toml").to_string()) 40 | } 41 | } 42 | 43 | impl FsCache { 44 | pub(crate) fn get(&self, day: NaiveDate) -> Option { 45 | if self.storage_path.is_none() { 46 | return None; 47 | } 48 | 49 | let file_name = self.make_file_name(day); 50 | if !file_name.exists() { 51 | log::debug!("File {:?} doesn't exist", file_name); 52 | return None; 53 | } 54 | Manifest::load_from_fs(&file_name) 55 | .map_err(|e| log::warn!("Can't load manifest: {}", e)) 56 | .ok() 57 | } 58 | 59 | pub(crate) fn store(&self, manifest: &Manifest) { 60 | if self.storage_path.is_none() { 61 | return; 62 | } 63 | 64 | let file_name = self.make_file_name(manifest.date); 65 | match manifest.save_to_file(&file_name) { 66 | Ok(_) => log::debug!("Manifest stored at {:?}", file_name), 67 | Err(e) => log::warn!("Can't save a manifest to the disk: {}", e), 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /library/src/downloader.rs: -------------------------------------------------------------------------------- 1 | use super::skip_errors::SkipMissingExt; 2 | use crate::{ 3 | cache::FsCache, 4 | manifest::Manifest, 5 | source::{DefaultSource, SourceInfo}, 6 | Error, 7 | }; 8 | use chrono::{Duration, NaiveDate}; 9 | use std::{io, iter}; 10 | 11 | /// Manifests downloader and parser. 12 | pub struct Downloader { 13 | client: reqwest::blocking::Client, 14 | source: S, 15 | cache: FsCache, 16 | skip_missing_days: usize, 17 | } 18 | 19 | impl<'a> Downloader> { 20 | /// Create a new instance of the [`Downloader`] with a [`DefaultSource`]. 21 | pub fn with_default_source(channel: &'a str) -> Self { 22 | Self::new(DefaultSource::new(channel)) 23 | } 24 | } 25 | 26 | impl Downloader { 27 | /// Create a new instance of the [`Downloader`] with a provided instance of [`SourceInfo`]. 28 | pub fn new(source: S) -> Self { 29 | Downloader { 30 | client: reqwest::blocking::Client::new(), 31 | source, 32 | cache: FsCache::noop(), 33 | skip_missing_days: 0, 34 | } 35 | } 36 | } 37 | 38 | impl Downloader 39 | where 40 | S: SourceInfo, 41 | { 42 | /// Sets a cache for the downloader. By default a [`NoopCache`] is used. 43 | pub fn set_cache(self, c: FsCache) -> Downloader { 44 | Downloader { 45 | client: self.client, 46 | source: self.source, 47 | cache: c, 48 | skip_missing_days: self.skip_missing_days, 49 | } 50 | } 51 | 52 | /// Set to non zero if you want to silently skip days for which manifest files are missing. 53 | /// Not more than `skip` days will be skipped. 54 | /// Please not that this setting only affects the [`get_last_manifests`] method. 55 | /// 56 | /// Off (zero) by default. 57 | pub fn skip_missing_days(self, skip: usize) -> Downloader { 58 | Downloader { 59 | client: self.client, 60 | source: self.source, 61 | cache: self.cache, 62 | skip_missing_days: skip, 63 | } 64 | } 65 | 66 | /// Get latest available manifests for given `days`. If `days` is 0 or 1 only the latest 67 | /// manifest is fetched. 68 | /// 69 | /// The returned vector is sorted in descending order of dates. 70 | pub fn get_last_manifests(&self, days: usize) -> Result, Error> { 71 | let latest = self.get_latest_manifest()?; 72 | let latest_day = latest.date; 73 | log::info!("Latest manifest is for {}", latest_day); 74 | let rest = (1..days) 75 | .filter_map(|day| latest_day.checked_sub_signed(Duration::days(day as i64))) 76 | .map(|date| self.get_manifest(date)) 77 | .skip_missing(self.skip_missing_days); 78 | iter::once(Ok(latest)).chain(rest).collect() 79 | } 80 | 81 | /// Gets manifest for a given date. 82 | pub fn get_manifest(&self, day: NaiveDate) -> Result { 83 | if let Some(cached) = self.cache.get(day) { 84 | return Ok(cached); 85 | } 86 | let manifest = self.get_manifest_by_url(self.source.make_manifest_url(day))?; 87 | self.cache.store(&manifest); 88 | Ok(manifest) 89 | } 90 | 91 | /// Gets manifest for a given date. If the `date` is `None`, the latest available manifest is 92 | /// requested. 93 | /// 94 | /// This call is never cached. 95 | pub fn get_latest_manifest(&self) -> Result { 96 | self.get_manifest_by_url(self.source.make_latest_manifest_url()) 97 | } 98 | 99 | /// Fetches a manifest from a given url. 100 | /// 101 | /// This call is never cached. 102 | pub fn get_manifest_by_url(&self, url: impl AsRef) -> Result { 103 | let url = url.as_ref(); 104 | log::info!("Fetching a manifest from {}", url); 105 | let mut response = self 106 | .client 107 | .get(url) 108 | .send() 109 | .map_err(|e| Error::Reqwest(e, url.into()))?; 110 | if !response.status().is_success() { 111 | return Err(Error::BadResponse(response.status(), url.into())); 112 | } 113 | let mut bytes = Vec::new(); 114 | io::copy(&mut response, &mut bytes).map_err(|e| Error::Io(e, url.into()))?; 115 | 116 | toml::from_slice(&bytes).map_err(|e| Error::TomlDe(e, url.to_string())) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /library/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! [![Travis CI badge](https://api.travis-ci.com/rust-lang/rustup-components-history.svg?branch=master)](https://travis-ci.com/github/rust-lang/rustup-components-history) 2 | //! [![crates.io](https://img.shields.io/crates/v/rustup-available-packages.svg)](https://crates.io/crates/rustup-available-packages) 3 | //! [![docs.rs](https://docs.rs/rustup-available-packages/badge.svg)](https://docs.rs/rustup-available-packages) 4 | //! 5 | //! [[Release docs]](https://docs.rs/rustup-available-packages/) 6 | //! 7 | //! A library that helps you to find out which packages are available in your **rustup** tool for 8 | //! specific dates and targets. 9 | //! 10 | //! Suggestions and critiques are welcome! 11 | 12 | #![deny(missing_docs)] 13 | 14 | pub mod availability; 15 | pub mod cache; 16 | mod downloader; 17 | pub mod manifest; 18 | mod skip_errors; 19 | mod source; 20 | pub mod table; 21 | mod types; 22 | 23 | pub use availability::AvailabilityData; 24 | pub use downloader::Downloader; 25 | pub use source::{DefaultSource, SourceInfo}; 26 | use std::io; 27 | 28 | /// An error that might happen inside the library. 29 | #[derive(Debug, thiserror::Error)] 30 | pub enum Error { 31 | /// TOML parsing error. 32 | #[error("TOML deserialization error {0} on manifest {1}")] 33 | TomlDe(#[source] toml::de::Error, String), 34 | 35 | /// TOML serialization error. 36 | #[error("TOML serialization error {0} on manifest {1}")] 37 | TomlSer(#[source] toml::ser::Error, String), 38 | 39 | /// Error in the `reqwest` library. 40 | #[error("reqwest error {0} on url {1}")] 41 | Reqwest(#[source] reqwest::Error, String), 42 | 43 | /// Got a bad HTTP response. 44 | #[error("HTTP error {0} on url {1}")] 45 | BadResponse(reqwest::StatusCode, String), 46 | 47 | /// I/O error. 48 | #[error("I/O error {0} at {1}")] 49 | Io(#[source] io::Error, String), 50 | } 51 | -------------------------------------------------------------------------------- /library/src/manifest.rs: -------------------------------------------------------------------------------- 1 | //! A rustup manifest types. 2 | //! 3 | //! Currently only fields required to get an availability status are implemented, but if you need 4 | //! more please feel free to send a PR. 5 | 6 | use crate::Error; 7 | use chrono::NaiveDate; 8 | use serde::{Deserialize, Serialize}; 9 | use std::{ 10 | collections::HashMap, 11 | fs::File, 12 | io::{Read, Write}, 13 | path::Path, 14 | }; 15 | 16 | /// A rustup manifest. 17 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 18 | #[serde(rename_all = "kebab-case")] 19 | pub struct Manifest { 20 | /// A date for which the manifest is generated. 21 | pub date: NaiveDate, 22 | /// A map of available packages and their targets. 23 | #[serde(rename = "pkg")] 24 | pub packages: HashMap, 25 | /// A map of package "renames". 26 | #[serde(default)] 27 | pub renames: HashMap, 28 | } 29 | 30 | /// Package renaming 31 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 32 | pub struct Rename { 33 | /// New name of the package. 34 | pub to: String, 35 | } 36 | 37 | /// Package info. 38 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 39 | pub struct PackageTargets { 40 | /// Maps targets onto package availability info. 41 | #[serde(rename = "target")] 42 | pub targets: HashMap, 43 | } 44 | 45 | /// A per-target package information. 46 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 47 | pub struct PackageInfo { 48 | /// If a package is available for a specific target. 49 | pub available: bool, 50 | } 51 | 52 | impl Manifest { 53 | /// Tries to load a `Manifest` from the file system. 54 | pub fn load_from_fs(path: impl AsRef) -> Result { 55 | let path = path.as_ref(); 56 | let mut f = File::open(path).map_err(|e| Error::Io(e, format!("opening {:?}", path)))?; 57 | let mut data = String::new(); 58 | f.read_to_string(&mut data) 59 | .map_err(|e| Error::Io(e, format!("reading {:?}", path)))?; 60 | toml::from_str(&data).map_err(|e| Error::TomlDe(e, format!("{:?}", path))) 61 | } 62 | 63 | /// Serializes the `Manifest` to a given path. 64 | pub fn save_to_file(&self, path: impl AsRef) -> Result<(), Error> { 65 | let path = path.as_ref(); 66 | let mut f = File::create(path).map_err(|e| Error::Io(e, format!("creating {:?}", path)))?; 67 | let data = toml::to_vec(self) 68 | .map_err(|e| Error::TomlSer(e, format!("serializing {}", self.date)))?; 69 | f.write_all(&data) 70 | .map_err(|e| Error::Io(e, format!("writing to {:?}", path)))?; 71 | Ok(()) 72 | } 73 | } 74 | 75 | #[cfg(test)] 76 | mod tests { 77 | use super::*; 78 | 79 | #[test] 80 | fn check() { 81 | let data = r#"date = "2018-09-03" 82 | [pkg.cargo.target.aarch64-unknown-linux-gnu] 83 | available = true 84 | 85 | [pkg.cargo.target.arm-unknown-linux-gnueabi] 86 | available = true 87 | 88 | [pkg.cargo.target.arm-unknown-linux-gnueabihf] 89 | available = false 90 | 91 | [pkg.rustfmt-preview.target.aarch64-unknown-linux-gnu] 92 | available = true 93 | 94 | [pkg.rustfmt-preview.target.x86_64-unknown-freebsd] 95 | available = false 96 | 97 | [pkg.rustfmt-preview.target.x86_64-unknown-linux-gnu] 98 | available = true 99 | 100 | [renames.rls] 101 | to = "rls-preview" 102 | 103 | [renames.rustfmt] 104 | to = "rustfmt-preview" 105 | "#; 106 | let parsed_manifest: Manifest = toml::from_str(data).unwrap(); 107 | let reference_manifest = Manifest { 108 | date: NaiveDate::from_ymd(2018, 9, 3), 109 | packages: vec![ 110 | ( 111 | "cargo".to_string(), 112 | PackageTargets { 113 | targets: vec![ 114 | ( 115 | "aarch64-unknown-linux-gnu".to_string(), 116 | PackageInfo { available: true }, 117 | ), 118 | ( 119 | "arm-unknown-linux-gnueabi".to_string(), 120 | PackageInfo { available: true }, 121 | ), 122 | ( 123 | "arm-unknown-linux-gnueabihf".to_string(), 124 | PackageInfo { available: false }, 125 | ), 126 | ] 127 | .into_iter() 128 | .collect(), 129 | }, 130 | ), 131 | ( 132 | "rustfmt-preview".to_string(), 133 | PackageTargets { 134 | targets: vec![ 135 | ( 136 | "aarch64-unknown-linux-gnu".to_string(), 137 | PackageInfo { available: true }, 138 | ), 139 | ( 140 | "x86_64-unknown-freebsd".to_string(), 141 | PackageInfo { available: false }, 142 | ), 143 | ( 144 | "x86_64-unknown-linux-gnu".to_string(), 145 | PackageInfo { available: true }, 146 | ), 147 | ] 148 | .into_iter() 149 | .collect(), 150 | }, 151 | ), 152 | ] 153 | .into_iter() 154 | .collect(), 155 | renames: vec![ 156 | ( 157 | "rls".to_string(), 158 | Rename { 159 | to: "rls-preview".to_string(), 160 | }, 161 | ), 162 | ( 163 | "rustfmt".to_string(), 164 | Rename { 165 | to: "rustfmt-preview".to_string(), 166 | }, 167 | ), 168 | ] 169 | .into_iter() 170 | .collect(), 171 | }; 172 | assert_eq!(reference_manifest, parsed_manifest); 173 | } 174 | 175 | #[test] 176 | fn check_wildcard() { 177 | let data = r#"date = "2018-09-03" 178 | [pkg.rust-src.target."*"] 179 | available = true 180 | url = "https://static.rust-lang.org/dist/2018-09-03/rust-src-nightly.tar.gz" 181 | hash = "79f524d39ccd7fc28be473d1ec35e77ff18322488d455e046c7fe850f2a56b67" 182 | xz_url = "https://static.rust-lang.org/dist/2018-09-03/rust-src-nightly.tar.xz" 183 | xz_hash = "dbb913da0a207ae80c53bc6a42074b637920c2a80121420416579fed3e7f2499" 184 | "#; 185 | let parsed_manifest: Manifest = toml::from_str(data).unwrap(); 186 | let reference_manifest = Manifest { 187 | date: NaiveDate::from_ymd(2018, 9, 3), 188 | packages: vec![( 189 | "rust-src".to_string(), 190 | PackageTargets { 191 | targets: vec![("*".to_string(), PackageInfo { available: true })] 192 | .into_iter() 193 | .collect(), 194 | }, 195 | )] 196 | .into_iter() 197 | .collect(), 198 | renames: vec![].into_iter().collect(), 199 | }; 200 | assert_eq!(reference_manifest, parsed_manifest); 201 | } 202 | 203 | #[test] 204 | fn check_validity() { 205 | let data = r#"manifest-version = "2" 206 | date = "2018-09-03" 207 | [pkg.cargo] 208 | version = "0.30.0-nightly (0ec7281b9 2018-08-20)" 209 | [pkg.cargo.target.aarch64-unknown-linux-gnu] 210 | available = true 211 | url = "https://static.rust-lang.org/dist/2018-09-04/cargo-nightly-aarch64-unknown-linux-gnu.tar.gz" 212 | hash = "15b6e8011af001afb8bc4ec0b951b2b7fcd139f5d83ce82fea7c711c259d217a" 213 | xz_url = "https://static.rust-lang.org/dist/2018-09-04/cargo-nightly-aarch64-unknown-linux-gnu.tar.xz" 214 | xz_hash = "23844e04a62c5fc74a2aebb8e084d6d066deae486f080e0f746eb34148e787f9" 215 | 216 | [pkg.cargo.target.arm-unknown-linux-gnueabi] 217 | available = true 218 | url = "https://static.rust-lang.org/dist/2018-09-04/cargo-nightly-arm-unknown-linux-gnueabi.tar.gz" 219 | hash = "bae7e0f9450b64a42e75db53c3f733ccacc1108393473c64cc13ef79900dbe71" 220 | xz_url = "https://static.rust-lang.org/dist/2018-09-04/cargo-nightly-arm-unknown-linux-gnueabi.tar.xz" 221 | xz_hash = "d9ccfec25501c9a9a247867b711d59886c2913367d16f6ba5887612abb8325d1" 222 | 223 | [pkg.cargo.target.arm-unknown-linux-gnueabihf] 224 | available = true 225 | url = "https://static.rust-lang.org/dist/2018-09-04/cargo-nightly-arm-unknown-linux-gnueabihf.tar.gz" 226 | hash = "62f6cbfa88e7301414a40d2bfcdc77809a3eace1dbbc8c47b839bdaa5756d326" 227 | xz_url = "https://static.rust-lang.org/dist/2018-09-04/cargo-nightly-arm-unknown-linux-gnueabihf.tar.xz" 228 | xz_hash = "ca0e68e7e9827ba6221a40bb17170e8dc6271a9f6991ee5030dcf6acb1a1d8c8" 229 | 230 | [pkg.rustfmt-preview] 231 | version = "0.99.2-nightly (5c9a2b6c1 2018-08-07)" 232 | [pkg.rustfmt-preview.target.aarch64-unknown-linux-gnu] 233 | available = true 234 | url = "https://static.rust-lang.org/dist/2018-09-03/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz" 235 | hash = "cbb44cfb0148ec10335f312a74459c2874b2c2e65c11940cb762f55b75a846e1" 236 | xz_url = "https://static.rust-lang.org/dist/2018-09-03/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz" 237 | xz_hash = "04afbca685b921b5bbbce9bce728430f410d4ebc293407893795acc9054dec2a" 238 | 239 | [pkg.rustfmt-preview.target.x86_64-unknown-freebsd] 240 | available = true 241 | url = "https://static.rust-lang.org/dist/2018-09-03/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz" 242 | hash = "76566ab4d9373b3fe54466984aadebc89f1ce91c79c3e8fd60d559744af5b40c" 243 | xz_url = "https://static.rust-lang.org/dist/2018-09-03/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz" 244 | xz_hash = "6aa97c093f923399b5b00dbc489120b6f1a225eb1e54e2cb3135ec7674aa1d48" 245 | 246 | [pkg.rustfmt-preview.target.x86_64-unknown-linux-gnu] 247 | available = true 248 | url = "https://static.rust-lang.org/dist/2018-09-03/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz" 249 | hash = "74b5d5ff8434c15359eccd15dfc097a1d9c7a3ac44a21718b2998e8cccc347a9" 250 | xz_url = "https://static.rust-lang.org/dist/2018-09-03/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz" 251 | xz_hash = "85c786cfd3f7531a26e004819651da00540e24f83f5d8de0e3ab991730b4cc0d" 252 | "#; 253 | let _manifest: Manifest = toml::from_str(data).unwrap(); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /library/src/skip_errors.rs: -------------------------------------------------------------------------------- 1 | use crate::Error; 2 | 3 | /// An iterator wrapper to skip missing manifests. 4 | pub struct SkipMissing { 5 | inner: I::IntoIter, 6 | to_skip: usize, 7 | } 8 | 9 | impl SkipMissing { 10 | /// Create a wrapper. 11 | pub fn new(inner: I, to_skip: usize) -> Self { 12 | SkipMissing { 13 | inner: inner.into_iter(), 14 | to_skip, 15 | } 16 | } 17 | } 18 | 19 | impl>, T> Iterator for SkipMissing { 20 | type Item = Result; 21 | 22 | fn next(&mut self) -> Option { 23 | loop { 24 | let next = self.inner.next()?; 25 | if self.to_skip == 0 { 26 | break Some(next); 27 | } else { 28 | match next { 29 | Ok(x) => break Some(Ok(x)), 30 | Err(Error::BadResponse(reqwest::StatusCode::NOT_FOUND, url)) => { 31 | log::warn!("Missing a manifest: {}", url); 32 | self.to_skip -= 1; 33 | } 34 | Err(e) => break Some(Err(e)), 35 | } 36 | } 37 | } 38 | } 39 | } 40 | 41 | /// An extension trait that adds a `skip_missing` method for iterators. 42 | pub trait SkipMissingExt: Iterator { 43 | /// Skips 404 HTTP errors, but not more than `days` times. 44 | fn skip_missing(self, days: usize) -> SkipMissing 45 | where 46 | Self: Iterator> + Sized, 47 | { 48 | SkipMissing::new(self, days) 49 | } 50 | } 51 | 52 | impl SkipMissingExt for I {} 53 | -------------------------------------------------------------------------------- /library/src/source.rs: -------------------------------------------------------------------------------- 1 | use chrono::NaiveDate; 2 | use std::borrow::Cow; 3 | 4 | /// A set of methods that we need to retrieve manifest from a source. 5 | pub trait SourceInfo { 6 | /// A type of URLs returned by this trait. 7 | type Url: AsRef; 8 | 9 | /// Makes a URL for a manifest for a specified date. 10 | fn make_manifest_url(&self, _: NaiveDate) -> Self::Url; 11 | 12 | /// Makes a URL for the latest manifest. 13 | fn make_latest_manifest_url(&self) -> Self::Url; 14 | } 15 | 16 | /// Default source, i.e. `https://static.rust-lang.org/...`. 17 | pub struct DefaultSource<'a> { 18 | channel: &'a str, 19 | base_url: Cow<'a, str>, 20 | } 21 | 22 | impl<'a> DefaultSource<'a> { 23 | /// Default base url. 24 | pub const DEFAULT_BASE_URL: &'static str = "https://static.rust-lang.org/dist"; 25 | 26 | /// Initializes a new default source instance for a channel. 27 | pub fn new(channel: &'a str) -> Self { 28 | DefaultSource { 29 | channel, 30 | base_url: Cow::Borrowed(Self::DEFAULT_BASE_URL), 31 | } 32 | } 33 | 34 | /// Overrides the base URL. 35 | pub fn override_base(&mut self, base_url: Cow<'a, str>) { 36 | self.base_url = base_url 37 | } 38 | } 39 | 40 | impl<'a> SourceInfo for DefaultSource<'a> { 41 | type Url = String; 42 | 43 | fn make_manifest_url(&self, date: NaiveDate) -> Self::Url { 44 | format!( 45 | "{}/{}/channel-rust-{}.toml", 46 | self.base_url, date, self.channel 47 | ) 48 | } 49 | 50 | fn make_latest_manifest_url(&self) -> Self::Url { 51 | format!("{}/channel-rust-{}.toml", self.base_url, self.channel) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /library/src/table.rs: -------------------------------------------------------------------------------- 1 | //! A table of statuses. 2 | 3 | use crate::availability::{AvailabilityData, AvailabilityRow}; 4 | use chrono::NaiveDate; 5 | use std::{borrow::Borrow, iter}; 6 | 7 | /// A ready-to-render table of packages statuses per target. 8 | #[derive(Debug, serde::Serialize)] 9 | pub struct Table<'a, Additional: 'a = ()> { 10 | /// A target which the table is built for. 11 | pub current_target: &'a str, 12 | /// Table's title. 13 | pub title: Vec, 14 | /// A list of packages and their availabilities sorted by package name in an ascending order. 15 | pub packages_availability: Vec>, 16 | /// Additional data to render. 17 | pub additional: Additional, 18 | } 19 | 20 | /// Sorts a given container (in a form of an iterator) into a vector of its items in an ascending 21 | /// order. 22 | fn sort(data: impl IntoIterator) -> Vec { 23 | let mut vec: Vec<_> = data.into_iter().collect(); 24 | vec.sort_unstable(); 25 | vec 26 | } 27 | 28 | /// Table builder. 29 | #[derive(Debug, Clone)] 30 | pub struct TableBuilder<'a, Dates = iter::Empty, DateFmt = &'static str, Additional = ()> 31 | { 32 | data: &'a AvailabilityData, 33 | target: &'a str, 34 | dates: Dates, 35 | first_cell: String, 36 | date_fmt: DateFmt, 37 | additional_data: Additional, 38 | } 39 | 40 | impl<'a> TableBuilder<'a> { 41 | /// Initializes a table builder for given data and target. 42 | /// 43 | /// By default list of `dates` is empty (which is probably not what you want), as is the first 44 | /// cell in the table (which is probably what you want). 45 | pub fn default(data: &'a AvailabilityData, target: &'a str) -> Self { 46 | TableBuilder { 47 | data, 48 | target, 49 | dates: iter::empty(), 50 | first_cell: String::new(), 51 | date_fmt: "%Y-%m-%d", 52 | additional_data: (), 53 | } 54 | } 55 | } 56 | 57 | impl<'a, Dates, DateFmt, Additional> TableBuilder<'a, Dates, DateFmt, Additional> { 58 | /// Sets the very first cell in the table (top-left corner). 59 | pub fn first_cell(self, first_cell: &impl ToString) -> Self { 60 | TableBuilder { 61 | first_cell: first_cell.to_string(), 62 | ..self 63 | } 64 | } 65 | 66 | /// Sets the dates range to a given iterator over dates. 67 | /// 68 | /// Please note that the iterator (not your object, but rather the iterator it resolves to) 69 | /// should be cloneable. If you provide a `Vec`, you are on the safe side :) 70 | pub fn dates(self, dates: I) -> TableBuilder<'a, I::IntoIter, DateFmt, Additional> 71 | where 72 | I: IntoIterator, 73 | I::IntoIter: Clone, 74 | I::Item: Borrow, 75 | { 76 | TableBuilder { 77 | data: self.data, 78 | target: self.target, 79 | dates: dates.into_iter(), 80 | first_cell: self.first_cell, 81 | date_fmt: self.date_fmt, 82 | additional_data: self.additional_data, 83 | } 84 | } 85 | 86 | /// Sets a format in which the dates will be formatted. Here's a formatting syntax for your 87 | /// convenience: 88 | /// [chrono::format::strftime](https://docs.rs/chrono/0.4.6/chrono/format/strftime/index.html). 89 | /// 90 | /// The default is `"%Y-%m-%d"`. 91 | pub fn date_format(self, date_fmt: T) -> TableBuilder<'a, Dates, T, Additional> 92 | where 93 | T: AsRef, 94 | { 95 | TableBuilder { 96 | data: self.data, 97 | target: self.target, 98 | dates: self.dates, 99 | first_cell: self.first_cell, 100 | date_fmt, 101 | additional_data: self.additional_data, 102 | } 103 | } 104 | 105 | /// Sets the additional data. 106 | pub fn additional( 107 | self, 108 | data: NewAdditional, 109 | ) -> TableBuilder<'a, Dates, DateFmt, NewAdditional> { 110 | TableBuilder { 111 | data: self.data, 112 | target: self.target, 113 | dates: self.dates, 114 | first_cell: self.first_cell, 115 | date_fmt: self.date_fmt, 116 | additional_data: data, 117 | } 118 | } 119 | 120 | /// Builds a table using all the supplied data. 121 | pub fn build(self) -> Table<'a, Additional> 122 | where 123 | Dates: Iterator + Clone, 124 | Dates::Item: Borrow, 125 | DateFmt: AsRef, 126 | { 127 | Table::new( 128 | self.data, 129 | self.target, 130 | &self.dates, 131 | self.first_cell, 132 | self.date_fmt.as_ref(), 133 | self.additional_data, 134 | ) 135 | } 136 | } 137 | 138 | impl<'a> Table<'a> { 139 | /// Initializes a table builder. 140 | pub fn builder(data: &'a AvailabilityData, target: &'a str) -> TableBuilder<'a> { 141 | TableBuilder::default(data, target) 142 | } 143 | } 144 | 145 | impl<'a, Additional> Table<'a, Additional> { 146 | /// Construct an availability table for a target for specific dates. 147 | fn new( 148 | data: &'a AvailabilityData, 149 | target: &'a str, 150 | dates: &I, 151 | first_cell: String, 152 | date_fmt: &str, 153 | additional_data: Additional, 154 | ) -> Self 155 | where 156 | I: Iterator + Clone, 157 | I::Item: Borrow, 158 | { 159 | let title = iter::once(first_cell) 160 | .chain( 161 | dates 162 | .clone() 163 | .map(|date| date.borrow().format(date_fmt).to_string()), 164 | ) 165 | .collect(); 166 | let packages = sort(data.get_available_packages()); 167 | let availability = packages 168 | .into_iter() 169 | .filter_map(|pkg| data.get_availability_row(target, pkg, dates.clone())) 170 | .collect(); 171 | Table { 172 | current_target: target, 173 | title, 174 | packages_availability: availability, 175 | additional: additional_data, 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /library/src/types.rs: -------------------------------------------------------------------------------- 1 | //! Types that are used internally. 2 | 3 | use std::fmt; 4 | use std::{borrow::Borrow, ops::Deref, rc::Rc}; 5 | 6 | /// Reference-counted build-target triple. 7 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] 8 | pub struct TargetTriple(Rc); 9 | 10 | impl fmt::Display for TargetTriple { 11 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 12 | write!(f, "{}", self.0) 13 | } 14 | } 15 | 16 | impl Deref for TargetTriple { 17 | type Target = str; 18 | 19 | fn deref(&self) -> &str { 20 | &self.0 21 | } 22 | } 23 | 24 | impl Borrow for TargetTriple { 25 | fn borrow(&self) -> &str { 26 | &self.0 27 | } 28 | } 29 | 30 | impl From for TargetTriple 31 | where 32 | Rc: From, 33 | { 34 | fn from(t: T) -> Self { 35 | TargetTriple(t.into()) 36 | } 37 | } 38 | 39 | /// Reference-counted package name. 40 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] 41 | pub struct PackageName(Rc); 42 | 43 | impl Deref for PackageName { 44 | type Target = str; 45 | 46 | fn deref(&self) -> &str { 47 | &self.0 48 | } 49 | } 50 | 51 | impl AsRef for PackageName { 52 | fn as_ref(&self) -> &str { 53 | &self.0 54 | } 55 | } 56 | 57 | impl Borrow for PackageName { 58 | fn borrow(&self) -> &str { 59 | &self.0 60 | } 61 | } 62 | 63 | impl From for PackageName 64 | where 65 | Rc: From, 66 | { 67 | fn from(t: T) -> Self { 68 | PackageName(t.into()) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /prepare-pages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Fail on errors 4 | set -e -u 5 | 6 | # Build the project 7 | cargo build --verbose --all 8 | 9 | # Test everything 10 | cargo test --verbose --all 11 | 12 | # Generate the pages 13 | mkdir -p output 14 | cargo run --release --bin=rustup-available-packages-web -- print_config -c config.yaml 15 | cat config.yaml 16 | cargo run --release --bin=rustup-available-packages-web -- render -c config.yaml 17 | ln -fs x86_64-unknown-linux-gnu.html output/index.html 18 | -------------------------------------------------------------------------------- /template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Rustup packages availability on {{ current_target }} 6 | 12 | 29 | 30 | 31 |
32 |

{{ current_target }}

33 | 34 | 35 | 36 | {{#each title as |t|}} 37 | 38 | {{/each}} 39 | 40 | 41 | 42 | 43 | {{#each packages_availability as |row|}} 44 | 45 | 46 | {{#each row.availability_list as |status|}} 47 | {{#if status}} 48 | 49 | {{else}} 50 | 51 | {{/if}} 52 | {{/each}} 53 | {{#if row.last_available}} 54 | 55 | {{else}} 56 | 57 | {{/if}} 58 | 59 | {{/each}} 60 | 61 |
{{t}}Last available
{{row.package_name}}presentmissing{{row.last_available}}N/A
62 |

Other targets

63 |
64 |
65 | {{#each additional.tiers.tiers_and_targets as |tier_info|}} 66 |
67 |

{{tier_info.0}}

68 |
69 | {{#each tier_info.1 as |target|}} 70 | 71 | {{~#if target.1~}} 72 | {{target.0}} 73 | {{~else~}} 74 | {{target.0~}} 75 | {{/if~}} 76 |
77 | {{/each}} 78 |
79 |
80 | {{/each}} 81 |
82 |
83 | {{#if additional.tiers.unknown_tier}} 84 |

Unknown tier targets

85 |
86 |
87 |
88 |
89 | {{#each additional.tiers.unknown_tier as |target|}} 90 | 91 | {{/each}} 92 |
93 |
94 |
95 |
96 | {{/if}} 97 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /web/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustup-available-packages-web" 3 | version = "0.1.3" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | anyhow = "1" 8 | chrono = "0.4.23" 9 | env_logger = "0.10" 10 | handlebars = "4" 11 | log = { version = "0.4.17", features = ["serde"] } 12 | reqwest = "0.11.14" 13 | rustup-available-packages = { path = "../library" } 14 | serde = { version = "1", features = [ "derive" ] } 15 | serde_json = "1" 16 | serde_yaml = "0.9" 17 | structopt = "0.3" 18 | strum = { version = "0.24.1", features = ["derive"] } 19 | tl = "0.7.7" 20 | -------------------------------------------------------------------------------- /web/src/config_gen.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use anyhow::Context; 4 | use log::LevelFilter; 5 | use strum::IntoEnumIterator; 6 | use tl::{ParserOptions, VDom}; 7 | 8 | use crate::opts::{Config, Html, Tier}; 9 | 10 | pub fn gen_config() -> anyhow::Result { 11 | Ok(Config { 12 | html: Html { 13 | template_path: "template.html".into(), 14 | output_pattern: "output/{{target}}.html".into(), 15 | tiers: gen_tiers()?, 16 | }, 17 | days_in_past: 7, 18 | additional_lookup_days: 22, 19 | channel: "nightly".into(), 20 | verbosity: LevelFilter::Info, 21 | cache_path: Some("/tmp/manifests/".into()), 22 | file_tree_output: "output/".into(), 23 | }) 24 | } 25 | 26 | fn gen_tiers() -> anyhow::Result>> { 27 | let bytes = 28 | reqwest::blocking::get("https://doc.rust-lang.org/nightly/rustc/platform-support.html")? 29 | .text()?; 30 | let html = tl::parse(&bytes, ParserOptions::default())?; 31 | 32 | let mut tiers = HashMap::new(); 33 | for tier in Tier::iter().filter(|t| *t != Tier::UnknownTier) { 34 | tiers.insert(tier, collect_targets_for_tier(&html, tier)?); 35 | } 36 | Ok(tiers) 37 | } 38 | 39 | fn collect_targets_for_tier(html: &VDom, tier: Tier) -> anyhow::Result> { 40 | let mut targets = Vec::new(); 41 | 42 | for table_row in html 43 | .query_selector("tbody") 44 | .unwrap() 45 | .nth(match tier { 46 | Tier::Tier1 => 0, 47 | Tier::Tier2 => 1, 48 | Tier::Tier25 => 2, 49 | Tier::Tier3 => 3, 50 | Tier::UnknownTier => unreachable!(), 51 | }) 52 | .context("Unexpected tier table layout")? 53 | .get(html.parser()) 54 | .unwrap() 55 | .children() 56 | .context("tbody is not a tag")? 57 | .top() 58 | .iter() 59 | { 60 | if let Some(table_row) = table_row.get(html.parser()).unwrap().as_tag() { 61 | if table_row.name() != "tr" { 62 | continue; 63 | } 64 | 65 | targets.push( 66 | table_row 67 | .query_selector(html.parser(), "td") 68 | .unwrap() 69 | .next() 70 | .context("Table row does not have any columns.")? 71 | .get(html.parser()) 72 | .unwrap() 73 | .as_tag() 74 | .context("td is not a tag")? 75 | .query_selector(html.parser(), "code") 76 | .unwrap() 77 | .next() 78 | .context("Table row does not have a code element in its first column")? 79 | .get(html.parser()) 80 | .unwrap() 81 | .inner_text(html.parser()) 82 | .into_owned(), 83 | ); 84 | } 85 | } 86 | 87 | Ok(targets) 88 | } 89 | -------------------------------------------------------------------------------- /web/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | fmt::Display, 4 | fs::{create_dir_all, File}, 5 | io::{self, Write}, 6 | path::{Path, PathBuf}, 7 | }; 8 | 9 | use anyhow::Context; 10 | use chrono::{NaiveDate, Utc}; 11 | use handlebars::{handlebars_helper, Handlebars}; 12 | use opts::Config; 13 | use rustup_available_packages::{cache::FsCache, table::Table, AvailabilityData, Downloader}; 14 | use serde::Serialize; 15 | use structopt::StructOpt; 16 | use tiers_table::TiersTable; 17 | 18 | mod config_gen; 19 | mod opts; 20 | mod tiers_table; 21 | 22 | #[derive(StructOpt)] 23 | #[structopt(about = "Rust tools per-release availability monitor")] 24 | enum CmdOpts { 25 | #[structopt(name = "render", about = "Renders pages using provided configuration")] 26 | Render(ConfigOpt), 27 | #[structopt( 28 | name = "print_config", 29 | about = "Prints the default configuration to stdout" 30 | )] 31 | PrintConfig(PrintConfig), 32 | } 33 | 34 | #[derive(StructOpt)] 35 | struct ConfigOpt { 36 | #[structopt( 37 | short = "c", 38 | long = "config", 39 | help = "Path to a configuration file", 40 | parse(from_os_str) 41 | )] 42 | config_path: PathBuf, 43 | } 44 | 45 | #[derive(StructOpt)] 46 | struct PrintConfig { 47 | #[structopt( 48 | short = "c", 49 | long = "config", 50 | help = "Path to a configuration file", 51 | parse(from_os_str) 52 | )] 53 | config_path: Option, 54 | } 55 | 56 | #[derive(Serialize)] 57 | struct PathRenderData<'a> { 58 | target: &'a str, 59 | } 60 | 61 | #[derive(Serialize)] 62 | struct TiersData<'a> { 63 | tiers: TiersTable<'a>, 64 | datetime: String, 65 | } 66 | 67 | fn generate_html( 68 | data: &AvailabilityData, 69 | dates: &[NaiveDate], 70 | opts::Html { 71 | template_path, 72 | output_pattern, 73 | tiers, 74 | }: opts::Html, 75 | ) -> anyhow::Result<()> { 76 | const TEMPLATE_NAME: &str = "target_info"; 77 | let mut handlebars = Handlebars::new(); 78 | handlebars_helper!(streq: |x: str, y: str| x == y); 79 | handlebars.register_helper("streq", Box::new(streq)); 80 | handlebars.set_strict_mode(true); 81 | handlebars 82 | .register_template_file(TEMPLATE_NAME, &template_path) 83 | .with_context(|| format!("File path: {:?}", &template_path))?; 84 | 85 | let all_targets = data.get_available_targets(); 86 | 87 | let additional = TiersData { 88 | tiers: TiersTable::new(tiers, &all_targets), 89 | datetime: Utc::now().format("%d %b %Y, %H:%M:%S UTC").to_string(), 90 | }; 91 | 92 | for target in &all_targets { 93 | log::info!("Processing target {}", target); 94 | let output_path = handlebars 95 | .render_template(&output_pattern, &PathRenderData { target }) 96 | .with_context(|| format!("Invalid output pattern: {}", &output_pattern))?; 97 | if let Some(parent) = Path::new(&output_path).parent() { 98 | create_dir_all(parent) 99 | .with_context(|| format!("Can't create path {}", parent.display()))?; 100 | } 101 | log::info!("Preparing file {}", output_path); 102 | let out = File::create(&output_path) 103 | .with_context(|| format!("Can't create file [{}]", output_path))?; 104 | 105 | let table = Table::builder(&data, target) 106 | .dates(dates) 107 | .additional(&additional) 108 | .build(); 109 | 110 | log::info!("Writing target {} to {:?}", target, output_path); 111 | handlebars 112 | .render_to_write(TEMPLATE_NAME, &table, out) 113 | .with_context(|| format!("Can't render [{:?}] for [{}]", template_path, target))?; 114 | } 115 | Ok(()) 116 | } 117 | 118 | /// Saves a list of packages to a json file. 119 | fn packages_json( 120 | pkgs: impl IntoIterator, 121 | path: impl AsRef, 122 | ) -> io::Result<()> { 123 | let contents = serde_json::to_vec( 124 | &pkgs 125 | .into_iter() 126 | .map(|p| p.to_string()) 127 | .collect::>(), 128 | )?; 129 | std::fs::write(path, contents) 130 | } 131 | 132 | fn generate_fs_tree( 133 | data: &AvailabilityData, 134 | dates: &[NaiveDate], 135 | output: &Path, 136 | ) -> anyhow::Result<()> { 137 | let targets = data.get_available_targets(); 138 | let pkgs = data.get_available_packages(); 139 | 140 | packages_json(&pkgs, output.join("packages.json")).with_context(|| "packages.json")?; 141 | 142 | for target in targets { 143 | let target_path = output.join(target); 144 | create_dir_all(&target_path) 145 | .with_context(|| format!("Can't create path {}", target_path.display()))?; 146 | 147 | for pkg in &pkgs { 148 | let row = if let Some(row) = data.get_availability_row(target, pkg, dates) { 149 | row 150 | } else { 151 | continue; 152 | }; 153 | if let Some(date) = row.last_available { 154 | let path = target_path.join(pkg); 155 | let mut f = File::create(&path) 156 | .with_context(|| format!("Can't create file {}", path.display()))?; 157 | writeln!(f, "{}", date.format("%Y-%m-%d"))?; 158 | } else { 159 | // If a package is not available, don't create a file for it at 160 | // all. 161 | } 162 | 163 | // This should always be true, but better to output nothing than to panic 164 | // or output corrupt data. 165 | if dates.len() == row.availability_list.len() { 166 | let path = target_path.join(&format!("{}.json", pkg)); 167 | 168 | let contents = serde_json::to_vec_pretty(&TargetPkg { 169 | availability: dates 170 | .iter() 171 | .zip(row.availability_list.iter()) 172 | .map(|(date, avail)| (date.format("%Y-%m-%d").to_string(), *avail)) 173 | .collect(), 174 | last_available: row.last_available.map(|d| d.format("%Y-%m-%d").to_string()), 175 | })?; 176 | std::fs::write(&path, contents) 177 | .with_context(|| format!("Can't write file {}", path.display()))?; 178 | } 179 | } 180 | } 181 | Ok(()) 182 | } 183 | 184 | #[derive(serde::Serialize)] 185 | struct TargetPkg { 186 | #[serde(flatten)] 187 | availability: HashMap, 188 | last_available: Option, 189 | } 190 | 191 | fn main() -> anyhow::Result<()> { 192 | let cmd_opts = CmdOpts::from_args(); 193 | let config = match cmd_opts { 194 | CmdOpts::Render(cmd_opts) => Config::load(&cmd_opts.config_path) 195 | .with_context(|| format!("Can't load config {:?}", cmd_opts.config_path))?, 196 | CmdOpts::PrintConfig(PrintConfig { config_path }) => { 197 | const HEADER: &str = "\ 198 | # DO NOT EDIT MANUALLY 199 | # 200 | # This file was auto-generated by the print_config command: 201 | # $ cargo r -p rustup-available-packages-web -- print_config -c config.yaml 202 | "; 203 | 204 | let config = config_gen::gen_config()?; 205 | if let Some(path) = config_path { 206 | if let Some(parent) = path.parent() { 207 | create_dir_all(parent)?; 208 | } 209 | 210 | let f = File::create(&path)?; 211 | writeln!(&f, "{HEADER}")?; 212 | serde_yaml::to_writer(&f, &config) 213 | .with_context(|| format!("Failed to write config {path:?}"))?; 214 | } else { 215 | let mut out = io::stdout().lock(); 216 | writeln!(out, "{HEADER}")?; 217 | serde_yaml::to_writer(out, &config)?; 218 | } 219 | return Ok(()); 220 | } 221 | }; 222 | env_logger::Builder::from_default_env() 223 | .filter_level(config.verbosity) 224 | .init(); 225 | 226 | let mut data: AvailabilityData = Default::default(); 227 | let cache = if let Some(cache_path) = config.cache_path.as_ref() { 228 | FsCache::new(cache_path).with_context(|| "Can't initialize cache")? 229 | } else { 230 | FsCache::noop() 231 | }; 232 | let downloader = Downloader::with_default_source(&config.channel) 233 | .set_cache(cache) 234 | .skip_missing_days(7); 235 | let manifests = 236 | downloader.get_last_manifests(config.days_in_past + config.additional_lookup_days)?; 237 | let dates: Vec<_> = manifests 238 | .iter() 239 | .map(|manifest| manifest.date) 240 | .take(config.days_in_past) 241 | .collect(); 242 | data.add_manifests(manifests); 243 | log::info!("Available targets: {:?}", data.get_available_targets()); 244 | log::info!("Available packages: {:?}", data.get_available_packages()); 245 | 246 | generate_html(&data, &dates, config.html)?; 247 | generate_fs_tree(&data, &dates, &config.file_tree_output)?; 248 | 249 | Ok(()) 250 | } 251 | -------------------------------------------------------------------------------- /web/src/opts.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::{BTreeMap, HashMap}, 3 | fs::File, 4 | path::{Path, PathBuf}, 5 | }; 6 | 7 | use log::LevelFilter; 8 | use serde::{Deserialize, Serialize, Serializer}; 9 | use strum::EnumIter; 10 | 11 | /// Support tiers: https://doc.rust-lang.org/nightly/rustc/platform-support.html. 12 | #[derive( 13 | Debug, Serialize, Deserialize, Hash, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, EnumIter, 14 | )] 15 | pub enum Tier { 16 | /// Tier 1 platforms. 17 | #[serde(rename = "Tier 1")] 18 | Tier1, 19 | /// Tier 2 platforms. 20 | #[serde(rename = "Tier 2")] 21 | Tier2, 22 | /// Tier 2.5 platforms. 23 | #[serde(rename = "Tier 2.5")] 24 | Tier25, 25 | /// Tier 3 platforms. 26 | #[serde(rename = "Tier 3")] 27 | Tier3, 28 | #[doc(hidden)] 29 | UnknownTier, 30 | } 31 | 32 | fn default_verbosity() -> LevelFilter { 33 | LevelFilter::Warn 34 | } 35 | 36 | fn default_channel() -> String { 37 | String::from("nightly") 38 | } 39 | 40 | fn default_additional_days() -> usize { 41 | 0 42 | } 43 | 44 | #[derive(Debug, Serialize, Deserialize)] 45 | pub struct Config { 46 | #[serde(flatten)] 47 | pub html: Html, 48 | /// For how many days in the past would you like to peek. 49 | pub days_in_past: usize, 50 | /// For how many additional days should we look into to calculate "the last 51 | /// available" date. 52 | #[serde(default = "default_additional_days")] 53 | pub additional_lookup_days: usize, 54 | /// A release channel to check. 55 | /// 56 | /// If omitted, the default channel is nightly. 57 | #[serde(default = "default_channel")] 58 | pub channel: String, 59 | /// Verbosity level, might be one of the following: OFF, ERROR, WARN, INFO, 60 | /// DEBUG or TRACE. 61 | /// 62 | /// If omitted, the default level is WARN. 63 | #[serde(default = "default_verbosity")] 64 | pub verbosity: LevelFilter, 65 | /// A path where to store the downloaded manifests. 66 | /// 67 | /// If omitted, no cache will be used, i.e. all the manifests will be 68 | /// re-downloaded every time you run the tool. 69 | #[serde(default)] 70 | pub cache_path: Option, 71 | ///A path where a file tree of available packages will be created. The tool 72 | /// will generate a set of files under a given *output* directory with the 73 | /// following pattern: file_tree_output/$target/$package, where $target 74 | /// stands for a target host architecture, like x86_64-unknown-linux-gnu, 75 | /// and $package stands for a package name, like rls or rust-src. Each of 76 | /// those files will contain a date in a "%Y-%m-%d" format (e.g. 2019-12-24) 77 | /// which represents the latest date when the package was (is) available for 78 | /// that specific target. 79 | pub file_tree_output: PathBuf, 80 | } 81 | 82 | /// Html-related configuration 83 | #[derive(Debug, Serialize, Deserialize)] 84 | pub struct Html { 85 | /// Path to a handlebars template file. 86 | pub template_path: PathBuf, 87 | /// A pattern that will be used to render output files. Any instance of a 88 | /// `{{target}}` will be replaced with a target name. 89 | pub output_pattern: String, 90 | /// Platform tiers lists. 91 | #[serde(default)] 92 | #[serde(serialize_with = "ordered_map")] 93 | pub tiers: HashMap>, 94 | } 95 | 96 | fn ordered_map( 97 | value: &HashMap, 98 | serializer: S, 99 | ) -> Result { 100 | value 101 | .iter() 102 | .collect::>() 103 | .serialize(serializer) 104 | } 105 | 106 | impl Config { 107 | pub fn load(path: impl AsRef) -> anyhow::Result { 108 | let path = path.as_ref(); 109 | let f = File::open(path)?; 110 | Ok(serde_yaml::from_reader(f)?) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /web/src/tiers_table.rs: -------------------------------------------------------------------------------- 1 | use crate::opts::Tier; 2 | use std::{ 3 | borrow::Cow, 4 | collections::{HashMap, HashSet}, 5 | }; 6 | 7 | #[derive(serde::Serialize)] 8 | pub struct TiersTable<'a> { 9 | /// A list of tier names and their targets. 10 | tiers_and_targets: Vec<(Tier, Vec<(String, bool)>)>, 11 | unknown_tier: Vec>, 12 | } 13 | 14 | fn inverse_tiers_map(map: &HashMap>) -> HashMap<&str, Tier> { 15 | map.iter() 16 | .flat_map(|(tier, targets)| targets.iter().map(move |target| (target as &str, *tier))) 17 | .collect() 18 | } 19 | 20 | fn find_unknown<'a>( 21 | tiers: &HashMap>, 22 | targets: &HashSet<&'a str>, 23 | ) -> Vec> { 24 | let inversed_tiers = inverse_tiers_map(&tiers); 25 | let not_listed = targets.iter().filter_map(|&target| { 26 | let tier = inversed_tiers 27 | .get(target) 28 | .cloned() 29 | .unwrap_or(Tier::UnknownTier); 30 | if tier == Tier::UnknownTier { 31 | Some(Cow::Borrowed(target)) 32 | } else { 33 | None 34 | } 35 | }); 36 | let unknown = tiers 37 | .get(&Tier::UnknownTier) 38 | .into_iter() 39 | .flat_map(|targets| targets.iter()) 40 | .map(ToString::to_string) 41 | .map(Cow::Owned); 42 | let mut result: Vec<_> = not_listed.chain(unknown).collect(); 43 | result.sort_unstable(); 44 | result 45 | } 46 | 47 | impl<'a> TiersTable<'a> { 48 | pub fn new(tiers: HashMap>, targets: &HashSet<&'a str>) -> Self { 49 | let unknown = find_unknown(&tiers, targets); 50 | let mut v: Vec<_> = tiers 51 | .into_iter() 52 | .filter(|(tier, _)| tier != &Tier::UnknownTier) 53 | .map(|(tier, mut tier_targets)| { 54 | tier_targets.sort_unstable(); 55 | let tier_targets = tier_targets 56 | .into_iter() 57 | .map(|target| { 58 | let contains = targets.contains(&target as &str); 59 | (target, contains) 60 | }) 61 | .collect(); 62 | (tier, tier_targets) 63 | }) 64 | .collect(); 65 | v.sort_unstable(); 66 | TiersTable { 67 | tiers_and_targets: v, 68 | unknown_tier: unknown, 69 | } 70 | } 71 | } 72 | --------------------------------------------------------------------------------