├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── build.rs ├── ci ├── before_deploy.sh ├── install.sh └── script.sh ├── screenshots ├── change_region.png ├── console.png ├── details.png └── instances.png └── src ├── bin └── cloudman.rs ├── cloudman.rs ├── help.md ├── lib.rs ├── ncurses_theme.toml └── views ├── bottombar_view.rs ├── foo_view.rs ├── key_codes.rs ├── log_view.rs ├── mod.rs └── table_view.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | *.log 4 | tmp/ 5 | 6 | target/ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | 4 | addons: 5 | apt: 6 | packages: 7 | - libncurses-dev 8 | 9 | git: 10 | depth: 1 11 | 12 | os: 13 | - linux 14 | - osx 15 | 16 | rust: 17 | - stable 18 | - nightly 19 | 20 | matrix: 21 | fast_finish: true 22 | include: 23 | - name: "linux i586 (clippy))" 24 | os: linux 25 | env: CLIPPY=true 26 | rust: stable 27 | env: TARGET=i586-unknown-linux-gnu 28 | - name: "linux x86_64 (clippy))" 29 | os: linux 30 | env: CLIPPY=true 31 | rust: stable 32 | env: TARGET=x86_64-unknown-linux-gnu 33 | - name: "osx x86_64 (clippy)" 34 | os: osx 35 | env: CLIPPY=true 36 | rust: stable 37 | env: TARGET=x86_64-apple-darwin 38 | - if: tag IS present 39 | name: "linux x86_64 (release)" 40 | os: linux 41 | rust: stable 42 | env: TARGET=x86_64-unknown-linux-gnu DEPLOY=1 43 | - if: tag IS present 44 | name: "osx x86_64 (release)" 45 | os: osx 46 | rust: stable 47 | env: TARGET=x86_64-apple-darwin DEPLOY=1 48 | allow_failures: 49 | - rust: nightly 50 | 51 | script: ci/script.sh 52 | before_deploy: ci/before_deploy.sh 53 | 54 | deploy: 55 | - provider: releases 56 | api_key: 57 | secure: yvqTnPO5fYon6+LXZnqlzb5nV/BtDhCmDn7Xh7mMw0PVxOYciQp76DEbAXVT2E0IRsZhJRbxDRiW7g31h4vzizqh8rFJTfa42dPLklivnEHvj5YL8tX7KacP+ALkuxAAKa+6JkeEiqnInbz5AROnZY+e748tRAD6hdoqImFg0WeGZZ18V+Vq/uHSJHbETxBKQ+Di0S7pDzVJ1zam4clbsD8nq3SxF9W3JviT8q1F8WylWd2L0+OJ1nV69Ikb4+lGW3YQCM7+1q13hDv19cWyILthqC8OxG0Yen19Qkc6NRxBWtTXETv5Xupro/cF56ywwECKUIVyE8lfGsP471SveDJm85pK9BF665yn7q5wkBy7xgXP9775koErfd34T8r1OG9T3c9n2SwyFZ2j1O0JDqbKH0gAU6ogIe6S8pkQ80PfwJ1BMG2NxCz/28c1hVODS+A/lTSd0D3jI7qJbEC2J1wpKd+pDHdkYlrHd3hHWzdMbE66gTUcG8gvqmjkgc7CUnXvpl9sR1GBBR9AC/p0eySUTYrIlzGMwNslrwZzs+LLGEc16XcKpET3WIQJ1Fy4e01n5wnEeSM3GmgE/Xp34XYBLMoSQ5RdIVivnuwLkvUzwtQkKwzIsAjXUOD8e4e5+TUCjcFSKwJnLU14X7/7vHWjcURzPhlfpTACiMwasxc= 58 | skip_cleanup: true 59 | file_glob: true 60 | file: cloudman-$TRAVIS_TAG-$TARGET.tar.gz 61 | on: 62 | tags: true 63 | rust: stable 64 | condition: $DEPLOY = 1 65 | repo: dutchcoders/cloudman 66 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.3.8" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" 10 | 11 | [[package]] 12 | name = "ahash" 13 | version = "0.6.3" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "796540673305a66d127804eef19ad696f1f204b8c1025aaca4958c17eab32877" 16 | dependencies = [ 17 | "getrandom 0.2.2", 18 | "once_cell", 19 | "version_check", 20 | ] 21 | 22 | [[package]] 23 | name = "aho-corasick" 24 | version = "0.7.10" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" 27 | dependencies = [ 28 | "memchr", 29 | ] 30 | 31 | [[package]] 32 | name = "array-macro" 33 | version = "1.0.5" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "06e97b4e522f9e55523001238ac59d13a8603af57f69980de5d8de4bbbe8ada6" 36 | 37 | [[package]] 38 | name = "arrayref" 39 | version = "0.3.6" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 42 | 43 | [[package]] 44 | name = "arrayvec" 45 | version = "0.5.1" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" 48 | 49 | [[package]] 50 | name = "async-trait" 51 | version = "0.1.35" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "89cb5d814ab2a47fd66d3266e9efccb53ca4c740b7451043b8ffcf9a6208f3f8" 54 | dependencies = [ 55 | "proc-macro2", 56 | "quote", 57 | "syn", 58 | ] 59 | 60 | [[package]] 61 | name = "atty" 62 | version = "0.2.14" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 65 | dependencies = [ 66 | "hermit-abi", 67 | "libc", 68 | "winapi 0.3.8", 69 | ] 70 | 71 | [[package]] 72 | name = "autocfg" 73 | version = "1.0.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 76 | 77 | [[package]] 78 | name = "base-x" 79 | version = "0.2.6" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1" 82 | 83 | [[package]] 84 | name = "base64" 85 | version = "0.11.0" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" 88 | 89 | [[package]] 90 | name = "base64" 91 | version = "0.12.1" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42" 94 | 95 | [[package]] 96 | name = "bitflags" 97 | version = "1.2.1" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 100 | 101 | [[package]] 102 | name = "blake2b_simd" 103 | version = "0.5.10" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" 106 | dependencies = [ 107 | "arrayref", 108 | "arrayvec", 109 | "constant_time_eq", 110 | ] 111 | 112 | [[package]] 113 | name = "block-buffer" 114 | version = "0.9.0" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 117 | dependencies = [ 118 | "generic-array", 119 | ] 120 | 121 | [[package]] 122 | name = "built" 123 | version = "0.4.2" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "161483ae87631dd826cb40fc696d9e5a9fa94e19c2e69a372dcedd7dc68e7c0a" 126 | dependencies = [ 127 | "cargo-lock", 128 | ] 129 | 130 | [[package]] 131 | name = "bumpalo" 132 | version = "3.4.0" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" 135 | 136 | [[package]] 137 | name = "byteorder" 138 | version = "1.3.4" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 141 | 142 | [[package]] 143 | name = "bytes" 144 | version = "0.4.12" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" 147 | dependencies = [ 148 | "byteorder", 149 | "iovec", 150 | ] 151 | 152 | [[package]] 153 | name = "bytes" 154 | version = "0.5.4" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" 157 | 158 | [[package]] 159 | name = "cargo-lock" 160 | version = "4.0.1" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "8504b63dd1249fd1745b7b4ef9b6f7b107ddeb3c95370043c7dbcc38653a2679" 163 | dependencies = [ 164 | "semver", 165 | "serde", 166 | "toml", 167 | "url", 168 | ] 169 | 170 | [[package]] 171 | name = "caseless" 172 | version = "0.2.1" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "808dab3318747be122cb31d36de18d4d1c81277a76f8332a02b81a3d73463d7f" 175 | dependencies = [ 176 | "regex", 177 | "unicode-normalization", 178 | ] 179 | 180 | [[package]] 181 | name = "cc" 182 | version = "1.0.54" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" 185 | 186 | [[package]] 187 | name = "cfg-if" 188 | version = "0.1.10" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 191 | 192 | [[package]] 193 | name = "cfg-if" 194 | version = "1.0.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 197 | 198 | [[package]] 199 | name = "chrono" 200 | version = "0.4.11" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" 203 | dependencies = [ 204 | "num-integer", 205 | "num-traits", 206 | "serde", 207 | "time 0.1.43", 208 | ] 209 | 210 | [[package]] 211 | name = "clap" 212 | version = "3.0.0-beta.1" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "860643c53f980f0d38a5e25dfab6c3c93b2cb3aa1fe192643d17a293c6c41936" 215 | dependencies = [ 216 | "atty", 217 | "bitflags", 218 | "clap_derive", 219 | "indexmap", 220 | "lazy_static", 221 | "os_str_bytes", 222 | "strsim 0.10.0", 223 | "termcolor", 224 | "textwrap", 225 | "unicode-width", 226 | "vec_map", 227 | ] 228 | 229 | [[package]] 230 | name = "clap_derive" 231 | version = "3.0.0-beta.1" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "fb51c9e75b94452505acd21d929323f5a5c6c4735a852adbd39ef5fb1b014f30" 234 | dependencies = [ 235 | "heck", 236 | "proc-macro-error", 237 | "proc-macro2", 238 | "quote", 239 | "syn", 240 | ] 241 | 242 | [[package]] 243 | name = "cloudabi" 244 | version = "0.0.3" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 247 | dependencies = [ 248 | "bitflags", 249 | ] 250 | 251 | [[package]] 252 | name = "cloudman-rs" 253 | version = "0.1.7" 254 | dependencies = [ 255 | "base64 0.11.0", 256 | "built", 257 | "caseless", 258 | "clap", 259 | "cursive", 260 | "dirs", 261 | "enum-map", 262 | "hashbrown", 263 | "rand", 264 | "rusoto_core", 265 | "rusoto_ec2", 266 | "rusoto_logs", 267 | "tokio 0.2.21", 268 | "tokio-core", 269 | "vte", 270 | ] 271 | 272 | [[package]] 273 | name = "constant_time_eq" 274 | version = "0.1.5" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 277 | 278 | [[package]] 279 | name = "core-foundation" 280 | version = "0.7.0" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" 283 | dependencies = [ 284 | "core-foundation-sys", 285 | "libc", 286 | ] 287 | 288 | [[package]] 289 | name = "core-foundation-sys" 290 | version = "0.7.0" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" 293 | 294 | [[package]] 295 | name = "cpuid-bool" 296 | version = "0.1.2" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" 299 | 300 | [[package]] 301 | name = "crc32fast" 302 | version = "1.2.0" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" 305 | dependencies = [ 306 | "cfg-if 0.1.10", 307 | ] 308 | 309 | [[package]] 310 | name = "crossbeam-channel" 311 | version = "0.5.0" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" 314 | dependencies = [ 315 | "cfg-if 1.0.0", 316 | "crossbeam-utils 0.8.3", 317 | ] 318 | 319 | [[package]] 320 | name = "crossbeam-deque" 321 | version = "0.7.3" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" 324 | dependencies = [ 325 | "crossbeam-epoch", 326 | "crossbeam-utils 0.7.2", 327 | "maybe-uninit", 328 | ] 329 | 330 | [[package]] 331 | name = "crossbeam-epoch" 332 | version = "0.8.2" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" 335 | dependencies = [ 336 | "autocfg", 337 | "cfg-if 0.1.10", 338 | "crossbeam-utils 0.7.2", 339 | "lazy_static", 340 | "maybe-uninit", 341 | "memoffset", 342 | "scopeguard", 343 | ] 344 | 345 | [[package]] 346 | name = "crossbeam-queue" 347 | version = "0.2.3" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" 350 | dependencies = [ 351 | "cfg-if 0.1.10", 352 | "crossbeam-utils 0.7.2", 353 | "maybe-uninit", 354 | ] 355 | 356 | [[package]] 357 | name = "crossbeam-utils" 358 | version = "0.7.2" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 361 | dependencies = [ 362 | "autocfg", 363 | "cfg-if 0.1.10", 364 | "lazy_static", 365 | ] 366 | 367 | [[package]] 368 | name = "crossbeam-utils" 369 | version = "0.8.3" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" 372 | dependencies = [ 373 | "autocfg", 374 | "cfg-if 1.0.0", 375 | "lazy_static", 376 | ] 377 | 378 | [[package]] 379 | name = "crypto-mac" 380 | version = "0.8.0" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" 383 | dependencies = [ 384 | "generic-array", 385 | "subtle", 386 | ] 387 | 388 | [[package]] 389 | name = "cursive" 390 | version = "0.16.3" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "6593c3409eb794bf22090bec60dda1e19d1def284478bec7e5a92da3cf977c52" 393 | dependencies = [ 394 | "ahash 0.6.3", 395 | "cfg-if 1.0.0", 396 | "crossbeam-channel", 397 | "cursive_core", 398 | "lazy_static", 399 | "libc", 400 | "log", 401 | "maplit", 402 | "ncurses", 403 | "signal-hook", 404 | "term_size", 405 | "unicode-segmentation", 406 | "unicode-width", 407 | "wasmer_enumset", 408 | ] 409 | 410 | [[package]] 411 | name = "cursive_core" 412 | version = "0.2.2" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "025ac0bcd21ced752d27b70e6aa2285a3513d07b5a0c7f89e71121d20ca1429d" 415 | dependencies = [ 416 | "ahash 0.6.3", 417 | "chrono", 418 | "crossbeam-channel", 419 | "enum-map", 420 | "lazy_static", 421 | "libc", 422 | "log", 423 | "num", 424 | "owning_ref", 425 | "pulldown-cmark", 426 | "syn", 427 | "toml", 428 | "unicode-segmentation", 429 | "unicode-width", 430 | "wasmer_enumset", 431 | "xi-unicode", 432 | ] 433 | 434 | [[package]] 435 | name = "darling" 436 | version = "0.10.2" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" 439 | dependencies = [ 440 | "darling_core", 441 | "darling_macro", 442 | ] 443 | 444 | [[package]] 445 | name = "darling_core" 446 | version = "0.10.2" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" 449 | dependencies = [ 450 | "fnv", 451 | "ident_case", 452 | "proc-macro2", 453 | "quote", 454 | "strsim 0.9.3", 455 | "syn", 456 | ] 457 | 458 | [[package]] 459 | name = "darling_macro" 460 | version = "0.10.2" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" 463 | dependencies = [ 464 | "darling_core", 465 | "quote", 466 | "syn", 467 | ] 468 | 469 | [[package]] 470 | name = "digest" 471 | version = "0.9.0" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 474 | dependencies = [ 475 | "generic-array", 476 | ] 477 | 478 | [[package]] 479 | name = "dirs" 480 | version = "2.0.2" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" 483 | dependencies = [ 484 | "cfg-if 0.1.10", 485 | "dirs-sys", 486 | ] 487 | 488 | [[package]] 489 | name = "dirs-sys" 490 | version = "0.3.5" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" 493 | dependencies = [ 494 | "libc", 495 | "redox_users", 496 | "winapi 0.3.8", 497 | ] 498 | 499 | [[package]] 500 | name = "discard" 501 | version = "1.0.4" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" 504 | 505 | [[package]] 506 | name = "dtoa" 507 | version = "0.4.5" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" 510 | 511 | [[package]] 512 | name = "enum-map" 513 | version = "0.6.2" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "70a375f899a53b9848ad9fb459b5bf90e4851ae5d9fea89134b062dc1828b26e" 516 | dependencies = [ 517 | "array-macro", 518 | "enum-map-derive", 519 | ] 520 | 521 | [[package]] 522 | name = "enum-map-derive" 523 | version = "0.4.3" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "e57001dfb2532f5a103ff869656887fae9a8defa7d236f3e39d2ee86ed629ad7" 526 | dependencies = [ 527 | "proc-macro2", 528 | "quote", 529 | "syn", 530 | ] 531 | 532 | [[package]] 533 | name = "fnv" 534 | version = "1.0.7" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 537 | 538 | [[package]] 539 | name = "foreign-types" 540 | version = "0.3.2" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 543 | dependencies = [ 544 | "foreign-types-shared", 545 | ] 546 | 547 | [[package]] 548 | name = "foreign-types-shared" 549 | version = "0.1.1" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 552 | 553 | [[package]] 554 | name = "fuchsia-zircon" 555 | version = "0.3.3" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 558 | dependencies = [ 559 | "bitflags", 560 | "fuchsia-zircon-sys", 561 | ] 562 | 563 | [[package]] 564 | name = "fuchsia-zircon-sys" 565 | version = "0.3.3" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 568 | 569 | [[package]] 570 | name = "futures" 571 | version = "0.1.29" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" 574 | 575 | [[package]] 576 | name = "futures" 577 | version = "0.3.5" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" 580 | dependencies = [ 581 | "futures-channel", 582 | "futures-core", 583 | "futures-executor", 584 | "futures-io", 585 | "futures-sink", 586 | "futures-task", 587 | "futures-util", 588 | ] 589 | 590 | [[package]] 591 | name = "futures-channel" 592 | version = "0.3.5" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" 595 | dependencies = [ 596 | "futures-core", 597 | "futures-sink", 598 | ] 599 | 600 | [[package]] 601 | name = "futures-core" 602 | version = "0.3.5" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" 605 | 606 | [[package]] 607 | name = "futures-executor" 608 | version = "0.3.5" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" 611 | dependencies = [ 612 | "futures-core", 613 | "futures-task", 614 | "futures-util", 615 | ] 616 | 617 | [[package]] 618 | name = "futures-io" 619 | version = "0.3.5" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" 622 | 623 | [[package]] 624 | name = "futures-macro" 625 | version = "0.3.5" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" 628 | dependencies = [ 629 | "proc-macro-hack", 630 | "proc-macro2", 631 | "quote", 632 | "syn", 633 | ] 634 | 635 | [[package]] 636 | name = "futures-sink" 637 | version = "0.3.5" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" 640 | 641 | [[package]] 642 | name = "futures-task" 643 | version = "0.3.5" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" 646 | dependencies = [ 647 | "once_cell", 648 | ] 649 | 650 | [[package]] 651 | name = "futures-util" 652 | version = "0.3.5" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" 655 | dependencies = [ 656 | "futures-channel", 657 | "futures-core", 658 | "futures-io", 659 | "futures-macro", 660 | "futures-sink", 661 | "futures-task", 662 | "memchr", 663 | "pin-project", 664 | "pin-utils", 665 | "proc-macro-hack", 666 | "proc-macro-nested", 667 | "slab", 668 | ] 669 | 670 | [[package]] 671 | name = "generic-array" 672 | version = "0.14.3" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "60fb4bb6bba52f78a471264d9a3b7d026cc0af47b22cd2cffbc0b787ca003e63" 675 | dependencies = [ 676 | "typenum", 677 | "version_check", 678 | ] 679 | 680 | [[package]] 681 | name = "getrandom" 682 | version = "0.1.14" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 685 | dependencies = [ 686 | "cfg-if 0.1.10", 687 | "libc", 688 | "wasi 0.9.0+wasi-snapshot-preview1", 689 | ] 690 | 691 | [[package]] 692 | name = "getrandom" 693 | version = "0.2.2" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" 696 | dependencies = [ 697 | "cfg-if 1.0.0", 698 | "libc", 699 | "wasi 0.10.2+wasi-snapshot-preview1", 700 | ] 701 | 702 | [[package]] 703 | name = "h2" 704 | version = "0.2.5" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" 707 | dependencies = [ 708 | "bytes 0.5.4", 709 | "fnv", 710 | "futures-core", 711 | "futures-sink", 712 | "futures-util", 713 | "http", 714 | "indexmap", 715 | "log", 716 | "slab", 717 | "tokio 0.2.21", 718 | "tokio-util", 719 | ] 720 | 721 | [[package]] 722 | name = "hashbrown" 723 | version = "0.7.2" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" 726 | dependencies = [ 727 | "ahash 0.3.8", 728 | "autocfg", 729 | ] 730 | 731 | [[package]] 732 | name = "heck" 733 | version = "0.3.1" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" 736 | dependencies = [ 737 | "unicode-segmentation", 738 | ] 739 | 740 | [[package]] 741 | name = "hermit-abi" 742 | version = "0.1.13" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" 745 | dependencies = [ 746 | "libc", 747 | ] 748 | 749 | [[package]] 750 | name = "hex" 751 | version = "0.4.2" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" 754 | 755 | [[package]] 756 | name = "hmac" 757 | version = "0.8.1" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" 760 | dependencies = [ 761 | "crypto-mac", 762 | "digest", 763 | ] 764 | 765 | [[package]] 766 | name = "http" 767 | version = "0.2.1" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" 770 | dependencies = [ 771 | "bytes 0.5.4", 772 | "fnv", 773 | "itoa", 774 | ] 775 | 776 | [[package]] 777 | name = "http-body" 778 | version = "0.3.1" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" 781 | dependencies = [ 782 | "bytes 0.5.4", 783 | "http", 784 | ] 785 | 786 | [[package]] 787 | name = "httparse" 788 | version = "1.3.4" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" 791 | 792 | [[package]] 793 | name = "hyper" 794 | version = "0.13.6" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "a6e7655b9594024ad0ee439f3b5a7299369dc2a3f459b47c696f9ff676f9aa1f" 797 | dependencies = [ 798 | "bytes 0.5.4", 799 | "futures-channel", 800 | "futures-core", 801 | "futures-util", 802 | "h2", 803 | "http", 804 | "http-body", 805 | "httparse", 806 | "itoa", 807 | "log", 808 | "pin-project", 809 | "socket2", 810 | "time 0.1.43", 811 | "tokio 0.2.21", 812 | "tower-service", 813 | "want", 814 | ] 815 | 816 | [[package]] 817 | name = "hyper-tls" 818 | version = "0.4.1" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa" 821 | dependencies = [ 822 | "bytes 0.5.4", 823 | "hyper", 824 | "native-tls", 825 | "tokio 0.2.21", 826 | "tokio-tls", 827 | ] 828 | 829 | [[package]] 830 | name = "ident_case" 831 | version = "1.0.1" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 834 | 835 | [[package]] 836 | name = "idna" 837 | version = "0.2.0" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 840 | dependencies = [ 841 | "matches", 842 | "unicode-bidi", 843 | "unicode-normalization", 844 | ] 845 | 846 | [[package]] 847 | name = "indexmap" 848 | version = "1.4.0" 849 | source = "registry+https://github.com/rust-lang/crates.io-index" 850 | checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" 851 | dependencies = [ 852 | "autocfg", 853 | ] 854 | 855 | [[package]] 856 | name = "iovec" 857 | version = "0.1.4" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 860 | dependencies = [ 861 | "libc", 862 | ] 863 | 864 | [[package]] 865 | name = "itoa" 866 | version = "0.4.5" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" 869 | 870 | [[package]] 871 | name = "kernel32-sys" 872 | version = "0.2.2" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 875 | dependencies = [ 876 | "winapi 0.2.8", 877 | "winapi-build", 878 | ] 879 | 880 | [[package]] 881 | name = "lazy_static" 882 | version = "1.4.0" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 885 | 886 | [[package]] 887 | name = "libc" 888 | version = "0.2.71" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" 891 | 892 | [[package]] 893 | name = "lock_api" 894 | version = "0.3.4" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" 897 | dependencies = [ 898 | "scopeguard", 899 | ] 900 | 901 | [[package]] 902 | name = "log" 903 | version = "0.4.8" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 906 | dependencies = [ 907 | "cfg-if 0.1.10", 908 | ] 909 | 910 | [[package]] 911 | name = "maplit" 912 | version = "1.0.2" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" 915 | 916 | [[package]] 917 | name = "matches" 918 | version = "0.1.8" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 921 | 922 | [[package]] 923 | name = "maybe-uninit" 924 | version = "2.0.0" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 927 | 928 | [[package]] 929 | name = "md5" 930 | version = "0.7.0" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" 933 | 934 | [[package]] 935 | name = "memchr" 936 | version = "2.3.3" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 939 | 940 | [[package]] 941 | name = "memoffset" 942 | version = "0.5.4" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" 945 | dependencies = [ 946 | "autocfg", 947 | ] 948 | 949 | [[package]] 950 | name = "mio" 951 | version = "0.6.22" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" 954 | dependencies = [ 955 | "cfg-if 0.1.10", 956 | "fuchsia-zircon", 957 | "fuchsia-zircon-sys", 958 | "iovec", 959 | "kernel32-sys", 960 | "libc", 961 | "log", 962 | "miow 0.2.1", 963 | "net2", 964 | "slab", 965 | "winapi 0.2.8", 966 | ] 967 | 968 | [[package]] 969 | name = "mio-named-pipes" 970 | version = "0.1.6" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" 973 | dependencies = [ 974 | "log", 975 | "mio", 976 | "miow 0.3.5", 977 | "winapi 0.3.8", 978 | ] 979 | 980 | [[package]] 981 | name = "mio-uds" 982 | version = "0.6.8" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" 985 | dependencies = [ 986 | "iovec", 987 | "libc", 988 | "mio", 989 | ] 990 | 991 | [[package]] 992 | name = "miow" 993 | version = "0.2.1" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 996 | dependencies = [ 997 | "kernel32-sys", 998 | "net2", 999 | "winapi 0.2.8", 1000 | "ws2_32-sys", 1001 | ] 1002 | 1003 | [[package]] 1004 | name = "miow" 1005 | version = "0.3.5" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" 1008 | dependencies = [ 1009 | "socket2", 1010 | "winapi 0.3.8", 1011 | ] 1012 | 1013 | [[package]] 1014 | name = "native-tls" 1015 | version = "0.2.4" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" 1018 | dependencies = [ 1019 | "lazy_static", 1020 | "libc", 1021 | "log", 1022 | "openssl", 1023 | "openssl-probe", 1024 | "openssl-sys", 1025 | "schannel", 1026 | "security-framework", 1027 | "security-framework-sys", 1028 | "tempfile", 1029 | ] 1030 | 1031 | [[package]] 1032 | name = "ncurses" 1033 | version = "5.99.0" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "15699bee2f37e9f8828c7b35b2bc70d13846db453f2d507713b758fabe536b82" 1036 | dependencies = [ 1037 | "cc", 1038 | "libc", 1039 | "pkg-config", 1040 | ] 1041 | 1042 | [[package]] 1043 | name = "net2" 1044 | version = "0.2.34" 1045 | source = "registry+https://github.com/rust-lang/crates.io-index" 1046 | checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" 1047 | dependencies = [ 1048 | "cfg-if 0.1.10", 1049 | "libc", 1050 | "winapi 0.3.8", 1051 | ] 1052 | 1053 | [[package]] 1054 | name = "num" 1055 | version = "0.3.1" 1056 | source = "registry+https://github.com/rust-lang/crates.io-index" 1057 | checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" 1058 | dependencies = [ 1059 | "num-complex", 1060 | "num-integer", 1061 | "num-iter", 1062 | "num-rational", 1063 | "num-traits", 1064 | ] 1065 | 1066 | [[package]] 1067 | name = "num-complex" 1068 | version = "0.3.1" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" 1071 | dependencies = [ 1072 | "num-traits", 1073 | ] 1074 | 1075 | [[package]] 1076 | name = "num-integer" 1077 | version = "0.1.44" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 1080 | dependencies = [ 1081 | "autocfg", 1082 | "num-traits", 1083 | ] 1084 | 1085 | [[package]] 1086 | name = "num-iter" 1087 | version = "0.1.42" 1088 | source = "registry+https://github.com/rust-lang/crates.io-index" 1089 | checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" 1090 | dependencies = [ 1091 | "autocfg", 1092 | "num-integer", 1093 | "num-traits", 1094 | ] 1095 | 1096 | [[package]] 1097 | name = "num-rational" 1098 | version = "0.3.2" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" 1101 | dependencies = [ 1102 | "autocfg", 1103 | "num-integer", 1104 | "num-traits", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "num-traits" 1109 | version = "0.2.14" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 1112 | dependencies = [ 1113 | "autocfg", 1114 | ] 1115 | 1116 | [[package]] 1117 | name = "num_cpus" 1118 | version = "1.13.0" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 1121 | dependencies = [ 1122 | "hermit-abi", 1123 | "libc", 1124 | ] 1125 | 1126 | [[package]] 1127 | name = "once_cell" 1128 | version = "1.7.2" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" 1131 | 1132 | [[package]] 1133 | name = "opaque-debug" 1134 | version = "0.3.0" 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" 1136 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 1137 | 1138 | [[package]] 1139 | name = "openssl" 1140 | version = "0.10.29" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" 1143 | dependencies = [ 1144 | "bitflags", 1145 | "cfg-if 0.1.10", 1146 | "foreign-types", 1147 | "lazy_static", 1148 | "libc", 1149 | "openssl-sys", 1150 | ] 1151 | 1152 | [[package]] 1153 | name = "openssl-probe" 1154 | version = "0.1.2" 1155 | source = "registry+https://github.com/rust-lang/crates.io-index" 1156 | checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" 1157 | 1158 | [[package]] 1159 | name = "openssl-sys" 1160 | version = "0.9.58" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" 1163 | dependencies = [ 1164 | "autocfg", 1165 | "cc", 1166 | "libc", 1167 | "pkg-config", 1168 | "vcpkg", 1169 | ] 1170 | 1171 | [[package]] 1172 | name = "os_str_bytes" 1173 | version = "2.3.1" 1174 | source = "registry+https://github.com/rust-lang/crates.io-index" 1175 | checksum = "06de47b848347d8c4c94219ad8ecd35eb90231704b067e67e6ae2e36ee023510" 1176 | 1177 | [[package]] 1178 | name = "owning_ref" 1179 | version = "0.4.1" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" 1182 | dependencies = [ 1183 | "stable_deref_trait", 1184 | ] 1185 | 1186 | [[package]] 1187 | name = "parking_lot" 1188 | version = "0.9.0" 1189 | source = "registry+https://github.com/rust-lang/crates.io-index" 1190 | checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" 1191 | dependencies = [ 1192 | "lock_api", 1193 | "parking_lot_core", 1194 | "rustc_version", 1195 | ] 1196 | 1197 | [[package]] 1198 | name = "parking_lot_core" 1199 | version = "0.6.2" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" 1202 | dependencies = [ 1203 | "cfg-if 0.1.10", 1204 | "cloudabi", 1205 | "libc", 1206 | "redox_syscall", 1207 | "rustc_version", 1208 | "smallvec 0.6.13", 1209 | "winapi 0.3.8", 1210 | ] 1211 | 1212 | [[package]] 1213 | name = "percent-encoding" 1214 | version = "2.1.0" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 1217 | 1218 | [[package]] 1219 | name = "pin-project" 1220 | version = "0.4.22" 1221 | source = "registry+https://github.com/rust-lang/crates.io-index" 1222 | checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17" 1223 | dependencies = [ 1224 | "pin-project-internal", 1225 | ] 1226 | 1227 | [[package]] 1228 | name = "pin-project-internal" 1229 | version = "0.4.22" 1230 | source = "registry+https://github.com/rust-lang/crates.io-index" 1231 | checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" 1232 | dependencies = [ 1233 | "proc-macro2", 1234 | "quote", 1235 | "syn", 1236 | ] 1237 | 1238 | [[package]] 1239 | name = "pin-project-lite" 1240 | version = "0.1.7" 1241 | source = "registry+https://github.com/rust-lang/crates.io-index" 1242 | checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" 1243 | 1244 | [[package]] 1245 | name = "pin-utils" 1246 | version = "0.1.0" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1249 | 1250 | [[package]] 1251 | name = "pkg-config" 1252 | version = "0.3.17" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" 1255 | 1256 | [[package]] 1257 | name = "ppv-lite86" 1258 | version = "0.2.8" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" 1261 | 1262 | [[package]] 1263 | name = "proc-macro-error" 1264 | version = "0.4.12" 1265 | source = "registry+https://github.com/rust-lang/crates.io-index" 1266 | checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" 1267 | dependencies = [ 1268 | "proc-macro-error-attr", 1269 | "proc-macro2", 1270 | "quote", 1271 | "syn", 1272 | "version_check", 1273 | ] 1274 | 1275 | [[package]] 1276 | name = "proc-macro-error-attr" 1277 | version = "0.4.12" 1278 | source = "registry+https://github.com/rust-lang/crates.io-index" 1279 | checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" 1280 | dependencies = [ 1281 | "proc-macro2", 1282 | "quote", 1283 | "syn", 1284 | "syn-mid", 1285 | "version_check", 1286 | ] 1287 | 1288 | [[package]] 1289 | name = "proc-macro-hack" 1290 | version = "0.5.16" 1291 | source = "registry+https://github.com/rust-lang/crates.io-index" 1292 | checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" 1293 | 1294 | [[package]] 1295 | name = "proc-macro-nested" 1296 | version = "0.1.6" 1297 | source = "registry+https://github.com/rust-lang/crates.io-index" 1298 | checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" 1299 | 1300 | [[package]] 1301 | name = "proc-macro2" 1302 | version = "1.0.18" 1303 | source = "registry+https://github.com/rust-lang/crates.io-index" 1304 | checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" 1305 | dependencies = [ 1306 | "unicode-xid", 1307 | ] 1308 | 1309 | [[package]] 1310 | name = "pulldown-cmark" 1311 | version = "0.8.0" 1312 | source = "registry+https://github.com/rust-lang/crates.io-index" 1313 | checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" 1314 | dependencies = [ 1315 | "bitflags", 1316 | "memchr", 1317 | "unicase", 1318 | ] 1319 | 1320 | [[package]] 1321 | name = "quote" 1322 | version = "1.0.7" 1323 | source = "registry+https://github.com/rust-lang/crates.io-index" 1324 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 1325 | dependencies = [ 1326 | "proc-macro2", 1327 | ] 1328 | 1329 | [[package]] 1330 | name = "rand" 1331 | version = "0.7.3" 1332 | source = "registry+https://github.com/rust-lang/crates.io-index" 1333 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1334 | dependencies = [ 1335 | "getrandom 0.1.14", 1336 | "libc", 1337 | "rand_chacha", 1338 | "rand_core", 1339 | "rand_hc", 1340 | ] 1341 | 1342 | [[package]] 1343 | name = "rand_chacha" 1344 | version = "0.2.2" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1347 | dependencies = [ 1348 | "ppv-lite86", 1349 | "rand_core", 1350 | ] 1351 | 1352 | [[package]] 1353 | name = "rand_core" 1354 | version = "0.5.1" 1355 | source = "registry+https://github.com/rust-lang/crates.io-index" 1356 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1357 | dependencies = [ 1358 | "getrandom 0.1.14", 1359 | ] 1360 | 1361 | [[package]] 1362 | name = "rand_hc" 1363 | version = "0.2.0" 1364 | source = "registry+https://github.com/rust-lang/crates.io-index" 1365 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1366 | dependencies = [ 1367 | "rand_core", 1368 | ] 1369 | 1370 | [[package]] 1371 | name = "redox_syscall" 1372 | version = "0.1.56" 1373 | source = "registry+https://github.com/rust-lang/crates.io-index" 1374 | checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 1375 | 1376 | [[package]] 1377 | name = "redox_users" 1378 | version = "0.3.4" 1379 | source = "registry+https://github.com/rust-lang/crates.io-index" 1380 | checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" 1381 | dependencies = [ 1382 | "getrandom 0.1.14", 1383 | "redox_syscall", 1384 | "rust-argon2", 1385 | ] 1386 | 1387 | [[package]] 1388 | name = "regex" 1389 | version = "1.3.9" 1390 | source = "registry+https://github.com/rust-lang/crates.io-index" 1391 | checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" 1392 | dependencies = [ 1393 | "aho-corasick", 1394 | "memchr", 1395 | "regex-syntax", 1396 | "thread_local", 1397 | ] 1398 | 1399 | [[package]] 1400 | name = "regex-syntax" 1401 | version = "0.6.18" 1402 | source = "registry+https://github.com/rust-lang/crates.io-index" 1403 | checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" 1404 | 1405 | [[package]] 1406 | name = "remove_dir_all" 1407 | version = "0.5.3" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1410 | dependencies = [ 1411 | "winapi 0.3.8", 1412 | ] 1413 | 1414 | [[package]] 1415 | name = "rusoto_core" 1416 | version = "0.45.0" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "e977941ee0658df96fca7291ecc6fc9a754600b21ad84b959eb1dbbc9d5abcc7" 1419 | dependencies = [ 1420 | "async-trait", 1421 | "base64 0.12.1", 1422 | "bytes 0.5.4", 1423 | "crc32fast", 1424 | "futures 0.3.5", 1425 | "http", 1426 | "hyper", 1427 | "hyper-tls", 1428 | "lazy_static", 1429 | "log", 1430 | "md5", 1431 | "percent-encoding", 1432 | "pin-project", 1433 | "rusoto_credential", 1434 | "rusoto_signature", 1435 | "rustc_version", 1436 | "serde", 1437 | "serde_json", 1438 | "tokio 0.2.21", 1439 | "xml-rs", 1440 | ] 1441 | 1442 | [[package]] 1443 | name = "rusoto_credential" 1444 | version = "0.45.0" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "09ac05563f83489b19b4d413607a30821ab08bbd9007d14fa05618da3ef09d8b" 1447 | dependencies = [ 1448 | "async-trait", 1449 | "chrono", 1450 | "dirs", 1451 | "futures 0.3.5", 1452 | "hyper", 1453 | "pin-project", 1454 | "regex", 1455 | "serde", 1456 | "serde_json", 1457 | "shlex", 1458 | "tokio 0.2.21", 1459 | "zeroize", 1460 | ] 1461 | 1462 | [[package]] 1463 | name = "rusoto_ec2" 1464 | version = "0.45.0" 1465 | source = "registry+https://github.com/rust-lang/crates.io-index" 1466 | checksum = "5145366791ba9097d917330944ef460e1ebd67da871a8e04ad9f51cecc64375f" 1467 | dependencies = [ 1468 | "async-trait", 1469 | "bytes 0.5.4", 1470 | "futures 0.3.5", 1471 | "rusoto_core", 1472 | "serde_urlencoded", 1473 | "xml-rs", 1474 | ] 1475 | 1476 | [[package]] 1477 | name = "rusoto_logs" 1478 | version = "0.45.0" 1479 | source = "registry+https://github.com/rust-lang/crates.io-index" 1480 | checksum = "47079cf81cf7301d96195e495f2649cd731bedfe592925741997eb5a02d253e6" 1481 | dependencies = [ 1482 | "async-trait", 1483 | "bytes 0.5.4", 1484 | "futures 0.3.5", 1485 | "rusoto_core", 1486 | "serde", 1487 | "serde_json", 1488 | ] 1489 | 1490 | [[package]] 1491 | name = "rusoto_signature" 1492 | version = "0.45.0" 1493 | source = "registry+https://github.com/rust-lang/crates.io-index" 1494 | checksum = "97a740a88dde8ded81b6f2cff9cd5e054a5a2e38a38397260f7acdd2c85d17dd" 1495 | dependencies = [ 1496 | "base64 0.12.1", 1497 | "bytes 0.5.4", 1498 | "futures 0.3.5", 1499 | "hex", 1500 | "hmac", 1501 | "http", 1502 | "hyper", 1503 | "log", 1504 | "md5", 1505 | "percent-encoding", 1506 | "pin-project", 1507 | "rusoto_credential", 1508 | "rustc_version", 1509 | "serde", 1510 | "sha2", 1511 | "time 0.2.16", 1512 | "tokio 0.2.21", 1513 | ] 1514 | 1515 | [[package]] 1516 | name = "rust-argon2" 1517 | version = "0.7.0" 1518 | source = "registry+https://github.com/rust-lang/crates.io-index" 1519 | checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" 1520 | dependencies = [ 1521 | "base64 0.11.0", 1522 | "blake2b_simd", 1523 | "constant_time_eq", 1524 | "crossbeam-utils 0.7.2", 1525 | ] 1526 | 1527 | [[package]] 1528 | name = "rustc_version" 1529 | version = "0.2.3" 1530 | source = "registry+https://github.com/rust-lang/crates.io-index" 1531 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 1532 | dependencies = [ 1533 | "semver", 1534 | ] 1535 | 1536 | [[package]] 1537 | name = "ryu" 1538 | version = "1.0.5" 1539 | source = "registry+https://github.com/rust-lang/crates.io-index" 1540 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1541 | 1542 | [[package]] 1543 | name = "schannel" 1544 | version = "0.1.19" 1545 | source = "registry+https://github.com/rust-lang/crates.io-index" 1546 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 1547 | dependencies = [ 1548 | "lazy_static", 1549 | "winapi 0.3.8", 1550 | ] 1551 | 1552 | [[package]] 1553 | name = "scoped-tls" 1554 | version = "0.1.2" 1555 | source = "registry+https://github.com/rust-lang/crates.io-index" 1556 | checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" 1557 | 1558 | [[package]] 1559 | name = "scopeguard" 1560 | version = "1.1.0" 1561 | source = "registry+https://github.com/rust-lang/crates.io-index" 1562 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1563 | 1564 | [[package]] 1565 | name = "security-framework" 1566 | version = "0.4.4" 1567 | source = "registry+https://github.com/rust-lang/crates.io-index" 1568 | checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" 1569 | dependencies = [ 1570 | "bitflags", 1571 | "core-foundation", 1572 | "core-foundation-sys", 1573 | "libc", 1574 | "security-framework-sys", 1575 | ] 1576 | 1577 | [[package]] 1578 | name = "security-framework-sys" 1579 | version = "0.4.3" 1580 | source = "registry+https://github.com/rust-lang/crates.io-index" 1581 | checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" 1582 | dependencies = [ 1583 | "core-foundation-sys", 1584 | "libc", 1585 | ] 1586 | 1587 | [[package]] 1588 | name = "semver" 1589 | version = "0.9.0" 1590 | source = "registry+https://github.com/rust-lang/crates.io-index" 1591 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 1592 | dependencies = [ 1593 | "semver-parser", 1594 | "serde", 1595 | ] 1596 | 1597 | [[package]] 1598 | name = "semver-parser" 1599 | version = "0.7.0" 1600 | source = "registry+https://github.com/rust-lang/crates.io-index" 1601 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 1602 | 1603 | [[package]] 1604 | name = "serde" 1605 | version = "1.0.111" 1606 | source = "registry+https://github.com/rust-lang/crates.io-index" 1607 | checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" 1608 | dependencies = [ 1609 | "serde_derive", 1610 | ] 1611 | 1612 | [[package]] 1613 | name = "serde_derive" 1614 | version = "1.0.111" 1615 | source = "registry+https://github.com/rust-lang/crates.io-index" 1616 | checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" 1617 | dependencies = [ 1618 | "proc-macro2", 1619 | "quote", 1620 | "syn", 1621 | ] 1622 | 1623 | [[package]] 1624 | name = "serde_json" 1625 | version = "1.0.55" 1626 | source = "registry+https://github.com/rust-lang/crates.io-index" 1627 | checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226" 1628 | dependencies = [ 1629 | "itoa", 1630 | "ryu", 1631 | "serde", 1632 | ] 1633 | 1634 | [[package]] 1635 | name = "serde_urlencoded" 1636 | version = "0.6.1" 1637 | source = "registry+https://github.com/rust-lang/crates.io-index" 1638 | checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" 1639 | dependencies = [ 1640 | "dtoa", 1641 | "itoa", 1642 | "serde", 1643 | "url", 1644 | ] 1645 | 1646 | [[package]] 1647 | name = "sha1" 1648 | version = "0.6.0" 1649 | source = "registry+https://github.com/rust-lang/crates.io-index" 1650 | checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" 1651 | 1652 | [[package]] 1653 | name = "sha2" 1654 | version = "0.9.1" 1655 | source = "registry+https://github.com/rust-lang/crates.io-index" 1656 | checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" 1657 | dependencies = [ 1658 | "block-buffer", 1659 | "cfg-if 0.1.10", 1660 | "cpuid-bool", 1661 | "digest", 1662 | "opaque-debug", 1663 | ] 1664 | 1665 | [[package]] 1666 | name = "shlex" 1667 | version = "0.1.1" 1668 | source = "registry+https://github.com/rust-lang/crates.io-index" 1669 | checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" 1670 | 1671 | [[package]] 1672 | name = "signal-hook" 1673 | version = "0.3.7" 1674 | source = "registry+https://github.com/rust-lang/crates.io-index" 1675 | checksum = "6aa894ef3fade0ee7243422f4fbbd6c2b48e6de767e621d37ef65f2310f53cea" 1676 | dependencies = [ 1677 | "libc", 1678 | "signal-hook-registry", 1679 | ] 1680 | 1681 | [[package]] 1682 | name = "signal-hook-registry" 1683 | version = "1.3.0" 1684 | source = "registry+https://github.com/rust-lang/crates.io-index" 1685 | checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" 1686 | dependencies = [ 1687 | "libc", 1688 | ] 1689 | 1690 | [[package]] 1691 | name = "slab" 1692 | version = "0.4.2" 1693 | source = "registry+https://github.com/rust-lang/crates.io-index" 1694 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 1695 | 1696 | [[package]] 1697 | name = "smallvec" 1698 | version = "0.6.13" 1699 | source = "registry+https://github.com/rust-lang/crates.io-index" 1700 | checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" 1701 | dependencies = [ 1702 | "maybe-uninit", 1703 | ] 1704 | 1705 | [[package]] 1706 | name = "smallvec" 1707 | version = "1.4.0" 1708 | source = "registry+https://github.com/rust-lang/crates.io-index" 1709 | checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" 1710 | 1711 | [[package]] 1712 | name = "socket2" 1713 | version = "0.3.12" 1714 | source = "registry+https://github.com/rust-lang/crates.io-index" 1715 | checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" 1716 | dependencies = [ 1717 | "cfg-if 0.1.10", 1718 | "libc", 1719 | "redox_syscall", 1720 | "winapi 0.3.8", 1721 | ] 1722 | 1723 | [[package]] 1724 | name = "stable_deref_trait" 1725 | version = "1.1.1" 1726 | source = "registry+https://github.com/rust-lang/crates.io-index" 1727 | checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" 1728 | 1729 | [[package]] 1730 | name = "standback" 1731 | version = "0.2.9" 1732 | source = "registry+https://github.com/rust-lang/crates.io-index" 1733 | checksum = "b0437cfb83762844799a60e1e3b489d5ceb6a650fbacb86437badc1b6d87b246" 1734 | dependencies = [ 1735 | "version_check", 1736 | ] 1737 | 1738 | [[package]] 1739 | name = "stdweb" 1740 | version = "0.4.20" 1741 | source = "registry+https://github.com/rust-lang/crates.io-index" 1742 | checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" 1743 | dependencies = [ 1744 | "discard", 1745 | "rustc_version", 1746 | "stdweb-derive", 1747 | "stdweb-internal-macros", 1748 | "stdweb-internal-runtime", 1749 | "wasm-bindgen", 1750 | ] 1751 | 1752 | [[package]] 1753 | name = "stdweb-derive" 1754 | version = "0.5.3" 1755 | source = "registry+https://github.com/rust-lang/crates.io-index" 1756 | checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" 1757 | dependencies = [ 1758 | "proc-macro2", 1759 | "quote", 1760 | "serde", 1761 | "serde_derive", 1762 | "syn", 1763 | ] 1764 | 1765 | [[package]] 1766 | name = "stdweb-internal-macros" 1767 | version = "0.2.9" 1768 | source = "registry+https://github.com/rust-lang/crates.io-index" 1769 | checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" 1770 | dependencies = [ 1771 | "base-x", 1772 | "proc-macro2", 1773 | "quote", 1774 | "serde", 1775 | "serde_derive", 1776 | "serde_json", 1777 | "sha1", 1778 | "syn", 1779 | ] 1780 | 1781 | [[package]] 1782 | name = "stdweb-internal-runtime" 1783 | version = "0.1.5" 1784 | source = "registry+https://github.com/rust-lang/crates.io-index" 1785 | checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" 1786 | 1787 | [[package]] 1788 | name = "strsim" 1789 | version = "0.9.3" 1790 | source = "registry+https://github.com/rust-lang/crates.io-index" 1791 | checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" 1792 | 1793 | [[package]] 1794 | name = "strsim" 1795 | version = "0.10.0" 1796 | source = "registry+https://github.com/rust-lang/crates.io-index" 1797 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1798 | 1799 | [[package]] 1800 | name = "subtle" 1801 | version = "2.2.3" 1802 | source = "registry+https://github.com/rust-lang/crates.io-index" 1803 | checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" 1804 | 1805 | [[package]] 1806 | name = "syn" 1807 | version = "1.0.31" 1808 | source = "registry+https://github.com/rust-lang/crates.io-index" 1809 | checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" 1810 | dependencies = [ 1811 | "proc-macro2", 1812 | "quote", 1813 | "unicode-xid", 1814 | ] 1815 | 1816 | [[package]] 1817 | name = "syn-mid" 1818 | version = "0.5.0" 1819 | source = "registry+https://github.com/rust-lang/crates.io-index" 1820 | checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" 1821 | dependencies = [ 1822 | "proc-macro2", 1823 | "quote", 1824 | "syn", 1825 | ] 1826 | 1827 | [[package]] 1828 | name = "tempfile" 1829 | version = "3.1.0" 1830 | source = "registry+https://github.com/rust-lang/crates.io-index" 1831 | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 1832 | dependencies = [ 1833 | "cfg-if 0.1.10", 1834 | "libc", 1835 | "rand", 1836 | "redox_syscall", 1837 | "remove_dir_all", 1838 | "winapi 0.3.8", 1839 | ] 1840 | 1841 | [[package]] 1842 | name = "term_size" 1843 | version = "0.3.2" 1844 | source = "registry+https://github.com/rust-lang/crates.io-index" 1845 | checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" 1846 | dependencies = [ 1847 | "libc", 1848 | "winapi 0.3.8", 1849 | ] 1850 | 1851 | [[package]] 1852 | name = "termcolor" 1853 | version = "1.1.0" 1854 | source = "registry+https://github.com/rust-lang/crates.io-index" 1855 | checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 1856 | dependencies = [ 1857 | "winapi-util", 1858 | ] 1859 | 1860 | [[package]] 1861 | name = "textwrap" 1862 | version = "0.11.0" 1863 | source = "registry+https://github.com/rust-lang/crates.io-index" 1864 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 1865 | dependencies = [ 1866 | "unicode-width", 1867 | ] 1868 | 1869 | [[package]] 1870 | name = "thread_local" 1871 | version = "1.0.1" 1872 | source = "registry+https://github.com/rust-lang/crates.io-index" 1873 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 1874 | dependencies = [ 1875 | "lazy_static", 1876 | ] 1877 | 1878 | [[package]] 1879 | name = "time" 1880 | version = "0.1.43" 1881 | source = "registry+https://github.com/rust-lang/crates.io-index" 1882 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 1883 | dependencies = [ 1884 | "libc", 1885 | "winapi 0.3.8", 1886 | ] 1887 | 1888 | [[package]] 1889 | name = "time" 1890 | version = "0.2.16" 1891 | source = "registry+https://github.com/rust-lang/crates.io-index" 1892 | checksum = "3a51cadc5b1eec673a685ff7c33192ff7b7603d0b75446fb354939ee615acb15" 1893 | dependencies = [ 1894 | "cfg-if 0.1.10", 1895 | "libc", 1896 | "standback", 1897 | "stdweb", 1898 | "time-macros", 1899 | "version_check", 1900 | "winapi 0.3.8", 1901 | ] 1902 | 1903 | [[package]] 1904 | name = "time-macros" 1905 | version = "0.1.0" 1906 | source = "registry+https://github.com/rust-lang/crates.io-index" 1907 | checksum = "9ae9b6e9f095bc105e183e3cd493d72579be3181ad4004fceb01adbe9eecab2d" 1908 | dependencies = [ 1909 | "proc-macro-hack", 1910 | "time-macros-impl", 1911 | ] 1912 | 1913 | [[package]] 1914 | name = "time-macros-impl" 1915 | version = "0.1.1" 1916 | source = "registry+https://github.com/rust-lang/crates.io-index" 1917 | checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" 1918 | dependencies = [ 1919 | "proc-macro-hack", 1920 | "proc-macro2", 1921 | "quote", 1922 | "standback", 1923 | "syn", 1924 | ] 1925 | 1926 | [[package]] 1927 | name = "tokio" 1928 | version = "0.1.22" 1929 | source = "registry+https://github.com/rust-lang/crates.io-index" 1930 | checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" 1931 | dependencies = [ 1932 | "bytes 0.4.12", 1933 | "futures 0.1.29", 1934 | "mio", 1935 | "num_cpus", 1936 | "tokio-codec", 1937 | "tokio-current-thread", 1938 | "tokio-executor", 1939 | "tokio-fs", 1940 | "tokio-io", 1941 | "tokio-reactor", 1942 | "tokio-sync", 1943 | "tokio-tcp", 1944 | "tokio-threadpool", 1945 | "tokio-timer", 1946 | "tokio-udp", 1947 | "tokio-uds", 1948 | ] 1949 | 1950 | [[package]] 1951 | name = "tokio" 1952 | version = "0.2.21" 1953 | source = "registry+https://github.com/rust-lang/crates.io-index" 1954 | checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58" 1955 | dependencies = [ 1956 | "bytes 0.5.4", 1957 | "fnv", 1958 | "futures-core", 1959 | "iovec", 1960 | "lazy_static", 1961 | "libc", 1962 | "memchr", 1963 | "mio", 1964 | "mio-named-pipes", 1965 | "mio-uds", 1966 | "pin-project-lite", 1967 | "signal-hook-registry", 1968 | "slab", 1969 | "tokio-macros", 1970 | "winapi 0.3.8", 1971 | ] 1972 | 1973 | [[package]] 1974 | name = "tokio-codec" 1975 | version = "0.1.2" 1976 | source = "registry+https://github.com/rust-lang/crates.io-index" 1977 | checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" 1978 | dependencies = [ 1979 | "bytes 0.4.12", 1980 | "futures 0.1.29", 1981 | "tokio-io", 1982 | ] 1983 | 1984 | [[package]] 1985 | name = "tokio-core" 1986 | version = "0.1.17" 1987 | source = "registry+https://github.com/rust-lang/crates.io-index" 1988 | checksum = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" 1989 | dependencies = [ 1990 | "bytes 0.4.12", 1991 | "futures 0.1.29", 1992 | "iovec", 1993 | "log", 1994 | "mio", 1995 | "scoped-tls", 1996 | "tokio 0.1.22", 1997 | "tokio-executor", 1998 | "tokio-io", 1999 | "tokio-reactor", 2000 | "tokio-timer", 2001 | ] 2002 | 2003 | [[package]] 2004 | name = "tokio-current-thread" 2005 | version = "0.1.7" 2006 | source = "registry+https://github.com/rust-lang/crates.io-index" 2007 | checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" 2008 | dependencies = [ 2009 | "futures 0.1.29", 2010 | "tokio-executor", 2011 | ] 2012 | 2013 | [[package]] 2014 | name = "tokio-executor" 2015 | version = "0.1.10" 2016 | source = "registry+https://github.com/rust-lang/crates.io-index" 2017 | checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" 2018 | dependencies = [ 2019 | "crossbeam-utils 0.7.2", 2020 | "futures 0.1.29", 2021 | ] 2022 | 2023 | [[package]] 2024 | name = "tokio-fs" 2025 | version = "0.1.7" 2026 | source = "registry+https://github.com/rust-lang/crates.io-index" 2027 | checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" 2028 | dependencies = [ 2029 | "futures 0.1.29", 2030 | "tokio-io", 2031 | "tokio-threadpool", 2032 | ] 2033 | 2034 | [[package]] 2035 | name = "tokio-io" 2036 | version = "0.1.13" 2037 | source = "registry+https://github.com/rust-lang/crates.io-index" 2038 | checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" 2039 | dependencies = [ 2040 | "bytes 0.4.12", 2041 | "futures 0.1.29", 2042 | "log", 2043 | ] 2044 | 2045 | [[package]] 2046 | name = "tokio-macros" 2047 | version = "0.2.5" 2048 | source = "registry+https://github.com/rust-lang/crates.io-index" 2049 | checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" 2050 | dependencies = [ 2051 | "proc-macro2", 2052 | "quote", 2053 | "syn", 2054 | ] 2055 | 2056 | [[package]] 2057 | name = "tokio-reactor" 2058 | version = "0.1.12" 2059 | source = "registry+https://github.com/rust-lang/crates.io-index" 2060 | checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" 2061 | dependencies = [ 2062 | "crossbeam-utils 0.7.2", 2063 | "futures 0.1.29", 2064 | "lazy_static", 2065 | "log", 2066 | "mio", 2067 | "num_cpus", 2068 | "parking_lot", 2069 | "slab", 2070 | "tokio-executor", 2071 | "tokio-io", 2072 | "tokio-sync", 2073 | ] 2074 | 2075 | [[package]] 2076 | name = "tokio-sync" 2077 | version = "0.1.8" 2078 | source = "registry+https://github.com/rust-lang/crates.io-index" 2079 | checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" 2080 | dependencies = [ 2081 | "fnv", 2082 | "futures 0.1.29", 2083 | ] 2084 | 2085 | [[package]] 2086 | name = "tokio-tcp" 2087 | version = "0.1.4" 2088 | source = "registry+https://github.com/rust-lang/crates.io-index" 2089 | checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" 2090 | dependencies = [ 2091 | "bytes 0.4.12", 2092 | "futures 0.1.29", 2093 | "iovec", 2094 | "mio", 2095 | "tokio-io", 2096 | "tokio-reactor", 2097 | ] 2098 | 2099 | [[package]] 2100 | name = "tokio-threadpool" 2101 | version = "0.1.18" 2102 | source = "registry+https://github.com/rust-lang/crates.io-index" 2103 | checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" 2104 | dependencies = [ 2105 | "crossbeam-deque", 2106 | "crossbeam-queue", 2107 | "crossbeam-utils 0.7.2", 2108 | "futures 0.1.29", 2109 | "lazy_static", 2110 | "log", 2111 | "num_cpus", 2112 | "slab", 2113 | "tokio-executor", 2114 | ] 2115 | 2116 | [[package]] 2117 | name = "tokio-timer" 2118 | version = "0.2.13" 2119 | source = "registry+https://github.com/rust-lang/crates.io-index" 2120 | checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" 2121 | dependencies = [ 2122 | "crossbeam-utils 0.7.2", 2123 | "futures 0.1.29", 2124 | "slab", 2125 | "tokio-executor", 2126 | ] 2127 | 2128 | [[package]] 2129 | name = "tokio-tls" 2130 | version = "0.3.1" 2131 | source = "registry+https://github.com/rust-lang/crates.io-index" 2132 | checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" 2133 | dependencies = [ 2134 | "native-tls", 2135 | "tokio 0.2.21", 2136 | ] 2137 | 2138 | [[package]] 2139 | name = "tokio-udp" 2140 | version = "0.1.6" 2141 | source = "registry+https://github.com/rust-lang/crates.io-index" 2142 | checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" 2143 | dependencies = [ 2144 | "bytes 0.4.12", 2145 | "futures 0.1.29", 2146 | "log", 2147 | "mio", 2148 | "tokio-codec", 2149 | "tokio-io", 2150 | "tokio-reactor", 2151 | ] 2152 | 2153 | [[package]] 2154 | name = "tokio-uds" 2155 | version = "0.2.6" 2156 | source = "registry+https://github.com/rust-lang/crates.io-index" 2157 | checksum = "5076db410d6fdc6523df7595447629099a1fdc47b3d9f896220780fa48faf798" 2158 | dependencies = [ 2159 | "bytes 0.4.12", 2160 | "futures 0.1.29", 2161 | "iovec", 2162 | "libc", 2163 | "log", 2164 | "mio", 2165 | "mio-uds", 2166 | "tokio-codec", 2167 | "tokio-io", 2168 | "tokio-reactor", 2169 | ] 2170 | 2171 | [[package]] 2172 | name = "tokio-util" 2173 | version = "0.3.1" 2174 | source = "registry+https://github.com/rust-lang/crates.io-index" 2175 | checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" 2176 | dependencies = [ 2177 | "bytes 0.5.4", 2178 | "futures-core", 2179 | "futures-sink", 2180 | "log", 2181 | "pin-project-lite", 2182 | "tokio 0.2.21", 2183 | ] 2184 | 2185 | [[package]] 2186 | name = "toml" 2187 | version = "0.5.6" 2188 | source = "registry+https://github.com/rust-lang/crates.io-index" 2189 | checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" 2190 | dependencies = [ 2191 | "serde", 2192 | ] 2193 | 2194 | [[package]] 2195 | name = "tower-service" 2196 | version = "0.3.0" 2197 | source = "registry+https://github.com/rust-lang/crates.io-index" 2198 | checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" 2199 | 2200 | [[package]] 2201 | name = "try-lock" 2202 | version = "0.2.2" 2203 | source = "registry+https://github.com/rust-lang/crates.io-index" 2204 | checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" 2205 | 2206 | [[package]] 2207 | name = "typenum" 2208 | version = "1.12.0" 2209 | source = "registry+https://github.com/rust-lang/crates.io-index" 2210 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 2211 | 2212 | [[package]] 2213 | name = "unicase" 2214 | version = "2.6.0" 2215 | source = "registry+https://github.com/rust-lang/crates.io-index" 2216 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 2217 | dependencies = [ 2218 | "version_check", 2219 | ] 2220 | 2221 | [[package]] 2222 | name = "unicode-bidi" 2223 | version = "0.3.4" 2224 | source = "registry+https://github.com/rust-lang/crates.io-index" 2225 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 2226 | dependencies = [ 2227 | "matches", 2228 | ] 2229 | 2230 | [[package]] 2231 | name = "unicode-normalization" 2232 | version = "0.1.12" 2233 | source = "registry+https://github.com/rust-lang/crates.io-index" 2234 | checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" 2235 | dependencies = [ 2236 | "smallvec 1.4.0", 2237 | ] 2238 | 2239 | [[package]] 2240 | name = "unicode-segmentation" 2241 | version = "1.6.0" 2242 | source = "registry+https://github.com/rust-lang/crates.io-index" 2243 | checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" 2244 | 2245 | [[package]] 2246 | name = "unicode-width" 2247 | version = "0.1.7" 2248 | source = "registry+https://github.com/rust-lang/crates.io-index" 2249 | checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" 2250 | 2251 | [[package]] 2252 | name = "unicode-xid" 2253 | version = "0.2.0" 2254 | source = "registry+https://github.com/rust-lang/crates.io-index" 2255 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 2256 | 2257 | [[package]] 2258 | name = "url" 2259 | version = "2.1.1" 2260 | source = "registry+https://github.com/rust-lang/crates.io-index" 2261 | checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" 2262 | dependencies = [ 2263 | "idna", 2264 | "matches", 2265 | "percent-encoding", 2266 | ] 2267 | 2268 | [[package]] 2269 | name = "utf8parse" 2270 | version = "0.2.0" 2271 | source = "registry+https://github.com/rust-lang/crates.io-index" 2272 | checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" 2273 | 2274 | [[package]] 2275 | name = "vcpkg" 2276 | version = "0.2.10" 2277 | source = "registry+https://github.com/rust-lang/crates.io-index" 2278 | checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" 2279 | 2280 | [[package]] 2281 | name = "vec_map" 2282 | version = "0.8.2" 2283 | source = "registry+https://github.com/rust-lang/crates.io-index" 2284 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 2285 | 2286 | [[package]] 2287 | name = "version_check" 2288 | version = "0.9.2" 2289 | source = "registry+https://github.com/rust-lang/crates.io-index" 2290 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 2291 | 2292 | [[package]] 2293 | name = "vte" 2294 | version = "0.8.0" 2295 | source = "registry+https://github.com/rust-lang/crates.io-index" 2296 | checksum = "96cc8a191608603611e78c6ec11dafef37e3cca0775aeef1931824753e81711d" 2297 | dependencies = [ 2298 | "arrayvec", 2299 | "utf8parse", 2300 | "vte_generate_state_changes", 2301 | ] 2302 | 2303 | [[package]] 2304 | name = "vte_generate_state_changes" 2305 | version = "0.1.1" 2306 | source = "registry+https://github.com/rust-lang/crates.io-index" 2307 | checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" 2308 | dependencies = [ 2309 | "proc-macro2", 2310 | "quote", 2311 | ] 2312 | 2313 | [[package]] 2314 | name = "want" 2315 | version = "0.3.0" 2316 | source = "registry+https://github.com/rust-lang/crates.io-index" 2317 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 2318 | dependencies = [ 2319 | "log", 2320 | "try-lock", 2321 | ] 2322 | 2323 | [[package]] 2324 | name = "wasi" 2325 | version = "0.9.0+wasi-snapshot-preview1" 2326 | source = "registry+https://github.com/rust-lang/crates.io-index" 2327 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 2328 | 2329 | [[package]] 2330 | name = "wasi" 2331 | version = "0.10.2+wasi-snapshot-preview1" 2332 | source = "registry+https://github.com/rust-lang/crates.io-index" 2333 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 2334 | 2335 | [[package]] 2336 | name = "wasm-bindgen" 2337 | version = "0.2.63" 2338 | source = "registry+https://github.com/rust-lang/crates.io-index" 2339 | checksum = "4c2dc4aa152834bc334f506c1a06b866416a8b6697d5c9f75b9a689c8486def0" 2340 | dependencies = [ 2341 | "cfg-if 0.1.10", 2342 | "wasm-bindgen-macro", 2343 | ] 2344 | 2345 | [[package]] 2346 | name = "wasm-bindgen-backend" 2347 | version = "0.2.63" 2348 | source = "registry+https://github.com/rust-lang/crates.io-index" 2349 | checksum = "ded84f06e0ed21499f6184df0e0cb3494727b0c5da89534e0fcc55c51d812101" 2350 | dependencies = [ 2351 | "bumpalo", 2352 | "lazy_static", 2353 | "log", 2354 | "proc-macro2", 2355 | "quote", 2356 | "syn", 2357 | "wasm-bindgen-shared", 2358 | ] 2359 | 2360 | [[package]] 2361 | name = "wasm-bindgen-macro" 2362 | version = "0.2.63" 2363 | source = "registry+https://github.com/rust-lang/crates.io-index" 2364 | checksum = "838e423688dac18d73e31edce74ddfac468e37b1506ad163ffaf0a46f703ffe3" 2365 | dependencies = [ 2366 | "quote", 2367 | "wasm-bindgen-macro-support", 2368 | ] 2369 | 2370 | [[package]] 2371 | name = "wasm-bindgen-macro-support" 2372 | version = "0.2.63" 2373 | source = "registry+https://github.com/rust-lang/crates.io-index" 2374 | checksum = "3156052d8ec77142051a533cdd686cba889537b213f948cd1d20869926e68e92" 2375 | dependencies = [ 2376 | "proc-macro2", 2377 | "quote", 2378 | "syn", 2379 | "wasm-bindgen-backend", 2380 | "wasm-bindgen-shared", 2381 | ] 2382 | 2383 | [[package]] 2384 | name = "wasm-bindgen-shared" 2385 | version = "0.2.63" 2386 | source = "registry+https://github.com/rust-lang/crates.io-index" 2387 | checksum = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd" 2388 | 2389 | [[package]] 2390 | name = "wasmer_enumset" 2391 | version = "1.0.1" 2392 | source = "registry+https://github.com/rust-lang/crates.io-index" 2393 | checksum = "cf088cc1f7d247fd96dff0df46fb1bbb747d8a69ae1ecd71aed55c55e354b2d8" 2394 | dependencies = [ 2395 | "num-traits", 2396 | "wasmer_enumset_derive", 2397 | ] 2398 | 2399 | [[package]] 2400 | name = "wasmer_enumset_derive" 2401 | version = "0.5.0" 2402 | source = "registry+https://github.com/rust-lang/crates.io-index" 2403 | checksum = "a8d1b32d98e11194200baf6d3f85eb2d6cfe56f6d9af0dd617f90ca48f958a88" 2404 | dependencies = [ 2405 | "darling", 2406 | "proc-macro2", 2407 | "quote", 2408 | "syn", 2409 | ] 2410 | 2411 | [[package]] 2412 | name = "winapi" 2413 | version = "0.2.8" 2414 | source = "registry+https://github.com/rust-lang/crates.io-index" 2415 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 2416 | 2417 | [[package]] 2418 | name = "winapi" 2419 | version = "0.3.8" 2420 | source = "registry+https://github.com/rust-lang/crates.io-index" 2421 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 2422 | dependencies = [ 2423 | "winapi-i686-pc-windows-gnu", 2424 | "winapi-x86_64-pc-windows-gnu", 2425 | ] 2426 | 2427 | [[package]] 2428 | name = "winapi-build" 2429 | version = "0.1.1" 2430 | source = "registry+https://github.com/rust-lang/crates.io-index" 2431 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 2432 | 2433 | [[package]] 2434 | name = "winapi-i686-pc-windows-gnu" 2435 | version = "0.4.0" 2436 | source = "registry+https://github.com/rust-lang/crates.io-index" 2437 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2438 | 2439 | [[package]] 2440 | name = "winapi-util" 2441 | version = "0.1.5" 2442 | source = "registry+https://github.com/rust-lang/crates.io-index" 2443 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 2444 | dependencies = [ 2445 | "winapi 0.3.8", 2446 | ] 2447 | 2448 | [[package]] 2449 | name = "winapi-x86_64-pc-windows-gnu" 2450 | version = "0.4.0" 2451 | source = "registry+https://github.com/rust-lang/crates.io-index" 2452 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2453 | 2454 | [[package]] 2455 | name = "ws2_32-sys" 2456 | version = "0.2.1" 2457 | source = "registry+https://github.com/rust-lang/crates.io-index" 2458 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 2459 | dependencies = [ 2460 | "winapi 0.2.8", 2461 | "winapi-build", 2462 | ] 2463 | 2464 | [[package]] 2465 | name = "xi-unicode" 2466 | version = "0.3.0" 2467 | source = "registry+https://github.com/rust-lang/crates.io-index" 2468 | checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" 2469 | 2470 | [[package]] 2471 | name = "xml-rs" 2472 | version = "0.8.3" 2473 | source = "registry+https://github.com/rust-lang/crates.io-index" 2474 | checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" 2475 | 2476 | [[package]] 2477 | name = "zeroize" 2478 | version = "1.1.0" 2479 | source = "registry+https://github.com/rust-lang/crates.io-index" 2480 | checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" 2481 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cloudman-rs" 3 | version = "0.1.7" 4 | authors = ["Remco Verhoef "] 5 | edition = "2018" 6 | build = "build.rs" 7 | readme = "README.md" 8 | 9 | [build-dependencies] 10 | built = "0.4" 11 | 12 | [dependencies] 13 | cursive = { version = "0.16", features = ["toml", "markdown"] } 14 | caseless = "*" 15 | dirs = "*" 16 | rand = "*" 17 | tokio = "0.2" 18 | tokio-core = "*" 19 | enum-map = "*" 20 | hashbrown = "*" 21 | base64 = "*" 22 | vte = "*" 23 | clap = "3.0.0-beta.1" 24 | 25 | rusoto_core = { version = "0.45", default_features = false, features = ["native-tls"] } 26 | rusoto_ec2 = { version = "0.45", default_features = false, features = ["native-tls"] } 27 | rusoto_logs = { version = "0.45", default_features = false, features = ["native-tls"] } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2018 DutchCoders [https://github.com/dutchcoders/] 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CARGO = cargo 2 | CARGO_ARGS = 3 | 4 | ROOTDIR = $(abspath $(dir $(firstword $(MAKEFILE_LIST)))) 5 | WORKDIR = ${ROOTDIR}/.build 6 | 7 | DATE = $(shell date +'%Y%m%d%H%M%S') 8 | 9 | build-darwin: 10 | $(CARGO) $(CARGO_ARGS) build --target=x86_64-apple-darwin --release 11 | cp ./target/x86_64-apple-darwin/release/cloudman $(WORKDIR)/cloudman-darwin-x86_64 12 | 13 | build-linux: 14 | $(CARGO) $(CARGO_ARGS) build --target=x86_64-unknown-linux-gnu --release 15 | cp ./target/x86_64-unknown-linux-gnu/release/cloudman $(WORKDIR)/cloudman-linux-x86_64 16 | 17 | fmt: 18 | $(CARGO) fmt 19 | 20 | run: 21 | $(CARGO) $(CARGO_ARGS) run 22 | 23 | .PHONY: build run 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cloudman 2 | ---------- 3 | Cloudman is a textual user interface (heavily inspired by htop) to manage your Amazon EC2 fleet instantly. By using Cloudman you'll find an overview of your instances, navigate through regions, retrieve instance details, show console outputs and connect to instance terminal using SSM. 4 | 5 | The profiles and defaults as configured in ~/.aws/credentials will be used. 6 | 7 | [![Build status](https://api.travis-ci.org/dutchcoders/cloudman.svg?branch=master&status=passed)](https://travis-ci.org/github/dutchcoders/cloudman) 8 | [![Crates.io](https://img.shields.io/crates/v/cloudman.svg)](https://crates.io/crates/cloudman) 9 | [![Packaging status](https://repology.org/badge/tiny-repos/cloudman.svg)](https://repology.org/project/cloudman/badges) 10 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) 11 | 12 | ### Screenshots 13 |
14 | 15 | 16 | 17 | 18 |
19 | 20 | ## Usage 21 | Cloudman can be started optionally with a region and profile to use. 22 | 23 | ``` 24 | cloudman-rs 0.1.7 25 | Remco Verhoef 26 | 27 | USAGE: 28 | cloudman [FLAGS] [OPTIONS] 29 | 30 | FLAGS: 31 | --disable-dry-run Disable dry run 32 | -h, --help Prints help information 33 | --use-env Usen environment credentials 34 | -V, --version Prints version information 35 | 36 | OPTIONS: 37 | -p, --profile ... One or more profiles to use 38 | -r, --region ... One or more regions to use 39 | ``` 40 | 41 | ## Shortcuts 42 | 43 | | Shortcut | Description | 44 | | ------------- | ------------- | 45 | | F1 | display help | 46 | | F2 | connect using [ssm](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/session-manager.html) to instance | 47 | | F3 | search through displayed instances | 48 | | F4 | filter displayed instances | 49 | | F5 | refresh displayed instances | 50 | | F6 | show actions for instances | 51 | | F7 | switch region | 52 | | L | display console output for instance | 53 | | ESC | close window | 54 | | Q | quit | 55 | 56 | ## Installation 57 | 58 | ### Homebrew 59 | If you're a **macOS Homebrew** or a **Linuxbrew** user, then you can install 60 | cloudman from homebrew tap: 61 | 62 | ```bash 63 | brew install dutchcoders/cloudman/cloudman 64 | ``` 65 | 66 | ### MacPorts 67 | If you're a **MacPorts** user, you can install Cloudman using: 68 | 69 | ```bash 70 | sudo port selfupdate 71 | sudo port install cloudman 72 | ``` 73 | 74 | ## Building 75 | cloudman is written in Rust, so you'll need to grab a 76 | [Rust installation](https://www.rust-lang.org/) in order to compile it. 77 | cloudman compiles with Rust 1.44.0 (stable) or newer. In general, cloudman tracks 78 | the latest stable release of the Rust compiler. 79 | 80 | To build cloudman: 81 | 82 | ``` 83 | $ git clone https://github.com/dutchcoders/cloudman 84 | $ cd cloudman 85 | $ cargo build --release 86 | $ ./target/release/cloudman --version 87 | 0.1.0 88 | ``` 89 | 90 | ## Current features 91 | * overview of all instances 92 | * support different profiles 93 | * switch easily between aws regions 94 | * connect using SSM to instance (using tmux) 95 | * search through instances 96 | * filter instances 97 | * show detailed information for instances 98 | * show console output if supported 99 | 100 | # Roadmap 101 | * start and stop instances (actions) 102 | * request spot instances 103 | * modifyable columns 104 | * sorting 105 | * show filter active 106 | * show indicator of loading 107 | * search through console output 108 | * much more 109 | 110 | ## Contribute 111 | 112 | + I :heart: pull requests and bug reports 113 | + Don't hesitate to [tell me my rust skills suck](https://github.com/dutchcoders/cloudman/issues/new), but please tell me why. 114 | 115 | ## Thanks 116 | 117 | Special thanks to: 118 | 119 | * [Doom Emacs](https://github.com/hlissner/doom-emacs) for making the Emacs framework I love. 120 | * [Cursive](https://github.com/gyscos/cursive/) for making the textual user interface Cloudman is built upon. 121 | * [htop](https://github.com/hishamhm/htop) for the inspirational interface. 122 | 123 | Everyone else that inspired me. 124 | 125 | ## Creator 126 | 127 | **Remco Verhoef** 128 | 129 | - 130 | - 131 | 132 | 133 | ## Copyright and license 134 | 135 | Code and documentation copyright 2011-2020 Remco Verhoef. 136 | 137 | Code released under [the MIT license](LICENSE). 138 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | built::write_built_file().expect("Failed to acquire build-time information"); 3 | } 4 | -------------------------------------------------------------------------------- /ci/before_deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # All files which should be added only if they changed 4 | aux_files=() 5 | 6 | # Output binary name 7 | NAME="cloudman-$TRAVIS_TAG-$TARGET" 8 | 9 | # Everything in this directory will be offered as download for the release 10 | mkdir "./target/deploy" 11 | 12 | mkdir $NAME 13 | cp target/$TARGET/release/cloudman $NAME/ 14 | cp README.md LICENSE $NAME/ 15 | tar czvf $NAME.tar.gz $NAME 16 | -------------------------------------------------------------------------------- /ci/install.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dutchcoders/cloudman/b63a71a342b6d7d34dab734cd859b0304d11760d/ci/install.sh -------------------------------------------------------------------------------- /ci/script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run clippy checks 4 | if [ "$CLIPPY" == "true" ]; then 5 | rustup component add clippy 6 | cargo clippy --all-targets 7 | exit 8 | fi 9 | 10 | # Run clippy rustfmt 11 | if [ "$RUSTFMT" == "true" ]; then 12 | cargo fmt -- --check 13 | exit 14 | fi 15 | 16 | # Run test in release mode if a tag is present, to produce an optimized binary 17 | if [ -n "$TRAVIS_TAG" ]; then 18 | # Build separately so we generate an 'cloudman' binary without -HASH appended 19 | cargo build --release --target $TARGET 20 | cargo test --release 21 | else 22 | cargo test 23 | fi 24 | -------------------------------------------------------------------------------- /screenshots/change_region.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dutchcoders/cloudman/b63a71a342b6d7d34dab734cd859b0304d11760d/screenshots/change_region.png -------------------------------------------------------------------------------- /screenshots/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dutchcoders/cloudman/b63a71a342b6d7d34dab734cd859b0304d11760d/screenshots/console.png -------------------------------------------------------------------------------- /screenshots/details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dutchcoders/cloudman/b63a71a342b6d7d34dab734cd859b0304d11760d/screenshots/details.png -------------------------------------------------------------------------------- /screenshots/instances.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dutchcoders/cloudman/b63a71a342b6d7d34dab734cd859b0304d11760d/screenshots/instances.png -------------------------------------------------------------------------------- /src/bin/cloudman.rs: -------------------------------------------------------------------------------- 1 | extern crate cursive; 2 | extern crate dirs; 3 | extern crate rusoto_core; 4 | extern crate rusoto_ec2; 5 | extern crate tokio; 6 | 7 | use clap::Clap; 8 | use cursive::align::HAlign; 9 | use cursive::direction::Orientation; 10 | use cursive::event::{Event, EventResult, Key}; 11 | use cursive::theme::{BaseColor, Color, ColorStyle, PaletteColor}; 12 | use cursive::traits::*; 13 | use cursive::view::View; 14 | use cursive::view::*; 15 | use cursive::views::{ 16 | Canvas, Dialog, EditView, LinearLayout, OnEventView, ResizedView, SelectView, TextContent, 17 | TextView, 18 | }; 19 | use cursive::Cursive; 20 | use cursive::CursiveExt; 21 | use rusoto_core::credential::{ProfileProvider,EnvironmentProvider}; 22 | use rusoto_core::request::HttpClient; 23 | use rusoto_core::Region; 24 | use rusoto_core::Region::*; 25 | use rusoto_ec2::{ 26 | DescribeInstancesRequest, Ec2, Ec2Client, RebootInstancesRequest, StartInstancesRequest, 27 | TerminateInstancesRequest, 28 | StopInstancesRequest, Tag, 29 | }; 30 | use std::cmp::Ordering; 31 | use std::env; 32 | use std::error::Error; 33 | use std::hash::Hash; 34 | use std::panic; 35 | use std::process::Command; 36 | use std::str::FromStr; 37 | 38 | use cloudman_rs::views::{ 39 | BottomBarType, BottomBarView, Foo, Header, InstancesView, KeyCodeView, LogView, TableViewItem, 40 | }; 41 | 42 | // Use of a mod or pub mod is not actually necessary. 43 | pub mod built_info { 44 | // The file has been placed there by the build script. 45 | include!(concat!(env!("OUT_DIR"), "/built.rs")); 46 | } 47 | 48 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] 49 | enum BasicColumn { 50 | InstanceID, 51 | Name, 52 | Architecture, 53 | Region, 54 | Profile, 55 | VpcID, 56 | Type, 57 | Key, 58 | State, 59 | PublicIp, 60 | PrivateIp, 61 | } 62 | 63 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] 64 | enum Actions { 65 | Start, 66 | Stop, 67 | Terminate, 68 | Reboot, 69 | Connect, 70 | SerialConnect, 71 | } 72 | 73 | #[derive(Clone, PartialEq)] 74 | pub struct Instance { 75 | instance: rusoto_ec2::Instance, 76 | profile: String, 77 | region: Region, 78 | } 79 | fn find_tag(key: String, tags: Option>) -> Option { 80 | match tags { 81 | Some(tags) => { 82 | match tags 83 | .iter() 84 | .find(|t| t.key.clone().unwrap().eq_ignore_ascii_case(&key)) 85 | { 86 | Some(tag) => tag.clone().value, 87 | None => None, 88 | } 89 | } 90 | None => None, 91 | } 92 | } 93 | 94 | impl TableViewItem for Instance { 95 | fn to_column_color(&self, column: BasicColumn) -> ColorStyle { 96 | match column { 97 | BasicColumn::Name => { 98 | ColorStyle::new(Color::Dark(BaseColor::Green), Color::TerminalDefault) 99 | } 100 | BasicColumn::State => match self.instance.state.as_ref().unwrap().code { 101 | Some(16) => ColorStyle::new(Color::Light(BaseColor::Green), Color::TerminalDefault), 102 | _ => ColorStyle::new(Color::Light(BaseColor::Red), Color::TerminalDefault), 103 | }, 104 | _ => ColorStyle::primary(), 105 | } 106 | } 107 | 108 | fn to_column(&self, column: BasicColumn) -> String { 109 | match column { 110 | BasicColumn::InstanceID => self 111 | .instance 112 | .instance_id 113 | .clone() 114 | .unwrap_or_else(|| "".to_string()), 115 | BasicColumn::Name => find_tag("name".to_string(), self.instance.tags.clone()) 116 | .unwrap_or_else(|| "".to_string()), 117 | BasicColumn::Region => self.region.clone().name().to_string(), 118 | BasicColumn::Profile => self.profile.to_string(), 119 | BasicColumn::Architecture => self 120 | .instance 121 | .architecture 122 | .clone() 123 | .unwrap_or_else(|| "".to_string()), 124 | BasicColumn::VpcID => self 125 | .instance 126 | .vpc_id 127 | .clone() 128 | .unwrap_or_else(|| "".to_string()), 129 | BasicColumn::Type => self 130 | .instance 131 | .instance_type 132 | .clone() 133 | .unwrap_or_else(|| "".to_string()), 134 | BasicColumn::Key => self 135 | .instance 136 | .key_name 137 | .clone() 138 | .unwrap_or_else(|| "".to_string()), 139 | BasicColumn::State => self 140 | .instance 141 | .state 142 | .clone() 143 | .unwrap() 144 | .name 145 | .unwrap_or_else(|| "".to_string()), 146 | BasicColumn::PublicIp => self 147 | .instance 148 | .public_ip_address 149 | .clone() 150 | .unwrap_or_else(|| "".to_string()), 151 | BasicColumn::PrivateIp => self 152 | .instance 153 | .private_ip_address 154 | .clone() 155 | .unwrap_or_else(|| "".to_string()), 156 | } 157 | } 158 | 159 | fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering 160 | where 161 | Self: Sized, 162 | { 163 | match column { 164 | BasicColumn::Name => self.instance.instance_id.cmp(&other.instance.instance_id), 165 | BasicColumn::InstanceID => self.instance.instance_id.cmp(&other.instance.instance_id), 166 | BasicColumn::Region => self.region.name().cmp(&other.region.name()), 167 | BasicColumn::Profile => self.profile.cmp(&other.profile), 168 | BasicColumn::Architecture => { 169 | self.instance.architecture.cmp(&other.instance.architecture) 170 | } 171 | BasicColumn::VpcID => self.instance.vpc_id.cmp(&other.instance.vpc_id), 172 | BasicColumn::Type => self 173 | .instance 174 | .instance_type 175 | .cmp(&other.instance.instance_type), 176 | BasicColumn::Key => self.instance.key_name.cmp(&other.instance.key_name), 177 | BasicColumn::State => self.instance.key_name.cmp(&other.instance.key_name), 178 | BasicColumn::PublicIp => self 179 | .instance 180 | .public_ip_address 181 | .cmp(&other.instance.public_ip_address), 182 | BasicColumn::PrivateIp => self 183 | .instance 184 | .private_ip_address 185 | .cmp(&other.instance.private_ip_address), 186 | } 187 | } 188 | } 189 | 190 | // TODO: add parallel loading of multiple envs 191 | fn get_instances_with_region( 192 | profiles: Vec, 193 | regions: Vec, 194 | ) -> Result, Box> { 195 | let mut runtime = tokio::runtime::Runtime::new().unwrap(); 196 | 197 | let mut instances: Vec = vec![]; 198 | for profile in profiles.iter() { 199 | for region in regions.iter() { 200 | let client = new_ec2client(region, profile)?; 201 | 202 | let req: DescribeInstancesRequest = ec2_describe_input(); 203 | 204 | let ft = client.describe_instances(req); 205 | 206 | let response = runtime.block_on(ft)?; 207 | if let Some(reservations) = response.reservations { 208 | for reservation in reservations { 209 | if let Some(res_instances) = reservation.instances { 210 | for instance in res_instances { 211 | instances.push(Instance { 212 | instance, 213 | region: region.clone(), 214 | profile: profile.clone(), 215 | }); 216 | } 217 | } 218 | } 219 | } 220 | } 221 | } 222 | 223 | Ok(instances) 224 | } 225 | 226 | #[derive(Clap)] 227 | #[clap(version = built_info::PKG_VERSION, author = built_info::PKG_AUTHORS)] 228 | struct Opts { 229 | #[clap(short, long, about("One or more regions to use"))] 230 | region: Vec, 231 | 232 | #[clap(short, long, about("One or more profiles to use"))] 233 | profile: Vec, 234 | 235 | #[clap(long, about("Disable dry run"))] 236 | disable_dry_run: bool, 237 | 238 | #[clap(long, about("Usen environment credentials"))] 239 | use_env: bool, 240 | } 241 | 242 | fn main() { 243 | match panic::catch_unwind(|| { 244 | run(); 245 | }) { 246 | Ok(_) => {} // we're ok 247 | Err(_) => run_bsod(), 248 | } 249 | } 250 | 251 | impl Header for BasicColumn { 252 | fn to_header_size(&self, w: usize) -> usize { 253 | match self { 254 | BasicColumn::InstanceID => 19, 255 | BasicColumn::Name => (40 * w) / 160, 256 | BasicColumn::Architecture => 8, 257 | BasicColumn::VpcID => 22, 258 | BasicColumn::Profile => 12, 259 | BasicColumn::Region => 12, 260 | BasicColumn::Type => 12, 261 | BasicColumn::Key => (20 * w) / 160, 262 | BasicColumn::State => 10, 263 | BasicColumn::PublicIp => 15, 264 | BasicColumn::PrivateIp => 15, 265 | } 266 | } 267 | 268 | fn to_header(&self) -> String { 269 | match self { 270 | BasicColumn::InstanceID => "instance-id".to_string(), 271 | BasicColumn::Name => "name".to_string(), 272 | BasicColumn::Architecture => "arch".to_string(), 273 | BasicColumn::VpcID => "vpc-id".to_string(), 274 | BasicColumn::Profile => "profile".to_string(), 275 | BasicColumn::Region => "region".to_string(), 276 | BasicColumn::Type => "type".to_string(), 277 | BasicColumn::Key => "key".to_string(), 278 | BasicColumn::State => "state".to_string(), 279 | BasicColumn::PublicIp => "public-ip".to_string(), 280 | BasicColumn::PrivateIp => "private-ip".to_string(), 281 | } 282 | } 283 | } 284 | 285 | fn cursive_new() -> Cursive { 286 | let mut siv = Cursive::default(); 287 | siv.set_theme(cursive::theme::load_toml(include_str!("../ncurses_theme.toml")).unwrap()); 288 | 289 | siv 290 | } 291 | 292 | fn run() { 293 | let opts: Opts = Opts::parse(); 294 | 295 | cursive::logger::init(); 296 | 297 | let regions: Vec = { 298 | if opts.region.is_empty() { 299 | let regions: Vec = [Region::default()].to_vec(); 300 | regions 301 | } else { 302 | opts.region 303 | .iter() 304 | .flat_map(|r| r.split(",")) 305 | .map(|r| Region::from_str(&r).unwrap()) 306 | .collect() 307 | } 308 | }; 309 | 310 | let profiles: Vec = { 311 | if opts.profile.is_empty() { 312 | let profiles: Vec = ["default".to_string()].to_vec(); 313 | profiles 314 | } else { 315 | opts.profile 316 | .iter() 317 | .flat_map(|r| r.split(',')) 318 | .map(|r| r.to_string()) 319 | .collect() 320 | } 321 | }; 322 | 323 | let instances = match get_instances_with_region(profiles.clone(), regions.clone()) { 324 | Ok(instances) => instances, 325 | Err(err) => { 326 | eprintln!("Could not retrieve instances\n\n{}", err); 327 | std::process::exit(1); 328 | } 329 | }; 330 | 331 | let mut siv = cursive_new(); 332 | 333 | let rv = ReturnValues{ 334 | profiles, 335 | regions: regions.clone(), 336 | instances: instances.clone(), 337 | dry_run: !opts.disable_dry_run, 338 | ..Default::default() 339 | }; 340 | 341 | siv.set_user_data::(rv); 342 | 343 | let mut layout = LinearLayout::new(Orientation::Vertical); 344 | 345 | let dialog_title = TextView::new(built_info::PKG_NAME) 346 | .h_align(HAlign::Center) 347 | .with_name("title"); 348 | 349 | layout.add_child(dialog_title); 350 | 351 | let mut iv = InstancesView::::scrollable(&instances) 352 | .column(BasicColumn::InstanceID) 353 | .column(BasicColumn::Name) 354 | .column(BasicColumn::Architecture) 355 | .column(BasicColumn::Region) 356 | .column(BasicColumn::Profile) 357 | .column(BasicColumn::VpcID) 358 | .column(BasicColumn::Type) 359 | .column(BasicColumn::Key) 360 | .column(BasicColumn::State) 361 | .column(BasicColumn::PublicIp) 362 | .column(BasicColumn::PrivateIp); 363 | 364 | iv.set_on_submit(|s: &mut Cursive, _instance: Option| { 365 | let table = s 366 | .find_name::>("instances") 367 | .unwrap(); 368 | 369 | if let Some(instance) = table.item() { 370 | instance_details(s, instance); 371 | } 372 | }); 373 | 374 | layout.add_child(iv.with_name("instances")); 375 | 376 | let bottom_bar = BottomBarView::new(&"".to_string(), regions.clone()).with_name("bottom_bar"); 377 | 378 | layout.add_child(bottom_bar); 379 | 380 | siv.add_fullscreen_layer( 381 | OnEventView::new(layout) 382 | .on_event('/', on_search) 383 | .on_event(Key::F3, on_search) 384 | .on_event(Key::F4, on_filter) 385 | .on_event(Event::CtrlChar('k'), |s| { 386 | let d = KeyCodeView::new(10).full_width().fixed_height(10); 387 | s.add_layer(d); 388 | }) 389 | .on_event('L', |s| { 390 | let table = s 391 | .find_name::>("instances") 392 | .unwrap(); 393 | 394 | if let Some(instance) = table.item() { 395 | instance_log(s, instance); 396 | } 397 | }) 398 | .on_event('l', |s| { 399 | let table = s 400 | .find_name::>("instances") 401 | .unwrap(); 402 | 403 | if let Some(instance) = table.item() { 404 | instance_log(s, instance); 405 | } 406 | }) 407 | .on_event(Key::Esc, |s| reset_filter(s)) 408 | .on_event(Key::F9, |s| change_profile(s)) 409 | .on_event(Key::F7, |s| change_region(s)) 410 | .on_event(Key::F6, |s| action(s)) 411 | .on_event(Key::F5, |s| refresh(s)) 412 | .on_event(Key::F1, |s| help(s)) 413 | .on_event(Key::F2, |s| { 414 | let table = s 415 | .find_name::>("instances") 416 | .unwrap(); 417 | 418 | if let Some(instance) = table.item() { 419 | if connect(instance).is_err() { 420 | let d = Dialog::around(TextView::new("Not running within tmux.")) 421 | .title("Error") 422 | .button("Cancel", |s| { 423 | s.pop_layer(); 424 | }); 425 | 426 | let dl = event_view(d); 427 | 428 | s.add_layer(dl); 429 | } 430 | } 431 | }) 432 | .on_event('q', |s| s.quit()), 433 | ); 434 | 435 | update_bottom_bar(&mut siv); 436 | 437 | siv.add_global_callback(Key::F10, |s| s.quit()); 438 | 439 | siv.add_global_callback('s', |s| s.toggle_debug_console()); 440 | 441 | siv.run() 442 | } 443 | 444 | struct ReturnValues { 445 | profiles: Vec, 446 | regions: Vec, 447 | search: String, 448 | search_found: bool, 449 | searching: bool, 450 | filter: String, 451 | filtering: bool, 452 | instances: Vec, 453 | dry_run: bool, 454 | } 455 | 456 | impl Default for ReturnValues { 457 | fn default() -> Self { 458 | Self { 459 | profiles: vec!["default".to_string()], 460 | regions: vec![Region::default()], 461 | search: "".to_string(), 462 | searching: false, 463 | search_found: false, 464 | filter: "".to_string(), 465 | filtering: false, 466 | instances: vec![], 467 | dry_run: false, 468 | } 469 | } 470 | } 471 | 472 | fn on_filter(s: &mut Cursive) { 473 | s.with_user_data(|v: &mut ReturnValues| { 474 | v.filtering = true; 475 | }); 476 | 477 | let ud = s.user_data::().unwrap(); 478 | 479 | let mut overlay = Foo::with_string(&ud.filter.to_string()); 480 | 481 | overlay.set_on_search(|s, ss, _| { 482 | s.with_user_data(|v: &mut ReturnValues| { 483 | v.filter = ss.to_string(); 484 | }); 485 | 486 | let ud = s.user_data::().unwrap(); 487 | 488 | let filtered_instances: Vec = ud 489 | .instances 490 | .clone() 491 | .into_iter() 492 | .filter(|i| { 493 | find_tag("Name".to_string(), i.instance.tags.clone()) 494 | .unwrap_or_default() 495 | .to_lowercase() 496 | .contains(&ss.to_lowercase()) 497 | || i.region.name().to_lowercase().contains(&ss.to_lowercase()) 498 | || i.profile.to_lowercase().contains(&ss.to_lowercase()) 499 | }) 500 | .collect(); 501 | 502 | let mut table = s 503 | .find_name::>("instances") 504 | .unwrap(); 505 | 506 | let item = table.item(); 507 | 508 | match item { 509 | Some(item) => { 510 | let item2 = item.clone(); 511 | table.set_instances(filtered_instances); 512 | table.set_item(&item2); 513 | } 514 | None => { 515 | table.set_instances(filtered_instances); 516 | } 517 | } 518 | 519 | update_bottom_bar(s); 520 | }); 521 | 522 | overlay.set_on_search_next(|_, _, _| {}); 523 | 524 | overlay.set_on_cancel(|s| { 525 | reset_filter(s); 526 | s.pop_layer(); 527 | }); 528 | 529 | overlay.set_on_close(|s| { 530 | s.with_user_data(|v: &mut ReturnValues| { 531 | v.filtering = false; 532 | }); 533 | 534 | update_bottom_bar(s); 535 | s.pop_layer(); 536 | }); 537 | 538 | s.add_fullscreen_layer(overlay); 539 | update_bottom_bar(s); 540 | } 541 | 542 | fn reset_filter(s: &mut Cursive) { 543 | s.with_user_data(|v: &mut ReturnValues| { 544 | v.filtering = false; 545 | v.filter = "".to_string(); 546 | }); 547 | 548 | let mut table = s 549 | .find_name::>("instances") 550 | .unwrap(); 551 | 552 | let ud = s.user_data::().unwrap(); 553 | 554 | let instances = ud.instances.clone(); 555 | 556 | let item = table.item(); 557 | 558 | match item { 559 | Some(item) => { 560 | let item2 = item.clone(); 561 | table.set_instances(instances); 562 | table.set_item(&item2); 563 | } 564 | None => { 565 | table.set_instances(instances); 566 | } 567 | } 568 | 569 | update_bottom_bar(s); 570 | } 571 | 572 | fn on_search(s: &mut Cursive) { 573 | s.with_user_data(|v: &mut ReturnValues| { 574 | v.search = String::new(); 575 | v.searching = true; 576 | }); 577 | 578 | let mut overlay = Foo::default(); 579 | overlay.set_on_search(|s, ss, _| { 580 | let mut table = s 581 | .find_name::>("instances") 582 | .unwrap(); 583 | 584 | let instances = table.items(); 585 | 586 | match instances.iter().position(|i| { 587 | find_tag("Name".to_string(), i.instance.tags.clone()) 588 | .unwrap_or_default() 589 | .to_lowercase() 590 | .contains(&ss.to_lowercase()) 591 | }) { 592 | Some(idx) => { 593 | table.set_selected_item(idx); 594 | s.with_user_data(|v: &mut ReturnValues| { 595 | v.search_found = true; 596 | v.search = ss.to_string(); 597 | }); 598 | } 599 | None => { 600 | s.with_user_data(|v: &mut ReturnValues| { 601 | v.search_found = false; 602 | v.search = ss.to_string(); 603 | }); 604 | } 605 | } 606 | 607 | update_bottom_bar(s); 608 | }); 609 | 610 | overlay.set_on_search_next(|s, ss, _| { 611 | let mut table = s 612 | .find_name::>("instances") 613 | .unwrap(); 614 | 615 | let instances = table.items(); 616 | 617 | let selected_row = table.selected_item().unwrap(); 618 | 619 | if let Some(idx) = instances.iter().skip(selected_row + 1).position(|i| { 620 | find_tag("Name".to_string(), i.instance.tags.clone()) 621 | .unwrap_or_default() 622 | .to_lowercase() 623 | .contains(&ss.to_lowercase()) 624 | }) { 625 | table.set_selected_item(idx + selected_row + 1); 626 | } else if let Some(idx) = instances.iter().position(|i| { 627 | find_tag("Name".to_string(), i.instance.tags.clone()) 628 | .unwrap_or_default() 629 | .to_lowercase() 630 | .contains(&ss.to_lowercase()) 631 | }) { 632 | table.set_selected_item(idx); 633 | } else { 634 | } 635 | }); 636 | 637 | overlay.set_on_cancel(|s| { 638 | s.with_user_data(|v: &mut ReturnValues| { 639 | v.searching = false; 640 | }); 641 | 642 | update_bottom_bar(s); 643 | s.pop_layer(); 644 | }); 645 | 646 | overlay.set_on_close(|s| { 647 | s.with_user_data(|v: &mut ReturnValues| { 648 | v.searching = false; 649 | }); 650 | 651 | update_bottom_bar(s); 652 | s.pop_layer(); 653 | }); 654 | 655 | s.add_fullscreen_layer(overlay); 656 | update_bottom_bar(s); 657 | } 658 | 659 | fn update_bottom_bar(s: &mut Cursive) { 660 | let mut bottom_bar = s.find_name::("bottom_bar").unwrap(); 661 | 662 | let ud = s.user_data::().unwrap(); 663 | 664 | if ud.searching { 665 | bottom_bar 666 | .set_content(&ud.search.clone()) 667 | .set_valid(ud.search_found) 668 | .set_region(ud.regions.clone()) 669 | .set_profile(ud.profiles.clone()) 670 | .set_type(BottomBarType::Search); 671 | } else if ud.filtering { 672 | bottom_bar 673 | .set_content(&ud.filter.clone()) 674 | .set_region(ud.regions.clone()) 675 | .set_profile(ud.profiles.clone()) 676 | .set_type(BottomBarType::Filter); 677 | } else { 678 | bottom_bar 679 | .set_region(ud.regions.clone()) 680 | .set_profile(ud.profiles.clone()) 681 | .set_type(BottomBarType::Standard); 682 | } 683 | } 684 | 685 | fn serial_connect(instance: &Instance) -> Result<(), Box> { 686 | env::var("TMUX")?; 687 | 688 | Command::new("tmux") 689 | .arg("split-window") 690 | .arg("-h") 691 | .arg("bash") 692 | .arg("-c") 693 | .arg(format!( 694 | "TEMP_KEY=$(mktemp -u) 695 | ssh-keygen -b 4096 -t rsa -f $TEMP_KEY -q -N \"\" && \ 696 | aws --no-cli-pager --no-cli-auto-prompt --output text ec2-instance-connect send-serial-console-ssh-public-key --profile {profile} --region {region} --instance-id {instance_id} --ssh-public-key \"file://$TEMP_KEY.pub\" --serial-port {port} > /dev/null && \ 697 | echo \"Key succesfully generated and send, connecting to terminal.\" && \ 698 | ssh -o IdentitiesOnly=yes -i \"$TEMP_KEY\" {instance_id}.port{port}@serial-console.ec2-instance-connect.{region}.aws; \ 699 | rm $TEMP_KEY; \ 700 | read -n 1 -s -r -p \"Press any key to continue\"", 701 | instance_id=instance.instance.instance_id.clone().unwrap(), 702 | profile=&instance.profile, 703 | port=0, 704 | region=instance.region.name(), 705 | )) 706 | .output()?; 707 | 708 | Ok(()) 709 | } 710 | 711 | fn connect(instance: &Instance) -> Result<(), Box> { 712 | env::var("TMUX")?; 713 | 714 | Command::new("tmux") 715 | .arg("split-window") 716 | .arg("-h") 717 | .arg("bash") 718 | .arg("-c") 719 | .arg(format!(r#"aws --no-cli-pager --no-cli-auto-prompt --output text ssm start-session --profile "{:?}" --region "{:?}" --target "{:}"; read -n 1 -s -r -p "Press any key to continue""#, &instance.profile, instance.region.name(), instance.instance.instance_id.clone().unwrap())) 720 | .output()?; 721 | 722 | Ok(()) 723 | } 724 | 725 | fn refresh(s: &mut Cursive) { 726 | let mut iv = s 727 | .find_name::>("instances") 728 | .unwrap(); 729 | 730 | let ud = s.user_data::().unwrap(); 731 | 732 | let item = iv.item(); 733 | 734 | match get_instances_with_region(ud.profiles.clone(), ud.regions.clone()) { 735 | Ok(instances) => { 736 | let instances: Vec = if !ud.filter.is_empty() { 737 | instances 738 | .into_iter() 739 | .filter(|i| { 740 | find_tag("name".to_string(), i.instance.tags.clone()) 741 | .unwrap_or_else(|| "".to_string()) 742 | .contains(&ud.filter) 743 | }) 744 | .collect() 745 | } else { 746 | instances 747 | }; 748 | 749 | 750 | match item { 751 | Some(item) => { 752 | let item2 = item.clone(); 753 | iv.set_instances(instances); 754 | iv.set_item(&item2); 755 | } 756 | None => { 757 | iv.set_instances(instances); 758 | } 759 | } 760 | } 761 | Err(err) => { 762 | let d = Dialog::around(TextView::new(format!( 763 | "Could not retrieve instances.\n\n{}", 764 | err 765 | ))) 766 | .title("Error") 767 | .button("Cancel", |s| { 768 | s.pop_layer(); 769 | }); 770 | 771 | let dl = event_view(d); 772 | 773 | s.add_layer(dl); 774 | } 775 | } 776 | } 777 | 778 | fn new_ec2client( 779 | region: &rusoto_core::Region, 780 | profile: &str, 781 | ) -> Result { 782 | let opts: Opts = Opts::parse(); 783 | 784 | let http_client = HttpClient::new()?; 785 | 786 | if opts.use_env { 787 | let provider = EnvironmentProvider::default(); 788 | Ok(Ec2Client::new_with(http_client, provider, region.clone())) 789 | } else { 790 | let aws_creds_dir: String = dirs::home_dir().unwrap().to_str().unwrap().to_owned() + "/.aws/credentials"; 791 | let provider = ProfileProvider::with_configuration(aws_creds_dir, profile); 792 | Ok(Ec2Client::new_with(http_client, provider, region.clone())) 793 | } 794 | } 795 | 796 | fn get_instance_log(instance: &Instance) -> Result, Box> { 797 | let client = new_ec2client(&instance.region, &instance.profile)?; 798 | 799 | let req = rusoto_ec2::GetConsoleOutputRequest { 800 | instance_id: instance.instance.instance_id.clone().unwrap(), 801 | ..Default::default() 802 | }; 803 | 804 | let mut runtime = tokio::runtime::Runtime::new().unwrap(); 805 | 806 | let ft = client.get_console_output(req); 807 | 808 | let response = runtime.block_on(ft)?; 809 | 810 | let output = response.output.unwrap(); 811 | 812 | let buf = &base64::decode(&output).unwrap()[..]; 813 | 814 | Ok(buf.to_vec()) 815 | } 816 | 817 | fn instance_log(siv: &mut Cursive, instance: &Instance) { 818 | match get_instance_log(&instance) { 819 | Ok(buf) => { 820 | let mut dl = LinearLayout::new(Orientation::Vertical); 821 | 822 | let instance = instance.clone(); 823 | 824 | let dialog_title = TextView::new(format!( 825 | "{} ({:})", 826 | built_info::PKG_NAME, 827 | instance.instance.instance_id.unwrap() 828 | )) 829 | .h_align(HAlign::Center) 830 | .with_name("title"); 831 | 832 | dl.add_child(dialog_title); 833 | 834 | dl.add_child(ResizedView::new( 835 | SizeConstraint::Full, 836 | SizeConstraint::Full, 837 | LogView::scrollable(&buf), 838 | )); 839 | 840 | let dl = event_view(dl); 841 | 842 | siv.add_fullscreen_layer(dl); 843 | } 844 | Err(err) => { 845 | let d = Dialog::around(TextView::new(format!( 846 | "Could not retrieve the instance log.\n\n{}", 847 | err 848 | ))) 849 | .title("Error") 850 | .button("Cancel", |s| { 851 | s.pop_layer(); 852 | }); 853 | 854 | let dl = event_view(d); 855 | 856 | siv.add_layer(dl); 857 | } 858 | } 859 | } 860 | 861 | fn event_view(v: V) -> OnEventView { 862 | OnEventView::new(v) 863 | .on_event(Key::Esc, |s| { 864 | s.pop_layer(); 865 | }) 866 | .on_event('q', |s| { 867 | s.pop_layer(); 868 | }) 869 | } 870 | 871 | fn instance_details(siv: &mut Cursive, instance: &Instance) { 872 | let mut dl = LinearLayout::new(Orientation::Vertical); 873 | 874 | let instance = instance.clone(); 875 | 876 | let dialog_title = TextView::new(format!( 877 | "{} ({:})", 878 | built_info::PKG_NAME, 879 | instance.instance.instance_id.clone().unwrap() 880 | )) 881 | .h_align(HAlign::Center) 882 | .with_name("title"); 883 | 884 | dl.add_child(dialog_title); 885 | 886 | let canvas = Canvas::new(instance).with_draw(|instance, printer| { 887 | let x = [ 888 | ( 889 | "instance-id", 890 | &instance.instance.instance_id.clone().unwrap(), 891 | ), 892 | ( 893 | "name", 894 | &find_tag("name".to_string(), instance.instance.tags.clone()) 895 | .unwrap_or_else(|| "".to_string()), 896 | ), 897 | ( 898 | "architecture", 899 | &instance 900 | .instance 901 | .architecture 902 | .clone() 903 | .unwrap_or_else(|| "".to_string()), 904 | ), 905 | ( 906 | "vpc-id", 907 | &instance 908 | .instance 909 | .vpc_id 910 | .clone() 911 | .unwrap_or_else(|| "".to_string()), 912 | ), 913 | ( 914 | "subnet_type", 915 | &instance 916 | .instance 917 | .subnet_id 918 | .clone() 919 | .unwrap_or_else(|| "".to_string()), 920 | ), 921 | ( 922 | "instance_type", 923 | &instance 924 | .instance 925 | .instance_type 926 | .clone() 927 | .unwrap_or_else(|| "".to_string()), 928 | ), 929 | ( 930 | "key_name", 931 | &instance 932 | .instance 933 | .key_name 934 | .clone() 935 | .unwrap_or_else(|| "".to_string()), 936 | ), 937 | ( 938 | "state", 939 | &instance.instance.state.clone().unwrap().name.unwrap(), 940 | ), 941 | ( 942 | "public ip", 943 | &instance 944 | .instance 945 | .public_ip_address 946 | .clone() 947 | .unwrap_or_else(|| "".to_string()), 948 | ), 949 | ( 950 | "public dns", 951 | &instance 952 | .instance 953 | .public_dns_name 954 | .clone() 955 | .unwrap_or_else(|| "".to_string()), 956 | ), 957 | ( 958 | "private ip", 959 | &instance 960 | .instance 961 | .private_ip_address 962 | .clone() 963 | .unwrap_or_else(|| "".to_string()), 964 | ), 965 | ( 966 | "private dns", 967 | &instance 968 | .instance 969 | .private_dns_name 970 | .clone() 971 | .unwrap_or_else(|| "".to_string()), 972 | ), 973 | ( 974 | "placement", 975 | &instance 976 | .instance 977 | .placement 978 | .clone() 979 | .unwrap() 980 | .group_name 981 | .unwrap(), 982 | ), 983 | ( 984 | "lifecycle", 985 | &instance 986 | .instance 987 | .instance_lifecycle 988 | .clone() 989 | .unwrap_or_else(|| "".to_string()), 990 | ), 991 | ( 992 | "image-id", 993 | &instance 994 | .instance 995 | .image_id 996 | .clone() 997 | .unwrap_or_else(|| "".to_string()), 998 | ), 999 | ( 1000 | "ramdisk-id", 1001 | &instance 1002 | .instance 1003 | .ramdisk_id 1004 | .clone() 1005 | .unwrap_or_else(|| "".to_string()), 1006 | ), 1007 | ( 1008 | "root device", 1009 | &format!( 1010 | "{:} ({:})", 1011 | &instance 1012 | .instance 1013 | .root_device_name 1014 | .clone() 1015 | .unwrap_or_else(|| "".to_string()), 1016 | &instance 1017 | .instance 1018 | .root_device_type 1019 | .clone() 1020 | .unwrap_or_else(|| "".to_string()), 1021 | ), 1022 | ), 1023 | ( 1024 | "state", 1025 | &instance.instance.state.clone().unwrap().name.unwrap(), 1026 | ), 1027 | //("state-reason", &instance.state_reason.clone().unwrap().message.unwrap()), 1028 | ]; 1029 | 1030 | for (i, pair) in x.iter().enumerate() { 1031 | printer.with_color( 1032 | ColorStyle::new(PaletteColor::TitleSecondary, PaletteColor::Background), 1033 | |p| p.print((0, i + 1), &format!("{:>20}:", pair.0)), 1034 | ); 1035 | printer.print((22, i + 1), &pair.1.to_string()); 1036 | } 1037 | 1038 | let mut y = 22; 1039 | 1040 | printer.with_color( 1041 | ColorStyle::new(PaletteColor::TitleSecondary, PaletteColor::Background), 1042 | |p| p.print((0, y), "security groups"), 1043 | ); 1044 | y += 1; 1045 | 1046 | let security_groups = instance.instance.security_groups.clone(); 1047 | 1048 | for sg in security_groups.unwrap().iter() { 1049 | printer.print( 1050 | (0, y), 1051 | &format!( 1052 | "{:>20}: {:}", 1053 | &sg.group_id.clone().unwrap(), 1054 | &sg.group_name.clone().unwrap(), 1055 | ), 1056 | ); 1057 | 1058 | y += 1; 1059 | } 1060 | 1061 | y += 1; 1062 | 1063 | printer.with_color( 1064 | ColorStyle::new(PaletteColor::TitleSecondary, PaletteColor::Background), 1065 | |p| p.print((0, y), "network interfaces"), 1066 | ); 1067 | y += 1; 1068 | 1069 | let network_interfaces = instance.instance.network_interfaces.clone(); 1070 | for sg in network_interfaces.unwrap().iter() { 1071 | printer.print( 1072 | (0, y), 1073 | &format!( 1074 | "{:>20}: {:}", 1075 | &sg.network_interface_id.clone().unwrap(), 1076 | &sg.description.clone().unwrap(), 1077 | ), 1078 | ); 1079 | 1080 | y += 1; 1081 | } 1082 | }); 1083 | 1084 | dl.add_child(ResizedView::new( 1085 | SizeConstraint::Full, 1086 | SizeConstraint::Full, 1087 | canvas, 1088 | )); 1089 | 1090 | let dl = event_view(dl); 1091 | 1092 | siv.add_fullscreen_layer(dl); 1093 | } 1094 | 1095 | fn help(siv: &mut Cursive) { 1096 | let mut dl = LinearLayout::new(Orientation::Vertical); 1097 | 1098 | let content = TextContent::new(include_str!("../help.md")); 1099 | let view = TextView::new_with_content(content); 1100 | 1101 | dl.add_child(ResizedView::new( 1102 | SizeConstraint::Full, 1103 | SizeConstraint::Full, 1104 | view, 1105 | )); 1106 | 1107 | let dl = event_view(dl); 1108 | 1109 | siv.add_fullscreen_layer(dl); 1110 | } 1111 | 1112 | fn run_bsod() { 1113 | let mut siv = cursive_new(); 1114 | 1115 | let d = Dialog::around(TextView::new( 1116 | "Cloudman has encountered an error and needs to exit.", 1117 | )) 1118 | .title("Panic") 1119 | .button("Exit", |s| s.quit()); 1120 | 1121 | siv.add_layer(d); 1122 | siv.run(); 1123 | } 1124 | 1125 | fn action(s: &mut Cursive) { 1126 | let mut select = SelectView::::new() 1127 | .h_align(HAlign::Center) 1128 | .autojump() 1129 | .item("start", Actions::Start) 1130 | .item("stop", Actions::Stop) 1131 | .item("terminate", Actions::Terminate) 1132 | .item("connect (ssm)", Actions::Connect) 1133 | .item("connect (serial)", Actions::SerialConnect) 1134 | .item("reboot", Actions::Reboot); 1135 | 1136 | fn ok(s: &mut Cursive, action: &Actions) { 1137 | let table = &s 1138 | .find_name::>("instances") 1139 | .unwrap(); 1140 | 1141 | let instance = table.item(); 1142 | 1143 | if instance.is_none() { 1144 | return; 1145 | } 1146 | 1147 | let ud = s.user_data::().unwrap(); 1148 | 1149 | let client = new_ec2client(&instance.unwrap().region, &instance.unwrap().profile); 1150 | if client.is_err() { 1151 | return; 1152 | }; 1153 | 1154 | let client = client.unwrap(); 1155 | match action { 1156 | Actions::Start => { 1157 | let req = StartInstancesRequest { 1158 | dry_run: Some(ud.dry_run), 1159 | instance_ids: [instance.unwrap().instance.instance_id.clone().unwrap()] 1160 | .to_vec(), 1161 | ..Default::default() 1162 | }; 1163 | 1164 | let ft = client.start_instances(req); 1165 | 1166 | let mut runtime = tokio::runtime::Runtime::new().unwrap(); 1167 | 1168 | match runtime.block_on(ft) { 1169 | Ok(_) => { 1170 | match s.cb_sink().send(Box::new(|s| { 1171 | let d = Dialog::around(TextView::new("The instance will start.")) 1172 | .title("Start instance") 1173 | .button("Cancel", |s| { 1174 | s.pop_layer(); 1175 | }); 1176 | 1177 | let dl = event_view(d); 1178 | 1179 | s.add_layer(dl); 1180 | })) { 1181 | Ok(_) => (), 1182 | Err(err) => { 1183 | s.pop_layer(); 1184 | error_dialog(s, "Could not start instance.", &format!("{}", err)); 1185 | } 1186 | }; 1187 | } 1188 | Err(err) => { 1189 | s.pop_layer(); 1190 | error_dialog(s, "Could not start instance.", &format!("{}", err)); 1191 | } 1192 | }; 1193 | } 1194 | Actions::Stop => { 1195 | let req = StopInstancesRequest { 1196 | dry_run: Some(ud.dry_run), 1197 | instance_ids: [instance.unwrap().instance.instance_id.clone().unwrap()] 1198 | .to_vec(), 1199 | ..Default::default() 1200 | }; 1201 | 1202 | let ft = client.stop_instances(req); 1203 | 1204 | let mut runtime = tokio::runtime::Runtime::new().unwrap(); 1205 | 1206 | match runtime.block_on(ft) { 1207 | Ok(_) => { 1208 | match s.cb_sink().send(Box::new(|s| { 1209 | let d = Dialog::around(TextView::new("The instance will be stopped.")) 1210 | .title("Stop instance") 1211 | .button("Cancel", |s| { 1212 | s.pop_layer(); 1213 | }); 1214 | 1215 | let dl = event_view(d); 1216 | 1217 | s.add_layer(dl); 1218 | })) { 1219 | Ok(_) => (), 1220 | Err(err) => { 1221 | s.pop_layer(); 1222 | error_dialog(s, "Could not stop instance.", &format!("{}", err)); 1223 | } 1224 | }; 1225 | } 1226 | Err(err) => { 1227 | s.pop_layer(); 1228 | error_dialog(s, "Could not stop instance.", &format!("{}", err)); 1229 | } 1230 | }; 1231 | } 1232 | Actions::Connect => { 1233 | connect(&instance.unwrap()); 1234 | } 1235 | Actions::SerialConnect => { 1236 | serial_connect(&instance.unwrap()); 1237 | } 1238 | Actions::Terminate => { 1239 | let req = TerminateInstancesRequest { 1240 | dry_run: Some(ud.dry_run), 1241 | instance_ids: [instance.unwrap().instance.instance_id.clone().unwrap()] 1242 | .to_vec(), 1243 | ..Default::default() 1244 | }; 1245 | 1246 | let ft = client.terminate_instances(req); 1247 | 1248 | let mut runtime = tokio::runtime::Runtime::new().unwrap(); 1249 | 1250 | match runtime.block_on(ft) { 1251 | Ok(_) => { 1252 | match s.cb_sink().send(Box::new(|s| { 1253 | let d = Dialog::around(TextView::new("The instance will be terminated.")) 1254 | .title("Stop instance") 1255 | .button("Cancel", |s| { 1256 | s.pop_layer(); 1257 | }); 1258 | 1259 | let dl = event_view(d); 1260 | 1261 | s.add_layer(dl); 1262 | })) { 1263 | Ok(_) => (), 1264 | Err(err) => { 1265 | s.pop_layer(); 1266 | error_dialog(s, "Could not terminate instance.", &format!("{}", err)); 1267 | } 1268 | }; 1269 | } 1270 | Err(err) => { 1271 | s.pop_layer(); 1272 | error_dialog(s, "Could not terminate instance.", &format!("{}", err)); 1273 | } 1274 | }; 1275 | } 1276 | Actions::Reboot => { 1277 | let req = RebootInstancesRequest { 1278 | dry_run: Some(ud.dry_run), 1279 | instance_ids: [instance.unwrap().instance.instance_id.clone().unwrap()] 1280 | .to_vec(), 1281 | //..Default::default() 1282 | }; 1283 | 1284 | let ft = client.reboot_instances(req); 1285 | 1286 | let mut runtime = tokio::runtime::Runtime::new().unwrap(); 1287 | 1288 | match runtime.block_on(ft) { 1289 | Ok(_) => { 1290 | match s.cb_sink().send(Box::new(|s| { 1291 | let d = Dialog::around(TextView::new("The instance will reboot.")) 1292 | .title("Reboot instance") 1293 | .button("Cancel", |s| { 1294 | s.pop_layer(); 1295 | }); 1296 | 1297 | let dl = event_view(d); 1298 | 1299 | s.add_layer(dl); 1300 | })) { 1301 | Ok(_) => (), 1302 | Err(err) => { 1303 | s.pop_layer(); 1304 | error_dialog(s, "Could not reboot instance.", &format!("{}", err)); 1305 | } 1306 | }; 1307 | } 1308 | Err(err) => { 1309 | s.pop_layer(); 1310 | error_dialog(s, "Could not reboot instance.", &format!("{}", err)); 1311 | } 1312 | }; 1313 | } 1314 | } 1315 | } 1316 | select.set_on_submit(ok); 1317 | 1318 | let table = &s 1319 | .find_name::>("instances") 1320 | .unwrap(); 1321 | 1322 | let instance = table.item(); 1323 | 1324 | if instance.is_none() { 1325 | return; 1326 | } 1327 | 1328 | let instance = instance.unwrap(); 1329 | 1330 | let select = OnEventView::new(select); 1331 | s.add_layer(event_view( 1332 | Dialog::around(select.scrollable()) 1333 | .h_align(HAlign::Center) 1334 | .title(format!( 1335 | "Action ({})", 1336 | &instance.instance.instance_id.clone().unwrap() 1337 | )) 1338 | .button("Cancel", |s| { 1339 | s.pop_layer(); 1340 | }), 1341 | )); 1342 | } 1343 | 1344 | fn change_region(s: &mut Cursive) { 1345 | let regions = vec![ 1346 | ApEast1, 1347 | AfSouth1, 1348 | ApNortheast1, 1349 | ApNortheast2, 1350 | ApSouth1, 1351 | ApSoutheast1, 1352 | ApSoutheast2, 1353 | CaCentral1, 1354 | EuCentral1, 1355 | EuWest1, 1356 | EuWest2, 1357 | EuWest3, 1358 | EuNorth1, 1359 | EuSouth1, 1360 | SaEast1, 1361 | UsEast1, 1362 | UsEast2, 1363 | UsWest1, 1364 | UsWest2, 1365 | UsGovEast1, 1366 | UsGovWest1, 1367 | CnNorth1, 1368 | CnNorthwest1, 1369 | ]; 1370 | 1371 | let mut select = SelectView::::new() 1372 | // Center the text horizontally 1373 | .h_align(HAlign::Center) 1374 | // Use keyboard to jump to the pressed letters 1375 | .autojump(); 1376 | 1377 | select.add_all_str(regions.iter().map(|r| r.name())); 1378 | 1379 | let ud = s.user_data::().unwrap(); 1380 | 1381 | // TODO: change to support multi checkbox, need modified select view 1382 | // currently defaulting to first 1383 | let idx = &select 1384 | .iter() 1385 | .position(|item| item.0 == ud.regions[0].name()) 1386 | .unwrap(); 1387 | 1388 | let mut select = select.selected(*idx); 1389 | 1390 | fn ok(s: &mut Cursive, name: &str) { 1391 | let region = Region::from_str(name).unwrap(); 1392 | 1393 | let ud = s.user_data::().unwrap(); 1394 | 1395 | if ud.regions[0].name() == name { 1396 | s.pop_layer(); 1397 | return; 1398 | } 1399 | 1400 | match get_instances_with_region(ud.profiles.clone(), [region.clone()].to_vec()) { 1401 | Ok(instances) => { 1402 | let mut iv = s 1403 | .find_name::>("instances") 1404 | .unwrap(); 1405 | iv.set_instances(instances.clone()); 1406 | 1407 | s.with_user_data(|v: &mut ReturnValues| { 1408 | v.regions = [region.clone()].to_vec(); 1409 | v.instances = instances; 1410 | }); 1411 | 1412 | s.pop_layer(); 1413 | 1414 | update_bottom_bar(s); 1415 | } 1416 | Err(err) => { 1417 | let d = Dialog::around(TextView::new(format!( 1418 | "Could not retrieve instances\n\n{}", 1419 | err 1420 | ))) 1421 | .title("Error") 1422 | .button("Cancel", |s| { 1423 | s.pop_layer(); 1424 | }); 1425 | 1426 | let dl = event_view(d); 1427 | 1428 | s.add_layer(dl); 1429 | } 1430 | } 1431 | } 1432 | 1433 | // Sets the callback for when "Enter" is pressed. 1434 | select.set_on_submit(ok); 1435 | 1436 | // Let's override the `j` and `k` keys for navigation 1437 | let select = OnEventView::new(select) 1438 | .on_pre_event_inner('k', |s, _| { 1439 | s.select_up(1); 1440 | Some(EventResult::Consumed(None)) 1441 | }) 1442 | .on_pre_event_inner('j', |s, _| { 1443 | s.select_down(1); 1444 | Some(EventResult::Consumed(None)) 1445 | }); 1446 | 1447 | s.add_layer( 1448 | OnEventView::new( 1449 | Dialog::around(select.scrollable().fixed_size((20, 10))) 1450 | .title("Switch region") 1451 | .button("Cancel", |s| { 1452 | s.pop_layer(); 1453 | }), 1454 | ) 1455 | .on_event(Event::Key(Key::Esc), |s| { 1456 | s.pop_layer(); 1457 | }), 1458 | ); 1459 | } 1460 | 1461 | fn error_dialog(s: &mut Cursive, title: &str, description: &str) { 1462 | let d = Dialog::around(TextView::new(description)) 1463 | .title(title) 1464 | .button("Cancel", |s| { 1465 | s.pop_layer(); 1466 | }); 1467 | 1468 | let dl = event_view(d); 1469 | 1470 | s.add_layer(dl); 1471 | } 1472 | 1473 | fn change_profile(s: &mut Cursive) { 1474 | fn ok(s: &mut Cursive, name: &str) { 1475 | s.call_on_name("select", |view: &mut SelectView| { 1476 | view.add_item_str(name) 1477 | }); 1478 | s.pop_layer(); 1479 | } 1480 | 1481 | s.add_layer( 1482 | Dialog::around( 1483 | EditView::new() 1484 | .on_submit(ok) 1485 | .with_name("name") 1486 | .fixed_width(10), 1487 | ) 1488 | .title("Pick a region") 1489 | .button("Ok", |s| { 1490 | let name = s 1491 | .call_on_name("name", |view: &mut EditView| view.get_content()) 1492 | .unwrap(); 1493 | ok(s, &name); 1494 | }) 1495 | .button("Cancel", |s| { 1496 | s.pop_layer(); 1497 | }), 1498 | ); 1499 | } 1500 | 1501 | fn ec2_describe_input() -> DescribeInstancesRequest { 1502 | DescribeInstancesRequest { 1503 | ..Default::default() 1504 | } 1505 | } 1506 | -------------------------------------------------------------------------------- /src/cloudman.rs: -------------------------------------------------------------------------------- 1 | extern crate cursive; 2 | extern crate enum_map; 3 | extern crate base64; 4 | 5 | use clap::Clap; 6 | use cursive::CursiveExt; 7 | use cursive::align::HAlign; 8 | use cursive::direction::Direction; 9 | use cursive::direction::Orientation; 10 | use cursive::event::*; 11 | use cursive::event::{Event, EventResult, Key}; 12 | use cursive::traits::*; 13 | use cursive::theme::{ 14 | BaseColor, BorderStyle, Color, ColorStyle, Effect, PaletteColor, Style, Theme, 15 | }; 16 | use cursive::utils::markup::StyledString; 17 | use cursive::vec::Vec2; 18 | use cursive::view::*; 19 | use cursive::view::View; 20 | use cursive::views::*; 21 | use cursive::views::{DebugView, Dialog, EditView, LinearLayout, ResizedView, TextView}; 22 | use cursive::{Cursive, Printer}; 23 | use rusoto_core::Region::*; 24 | use rusoto_core::Region; 25 | use rusoto_core::credential::ProfileProvider; 26 | use rusoto_core::request::HttpClient; 27 | use rusoto_ec2::{DescribeInstancesRequest, Ec2, Ec2Client, Filter, Instance, Tag}; 28 | use std::cmp::Ordering; 29 | use std::cell::{Cell, RefCell}; 30 | use std::env; 31 | use std::hash::Hash; 32 | use std::panic; 33 | use std::process::Command; 34 | use std::rc::Rc; 35 | use std::str::FromStr; 36 | -------------------------------------------------------------------------------- /src/help.md: -------------------------------------------------------------------------------- 1 | Cloudman 0.1.7 2 | ---------- 3 | Cloudman is a textual user interface (heavily inspired by htop) to manage your Amazon EC2 fleet instantly. By using Cloudman you'll find an overview of your instances, navigate through regions, retrieve instance details, show console outputs and connect to instance terminal using SSM. 4 | 5 | The profiles and defaults as configured in ~/.aws/credentials will be used. 6 | 7 | 8 | https://github.com/dutchcoders/cloudman 9 | 10 | 11 | (C) 2021 Remco Verhoef (@remco_verhoef) 12 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod views; 2 | -------------------------------------------------------------------------------- /src/ncurses_theme.toml: -------------------------------------------------------------------------------- 1 | borders = "outset" 2 | shadow = false 3 | 4 | [colors] 5 | background = "default" 6 | shadow = "blue" 7 | view = "default" 8 | primary = "default" 9 | secondary ="orange" 10 | highlight = "#91C6C2" 11 | highlight_inactive = "#91C6C2" 12 | highlight_text = "orange" 13 | tertiary = "blue" 14 | title_primary = "white" 15 | title_secondary = "yellow" 16 | -------------------------------------------------------------------------------- /src/views/bottombar_view.rs: -------------------------------------------------------------------------------- 1 | extern crate cursive; 2 | 3 | use cursive::theme::{Color, ColorStyle}; 4 | use cursive::vec::Vec2; 5 | use cursive::view::View; 6 | use cursive::Printer; 7 | use rusoto_core::Region; 8 | 9 | pub struct Column { 10 | key: String, 11 | name: String, 12 | } 13 | 14 | pub enum BottomBarType { 15 | Standard, 16 | Search, 17 | Filter, 18 | } 19 | 20 | pub struct BottomBarView { 21 | s: String, 22 | valid: bool, 23 | profiles: Vec, 24 | r: Vec, 25 | type_: BottomBarType, 26 | } 27 | 28 | impl BottomBarView { 29 | pub fn new(s: &str, r: Vec) -> Self { 30 | BottomBarView { 31 | s: s.to_string(), 32 | valid: true, 33 | profiles: ["".to_string()].to_vec(), 34 | r: r.clone(), 35 | type_: BottomBarType::Standard, 36 | } 37 | } 38 | 39 | pub fn set_type(&mut self, t: BottomBarType) -> &mut Self { 40 | self.type_ = t; 41 | 42 | self 43 | } 44 | 45 | pub fn set_profile(&mut self, p: Vec) -> &mut Self { 46 | self.profiles = p; 47 | 48 | self 49 | } 50 | 51 | pub fn set_region(&mut self, r: Vec) -> &mut Self { 52 | self.r = r; 53 | 54 | self 55 | } 56 | 57 | pub fn set_valid(&mut self, s: bool) -> &mut Self { 58 | self.valid = s; 59 | 60 | self 61 | } 62 | 63 | pub fn set_content(&mut self, s: &str) -> &mut Self { 64 | self.s = s.to_string(); 65 | 66 | self 67 | } 68 | } 69 | 70 | impl View for BottomBarView { 71 | fn draw(&self, printer: &Printer<'_, '_>) { 72 | printer.with_color( 73 | ColorStyle::new(Color::Rgb(145, 198, 194), Color::Rgb(145, 198, 194)), 74 | |printer| { 75 | for y in 0..printer.size.y { 76 | printer.print_hline((0, y), printer.size.x, " "); 77 | } 78 | }, 79 | ); 80 | 81 | match self.type_ { 82 | BottomBarType::Search => { 83 | let cols = [ 84 | Column { 85 | key: "F3".to_string(), 86 | name: "Next ".to_string(), 87 | }, 88 | Column { 89 | key: "Esc".to_string(), 90 | name: "Cancel ".to_string(), 91 | }, 92 | Column { 93 | key: " ".to_string(), 94 | name: "Search: ".to_string(), 95 | }, 96 | ]; 97 | 98 | printer.with_color( 99 | ColorStyle::new(Color::TerminalDefault, Color::TerminalDefault), 100 | |printer| { 101 | let mut x = 0; 102 | for col in &cols { 103 | printer.print((x, 0), &col.key); 104 | x += col.key.len(); 105 | x += col.name.len(); 106 | } 107 | }, 108 | ); 109 | 110 | let mut x = 0; 111 | 112 | printer.with_color( 113 | ColorStyle::new(Color::Rgb(0, 0, 0), Color::Rgb(145, 198, 194)), 114 | |printer| { 115 | for col in &cols { 116 | x += col.key.len(); 117 | printer.print((x, 0), &col.name); 118 | x += col.name.len(); 119 | } 120 | }, 121 | ); 122 | 123 | let cs = if self.valid { 124 | ColorStyle::new(Color::Rgb(0, 0, 0), Color::Rgb(145, 198, 194)) 125 | } else { 126 | ColorStyle::new(Color::Rgb(255, 0, 0), Color::Rgb(145, 198, 194)) 127 | }; 128 | 129 | printer.with_color(cs, |printer| { 130 | printer.print((x, 0), &self.s); 131 | }); 132 | } 133 | BottomBarType::Filter => { 134 | let cols = [ 135 | Column { 136 | key: "Enter".to_string(), 137 | name: "Done ".to_string(), 138 | }, 139 | Column { 140 | key: "Esc".to_string(), 141 | name: "Clear ".to_string(), 142 | }, 143 | Column { 144 | key: " ".to_string(), 145 | name: "Filter: ".to_string(), 146 | }, 147 | ]; 148 | 149 | printer.with_color( 150 | ColorStyle::new(Color::TerminalDefault, Color::TerminalDefault), 151 | |printer| { 152 | let mut x = 0; 153 | for col in &cols { 154 | printer.print((x, 0), &col.key); 155 | x += col.key.len(); 156 | x += col.name.len(); 157 | } 158 | }, 159 | ); 160 | 161 | printer.with_color( 162 | ColorStyle::new(Color::Rgb(0, 0, 0), Color::Rgb(145, 198, 194)), 163 | |printer| { 164 | let mut x = 0; 165 | for col in &cols { 166 | x += col.key.len(); 167 | printer.print((x, 0), &col.name); 168 | x += col.name.len(); 169 | } 170 | 171 | printer.print((x, 0), &self.s); 172 | }, 173 | ); 174 | } 175 | 176 | BottomBarType::Standard => { 177 | let cols = [ 178 | Column { 179 | key: "F1".to_string(), 180 | name: "Help ".to_string(), 181 | }, 182 | Column { 183 | key: "F2".to_string(), 184 | name: "Connect".to_string(), 185 | }, 186 | Column { 187 | key: "F3".to_string(), 188 | name: "Search".to_string(), 189 | }, 190 | Column { 191 | key: "F4".to_string(), 192 | name: "Filter".to_string(), 193 | }, 194 | Column { 195 | key: "F5".to_string(), 196 | name: "Refresh".to_string(), 197 | }, 198 | Column { 199 | key: "F6".to_string(), 200 | name: "Actions".to_string(), 201 | }, 202 | Column { 203 | key: "F7".to_string(), 204 | name: "Region".to_string(), 205 | }, 206 | Column { 207 | key: "F10".to_string(), 208 | name: "Quit".to_string(), 209 | }, 210 | Column { 211 | key: "L".to_string(), 212 | name: "Log".to_string(), 213 | }, 214 | ]; 215 | 216 | printer.with_color( 217 | ColorStyle::new(Color::TerminalDefault, Color::TerminalDefault), 218 | |printer| { 219 | let mut x = 0; 220 | for col in &cols { 221 | let s = format!("{: = self.r.iter().map(|r| r.name()).collect(); 248 | let profiles: Vec<&str> = self.profiles.iter().map(|r| &r[..]).collect(); 249 | 250 | let s = format!("{} ({})", regions.join(","), profiles.join(",")); 251 | printer.print((printer.size.x - s.len() - 1, 0), &s); 252 | }, 253 | ); 254 | } 255 | 256 | fn required_size(&mut self, constraint: Vec2) -> Vec2 { 257 | let h = 1; 258 | let w = &constraint.x; 259 | // let h = std::cmp::max(h, &constraint.y); 260 | 261 | Vec2::new(*w, h) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /src/views/foo_view.rs: -------------------------------------------------------------------------------- 1 | extern crate cursive; 2 | 3 | use cursive::direction::Direction; 4 | use cursive::event::{Callback, Event, EventResult, Key}; 5 | use cursive::view::View; 6 | use cursive::{Cursive, Printer}; 7 | use std::rc::Rc; 8 | 9 | pub type OnEdit = dyn Fn(&mut Cursive, &str, usize); 10 | pub type OnClose = dyn Fn(&mut Cursive); 11 | 12 | #[derive(Default)] 13 | pub struct Foo { 14 | info: Rc, 15 | on_search: Option>, 16 | on_search_next: Option>, 17 | on_cancel: Option>, 18 | on_close: Option>, 19 | } 20 | 21 | impl Foo { 22 | pub fn with_string(s: &str) -> Self { 23 | Foo { 24 | info: Rc::new(String::from(s)), 25 | 26 | on_search: None, 27 | on_search_next: None, 28 | on_cancel: None, 29 | on_close: None, 30 | } 31 | } 32 | 33 | pub fn set_on_search(&mut self, callback: F) 34 | where 35 | F: Fn(&mut Cursive, &str, usize) + 'static, 36 | { 37 | self.on_search = Some(Rc::new(callback)); 38 | } 39 | 40 | pub fn set_on_search_next(&mut self, callback: F) 41 | where 42 | F: Fn(&mut Cursive, &str, usize) + 'static, 43 | { 44 | self.on_search_next = Some(Rc::new(callback)); 45 | } 46 | 47 | pub fn set_on_cancel(&mut self, callback: F) 48 | where 49 | F: Fn(&mut Cursive) + 'static, 50 | { 51 | self.on_cancel = Some(Rc::new(callback)); 52 | } 53 | 54 | pub fn set_on_close(&mut self, callback: F) 55 | where 56 | F: Fn(&mut Cursive) + 'static, 57 | { 58 | self.on_close = Some(Rc::new(callback)); 59 | } 60 | 61 | fn make_cancel_cb(&self) -> Option { 62 | self.on_cancel.clone().map(|cb| { 63 | // Get a new Rc on the content 64 | Callback::from_fn(move |s| { 65 | cb(s); 66 | }) 67 | }) 68 | } 69 | 70 | fn make_close_cb(&self) -> Option { 71 | self.on_close.clone().map(|cb| { 72 | // Get a new Rc on the content 73 | Callback::from_fn(move |s| { 74 | cb(s); 75 | }) 76 | }) 77 | } 78 | 79 | fn make_search_next_cb(&self) -> Option { 80 | self.on_search_next.clone().map(|cb| { 81 | // Get a new Rc on the content 82 | let content = Rc::clone(&self.info); 83 | let cursor = 0; 84 | 85 | Callback::from_fn(move |s| { 86 | cb(s, &content, cursor); 87 | }) 88 | }) 89 | } 90 | 91 | fn make_search_cb(&self) -> Option { 92 | self.on_search.clone().map(|cb| { 93 | // Get a new Rc on the content 94 | let content = Rc::clone(&self.info); 95 | let cursor = 0; 96 | 97 | Callback::from_fn(move |s| { 98 | cb(s, &content, cursor); 99 | }) 100 | }) 101 | } 102 | 103 | pub fn delete(&mut self) -> Callback { 104 | Rc::make_mut(&mut self.info).pop(); 105 | self.make_search_cb().unwrap_or_else(Callback::dummy) 106 | } 107 | 108 | pub fn insert(&mut self, ch: char) -> Callback { 109 | Rc::make_mut(&mut self.info).push(ch); 110 | self.make_search_cb().unwrap_or_else(Callback::dummy) 111 | } 112 | } 113 | 114 | impl View for Foo { 115 | fn draw(&self, _: &Printer) { 116 | // printer.print((0, 0), &self.info); 117 | } 118 | 119 | fn take_focus(&mut self, _: Direction) -> bool { 120 | true 121 | } 122 | 123 | fn on_event(&mut self, event: Event) -> EventResult { 124 | match event { 125 | Event::Key(Key::Backspace) => EventResult::Consumed(Some(self.delete())), 126 | Event::Key(Key::Enter) => EventResult::Consumed(Some(self.make_close_cb().unwrap())), 127 | Event::Key(Key::Esc) => EventResult::Consumed(Some(self.make_cancel_cb().unwrap())), 128 | Event::Key(Key::F3) => EventResult::Consumed(Some(self.make_search_next_cb().unwrap())), 129 | Event::Char(ch) => EventResult::Consumed(Some(self.insert(ch))), 130 | _ => EventResult::Consumed(Some(self.make_close_cb().unwrap())), 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/views/key_codes.rs: -------------------------------------------------------------------------------- 1 | use cursive::event::{Event, EventResult}; 2 | use cursive::traits::*; 3 | use cursive::Printer; 4 | 5 | // Our view will have a small history of the last events. 6 | pub struct KeyCodeView { 7 | history: Vec, 8 | size: usize, 9 | } 10 | 11 | impl KeyCodeView { 12 | pub fn new(size: usize) -> Self { 13 | KeyCodeView { 14 | history: Vec::new(), 15 | size, 16 | } 17 | } 18 | } 19 | 20 | // Let's implement the `View` trait. 21 | // `View` contains many methods, but only a few are required. 22 | impl View for KeyCodeView { 23 | fn draw(&self, printer: &Printer) { 24 | // We simply draw every event from the history. 25 | for (y, line) in self.history.iter().enumerate() { 26 | printer.print((0, y), line); 27 | } 28 | } 29 | 30 | fn on_event(&mut self, event: Event) -> EventResult { 31 | // Each line will be a debug-format of the event. 32 | let line = format!("{:?}", event); 33 | self.history.push(line); 34 | 35 | // Keep a fixed-sized history. 36 | while self.history.len() > self.size { 37 | self.history.remove(0); 38 | } 39 | 40 | // No need to return any callback. 41 | EventResult::Consumed(None) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/views/log_view.rs: -------------------------------------------------------------------------------- 1 | extern crate cursive; 2 | 3 | use cursive::{Rect, direction::Direction, view::scroll::{self, Core}}; 4 | use cursive::event::{Event, EventResult, Key}; 5 | use cursive::vec::Vec2; 6 | use cursive::{Printer, view::View}; 7 | 8 | cursive::impl_scroller!(LogView::core); 9 | 10 | pub struct LogView { 11 | lines: Vec, 12 | 13 | core: cursive::view::scroll::Core, 14 | } 15 | 16 | impl LogView { 17 | pub fn scrollable(buf: &[u8]) -> Self { 18 | let lines = parse_lines(&buf); 19 | 20 | LogView { 21 | lines, 22 | core: Core::new(), 23 | } 24 | } 25 | 26 | fn inner_required_size(&mut self, _req: Vec2) -> Vec2 { 27 | Vec2::new(80, self.lines.len()) 28 | } 29 | 30 | fn inner_on_event(&mut self, event: Event) -> EventResult { 31 | let height = self.core.content_viewport().height(); 32 | match event { 33 | Event::Key(Key::Up) => { 34 | self.core.scroll_up(1); 35 | EventResult::Consumed(None) 36 | } 37 | Event::Key(Key::Down) => { 38 | self.core.scroll_down(1); 39 | EventResult::Consumed(None) 40 | } 41 | Event::Key(Key::PageUp) => { 42 | self.core.scroll_up(height); 43 | EventResult::Consumed(None) 44 | } 45 | Event::Key(Key::PageDown) => { 46 | self.core.scroll_down(height); 47 | EventResult::Consumed(None) 48 | } 49 | Event::Char('g') => { 50 | self.core.scroll_to_top(); 51 | EventResult::Consumed(None) 52 | } 53 | Event::Shift(Key::Home) => { 54 | self.core.scroll_to_top(); 55 | EventResult::Consumed(None) 56 | } 57 | Event::Shift(Key::End) => { 58 | self.core.scroll_to_bottom(); 59 | EventResult::Consumed(None) 60 | } 61 | Event::Key(Key::Home) => { 62 | self.core.scroll_to_top(); 63 | EventResult::Consumed(None) 64 | } 65 | Event::Key(Key::End) => { 66 | self.core.scroll_to_bottom(); 67 | EventResult::Consumed(None) 68 | } 69 | Event::Char('H') => { 70 | self.core.scroll_to_bottom(); 71 | EventResult::Consumed(None) 72 | } 73 | Event::Char('/') => { 74 | // search 75 | EventResult::Consumed(None) 76 | } 77 | _ => EventResult::Ignored 78 | } 79 | } 80 | 81 | fn inner_important_area(&self, size: Vec2) -> Rect { 82 | Rect::from_size((0, 0), (size.x, self.lines.len())) 83 | } 84 | } 85 | 86 | fn parse_lines(buf: &[u8]) -> Vec { 87 | let mut statemachine = vte::Parser::new(); 88 | let mut parser = Log::new(); 89 | 90 | for byte in &buf[..] { 91 | statemachine.advance(&mut parser, *byte); 92 | } 93 | 94 | parser.lines 95 | } 96 | 97 | impl View for LogView { 98 | fn draw(&self, printer: &Printer<'_, '_>) { 99 | let lines = self.lines.clone(); 100 | scroll::draw_lines(self, &printer, |_, printer, i| { 101 | // ignore the first line, as it is incomplete 102 | if let Some(line) = lines.get(i + 1) { 103 | printer.print((0, 0), line); 104 | } else { 105 | printer.print((0, 0), "⍇"); 106 | } 107 | 108 | }); 109 | } 110 | 111 | fn required_size(&mut self, req: Vec2) -> Vec2 { 112 | scroll::required_size( 113 | self, 114 | req, 115 | true, 116 | Self::inner_required_size, 117 | ) 118 | } 119 | 120 | fn take_focus(&mut self, _: Direction) -> bool { 121 | true 122 | } 123 | 124 | fn layout(&mut self, size: Vec2) { 125 | scroll::layout( 126 | self, 127 | size, 128 | true, 129 | |_s, _size| (), 130 | Self::inner_required_size, 131 | ); 132 | } 133 | 134 | fn important_area(&self, size: Vec2) -> Rect { 135 | scroll::important_area( 136 | self, 137 | size, 138 | Self::inner_important_area, 139 | ) 140 | } 141 | 142 | fn on_event(&mut self, event: Event) -> EventResult { 143 | scroll::on_event( 144 | self, 145 | event, 146 | Self::inner_on_event, 147 | Self::inner_important_area, 148 | ) 149 | } 150 | 151 | } 152 | 153 | #[derive(Default)] 154 | struct Log { 155 | s: String, 156 | lines: Vec, 157 | } 158 | 159 | impl Log { 160 | fn new() -> Self { 161 | Log { 162 | s: String::new(), 163 | lines: vec![], 164 | } 165 | } 166 | } 167 | 168 | impl vte::Perform for Log { 169 | fn print(&mut self, c: char) { 170 | self.s.push(c); 171 | } 172 | 173 | fn execute(&mut self, _c: u8) { 174 | let s = self.s.clone(); 175 | if s.is_empty() { 176 | return; 177 | } 178 | 179 | self.lines.push(s); 180 | self.s = String::new(); 181 | } 182 | 183 | fn hook(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool, _c: char) {} 184 | 185 | fn put(&mut self, _byte: u8) {} 186 | 187 | fn unhook(&mut self) {} 188 | 189 | fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) {} 190 | 191 | fn csi_dispatch(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool, _c: char) {} 192 | 193 | fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) {} 194 | } 195 | -------------------------------------------------------------------------------- /src/views/mod.rs: -------------------------------------------------------------------------------- 1 | mod bottombar_view; 2 | mod foo_view; 3 | mod key_codes; 4 | mod log_view; 5 | mod table_view; 6 | 7 | pub use self::bottombar_view::{BottomBarType, BottomBarView, Column}; 8 | pub use self::foo_view::Foo; 9 | pub use self::key_codes::KeyCodeView; 10 | pub use self::log_view::LogView; 11 | pub use self::table_view::{Header, InstancesView, TableViewItem}; 12 | -------------------------------------------------------------------------------- /src/views/table_view.rs: -------------------------------------------------------------------------------- 1 | extern crate cursive; 2 | 3 | use cursive::XY; 4 | use cursive::Rect; 5 | use cursive::view::scroll::Core; 6 | use cursive::direction::Direction; 7 | use cursive::event::*; 8 | use cursive::event::{Event, EventResult, Key}; 9 | use cursive::theme::{Color, ColorStyle, PaletteColor}; 10 | use cursive::vec::Vec2; 11 | use cursive::view::View; 12 | use cursive::view::*; 13 | use cursive::{Cursive, Printer}; 14 | use std::cmp::{Eq, Ordering}; 15 | use std::hash::Hash; 16 | use std::rc::Rc; 17 | 18 | pub type OnSubmit = Option)>>; 19 | 20 | cursive::impl_scroller!(InstancesView::core); 21 | 22 | pub struct InstancesView { 23 | instances: Vec, 24 | core: cursive::view::scroll::Core, 25 | // scrollbase: ScrollBase, 26 | current_index: usize, 27 | columns: Vec, 28 | 29 | on_submit: OnSubmit, 30 | } 31 | 32 | pub trait TableViewItem: Clone + Sized 33 | where 34 | H: Eq + Hash + Copy + Clone + Header + 'static, 35 | { 36 | /// Method returning a string representation of the item for the 37 | /// specified column from type `H`. 38 | fn to_column(&self, column: H) -> String; 39 | 40 | fn to_column_color(&self, column: H) -> ColorStyle; 41 | 42 | /// Method comparing two items via their specified column from type `H`. 43 | fn cmp(&self, other: &Self, column: H) -> Ordering 44 | where 45 | Self: Sized; 46 | } 47 | 48 | pub trait Header { 49 | fn to_header(&self) -> String; 50 | fn to_header_size(&self, w: usize) -> usize; 51 | } 52 | 53 | impl + PartialEq + 'static, H: Eq + Hash + Copy + Clone + Header + 'static> 54 | Default for InstancesView 55 | { 56 | /// Creates a new empty `TableView` without any columns. 57 | /// 58 | /// See [`TableView::new()`]. 59 | fn default() -> Self { 60 | Self::new() 61 | } 62 | } 63 | 64 | impl + PartialEq + 'static, H: Eq + Hash + Copy + Clone + Header + 'static> 65 | InstancesView 66 | { 67 | /// Create a new `FlexiLoggerView` which is wrapped in a `ScrollView`. 68 | pub fn new() -> Self { 69 | InstancesView { 70 | instances: vec![], 71 | // scrollbase: ScrollBase::new().right_padding(0), 72 | core: Core::new(), 73 | current_index: 0, 74 | columns: vec![], 75 | 76 | on_submit: None, 77 | } 78 | } 79 | 80 | pub fn scrollable(instances: &[T]) -> Self { 81 | InstancesView { 82 | instances: instances.to_owned(), 83 | core: Core::new(), 84 | // scrollbase: ScrollBase::new().right_padding(0), 85 | current_index: 0, 86 | columns: vec![], 87 | 88 | on_submit: None, 89 | } 90 | } 91 | 92 | pub fn set_on_submit(&mut self, callback: F) 93 | where 94 | F: Fn(&mut Cursive, Option) + 'static, 95 | { 96 | self.on_submit = Some(Rc::new(callback)); 97 | } 98 | 99 | pub fn on_submit(mut self, callback: F) -> Self 100 | where 101 | F: Fn(&mut Cursive, Option) + 'static, 102 | { 103 | self.set_on_submit(callback); 104 | self 105 | } 106 | 107 | pub fn column(mut self, column: H) -> Self { 108 | self.columns.push(column); 109 | 110 | self 111 | } 112 | 113 | pub fn selected_item(&self) -> Option { 114 | Some(self.current_index) 115 | } 116 | 117 | pub fn set_selected_item(&mut self, i: usize) { 118 | self.current_index = i; 119 | self.core.scroll_to(XY::new(0, self.current_index+2)); 120 | } 121 | 122 | pub fn item(&self) -> Option<&T> { 123 | self.instances.get(self.current_index) 124 | } 125 | 126 | pub fn set_item(&mut self, item: &T) -> &Self { 127 | match self.instances.iter().position(|t| t.eq(item)) { 128 | Some(row) => { 129 | self.set_selected_item(row); 130 | } 131 | None => { 132 | self.set_selected_item(0); 133 | } 134 | } 135 | 136 | self 137 | } 138 | 139 | pub fn items(&self) -> &Vec { 140 | &self.instances 141 | } 142 | 143 | pub fn set_instances(&mut self, instances: Vec) -> &Self { 144 | self.instances = instances; 145 | self.set_selected_item(0); 146 | self 147 | } 148 | 149 | fn make_submit_cb(&self) -> Option { 150 | self.on_submit.clone().map(|cb| { 151 | Callback::from_fn(move |s| { 152 | cb(s, None); 153 | }) 154 | }) 155 | } 156 | 157 | fn inner_required_size(&mut self, constraint: Vec2) -> Vec2 { 158 | let h = self.instances.len(); 159 | let h = std::cmp::max(h, constraint.y); 160 | 161 | // +1 for header 162 | Vec2::new(constraint.x, h+2) 163 | } 164 | 165 | fn inner_important_area(&self, size: Vec2) -> Rect { 166 | Rect::from_size((0, 0), (size.x, self.instances.len() + 1)) 167 | } 168 | 169 | fn inner_on_event(&mut self, event: Event) -> EventResult { 170 | match event { 171 | Event::Key(Key::Up) => { 172 | if self.current_index > 0 { 173 | self.current_index -= 1; 174 | self.core.scroll_to(XY::new(0, self.current_index+2)); 175 | } 176 | EventResult::Consumed(None) 177 | } 178 | Event::Key(Key::Down) => { 179 | if self.current_index + 1 < self.instances.len() { 180 | self.current_index += 1; 181 | self.core.scroll_to(XY::new(0, self.current_index+2)); 182 | } 183 | EventResult::Consumed(None) 184 | } 185 | Event::Char('g') => { 186 | self.core.scroll_to_top(); 187 | EventResult::Consumed(None) 188 | } 189 | Event::Key(Key::PageUp) => { 190 | if self.current_index < 10 { 191 | self.current_index = 0; 192 | } else { 193 | self.current_index -= 10; 194 | } 195 | self.core.scroll_to(XY::new(0, self.current_index)); 196 | EventResult::Consumed(None) 197 | } 198 | Event::Key(Key::PageDown) => { 199 | let idx = std::cmp::min(self.instances.len() - 1, self.current_index + 10); 200 | self.current_index = idx; 201 | self.core.scroll_to(XY::new(0, self.current_index+2)); 202 | EventResult::Consumed(None) 203 | } 204 | Event::Key(Key::Home) => { 205 | self.current_index = 0; 206 | self.core.scroll_to(XY::new(0, self.current_index)); 207 | EventResult::Consumed(None) 208 | } 209 | Event::Key(Key::End) => { 210 | self.current_index = self.instances.len() - 1; 211 | self.core.scroll_to(XY::new(0, self.current_index+2)); 212 | EventResult::Consumed(None) 213 | } 214 | Event::Shift(Key::Home) => { 215 | self.current_index = 0; 216 | self.core.scroll_to(XY::new(0, self.current_index)); 217 | EventResult::Consumed(None) 218 | } 219 | Event::Shift(Key::End) => { 220 | self.current_index = self.instances.len() - 1; 221 | self.core.scroll_to(XY::new(0, self.current_index+2)); 222 | EventResult::Consumed(None) 223 | } 224 | Event::Key(Key::Enter) => EventResult::Consumed(self.make_submit_cb()), 225 | Event::Char('H') => { 226 | self.current_index = self.instances.len() - 1; 227 | self.core.scroll_to(XY::new(0, self.current_index+2)); 228 | EventResult::Consumed(None) 229 | } 230 | _ => EventResult::Ignored, 231 | } 232 | } 233 | } 234 | 235 | impl + PartialEq + 'static, H: Eq + Hash + Copy + Clone + Header + 'static> View 236 | for InstancesView 237 | { 238 | fn draw(&self, printer: &Printer<'_, '_>) { 239 | scroll::draw_lines(self, &printer, |_, printer, i| { 240 | // draw header 241 | if i == self.core.content_viewport().top() { 242 | printer.with_color( 243 | ColorStyle::new(Color::Rgb(0, 0, 0), Color::Rgb(185, 202, 74)), 244 | |printer| { 245 | printer.print_hline((0, 0), printer.size.x, " "); 246 | 247 | let mut x = 0; 248 | 249 | for column in self.columns.iter() { 250 | let w = column.to_header_size(printer.size.x); 251 | 252 | let s = format!( 253 | "{:.width$} ", 254 | format!("{: Vec2 { 319 | scroll::required_size( 320 | self, 321 | req, 322 | true, 323 | Self::inner_required_size, 324 | ) 325 | } 326 | 327 | fn layout(&mut self, size: Vec2) { 328 | scroll::layout( 329 | self, 330 | size, 331 | true, 332 | |_s, _size| (), 333 | Self::inner_required_size, 334 | ); 335 | } 336 | 337 | fn important_area(&self, size: Vec2) -> Rect { 338 | scroll::important_area( 339 | self, 340 | size, 341 | Self::inner_important_area, 342 | ) 343 | } 344 | 345 | fn on_event(&mut self, event: Event) -> EventResult { 346 | scroll::on_event( 347 | self, 348 | event, 349 | Self::inner_on_event, 350 | Self::inner_important_area, 351 | ) 352 | } 353 | 354 | fn take_focus(&mut self, _: Direction) -> bool { 355 | true 356 | } 357 | } 358 | --------------------------------------------------------------------------------