├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── api ├── chat.rs ├── deserializers.rs ├── info.rs ├── machines.rs ├── mod.rs └── util.rs ├── args.rs ├── cli.rs ├── cmds ├── chat │ ├── display.rs │ ├── mod.rs │ ├── shell.rs │ ├── talk.rs │ └── util.rs ├── config │ ├── mod.rs │ └── token.rs ├── info │ ├── mod.rs │ └── status.rs ├── machines │ ├── list.rs │ ├── mod.rs │ ├── own.rs │ ├── show.rs │ ├── state.rs │ └── todo.rs └── mod.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ansi_term" 5 | version = "0.12.1" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 8 | dependencies = [ 9 | "winapi 0.3.8", 10 | ] 11 | 12 | [[package]] 13 | name = "arc-swap" 14 | version = "0.4.5" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825" 17 | 18 | [[package]] 19 | name = "arrayref" 20 | version = "0.3.6" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 23 | 24 | [[package]] 25 | name = "arrayvec" 26 | version = "0.5.1" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" 29 | 30 | [[package]] 31 | name = "async-trait" 32 | version = "0.1.30" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "da71fef07bc806586090247e971229289f64c210a278ee5ae419314eb386b31d" 35 | dependencies = [ 36 | "proc-macro2", 37 | "quote", 38 | "syn", 39 | ] 40 | 41 | [[package]] 42 | name = "atty" 43 | version = "0.2.14" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 46 | dependencies = [ 47 | "hermit-abi", 48 | "libc", 49 | "winapi 0.3.8", 50 | ] 51 | 52 | [[package]] 53 | name = "autocfg" 54 | version = "0.1.7" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" 57 | 58 | [[package]] 59 | name = "autocfg" 60 | version = "1.0.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 63 | 64 | [[package]] 65 | name = "base64" 66 | version = "0.11.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" 69 | 70 | [[package]] 71 | name = "bitflags" 72 | version = "1.2.1" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 75 | 76 | [[package]] 77 | name = "blake2b_simd" 78 | version = "0.5.10" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" 81 | dependencies = [ 82 | "arrayref", 83 | "arrayvec", 84 | "constant_time_eq", 85 | ] 86 | 87 | [[package]] 88 | name = "bstr" 89 | version = "0.2.12" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41" 92 | dependencies = [ 93 | "lazy_static", 94 | "memchr", 95 | "regex-automata", 96 | "serde", 97 | ] 98 | 99 | [[package]] 100 | name = "bumpalo" 101 | version = "3.2.1" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" 104 | 105 | [[package]] 106 | name = "byteorder" 107 | version = "1.3.4" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 110 | 111 | [[package]] 112 | name = "bytes" 113 | version = "0.5.4" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" 116 | 117 | [[package]] 118 | name = "cc" 119 | version = "1.0.50" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" 122 | 123 | [[package]] 124 | name = "cfg-if" 125 | version = "0.1.10" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 128 | 129 | [[package]] 130 | name = "chrono" 131 | version = "0.4.11" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" 134 | dependencies = [ 135 | "num-integer", 136 | "num-traits", 137 | "time", 138 | ] 139 | 140 | [[package]] 141 | name = "clap" 142 | version = "3.0.0-beta.1" 143 | source = "git+https://github.com/clap-rs/clap#4b5d7722bfbd7c6c5f78d0fe1d0aaae46c98e1a8" 144 | dependencies = [ 145 | "ansi_term", 146 | "atty", 147 | "bitflags", 148 | "clap_derive", 149 | "indexmap", 150 | "lazy_static", 151 | "strsim", 152 | "textwrap", 153 | "unicode-width", 154 | "vec_map", 155 | ] 156 | 157 | [[package]] 158 | name = "clap_derive" 159 | version = "3.0.0-beta.1" 160 | source = "git+https://github.com/clap-rs/clap#4b5d7722bfbd7c6c5f78d0fe1d0aaae46c98e1a8" 161 | dependencies = [ 162 | "heck", 163 | "proc-macro-error", 164 | "proc-macro2", 165 | "quote", 166 | "syn", 167 | ] 168 | 169 | [[package]] 170 | name = "cloudabi" 171 | version = "0.0.3" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 174 | dependencies = [ 175 | "bitflags", 176 | ] 177 | 178 | [[package]] 179 | name = "colored" 180 | version = "1.9.3" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" 183 | dependencies = [ 184 | "atty", 185 | "lazy_static", 186 | "winapi 0.3.8", 187 | ] 188 | 189 | [[package]] 190 | name = "constant_time_eq" 191 | version = "0.1.5" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 194 | 195 | [[package]] 196 | name = "core-foundation" 197 | version = "0.7.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" 200 | dependencies = [ 201 | "core-foundation-sys", 202 | "libc", 203 | ] 204 | 205 | [[package]] 206 | name = "core-foundation-sys" 207 | version = "0.7.0" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" 210 | 211 | [[package]] 212 | name = "crossbeam-utils" 213 | version = "0.7.2" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 216 | dependencies = [ 217 | "autocfg 1.0.0", 218 | "cfg-if", 219 | "lazy_static", 220 | ] 221 | 222 | [[package]] 223 | name = "csv" 224 | version = "1.1.3" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279" 227 | dependencies = [ 228 | "bstr", 229 | "csv-core", 230 | "itoa", 231 | "ryu", 232 | "serde", 233 | ] 234 | 235 | [[package]] 236 | name = "csv-core" 237 | version = "0.1.10" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" 240 | dependencies = [ 241 | "memchr", 242 | ] 243 | 244 | [[package]] 245 | name = "dirs" 246 | version = "1.0.5" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" 249 | dependencies = [ 250 | "libc", 251 | "redox_users", 252 | "winapi 0.3.8", 253 | ] 254 | 255 | [[package]] 256 | name = "dirs" 257 | version = "2.0.2" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" 260 | dependencies = [ 261 | "cfg-if", 262 | "dirs-sys", 263 | ] 264 | 265 | [[package]] 266 | name = "dirs-sys" 267 | version = "0.3.4" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" 270 | dependencies = [ 271 | "cfg-if", 272 | "libc", 273 | "redox_users", 274 | "winapi 0.3.8", 275 | ] 276 | 277 | [[package]] 278 | name = "dtoa" 279 | version = "0.4.5" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" 282 | 283 | [[package]] 284 | name = "encode_unicode" 285 | version = "0.3.6" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 288 | 289 | [[package]] 290 | name = "encoding_rs" 291 | version = "0.8.22" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" 294 | dependencies = [ 295 | "cfg-if", 296 | ] 297 | 298 | [[package]] 299 | name = "fnv" 300 | version = "1.0.6" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" 303 | 304 | [[package]] 305 | name = "foreign-types" 306 | version = "0.3.2" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 309 | dependencies = [ 310 | "foreign-types-shared", 311 | ] 312 | 313 | [[package]] 314 | name = "foreign-types-shared" 315 | version = "0.1.1" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 318 | 319 | [[package]] 320 | name = "fuchsia-cprng" 321 | version = "0.1.1" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 324 | 325 | [[package]] 326 | name = "fuchsia-zircon" 327 | version = "0.3.3" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 330 | dependencies = [ 331 | "bitflags", 332 | "fuchsia-zircon-sys", 333 | ] 334 | 335 | [[package]] 336 | name = "fuchsia-zircon-sys" 337 | version = "0.3.3" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 340 | 341 | [[package]] 342 | name = "futures-channel" 343 | version = "0.3.4" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" 346 | dependencies = [ 347 | "futures-core", 348 | ] 349 | 350 | [[package]] 351 | name = "futures-core" 352 | version = "0.3.4" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" 355 | 356 | [[package]] 357 | name = "futures-sink" 358 | version = "0.3.4" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" 361 | 362 | [[package]] 363 | name = "futures-task" 364 | version = "0.3.4" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" 367 | 368 | [[package]] 369 | name = "futures-util" 370 | version = "0.3.4" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" 373 | dependencies = [ 374 | "futures-core", 375 | "futures-task", 376 | "pin-utils", 377 | ] 378 | 379 | [[package]] 380 | name = "gdk-pixbuf" 381 | version = "0.3.0" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "16160d212ae91abe9f3324c3fb233929ba322dde63585d15cda3336f8c529ed1" 384 | dependencies = [ 385 | "gdk-pixbuf-sys", 386 | "glib", 387 | "glib-sys", 388 | "gobject-sys", 389 | "libc", 390 | ] 391 | 392 | [[package]] 393 | name = "gdk-pixbuf-sys" 394 | version = "0.5.0" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "798f97101eea8180da363d0e80e07ec7ec6d1809306601c0100c1de5bc8b4f52" 397 | dependencies = [ 398 | "bitflags", 399 | "gio-sys", 400 | "glib-sys", 401 | "gobject-sys", 402 | "libc", 403 | "pkg-config", 404 | ] 405 | 406 | [[package]] 407 | name = "getrandom" 408 | version = "0.1.14" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 411 | dependencies = [ 412 | "cfg-if", 413 | "libc", 414 | "wasi", 415 | ] 416 | 417 | [[package]] 418 | name = "gio-sys" 419 | version = "0.5.0" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "a303bbf7a5e75ab3b627117ff10e495d1b9e97e1d68966285ac2b1f6270091bc" 422 | dependencies = [ 423 | "bitflags", 424 | "glib-sys", 425 | "gobject-sys", 426 | "libc", 427 | "pkg-config", 428 | ] 429 | 430 | [[package]] 431 | name = "glib" 432 | version = "0.4.1" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "b9b0452824cc63066940f01adc721804919f0b76cdba3cfab977b00b87f16d4a" 435 | dependencies = [ 436 | "bitflags", 437 | "glib-sys", 438 | "gobject-sys", 439 | "lazy_static", 440 | "libc", 441 | ] 442 | 443 | [[package]] 444 | name = "glib-sys" 445 | version = "0.5.0" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "d9693049613ff52b93013cc3d2590366d8e530366d288438724b73f6c7dc4be8" 448 | dependencies = [ 449 | "bitflags", 450 | "libc", 451 | "pkg-config", 452 | ] 453 | 454 | [[package]] 455 | name = "gobject-sys" 456 | version = "0.5.0" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "60d507c87a71b1143c66ed21a969be9b99a76df234b342d733e787e6c9c7d7c2" 459 | dependencies = [ 460 | "bitflags", 461 | "glib-sys", 462 | "libc", 463 | "pkg-config", 464 | ] 465 | 466 | [[package]] 467 | name = "h2" 468 | version = "0.2.4" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "377038bf3c89d18d6ca1431e7a5027194fbd724ca10592b9487ede5e8e144f42" 471 | dependencies = [ 472 | "bytes", 473 | "fnv", 474 | "futures-core", 475 | "futures-sink", 476 | "futures-util", 477 | "http", 478 | "indexmap", 479 | "log", 480 | "slab", 481 | "tokio", 482 | "tokio-util", 483 | ] 484 | 485 | [[package]] 486 | name = "heck" 487 | version = "0.3.1" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" 490 | dependencies = [ 491 | "unicode-segmentation", 492 | ] 493 | 494 | [[package]] 495 | name = "hermit-abi" 496 | version = "0.1.10" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" 499 | dependencies = [ 500 | "libc", 501 | ] 502 | 503 | [[package]] 504 | name = "htb" 505 | version = "0.1.0" 506 | dependencies = [ 507 | "async-trait", 508 | "chrono", 509 | "clap", 510 | "colored", 511 | "dirs 2.0.2", 512 | "htmlescape", 513 | "lazy_static", 514 | "libnotify", 515 | "linefeed", 516 | "prettytable-rs", 517 | "reqwest", 518 | "serde", 519 | "serde_json", 520 | "termion", 521 | "tokio", 522 | ] 523 | 524 | [[package]] 525 | name = "htmlescape" 526 | version = "0.3.1" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" 529 | 530 | [[package]] 531 | name = "http" 532 | version = "0.2.1" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" 535 | dependencies = [ 536 | "bytes", 537 | "fnv", 538 | "itoa", 539 | ] 540 | 541 | [[package]] 542 | name = "http-body" 543 | version = "0.3.1" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" 546 | dependencies = [ 547 | "bytes", 548 | "http", 549 | ] 550 | 551 | [[package]] 552 | name = "httparse" 553 | version = "1.3.4" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" 556 | 557 | [[package]] 558 | name = "hyper" 559 | version = "0.13.4" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "ed6081100e960d9d74734659ffc9cc91daf1c0fc7aceb8eaa94ee1a3f5046f2e" 562 | dependencies = [ 563 | "bytes", 564 | "futures-channel", 565 | "futures-core", 566 | "futures-util", 567 | "h2", 568 | "http", 569 | "http-body", 570 | "httparse", 571 | "itoa", 572 | "log", 573 | "net2", 574 | "pin-project", 575 | "time", 576 | "tokio", 577 | "tower-service", 578 | "want", 579 | ] 580 | 581 | [[package]] 582 | name = "hyper-tls" 583 | version = "0.4.1" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa" 586 | dependencies = [ 587 | "bytes", 588 | "hyper", 589 | "native-tls", 590 | "tokio", 591 | "tokio-tls", 592 | ] 593 | 594 | [[package]] 595 | name = "idna" 596 | version = "0.2.0" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 599 | dependencies = [ 600 | "matches", 601 | "unicode-bidi", 602 | "unicode-normalization", 603 | ] 604 | 605 | [[package]] 606 | name = "indexmap" 607 | version = "1.3.2" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" 610 | dependencies = [ 611 | "autocfg 1.0.0", 612 | ] 613 | 614 | [[package]] 615 | name = "iovec" 616 | version = "0.1.4" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 619 | dependencies = [ 620 | "libc", 621 | ] 622 | 623 | [[package]] 624 | name = "itoa" 625 | version = "0.4.5" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" 628 | 629 | [[package]] 630 | name = "js-sys" 631 | version = "0.3.37" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" 634 | dependencies = [ 635 | "wasm-bindgen", 636 | ] 637 | 638 | [[package]] 639 | name = "kernel32-sys" 640 | version = "0.2.2" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 643 | dependencies = [ 644 | "winapi 0.2.8", 645 | "winapi-build", 646 | ] 647 | 648 | [[package]] 649 | name = "lazy_static" 650 | version = "1.4.0" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 653 | 654 | [[package]] 655 | name = "libc" 656 | version = "0.2.68" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" 659 | 660 | [[package]] 661 | name = "libnotify" 662 | version = "1.0.3" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "10506a4f8bc6f8f7ccc6fde3a8290378d7aed3d1a26dca606a73e2ffe140cc2d" 665 | dependencies = [ 666 | "gdk-pixbuf", 667 | "gdk-pixbuf-sys", 668 | "glib", 669 | "glib-sys", 670 | "gobject-sys", 671 | "libnotify-sys", 672 | ] 673 | 674 | [[package]] 675 | name = "libnotify-sys" 676 | version = "1.0.2" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "a0a716b9b7d24ed10f1eb431e1527fa13c9a4bf2d4fa68bb3e54da1d0747383c" 679 | dependencies = [ 680 | "bitflags", 681 | "gdk-pixbuf-sys", 682 | "glib-sys", 683 | "gobject-sys", 684 | "libc", 685 | "pkg-config", 686 | ] 687 | 688 | [[package]] 689 | name = "linefeed" 690 | version = "0.6.0" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "28715d08e35c6c074f9ae6b2e6a2420bac75d050c66ecd669d7d5b98e2caa036" 693 | dependencies = [ 694 | "dirs 1.0.5", 695 | "mortal", 696 | "winapi 0.3.8", 697 | ] 698 | 699 | [[package]] 700 | name = "log" 701 | version = "0.4.8" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 704 | dependencies = [ 705 | "cfg-if", 706 | ] 707 | 708 | [[package]] 709 | name = "matches" 710 | version = "0.1.8" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 713 | 714 | [[package]] 715 | name = "maybe-uninit" 716 | version = "2.0.0" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 719 | 720 | [[package]] 721 | name = "memchr" 722 | version = "2.3.3" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 725 | 726 | [[package]] 727 | name = "mime" 728 | version = "0.3.16" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 731 | 732 | [[package]] 733 | name = "mime_guess" 734 | version = "2.0.3" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" 737 | dependencies = [ 738 | "mime", 739 | "unicase", 740 | ] 741 | 742 | [[package]] 743 | name = "mio" 744 | version = "0.6.21" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" 747 | dependencies = [ 748 | "cfg-if", 749 | "fuchsia-zircon", 750 | "fuchsia-zircon-sys", 751 | "iovec", 752 | "kernel32-sys", 753 | "libc", 754 | "log", 755 | "miow 0.2.1", 756 | "net2", 757 | "slab", 758 | "winapi 0.2.8", 759 | ] 760 | 761 | [[package]] 762 | name = "mio-named-pipes" 763 | version = "0.1.6" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" 766 | dependencies = [ 767 | "log", 768 | "mio", 769 | "miow 0.3.3", 770 | "winapi 0.3.8", 771 | ] 772 | 773 | [[package]] 774 | name = "mio-uds" 775 | version = "0.6.7" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" 778 | dependencies = [ 779 | "iovec", 780 | "libc", 781 | "mio", 782 | ] 783 | 784 | [[package]] 785 | name = "miow" 786 | version = "0.2.1" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 789 | dependencies = [ 790 | "kernel32-sys", 791 | "net2", 792 | "winapi 0.2.8", 793 | "ws2_32-sys", 794 | ] 795 | 796 | [[package]] 797 | name = "miow" 798 | version = "0.3.3" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" 801 | dependencies = [ 802 | "socket2", 803 | "winapi 0.3.8", 804 | ] 805 | 806 | [[package]] 807 | name = "mortal" 808 | version = "0.2.1" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "d96ca97d37cb87ea3d658e71c8c2e2f2d420eca5deec047c5a283aa810a900ff" 811 | dependencies = [ 812 | "bitflags", 813 | "libc", 814 | "nix", 815 | "smallstr", 816 | "terminfo", 817 | "unicode-normalization", 818 | "unicode-width", 819 | "winapi 0.3.8", 820 | ] 821 | 822 | [[package]] 823 | name = "native-tls" 824 | version = "0.2.4" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" 827 | dependencies = [ 828 | "lazy_static", 829 | "libc", 830 | "log", 831 | "openssl", 832 | "openssl-probe", 833 | "openssl-sys", 834 | "schannel", 835 | "security-framework", 836 | "security-framework-sys", 837 | "tempfile", 838 | ] 839 | 840 | [[package]] 841 | name = "net2" 842 | version = "0.2.33" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" 845 | dependencies = [ 846 | "cfg-if", 847 | "libc", 848 | "winapi 0.3.8", 849 | ] 850 | 851 | [[package]] 852 | name = "nix" 853 | version = "0.14.1" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" 856 | dependencies = [ 857 | "bitflags", 858 | "cc", 859 | "cfg-if", 860 | "libc", 861 | "void", 862 | ] 863 | 864 | [[package]] 865 | name = "nom" 866 | version = "4.2.3" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" 869 | dependencies = [ 870 | "memchr", 871 | "version_check 0.1.5", 872 | ] 873 | 874 | [[package]] 875 | name = "num-integer" 876 | version = "0.1.42" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" 879 | dependencies = [ 880 | "autocfg 1.0.0", 881 | "num-traits", 882 | ] 883 | 884 | [[package]] 885 | name = "num-traits" 886 | version = "0.2.11" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" 889 | dependencies = [ 890 | "autocfg 1.0.0", 891 | ] 892 | 893 | [[package]] 894 | name = "num_cpus" 895 | version = "1.12.0" 896 | source = "registry+https://github.com/rust-lang/crates.io-index" 897 | checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" 898 | dependencies = [ 899 | "hermit-abi", 900 | "libc", 901 | ] 902 | 903 | [[package]] 904 | name = "numtoa" 905 | version = "0.1.0" 906 | source = "registry+https://github.com/rust-lang/crates.io-index" 907 | checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" 908 | 909 | [[package]] 910 | name = "openssl" 911 | version = "0.10.28" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52" 914 | dependencies = [ 915 | "bitflags", 916 | "cfg-if", 917 | "foreign-types", 918 | "lazy_static", 919 | "libc", 920 | "openssl-sys", 921 | ] 922 | 923 | [[package]] 924 | name = "openssl-probe" 925 | version = "0.1.2" 926 | source = "registry+https://github.com/rust-lang/crates.io-index" 927 | checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" 928 | 929 | [[package]] 930 | name = "openssl-sys" 931 | version = "0.9.54" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" 934 | dependencies = [ 935 | "autocfg 1.0.0", 936 | "cc", 937 | "libc", 938 | "pkg-config", 939 | "vcpkg", 940 | ] 941 | 942 | [[package]] 943 | name = "percent-encoding" 944 | version = "2.1.0" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 947 | 948 | [[package]] 949 | name = "phf" 950 | version = "0.7.24" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" 953 | dependencies = [ 954 | "phf_shared", 955 | ] 956 | 957 | [[package]] 958 | name = "phf_codegen" 959 | version = "0.7.24" 960 | source = "registry+https://github.com/rust-lang/crates.io-index" 961 | checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" 962 | dependencies = [ 963 | "phf_generator", 964 | "phf_shared", 965 | ] 966 | 967 | [[package]] 968 | name = "phf_generator" 969 | version = "0.7.24" 970 | source = "registry+https://github.com/rust-lang/crates.io-index" 971 | checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" 972 | dependencies = [ 973 | "phf_shared", 974 | "rand 0.6.5", 975 | ] 976 | 977 | [[package]] 978 | name = "phf_shared" 979 | version = "0.7.24" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" 982 | dependencies = [ 983 | "siphasher", 984 | ] 985 | 986 | [[package]] 987 | name = "pin-project" 988 | version = "0.4.8" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" 991 | dependencies = [ 992 | "pin-project-internal", 993 | ] 994 | 995 | [[package]] 996 | name = "pin-project-internal" 997 | version = "0.4.8" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" 1000 | dependencies = [ 1001 | "proc-macro2", 1002 | "quote", 1003 | "syn", 1004 | ] 1005 | 1006 | [[package]] 1007 | name = "pin-project-lite" 1008 | version = "0.1.4" 1009 | source = "registry+https://github.com/rust-lang/crates.io-index" 1010 | checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" 1011 | 1012 | [[package]] 1013 | name = "pin-utils" 1014 | version = "0.1.0-alpha.4" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" 1017 | 1018 | [[package]] 1019 | name = "pkg-config" 1020 | version = "0.3.17" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" 1023 | 1024 | [[package]] 1025 | name = "ppv-lite86" 1026 | version = "0.2.6" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" 1029 | 1030 | [[package]] 1031 | name = "prettytable-rs" 1032 | version = "0.8.0" 1033 | source = "registry+https://github.com/rust-lang/crates.io-index" 1034 | checksum = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e" 1035 | dependencies = [ 1036 | "atty", 1037 | "csv", 1038 | "encode_unicode", 1039 | "lazy_static", 1040 | "term", 1041 | "unicode-width", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "proc-macro-error" 1046 | version = "0.4.12" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" 1049 | dependencies = [ 1050 | "proc-macro-error-attr", 1051 | "proc-macro2", 1052 | "quote", 1053 | "syn", 1054 | "version_check 0.9.1", 1055 | ] 1056 | 1057 | [[package]] 1058 | name = "proc-macro-error-attr" 1059 | version = "0.4.12" 1060 | source = "registry+https://github.com/rust-lang/crates.io-index" 1061 | checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" 1062 | dependencies = [ 1063 | "proc-macro2", 1064 | "quote", 1065 | "syn", 1066 | "syn-mid", 1067 | "version_check 0.9.1", 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "proc-macro2" 1072 | version = "1.0.10" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" 1075 | dependencies = [ 1076 | "unicode-xid", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "quote" 1081 | version = "1.0.3" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" 1084 | dependencies = [ 1085 | "proc-macro2", 1086 | ] 1087 | 1088 | [[package]] 1089 | name = "rand" 1090 | version = "0.6.5" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 1093 | dependencies = [ 1094 | "autocfg 0.1.7", 1095 | "libc", 1096 | "rand_chacha 0.1.1", 1097 | "rand_core 0.4.2", 1098 | "rand_hc 0.1.0", 1099 | "rand_isaac", 1100 | "rand_jitter", 1101 | "rand_os", 1102 | "rand_pcg", 1103 | "rand_xorshift", 1104 | "winapi 0.3.8", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "rand" 1109 | version = "0.7.3" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1112 | dependencies = [ 1113 | "getrandom", 1114 | "libc", 1115 | "rand_chacha 0.2.2", 1116 | "rand_core 0.5.1", 1117 | "rand_hc 0.2.0", 1118 | ] 1119 | 1120 | [[package]] 1121 | name = "rand_chacha" 1122 | version = "0.1.1" 1123 | source = "registry+https://github.com/rust-lang/crates.io-index" 1124 | checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 1125 | dependencies = [ 1126 | "autocfg 0.1.7", 1127 | "rand_core 0.3.1", 1128 | ] 1129 | 1130 | [[package]] 1131 | name = "rand_chacha" 1132 | version = "0.2.2" 1133 | source = "registry+https://github.com/rust-lang/crates.io-index" 1134 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1135 | dependencies = [ 1136 | "ppv-lite86", 1137 | "rand_core 0.5.1", 1138 | ] 1139 | 1140 | [[package]] 1141 | name = "rand_core" 1142 | version = "0.3.1" 1143 | source = "registry+https://github.com/rust-lang/crates.io-index" 1144 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 1145 | dependencies = [ 1146 | "rand_core 0.4.2", 1147 | ] 1148 | 1149 | [[package]] 1150 | name = "rand_core" 1151 | version = "0.4.2" 1152 | source = "registry+https://github.com/rust-lang/crates.io-index" 1153 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 1154 | 1155 | [[package]] 1156 | name = "rand_core" 1157 | version = "0.5.1" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1160 | dependencies = [ 1161 | "getrandom", 1162 | ] 1163 | 1164 | [[package]] 1165 | name = "rand_hc" 1166 | version = "0.1.0" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 1169 | dependencies = [ 1170 | "rand_core 0.3.1", 1171 | ] 1172 | 1173 | [[package]] 1174 | name = "rand_hc" 1175 | version = "0.2.0" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1178 | dependencies = [ 1179 | "rand_core 0.5.1", 1180 | ] 1181 | 1182 | [[package]] 1183 | name = "rand_isaac" 1184 | version = "0.1.1" 1185 | source = "registry+https://github.com/rust-lang/crates.io-index" 1186 | checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 1187 | dependencies = [ 1188 | "rand_core 0.3.1", 1189 | ] 1190 | 1191 | [[package]] 1192 | name = "rand_jitter" 1193 | version = "0.1.4" 1194 | source = "registry+https://github.com/rust-lang/crates.io-index" 1195 | checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" 1196 | dependencies = [ 1197 | "libc", 1198 | "rand_core 0.4.2", 1199 | "winapi 0.3.8", 1200 | ] 1201 | 1202 | [[package]] 1203 | name = "rand_os" 1204 | version = "0.1.3" 1205 | source = "registry+https://github.com/rust-lang/crates.io-index" 1206 | checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 1207 | dependencies = [ 1208 | "cloudabi", 1209 | "fuchsia-cprng", 1210 | "libc", 1211 | "rand_core 0.4.2", 1212 | "rdrand", 1213 | "winapi 0.3.8", 1214 | ] 1215 | 1216 | [[package]] 1217 | name = "rand_pcg" 1218 | version = "0.1.2" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 1221 | dependencies = [ 1222 | "autocfg 0.1.7", 1223 | "rand_core 0.4.2", 1224 | ] 1225 | 1226 | [[package]] 1227 | name = "rand_xorshift" 1228 | version = "0.1.1" 1229 | source = "registry+https://github.com/rust-lang/crates.io-index" 1230 | checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 1231 | dependencies = [ 1232 | "rand_core 0.3.1", 1233 | ] 1234 | 1235 | [[package]] 1236 | name = "rdrand" 1237 | version = "0.4.0" 1238 | source = "registry+https://github.com/rust-lang/crates.io-index" 1239 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 1240 | dependencies = [ 1241 | "rand_core 0.3.1", 1242 | ] 1243 | 1244 | [[package]] 1245 | name = "redox_syscall" 1246 | version = "0.1.56" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 1249 | 1250 | [[package]] 1251 | name = "redox_termios" 1252 | version = "0.1.1" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 1255 | dependencies = [ 1256 | "redox_syscall", 1257 | ] 1258 | 1259 | [[package]] 1260 | name = "redox_users" 1261 | version = "0.3.4" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" 1264 | dependencies = [ 1265 | "getrandom", 1266 | "redox_syscall", 1267 | "rust-argon2", 1268 | ] 1269 | 1270 | [[package]] 1271 | name = "regex-automata" 1272 | version = "0.1.9" 1273 | source = "registry+https://github.com/rust-lang/crates.io-index" 1274 | checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" 1275 | dependencies = [ 1276 | "byteorder", 1277 | ] 1278 | 1279 | [[package]] 1280 | name = "remove_dir_all" 1281 | version = "0.5.2" 1282 | source = "registry+https://github.com/rust-lang/crates.io-index" 1283 | checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" 1284 | dependencies = [ 1285 | "winapi 0.3.8", 1286 | ] 1287 | 1288 | [[package]] 1289 | name = "reqwest" 1290 | version = "0.10.4" 1291 | source = "registry+https://github.com/rust-lang/crates.io-index" 1292 | checksum = "02b81e49ddec5109a9dcfc5f2a317ff53377c915e9ae9d4f2fb50914b85614e2" 1293 | dependencies = [ 1294 | "base64", 1295 | "bytes", 1296 | "encoding_rs", 1297 | "futures-core", 1298 | "futures-util", 1299 | "http", 1300 | "http-body", 1301 | "hyper", 1302 | "hyper-tls", 1303 | "js-sys", 1304 | "lazy_static", 1305 | "log", 1306 | "mime", 1307 | "mime_guess", 1308 | "native-tls", 1309 | "percent-encoding", 1310 | "pin-project-lite", 1311 | "serde", 1312 | "serde_json", 1313 | "serde_urlencoded", 1314 | "time", 1315 | "tokio", 1316 | "tokio-tls", 1317 | "url", 1318 | "wasm-bindgen", 1319 | "wasm-bindgen-futures", 1320 | "web-sys", 1321 | "winreg", 1322 | ] 1323 | 1324 | [[package]] 1325 | name = "rust-argon2" 1326 | version = "0.7.0" 1327 | source = "registry+https://github.com/rust-lang/crates.io-index" 1328 | checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" 1329 | dependencies = [ 1330 | "base64", 1331 | "blake2b_simd", 1332 | "constant_time_eq", 1333 | "crossbeam-utils", 1334 | ] 1335 | 1336 | [[package]] 1337 | name = "ryu" 1338 | version = "1.0.3" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" 1341 | 1342 | [[package]] 1343 | name = "schannel" 1344 | version = "0.1.18" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | checksum = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19" 1347 | dependencies = [ 1348 | "lazy_static", 1349 | "winapi 0.3.8", 1350 | ] 1351 | 1352 | [[package]] 1353 | name = "security-framework" 1354 | version = "0.4.2" 1355 | source = "registry+https://github.com/rust-lang/crates.io-index" 1356 | checksum = "572dfa3a0785509e7a44b5b4bebcf94d41ba34e9ed9eb9df722545c3b3c4144a" 1357 | dependencies = [ 1358 | "bitflags", 1359 | "core-foundation", 1360 | "core-foundation-sys", 1361 | "libc", 1362 | "security-framework-sys", 1363 | ] 1364 | 1365 | [[package]] 1366 | name = "security-framework-sys" 1367 | version = "0.4.2" 1368 | source = "registry+https://github.com/rust-lang/crates.io-index" 1369 | checksum = "8ddb15a5fec93b7021b8a9e96009c5d8d51c15673569f7c0f6b7204e5b7b404f" 1370 | dependencies = [ 1371 | "core-foundation-sys", 1372 | "libc", 1373 | ] 1374 | 1375 | [[package]] 1376 | name = "serde" 1377 | version = "1.0.106" 1378 | source = "registry+https://github.com/rust-lang/crates.io-index" 1379 | checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" 1380 | dependencies = [ 1381 | "serde_derive", 1382 | ] 1383 | 1384 | [[package]] 1385 | name = "serde_derive" 1386 | version = "1.0.106" 1387 | source = "registry+https://github.com/rust-lang/crates.io-index" 1388 | checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" 1389 | dependencies = [ 1390 | "proc-macro2", 1391 | "quote", 1392 | "syn", 1393 | ] 1394 | 1395 | [[package]] 1396 | name = "serde_json" 1397 | version = "1.0.50" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "78a7a12c167809363ec3bd7329fc0a3369056996de43c4b37ef3cd54a6ce4867" 1400 | dependencies = [ 1401 | "itoa", 1402 | "ryu", 1403 | "serde", 1404 | ] 1405 | 1406 | [[package]] 1407 | name = "serde_urlencoded" 1408 | version = "0.6.1" 1409 | source = "registry+https://github.com/rust-lang/crates.io-index" 1410 | checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" 1411 | dependencies = [ 1412 | "dtoa", 1413 | "itoa", 1414 | "serde", 1415 | "url", 1416 | ] 1417 | 1418 | [[package]] 1419 | name = "signal-hook-registry" 1420 | version = "1.2.0" 1421 | source = "registry+https://github.com/rust-lang/crates.io-index" 1422 | checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" 1423 | dependencies = [ 1424 | "arc-swap", 1425 | "libc", 1426 | ] 1427 | 1428 | [[package]] 1429 | name = "siphasher" 1430 | version = "0.2.3" 1431 | source = "registry+https://github.com/rust-lang/crates.io-index" 1432 | checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" 1433 | 1434 | [[package]] 1435 | name = "slab" 1436 | version = "0.4.2" 1437 | source = "registry+https://github.com/rust-lang/crates.io-index" 1438 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 1439 | 1440 | [[package]] 1441 | name = "smallstr" 1442 | version = "0.1.0" 1443 | source = "registry+https://github.com/rust-lang/crates.io-index" 1444 | checksum = "6aa65bb4d5b2bbc90d36af64e29802f788aa614783fa1d0df011800ddcec6e8e" 1445 | dependencies = [ 1446 | "smallvec 0.6.13", 1447 | ] 1448 | 1449 | [[package]] 1450 | name = "smallvec" 1451 | version = "0.6.13" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" 1454 | dependencies = [ 1455 | "maybe-uninit", 1456 | ] 1457 | 1458 | [[package]] 1459 | name = "smallvec" 1460 | version = "1.2.0" 1461 | source = "registry+https://github.com/rust-lang/crates.io-index" 1462 | checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" 1463 | 1464 | [[package]] 1465 | name = "socket2" 1466 | version = "0.3.12" 1467 | source = "registry+https://github.com/rust-lang/crates.io-index" 1468 | checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" 1469 | dependencies = [ 1470 | "cfg-if", 1471 | "libc", 1472 | "redox_syscall", 1473 | "winapi 0.3.8", 1474 | ] 1475 | 1476 | [[package]] 1477 | name = "strsim" 1478 | version = "0.9.3" 1479 | source = "registry+https://github.com/rust-lang/crates.io-index" 1480 | checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" 1481 | 1482 | [[package]] 1483 | name = "syn" 1484 | version = "1.0.17" 1485 | source = "registry+https://github.com/rust-lang/crates.io-index" 1486 | checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" 1487 | dependencies = [ 1488 | "proc-macro2", 1489 | "quote", 1490 | "unicode-xid", 1491 | ] 1492 | 1493 | [[package]] 1494 | name = "syn-mid" 1495 | version = "0.5.0" 1496 | source = "registry+https://github.com/rust-lang/crates.io-index" 1497 | checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" 1498 | dependencies = [ 1499 | "proc-macro2", 1500 | "quote", 1501 | "syn", 1502 | ] 1503 | 1504 | [[package]] 1505 | name = "tempfile" 1506 | version = "3.1.0" 1507 | source = "registry+https://github.com/rust-lang/crates.io-index" 1508 | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 1509 | dependencies = [ 1510 | "cfg-if", 1511 | "libc", 1512 | "rand 0.7.3", 1513 | "redox_syscall", 1514 | "remove_dir_all", 1515 | "winapi 0.3.8", 1516 | ] 1517 | 1518 | [[package]] 1519 | name = "term" 1520 | version = "0.5.2" 1521 | source = "registry+https://github.com/rust-lang/crates.io-index" 1522 | checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" 1523 | dependencies = [ 1524 | "byteorder", 1525 | "dirs 1.0.5", 1526 | "winapi 0.3.8", 1527 | ] 1528 | 1529 | [[package]] 1530 | name = "terminfo" 1531 | version = "0.6.1" 1532 | source = "registry+https://github.com/rust-lang/crates.io-index" 1533 | checksum = "8e51065bafd2abe106b6036483b69d1741f4a1ec56ce8a2378de341637de689e" 1534 | dependencies = [ 1535 | "fnv", 1536 | "nom", 1537 | "phf", 1538 | "phf_codegen", 1539 | ] 1540 | 1541 | [[package]] 1542 | name = "termion" 1543 | version = "1.5.5" 1544 | source = "registry+https://github.com/rust-lang/crates.io-index" 1545 | checksum = "c22cec9d8978d906be5ac94bceb5a010d885c626c4c8855721a4dbd20e3ac905" 1546 | dependencies = [ 1547 | "libc", 1548 | "numtoa", 1549 | "redox_syscall", 1550 | "redox_termios", 1551 | ] 1552 | 1553 | [[package]] 1554 | name = "textwrap" 1555 | version = "0.11.0" 1556 | source = "registry+https://github.com/rust-lang/crates.io-index" 1557 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 1558 | dependencies = [ 1559 | "unicode-width", 1560 | ] 1561 | 1562 | [[package]] 1563 | name = "time" 1564 | version = "0.1.42" 1565 | source = "registry+https://github.com/rust-lang/crates.io-index" 1566 | checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" 1567 | dependencies = [ 1568 | "libc", 1569 | "redox_syscall", 1570 | "winapi 0.3.8", 1571 | ] 1572 | 1573 | [[package]] 1574 | name = "tokio" 1575 | version = "0.2.16" 1576 | source = "registry+https://github.com/rust-lang/crates.io-index" 1577 | checksum = "ee5a0dd887e37d37390c13ff8ac830f992307fe30a1fff0ab8427af67211ba28" 1578 | dependencies = [ 1579 | "bytes", 1580 | "fnv", 1581 | "futures-core", 1582 | "iovec", 1583 | "lazy_static", 1584 | "libc", 1585 | "memchr", 1586 | "mio", 1587 | "mio-named-pipes", 1588 | "mio-uds", 1589 | "num_cpus", 1590 | "pin-project-lite", 1591 | "signal-hook-registry", 1592 | "slab", 1593 | "tokio-macros", 1594 | "winapi 0.3.8", 1595 | ] 1596 | 1597 | [[package]] 1598 | name = "tokio-macros" 1599 | version = "0.2.5" 1600 | source = "registry+https://github.com/rust-lang/crates.io-index" 1601 | checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" 1602 | dependencies = [ 1603 | "proc-macro2", 1604 | "quote", 1605 | "syn", 1606 | ] 1607 | 1608 | [[package]] 1609 | name = "tokio-tls" 1610 | version = "0.3.0" 1611 | source = "registry+https://github.com/rust-lang/crates.io-index" 1612 | checksum = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828" 1613 | dependencies = [ 1614 | "native-tls", 1615 | "tokio", 1616 | ] 1617 | 1618 | [[package]] 1619 | name = "tokio-util" 1620 | version = "0.3.1" 1621 | source = "registry+https://github.com/rust-lang/crates.io-index" 1622 | checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" 1623 | dependencies = [ 1624 | "bytes", 1625 | "futures-core", 1626 | "futures-sink", 1627 | "log", 1628 | "pin-project-lite", 1629 | "tokio", 1630 | ] 1631 | 1632 | [[package]] 1633 | name = "tower-service" 1634 | version = "0.3.0" 1635 | source = "registry+https://github.com/rust-lang/crates.io-index" 1636 | checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" 1637 | 1638 | [[package]] 1639 | name = "try-lock" 1640 | version = "0.2.2" 1641 | source = "registry+https://github.com/rust-lang/crates.io-index" 1642 | checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" 1643 | 1644 | [[package]] 1645 | name = "unicase" 1646 | version = "2.6.0" 1647 | source = "registry+https://github.com/rust-lang/crates.io-index" 1648 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1649 | dependencies = [ 1650 | "version_check 0.9.1", 1651 | ] 1652 | 1653 | [[package]] 1654 | name = "unicode-bidi" 1655 | version = "0.3.4" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1658 | dependencies = [ 1659 | "matches", 1660 | ] 1661 | 1662 | [[package]] 1663 | name = "unicode-normalization" 1664 | version = "0.1.12" 1665 | source = "registry+https://github.com/rust-lang/crates.io-index" 1666 | checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" 1667 | dependencies = [ 1668 | "smallvec 1.2.0", 1669 | ] 1670 | 1671 | [[package]] 1672 | name = "unicode-segmentation" 1673 | version = "1.6.0" 1674 | source = "registry+https://github.com/rust-lang/crates.io-index" 1675 | checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" 1676 | 1677 | [[package]] 1678 | name = "unicode-width" 1679 | version = "0.1.7" 1680 | source = "registry+https://github.com/rust-lang/crates.io-index" 1681 | checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" 1682 | 1683 | [[package]] 1684 | name = "unicode-xid" 1685 | version = "0.2.0" 1686 | source = "registry+https://github.com/rust-lang/crates.io-index" 1687 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 1688 | 1689 | [[package]] 1690 | name = "url" 1691 | version = "2.1.1" 1692 | source = "registry+https://github.com/rust-lang/crates.io-index" 1693 | checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" 1694 | dependencies = [ 1695 | "idna", 1696 | "matches", 1697 | "percent-encoding", 1698 | ] 1699 | 1700 | [[package]] 1701 | name = "vcpkg" 1702 | version = "0.2.8" 1703 | source = "registry+https://github.com/rust-lang/crates.io-index" 1704 | checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" 1705 | 1706 | [[package]] 1707 | name = "vec_map" 1708 | version = "0.8.1" 1709 | source = "registry+https://github.com/rust-lang/crates.io-index" 1710 | checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 1711 | 1712 | [[package]] 1713 | name = "version_check" 1714 | version = "0.1.5" 1715 | source = "registry+https://github.com/rust-lang/crates.io-index" 1716 | checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" 1717 | 1718 | [[package]] 1719 | name = "version_check" 1720 | version = "0.9.1" 1721 | source = "registry+https://github.com/rust-lang/crates.io-index" 1722 | checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" 1723 | 1724 | [[package]] 1725 | name = "void" 1726 | version = "1.0.2" 1727 | source = "registry+https://github.com/rust-lang/crates.io-index" 1728 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 1729 | 1730 | [[package]] 1731 | name = "want" 1732 | version = "0.3.0" 1733 | source = "registry+https://github.com/rust-lang/crates.io-index" 1734 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1735 | dependencies = [ 1736 | "log", 1737 | "try-lock", 1738 | ] 1739 | 1740 | [[package]] 1741 | name = "wasi" 1742 | version = "0.9.0+wasi-snapshot-preview1" 1743 | source = "registry+https://github.com/rust-lang/crates.io-index" 1744 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1745 | 1746 | [[package]] 1747 | name = "wasm-bindgen" 1748 | version = "0.2.60" 1749 | source = "registry+https://github.com/rust-lang/crates.io-index" 1750 | checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" 1751 | dependencies = [ 1752 | "cfg-if", 1753 | "serde", 1754 | "serde_json", 1755 | "wasm-bindgen-macro", 1756 | ] 1757 | 1758 | [[package]] 1759 | name = "wasm-bindgen-backend" 1760 | version = "0.2.60" 1761 | source = "registry+https://github.com/rust-lang/crates.io-index" 1762 | checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" 1763 | dependencies = [ 1764 | "bumpalo", 1765 | "lazy_static", 1766 | "log", 1767 | "proc-macro2", 1768 | "quote", 1769 | "syn", 1770 | "wasm-bindgen-shared", 1771 | ] 1772 | 1773 | [[package]] 1774 | name = "wasm-bindgen-futures" 1775 | version = "0.4.10" 1776 | source = "registry+https://github.com/rust-lang/crates.io-index" 1777 | checksum = "7add542ea1ac7fdaa9dc25e031a6af33b7d63376292bd24140c637d00d1c312a" 1778 | dependencies = [ 1779 | "cfg-if", 1780 | "js-sys", 1781 | "wasm-bindgen", 1782 | "web-sys", 1783 | ] 1784 | 1785 | [[package]] 1786 | name = "wasm-bindgen-macro" 1787 | version = "0.2.60" 1788 | source = "registry+https://github.com/rust-lang/crates.io-index" 1789 | checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" 1790 | dependencies = [ 1791 | "quote", 1792 | "wasm-bindgen-macro-support", 1793 | ] 1794 | 1795 | [[package]] 1796 | name = "wasm-bindgen-macro-support" 1797 | version = "0.2.60" 1798 | source = "registry+https://github.com/rust-lang/crates.io-index" 1799 | checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" 1800 | dependencies = [ 1801 | "proc-macro2", 1802 | "quote", 1803 | "syn", 1804 | "wasm-bindgen-backend", 1805 | "wasm-bindgen-shared", 1806 | ] 1807 | 1808 | [[package]] 1809 | name = "wasm-bindgen-shared" 1810 | version = "0.2.60" 1811 | source = "registry+https://github.com/rust-lang/crates.io-index" 1812 | checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" 1813 | 1814 | [[package]] 1815 | name = "web-sys" 1816 | version = "0.3.37" 1817 | source = "registry+https://github.com/rust-lang/crates.io-index" 1818 | checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb" 1819 | dependencies = [ 1820 | "js-sys", 1821 | "wasm-bindgen", 1822 | ] 1823 | 1824 | [[package]] 1825 | name = "winapi" 1826 | version = "0.2.8" 1827 | source = "registry+https://github.com/rust-lang/crates.io-index" 1828 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1829 | 1830 | [[package]] 1831 | name = "winapi" 1832 | version = "0.3.8" 1833 | source = "registry+https://github.com/rust-lang/crates.io-index" 1834 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 1835 | dependencies = [ 1836 | "winapi-i686-pc-windows-gnu", 1837 | "winapi-x86_64-pc-windows-gnu", 1838 | ] 1839 | 1840 | [[package]] 1841 | name = "winapi-build" 1842 | version = "0.1.1" 1843 | source = "registry+https://github.com/rust-lang/crates.io-index" 1844 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1845 | 1846 | [[package]] 1847 | name = "winapi-i686-pc-windows-gnu" 1848 | version = "0.4.0" 1849 | source = "registry+https://github.com/rust-lang/crates.io-index" 1850 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1851 | 1852 | [[package]] 1853 | name = "winapi-x86_64-pc-windows-gnu" 1854 | version = "0.4.0" 1855 | source = "registry+https://github.com/rust-lang/crates.io-index" 1856 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1857 | 1858 | [[package]] 1859 | name = "winreg" 1860 | version = "0.6.2" 1861 | source = "registry+https://github.com/rust-lang/crates.io-index" 1862 | checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" 1863 | dependencies = [ 1864 | "winapi 0.3.8", 1865 | ] 1866 | 1867 | [[package]] 1868 | name = "ws2_32-sys" 1869 | version = "0.2.1" 1870 | source = "registry+https://github.com/rust-lang/crates.io-index" 1871 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 1872 | dependencies = [ 1873 | "winapi 0.2.8", 1874 | "winapi-build", 1875 | ] 1876 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "htb" 3 | version = "0.1.0" 4 | authors = ["Antoine POPINEAU "] 5 | repository = "https://github.com/apognu/htb" 6 | license = "MIT" 7 | 8 | edition = "2018" 9 | 10 | [dependencies] 11 | tokio = { version = "^0.2", features = ["full"] } 12 | clap = { git = "https://github.com/clap-rs/clap" } 13 | serde = { version = "^1.0", features = ["derive"] } 14 | serde_json = "^1.0" 15 | reqwest = { version = "^0.10", features = ["json"] } 16 | prettytable-rs = "^0.8" 17 | colored = "1.9" 18 | dirs = "^2.0" 19 | termion = "^1.5" 20 | linefeed = "0.6" 21 | htmlescape = "0.3.1" 22 | libnotify = "^1.0" 23 | chrono = "0.4" 24 | lazy_static = "^1.4" 25 | async-trait = "0.1.30" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Antoine POPINEAU 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 | # htb - command-line interface to Hack The Box 2 | 3 | `htb` is a command-line client to [Hack The Box](https://www.hackthebox.eu). 4 | 5 | ![Screenshot of htb](https://repository-images.githubusercontent.com/253286330/92358b00-782f-11ea-96cf-e55b6374a079) 6 | 7 | ## Machines 8 | 9 | ### Search machines 10 | 11 | Machines can be listed with filters applied to find the find you are looking for. Currently, the following filters are supported: 12 | 13 | * `--owned`: display machines where you owned both _user_ and _root_ 14 | * `--unowned`: display machines you did not complete 15 | * `--spawned`: display machines that are currently started 16 | * `--active`: display active machines 17 | * `--retired`: display retired machines 18 | * `--todo`: display the machines that you added to your to-do list 19 | * `--assigned`: display the machines that are assigned to you 20 | * `--difficulty DIFFICULTY`: filter machines by their difficulty level 21 | * `--os OS`: filter by operating systems 22 | * `--name NAME`: display the machines matching the given name 23 | 24 | Also, the output can be sorted by `id`, `name`, `rating` or `release` date by providing the `--sort` option. Results can be sorted in descending order by providing the `-x` option. 25 | 26 | **Note:** `machines` is aliased to `machine`, `box` and `vm`. 27 | 28 | ```shell 29 | $ htb machines list --owned --retired 30 | NAME | OS | USER | ROOT | IP ADDRESS | RATING | POINTS 31 | Lame | Linux | 👤 15430 | # 16559 | 10.10.10.3 | ★ 4.3 | 🞋 20 32 | Legacy | Windows | 👤 12370 | # 12860 | 10.10.10.4 | ★ 4.2 | 🞋 20 33 | Devel | Windows | 👤 10626 | # 11128 | 10.10.10.5 | ★ 4.0 | 🞋 20 34 | Beep | Linux | 👤 7212 | # 7606 | 10.10.10.7 | ★ 4.7 | 🞋 20 35 | Optimum | Windows | 👤 11634 | # 7446 | 10.10.10.8 | ★ 4.7 | 🞋 20 36 | Grandpa | Windows | 👤 5104 | # 5257 | 10.10.10.14 | ★ 3.8 | 🞋 20 37 | Granny | Windows | 👤 4365 | # 4523 | 10.10.10.15 | ★ 3.2 | 🞋 20 38 | Blocky | Linux | 👤 4322 | # 4312 | 10.10.10.37 | ★ 4.7 | 🞋 20 39 | Blue | Windows | 👤 12210 | # 12634 | 10.10.10.40 | ★ 4.5 | 🞋 20 40 | Mirai | Linux | 👤 7235 | # 6858 | 10.10.10.48 | ★ 4.2 | 🞋 20 41 | [...] 42 | ``` 43 | 44 | ### Display machine details 45 | 46 | ``` 47 | $ htb machines show registry 48 | Registry 49 | ■■■■■■■■■■■■■■■■■■■■ ★ 4.5 50 | Hard 💻 Linux - 🞋 40 - 👤 3657 - # 2968 51 | 52 | Made by thek 53 | Released on 19 Oct 2019 54 | IP address: 10.10.10.159 55 | 56 | First bloods: 57 | 👤 InfoSecJack in 00 days, 03 hours, 28 mins, 17 seconds 58 | # jkr in 00 days, 04 hours, 26 mins, 08 seconds 59 | ``` 60 | 61 | ### Add and remove machines from your to-do list 62 | 63 | ```shell 64 | $ htb machines todo blue 65 | ✓ Blue was added to your todo list 66 | $ htb machines todo blue 67 | ✓ Blue was removed from your todo list 68 | ``` 69 | 70 | ### Submit flags with difficulty 71 | 72 | ```shell 73 | $ htb machines own themachine -f e825b7d1941c15cee7512238715f50ff -d 3 74 | ✗ Incorrect flag! 75 | ``` 76 | 77 | ### Start, stop and reset machines 78 | 79 | ```shell 80 | $ htb machines start registry 81 | 🕑 please wait while we try and assign `Registry` to you... 82 | ✓ Machine deployed to lab. 83 | 84 | $ htb machines start lame 85 | 🕑 please wait while we try and assign `Lame` to you... 86 | ✗ You already have an active machine. 87 | 88 | $ htb machines reset mirai 89 | 🕑 please wait while we try and reset `Mirai`... 90 | ✗ Mirai was not reset. Another reset from this user is pending. 91 | ``` 92 | 93 | ## Conversations 94 | 95 | ### List conversations 96 | 97 | ```shell 98 | $ htb chat list 99 | 💬 user1 (123) 100 | user1: Why? 101 | 💬 user2 (456) 102 | You: Did you start ForwardSlash? 103 | ``` 104 | 105 | ### Show messages from conversations 106 | 107 | ```shell 108 | $ htb chat show 123 109 | 03:27am user1: Lorem ipsum dolor sit amet, consectetur adipiscing elit. 110 | 03:35am user1: Ut vulputate sit amet neque et aliquam. Vestibulum ac interdum dui, eu placerat lectus. Phasellus in risus velit. 111 | 03:36am user1: :sweat_smile: 112 | 01:39pm apognu: Vestibulum sollicitudin ullamcorper neque non pharetra. Integer at ipsum ut mauris lobortis semper. Praesent ut erat ac ligula vehicula vulputate vitae at mauris. 113 | 01:40pm apognu: Phasellus consequat mi eu augue aliquam placerat. 114 | ``` 115 | 116 | ### Open an interactive shell to a chat 117 | 118 | ```shell 119 | $ htb chat open 123 120 | ✓ Chat session started 121 | /history to display latest messages 122 | /quit or ^D to exit 123 | 05:04pm user1: Lorem ipsum dolor sit amet, consectetur adipiscing elit. 124 | 05:10pm apognu: Ut vulputate sit amet neque et aliquam. Vestibulum ac interdum dui, eu placerat lectus. 125 | 05:14pm apognu: Phasellus consequat mi eu augue aliquam placerat. 126 | > your message... 127 | ``` -------------------------------------------------------------------------------- /src/api/chat.rs: -------------------------------------------------------------------------------- 1 | use super::util::{HtbError, HtbParser, HtbResponder}; 2 | use serde::{Deserialize, Serialize}; 3 | use std::error::Error; 4 | 5 | #[derive(Debug, Deserialize)] 6 | pub struct Conversation { 7 | pub id: u64, 8 | pub usernames: Vec, 9 | pub lastmessage: String, 10 | } 11 | 12 | #[derive(Debug, Deserialize)] 13 | pub struct Message { 14 | pub id: u64, 15 | pub username: String, 16 | pub rank: String, 17 | pub text: String, 18 | pub time: String, 19 | } 20 | 21 | pub async fn list() -> Result, Box> { 22 | let api = super::client()?; 23 | let response = api 24 | .post(&super::url("/conversations/list")) 25 | .send() 26 | .await 27 | .check()? 28 | .from_json() 29 | .await?; 30 | 31 | Ok(response) 32 | } 33 | 34 | pub async fn get(id: u64) -> Result, Box> { 35 | let api = super::client()?; 36 | let response = api 37 | .post(&super::url(&format!("/conversations/load/{}", id))) 38 | .send() 39 | .await 40 | .check()? 41 | .from_json() 42 | .await?; 43 | 44 | Ok(response) 45 | } 46 | 47 | #[derive(Debug, Serialize)] 48 | struct NewConversation { 49 | recipients: Vec, 50 | message: String, 51 | } 52 | 53 | #[allow(dead_code)] 54 | pub async fn new(recipients: &[String], message: &str) -> Result<(), Box> { 55 | let body = NewConversation { 56 | recipients: recipients.to_owned(), 57 | message: message.to_owned(), 58 | }; 59 | 60 | let api = super::client()?; 61 | api.post(&super::url("/conversations/new")) 62 | .json(&body) 63 | .send() 64 | .await 65 | .check()?; 66 | 67 | Ok(()) 68 | } 69 | 70 | pub async fn send(id: u64, message: &str) -> Result<(), Box> { 71 | let id = id.to_string(); 72 | let params = [("id", id.as_ref()), ("message", message)]; 73 | 74 | let api = super::client()?; 75 | let response = api 76 | .post(&super::url(&format!("/conversations/send/{}", id))) 77 | .form(¶ms) 78 | .send() 79 | .await 80 | .check()?; 81 | 82 | if response.status().is_success() { 83 | Ok(()) 84 | } else { 85 | Err(box HtbError::new(response.json::().await?)) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/api/deserializers.rs: -------------------------------------------------------------------------------- 1 | use serde::{ 2 | de::{value::MapAccessDeserializer, MapAccess, Visitor}, 3 | Deserialize, Deserializer, 4 | }; 5 | use std::{fmt, marker::PhantomData}; 6 | 7 | pub(super) fn int_or_string<'de, D>(deserializer: D) -> Result 8 | where 9 | D: Deserializer<'de>, 10 | { 11 | struct StateSuccessVisitor; 12 | 13 | impl<'de> Visitor<'de> for StateSuccessVisitor { 14 | type Value = u8; 15 | 16 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 17 | formatter.write_str("unsigned integer or string") 18 | } 19 | 20 | fn visit_u64(self, value: u64) -> Result 21 | where 22 | E: serde::de::Error, 23 | { 24 | Ok(value as u8) 25 | } 26 | 27 | fn visit_str(self, value: &str) -> Result 28 | where 29 | E: serde::de::Error, 30 | { 31 | match value.parse::() { 32 | Ok(value) => self.visit_u8(value), 33 | Err(_) => Err(E::custom("failed to parse integer")), 34 | } 35 | } 36 | }; 37 | 38 | deserializer.deserialize_any(StateSuccessVisitor) 39 | } 40 | 41 | pub(super) fn string_or_struct<'de, D, T>(deserializer: D) -> Result 42 | where 43 | D: Deserializer<'de>, 44 | T: Deserialize<'de> + Default, 45 | { 46 | struct ConnectionStatusVisitor(PhantomData Option>); 47 | 48 | impl<'de, T> Visitor<'de> for ConnectionStatusVisitor 49 | where 50 | T: Deserialize<'de> + Default, 51 | { 52 | type Value = T; 53 | 54 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 55 | formatter.write_str("string or connection struct") 56 | } 57 | 58 | fn visit_str(self, _: &str) -> Result 59 | where 60 | E: serde::de::Error, 61 | { 62 | Ok(T::default()) 63 | } 64 | 65 | fn visit_map(self, value: M) -> Result 66 | where 67 | M: MapAccess<'de>, 68 | { 69 | Deserialize::deserialize(MapAccessDeserializer::new(value)) 70 | } 71 | } 72 | 73 | deserializer.deserialize_any(ConnectionStatusVisitor::(PhantomData)) 74 | } 75 | -------------------------------------------------------------------------------- /src/api/info.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | deserializers::string_or_struct, 3 | util::{HtbParser, HtbResponder}, 4 | }; 5 | use serde::Deserialize; 6 | use std::error::Error; 7 | 8 | #[derive(Debug, Deserialize)] 9 | pub struct Status { 10 | pub success: String, 11 | #[serde(deserialize_with = "string_or_struct")] 12 | pub connection: Connection, 13 | } 14 | 15 | #[derive(Debug, Deserialize, Default)] 16 | pub struct Connection { 17 | pub name: String, 18 | pub ip4: String, 19 | pub ip6: String, 20 | } 21 | 22 | pub async fn status() -> Result> { 23 | let api = super::client()?; 24 | let response = api 25 | .post(&super::url("/users/htb/connection/status")) 26 | .send() 27 | .await 28 | .check()? 29 | .from_json() 30 | .await?; 31 | 32 | Ok(response) 33 | } 34 | -------------------------------------------------------------------------------- /src/api/machines.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | deserializers::int_or_string, 3 | util::{HtbParser, HtbResponder}, 4 | }; 5 | use serde::{Deserialize, Serialize}; 6 | use std::{collections::HashMap, error::Error}; 7 | 8 | #[derive(Debug, Deserialize)] 9 | pub struct Machine { 10 | pub id: u32, 11 | pub retired: bool, 12 | pub name: String, 13 | pub ip: String, 14 | pub os: String, 15 | pub rating: String, 16 | pub release: Option, 17 | pub points: u8, 18 | pub user_owns: u32, 19 | pub root_owns: u32, 20 | } 21 | 22 | #[derive(Debug, Deserialize)] 23 | pub struct MachineDetails { 24 | pub avatar: String, 25 | pub avatar_thumb: String, 26 | pub maker: Maker, 27 | pub maker2: Option, 28 | pub user_blood: Option, 29 | pub root_blood: Option, 30 | } 31 | 32 | #[derive(Debug, Deserialize)] 33 | pub struct Maker { 34 | pub id: u64, 35 | pub name: String, 36 | } 37 | 38 | #[derive(Debug, Deserialize)] 39 | pub struct BloodUser { 40 | pub id: u64, 41 | pub name: String, 42 | pub time: String, 43 | } 44 | 45 | #[derive(Debug, Deserialize)] 46 | pub struct Spawned { 47 | pub id: u32, 48 | } 49 | 50 | #[derive(Debug, Deserialize)] 51 | pub struct Owned { 52 | pub id: u32, 53 | pub owned_user: bool, 54 | pub owned_root: bool, 55 | } 56 | 57 | #[derive(Debug, Deserialize)] 58 | pub struct Difficulty { 59 | pub id: u32, 60 | pub difficulty_ratings: Vec, 61 | } 62 | 63 | #[derive(Debug, Deserialize)] 64 | pub struct Todo { 65 | pub id: u32, 66 | } 67 | 68 | #[derive(Debug, Deserialize)] 69 | pub struct Assigned { 70 | pub id: u32, 71 | } 72 | 73 | #[derive(Debug, Deserialize)] 74 | pub struct StateResponse { 75 | #[serde(deserialize_with = "int_or_string")] 76 | pub success: u8, 77 | #[serde(alias = "output")] 78 | pub status: String, 79 | } 80 | 81 | pub async fn list() -> Result, Box> { 82 | let api = super::client()?; 83 | let response = api 84 | .get(&super::url("/machines/get/all")) 85 | .send() 86 | .await 87 | .check()? 88 | .from_json() 89 | .await?; 90 | 91 | Ok(response) 92 | } 93 | 94 | pub async fn get(id: u32) -> Result> { 95 | let api = super::client()?; 96 | let response = api 97 | .get(&super::url(&format!("/machines/get/{}", id))) 98 | .send() 99 | .await 100 | .check()? 101 | .from_json() 102 | .await?; 103 | 104 | Ok(response) 105 | } 106 | 107 | pub async fn get_by_name(name: &str) -> Result, Box> { 108 | let machines = list().await?; 109 | let machine = machines 110 | .into_iter() 111 | .find(|m| m.name.to_lowercase() == name.to_lowercase()); 112 | 113 | Ok(machine) 114 | } 115 | 116 | pub async fn spawned() -> Result, Box> { 117 | let api = super::client()?; 118 | let response: Vec = api 119 | .get(&super::url("/machines/spawned")) 120 | .send() 121 | .await 122 | .check()? 123 | .from_json() 124 | .await?; 125 | 126 | Ok(response.iter().map(|m| m.id).collect()) 127 | } 128 | 129 | pub async fn owns() -> Result, Box> { 130 | let api = super::client()?; 131 | let response: Vec = api 132 | .get(&super::url("/machines/owns")) 133 | .send() 134 | .await 135 | .check()? 136 | .from_json() 137 | .await?; 138 | 139 | Ok(response 140 | .iter() 141 | .map(|m| (m.id, (m.owned_user, m.owned_root))) 142 | .collect()) 143 | } 144 | 145 | pub async fn difficulty(id: u32) -> Result> { 146 | let api = super::client()?; 147 | let response: Vec = api 148 | .get(&super::url("/machines/difficulty")) 149 | .send() 150 | .await 151 | .check()? 152 | .from_json() 153 | .await?; 154 | 155 | if let Some(machine) = response.iter().find(|d| d.id == id) { 156 | let votes: u64 = machine.difficulty_ratings.iter().sum(); 157 | let weighted: u64 = machine 158 | .difficulty_ratings 159 | .iter() 160 | .enumerate() 161 | .map(|(score, votes)| (score + 1) as u64 * votes) 162 | .sum(); 163 | 164 | return Ok(weighted as f64 / votes as f64); 165 | } 166 | 167 | Ok(0f64) 168 | } 169 | 170 | pub async fn todos() -> Result, Box> { 171 | let api = super::client()?; 172 | let response: Vec = api 173 | .get(&super::url("/machines/todo")) 174 | .send() 175 | .await 176 | .check()? 177 | .from_json() 178 | .await?; 179 | 180 | Ok(response.iter().map(|t| t.id).collect()) 181 | } 182 | 183 | pub async fn assigned() -> Result, Box> { 184 | let api = super::client()?; 185 | let response: Vec = api 186 | .get(&super::url("/machines/assigned")) 187 | .send() 188 | .await 189 | .check()? 190 | .from_json() 191 | .await?; 192 | 193 | Ok(response.iter().map(|t| t.id).collect()) 194 | } 195 | 196 | #[derive(Debug, Serialize)] 197 | pub struct OwnRequest { 198 | pub id: u32, 199 | pub flag: String, 200 | pub difficulty: u8, 201 | } 202 | 203 | pub async fn own(id: u32, flag: &str, difficulty: u8) -> Result> { 204 | let body = OwnRequest { 205 | id, 206 | flag: flag.to_string(), 207 | difficulty, 208 | }; 209 | 210 | let api = super::client()?; 211 | let response = api 212 | .post(&super::url("/machines/own")) 213 | .json(&body) 214 | .send() 215 | .await 216 | .check()? 217 | .from_json() 218 | .await?; 219 | 220 | Ok(response) 221 | } 222 | 223 | pub async fn toggle_todo(id: u32) -> Result, Box> { 224 | let api = super::client()?; 225 | let response = api 226 | .post(&super::url(&format!("/machines/todo/update/{}", id))) 227 | .send() 228 | .await 229 | .check()? 230 | .from_json() 231 | .await?; 232 | 233 | Ok(response) 234 | } 235 | 236 | pub async fn reset(id: u32) -> Result> { 237 | let api = super::client()?; 238 | let response = api 239 | .post(&super::url(&format!("/vm/reset/{}", id))) 240 | .send() 241 | .await 242 | .check()? 243 | .from_json() 244 | .await?; 245 | 246 | Ok(response) 247 | } 248 | 249 | pub async fn start(id: u32) -> Result> { 250 | let api = super::client()?; 251 | let response = api 252 | .post(&super::url(&format!("/vm/vip/assign/{}", id))) 253 | .send() 254 | .await 255 | .check()? 256 | .from_json() 257 | .await?; 258 | 259 | Ok(response) 260 | } 261 | 262 | pub async fn stop(id: u32) -> Result> { 263 | let api = super::client()?; 264 | let response = api 265 | .post(&super::url(&format!("/vm/vip/remove/{}", id))) 266 | .send() 267 | .await 268 | .check()? 269 | .from_json() 270 | .await?; 271 | 272 | Ok(response) 273 | } 274 | -------------------------------------------------------------------------------- /src/api/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod chat; 2 | mod deserializers; 3 | pub mod info; 4 | pub mod machines; 5 | mod util; 6 | 7 | pub use self::util::HtbError; 8 | 9 | use crate::cli; 10 | use colored::*; 11 | use reqwest::{ 12 | header::{self, HeaderMap, HeaderValue}, 13 | redirect::Policy, 14 | Client, ClientBuilder, 15 | }; 16 | use std::{error::Error, fs}; 17 | 18 | const BASE_URL: &str = "https://www.hackthebox.eu/api"; 19 | 20 | lazy_static! { 21 | static ref API_KEY: String = match dirs::config_dir() { 22 | Some(config) => { 23 | let path = format!("{}/htb/token", config.display()); 24 | 25 | if let Ok(token) = fs::read_to_string(&path) { 26 | token 27 | } else { 28 | cli::fatal(&format!( 29 | "could not read token, did you call `{}`?", 30 | "htb config token".bold(), 31 | )) 32 | } 33 | } 34 | 35 | None => cli::fatal(&format!( 36 | "could not read token, did you call `{}`?", 37 | "htb config token".bold(), 38 | )), 39 | }; 40 | } 41 | 42 | pub fn client() -> Result> { 43 | let creds = format!("Bearer {}", *API_KEY); 44 | 45 | let mut headers = HeaderMap::new(); 46 | headers.insert(header::AUTHORIZATION, HeaderValue::from_str(&creds.trim())?); 47 | 48 | Ok(ClientBuilder::new() 49 | .user_agent("htb/1.0.0 (https://github.com/apognu/htb") 50 | .default_headers(headers) 51 | .redirect(Policy::none()) 52 | .build()?) 53 | } 54 | 55 | pub fn url(path: &str) -> String { 56 | format!("{}{}", BASE_URL, path) 57 | } 58 | -------------------------------------------------------------------------------- /src/api/util.rs: -------------------------------------------------------------------------------- 1 | use reqwest::{Error as ReqwestError, Response, StatusCode}; 2 | use serde::de::DeserializeOwned; 3 | use std::{ 4 | error::Error, 5 | fmt::{self, Display, Formatter}, 6 | }; 7 | 8 | #[derive(Debug)] 9 | pub struct HtbError { 10 | pub message: String, 11 | } 12 | 13 | impl Display for HtbError { 14 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 15 | write!(f, "{}", self.message) 16 | } 17 | } 18 | 19 | impl Error for HtbError {} 20 | 21 | impl HtbError { 22 | pub fn new(message: S) -> HtbError 23 | where 24 | S: Into, 25 | { 26 | HtbError { 27 | message: message.into(), 28 | } 29 | } 30 | 31 | pub fn boxed(message: S) -> Box 32 | where 33 | S: Into, 34 | { 35 | box HtbError::new(message) 36 | } 37 | 38 | pub fn from(error: &dyn Error) -> HtbError { 39 | HtbError::new(error.to_string()) 40 | } 41 | } 42 | 43 | pub trait HtbResponder { 44 | fn check(self) -> Result; 45 | } 46 | 47 | impl HtbResponder for Result { 48 | fn check(self) -> Result { 49 | match self { 50 | Ok(response) => match response.status() { 51 | StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN | StatusCode::FOUND => Err( 52 | HtbError::new("You are unauthorized, please check your API key"), 53 | ), 54 | StatusCode::OK => Ok(response), 55 | status => Err(HtbError::new(&format!( 56 | "An unknown error occured (HTTP status {})", 57 | status.as_u16(), 58 | ))), 59 | }, 60 | Err(error) => match error.source() { 61 | Some(source) => Err(HtbError::from(source)), 62 | None => Err(HtbError::from(&error)), 63 | }, 64 | } 65 | } 66 | } 67 | 68 | #[async_trait] 69 | pub trait HtbParser { 70 | async fn from_json(self) -> Result 71 | where 72 | T: DeserializeOwned; 73 | } 74 | 75 | #[async_trait] 76 | impl HtbParser for Response { 77 | async fn from_json(self) -> Result 78 | where 79 | T: DeserializeOwned, 80 | { 81 | match self.json().await { 82 | Ok(data) => Ok(data), 83 | Err(_) => Err(HtbError::new("Could not parse response from server")), 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | use clap::Clap; 2 | 3 | pub fn parse() -> Arguments { 4 | Arguments::parse() 5 | } 6 | 7 | #[derive(Debug, Clap)] 8 | #[clap( 9 | version = "1.0", 10 | author = "Antoine POPINEAU , 133 | #[clap(long, help = "Only show spawned machines")] 134 | pub spawned: bool, 135 | #[clap(long, group = "group_active", help = "Only show active machines")] 136 | pub active: bool, 137 | #[clap(long, group = "group_active", help = "Only show retired machines")] 138 | pub retired: bool, 139 | #[clap( 140 | long, 141 | group = "group_owned", 142 | help = "Only show machines you completely onwned" 143 | )] 144 | pub owned: bool, 145 | #[clap( 146 | long, 147 | group = "group_owned", 148 | help = "Only show machines you did not complete" 149 | )] 150 | pub unowned: bool, 151 | #[clap(long, name="os", possible_values = OSES, help = "Only show machines running a specific operating system")] 152 | pub os: Option, 153 | #[clap(long, help = "Show machines from your to-do list")] 154 | pub todo: bool, 155 | #[clap(long, help = "Show machines assigned to you")] 156 | pub assigned: bool, 157 | #[clap(long, short = "x")] 158 | pub desc: bool, 159 | #[clap(long, short = "s", name = "FIELD", possible_values = FIELDS, help = "Sort the filtered machines according to one field", default_value="id")] 160 | pub sort: String, 161 | #[clap( 162 | long, 163 | name = "NAME", 164 | help = "Search for machine containing this substring" 165 | )] 166 | pub name: Option, 167 | } 168 | 169 | fn parse_difficulty(value: &str) -> Result { 170 | match value.parse::() { 171 | Ok(value) => { 172 | if value > 0 && value <= 10 { 173 | Ok(value) 174 | } else { 175 | Err("should be an integer between 1 and 10") 176 | } 177 | } 178 | 179 | Err(_) => Err("should be an integer between 1 and 10"), 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use colored::*; 2 | 3 | pub fn info(message: &str) { 4 | println!("{} {}", "•".blue(), message); 5 | } 6 | 7 | pub fn ok(message: &str) { 8 | println!("{} {}", "✓".green(), message); 9 | } 10 | 11 | pub fn error(message: &str) { 12 | println!("{} {} {}", "✗".red(), "ERROR:".bold(), message); 13 | } 14 | 15 | pub fn fatal(message: &str) -> ! { 16 | println!("{} {} {}", "✗".red(), "ERROR:".bold(), message); 17 | 18 | std::process::exit(1) 19 | } 20 | 21 | pub fn holdon(message: &str) { 22 | println!("{} {}", "⧗".yellow(), message); 23 | } 24 | -------------------------------------------------------------------------------- /src/cmds/chat/display.rs: -------------------------------------------------------------------------------- 1 | use super::util; 2 | use crate::api; 3 | use colored::*; 4 | use std::error::Error; 5 | 6 | pub async fn list() -> Result<(), Box> { 7 | let conversations = api::chat::list().await?; 8 | 9 | for conversation in &conversations { 10 | println!( 11 | "💬 {} ({})", 12 | conversation.usernames.join(", ").bold(), 13 | conversation.id 14 | ); 15 | println!( 16 | " {}", 17 | util::decode_message(&conversation.lastmessage).dimmed() 18 | ); 19 | } 20 | 21 | Ok(()) 22 | } 23 | 24 | pub async fn show(id: u64) -> Result<(), Box> { 25 | let messages = api::chat::get(id).await?; 26 | 27 | for message in &messages { 28 | println!( 29 | "{} {}: {}", 30 | message.time.dimmed(), 31 | message.username.bold(), 32 | util::decode_message(&message.text) 33 | ); 34 | } 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /src/cmds/chat/mod.rs: -------------------------------------------------------------------------------- 1 | mod display; 2 | mod shell; 3 | mod talk; 4 | mod util; 5 | 6 | use crate::args::Chat::{self, *}; 7 | use std::error::Error; 8 | 9 | pub async fn parse(subcommand: &Chat) -> Result<(), Box> { 10 | match subcommand { 11 | List => self::display::list().await, 12 | Show { id } => self::display::show(*id).await, 13 | Send { id, message } => self::talk::send(*id, message).await, 14 | Open { id } => self::shell::open(*id).await, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/cmds/chat/shell.rs: -------------------------------------------------------------------------------- 1 | use super::util; 2 | use crate::{ 3 | api::{self, chat::Conversation}, 4 | cli, 5 | }; 6 | use colored::*; 7 | use libnotify::Notification; 8 | use linefeed::{DefaultTerminal, Interface, ReadResult}; 9 | use std::{ 10 | error::Error, 11 | io::stdout, 12 | sync::{ 13 | atomic::{AtomicU64, Ordering}, 14 | Arc, 15 | }, 16 | time::Duration, 17 | }; 18 | use termion::{cursor::DetectCursorPos, raw::IntoRawMode}; 19 | use tokio::time::delay_for; 20 | 21 | pub async fn open(id: u64) -> Result<(), Box> { 22 | cli::ok("Chat session started"); 23 | println!(" {} to display latest messages", "/history".bold()); 24 | println!(" {} or {} to exit", "/quit".bold(), "^D".bold()); 25 | 26 | let conversation = Arc::new( 27 | api::chat::list() 28 | .await? 29 | .into_iter() 30 | .find(|c| c.id == id) 31 | .unwrap(), 32 | ); 33 | 34 | let messages = api::chat::get(id).await?; 35 | 36 | let last_seen_id = Arc::new(AtomicU64::new( 37 | messages.iter().map(|m| m.id).max().unwrap_or(0), 38 | )); 39 | 40 | for message in &*messages { 41 | println!( 42 | "{} {}: {}", 43 | message.time.dimmed(), 44 | message.username.bold(), 45 | util::decode_message(&message.text) 46 | ); 47 | } 48 | 49 | let rl = Arc::new(Interface::new("")?); 50 | rl.set_prompt("> ")?; 51 | 52 | { 53 | let conversation = Arc::clone(&conversation); 54 | let last_seen_id = Arc::clone(&last_seen_id); 55 | let w = Arc::clone(&rl); 56 | 57 | tokio::spawn(async move { 58 | loop { 59 | delay_for(Duration::from_secs(10)).await; 60 | print_all_messages(&conversation, &last_seen_id, &w, false).await; 61 | } 62 | }); 63 | } 64 | 65 | let _ = libnotify::init("Hack The Box"); 66 | 67 | loop { 68 | match rl.read_line()? { 69 | ReadResult::Input(line) => { 70 | let (_, x) = { 71 | let mut stdout = stdout().into_raw_mode().unwrap(); 72 | stdout.cursor_pos().unwrap() 73 | }; 74 | 75 | let message = line.trim(); 76 | 77 | if message.is_empty() { 78 | continue; 79 | } 80 | 81 | println!( 82 | "{}{}{}", 83 | termion::cursor::Goto(1, x - 1), 84 | termion::clear::UntilNewline, 85 | termion::cursor::Goto(1, x - 2) 86 | ); 87 | 88 | match message { 89 | "/quit" => break, 90 | 91 | "/history" => { 92 | cli::ok("Printing your history below"); 93 | 94 | print_all_messages(&conversation, &last_seen_id, &rl, true).await; 95 | } 96 | 97 | _ => { 98 | let conversation = Arc::clone(&conversation); 99 | let last_seen_id = Arc::clone(&last_seen_id); 100 | let w = Arc::clone(&rl); 101 | 102 | tokio::spawn(async move { 103 | api::chat::send(id, &line).await.unwrap(); 104 | print_all_messages(&conversation, &last_seen_id, &w, false).await; 105 | }); 106 | } 107 | } 108 | } 109 | 110 | ReadResult::Eof => break, 111 | 112 | _ => (), 113 | } 114 | } 115 | 116 | Ok(()) 117 | } 118 | 119 | async fn print_all_messages( 120 | conversation: &Arc, 121 | last_seen_id: &Arc, 122 | w: &Arc>, 123 | all: bool, 124 | ) { 125 | let messages = api::chat::get(conversation.id).await.unwrap(); 126 | let mut w = w.lock_writer_erase().unwrap(); 127 | 128 | for message in &messages { 129 | let seen = message.id <= last_seen_id.load(Ordering::Relaxed); 130 | 131 | if all || !seen { 132 | let body = util::decode_message(&message.text); 133 | writeln!( 134 | w, 135 | "{} {}: {}", 136 | message.time.dimmed(), 137 | message.username.bold(), 138 | body 139 | ) 140 | .unwrap(); 141 | 142 | if conversation.usernames.contains(&message.username) && !seen { 143 | let notification = Notification::new( 144 | &format!("Hack The Box: new message from {}", message.username), 145 | Some(body.as_ref()), 146 | None, 147 | ); 148 | 149 | notification.show().unwrap(); 150 | } 151 | 152 | if !all || message.id > last_seen_id.load(Ordering::Relaxed) { 153 | last_seen_id.store(message.id, Ordering::Relaxed); 154 | } 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/cmds/chat/talk.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | api::{self, HtbError}, 3 | cli, 4 | }; 5 | use std::error::Error; 6 | 7 | #[allow(dead_code)] 8 | pub async fn new(usernames: &[String], message: &str) -> Result<(), Box> { 9 | match api::chat::new(usernames, message).await { 10 | Ok(_) => { 11 | cli::ok(&format!( 12 | "Conversation was created with {}", 13 | usernames.join(", ") 14 | )); 15 | 16 | Ok(()) 17 | } 18 | 19 | Err(_) => Err(HtbError::boxed( 20 | "An error prevented us from creating this conversation", 21 | )), 22 | } 23 | } 24 | 25 | pub async fn send(id: u64, message: &str) -> Result<(), Box> { 26 | let conversations = api::chat::list().await?; 27 | 28 | if !conversations.iter().any(|c| c.id == id) { 29 | return Err(HtbError::boxed( 30 | "You do not have any conversation by that ID", 31 | )); 32 | } 33 | 34 | api::chat::send(id, message).await?; 35 | 36 | cli::ok("Your message was sent successfully"); 37 | 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /src/cmds/chat/util.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | pub fn decode_message(message: &str) -> Cow<'_, str> { 4 | match htmlescape::decode_html(message) { 5 | Ok(message) => Cow::Owned(message), 6 | Err(_) => Cow::Borrowed(message), 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/cmds/config/mod.rs: -------------------------------------------------------------------------------- 1 | mod token; 2 | 3 | use crate::args::Config::{self, *}; 4 | use std::error::Error; 5 | 6 | pub async fn parse(subcommand: &Config) -> Result<(), Box> { 7 | match subcommand { 8 | Token { token } => self::token::set_token(token).await, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/cmds/config/token.rs: -------------------------------------------------------------------------------- 1 | use crate::api::HtbError; 2 | use std::{error::Error, fs}; 3 | 4 | pub async fn set_token(token: &str) -> Result<(), Box> { 5 | match dirs::config_dir() { 6 | Some(config) => { 7 | let path = format!("{}/htb", config.display()); 8 | 9 | fs::create_dir_all(&path)?; 10 | fs::write(format!("{}/token", &path), token)?; 11 | } 12 | 13 | None => { 14 | return Err(box HtbError::new( 15 | "Could not create directory under ~/.config/htb", 16 | )) 17 | } 18 | } 19 | 20 | Ok(()) 21 | } 22 | -------------------------------------------------------------------------------- /src/cmds/info/mod.rs: -------------------------------------------------------------------------------- 1 | mod status; 2 | 3 | use crate::args::Info::{self, *}; 4 | use std::error::Error; 5 | 6 | pub async fn parse(subcommand: &Info) -> Result<(), Box> { 7 | match subcommand { 8 | Status => self::status::status().await, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/cmds/info/status.rs: -------------------------------------------------------------------------------- 1 | use crate::api; 2 | use colored::*; 3 | use std::error::Error; 4 | 5 | pub async fn status() -> Result<(), Box> { 6 | let status = api::info::status().await?; 7 | 8 | if status.success == "1" { 9 | println!("VPN status : {}", "CONNECTED".bold().green()); 10 | println!("Username: {}", status.connection.name); 11 | println!("IP addresses: {}", status.connection.ip4); 12 | println!(" {}", status.connection.ip6); 13 | } else { 14 | println!("VPN status: {}", "DISCONNECTED".bold().red()); 15 | } 16 | 17 | Ok(()) 18 | } 19 | -------------------------------------------------------------------------------- /src/cmds/machines/list.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | api::{self, HtbError}, 3 | args::MachineListArgs, 4 | }; 5 | use colored::*; 6 | use prettytable::{format::consts::*, Cell, Row, Table}; 7 | use std::error::Error; 8 | 9 | pub async fn list(args: &MachineListArgs) -> Result<(), Box> { 10 | let mut machines = api::machines::list().await?; 11 | let spawned = api::machines::spawned().await?; 12 | let owned = api::machines::owns().await?; 13 | let todos = api::machines::todos().await?; 14 | let assigned = api::machines::assigned().await?; 15 | 16 | let mut table = Table::new(); 17 | table.set_format(*FORMAT_NO_BORDER_LINE_SEPARATOR); 18 | table.add_row(row!["", b -> "NAME", b -> "OS", b -> "USER", b -> "ROOT", b -> "IP ADDRESS", b -> "RATING", b -> "POINTS"]); 19 | 20 | machines.sort_by(|m1, m2| { 21 | let (f1, f2) = match args.sort.as_ref() { 22 | "name" => (m1.name.clone(), m2.name.clone()), 23 | "rating" => (m1.rating.clone(), m2.rating.clone()), 24 | "release" => ( 25 | m1.release.clone().unwrap_or("Z".to_string()), 26 | m2.release.clone().unwrap_or("Z".to_string()), 27 | ), 28 | _ => (m1.id.to_string(), m2.id.to_string()), 29 | }; 30 | 31 | if args.desc { 32 | f2.partial_cmp(&f1).unwrap() 33 | } else { 34 | f1.partial_cmp(&f2).unwrap() 35 | } 36 | }); 37 | 38 | for machine in &machines { 39 | if let Some(difficulty) = &args.difficulty { 40 | let keep = match difficulty.as_ref() { 41 | "easy" => machine.points == 20, 42 | "medium" => machine.points == 30, 43 | "hard" => machine.points == 40, 44 | "insane" => machine.points == 50, 45 | _ => true, 46 | }; 47 | 48 | if !keep { 49 | continue; 50 | } 51 | } 52 | if let Some(os) = &args.os { 53 | if machine.os.to_lowercase() != os.to_lowercase() { 54 | continue; 55 | } 56 | } 57 | if args.spawned && !spawned.contains(&machine.id) { 58 | continue; 59 | } 60 | if args.active && machine.retired { 61 | continue; 62 | } 63 | if args.retired && !machine.retired { 64 | continue; 65 | } 66 | if args.todo && !todos.contains(&machine.id) { 67 | continue; 68 | } 69 | if args.assigned && !assigned.contains(&machine.id) { 70 | continue; 71 | } 72 | if let Some(name) = &args.name { 73 | if !machine.name.to_lowercase().contains(&name.to_lowercase()) { 74 | continue; 75 | } 76 | } 77 | 78 | let user = owned.get(&machine.id).map(|(u, _)| u).unwrap_or(&false); 79 | let root = owned.get(&machine.id).map(|(_, r)| r).unwrap_or(&false); 80 | 81 | if args.owned && (!user || !root) { 82 | continue; 83 | } 84 | if args.unowned && *user && *root { 85 | continue; 86 | } 87 | 88 | let spawned = if spawned.contains(&machine.id) { 89 | Cell::new("⚫").style_spec("Fg") 90 | } else { 91 | Cell::new("⚫").style_spec("Fr") 92 | }; 93 | 94 | table.add_row(Row::new(vec![ 95 | spawned, 96 | Cell::new(&machine.name), 97 | Cell::new(&machine.os), 98 | Cell::new(&format!("👤 {}", machine.user_owns)).style_spec(if *user { 99 | "Fg" 100 | } else { 101 | "" 102 | }), 103 | Cell::new(&format!("# {}", machine.root_owns)).style_spec(if *root { 104 | "Fr" 105 | } else { 106 | "" 107 | }), 108 | Cell::new(&machine.ip), 109 | Cell::new(&format!("{} {}", "★".yellow(), machine.rating)), 110 | Cell::new(&format!("{} {}", "🞋".green(), machine.points)), 111 | ])); 112 | } 113 | 114 | if table.len() <= 1 { 115 | Err(HtbError::boxed("No machine matches your criteria")) 116 | } else { 117 | table.printstd(); 118 | 119 | Ok(()) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/cmds/machines/mod.rs: -------------------------------------------------------------------------------- 1 | mod list; 2 | mod own; 3 | mod show; 4 | mod state; 5 | mod todo; 6 | 7 | use crate::args::Machines::{self, *}; 8 | use std::error::Error; 9 | 10 | pub async fn parse(subcommand: &Machines) -> Result<(), Box> { 11 | match subcommand { 12 | List(args) => self::list::list(args).await, 13 | Show { name } => self::show::show(name).await, 14 | 15 | Own { 16 | name, 17 | flag, 18 | difficulty, 19 | } => self::own::own(name, flag, *difficulty).await, 20 | 21 | Todo { name } => self::todo::toggle(name).await, 22 | Reset { name } => self::state::reset(name).await, 23 | Start { name } => self::state::start(name).await, 24 | Stop { name } => self::state::stop(name).await, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/cmds/machines/own.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | api::{self, HtbError}, 3 | cli, 4 | }; 5 | use std::error::Error; 6 | 7 | pub async fn own(name: &str, flag: &str, difficulty: u8) -> Result<(), Box> { 8 | let machine = api::machines::get_by_name(name).await?; 9 | 10 | if let Some(machine) = machine { 11 | let status = api::machines::own(machine.id, flag, difficulty * 10).await?; 12 | 13 | if status.success == 1 { 14 | cli::ok(&status.status); 15 | 16 | Ok(()) 17 | } else { 18 | Err(HtbError::boxed(status.status)) 19 | } 20 | } else { 21 | Err(HtbError::boxed("No machine was found by that name")) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/cmds/machines/show.rs: -------------------------------------------------------------------------------- 1 | use crate::api::{self, HtbError}; 2 | use chrono::prelude::*; 3 | use colored::*; 4 | use std::error::Error; 5 | 6 | pub async fn show(name: &str) -> Result<(), Box> { 7 | let machine = api::machines::get_by_name(name).await?; 8 | 9 | if let Some(machine) = machine { 10 | let details = api::machines::get(machine.id).await?; 11 | let difficulty = api::machines::difficulty(machine.id).await?.round() as usize; 12 | 13 | println!("{}", machine.name.bold()); 14 | 15 | let diffs = match machine.points { 16 | p if p <= 20 => ("Easy".green(), "■■".green()), 17 | p if p <= 30 => ("Medium".yellow(), "■■".yellow()), 18 | p if p <= 40 => ("Hard".red(), "■■".red()), 19 | p if p <= 50 => ("Insane".red(), "■■".red()), 20 | _ => ("N/A".dimmed(), "■■".normal()), 21 | }; 22 | 23 | for _ in 0..difficulty { 24 | print!("{}", diffs.1); 25 | } 26 | 27 | for _ in difficulty..10 { 28 | print!("{}", "■■".black()); 29 | } 30 | 31 | print!(" {} {}", "★".yellow(), machine.rating); 32 | 33 | println!(); 34 | println!( 35 | "{} 💻 {} - {} {} - {} {} - {} {}", 36 | diffs.0, 37 | machine.os, 38 | "🞋".green(), 39 | machine.points, 40 | "👤".blue(), 41 | machine.user_owns, 42 | "#".red(), 43 | machine.root_owns 44 | ); 45 | println!(); 46 | 47 | if let Some(maker2) = details.maker2 { 48 | println!( 49 | "Created by {} and {}", 50 | details.maker.name.green(), 51 | maker2.name.green() 52 | ); 53 | } else { 54 | println!("Created by {}", details.maker.name.green()); 55 | } 56 | 57 | if let Some(release) = machine.release { 58 | if let Ok(release) = 59 | NaiveDateTime::parse_from_str(&format!("{} 00:00:00", release), "%Y-%m-%d %H:%M:%S") 60 | { 61 | println!( 62 | "Released on {}", 63 | release.format("%d %b %Y").to_string().dimmed() 64 | ); 65 | } else { 66 | println!("Released on {}", release.dimmed()); 67 | } 68 | } else { 69 | println!("{}", "Unreleased".bold().red()); 70 | } 71 | 72 | println!("IP address: {}", machine.ip.bold()); 73 | println!(); 74 | println!("{}", "First bloods:".bold()); 75 | 76 | if let Some(user) = details.user_blood { 77 | println!( 78 | " {} {} in {}", 79 | "👤".blue(), 80 | user.name.bold(), 81 | user.time.dimmed() 82 | ); 83 | } 84 | 85 | if let Some(user) = details.root_blood { 86 | println!( 87 | " {} {} in {}", 88 | "#".red(), 89 | user.name.bold(), 90 | user.time.dimmed() 91 | ); 92 | } 93 | 94 | Ok(()) 95 | } else { 96 | Err(HtbError::boxed("No machine was found by that name")) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/cmds/machines/state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | api::{self, HtbError}, 3 | cli, 4 | }; 5 | use colored::*; 6 | use std::error::Error; 7 | 8 | pub async fn reset(name: &str) -> Result<(), Box> { 9 | let machine = api::machines::get_by_name(name).await?; 10 | 11 | if let Some(machine) = machine { 12 | cli::holdon(&format!( 13 | "Please wait while we try and reset `{}`...", 14 | machine.name.bold(), 15 | )); 16 | 17 | let status = api::machines::reset(machine.id).await?; 18 | 19 | if status.success == 1 { 20 | cli::ok(&status.status); 21 | cli::info("It might take some time for the machine to actually reboot"); 22 | 23 | Ok(()) 24 | } else { 25 | Err(HtbError::boxed(status.status)) 26 | } 27 | } else { 28 | Err(HtbError::boxed("No machine was found by that name")) 29 | } 30 | } 31 | 32 | pub async fn start(name: &str) -> Result<(), Box> { 33 | let machine = api::machines::get_by_name(name).await?; 34 | 35 | if let Some(machine) = machine { 36 | cli::holdon(&format!( 37 | "Please wait while we try and assign `{}` to you...", 38 | machine.name.bold(), 39 | )); 40 | 41 | let status = api::machines::start(machine.id).await?; 42 | 43 | if status.success == 1 { 44 | cli::ok(&status.status); 45 | cli::info("It might take some time for the machine to be reachable"); 46 | 47 | Ok(()) 48 | } else { 49 | Err(HtbError::boxed(status.status)) 50 | } 51 | } else { 52 | Err(HtbError::boxed("No machine was found by that name")) 53 | } 54 | } 55 | 56 | pub async fn stop(name: &str) -> Result<(), Box> { 57 | let machine = api::machines::get_by_name(name).await?; 58 | 59 | if let Some(machine) = machine { 60 | cli::holdon(&format!( 61 | "Please wait while we schedule `{}` for termination...", 62 | machine.name.bold() 63 | )); 64 | 65 | let status = api::machines::stop(machine.id).await?; 66 | 67 | if status.success == 1 { 68 | cli::ok(&status.status); 69 | cli::info("It might be some time for the machine to be deassigned from you"); 70 | 71 | Ok(()) 72 | } else { 73 | Err(HtbError::boxed(status.status)) 74 | } 75 | } else { 76 | Err(HtbError::boxed("No machine was found by that name")) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/cmds/machines/todo.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | api::{self, HtbError}, 3 | cli, 4 | }; 5 | use colored::*; 6 | use std::error::Error; 7 | 8 | pub async fn toggle(name: &str) -> Result<(), Box> { 9 | let machine = api::machines::get_by_name(name).await?; 10 | 11 | if let Some(machine) = machine { 12 | let todos = api::machines::toggle_todo(machine.id).await?; 13 | 14 | if todos.iter().any(|t| t.id == machine.id) { 15 | cli::ok(&format!( 16 | "`{}` was added to your todo list", 17 | machine.name.bold() 18 | )); 19 | } else { 20 | cli::ok(&format!( 21 | "`{}` was removed from your todo list", 22 | machine.name.bold() 23 | )); 24 | } 25 | 26 | Ok(()) 27 | } else { 28 | Err(HtbError::boxed("No machine was found by that name")) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/cmds/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod chat; 2 | pub mod config; 3 | pub mod info; 4 | pub mod machines; 5 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(async_closure)] 2 | #![feature(box_syntax)] 3 | 4 | #[macro_use] 5 | extern crate async_trait; 6 | #[macro_use] 7 | extern crate lazy_static; 8 | #[macro_use] 9 | extern crate prettytable; 10 | 11 | mod api; 12 | mod args; 13 | mod cli; 14 | mod cmds; 15 | 16 | use self::args::Subcommand::*; 17 | use std::error::Error; 18 | use std::process; 19 | 20 | #[tokio::main] 21 | async fn main() -> Result<(), Box> { 22 | let options = args::parse(); 23 | 24 | let result = match options.subcommand { 25 | Info(ref sc) => cmds::info::parse(sc).await, 26 | Config(ref sc) => cmds::config::parse(sc).await, 27 | Machines(ref sc) => cmds::machines::parse(sc).await, 28 | Chat(ref sc) => cmds::chat::parse(sc).await, 29 | }; 30 | 31 | if let Err(error) = result { 32 | cli::error(&error.to_string()); 33 | process::exit(1); 34 | } 35 | 36 | Ok(()) 37 | } 38 | --------------------------------------------------------------------------------