├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── demo.gif └── src ├── lib.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.json 3 | -------------------------------------------------------------------------------- /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.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "atty" 16 | version = "0.2.14" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 19 | dependencies = [ 20 | "hermit-abi", 21 | "libc", 22 | "winapi", 23 | ] 24 | 25 | [[package]] 26 | name = "autocfg" 27 | version = "1.0.1" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 30 | 31 | [[package]] 32 | name = "base64" 33 | version = "0.13.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 36 | 37 | [[package]] 38 | name = "bitflags" 39 | version = "1.3.2" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 42 | 43 | [[package]] 44 | name = "bumpalo" 45 | version = "3.7.0" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" 48 | 49 | [[package]] 50 | name = "bytes" 51 | version = "1.1.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" 54 | 55 | [[package]] 56 | name = "cc" 57 | version = "1.0.70" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" 60 | 61 | [[package]] 62 | name = "cfg-if" 63 | version = "1.0.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 66 | 67 | [[package]] 68 | name = "chrono" 69 | version = "0.4.19" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 72 | dependencies = [ 73 | "libc", 74 | "num-integer", 75 | "num-traits", 76 | "serde", 77 | "time", 78 | "winapi", 79 | ] 80 | 81 | [[package]] 82 | name = "clap" 83 | version = "3.0.0-beta.4" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "fcd70aa5597dbc42f7217a543f9ef2768b2ef823ba29036072d30e1d88e98406" 86 | dependencies = [ 87 | "atty", 88 | "bitflags", 89 | "clap_derive", 90 | "indexmap", 91 | "lazy_static", 92 | "os_str_bytes", 93 | "strsim", 94 | "termcolor", 95 | "textwrap", 96 | "vec_map", 97 | ] 98 | 99 | [[package]] 100 | name = "clap_derive" 101 | version = "3.0.0-beta.4" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "0b5bb0d655624a0b8770d1c178fb8ffcb1f91cc722cb08f451e3dc72465421ac" 104 | dependencies = [ 105 | "heck", 106 | "proc-macro-error", 107 | "proc-macro2", 108 | "quote", 109 | "syn", 110 | ] 111 | 112 | [[package]] 113 | name = "console" 114 | version = "0.14.1" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" 117 | dependencies = [ 118 | "encode_unicode", 119 | "lazy_static", 120 | "libc", 121 | "terminal_size", 122 | "winapi", 123 | ] 124 | 125 | [[package]] 126 | name = "core-foundation" 127 | version = "0.9.1" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" 130 | dependencies = [ 131 | "core-foundation-sys", 132 | "libc", 133 | ] 134 | 135 | [[package]] 136 | name = "core-foundation-sys" 137 | version = "0.8.2" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" 140 | 141 | [[package]] 142 | name = "crossbeam-channel" 143 | version = "0.5.1" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" 146 | dependencies = [ 147 | "cfg-if", 148 | "crossbeam-utils", 149 | ] 150 | 151 | [[package]] 152 | name = "crossbeam-utils" 153 | version = "0.8.5" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" 156 | dependencies = [ 157 | "cfg-if", 158 | "lazy_static", 159 | ] 160 | 161 | [[package]] 162 | name = "encode_unicode" 163 | version = "0.3.6" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 166 | 167 | [[package]] 168 | name = "encoding_rs" 169 | version = "0.8.28" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" 172 | dependencies = [ 173 | "cfg-if", 174 | ] 175 | 176 | [[package]] 177 | name = "fnv" 178 | version = "1.0.7" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 181 | 182 | [[package]] 183 | name = "foreign-types" 184 | version = "0.3.2" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 187 | dependencies = [ 188 | "foreign-types-shared", 189 | ] 190 | 191 | [[package]] 192 | name = "foreign-types-shared" 193 | version = "0.1.1" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 196 | 197 | [[package]] 198 | name = "form_urlencoded" 199 | version = "1.0.1" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 202 | dependencies = [ 203 | "matches", 204 | "percent-encoding", 205 | ] 206 | 207 | [[package]] 208 | name = "futures-channel" 209 | version = "0.3.17" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" 212 | dependencies = [ 213 | "futures-core", 214 | ] 215 | 216 | [[package]] 217 | name = "futures-core" 218 | version = "0.3.17" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" 221 | 222 | [[package]] 223 | name = "futures-sink" 224 | version = "0.3.17" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" 227 | 228 | [[package]] 229 | name = "futures-task" 230 | version = "0.3.17" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" 233 | 234 | [[package]] 235 | name = "futures-util" 236 | version = "0.3.17" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" 239 | dependencies = [ 240 | "autocfg", 241 | "futures-core", 242 | "futures-task", 243 | "pin-project-lite", 244 | "pin-utils", 245 | ] 246 | 247 | [[package]] 248 | name = "getrandom" 249 | version = "0.2.3" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 252 | dependencies = [ 253 | "cfg-if", 254 | "libc", 255 | "wasi", 256 | ] 257 | 258 | [[package]] 259 | name = "h2" 260 | version = "0.3.4" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "d7f3675cfef6a30c8031cf9e6493ebdc3bb3272a3fea3923c4210d1830e6a472" 263 | dependencies = [ 264 | "bytes", 265 | "fnv", 266 | "futures-core", 267 | "futures-sink", 268 | "futures-util", 269 | "http", 270 | "indexmap", 271 | "slab", 272 | "tokio", 273 | "tokio-util", 274 | "tracing", 275 | ] 276 | 277 | [[package]] 278 | name = "hashbrown" 279 | version = "0.11.2" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 282 | 283 | [[package]] 284 | name = "heck" 285 | version = "0.3.3" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 288 | dependencies = [ 289 | "unicode-segmentation", 290 | ] 291 | 292 | [[package]] 293 | name = "hermit-abi" 294 | version = "0.1.19" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 297 | dependencies = [ 298 | "libc", 299 | ] 300 | 301 | [[package]] 302 | name = "http" 303 | version = "0.2.4" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" 306 | dependencies = [ 307 | "bytes", 308 | "fnv", 309 | "itoa", 310 | ] 311 | 312 | [[package]] 313 | name = "http-body" 314 | version = "0.4.3" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5" 317 | dependencies = [ 318 | "bytes", 319 | "http", 320 | "pin-project-lite", 321 | ] 322 | 323 | [[package]] 324 | name = "httparse" 325 | version = "1.5.1" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" 328 | 329 | [[package]] 330 | name = "httpdate" 331 | version = "1.0.1" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" 334 | 335 | [[package]] 336 | name = "hyper" 337 | version = "0.14.12" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "13f67199e765030fa08fe0bd581af683f0d5bc04ea09c2b1102012c5fb90e7fd" 340 | dependencies = [ 341 | "bytes", 342 | "futures-channel", 343 | "futures-core", 344 | "futures-util", 345 | "h2", 346 | "http", 347 | "http-body", 348 | "httparse", 349 | "httpdate", 350 | "itoa", 351 | "pin-project-lite", 352 | "socket2", 353 | "tokio", 354 | "tower-service", 355 | "tracing", 356 | "want", 357 | ] 358 | 359 | [[package]] 360 | name = "hyper-tls" 361 | version = "0.5.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" 364 | dependencies = [ 365 | "bytes", 366 | "hyper", 367 | "native-tls", 368 | "tokio", 369 | "tokio-native-tls", 370 | ] 371 | 372 | [[package]] 373 | name = "idna" 374 | version = "0.2.3" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 377 | dependencies = [ 378 | "matches", 379 | "unicode-bidi", 380 | "unicode-normalization", 381 | ] 382 | 383 | [[package]] 384 | name = "indexmap" 385 | version = "1.7.0" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" 388 | dependencies = [ 389 | "autocfg", 390 | "hashbrown", 391 | ] 392 | 393 | [[package]] 394 | name = "indicatif" 395 | version = "0.16.2" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "2d207dc617c7a380ab07ff572a6e52fa202a2a8f355860ac9c38e23f8196be1b" 398 | dependencies = [ 399 | "console", 400 | "lazy_static", 401 | "number_prefix", 402 | "regex", 403 | ] 404 | 405 | [[package]] 406 | name = "instant" 407 | version = "0.1.10" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" 410 | dependencies = [ 411 | "cfg-if", 412 | ] 413 | 414 | [[package]] 415 | name = "ipnet" 416 | version = "2.3.1" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" 419 | 420 | [[package]] 421 | name = "itoa" 422 | version = "0.4.8" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 425 | 426 | [[package]] 427 | name = "js-sys" 428 | version = "0.3.53" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "e4bf49d50e2961077d9c99f4b7997d770a1114f087c3c2e0069b36c13fc2979d" 431 | dependencies = [ 432 | "wasm-bindgen", 433 | ] 434 | 435 | [[package]] 436 | name = "lazy_static" 437 | version = "1.4.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 440 | 441 | [[package]] 442 | name = "libc" 443 | version = "0.2.101" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" 446 | 447 | [[package]] 448 | name = "lock_api" 449 | version = "0.4.5" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" 452 | dependencies = [ 453 | "scopeguard", 454 | ] 455 | 456 | [[package]] 457 | name = "log" 458 | version = "0.4.14" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 461 | dependencies = [ 462 | "cfg-if", 463 | ] 464 | 465 | [[package]] 466 | name = "matches" 467 | version = "0.1.9" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" 470 | 471 | [[package]] 472 | name = "memchr" 473 | version = "2.4.1" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 476 | 477 | [[package]] 478 | name = "mime" 479 | version = "0.3.16" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 482 | 483 | [[package]] 484 | name = "mio" 485 | version = "0.7.13" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" 488 | dependencies = [ 489 | "libc", 490 | "log", 491 | "miow", 492 | "ntapi", 493 | "winapi", 494 | ] 495 | 496 | [[package]] 497 | name = "miow" 498 | version = "0.3.7" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 501 | dependencies = [ 502 | "winapi", 503 | ] 504 | 505 | [[package]] 506 | name = "native-tls" 507 | version = "0.2.8" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" 510 | dependencies = [ 511 | "lazy_static", 512 | "libc", 513 | "log", 514 | "openssl", 515 | "openssl-probe", 516 | "openssl-sys", 517 | "schannel", 518 | "security-framework", 519 | "security-framework-sys", 520 | "tempfile", 521 | ] 522 | 523 | [[package]] 524 | name = "ntapi" 525 | version = "0.3.6" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 528 | dependencies = [ 529 | "winapi", 530 | ] 531 | 532 | [[package]] 533 | name = "num-integer" 534 | version = "0.1.44" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 537 | dependencies = [ 538 | "autocfg", 539 | "num-traits", 540 | ] 541 | 542 | [[package]] 543 | name = "num-traits" 544 | version = "0.2.14" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 547 | dependencies = [ 548 | "autocfg", 549 | ] 550 | 551 | [[package]] 552 | name = "num_cpus" 553 | version = "1.13.0" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 556 | dependencies = [ 557 | "hermit-abi", 558 | "libc", 559 | ] 560 | 561 | [[package]] 562 | name = "number_prefix" 563 | version = "0.4.0" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 566 | 567 | [[package]] 568 | name = "once_cell" 569 | version = "1.8.0" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" 572 | 573 | [[package]] 574 | name = "openssl" 575 | version = "0.10.36" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a" 578 | dependencies = [ 579 | "bitflags", 580 | "cfg-if", 581 | "foreign-types", 582 | "libc", 583 | "once_cell", 584 | "openssl-sys", 585 | ] 586 | 587 | [[package]] 588 | name = "openssl-probe" 589 | version = "0.1.4" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" 592 | 593 | [[package]] 594 | name = "openssl-sys" 595 | version = "0.9.66" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "1996d2d305e561b70d1ee0c53f1542833f4e1ac6ce9a6708b6ff2738ca67dc82" 598 | dependencies = [ 599 | "autocfg", 600 | "cc", 601 | "libc", 602 | "pkg-config", 603 | "vcpkg", 604 | ] 605 | 606 | [[package]] 607 | name = "os_str_bytes" 608 | version = "3.1.0" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "6acbef58a60fe69ab50510a55bc8cdd4d6cf2283d27ad338f54cb52747a9cf2d" 611 | 612 | [[package]] 613 | name = "parking_lot" 614 | version = "0.11.2" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 617 | dependencies = [ 618 | "instant", 619 | "lock_api", 620 | "parking_lot_core", 621 | ] 622 | 623 | [[package]] 624 | name = "parking_lot_core" 625 | version = "0.8.5" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" 628 | dependencies = [ 629 | "cfg-if", 630 | "instant", 631 | "libc", 632 | "redox_syscall", 633 | "smallvec", 634 | "winapi", 635 | ] 636 | 637 | [[package]] 638 | name = "percent-encoding" 639 | version = "2.1.0" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 642 | 643 | [[package]] 644 | name = "pin-project-lite" 645 | version = "0.2.7" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" 648 | 649 | [[package]] 650 | name = "pin-utils" 651 | version = "0.1.0" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 654 | 655 | [[package]] 656 | name = "pkg-config" 657 | version = "0.3.19" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 660 | 661 | [[package]] 662 | name = "ppv-lite86" 663 | version = "0.2.10" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 666 | 667 | [[package]] 668 | name = "proc-macro-error" 669 | version = "1.0.4" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 672 | dependencies = [ 673 | "proc-macro-error-attr", 674 | "proc-macro2", 675 | "quote", 676 | "syn", 677 | "version_check", 678 | ] 679 | 680 | [[package]] 681 | name = "proc-macro-error-attr" 682 | version = "1.0.4" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 685 | dependencies = [ 686 | "proc-macro2", 687 | "quote", 688 | "version_check", 689 | ] 690 | 691 | [[package]] 692 | name = "proc-macro2" 693 | version = "1.0.29" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" 696 | dependencies = [ 697 | "unicode-xid", 698 | ] 699 | 700 | [[package]] 701 | name = "quote" 702 | version = "1.0.9" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 705 | dependencies = [ 706 | "proc-macro2", 707 | ] 708 | 709 | [[package]] 710 | name = "rand" 711 | version = "0.8.4" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 714 | dependencies = [ 715 | "libc", 716 | "rand_chacha", 717 | "rand_core", 718 | "rand_hc", 719 | ] 720 | 721 | [[package]] 722 | name = "rand_chacha" 723 | version = "0.3.1" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 726 | dependencies = [ 727 | "ppv-lite86", 728 | "rand_core", 729 | ] 730 | 731 | [[package]] 732 | name = "rand_core" 733 | version = "0.6.3" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 736 | dependencies = [ 737 | "getrandom", 738 | ] 739 | 740 | [[package]] 741 | name = "rand_hc" 742 | version = "0.3.1" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 745 | dependencies = [ 746 | "rand_core", 747 | ] 748 | 749 | [[package]] 750 | name = "redox_syscall" 751 | version = "0.2.10" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 754 | dependencies = [ 755 | "bitflags", 756 | ] 757 | 758 | [[package]] 759 | name = "regex" 760 | version = "1.5.4" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 763 | dependencies = [ 764 | "aho-corasick", 765 | "memchr", 766 | "regex-syntax", 767 | ] 768 | 769 | [[package]] 770 | name = "regex-syntax" 771 | version = "0.6.25" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 774 | 775 | [[package]] 776 | name = "remove_dir_all" 777 | version = "0.5.3" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 780 | dependencies = [ 781 | "winapi", 782 | ] 783 | 784 | [[package]] 785 | name = "reqwest" 786 | version = "0.11.4" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22" 789 | dependencies = [ 790 | "base64", 791 | "bytes", 792 | "encoding_rs", 793 | "futures-core", 794 | "futures-util", 795 | "http", 796 | "http-body", 797 | "hyper", 798 | "hyper-tls", 799 | "ipnet", 800 | "js-sys", 801 | "lazy_static", 802 | "log", 803 | "mime", 804 | "native-tls", 805 | "percent-encoding", 806 | "pin-project-lite", 807 | "serde", 808 | "serde_json", 809 | "serde_urlencoded", 810 | "tokio", 811 | "tokio-native-tls", 812 | "url", 813 | "wasm-bindgen", 814 | "wasm-bindgen-futures", 815 | "web-sys", 816 | "winreg", 817 | ] 818 | 819 | [[package]] 820 | name = "ryu" 821 | version = "1.0.5" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 824 | 825 | [[package]] 826 | name = "schannel" 827 | version = "0.1.19" 828 | source = "registry+https://github.com/rust-lang/crates.io-index" 829 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 830 | dependencies = [ 831 | "lazy_static", 832 | "winapi", 833 | ] 834 | 835 | [[package]] 836 | name = "scopeguard" 837 | version = "1.1.0" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 840 | 841 | [[package]] 842 | name = "security-framework" 843 | version = "2.4.2" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" 846 | dependencies = [ 847 | "bitflags", 848 | "core-foundation", 849 | "core-foundation-sys", 850 | "libc", 851 | "security-framework-sys", 852 | ] 853 | 854 | [[package]] 855 | name = "security-framework-sys" 856 | version = "2.4.2" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" 859 | dependencies = [ 860 | "core-foundation-sys", 861 | "libc", 862 | ] 863 | 864 | [[package]] 865 | name = "serde" 866 | version = "1.0.130" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 869 | dependencies = [ 870 | "serde_derive", 871 | ] 872 | 873 | [[package]] 874 | name = "serde_derive" 875 | version = "1.0.130" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" 878 | dependencies = [ 879 | "proc-macro2", 880 | "quote", 881 | "syn", 882 | ] 883 | 884 | [[package]] 885 | name = "serde_json" 886 | version = "1.0.67" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950" 889 | dependencies = [ 890 | "itoa", 891 | "ryu", 892 | "serde", 893 | ] 894 | 895 | [[package]] 896 | name = "serde_urlencoded" 897 | version = "0.7.0" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" 900 | dependencies = [ 901 | "form_urlencoded", 902 | "itoa", 903 | "ryu", 904 | "serde", 905 | ] 906 | 907 | [[package]] 908 | name = "signal-hook-registry" 909 | version = "1.4.0" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 912 | dependencies = [ 913 | "libc", 914 | ] 915 | 916 | [[package]] 917 | name = "slab" 918 | version = "0.4.4" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" 921 | 922 | [[package]] 923 | name = "smallvec" 924 | version = "1.6.1" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 927 | 928 | [[package]] 929 | name = "socket2" 930 | version = "0.4.1" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad" 933 | dependencies = [ 934 | "libc", 935 | "winapi", 936 | ] 937 | 938 | [[package]] 939 | name = "strsim" 940 | version = "0.10.0" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 943 | 944 | [[package]] 945 | name = "syn" 946 | version = "1.0.76" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" 949 | dependencies = [ 950 | "proc-macro2", 951 | "quote", 952 | "unicode-xid", 953 | ] 954 | 955 | [[package]] 956 | name = "tempfile" 957 | version = "3.2.0" 958 | source = "registry+https://github.com/rust-lang/crates.io-index" 959 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" 960 | dependencies = [ 961 | "cfg-if", 962 | "libc", 963 | "rand", 964 | "redox_syscall", 965 | "remove_dir_all", 966 | "winapi", 967 | ] 968 | 969 | [[package]] 970 | name = "termcolor" 971 | version = "1.1.2" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 974 | dependencies = [ 975 | "winapi-util", 976 | ] 977 | 978 | [[package]] 979 | name = "terminal_size" 980 | version = "0.1.17" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" 983 | dependencies = [ 984 | "libc", 985 | "winapi", 986 | ] 987 | 988 | [[package]] 989 | name = "textwrap" 990 | version = "0.14.2" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" 993 | dependencies = [ 994 | "unicode-width", 995 | ] 996 | 997 | [[package]] 998 | name = "time" 999 | version = "0.1.44" 1000 | source = "registry+https://github.com/rust-lang/crates.io-index" 1001 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 1002 | dependencies = [ 1003 | "libc", 1004 | "wasi", 1005 | "winapi", 1006 | ] 1007 | 1008 | [[package]] 1009 | name = "tinyvec" 1010 | version = "1.3.1" 1011 | source = "registry+https://github.com/rust-lang/crates.io-index" 1012 | checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338" 1013 | dependencies = [ 1014 | "tinyvec_macros", 1015 | ] 1016 | 1017 | [[package]] 1018 | name = "tinyvec_macros" 1019 | version = "0.1.0" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1022 | 1023 | [[package]] 1024 | name = "tokio" 1025 | version = "1.11.0" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "b4efe6fc2395938c8155973d7be49fe8d03a843726e285e100a8a383cc0154ce" 1028 | dependencies = [ 1029 | "autocfg", 1030 | "bytes", 1031 | "libc", 1032 | "memchr", 1033 | "mio", 1034 | "num_cpus", 1035 | "once_cell", 1036 | "parking_lot", 1037 | "pin-project-lite", 1038 | "signal-hook-registry", 1039 | "tokio-macros", 1040 | "winapi", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "tokio-macros" 1045 | version = "1.3.0" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" 1048 | dependencies = [ 1049 | "proc-macro2", 1050 | "quote", 1051 | "syn", 1052 | ] 1053 | 1054 | [[package]] 1055 | name = "tokio-native-tls" 1056 | version = "0.3.0" 1057 | source = "registry+https://github.com/rust-lang/crates.io-index" 1058 | checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 1059 | dependencies = [ 1060 | "native-tls", 1061 | "tokio", 1062 | ] 1063 | 1064 | [[package]] 1065 | name = "tokio-util" 1066 | version = "0.6.8" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "08d3725d3efa29485e87311c5b699de63cde14b00ed4d256b8318aa30ca452cd" 1069 | dependencies = [ 1070 | "bytes", 1071 | "futures-core", 1072 | "futures-sink", 1073 | "log", 1074 | "pin-project-lite", 1075 | "tokio", 1076 | ] 1077 | 1078 | [[package]] 1079 | name = "tower-service" 1080 | version = "0.3.1" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 1083 | 1084 | [[package]] 1085 | name = "tracing" 1086 | version = "0.1.26" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" 1089 | dependencies = [ 1090 | "cfg-if", 1091 | "pin-project-lite", 1092 | "tracing-core", 1093 | ] 1094 | 1095 | [[package]] 1096 | name = "tracing-core" 1097 | version = "0.1.19" 1098 | source = "registry+https://github.com/rust-lang/crates.io-index" 1099 | checksum = "2ca517f43f0fb96e0c3072ed5c275fe5eece87e8cb52f4a77b69226d3b1c9df8" 1100 | dependencies = [ 1101 | "lazy_static", 1102 | ] 1103 | 1104 | [[package]] 1105 | name = "try-lock" 1106 | version = "0.2.3" 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" 1108 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1109 | 1110 | [[package]] 1111 | name = "unicode-bidi" 1112 | version = "0.3.6" 1113 | source = "registry+https://github.com/rust-lang/crates.io-index" 1114 | checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085" 1115 | 1116 | [[package]] 1117 | name = "unicode-normalization" 1118 | version = "0.1.19" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" 1121 | dependencies = [ 1122 | "tinyvec", 1123 | ] 1124 | 1125 | [[package]] 1126 | name = "unicode-segmentation" 1127 | version = "1.8.0" 1128 | source = "registry+https://github.com/rust-lang/crates.io-index" 1129 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" 1130 | 1131 | [[package]] 1132 | name = "unicode-width" 1133 | version = "0.1.8" 1134 | source = "registry+https://github.com/rust-lang/crates.io-index" 1135 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 1136 | 1137 | [[package]] 1138 | name = "unicode-xid" 1139 | version = "0.2.2" 1140 | source = "registry+https://github.com/rust-lang/crates.io-index" 1141 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1142 | 1143 | [[package]] 1144 | name = "url" 1145 | version = "2.2.2" 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" 1147 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 1148 | dependencies = [ 1149 | "form_urlencoded", 1150 | "idna", 1151 | "matches", 1152 | "percent-encoding", 1153 | ] 1154 | 1155 | [[package]] 1156 | name = "vcpkg" 1157 | version = "0.2.15" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1160 | 1161 | [[package]] 1162 | name = "vec_map" 1163 | version = "0.8.2" 1164 | source = "registry+https://github.com/rust-lang/crates.io-index" 1165 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 1166 | 1167 | [[package]] 1168 | name = "version_check" 1169 | version = "0.9.3" 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" 1171 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 1172 | 1173 | [[package]] 1174 | name = "want" 1175 | version = "0.3.0" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1178 | dependencies = [ 1179 | "log", 1180 | "try-lock", 1181 | ] 1182 | 1183 | [[package]] 1184 | name = "wasi" 1185 | version = "0.10.0+wasi-snapshot-preview1" 1186 | source = "registry+https://github.com/rust-lang/crates.io-index" 1187 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1188 | 1189 | [[package]] 1190 | name = "wasm-bindgen" 1191 | version = "0.2.76" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "8ce9b1b516211d33767048e5d47fa2a381ed8b76fc48d2ce4aa39877f9f183e0" 1194 | dependencies = [ 1195 | "cfg-if", 1196 | "serde", 1197 | "serde_json", 1198 | "wasm-bindgen-macro", 1199 | ] 1200 | 1201 | [[package]] 1202 | name = "wasm-bindgen-backend" 1203 | version = "0.2.76" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "cfe8dc78e2326ba5f845f4b5bf548401604fa20b1dd1d365fb73b6c1d6364041" 1206 | dependencies = [ 1207 | "bumpalo", 1208 | "lazy_static", 1209 | "log", 1210 | "proc-macro2", 1211 | "quote", 1212 | "syn", 1213 | "wasm-bindgen-shared", 1214 | ] 1215 | 1216 | [[package]] 1217 | name = "wasm-bindgen-futures" 1218 | version = "0.4.26" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | checksum = "95fded345a6559c2cfee778d562300c581f7d4ff3edb9b0d230d69800d213972" 1221 | dependencies = [ 1222 | "cfg-if", 1223 | "js-sys", 1224 | "wasm-bindgen", 1225 | "web-sys", 1226 | ] 1227 | 1228 | [[package]] 1229 | name = "wasm-bindgen-macro" 1230 | version = "0.2.76" 1231 | source = "registry+https://github.com/rust-lang/crates.io-index" 1232 | checksum = "44468aa53335841d9d6b6c023eaab07c0cd4bddbcfdee3e2bb1e8d2cb8069fef" 1233 | dependencies = [ 1234 | "quote", 1235 | "wasm-bindgen-macro-support", 1236 | ] 1237 | 1238 | [[package]] 1239 | name = "wasm-bindgen-macro-support" 1240 | version = "0.2.76" 1241 | source = "registry+https://github.com/rust-lang/crates.io-index" 1242 | checksum = "0195807922713af1e67dc66132c7328206ed9766af3858164fb583eedc25fbad" 1243 | dependencies = [ 1244 | "proc-macro2", 1245 | "quote", 1246 | "syn", 1247 | "wasm-bindgen-backend", 1248 | "wasm-bindgen-shared", 1249 | ] 1250 | 1251 | [[package]] 1252 | name = "wasm-bindgen-shared" 1253 | version = "0.2.76" 1254 | source = "registry+https://github.com/rust-lang/crates.io-index" 1255 | checksum = "acdb075a845574a1fa5f09fd77e43f7747599301ea3417a9fbffdeedfc1f4a29" 1256 | 1257 | [[package]] 1258 | name = "wayback-archiver" 1259 | version = "0.1.1" 1260 | dependencies = [ 1261 | "chrono", 1262 | "clap", 1263 | "crossbeam-channel", 1264 | "indicatif", 1265 | "lazy_static", 1266 | "regex", 1267 | "reqwest", 1268 | "serde", 1269 | "serde_json", 1270 | "tokio", 1271 | ] 1272 | 1273 | [[package]] 1274 | name = "web-sys" 1275 | version = "0.3.53" 1276 | source = "registry+https://github.com/rust-lang/crates.io-index" 1277 | checksum = "224b2f6b67919060055ef1a67807367c2066ed520c3862cc013d26cf893a783c" 1278 | dependencies = [ 1279 | "js-sys", 1280 | "wasm-bindgen", 1281 | ] 1282 | 1283 | [[package]] 1284 | name = "winapi" 1285 | version = "0.3.9" 1286 | source = "registry+https://github.com/rust-lang/crates.io-index" 1287 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1288 | dependencies = [ 1289 | "winapi-i686-pc-windows-gnu", 1290 | "winapi-x86_64-pc-windows-gnu", 1291 | ] 1292 | 1293 | [[package]] 1294 | name = "winapi-i686-pc-windows-gnu" 1295 | version = "0.4.0" 1296 | source = "registry+https://github.com/rust-lang/crates.io-index" 1297 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1298 | 1299 | [[package]] 1300 | name = "winapi-util" 1301 | version = "0.1.5" 1302 | source = "registry+https://github.com/rust-lang/crates.io-index" 1303 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1304 | dependencies = [ 1305 | "winapi", 1306 | ] 1307 | 1308 | [[package]] 1309 | name = "winapi-x86_64-pc-windows-gnu" 1310 | version = "0.4.0" 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" 1312 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1313 | 1314 | [[package]] 1315 | name = "winreg" 1316 | version = "0.7.0" 1317 | source = "registry+https://github.com/rust-lang/crates.io-index" 1318 | checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" 1319 | dependencies = [ 1320 | "winapi", 1321 | ] 1322 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wayback-archiver" 3 | version = "0.1.1" 4 | edition = "2018" 5 | authors = ["Ben Congdon "] 6 | license = "MIT" 7 | description = "CLI tool for archiving URLs to the Internet Archive's \"Wayback Machine\"" 8 | repository = "https://github.com/bcongdon/wayback-archiver" 9 | categories = ["command-line-utilities"] 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | clap = "3.0.0-beta.4" 15 | reqwest = { version = "0.11", features = ["json"] } 16 | tokio = { version = "1", features = ["full"] } 17 | serde = { version = "1.0", features = ["derive"] } 18 | chrono = { version = "0.4.19", features = ["serde"] } 19 | serde_json = "1" 20 | indicatif = "0.16.2" 21 | crossbeam-channel = "0.5.1" 22 | lazy_static = "1.4.0" 23 | regex = "1" 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ben Congdon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wayback-archiver 2 | 3 | [![crates.io](https://img.shields.io/crates/v/wayback-archiver.svg)](https://crates.io/crates/wayback-archiver) 4 | 5 | CLI archival tool for the Wayback Machine 6 | 7 | ## Installation 8 | 9 | $ cargo install wayback-archiver 10 | 11 | ### Examples: 12 | 13 | ```sh 14 | $ wayback-archiver google.com 15 | 16 | $ wayback-archiver --urls-file urls.txt --out archive.json 17 | 18 | $ echo "google.com\nwikipedia.org\ngithub.com" | wayback-archiver --out=archive.json --merge 19 | ``` 20 | 21 | ### Demo 22 | 23 | ![](demo.gif) 24 | 25 | ## Usage 26 | 27 | ``` 28 | USAGE: 29 | wayback-archiver [FLAGS] [OPTIONS] [URLS]... 30 | 31 | ARGS: 32 | ... URLs to archive using the Wayback Machine. URLs can also be provided using 33 | stdin, or with --urls_file 34 | 35 | FLAGS: 36 | -h, --help Print help information 37 | -m, --merge If set, the results are merged with the (existing) contents of the --out file 38 | -V, --version Print version information 39 | 40 | OPTIONS: 41 | -o, --out If set, archived URLs are saved to the path specified by this 42 | flag. Otherwise, URLs are printed at the end of the command run 43 | -u, --urls-file A file containing urls to archive 44 | ``` 45 | 46 | ## Attribution 47 | 48 | This tool only functions because of the Internet Archive. Please consider [dontating](https://archive.org/donate) to their cause. 49 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcongdon/wayback-archiver/d42b01c621222845688bf6b272b3b9bff9124461/demo.gif -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use chrono::{Duration, NaiveDateTime, TimeZone, Utc}; 2 | use lazy_static::lazy_static; 3 | use regex::Regex; 4 | use serde::{Deserialize, Serialize}; 5 | use std::collections::HashMap; 6 | 7 | pub async fn archive_url(url: &str) -> Result { 8 | // Check to see if there's an existing archive of the requested URL. 9 | let latest_snapshot = fetch_latest_snapshot(url).await; 10 | if let Ok(ref snapshot) = latest_snapshot { 11 | // Only accept the existing snapshot if it was made recently. 12 | if (Utc::now() - Duration::days(90)).naive_utc() < snapshot.last_archived { 13 | return latest_snapshot; 14 | } 15 | } 16 | 17 | // Request a new snapshot of the URL. 18 | let resp = reqwest::get(format!("https://web.archive.org/save/{}", url)) 19 | .await 20 | .map_err(|err| ArchiveError::Unknown(err.to_string()))?; 21 | let archive_url: Result = match resp.status().as_u16() { 22 | // Return the redirected URL (which is the archive snapshot URL). 23 | 200 => Ok(resp.url().clone().to_string()), 24 | 404 => { 25 | // Sometimes, the snapshot URL returns a 404, even though the archival was successful. 26 | // Probably due to a race condition in the Wayback machine; these URLs do (eventually) exist. 27 | if resp.url().path().starts_with("/web") { 28 | Ok(resp.url().to_string()) 29 | } else { 30 | Err(ArchiveError::Unknown(format!( 31 | "Unexpected HTTP 404 at {:#?}", 32 | resp.url().to_string() 33 | ))) 34 | } 35 | } 36 | 509 => Err(ArchiveError::BandwidthExceeded), 37 | // There may be more status codes that indicate archive failure, but these were the most common. 38 | 403 | 520 | 523 => Err(ArchiveError::UnableToArchive), 39 | _ => { 40 | dbg!(&resp); 41 | Err(ArchiveError::Unknown(format!( 42 | "Got status {}: {:#?}", 43 | resp.status(), 44 | resp 45 | ))) 46 | } 47 | }; 48 | let result = archive_url.and_then(|url| { 49 | Ok(ArchivingResult { 50 | last_archived: timestamp_from_archive_url(&url)?, 51 | url: Some(url), 52 | existing_snapshot: false, 53 | }) 54 | }); 55 | match result { 56 | Err(ArchiveError::UnableToArchive) => { 57 | // If we weren't able to archive the URL, but a valid (if old) snapshot exists, 58 | // then return that older snapshot. 59 | latest_snapshot.map_err(|_| ArchiveError::UnableToArchive) 60 | } 61 | _ => result, 62 | } 63 | } 64 | 65 | fn timestamp_from_archive_url(url: &str) -> Result { 66 | lazy_static! { 67 | static ref RE: Regex = Regex::new(r"/web/(\d+)/").unwrap(); 68 | } 69 | let timestamp_url_component = RE 70 | .captures(url) 71 | .and_then(|cap| cap.get(1).map(|ts_str| ts_str.as_str())) 72 | .ok_or_else(|| ArchiveError::ParseError("unable to extract timestamp from url".into()))?; 73 | parse_wayback_timestamp(timestamp_url_component) 74 | } 75 | 76 | async fn fetch_latest_snapshot(url: &str) -> Result { 77 | let resp = reqwest::get(format!("http://archive.org/wayback/available?url={}", url)) 78 | .await 79 | .map_err(|err| ArchiveError::Unknown(err.to_string()))? 80 | .json::() 81 | .await 82 | .map_err(|err| ArchiveError::ParseError(err.to_string()))?; 83 | 84 | if let Some(snapshots) = resp.archived_snapshots { 85 | if let Some((_, latest)) = snapshots 86 | .iter() 87 | .max_by_key(|(_, snapshot)| &snapshot.timestamp) 88 | { 89 | return Ok(ArchivingResult { 90 | existing_snapshot: true, 91 | last_archived: parse_wayback_timestamp(&latest.timestamp)?, 92 | url: Some(latest.url.clone()), 93 | }); 94 | } 95 | } 96 | Err(ArchiveError::NoExistingSnapshot) 97 | } 98 | 99 | fn parse_wayback_timestamp(ts: &str) -> Result { 100 | let naive_utc = NaiveDateTime::parse_from_str(ts, "%Y%m%d%H%M%S") 101 | .map_err(|err| ArchiveError::ParseError(err.to_string()))?; 102 | Ok(Utc.from_utc_datetime(&naive_utc).naive_local()) 103 | } 104 | 105 | #[derive(Deserialize, Debug)] 106 | struct WaybackAvailabilityResponse { 107 | url: String, 108 | archived_snapshots: Option>, 109 | } 110 | 111 | #[derive(Deserialize, Debug)] 112 | struct WaybackSnapshot { 113 | status: String, 114 | available: bool, 115 | url: String, 116 | timestamp: String, 117 | } 118 | 119 | #[derive(Deserialize, Serialize, Debug)] 120 | pub struct ArchivingResult { 121 | pub url: Option, 122 | pub last_archived: NaiveDateTime, 123 | #[serde(skip)] 124 | pub existing_snapshot: bool, 125 | } 126 | 127 | #[derive(Debug, PartialEq)] 128 | pub enum ArchiveError { 129 | BandwidthExceeded, 130 | UnableToArchive, 131 | NoExistingSnapshot, 132 | ParseError(String), 133 | Unknown(String), 134 | } 135 | 136 | impl std::fmt::Display for ArchiveError { 137 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 138 | match self { 139 | ArchiveError::BandwidthExceeded => write!(f, "Bandwidth Exceeded"), 140 | ArchiveError::UnableToArchive => { 141 | write!(f, "Wayback Machine unable to archive this URL") 142 | } 143 | ArchiveError::NoExistingSnapshot => write!(f, "No existing snapshots"), 144 | ArchiveError::ParseError(err) => write!(f, "Parse error: {}", err), 145 | ArchiveError::Unknown(err) => write!(f, "Unknown error: {}", err), 146 | } 147 | } 148 | } 149 | 150 | impl std::error::Error for ArchiveError {} 151 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use chrono::{Duration, Utc}; 2 | use clap::{AppSettings, Clap}; 3 | use indicatif::{ProgressBar, ProgressStyle}; 4 | use std::collections::BTreeMap; 5 | use std::fs; 6 | use std::io::{self, BufRead, Write}; 7 | use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; 8 | use std::sync::Arc; 9 | use std::thread; 10 | 11 | mod lib; 12 | use crate::lib::{archive_url, ArchiveError, ArchivingResult}; 13 | 14 | #[derive(Clap)] 15 | #[clap(version = "1.0", author = "Ben Congdon ")] 16 | #[clap(setting = AppSettings::ColoredHelp)] 17 | struct Opts { 18 | /// If set, archived URLs are saved to the path specified by this flag. 19 | /// Otherwise, URLs are printed at the end of the command run. 20 | #[clap(short, long)] 21 | out: Option, 22 | /// If set, the results are merged with the (existing) contents of 23 | /// the --out file. 24 | #[clap(short, long)] 25 | merge: bool, 26 | /// A file containing urls to archive. 27 | #[clap(short = 'i', long)] 28 | urls_file: Option, 29 | /// URLs to archive using the Wayback Machine. URLs can also 30 | /// be provided using stdin, or with --urls_file. 31 | urls: Vec, 32 | } 33 | 34 | #[tokio::main] 35 | async fn main() -> Result<(), Box> { 36 | let opts = Opts::parse(); 37 | 38 | let (tx, rx) = crossbeam_channel::unbounded::(); 39 | 40 | let mut urls: BTreeMap = BTreeMap::new(); 41 | if opts.merge { 42 | let path = opts.out.as_ref().expect("--merge requires --out to be set"); 43 | match fs::read_to_string(path) { 44 | Ok(existing) => urls = serde_json::from_str(&existing)?, 45 | Err(error) => match error.kind() { 46 | // Ignore "file not found" error. 47 | io::ErrorKind::NotFound => {} 48 | _ => return Err(error.into()), 49 | }, 50 | } 51 | } 52 | 53 | let total_lines_count = Arc::new(AtomicUsize::new(0)); 54 | let total_lines_count_clone = total_lines_count.clone(); 55 | 56 | // Synchronous URL source(s). 57 | if !opts.urls.is_empty() { 58 | for url in &opts.urls { 59 | tx.send(url.into())?; 60 | } 61 | total_lines_count.fetch_add(opts.urls.len(), SeqCst); 62 | drop(tx); // Close channel. 63 | } 64 | // Asynchronous URL source(s). 65 | else { 66 | // Spawn a separate thread to pull from the lines source. 67 | let urls_file = opts.urls_file; 68 | thread::spawn(move || 69 | // This could probably be refactored... 70 | match urls_file { 71 | // Read URLs from a file. 72 | Some(path) => { 73 | // TODO: Propagate error better here. 74 | let file = fs::File::open(path).expect("unable to open file"); 75 | for line in std::io::BufReader::new(file).lines() { 76 | tx.send(line.expect("line")).expect("send"); 77 | total_lines_count.fetch_add(1, SeqCst); 78 | } 79 | } 80 | // Fall back on stdin. 81 | None => { 82 | let stdin = io::stdin(); 83 | for line in stdin.lock().lines() { 84 | tx.send(line.expect("line")).expect("send"); 85 | total_lines_count.fetch_add(1, SeqCst); 86 | } 87 | } 88 | }); 89 | } 90 | 91 | let mut num_archived = 0; 92 | for (line_idx, line) in rx.into_iter().map(|l| l.trim().to_string()).enumerate() { 93 | let pb = ProgressBar::new_spinner(); 94 | pb.enable_steady_tick(120); 95 | pb.set_style( 96 | ProgressStyle::default_spinner().template("{prefix:.bold.dim} {spinner:.blue} {msg}"), 97 | ); 98 | pb.set_prefix(format!( 99 | "[{}/{}]", 100 | line_idx + 1, 101 | total_lines_count_clone.load(SeqCst) 102 | )); 103 | 104 | if let Some(existing) = urls.get(&line) { 105 | // If the last archival time of the URL was within ~6 months, accept it and move on. 106 | if (Utc::now().naive_utc() - existing.last_archived) < Duration::days(30 * 6) { 107 | pb.finish_with_message(format!("URL already archived: {}", line)); 108 | continue; 109 | } 110 | } 111 | 112 | pb.set_message(format!("Archiving {} ...", line)); 113 | loop { 114 | let result = match archive_url(&line).await { 115 | Ok(success) => { 116 | pb.finish_with_message(format!( 117 | "Done: {}", 118 | &success.url.as_ref().expect("archive url") 119 | )); 120 | if !success.existing_snapshot { 121 | let pb = ProgressBar::new_spinner(); 122 | pb.enable_steady_tick(180); 123 | pb.set_message("Cooldown after archiving..."); 124 | std::thread::sleep(Duration::seconds(5).to_std().expect("sleep duration")); 125 | pb.finish_and_clear(); 126 | } 127 | num_archived += 1; 128 | success 129 | } 130 | Err(err) => { 131 | if err == ArchiveError::BandwidthExceeded { 132 | pb.set_message("Bandwidth exceeded. Waiting..."); 133 | std::thread::sleep(Duration::seconds(15).to_std().expect("sleep duration")); 134 | continue; 135 | } 136 | pb.finish_with_message(format!("Archiving failed: {} ({})", err, line)); 137 | ArchivingResult { 138 | last_archived: Utc::now().naive_local(), 139 | url: None, 140 | existing_snapshot: false, 141 | } 142 | } 143 | }; 144 | urls.insert(line.to_string(), result); 145 | break; 146 | } 147 | 148 | if (num_archived + 1) % 25 == 0 { 149 | if let Some(out_path) = &opts.out { 150 | eprintln!("Writing intermediate results..."); 151 | write_results(&urls, out_path)?; 152 | } 153 | } 154 | } 155 | 156 | match opts.out { 157 | Some(path) => write_results(&urls, &path)?, 158 | None => { 159 | println!("{}", serde_json::to_string_pretty(&urls)?); 160 | } 161 | } 162 | Ok(()) 163 | } 164 | 165 | fn write_results( 166 | results: &BTreeMap, 167 | path: &str, 168 | ) -> Result<(), Box> { 169 | let formatted_urls = serde_json::to_string_pretty(&results)?; 170 | let mut file = fs::OpenOptions::new() 171 | .write(true) 172 | .create(true) 173 | .truncate(true) 174 | .open(path)?; 175 | file.write_all(formatted_urls.as_bytes())?; 176 | Ok(()) 177 | } 178 | --------------------------------------------------------------------------------