├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── README.md ├── ci └── run.sh └── src ├── cli ├── helpers.rs ├── mod.rs ├── test.rs └── watch.rs ├── config ├── mod.rs ├── parser │ ├── checkers │ │ ├── http.rs │ │ └── mod.rs │ ├── common.rs │ ├── mod.rs │ └── notifiers │ │ ├── command.rs │ │ ├── hipchat.rs │ │ ├── mod.rs │ │ ├── slack.rs │ │ └── telegram.rs ├── types.rs └── validator.rs ├── error.rs ├── lib.rs ├── main.rs ├── notifiers ├── command.rs ├── hipchat.rs ├── mod.rs ├── slack.rs └── telegram.rs ├── reactor.rs └── watcher.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | 4 | # Files for local testing 5 | hellcheck.yml 6 | custom.sh 7 | /media 8 | server.rb 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | install: 5 | - rustup component add rustfmt-preview 6 | - rustup component add clippy-preview 7 | script: 8 | - ./ci/run.sh 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #### v0.1.2 - 2019-01-24 2 | * Implement CLI 3 | * Support basic authentication 4 | * Support Slack notifier 5 | * Support HipChat notifier 6 | * More meaningful error messages 7 | 8 | #### v0.1.1 - 2018-12-23 9 | * Better error handling 10 | * Validate configuration for inconsistencies 11 | * Add custom command notifier 12 | 13 | #### v0.1.0 - 2018-12-22 14 | * First MVP release 15 | * Support of Telegram notifier 16 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "adler32" 3 | version = "1.0.3" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "aho-corasick" 8 | version = "0.6.9" 9 | source = "registry+https://github.com/rust-lang/crates.io-index" 10 | dependencies = [ 11 | "memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 12 | ] 13 | 14 | [[package]] 15 | name = "ansi_term" 16 | version = "0.11.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | dependencies = [ 19 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 20 | ] 21 | 22 | [[package]] 23 | name = "arrayvec" 24 | version = "0.4.9" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | dependencies = [ 27 | "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 28 | ] 29 | 30 | [[package]] 31 | name = "atty" 32 | version = "0.2.11" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | dependencies = [ 35 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 36 | "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 37 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 38 | ] 39 | 40 | [[package]] 41 | name = "autocfg" 42 | version = "0.1.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | 45 | [[package]] 46 | name = "backtrace" 47 | version = "0.3.13" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | dependencies = [ 50 | "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 51 | "backtrace-sys 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", 52 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 53 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 54 | "rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", 55 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 56 | ] 57 | 58 | [[package]] 59 | name = "backtrace-sys" 60 | version = "0.1.26" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | dependencies = [ 63 | "cc 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", 64 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 65 | ] 66 | 67 | [[package]] 68 | name = "base64" 69 | version = "0.9.3" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | dependencies = [ 72 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 73 | "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 74 | ] 75 | 76 | [[package]] 77 | name = "base64" 78 | version = "0.10.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | dependencies = [ 81 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 82 | ] 83 | 84 | [[package]] 85 | name = "bitflags" 86 | version = "1.0.4" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | 89 | [[package]] 90 | name = "byteorder" 91 | version = "1.2.7" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | 94 | [[package]] 95 | name = "bytes" 96 | version = "0.4.11" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | dependencies = [ 99 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 100 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 101 | ] 102 | 103 | [[package]] 104 | name = "cc" 105 | version = "1.0.27" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | 108 | [[package]] 109 | name = "cfg-if" 110 | version = "0.1.6" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | 113 | [[package]] 114 | name = "chrono" 115 | version = "0.4.6" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | dependencies = [ 118 | "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 119 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 120 | "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 121 | ] 122 | 123 | [[package]] 124 | name = "clap" 125 | version = "2.32.0" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | dependencies = [ 128 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 129 | "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 130 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 131 | "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 132 | "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 133 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", 135 | ] 136 | 137 | [[package]] 138 | name = "cloudabi" 139 | version = "0.0.3" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | dependencies = [ 142 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 143 | ] 144 | 145 | [[package]] 146 | name = "core-foundation" 147 | version = "0.5.1" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | dependencies = [ 150 | "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 151 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 152 | ] 153 | 154 | [[package]] 155 | name = "core-foundation-sys" 156 | version = "0.5.1" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | dependencies = [ 159 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 160 | ] 161 | 162 | [[package]] 163 | name = "crc32fast" 164 | version = "1.1.2" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | dependencies = [ 167 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 168 | ] 169 | 170 | [[package]] 171 | name = "crossbeam-deque" 172 | version = "0.6.3" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | dependencies = [ 175 | "crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 176 | "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 177 | ] 178 | 179 | [[package]] 180 | name = "crossbeam-epoch" 181 | version = "0.7.0" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | dependencies = [ 184 | "arrayvec 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", 185 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 186 | "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 187 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 188 | "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 189 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 190 | ] 191 | 192 | [[package]] 193 | name = "crossbeam-utils" 194 | version = "0.6.3" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | dependencies = [ 197 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 198 | ] 199 | 200 | [[package]] 201 | name = "dtoa" 202 | version = "0.4.3" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | 205 | [[package]] 206 | name = "encoding_rs" 207 | version = "0.8.13" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | dependencies = [ 210 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 211 | ] 212 | 213 | [[package]] 214 | name = "env_logger" 215 | version = "0.6.0" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | dependencies = [ 218 | "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 219 | "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 220 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 221 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 222 | "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 223 | ] 224 | 225 | [[package]] 226 | name = "failure" 227 | version = "0.1.3" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | dependencies = [ 230 | "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", 231 | "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 232 | ] 233 | 234 | [[package]] 235 | name = "failure_derive" 236 | version = "0.1.3" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | dependencies = [ 239 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 240 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 241 | "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", 242 | "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 243 | ] 244 | 245 | [[package]] 246 | name = "fnv" 247 | version = "1.0.6" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | 250 | [[package]] 251 | name = "foreign-types" 252 | version = "0.3.2" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | dependencies = [ 255 | "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 256 | ] 257 | 258 | [[package]] 259 | name = "foreign-types-shared" 260 | version = "0.1.1" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | 263 | [[package]] 264 | name = "fuchsia-zircon" 265 | version = "0.3.3" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | dependencies = [ 268 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 269 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 270 | ] 271 | 272 | [[package]] 273 | name = "fuchsia-zircon-sys" 274 | version = "0.3.3" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | 277 | [[package]] 278 | name = "futures" 279 | version = "0.1.25" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | 282 | [[package]] 283 | name = "futures-cpupool" 284 | version = "0.1.8" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | dependencies = [ 287 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 288 | "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 289 | ] 290 | 291 | [[package]] 292 | name = "h2" 293 | version = "0.1.14" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | dependencies = [ 296 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 297 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 298 | "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 299 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 300 | "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", 301 | "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 302 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 303 | "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 304 | "string 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 305 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 306 | ] 307 | 308 | [[package]] 309 | name = "heck" 310 | version = "0.3.1" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | dependencies = [ 313 | "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 314 | ] 315 | 316 | [[package]] 317 | name = "hellcheck" 318 | version = "0.1.2" 319 | dependencies = [ 320 | "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 321 | "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 322 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 323 | "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 324 | "hyper 0.12.18 (registry+https://github.com/rust-lang/crates.io-index)", 325 | "hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 326 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 327 | "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 328 | "pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 329 | "reqwest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", 330 | "structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", 331 | "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", 332 | "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 333 | "yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 334 | ] 335 | 336 | [[package]] 337 | name = "http" 338 | version = "0.1.14" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | dependencies = [ 341 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 342 | "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 343 | "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 344 | ] 345 | 346 | [[package]] 347 | name = "httparse" 348 | version = "1.3.3" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | 351 | [[package]] 352 | name = "humantime" 353 | version = "1.2.0" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | dependencies = [ 356 | "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 357 | ] 358 | 359 | [[package]] 360 | name = "hyper" 361 | version = "0.12.18" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | dependencies = [ 364 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 365 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 366 | "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 367 | "h2 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", 368 | "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", 369 | "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 370 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 371 | "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 372 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 373 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 374 | "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 375 | "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 376 | "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 377 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 378 | "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 379 | "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 380 | "tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 381 | "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 382 | "want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 383 | ] 384 | 385 | [[package]] 386 | name = "hyper-tls" 387 | version = "0.3.1" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | dependencies = [ 390 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 391 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 392 | "hyper 0.12.18 (registry+https://github.com/rust-lang/crates.io-index)", 393 | "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 394 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 395 | ] 396 | 397 | [[package]] 398 | name = "idna" 399 | version = "0.1.5" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | dependencies = [ 402 | "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 403 | "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 404 | "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 405 | ] 406 | 407 | [[package]] 408 | name = "indexmap" 409 | version = "1.0.2" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | 412 | [[package]] 413 | name = "iovec" 414 | version = "0.1.2" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | dependencies = [ 417 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 418 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 419 | ] 420 | 421 | [[package]] 422 | name = "itoa" 423 | version = "0.4.3" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | 426 | [[package]] 427 | name = "kernel32-sys" 428 | version = "0.2.2" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | dependencies = [ 431 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 432 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 433 | ] 434 | 435 | [[package]] 436 | name = "lazy_static" 437 | version = "1.2.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | 440 | [[package]] 441 | name = "lazycell" 442 | version = "1.2.1" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | 445 | [[package]] 446 | name = "libc" 447 | version = "0.2.45" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | 450 | [[package]] 451 | name = "libflate" 452 | version = "0.1.19" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | dependencies = [ 455 | "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 456 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 457 | "crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 458 | ] 459 | 460 | [[package]] 461 | name = "linked-hash-map" 462 | version = "0.5.1" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | 465 | [[package]] 466 | name = "lock_api" 467 | version = "0.1.5" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | dependencies = [ 470 | "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 471 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 472 | ] 473 | 474 | [[package]] 475 | name = "log" 476 | version = "0.4.6" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | dependencies = [ 479 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 480 | ] 481 | 482 | [[package]] 483 | name = "matches" 484 | version = "0.1.8" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | 487 | [[package]] 488 | name = "memchr" 489 | version = "2.1.3" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | dependencies = [ 492 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 493 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 494 | ] 495 | 496 | [[package]] 497 | name = "memoffset" 498 | version = "0.2.1" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | 501 | [[package]] 502 | name = "mime" 503 | version = "0.3.12" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | dependencies = [ 506 | "unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 507 | ] 508 | 509 | [[package]] 510 | name = "mime_guess" 511 | version = "2.0.0-alpha.6" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | dependencies = [ 514 | "mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", 515 | "phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", 516 | "phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", 517 | "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 518 | ] 519 | 520 | [[package]] 521 | name = "mio" 522 | version = "0.6.16" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | dependencies = [ 525 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 526 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 527 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 528 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 529 | "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 530 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 531 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 532 | "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 533 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 534 | "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 535 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 536 | ] 537 | 538 | [[package]] 539 | name = "mio-uds" 540 | version = "0.6.7" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | dependencies = [ 543 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 544 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 545 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 546 | ] 547 | 548 | [[package]] 549 | name = "miow" 550 | version = "0.2.1" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | dependencies = [ 553 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 554 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 555 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 556 | "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 557 | ] 558 | 559 | [[package]] 560 | name = "native-tls" 561 | version = "0.2.2" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | dependencies = [ 564 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 565 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 566 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 567 | "openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)", 568 | "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 569 | "openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)", 570 | "schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", 571 | "security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 572 | "security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 573 | "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 574 | ] 575 | 576 | [[package]] 577 | name = "net2" 578 | version = "0.2.33" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | dependencies = [ 581 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 582 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 583 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 584 | ] 585 | 586 | [[package]] 587 | name = "nodrop" 588 | version = "0.1.13" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | 591 | [[package]] 592 | name = "num-integer" 593 | version = "0.1.39" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | dependencies = [ 596 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 597 | ] 598 | 599 | [[package]] 600 | name = "num-traits" 601 | version = "0.2.6" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | 604 | [[package]] 605 | name = "num_cpus" 606 | version = "1.9.0" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | dependencies = [ 609 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 610 | ] 611 | 612 | [[package]] 613 | name = "openssl" 614 | version = "0.10.16" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | dependencies = [ 617 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 618 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 619 | "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 620 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 621 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 622 | "openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)", 623 | ] 624 | 625 | [[package]] 626 | name = "openssl-probe" 627 | version = "0.1.2" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | 630 | [[package]] 631 | name = "openssl-sys" 632 | version = "0.9.40" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | dependencies = [ 635 | "cc 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", 636 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 637 | "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", 638 | "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 639 | ] 640 | 641 | [[package]] 642 | name = "owning_ref" 643 | version = "0.4.0" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | dependencies = [ 646 | "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 647 | ] 648 | 649 | [[package]] 650 | name = "parking_lot" 651 | version = "0.6.4" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | dependencies = [ 654 | "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 655 | "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 656 | ] 657 | 658 | [[package]] 659 | name = "parking_lot_core" 660 | version = "0.3.1" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | dependencies = [ 663 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 664 | "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", 665 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 666 | "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", 667 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 668 | ] 669 | 670 | [[package]] 671 | name = "percent-encoding" 672 | version = "1.0.1" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | 675 | [[package]] 676 | name = "phf" 677 | version = "0.7.23" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | dependencies = [ 680 | "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", 681 | ] 682 | 683 | [[package]] 684 | name = "phf_codegen" 685 | version = "0.7.23" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | dependencies = [ 688 | "phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", 689 | "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", 690 | ] 691 | 692 | [[package]] 693 | name = "phf_generator" 694 | version = "0.7.23" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | dependencies = [ 697 | "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", 698 | "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", 699 | ] 700 | 701 | [[package]] 702 | name = "phf_shared" 703 | version = "0.7.23" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | dependencies = [ 706 | "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 707 | "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 708 | ] 709 | 710 | [[package]] 711 | name = "pkg-config" 712 | version = "0.3.14" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | 715 | [[package]] 716 | name = "pretty_env_logger" 717 | version = "0.3.0" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | dependencies = [ 720 | "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 721 | "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 722 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 723 | ] 724 | 725 | [[package]] 726 | name = "proc-macro2" 727 | version = "0.4.24" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | dependencies = [ 730 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 731 | ] 732 | 733 | [[package]] 734 | name = "quick-error" 735 | version = "1.2.2" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | 738 | [[package]] 739 | name = "quote" 740 | version = "0.6.10" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | dependencies = [ 743 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 744 | ] 745 | 746 | [[package]] 747 | name = "rand" 748 | version = "0.5.5" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | dependencies = [ 751 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 752 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 753 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 754 | "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 755 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 756 | ] 757 | 758 | [[package]] 759 | name = "rand" 760 | version = "0.6.1" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | dependencies = [ 763 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 764 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 765 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 766 | "rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 767 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 768 | "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 769 | "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 770 | "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 771 | "rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 772 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 773 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 774 | ] 775 | 776 | [[package]] 777 | name = "rand_chacha" 778 | version = "0.1.0" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | dependencies = [ 781 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 782 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 783 | ] 784 | 785 | [[package]] 786 | name = "rand_core" 787 | version = "0.2.2" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | dependencies = [ 790 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 791 | ] 792 | 793 | [[package]] 794 | name = "rand_core" 795 | version = "0.3.0" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | 798 | [[package]] 799 | name = "rand_hc" 800 | version = "0.1.0" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | dependencies = [ 803 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 804 | ] 805 | 806 | [[package]] 807 | name = "rand_isaac" 808 | version = "0.1.1" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | dependencies = [ 811 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 812 | ] 813 | 814 | [[package]] 815 | name = "rand_pcg" 816 | version = "0.1.1" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | dependencies = [ 819 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 820 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 821 | ] 822 | 823 | [[package]] 824 | name = "rand_xorshift" 825 | version = "0.1.0" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | dependencies = [ 828 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 829 | ] 830 | 831 | [[package]] 832 | name = "redox_syscall" 833 | version = "0.1.44" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | 836 | [[package]] 837 | name = "redox_termios" 838 | version = "0.1.1" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | dependencies = [ 841 | "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 842 | ] 843 | 844 | [[package]] 845 | name = "regex" 846 | version = "1.1.0" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | dependencies = [ 849 | "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", 850 | "memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 851 | "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 852 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 853 | "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 854 | ] 855 | 856 | [[package]] 857 | name = "regex-syntax" 858 | version = "0.6.4" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | dependencies = [ 861 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 862 | ] 863 | 864 | [[package]] 865 | name = "remove_dir_all" 866 | version = "0.5.1" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | dependencies = [ 869 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 870 | ] 871 | 872 | [[package]] 873 | name = "reqwest" 874 | version = "0.9.5" 875 | source = "registry+https://github.com/rust-lang/crates.io-index" 876 | dependencies = [ 877 | "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", 878 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 879 | "encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)", 880 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 881 | "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", 882 | "hyper 0.12.18 (registry+https://github.com/rust-lang/crates.io-index)", 883 | "hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 884 | "libflate 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", 885 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 886 | "mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", 887 | "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", 888 | "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 889 | "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", 890 | "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 891 | "serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", 892 | "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 893 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 894 | "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 895 | "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 896 | ] 897 | 898 | [[package]] 899 | name = "rustc-demangle" 900 | version = "0.1.11" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | 903 | [[package]] 904 | name = "rustc_version" 905 | version = "0.2.3" 906 | source = "registry+https://github.com/rust-lang/crates.io-index" 907 | dependencies = [ 908 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 909 | ] 910 | 911 | [[package]] 912 | name = "ryu" 913 | version = "0.2.7" 914 | source = "registry+https://github.com/rust-lang/crates.io-index" 915 | 916 | [[package]] 917 | name = "safemem" 918 | version = "0.3.0" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | 921 | [[package]] 922 | name = "schannel" 923 | version = "0.1.14" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | dependencies = [ 926 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 927 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 928 | ] 929 | 930 | [[package]] 931 | name = "scoped-tls" 932 | version = "0.1.2" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | 935 | [[package]] 936 | name = "scopeguard" 937 | version = "0.3.3" 938 | source = "registry+https://github.com/rust-lang/crates.io-index" 939 | 940 | [[package]] 941 | name = "security-framework" 942 | version = "0.2.1" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | dependencies = [ 945 | "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 946 | "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 947 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 948 | "security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 949 | ] 950 | 951 | [[package]] 952 | name = "security-framework-sys" 953 | version = "0.2.1" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | dependencies = [ 956 | "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 957 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 958 | ] 959 | 960 | [[package]] 961 | name = "semver" 962 | version = "0.9.0" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | dependencies = [ 965 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 966 | ] 967 | 968 | [[package]] 969 | name = "semver-parser" 970 | version = "0.7.0" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | 973 | [[package]] 974 | name = "serde" 975 | version = "1.0.82" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | 978 | [[package]] 979 | name = "serde_json" 980 | version = "1.0.33" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | dependencies = [ 983 | "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 984 | "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 985 | "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", 986 | ] 987 | 988 | [[package]] 989 | name = "serde_urlencoded" 990 | version = "0.5.4" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | dependencies = [ 993 | "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 994 | "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 995 | "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", 996 | "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 997 | ] 998 | 999 | [[package]] 1000 | name = "siphasher" 1001 | version = "0.2.3" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | 1004 | [[package]] 1005 | name = "slab" 1006 | version = "0.4.1" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | 1009 | [[package]] 1010 | name = "smallvec" 1011 | version = "0.6.7" 1012 | source = "registry+https://github.com/rust-lang/crates.io-index" 1013 | dependencies = [ 1014 | "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 1015 | ] 1016 | 1017 | [[package]] 1018 | name = "stable_deref_trait" 1019 | version = "1.1.1" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | 1022 | [[package]] 1023 | name = "string" 1024 | version = "0.1.2" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | 1027 | [[package]] 1028 | name = "strsim" 1029 | version = "0.7.0" 1030 | source = "registry+https://github.com/rust-lang/crates.io-index" 1031 | 1032 | [[package]] 1033 | name = "structopt" 1034 | version = "0.2.14" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | dependencies = [ 1037 | "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", 1038 | "structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", 1039 | ] 1040 | 1041 | [[package]] 1042 | name = "structopt-derive" 1043 | version = "0.2.14" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | dependencies = [ 1046 | "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 1047 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 1048 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 1049 | "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "syn" 1054 | version = "0.15.23" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | dependencies = [ 1057 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 1058 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 1059 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1060 | ] 1061 | 1062 | [[package]] 1063 | name = "synstructure" 1064 | version = "0.10.1" 1065 | source = "registry+https://github.com/rust-lang/crates.io-index" 1066 | dependencies = [ 1067 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 1068 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 1069 | "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", 1070 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1071 | ] 1072 | 1073 | [[package]] 1074 | name = "tempfile" 1075 | version = "3.0.5" 1076 | source = "registry+https://github.com/rust-lang/crates.io-index" 1077 | dependencies = [ 1078 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 1079 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 1080 | "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 1081 | "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 1082 | "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 1083 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 1084 | ] 1085 | 1086 | [[package]] 1087 | name = "termcolor" 1088 | version = "1.0.4" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | dependencies = [ 1091 | "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 1092 | ] 1093 | 1094 | [[package]] 1095 | name = "termion" 1096 | version = "1.5.1" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | dependencies = [ 1099 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 1100 | "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 1101 | "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1102 | ] 1103 | 1104 | [[package]] 1105 | name = "textwrap" 1106 | version = "0.10.0" 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" 1108 | dependencies = [ 1109 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 1110 | ] 1111 | 1112 | [[package]] 1113 | name = "thread_local" 1114 | version = "0.3.6" 1115 | source = "registry+https://github.com/rust-lang/crates.io-index" 1116 | dependencies = [ 1117 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 1118 | ] 1119 | 1120 | [[package]] 1121 | name = "time" 1122 | version = "0.1.41" 1123 | source = "registry+https://github.com/rust-lang/crates.io-index" 1124 | dependencies = [ 1125 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 1126 | "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 1127 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 1128 | ] 1129 | 1130 | [[package]] 1131 | name = "tokio" 1132 | version = "0.1.13" 1133 | source = "registry+https://github.com/rust-lang/crates.io-index" 1134 | dependencies = [ 1135 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 1136 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1137 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 1138 | "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 1139 | "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1140 | "tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 1141 | "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 1142 | "tokio-fs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 1143 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 1144 | "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 1145 | "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 1146 | "tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 1147 | "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 1148 | "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 1149 | "tokio-uds 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 1150 | ] 1151 | 1152 | [[package]] 1153 | name = "tokio-codec" 1154 | version = "0.1.1" 1155 | source = "registry+https://github.com/rust-lang/crates.io-index" 1156 | dependencies = [ 1157 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 1158 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1159 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 1160 | ] 1161 | 1162 | [[package]] 1163 | name = "tokio-core" 1164 | version = "0.1.17" 1165 | source = "registry+https://github.com/rust-lang/crates.io-index" 1166 | dependencies = [ 1167 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 1168 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1169 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 1170 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 1171 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 1172 | "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 1173 | "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 1174 | "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 1175 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 1176 | "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 1177 | "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 1178 | ] 1179 | 1180 | [[package]] 1181 | name = "tokio-current-thread" 1182 | version = "0.1.4" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | dependencies = [ 1185 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1186 | "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 1187 | ] 1188 | 1189 | [[package]] 1190 | name = "tokio-executor" 1191 | version = "0.1.5" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | dependencies = [ 1194 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1195 | ] 1196 | 1197 | [[package]] 1198 | name = "tokio-fs" 1199 | version = "0.1.4" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | dependencies = [ 1202 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1203 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 1204 | "tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 1205 | ] 1206 | 1207 | [[package]] 1208 | name = "tokio-io" 1209 | version = "0.1.10" 1210 | source = "registry+https://github.com/rust-lang/crates.io-index" 1211 | dependencies = [ 1212 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 1213 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1214 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 1215 | ] 1216 | 1217 | [[package]] 1218 | name = "tokio-reactor" 1219 | version = "0.1.7" 1220 | source = "registry+https://github.com/rust-lang/crates.io-index" 1221 | dependencies = [ 1222 | "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 1223 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1224 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 1225 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 1226 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 1227 | "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 1228 | "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 1229 | "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 1230 | "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 1231 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 1232 | ] 1233 | 1234 | [[package]] 1235 | name = "tokio-tcp" 1236 | version = "0.1.2" 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" 1238 | dependencies = [ 1239 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 1240 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1241 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 1242 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 1243 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 1244 | "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 1245 | ] 1246 | 1247 | [[package]] 1248 | name = "tokio-threadpool" 1249 | version = "0.1.9" 1250 | source = "registry+https://github.com/rust-lang/crates.io-index" 1251 | dependencies = [ 1252 | "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 1253 | "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 1254 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1255 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 1256 | "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 1257 | "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 1258 | "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 1259 | ] 1260 | 1261 | [[package]] 1262 | name = "tokio-timer" 1263 | version = "0.2.8" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | dependencies = [ 1266 | "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 1267 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1268 | "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 1269 | "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 1270 | ] 1271 | 1272 | [[package]] 1273 | name = "tokio-udp" 1274 | version = "0.1.3" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | dependencies = [ 1277 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 1278 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1279 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 1280 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 1281 | "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1282 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 1283 | "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 1284 | ] 1285 | 1286 | [[package]] 1287 | name = "tokio-uds" 1288 | version = "0.2.4" 1289 | source = "registry+https://github.com/rust-lang/crates.io-index" 1290 | dependencies = [ 1291 | "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 1292 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1293 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 1294 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", 1295 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 1296 | "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", 1297 | "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", 1298 | "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1299 | "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 1300 | "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 1301 | ] 1302 | 1303 | [[package]] 1304 | name = "try-lock" 1305 | version = "0.2.2" 1306 | source = "registry+https://github.com/rust-lang/crates.io-index" 1307 | 1308 | [[package]] 1309 | name = "ucd-util" 1310 | version = "0.1.3" 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" 1312 | 1313 | [[package]] 1314 | name = "unicase" 1315 | version = "1.4.2" 1316 | source = "registry+https://github.com/rust-lang/crates.io-index" 1317 | dependencies = [ 1318 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 1319 | ] 1320 | 1321 | [[package]] 1322 | name = "unicase" 1323 | version = "2.2.0" 1324 | source = "registry+https://github.com/rust-lang/crates.io-index" 1325 | dependencies = [ 1326 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 1327 | ] 1328 | 1329 | [[package]] 1330 | name = "unicode-bidi" 1331 | version = "0.3.4" 1332 | source = "registry+https://github.com/rust-lang/crates.io-index" 1333 | dependencies = [ 1334 | "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 1335 | ] 1336 | 1337 | [[package]] 1338 | name = "unicode-normalization" 1339 | version = "0.1.7" 1340 | source = "registry+https://github.com/rust-lang/crates.io-index" 1341 | 1342 | [[package]] 1343 | name = "unicode-segmentation" 1344 | version = "1.2.1" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | 1347 | [[package]] 1348 | name = "unicode-width" 1349 | version = "0.1.5" 1350 | source = "registry+https://github.com/rust-lang/crates.io-index" 1351 | 1352 | [[package]] 1353 | name = "unicode-xid" 1354 | version = "0.1.0" 1355 | source = "registry+https://github.com/rust-lang/crates.io-index" 1356 | 1357 | [[package]] 1358 | name = "unreachable" 1359 | version = "1.0.0" 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" 1361 | dependencies = [ 1362 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 1363 | ] 1364 | 1365 | [[package]] 1366 | name = "url" 1367 | version = "1.7.2" 1368 | source = "registry+https://github.com/rust-lang/crates.io-index" 1369 | dependencies = [ 1370 | "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 1371 | "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 1372 | "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 1373 | ] 1374 | 1375 | [[package]] 1376 | name = "utf8-ranges" 1377 | version = "1.0.2" 1378 | source = "registry+https://github.com/rust-lang/crates.io-index" 1379 | 1380 | [[package]] 1381 | name = "uuid" 1382 | version = "0.7.1" 1383 | source = "registry+https://github.com/rust-lang/crates.io-index" 1384 | dependencies = [ 1385 | "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", 1386 | ] 1387 | 1388 | [[package]] 1389 | name = "vcpkg" 1390 | version = "0.2.6" 1391 | source = "registry+https://github.com/rust-lang/crates.io-index" 1392 | 1393 | [[package]] 1394 | name = "vec_map" 1395 | version = "0.8.1" 1396 | source = "registry+https://github.com/rust-lang/crates.io-index" 1397 | 1398 | [[package]] 1399 | name = "version_check" 1400 | version = "0.1.5" 1401 | source = "registry+https://github.com/rust-lang/crates.io-index" 1402 | 1403 | [[package]] 1404 | name = "void" 1405 | version = "1.0.2" 1406 | source = "registry+https://github.com/rust-lang/crates.io-index" 1407 | 1408 | [[package]] 1409 | name = "want" 1410 | version = "0.0.6" 1411 | source = "registry+https://github.com/rust-lang/crates.io-index" 1412 | dependencies = [ 1413 | "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 1414 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 1415 | "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 1416 | ] 1417 | 1418 | [[package]] 1419 | name = "winapi" 1420 | version = "0.2.8" 1421 | source = "registry+https://github.com/rust-lang/crates.io-index" 1422 | 1423 | [[package]] 1424 | name = "winapi" 1425 | version = "0.3.6" 1426 | source = "registry+https://github.com/rust-lang/crates.io-index" 1427 | dependencies = [ 1428 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1429 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1430 | ] 1431 | 1432 | [[package]] 1433 | name = "winapi-build" 1434 | version = "0.1.1" 1435 | source = "registry+https://github.com/rust-lang/crates.io-index" 1436 | 1437 | [[package]] 1438 | name = "winapi-i686-pc-windows-gnu" 1439 | version = "0.4.0" 1440 | source = "registry+https://github.com/rust-lang/crates.io-index" 1441 | 1442 | [[package]] 1443 | name = "winapi-util" 1444 | version = "0.1.1" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | dependencies = [ 1447 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 1448 | ] 1449 | 1450 | [[package]] 1451 | name = "winapi-x86_64-pc-windows-gnu" 1452 | version = "0.4.0" 1453 | source = "registry+https://github.com/rust-lang/crates.io-index" 1454 | 1455 | [[package]] 1456 | name = "wincolor" 1457 | version = "1.0.1" 1458 | source = "registry+https://github.com/rust-lang/crates.io-index" 1459 | dependencies = [ 1460 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 1461 | "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1462 | ] 1463 | 1464 | [[package]] 1465 | name = "ws2_32-sys" 1466 | version = "0.2.1" 1467 | source = "registry+https://github.com/rust-lang/crates.io-index" 1468 | dependencies = [ 1469 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 1470 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1471 | ] 1472 | 1473 | [[package]] 1474 | name = "yaml-rust" 1475 | version = "0.4.2" 1476 | source = "registry+https://github.com/rust-lang/crates.io-index" 1477 | dependencies = [ 1478 | "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 1479 | ] 1480 | 1481 | [metadata] 1482 | "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" 1483 | "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" 1484 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 1485 | "checksum arrayvec 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d18513977c2d8261c448511c5c53dc66b26dfccbc3d4446672dea1e71a7d8a26" 1486 | "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" 1487 | "checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" 1488 | "checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" 1489 | "checksum backtrace-sys 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3fcce89e5ad5c8949caa9434501f7b55415b3e7ad5270cb88c75a8d35e8f1279" 1490 | "checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2" 1491 | "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" 1492 | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" 1493 | "checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" 1494 | "checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" 1495 | "checksum cc 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "155ed195f7bd722d1dfeb30365b9d0c1f6a078fa7ca4014497e5935d90993d6f" 1496 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" 1497 | "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" 1498 | "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" 1499 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 1500 | "checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980" 1501 | "checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" 1502 | "checksum crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e91d5240c6975ef33aeb5f148f35275c25eda8e8a5f95abe421978b05b8bf192" 1503 | "checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" 1504 | "checksum crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f10a4f8f409aaac4b16a5474fb233624238fcdeefb9ba50d5ea059aab63ba31c" 1505 | "checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a" 1506 | "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" 1507 | "checksum encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1a8fa54e6689eb2549c4efed8d00d7f3b2b994a064555b0e8df4ae3764bcc4be" 1508 | "checksum env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afb070faf94c85d17d50ca44f6ad076bce18ae92f0037d350947240a36e9d42e" 1509 | "checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7" 1510 | "checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596" 1511 | "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" 1512 | "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 1513 | "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 1514 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 1515 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 1516 | "checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b" 1517 | "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" 1518 | "checksum h2 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1ac030ae20dee464c5d0f36544d8b914a6bc606da44a57e052d2b0f5dae129e0" 1519 | "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" 1520 | "checksum http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "02096a6d2c55e63f7fcb800690e4f889a25f6ec342e3adb4594e293b625215ab" 1521 | "checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" 1522 | "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" 1523 | "checksum hyper 0.12.18 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd7729fc83d88353415f6816fd4bb00897aa47c7f1506b69060e74e6e3d8e8b" 1524 | "checksum hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cd73f14ad370d3b4d4b7dce08f69b81536c82e39fcc89731930fe5788cd661" 1525 | "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" 1526 | "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" 1527 | "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" 1528 | "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" 1529 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 1530 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" 1531 | "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" 1532 | "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74" 1533 | "checksum libflate 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "bff3ac7d6f23730d3b533c35ed75eef638167634476a499feef16c428d74b57b" 1534 | "checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" 1535 | "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" 1536 | "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" 1537 | "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 1538 | "checksum memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e1dd4eaac298c32ce07eb6ed9242eda7d82955b9170b7d6db59b2e02cc63fcb8" 1539 | "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" 1540 | "checksum mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "0a907b83e7b9e987032439a387e187119cddafc92d5c2aaeb1d92580a793f630" 1541 | "checksum mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed" 1542 | "checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" 1543 | "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" 1544 | "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 1545 | "checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" 1546 | "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" 1547 | "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" 1548 | "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" 1549 | "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" 1550 | "checksum num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a69d464bdc213aaaff628444e99578ede64e9c854025aa43b9796530afa9238" 1551 | "checksum openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)" = "ec7bd7ca4cce6dbdc77e7c1230682740d307d1218a87fb0349a571272be749f9" 1552 | "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" 1553 | "checksum openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)" = "1bb974e77de925ef426b6bc82fce15fd45bdcbeb5728bffcfc7cdeeb7ce1c2d6" 1554 | "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" 1555 | "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" 1556 | "checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" 1557 | "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 1558 | "checksum phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "cec29da322b242f4c3098852c77a0ca261c9c01b806cae85a5572a1eb94db9a6" 1559 | "checksum phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "7d187f00cd98d5afbcd8898f6cf181743a449162aeb329dcd2f3849009e605ad" 1560 | "checksum phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "03dc191feb9b08b0dc1330d6549b795b9d81aec19efe6b4a45aec8d4caee0c4b" 1561 | "checksum phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "b539898d22d4273ded07f64a05737649dc69095d92cb87c7097ec68e3f150b93" 1562 | "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" 1563 | "checksum pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df8b3f4e0475def7d9c2e5de8e5a1306949849761e107b360d03e98eafaffd61" 1564 | "checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" 1565 | "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" 1566 | "checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" 1567 | "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" 1568 | "checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a" 1569 | "checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a" 1570 | "checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" 1571 | "checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" 1572 | "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 1573 | "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 1574 | "checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" 1575 | "checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3" 1576 | "checksum redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "a84bcd297b87a545980a2d25a0beb72a1f490c31f0a9fde52fca35bfbb1ceb70" 1577 | "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 1578 | "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" 1579 | "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1" 1580 | "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" 1581 | "checksum reqwest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ab52e462d1e15891441aeefadff68bdea005174328ce3da0a314f2ad313ec837" 1582 | "checksum rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "01b90379b8664dd83460d59bdc5dd1fd3172b8913788db483ed1325171eab2f7" 1583 | "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 1584 | "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" 1585 | "checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" 1586 | "checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56" 1587 | "checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" 1588 | "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 1589 | "checksum security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "697d3f3c23a618272ead9e1fb259c1411102b31c6af8b93f1d64cca9c3b0e8e0" 1590 | "checksum security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab01dfbe5756785b5b4d46e0289e5a18071dfa9a7c2b24213ea00b9ef9b665bf" 1591 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 1592 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 1593 | "checksum serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "6fa52f19aee12441d5ad11c9a00459122bd8f98707cadf9778c540674f1935b6" 1594 | "checksum serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "c37ccd6be3ed1fdf419ee848f7c758eb31b054d7cd3ae3600e3bae0adf569811" 1595 | "checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2" 1596 | "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" 1597 | "checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" 1598 | "checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db" 1599 | "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" 1600 | "checksum string 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98998cced76115b1da46f63388b909d118a37ae0be0f82ad35773d4a4bc9d18d" 1601 | "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" 1602 | "checksum structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "670ad348dc73012fcf78c71f06f9d942232cdd4c859d4b6975e27836c3efc0c3" 1603 | "checksum structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ef98172b1a00b0bec738508d3726540edcbd186d50dfd326f2b1febbb3559f04" 1604 | "checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc" 1605 | "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" 1606 | "checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2" 1607 | "checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" 1608 | "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" 1609 | "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" 1610 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 1611 | "checksum time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "847da467bf0db05882a9e2375934a8a55cffdc9db0d128af1518200260ba1f6c" 1612 | "checksum tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "a7817d4c98cc5be21360b3b37d6036fe9b7aefa5b7a201b7b16ff33423822f7d" 1613 | "checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" 1614 | "checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" 1615 | "checksum tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "331c8acc267855ec06eb0c94618dcbbfea45bed2d20b77252940095273fb58f6" 1616 | "checksum tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c117b6cf86bb730aab4834f10df96e4dd586eff2c3c27d3781348da49e255bde" 1617 | "checksum tokio-fs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "60ae25f6b17d25116d2cba342083abe5255d3c2c79cb21ea11aa049c53bf7c75" 1618 | "checksum tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "7392fe0a70d5ce0c882c4778116c519bd5dbaa8a7c3ae3d04578b3afafdcda21" 1619 | "checksum tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "502b625acb4ee13cbb3b90b8ca80e0addd263ddacf6931666ef751e610b07fb5" 1620 | "checksum tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ad235e9dadd126b2d47f6736f65aa1fdcd6420e66ca63f44177bc78df89f912" 1621 | "checksum tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "56c5556262383032878afad66943926a1d1f0967f17e94bd7764ceceb3b70e7f" 1622 | "checksum tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4f37f0111d76cc5da132fe9bc0590b9b9cfd079bc7e75ac3846278430a299ff8" 1623 | "checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" 1624 | "checksum tokio-uds 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "99ce87382f6c1a24b513a72c048b2c8efe66cb5161c9061d00bee510f08dc168" 1625 | "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" 1626 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" 1627 | "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" 1628 | "checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90" 1629 | "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1630 | "checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" 1631 | "checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" 1632 | "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" 1633 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 1634 | "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" 1635 | "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" 1636 | "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" 1637 | "checksum uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dab5c5526c5caa3d106653401a267fed923e7046f35895ffcb5ca42db64942e6" 1638 | "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" 1639 | "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 1640 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" 1641 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 1642 | "checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" 1643 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1644 | "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" 1645 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1646 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1647 | "checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" 1648 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1649 | "checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" 1650 | "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 1651 | "checksum yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95acf0db5515d07da9965ec0e0ba6cc2d825e2caeb7303b66ca441729801254e" 1652 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hellcheck" 3 | version = "0.1.2" 4 | authors = ["Sergey Potapov "] 5 | edition = "2018" 6 | description = "HTTP health checker" 7 | keywords = ["healthcheck", "http", "monitor", "notification", "health"] 8 | license = "MIT" 9 | repository = "https://github.com/greyblake/hellcheck" 10 | homepage = "https://github.com/greyblake/hellcheck" 11 | categories = ["web-programming", "network-programming"] 12 | 13 | [dependencies] 14 | hyper = "0.12.18" 15 | hyper-tls = "0.3.1" 16 | openssl-probe = "0.1.2" 17 | tokio-timer = "0.2.8" 18 | futures = "0.1.25" 19 | tokio-core = "0.1.17" 20 | humantime = "1.2.0" 21 | yaml-rust = "0.4.2" 22 | failure = "0.1.3" 23 | reqwest = "0.9.5" 24 | base64 = "0.10.0" 25 | structopt = "0.2.14" 26 | log = "0.4.6" 27 | pretty_env_logger = "0.3" 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hellcheck 2 | 3 | HTTP health checker. 4 | 5 | 6 | * [Installation](#installation) 7 | * [Install with cargo](#install-with-cargo) 8 | * [Configuration](#configuration) 9 | * [Basic configuration example](#basic-configuration-example) 10 | * [Notifiers](#notifiers) 11 | * [Slack notifier](#slack-notifier) 12 | * [Telegram notifier](#telegram-notifier) 13 | * [HipChat notifier](#hipchat-notifier) 14 | * [Command notifier](#command-notifier) 15 | * [Start](#start) 16 | * [Roadmap](#roadmap) 17 | * [License](#license) 18 | * [Contributors](#contributors) 19 | 20 | 21 | ## Installation 22 | 23 | ### Install with cargo 24 | 25 | Install system dependencies. 26 | 27 | On Debian/Ubuntu: 28 | 29 | ```sh 30 | apt-get install libssl-dev pkg-config 31 | ``` 32 | 33 | Install hellcheck crate: 34 | 35 | ```sh 36 | cargo install hellcheck 37 | ``` 38 | 39 | ## Configuration 40 | 41 | ### Basic configuration example 42 | 43 | Configuration file `hellcheck.yml` may have the following format: 44 | 45 | ```yaml 46 | # Declare what has to be watched 47 | checkers: 48 | example: 49 | url: https://www.example.com 50 | notifiers: [my_team] 51 | localhost8000: 52 | url: http://localhost:8000 53 | interval: 1500ms 54 | notifiers: [my_team, sound_alarm] 55 | basic_auth: 56 | username: "foo" 57 | password: "bar" 58 | 59 | # Declare notification channels 60 | notifiers: 61 | my_team: 62 | type: slack 63 | token: 64 | sound_alarm: 65 | type: command 66 | command: ["./custom.sh", "arg1", "arg2"] 67 | ``` 68 | 69 | ### Notifiers 70 | 71 | #### Slack notifier 72 | 73 | Create an [incoming webhook](https://api.slack.com/incoming-webhooks) in Slack. 74 | Then define your notifier with type `slack` and `webhook_url`: 75 | 76 | ```yaml 77 | notifiers: 78 | notifier_name: 79 | type: slack 80 | webhook_url: 81 | ``` 82 | 83 | #### Telegram notifier 84 | 85 | For telegram notifier you have to create a bot with [BotFather](https://telegram.me/BotFather) and 86 | obtain the bot token. 87 | 88 | Chat ID can be found out with [GetidsBot](https://telegram.me/getidsbot). 89 | 90 | ```yaml 91 | notifiers: 92 | notifier_name: 93 | type: telegram 94 | token: 95 | chat_id: 96 | ``` 97 | 98 | #### HipChat notifier 99 | 100 | ```yaml 101 | notifiers: 102 | notifier_name: 103 | type: hipchat 104 | base_url: https://hipchat.com 105 | token: 106 | room_id: 107 | ``` 108 | 109 | #### Command notifier 110 | 111 | Command notifier allows you to invoke any shell command or custom script as notifier. 112 | 113 | Example: 114 | 115 | ```yaml 116 | notifiers: 117 | custom: 118 | type: command 119 | command: ["/path/to/custom-notifier.sh", "arg1", "arg2"] 120 | ``` 121 | 122 | Within the script the following environment variables are accessible: 123 | 124 | * `HELLCHECK_ID` - checker id 125 | * `HELLCHECK_URL` - checker URL 126 | * `HELLCHECK_OK` 127 | * `true` - when service is up 128 | * `false` - when service is down 129 | 130 | 131 | ## Start 132 | 133 | Assuming, you have `./hellcheck.yml` in your current directory, this will start monitoring of the services, 134 | described in `checkers` configuration sections: 135 | 136 | ``` 137 | hellcheck watch --file ./hellcheck.yml 138 | ``` 139 | 140 | ## Roadmap 141 | 142 | * [x] Support notifiers 143 | * [x] Command customer notifier 144 | * [x] Telegram 145 | * [x] HipChat 146 | * [x] Slack 147 | * [ ] Checkers 148 | * [x] Custom intervals 149 | * [ ] Verify body (presence of some given text) 150 | * [ ] Custom OKish HTTP status 151 | * [x] Use structopt/clap for nice command line interface 152 | * [ ] Implement `hellcheck test` command to test notifiers 153 | * [x] Configure CI 154 | * [x] Run build/tests 155 | * [x] Setup clippy lint 156 | * [x] Setup rusmft 157 | * [x] Ensure endpoints with http basic authentication can be health checked 158 | * [ ] Inject credentials with env variables into yaml file 159 | * [ ] Allow customizable messages for notifiers 160 | * [ ] Allow custom scripts as checkers 161 | * [ ] Make pretty colorized output for console 162 | * [ ] Validate for unexpected panics in the code (unwrap, panic, expect, etc..) 163 | * [ ] Add logging 164 | 165 | ## License 166 | 167 | [MIT](https://github.com/greyblake/whatlang-rs/blob/master/LICENSE) © [Sergey Potapov](http://greyblake.com/) 168 | 169 | ## Contributors 170 | 171 | - [greyblake](https://github.com/greyblake) Potapov Sergey - creator, maintainer. 172 | -------------------------------------------------------------------------------- /ci/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Make the entire script fail if one of the commands fails 4 | set -ex 5 | 6 | # Formatting 7 | cargo fmt -- --check 8 | 9 | # Clippy 10 | touch ./*/*/*.rs 11 | cargo clippy -- -D warnings 12 | 13 | # Tests 14 | cargo test 15 | -------------------------------------------------------------------------------- /src/cli/helpers.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::prelude::*; 3 | use std::io::BufReader; 4 | 5 | use crate::config::parser::parse_config; 6 | use crate::config::validator::validate_config; 7 | use crate::config::FileConfig; 8 | 9 | pub fn load_config(file_path: &str) -> FileConfig { 10 | let file = File::open(file_path).unwrap_or_else(|err| { 11 | eprintln!("ERROR: Failed to open file {}.\n{}", file_path, err); 12 | std::process::exit(1); 13 | }); 14 | 15 | let mut buf_reader = BufReader::new(file); 16 | let mut content = String::new(); 17 | buf_reader 18 | .read_to_string(&mut content) 19 | .expect("Failed to read from file"); 20 | 21 | let config = match parse_config(&content) { 22 | Ok(config) => config, 23 | Err(err) => { 24 | eprintln!("ERROR: {}", err); 25 | std::process::exit(1); 26 | } 27 | }; 28 | 29 | match validate_config(&config) { 30 | Ok(warnings) => { 31 | for warning in warnings { 32 | eprintln!("WARNING: {}", warning); 33 | } 34 | } 35 | Err(err) => { 36 | eprintln!("ERROR: {}", err); 37 | std::process::exit(1); 38 | } 39 | } 40 | 41 | config 42 | } 43 | -------------------------------------------------------------------------------- /src/cli/mod.rs: -------------------------------------------------------------------------------- 1 | use structopt::clap::AppSettings; 2 | use structopt::StructOpt; 3 | 4 | mod helpers; 5 | mod test; 6 | mod watch; 7 | 8 | use test::TestOpts; 9 | use watch::WatchOpts; 10 | 11 | #[derive(StructOpt, Debug)] 12 | #[structopt(raw(setting = "AppSettings::InferSubcommands"))] 13 | enum Command { 14 | /// Start watcher 15 | #[structopt(name = "watch")] 16 | Watch(WatchOpts), 17 | 18 | /// Test checkers and notifiers 19 | #[structopt(name = "test")] 20 | Test(TestOpts), 21 | } 22 | 23 | pub fn run() { 24 | let command = Command::from_args(); 25 | 26 | match command { 27 | Command::Watch(opts) => watch::run(opts), 28 | Command::Test(opts) => test::run(opts), 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/cli/test.rs: -------------------------------------------------------------------------------- 1 | use structopt::StructOpt; 2 | 3 | #[derive(StructOpt, Debug)] 4 | pub struct TestOpts { 5 | #[structopt(short = "f", long = "file")] 6 | file: String, 7 | } 8 | 9 | pub fn run(_opts: TestOpts) { 10 | eprintln!("test command is not implemented yet"); 11 | } 12 | -------------------------------------------------------------------------------- /src/cli/watch.rs: -------------------------------------------------------------------------------- 1 | use structopt::StructOpt; 2 | 3 | use std::sync::mpsc; 4 | 5 | use crate::cli::helpers::load_config; 6 | use crate::reactor::StateMessage; 7 | 8 | #[derive(StructOpt, Debug)] 9 | pub struct WatchOpts { 10 | #[structopt(short = "f", long = "file")] 11 | file: String, 12 | } 13 | 14 | pub fn run(opts: WatchOpts) { 15 | let config = load_config(&opts.file); 16 | 17 | let (sender, receiver) = mpsc::channel::(); 18 | crate::reactor::spawn(receiver, config.clone()); 19 | crate::watcher::run(config, sender); 20 | } 21 | -------------------------------------------------------------------------------- /src/config/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod parser; 2 | mod types; 3 | pub mod validator; 4 | 5 | pub use self::types::{ 6 | BasicAuth, CheckerConfig, CommandNotifierConfig, FileConfig, HipchatNotifierConfig, Notifier, 7 | NotifierConfig, SlackNotifierConfig, TelegramNotifierConfig, 8 | }; 9 | -------------------------------------------------------------------------------- /src/config/parser/checkers/http.rs: -------------------------------------------------------------------------------- 1 | use hyper::Uri; 2 | use yaml_rust::yaml::Yaml; 3 | 4 | use std::time::Duration; 5 | 6 | use super::common::{parse_key, parse_yaml_to_string, parse_yaml_to_vec, Result}; 7 | use crate::config::{BasicAuth, CheckerConfig}; 8 | use crate::error::ConfigError; 9 | 10 | pub fn parse(key: &Yaml, body: &Yaml) -> Result { 11 | let id = parse_key(key)?; 12 | let mut notifiers: Vec = vec![]; 13 | let mut basic_auth: Option = None; 14 | 15 | // Default interval is 10 sec 16 | let mut interval = Duration::new(10, 0); 17 | let mut url_opt: Option = None; 18 | 19 | match body { 20 | Yaml::Hash(hash) => { 21 | for (attr_yaml_key, attr_yaml_val) in hash { 22 | let attr_key = parse_key(&attr_yaml_key)?; 23 | 24 | match attr_key.as_ref() { 25 | "interval" => { 26 | let attr_val = parse_key(&attr_yaml_val)?; 27 | match attr_val.parse::() { 28 | Ok(val) => { 29 | interval = val.into(); 30 | } 31 | Err(_) => { 32 | let e = ConfigError::InvalidCheckerInterval { 33 | interval: attr_val, 34 | checker_id: id, 35 | }; 36 | return Err(e); 37 | } 38 | } 39 | } 40 | "url" => { 41 | let attr_val = parse_yaml_to_string(&attr_yaml_val)?; 42 | match attr_val.parse::() { 43 | Ok(url) => { 44 | url_opt = Some(url); 45 | } 46 | Err(_) => { 47 | let e = ConfigError::InvalidCheckerUrl { 48 | checker_id: id, 49 | url: attr_val, 50 | }; 51 | return Err(e); 52 | } 53 | }; 54 | } 55 | "notifiers" => { 56 | notifiers = parse_yaml_to_vec(&attr_yaml_val)?; 57 | } 58 | "basic_auth" => { 59 | let raw_basic_auth = parse_basic_auth(&id, &attr_yaml_val)?; 60 | basic_auth = Some(raw_basic_auth); 61 | } 62 | _ => { 63 | let err = ConfigError::UnknownCheckerAttribute { 64 | checker_id: id.clone(), 65 | attr_name: attr_key, 66 | }; 67 | return Err(err); 68 | } 69 | } 70 | } 71 | } 72 | _ => { 73 | let message = format!("`checkers.{}` must be a hash. Got {:?}", id, body); 74 | return Err(ConfigError::GeneralError { message }); 75 | } 76 | }; 77 | 78 | let url = url_opt.ok_or(ConfigError::FieldMissing { 79 | path: format!("checkers.{}.url", id), 80 | })?; 81 | 82 | let cf = CheckerConfig { 83 | id, 84 | interval, 85 | url, 86 | basic_auth, 87 | notifiers, 88 | }; 89 | Ok(cf) 90 | } 91 | 92 | fn parse_basic_auth(checker_id: &str, val: &Yaml) -> Result { 93 | match val { 94 | Yaml::Hash(hash) => { 95 | let mut username_opt: Option = None; 96 | let mut password_opt: Option = None; 97 | 98 | for (attr_yaml_key, attr_yaml_val) in hash { 99 | let attr_key = parse_key(&attr_yaml_key)?; 100 | 101 | match attr_key.as_ref() { 102 | "username" => { 103 | let attr_val = parse_key(&attr_yaml_val)?; 104 | username_opt = Some(attr_val); 105 | } 106 | "password" => { 107 | let attr_val = parse_key(&attr_yaml_val)?; 108 | password_opt = Some(attr_val); 109 | } 110 | _ => { 111 | let message = format!( 112 | "Unknown attribute checkers.{}.basic_auth.{}", 113 | checker_id, attr_key 114 | ); 115 | return Err(ConfigError::GeneralError { message }); 116 | } 117 | } 118 | } 119 | 120 | let username = username_opt.ok_or(ConfigError::FieldMissing { 121 | path: format!("checkers.{}.basic_auth.username", checker_id), 122 | })?; 123 | 124 | let password = password_opt.ok_or(ConfigError::FieldMissing { 125 | path: format!("checkers.{}.basic_auth.password", checker_id), 126 | })?; 127 | 128 | let basic_auth = BasicAuth { username, password }; 129 | Ok(basic_auth) 130 | } 131 | _ => { 132 | let message = format!("basic_auth must be a hash. Got {:?}", val); 133 | Err(ConfigError::GeneralError { message }) 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/config/parser/checkers/mod.rs: -------------------------------------------------------------------------------- 1 | use yaml_rust::yaml::Yaml; 2 | 3 | use crate::config::CheckerConfig; 4 | use crate::error::ConfigError; 5 | 6 | use super::common; 7 | use super::common::Result; 8 | 9 | mod http; 10 | 11 | pub fn parse_checkers(checker_configs: &Yaml) -> Result> { 12 | let mut checkers = vec![]; 13 | 14 | match checker_configs { 15 | Yaml::Hash(hash) => { 16 | for (yaml_key, val) in hash.iter() { 17 | let checker = http::parse(yaml_key, val)?; 18 | checkers.push(checker); 19 | } 20 | } 21 | _ => { 22 | let message = format!("`checkers` must be a hash. Got {:?}", checker_configs); 23 | return Err(ConfigError::GeneralError { message }); 24 | } 25 | } 26 | 27 | Ok(checkers) 28 | } 29 | -------------------------------------------------------------------------------- /src/config/parser/common.rs: -------------------------------------------------------------------------------- 1 | use yaml_rust::yaml::Yaml; 2 | 3 | use crate::error::ConfigError; 4 | 5 | pub type Result = std::result::Result; 6 | 7 | pub fn parse_key(key: &Yaml) -> Result { 8 | match key { 9 | Yaml::String(s) => Ok(s.to_owned()), 10 | Yaml::Integer(num) => Ok(num.to_string()), 11 | _ => { 12 | let message = format!("Key must be a string. Got {:?}", key); 13 | Err(ConfigError::GeneralError { message }) 14 | } 15 | } 16 | } 17 | 18 | pub fn parse_yaml_to_string(val: &Yaml) -> Result { 19 | match val { 20 | Yaml::String(s) => Ok(s.to_owned()), 21 | Yaml::Integer(num) => Ok(num.to_string()), 22 | _ => { 23 | let message = format!("Expected a string. Got {:?}", val); 24 | Err(ConfigError::GeneralError { message }) 25 | } 26 | } 27 | } 28 | 29 | pub fn parse_yaml_to_vec(val: &Yaml) -> Result> { 30 | let mut items: Vec = vec![]; 31 | 32 | match val { 33 | Yaml::Array(arr) => { 34 | for yaml_item in arr.iter() { 35 | let item = parse_yaml_to_string(yaml_item)?; 36 | items.push(item); 37 | } 38 | } 39 | _ => { 40 | let message = format!("Value must be an array. Got {:?}", val); 41 | return Err(ConfigError::GeneralError { message }); 42 | } 43 | } 44 | 45 | Ok(items) 46 | } 47 | 48 | pub fn parse_yaml_to_hash(val: &Yaml) -> Result<&yaml_rust::yaml::Hash> { 49 | match val { 50 | Yaml::Hash(hash) => Ok(hash), 51 | _ => { 52 | let message = format!("Value must be a hash. Got {:?}", val); 53 | Err(ConfigError::GeneralError { message }) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/config/parser/mod.rs: -------------------------------------------------------------------------------- 1 | use yaml_rust::{yaml::Yaml, YamlLoader}; 2 | 3 | use crate::config::FileConfig; 4 | use crate::error::ConfigError; 5 | 6 | mod checkers; 7 | mod common; 8 | mod notifiers; 9 | 10 | use self::common::{parse_key, Result}; 11 | 12 | pub fn parse_config(yaml: &str) -> Result { 13 | let mut checkers = vec![]; 14 | let mut notifiers = vec![]; 15 | 16 | let docs = YamlLoader::load_from_str(yaml).map_err(|err| ConfigError::InvalidYaml { err })?; 17 | 18 | for doc in docs.iter() { 19 | match doc { 20 | Yaml::Hash(root) => { 21 | for (yaml_key, val) in root.iter() { 22 | let key = parse_key(yaml_key)?; 23 | 24 | match key.as_ref() { 25 | "checkers" => { 26 | checkers = checkers::parse_checkers(val)?; 27 | } 28 | "notifiers" => { 29 | notifiers = notifiers::parse_notifiers(val)?; 30 | } 31 | _ => { 32 | return Err(ConfigError::UnkownRootElement { name: key }); 33 | } 34 | }; 35 | } 36 | } 37 | _ => { 38 | return Err(ConfigError::GeneralError { 39 | message: "Root element of YAML must be Hash".to_owned(), 40 | }); 41 | } 42 | } 43 | } 44 | 45 | Ok(FileConfig { 46 | checkers, 47 | notifiers, 48 | }) 49 | } 50 | 51 | #[cfg(test)] 52 | mod tests { 53 | use super::*; 54 | 55 | #[test] 56 | fn test_valid_yaml() { 57 | let yaml = r#" 58 | checkers: 59 | greyblake: 60 | url: https://www.greyblake.com/ 61 | interval: 1s 62 | notifiers: [telebot] 63 | 64 | notifiers: 65 | telebot: 66 | type: telegram 67 | token: TOKENGOESHERE 68 | chat_id: 8677112 69 | "#; 70 | let config = parse_config(yaml).unwrap(); 71 | assert_eq!(config.checkers.len(), 1); 72 | assert_eq!(config.notifiers.len(), 1); 73 | } 74 | 75 | #[test] 76 | fn test_token_is_missing_for_telegram_notifier() { 77 | let yaml = r#" 78 | checkers: 79 | greyblake: 80 | url: https://www.greyblake.com/ 81 | interval: 1s 82 | notifiers: [telebot] 83 | 84 | notifiers: 85 | telebot: 86 | type: telegram 87 | chat_id: 8677112 88 | "#; 89 | let err = parse_config(yaml).unwrap_err(); 90 | assert_eq!( 91 | err, 92 | ConfigError::FieldMissing { 93 | path: "notifiers.telebot.token".to_owned() 94 | } 95 | ) 96 | } 97 | 98 | #[test] 99 | fn test_checker_has_no_url() { 100 | let yaml = r#" 101 | checkers: 102 | greyblake: 103 | interval: 1s 104 | notifiers: [telebot] 105 | 106 | notifiers: 107 | telebot: 108 | type: telegram 109 | token: TOKEN 110 | chat_id: 8677112 111 | "#; 112 | let err = parse_config(yaml).unwrap_err(); 113 | assert_eq!( 114 | err, 115 | ConfigError::FieldMissing { 116 | path: "checkers.greyblake.url".to_owned() 117 | } 118 | ) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/config/parser/notifiers/command.rs: -------------------------------------------------------------------------------- 1 | use yaml_rust::yaml::Yaml; 2 | 3 | use super::common::{parse_key, parse_yaml_to_vec, Result}; 4 | use crate::config::CommandNotifierConfig; 5 | use crate::error::ConfigError; 6 | 7 | pub fn parse(id: &str, body: &Yaml) -> Result { 8 | let mut config_opt: Option = None; 9 | 10 | match body { 11 | Yaml::Hash(hash) => { 12 | for (attr_yaml_key, attr_yaml_val) in hash { 13 | let attr_key = parse_key(&attr_yaml_key)?; 14 | 15 | match attr_key.as_ref() { 16 | "type" => (), 17 | "command" => match parse_yaml_to_vec(&attr_yaml_val) { 18 | Ok(vals) => { 19 | if let Some((command, arguments)) = vals.split_first() { 20 | let config = CommandNotifierConfig { 21 | command: command.clone(), 22 | arguments: arguments.to_vec(), 23 | }; 24 | config_opt = Some(config); 25 | } else { 26 | let message = format!( 27 | "`notifiers.{}.command` must have a command specified", 28 | id 29 | ); 30 | return Err(ConfigError::GeneralError { message }); 31 | } 32 | } 33 | Err(_) => { 34 | let message = format!("`notifiers.{}.command` must be an array.", id); 35 | return Err(ConfigError::GeneralError { message }); 36 | } 37 | }, 38 | _ => { 39 | let e = ConfigError::UnknownNotifierAttribute { 40 | notifier_id: id.to_string(), 41 | notifier_type: "command".to_owned(), 42 | attr_name: attr_key, 43 | }; 44 | return Err(e); 45 | } 46 | } 47 | } 48 | } 49 | _ => { 50 | let message = format!("`notifiers.{}` must be a hash. Got {:?}", id, body); 51 | return Err(ConfigError::GeneralError { message }); 52 | } 53 | }; 54 | 55 | let config = config_opt.ok_or(ConfigError::FieldMissing { 56 | path: format!("notifiers.{}.command", id), 57 | })?; 58 | Ok(config) 59 | } 60 | -------------------------------------------------------------------------------- /src/config/parser/notifiers/hipchat.rs: -------------------------------------------------------------------------------- 1 | use hyper::Uri; 2 | use yaml_rust::yaml::Yaml; 3 | 4 | use super::common::{parse_key, parse_yaml_to_string, Result}; 5 | use crate::config::HipchatNotifierConfig; 6 | use crate::error::ConfigError; 7 | 8 | pub fn parse(id: &str, body: &Yaml) -> Result { 9 | let mut token_opt: Option = None; 10 | let mut room_id_opt: Option = None; 11 | let mut base_url_opt: Option = None; 12 | 13 | match body { 14 | Yaml::Hash(hash) => { 15 | for (attr_yaml_key, attr_yaml_val) in hash { 16 | let attr_key = parse_key(&attr_yaml_key)?; 17 | 18 | match attr_key.as_ref() { 19 | "type" => (), 20 | "token" => { 21 | let attr_val = parse_yaml_to_string(&attr_yaml_val)?; 22 | token_opt = Some(attr_val); 23 | } 24 | "room_id" => { 25 | let attr_val = parse_yaml_to_string(&attr_yaml_val)?; 26 | room_id_opt = Some(attr_val); 27 | } 28 | "base_url" => { 29 | let val = parse_yaml_to_string(&attr_yaml_val)?; 30 | let url: Uri = val.parse().map_err(|_| { 31 | let message = format!( 32 | "`{}` in `notifiers.{}.base_url` is not a valid URL", 33 | val, id 34 | ); 35 | ConfigError::GeneralError { message } 36 | })?; 37 | base_url_opt = Some(url); 38 | } 39 | _ => { 40 | let e = ConfigError::UnknownNotifierAttribute { 41 | notifier_id: id.to_string(), 42 | notifier_type: "telegram".to_owned(), 43 | attr_name: attr_key, 44 | }; 45 | return Err(e); 46 | } 47 | } 48 | } 49 | } 50 | _ => { 51 | let message = format!("`notifiers.{}` must be a hash. Got {:?}", id, body); 52 | return Err(ConfigError::GeneralError { message }); 53 | } 54 | }; 55 | 56 | let token = token_opt.ok_or(ConfigError::FieldMissing { 57 | path: format!("notifiers.{}.token", id), 58 | })?; 59 | let room_id = room_id_opt.ok_or(ConfigError::FieldMissing { 60 | path: format!("notifiers.{}.room_id", id), 61 | })?; 62 | let base_url = base_url_opt.ok_or(ConfigError::FieldMissing { 63 | path: format!("notifiers.{}.base_url", id), 64 | })?; 65 | 66 | let config = HipchatNotifierConfig { 67 | token, 68 | room_id, 69 | base_url, 70 | }; 71 | Ok(config) 72 | } 73 | -------------------------------------------------------------------------------- /src/config/parser/notifiers/mod.rs: -------------------------------------------------------------------------------- 1 | use yaml_rust::yaml::Yaml; 2 | 3 | use crate::config::{Notifier, NotifierConfig}; 4 | use crate::error::ConfigError; 5 | 6 | use super::common; 7 | use super::common::{parse_key, parse_yaml_to_hash, parse_yaml_to_string, Result}; 8 | 9 | mod command; 10 | mod hipchat; 11 | mod slack; 12 | mod telegram; 13 | 14 | pub fn parse_notifiers(notifier_configs: &Yaml) -> Result> { 15 | let mut notifiers = vec![]; 16 | 17 | match notifier_configs { 18 | Yaml::Hash(hash) => { 19 | for (yaml_key, val) in hash.iter() { 20 | let checker = parse_notifier(yaml_key, val)?; 21 | notifiers.push(checker); 22 | } 23 | } 24 | _ => { 25 | let message = format!("`notifiers` must be a hash. Got {:?}", notifier_configs); 26 | return Err(ConfigError::GeneralError { message }); 27 | } 28 | } 29 | 30 | Ok(notifiers) 31 | } 32 | 33 | fn parse_notifier(key: &Yaml, body: &Yaml) -> Result { 34 | let id = parse_key(key)?; 35 | let config = parse_notifier_config(&id, body)?; 36 | 37 | let notifier = Notifier { id, config }; 38 | Ok(notifier) 39 | } 40 | 41 | fn parse_notifier_config(id: &str, body: &Yaml) -> Result { 42 | let hash = parse_yaml_to_hash(body)?; 43 | let type_val_yaml = 44 | hash.get(&Yaml::String("type".to_owned())) 45 | .ok_or(ConfigError::FieldMissing { 46 | path: format!("notifiers.{}.type", id), 47 | })?; 48 | 49 | let type_val = parse_yaml_to_string(type_val_yaml)?; 50 | 51 | match type_val.as_ref() { 52 | "telegram" => { 53 | let config = telegram::parse(id, body)?; 54 | Ok(NotifierConfig::Telegram(config)) 55 | } 56 | "command" => { 57 | let config = command::parse(id, body)?; 58 | Ok(NotifierConfig::Command(config)) 59 | } 60 | "hipchat" => { 61 | let config = hipchat::parse(id, body)?; 62 | Ok(NotifierConfig::Hipchat(config)) 63 | } 64 | "slack" => { 65 | let config = slack::parse(id, body)?; 66 | Ok(NotifierConfig::Slack(config)) 67 | } 68 | _ => { 69 | let e = ConfigError::InvalidNotifierType { 70 | notifier_id: id.to_owned(), 71 | type_value: type_val, 72 | }; 73 | Err(e) 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/config/parser/notifiers/slack.rs: -------------------------------------------------------------------------------- 1 | use hyper::Uri; 2 | use yaml_rust::yaml::Yaml; 3 | 4 | use super::common::{parse_key, parse_yaml_to_string, Result}; 5 | use crate::config::SlackNotifierConfig; 6 | use crate::error::ConfigError; 7 | 8 | pub fn parse(id: &str, body: &Yaml) -> Result { 9 | let mut webhook_url_opt: Option = None; 10 | 11 | match body { 12 | Yaml::Hash(hash) => { 13 | for (attr_yaml_key, attr_yaml_val) in hash { 14 | let attr_key = parse_key(&attr_yaml_key)?; 15 | 16 | match attr_key.as_ref() { 17 | "type" => (), 18 | "webhook_url" => { 19 | let val = parse_yaml_to_string(&attr_yaml_val)?; 20 | let url: Uri = val.parse().map_err(|_| { 21 | let message = format!( 22 | "`{}` in `notifiers.{}.webhook_url` is not a valid URL", 23 | val, id 24 | ); 25 | ConfigError::GeneralError { message } 26 | })?; 27 | webhook_url_opt = Some(url); 28 | } 29 | _ => { 30 | let e = ConfigError::UnknownNotifierAttribute { 31 | notifier_id: id.to_string(), 32 | notifier_type: "telegram".to_owned(), 33 | attr_name: attr_key, 34 | }; 35 | return Err(e); 36 | } 37 | } 38 | } 39 | } 40 | _ => { 41 | let message = format!("`notifiers.{}` must be a hash. Got {:?}", id, body); 42 | return Err(ConfigError::GeneralError { message }); 43 | } 44 | }; 45 | 46 | let webhook_url = webhook_url_opt.ok_or(ConfigError::FieldMissing { 47 | path: format!("notifiers.{}.webhook_url", id), 48 | })?; 49 | 50 | let config = SlackNotifierConfig { webhook_url }; 51 | Ok(config) 52 | } 53 | -------------------------------------------------------------------------------- /src/config/parser/notifiers/telegram.rs: -------------------------------------------------------------------------------- 1 | use yaml_rust::yaml::Yaml; 2 | 3 | use super::common::{parse_key, parse_yaml_to_string, Result}; 4 | use crate::config::TelegramNotifierConfig; 5 | use crate::error::ConfigError; 6 | 7 | pub fn parse(id: &str, body: &Yaml) -> Result { 8 | let mut token_opt: Option = None; 9 | let mut chat_id_opt: Option = None; 10 | 11 | match body { 12 | Yaml::Hash(hash) => { 13 | for (attr_yaml_key, attr_yaml_val) in hash { 14 | let attr_key = parse_key(&attr_yaml_key)?; 15 | 16 | match attr_key.as_ref() { 17 | "type" => (), 18 | "token" => { 19 | let attr_val = parse_yaml_to_string(&attr_yaml_val)?; 20 | token_opt = Some(attr_val); 21 | } 22 | "chat_id" => { 23 | let attr_val = parse_yaml_to_string(&attr_yaml_val)?; 24 | chat_id_opt = Some(attr_val); 25 | } 26 | _ => { 27 | let e = ConfigError::UnknownNotifierAttribute { 28 | notifier_id: id.to_string(), 29 | notifier_type: "telegram".to_owned(), 30 | attr_name: attr_key, 31 | }; 32 | return Err(e); 33 | } 34 | } 35 | } 36 | } 37 | _ => { 38 | let message = format!("`notifiers.{}` must be a hash. Got {:?}", id, body); 39 | return Err(ConfigError::GeneralError { message }); 40 | } 41 | }; 42 | 43 | let token = token_opt.ok_or(ConfigError::FieldMissing { 44 | path: format!("notifiers.{}.token", id), 45 | })?; 46 | let chat_id = chat_id_opt.ok_or(ConfigError::FieldMissing { 47 | path: format!("notifiers.{}.chat_id", id), 48 | })?; 49 | 50 | let config = TelegramNotifierConfig { token, chat_id }; 51 | Ok(config) 52 | } 53 | -------------------------------------------------------------------------------- /src/config/types.rs: -------------------------------------------------------------------------------- 1 | use hyper::Uri; 2 | use std::time::Duration; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct FileConfig { 6 | pub checkers: Vec, 7 | pub notifiers: Vec, 8 | } 9 | 10 | #[derive(Debug, Clone)] 11 | pub struct CheckerConfig { 12 | pub id: String, 13 | pub url: Uri, 14 | pub basic_auth: Option, 15 | pub interval: Duration, 16 | pub notifiers: Vec, 17 | } 18 | 19 | #[derive(Debug, Clone)] 20 | pub struct BasicAuth { 21 | pub username: String, 22 | pub password: String, 23 | } 24 | 25 | #[derive(Debug, Clone)] 26 | pub struct Notifier { 27 | pub id: String, 28 | pub config: NotifierConfig, 29 | } 30 | 31 | #[derive(Debug, Clone)] 32 | pub enum NotifierConfig { 33 | Telegram(TelegramNotifierConfig), 34 | Command(CommandNotifierConfig), 35 | Hipchat(HipchatNotifierConfig), 36 | Slack(SlackNotifierConfig), 37 | } 38 | 39 | #[derive(Debug, Clone)] 40 | pub struct TelegramNotifierConfig { 41 | pub token: String, 42 | pub chat_id: String, 43 | } 44 | 45 | #[derive(Debug, Clone)] 46 | pub struct HipchatNotifierConfig { 47 | pub base_url: Uri, 48 | pub token: String, 49 | pub room_id: String, 50 | } 51 | 52 | #[derive(Debug, Clone)] 53 | pub struct SlackNotifierConfig { 54 | pub webhook_url: Uri, 55 | } 56 | 57 | #[derive(Debug, Clone)] 58 | pub struct CommandNotifierConfig { 59 | pub command: String, 60 | pub arguments: Vec, 61 | } 62 | 63 | impl FileConfig { 64 | pub fn get_checker_by_id(&self, id: &str) -> Option { 65 | self.checkers.iter().find(|c| c.id == id).cloned() 66 | } 67 | 68 | pub fn get_notifier_by_id(&self, id: &str) -> Option { 69 | self.notifiers.iter().find(|n| n.id == id).cloned() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/config/validator.rs: -------------------------------------------------------------------------------- 1 | // Validates FileConfig for inconsistencies. 2 | 3 | use crate::config::{FileConfig, NotifierConfig}; 4 | use crate::error::ConfigValidationError; 5 | 6 | type Result = ::std::result::Result; 7 | 8 | pub fn validate_config(config: &FileConfig) -> Result> { 9 | verify_checker_presence(config)?; 10 | verify_checker_notifiers(config)?; 11 | verify_command_notifiers(config)?; 12 | 13 | let mut warnings: Vec = vec![]; 14 | verify_empty_notifiers(config, &mut warnings); 15 | verify_unused_notifiers(config, &mut warnings); 16 | 17 | Ok(warnings) 18 | } 19 | 20 | fn verify_checker_presence(config: &FileConfig) -> Result<()> { 21 | match config.checkers.len() { 22 | 0 => Err(ConfigValidationError::NoCheckers), 23 | _ => Ok(()), 24 | } 25 | } 26 | 27 | // Ensure, that all checkers refer to declared notifiers. 28 | fn verify_checker_notifiers(config: &FileConfig) -> Result<()> { 29 | for checker in config.checkers.iter() { 30 | for notifier_id in checker.notifiers.iter() { 31 | match config.get_notifier_by_id(notifier_id) { 32 | Some(_) => {} 33 | None => { 34 | let err = ConfigValidationError::UnknownNotifier { 35 | checker_id: checker.id.to_owned(), 36 | notifier_id: notifier_id.to_owned(), 37 | }; 38 | return Err(err); 39 | } 40 | } 41 | } 42 | } 43 | 44 | Ok(()) 45 | } 46 | 47 | // Ensure all CommandNotifier refers to an existing command 48 | fn verify_command_notifiers(config: &FileConfig) -> Result<()> { 49 | for notifier in config.notifiers.iter() { 50 | if let NotifierConfig::Command(c) = ¬ifier.config { 51 | if !command_exists(&c.command) { 52 | let err = ConfigValidationError::CommandNotFound { 53 | notifier_id: notifier.id.clone(), 54 | command: c.command.clone(), 55 | }; 56 | return Err(err); 57 | } 58 | } 59 | } 60 | Ok(()) 61 | } 62 | 63 | fn command_exists(command: &str) -> bool { 64 | let res = std::process::Command::new("which").arg(command).output(); 65 | 66 | match res { 67 | Ok(output) => output.status.success(), 68 | // if `which` command does not exist on the current system, we just return true 69 | Err(_) => true, 70 | } 71 | } 72 | 73 | fn verify_empty_notifiers(config: &FileConfig, warnings: &mut Vec) { 74 | if config.notifiers.is_empty() { 75 | warnings.push("Notifiers are not declared. You will not get notifications.".to_owned()); 76 | return; 77 | } 78 | 79 | for checker in config.checkers.iter() { 80 | if checker.notifiers.is_empty() { 81 | let msg = format!( 82 | "`checkers.{}.notifiers` is empty. You will not get notifications", 83 | checker.id 84 | ); 85 | warnings.push(msg); 86 | } 87 | } 88 | } 89 | 90 | fn verify_unused_notifiers(config: &FileConfig, warnings: &mut Vec) { 91 | let used_notifier_ids: Vec<&String> = config 92 | .checkers 93 | .iter() 94 | .map(|c| c.notifiers.iter()) 95 | .flatten() 96 | .collect(); 97 | 98 | for notifier in config.notifiers.iter() { 99 | if !used_notifier_ids.contains(&¬ifier.id) { 100 | let msg = format!( 101 | "Notifier `{}` is not used by any of the checkers.", 102 | notifier.id 103 | ); 104 | warnings.push(msg); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use failure::Fail; 2 | 3 | #[derive(Debug, Fail, PartialEq)] 4 | pub enum ConfigError { 5 | #[fail(display = "Invalid YAML file: {}", err)] 6 | InvalidYaml { err: yaml_rust::scanner::ScanError }, 7 | 8 | #[fail(display = "Unknown root element `{}`", name)] 9 | UnkownRootElement { name: String }, 10 | 11 | #[fail(display = "{}", message)] 12 | GeneralError { message: String }, 13 | 14 | #[fail( 15 | display = "Unknown checker attribute `{}` in checkers.{}", 16 | attr_name, checker_id 17 | )] 18 | UnknownCheckerAttribute { 19 | checker_id: String, 20 | attr_name: String, 21 | }, 22 | 23 | #[fail( 24 | display = "Failed to parse interval `{}` in checkers.{}.interval", 25 | interval, checker_id 26 | )] 27 | InvalidCheckerInterval { 28 | checker_id: String, 29 | interval: String, 30 | }, 31 | 32 | #[fail( 33 | display = "Failed to parse URL `{}` in checkers.{}.url", 34 | url, checker_id 35 | )] 36 | InvalidCheckerUrl { checker_id: String, url: String }, 37 | 38 | #[fail( 39 | display = "Invalid notifier type `{}` in `notifiers.{}.type`", 40 | type_value, notifier_id 41 | )] 42 | InvalidNotifierType { 43 | notifier_id: String, 44 | type_value: String, 45 | }, 46 | 47 | #[fail( 48 | display = "Unknown {} notifier attribute `{}` in checkers.{}", 49 | notifier_type, attr_name, notifier_id 50 | )] 51 | UnknownNotifierAttribute { 52 | notifier_id: String, 53 | notifier_type: String, 54 | attr_name: String, 55 | }, 56 | 57 | #[fail(display = "Field `{}` is missing", path)] 58 | FieldMissing { path: String }, 59 | } 60 | 61 | #[derive(Debug, Fail, PartialEq)] 62 | pub enum ConfigValidationError { 63 | #[fail( 64 | display = "`checkers.{}.notifiers` refers to an undeclared notifier `{}`", 65 | checker_id, notifier_id 66 | )] 67 | UnknownNotifier { 68 | checker_id: String, 69 | notifier_id: String, 70 | }, 71 | 72 | #[fail( 73 | display = "`notifiers.{}.command` refers to `{}`, but it is not executable", 74 | notifier_id, command 75 | )] 76 | CommandNotFound { 77 | notifier_id: String, 78 | command: String, 79 | }, 80 | 81 | #[fail(display = "There are no checkers defined in the config file")] 82 | NoCheckers, 83 | } 84 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | pub mod cli; 5 | 6 | mod config; 7 | mod error; 8 | mod notifiers; 9 | mod reactor; 10 | mod watcher; 11 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | pretty_env_logger::init(); 3 | 4 | // Necessary to make OpenSSL work in a static build. 5 | // See: https://github.com/emk/rust-musl-builder#making-openssl-work 6 | openssl_probe::init_ssl_cert_env_vars(); 7 | 8 | hellcheck::cli::run(); 9 | } 10 | -------------------------------------------------------------------------------- /src/notifiers/command.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | use crate::config::CommandNotifierConfig; 4 | use crate::notifiers::{Notification, Notifier}; 5 | use crate::reactor::State; 6 | 7 | pub struct CommandNotifier { 8 | command: String, 9 | arguments: Vec, 10 | } 11 | 12 | impl CommandNotifier { 13 | pub fn from_config(config: &CommandNotifierConfig) -> Self { 14 | Self { 15 | command: config.command.clone(), 16 | arguments: config.arguments.clone(), 17 | } 18 | } 19 | } 20 | 21 | impl Notifier for CommandNotifier { 22 | fn notify(&self, notification: &Notification) -> Result<(), ()> { 23 | let ok = match notification.state { 24 | State::Up => "true".to_owned(), 25 | State::Down => "false".to_owned(), 26 | }; 27 | 28 | let res = Command::new(&self.command) 29 | .args(self.arguments.iter()) 30 | .env("HELLCHECK_ID", notification.checker_id.clone()) 31 | .env("HELLCHECK_URL", notification.checker_url.clone()) 32 | .env("HELLCHECK_OK", ok) 33 | .status(); 34 | 35 | match res { 36 | Ok(status) => { 37 | if status.success() { 38 | Ok(()) 39 | } else { 40 | Err(()) 41 | } 42 | } 43 | Err(_) => Err(()), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/notifiers/hipchat.rs: -------------------------------------------------------------------------------- 1 | use hyper::Uri; 2 | 3 | use std::collections::HashMap; 4 | 5 | use crate::config::HipchatNotifierConfig; 6 | use crate::notifiers::{Notification, Notifier}; 7 | use crate::reactor::State; 8 | 9 | pub struct HipchatNotifier { 10 | http_client: ::reqwest::Client, 11 | base_url: Uri, 12 | token: String, 13 | room_id: String, 14 | } 15 | 16 | impl HipchatNotifier { 17 | pub fn from_config(config: &HipchatNotifierConfig) -> Self { 18 | Self { 19 | http_client: reqwest::Client::new(), 20 | base_url: config.base_url.clone(), 21 | token: config.token.clone(), 22 | room_id: config.room_id.clone(), 23 | } 24 | } 25 | } 26 | 27 | impl Notifier for HipchatNotifier { 28 | fn notify(&self, notification: &Notification) -> Result<(), ()> { 29 | let payload = build_payload(notification); 30 | let url = format!( 31 | "{}/v2/room/{}/notification?auth_token={}", 32 | self.base_url, self.room_id, self.token 33 | ); 34 | 35 | let res = self.http_client.post(&url).json(&payload).send(); 36 | res.map(|_| ()).map_err(|_| ()) 37 | } 38 | } 39 | 40 | fn build_payload(notification: &Notification) -> HashMap<&'static str, String> { 41 | let color = match notification.state { 42 | State::Up => "green".to_owned(), 43 | State::Down => "red".to_owned(), 44 | }; 45 | 46 | let message = match notification.state { 47 | State::Up => format!( 48 | "{} is up (dealwithit)\n{}", 49 | notification.checker_id, notification.checker_url 50 | ), 51 | State::Down => format!( 52 | "{} is down (boom)\n{}", 53 | notification.checker_id, notification.checker_url 54 | ), 55 | }; 56 | 57 | let mut payload = HashMap::new(); 58 | payload.insert("color", color); 59 | payload.insert("message", message); 60 | payload.insert("message_format", "text".to_owned()); 61 | 62 | payload 63 | } 64 | -------------------------------------------------------------------------------- /src/notifiers/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::reactor::State; 2 | 3 | mod telegram; 4 | pub use self::telegram::TelegramNotifier; 5 | 6 | mod command; 7 | pub use self::command::CommandNotifier; 8 | 9 | mod hipchat; 10 | pub use self::hipchat::HipchatNotifier; 11 | 12 | mod slack; 13 | pub use self::slack::SlackNotifier; 14 | 15 | #[derive(Debug)] 16 | pub struct Notification { 17 | pub checker_id: String, 18 | pub checker_url: String, 19 | pub state: State, 20 | } 21 | 22 | pub trait Notifier { 23 | fn notify(&self, notification: &Notification) -> Result<(), ()>; 24 | } 25 | -------------------------------------------------------------------------------- /src/notifiers/slack.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use hyper::Uri; 4 | 5 | use crate::config::SlackNotifierConfig; 6 | use crate::notifiers::{Notification, Notifier}; 7 | use crate::reactor::State; 8 | 9 | pub struct SlackNotifier { 10 | http_client: ::reqwest::Client, 11 | webhook_url: Uri, 12 | } 13 | 14 | impl SlackNotifier { 15 | pub fn from_config(config: &SlackNotifierConfig) -> Self { 16 | Self { 17 | http_client: reqwest::Client::new(), 18 | webhook_url: config.webhook_url.clone(), 19 | } 20 | } 21 | } 22 | 23 | impl Notifier for SlackNotifier { 24 | fn notify(&self, notification: &Notification) -> Result<(), ()> { 25 | let payload = build_payload(notification); 26 | let url = format!("{}", self.webhook_url); 27 | let res = self.http_client.post(&url).json(&payload).send(); 28 | res.map(|_| ()).map_err(|_| ()) 29 | } 30 | } 31 | 32 | fn build_payload( 33 | notification: &Notification, 34 | ) -> HashMap<&'static str, Vec>> { 35 | let fallback = match notification.state { 36 | State::Up => format!( 37 | "{} is up :thumbsup:\n{}", 38 | notification.checker_id, notification.checker_url 39 | ), 40 | State::Down => format!( 41 | "{} is down :fire:\n{}", 42 | notification.checker_id, notification.checker_url 43 | ), 44 | }; 45 | let color = match notification.state { 46 | State::Up => "good".to_owned(), 47 | State::Down => "danger".to_owned(), 48 | }; 49 | let title = match notification.state { 50 | State::Up => format!("{} is up :thumbsup:", notification.checker_id), 51 | State::Down => format!("{} is down :fire:", notification.checker_id), 52 | }; 53 | let title_link = notification.checker_url.clone(); 54 | 55 | let mut attachment = HashMap::new(); 56 | attachment.insert("fallback", fallback); 57 | attachment.insert("color", color); 58 | attachment.insert("title", title); 59 | attachment.insert("title_link", title_link); 60 | 61 | let attachments = vec![attachment]; 62 | 63 | let mut payload = HashMap::new(); 64 | payload.insert("attachments", attachments); 65 | 66 | payload 67 | } 68 | -------------------------------------------------------------------------------- /src/notifiers/telegram.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::config::TelegramNotifierConfig; 4 | use crate::notifiers::{Notification, Notifier}; 5 | use crate::reactor::State; 6 | 7 | pub struct TelegramNotifier { 8 | http_client: ::reqwest::Client, 9 | token: String, 10 | chat_id: String, 11 | } 12 | 13 | impl TelegramNotifier { 14 | pub fn from_config(config: &TelegramNotifierConfig) -> Self { 15 | Self { 16 | http_client: reqwest::Client::new(), 17 | token: config.token.clone(), 18 | chat_id: config.chat_id.clone(), 19 | } 20 | } 21 | } 22 | 23 | impl Notifier for TelegramNotifier { 24 | fn notify(&self, notification: &Notification) -> Result<(), ()> { 25 | let text = match notification.state { 26 | State::Up => { 27 | let emoji_baloon = '\u{1F388}'; 28 | format!( 29 | "{} is up {}\n{}", 30 | notification.checker_id, emoji_baloon, notification.checker_url 31 | ) 32 | } 33 | State::Down => { 34 | let emoji_fire = '\u{1F525}'; 35 | format!( 36 | "{} is down {}\n{}", 37 | notification.checker_id, emoji_fire, notification.checker_url 38 | ) 39 | } 40 | }; 41 | let mut payload = HashMap::new(); 42 | payload.insert("chat_id", self.chat_id.clone()); 43 | payload.insert("text", text); 44 | 45 | let url = format!("https://api.telegram.org/bot{}/sendMessage", self.token); 46 | 47 | let res = self.http_client.post(&url).json(&payload).send(); 48 | res.map(|_| ()).map_err(|_| ()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/reactor.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::sync::mpsc; 3 | 4 | use crate::config::{CheckerConfig, FileConfig, NotifierConfig}; 5 | use crate::notifiers::Notifier as NotifierTrait; 6 | use crate::notifiers::{ 7 | CommandNotifier, HipchatNotifier, Notification, SlackNotifier, TelegramNotifier, 8 | }; 9 | 10 | #[derive(Debug, Clone, PartialEq)] 11 | pub enum State { 12 | Up, 13 | Down, 14 | } 15 | 16 | #[derive(Debug)] 17 | pub struct StateMessage { 18 | pub checker_id: String, 19 | pub state: State, 20 | } 21 | 22 | pub fn spawn(receiver: mpsc::Receiver, config: FileConfig) { 23 | ::std::thread::spawn(move || { 24 | let mut states = build_initial_states(&config); 25 | let notifiers = build_notifiers(&config); 26 | 27 | loop { 28 | let msg = receiver.recv().unwrap(); 29 | let checker = config.get_checker_by_id(&msg.checker_id).unwrap(); 30 | 31 | // unwrap is safe here, because `states` was initialized with all possible checker ids. 32 | let prev_state = &states[&msg.checker_id]; 33 | 34 | // Send a message if state was changed 35 | if msg.state != *prev_state { 36 | for notifier_id in checker.notifiers.iter() { 37 | // unwrap is safe here, because notifiers were validated by config_validator. 38 | let notifier = ¬ifiers[notifier_id]; 39 | let notification = build_notification(&checker, msg.state.clone()); 40 | info!("Sending a notification to {}", notifier_id); 41 | let res = notifier.notify(¬ification); 42 | match res { 43 | Ok(_) => {} 44 | Err(_) => { 45 | eprintln!( 46 | "ERROR: Notifier `{}` failed to notify that {} is {:?}", 47 | notifier_id, checker.id, msg.state 48 | ); 49 | } 50 | } 51 | } 52 | } 53 | 54 | states.insert(msg.checker_id, msg.state); 55 | } 56 | }); 57 | } 58 | 59 | fn build_notification(checker: &CheckerConfig, state: State) -> Notification { 60 | Notification { 61 | checker_id: checker.id.clone(), 62 | checker_url: format!("{}", checker.url), 63 | state, 64 | } 65 | } 66 | 67 | fn build_initial_states(config: &FileConfig) -> HashMap { 68 | let mut states: HashMap = HashMap::new(); 69 | for checker in config.checkers.iter() { 70 | states.insert(checker.id.clone(), State::Up); 71 | } 72 | states 73 | } 74 | 75 | fn build_notifiers(config: &FileConfig) -> HashMap> { 76 | let mut notifiers: HashMap> = HashMap::new(); 77 | for notifier_config in config.notifiers.iter() { 78 | let notifier: Box = match ¬ifier_config.config { 79 | NotifierConfig::Telegram(telegram_config) => { 80 | Box::new(TelegramNotifier::from_config(telegram_config)) 81 | } 82 | NotifierConfig::Command(command_config) => { 83 | Box::new(CommandNotifier::from_config(command_config)) 84 | } 85 | NotifierConfig::Hipchat(hipchat_config) => { 86 | Box::new(HipchatNotifier::from_config(hipchat_config)) 87 | } 88 | NotifierConfig::Slack(slack_config) => { 89 | Box::new(SlackNotifier::from_config(slack_config)) 90 | } 91 | }; 92 | notifiers.insert(notifier_config.id.clone(), notifier); 93 | } 94 | 95 | notifiers 96 | } 97 | -------------------------------------------------------------------------------- /src/watcher.rs: -------------------------------------------------------------------------------- 1 | use hyper::rt::{Future, Stream}; 2 | use hyper::Client; 3 | use hyper_tls::HttpsConnector; 4 | 5 | use std::sync::mpsc; 6 | use std::time::Instant; 7 | 8 | use crate::config::{BasicAuth, CheckerConfig, FileConfig}; 9 | use crate::reactor::{State, StateMessage}; 10 | 11 | type HttpsClient = hyper::Client>; 12 | 13 | pub fn run(config: FileConfig, sender: mpsc::Sender) { 14 | let check_runner = CheckRunner { config, sender }; 15 | let checkers_futures = check_runner 16 | .config 17 | .checkers 18 | .iter() 19 | .map(|c| check_runner.build_future(c)); 20 | let f = futures::future::select_all(checkers_futures); 21 | 22 | let mut core = tokio_core::reactor::Core::new().unwrap(); 23 | 24 | // Run core forever 25 | let res = core.run(f); 26 | 27 | match res { 28 | Ok(_) => {} 29 | Err(_) => { 30 | eprintln!("ERROR: looks likes hellcheck crashed"); 31 | std::process::exit(1); 32 | } 33 | } 34 | } 35 | 36 | struct CheckRunner { 37 | config: FileConfig, 38 | sender: mpsc::Sender, 39 | } 40 | 41 | impl CheckRunner { 42 | fn build_future<'a>( 43 | &'a self, 44 | service: &'a CheckerConfig, 45 | ) -> Box + 'a> { 46 | let stream = tokio_timer::Interval::new(Instant::now(), service.interval); 47 | let client = build_client(); 48 | 49 | let id = service.id.clone(); 50 | 51 | let f = stream.for_each(move |_| { 52 | let checker_id = id.clone(); 53 | 54 | let req = build_request(&service); 55 | 56 | info!("Sending a request to {}", service.url); 57 | 58 | client.request(req).then(move |r| { 59 | let state = match r { 60 | Ok(resp) => { 61 | if resp.status() == 200 { 62 | State::Up 63 | } else { 64 | State::Down 65 | } 66 | } 67 | Err(_err) => State::Down, 68 | }; 69 | let msg = StateMessage { 70 | checker_id: checker_id.clone(), 71 | state, 72 | }; 73 | self.sender.send(msg).unwrap(); 74 | 75 | Ok(()) 76 | }) 77 | }); 78 | Box::new(f) 79 | } 80 | } 81 | 82 | fn build_request(service: &CheckerConfig) -> hyper::Request { 83 | let mut builder = hyper::Request::get(service.url.clone()); 84 | 85 | if let Some(ref basic_auth) = service.basic_auth { 86 | let authorization_header_value = build_authorization_header_value(basic_auth); 87 | builder.header(hyper::header::AUTHORIZATION, authorization_header_value); 88 | } 89 | 90 | builder.body(hyper::Body::empty()).unwrap() 91 | } 92 | 93 | fn build_authorization_header_value(auth: &BasicAuth) -> hyper::header::HeaderValue { 94 | let credentials = format!("{}:{}", auth.username, auth.password); 95 | let encoded_credentials = base64::encode(&credentials); 96 | let value = format!("Basic {}", encoded_credentials); 97 | hyper::header::HeaderValue::from_str(&value).unwrap() 98 | } 99 | 100 | fn build_client() -> HttpsClient { 101 | let https = HttpsConnector::new(1).expect("TLS initialization failed"); 102 | Client::builder().build::<_, hyper::Body>(https) 103 | } 104 | --------------------------------------------------------------------------------