├── .dockerignore ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── README.md ├── bacon.toml ├── compile-all-targets.sh ├── doc ├── flow-01-lpush-event.png ├── flow-02-brpoplpush-event.png ├── flow-03-lpush-task.png ├── flow-04-lrem-event.png ├── flow-05-brpoplpush-task.png ├── flow-06-lpush-done.png └── flow-07-lrem-task.png ├── examples ├── complexe-conf.json ├── doc │ ├── simple-example-complete.png │ └── simple-example-generated-tasks.png ├── go-client │ └── worker.go ├── java-client │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── org │ │ └── canop │ │ └── resc │ │ └── examples │ │ └── SimpleWorker.java ├── node-client │ ├── main.js │ ├── package.json │ └── yarn.lock ├── rust-client │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── simple-conf.hjson └── simple-example.md ├── release.sh └── src ├── conf.rs ├── errors.rs ├── fetcher.rs ├── main.rs ├── make.rs ├── pattern.rs ├── rule.rs ├── rule_result.rs ├── ruleset.rs ├── serde_format.rs └── watcher.rs /.dockerignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | .settings 4 | examples -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target 3 | **/*.rs.bk 4 | .idea 5 | *.iml 6 | dependency-reduced-pom.xml 7 | node_modules 8 | examples/go-client/go-client 9 | examples/java-client/build 10 | examples/java-client/.gradle 11 | trav 12 | build 13 | releases 14 | /*.zip 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ### v0.3.4 - 2023-04-21 3 | - dependency updates, minor cleaning of code and documentation 4 | 5 | 6 | ### v0.3.2 - 2021-09-30 7 | - upgrade the redis dependency to fix compilation on some platforms 8 | 9 | 10 | ### v0.3.1 - 2021-05-26 11 | - add support for CRLF in config file 12 | 13 | 14 | ### v0.3.0 - 2021-02-08 15 | - It's now possible to declare several `make` elements in a rule, which is useful when a "on" condition leads to many different tasks 16 | - Hjson has been added as an alternate configuration format 17 | 18 | 19 | ### v0.2.0 - 2020-09-03 20 | Task deduplicating changed: 21 | - it's per task queue (thus not preventing anymore homonyms in separate queues) 22 | - it's optional 23 | - it doesn't prevent a task from being queued again between processing start and end 24 | 25 | The global `task_set` property of the configuration has thus been removed and resc issues a warning when it's present. 26 | 27 | If you want to deduplicate a task queue, you now need to 28 | - declare a `set` (as a pattern, like the `task` and the `queue`) in the `make` part of a rule 29 | - have the worker remove the task from the set before executing it (or after if you don't want requeuing during processing) 30 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.13.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "0.2.3" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.0.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anyhow" 31 | version = "1.0.38" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" 34 | 35 | [[package]] 36 | name = "async-trait" 37 | version = "0.1.40" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "687c230d85c0a52504709705fc8a53e4a692b83a2184f03dae73e38e1e93a783" 40 | dependencies = [ 41 | "proc-macro2", 42 | "quote", 43 | "syn", 44 | ] 45 | 46 | [[package]] 47 | name = "atty" 48 | version = "0.2.14" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 51 | dependencies = [ 52 | "hermit-abi", 53 | "libc", 54 | "winapi 0.3.9", 55 | ] 56 | 57 | [[package]] 58 | name = "autocfg" 59 | version = "0.1.7" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" 62 | 63 | [[package]] 64 | name = "autocfg" 65 | version = "1.0.1" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 68 | 69 | [[package]] 70 | name = "backtrace" 71 | version = "0.3.50" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293" 74 | dependencies = [ 75 | "addr2line", 76 | "cfg-if", 77 | "libc", 78 | "miniz_oxide", 79 | "object", 80 | "rustc-demangle", 81 | ] 82 | 83 | [[package]] 84 | name = "base64" 85 | version = "0.10.1" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" 88 | dependencies = [ 89 | "byteorder", 90 | ] 91 | 92 | [[package]] 93 | name = "bitflags" 94 | version = "1.2.1" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 97 | 98 | [[package]] 99 | name = "byteorder" 100 | version = "1.3.4" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 103 | 104 | [[package]] 105 | name = "bytes" 106 | version = "0.4.12" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" 109 | dependencies = [ 110 | "byteorder", 111 | "either", 112 | "iovec", 113 | ] 114 | 115 | [[package]] 116 | name = "bytes" 117 | version = "1.1.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" 120 | 121 | [[package]] 122 | name = "cc" 123 | version = "1.0.59" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" 126 | 127 | [[package]] 128 | name = "cfg-if" 129 | version = "0.1.10" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 132 | 133 | [[package]] 134 | name = "chrono" 135 | version = "0.4.15" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" 138 | dependencies = [ 139 | "num-integer", 140 | "num-traits", 141 | "time", 142 | ] 143 | 144 | [[package]] 145 | name = "cloudabi" 146 | version = "0.0.3" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 149 | dependencies = [ 150 | "bitflags", 151 | ] 152 | 153 | [[package]] 154 | name = "combine" 155 | version = "4.6.1" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "a909e4d93292cd8e9c42e189f61681eff9d67b6541f96b8a1a737f23737bd001" 158 | dependencies = [ 159 | "bytes 1.1.0", 160 | "memchr", 161 | ] 162 | 163 | [[package]] 164 | name = "cookie" 165 | version = "0.12.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" 168 | dependencies = [ 169 | "time", 170 | "url 1.7.2", 171 | ] 172 | 173 | [[package]] 174 | name = "cookie_store" 175 | version = "0.7.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "46750b3f362965f197996c4448e4a0935e791bf7d6631bfce9ee0af3d24c919c" 178 | dependencies = [ 179 | "cookie", 180 | "failure", 181 | "idna 0.1.5", 182 | "log", 183 | "publicsuffix", 184 | "serde", 185 | "serde_json", 186 | "time", 187 | "try_from", 188 | "url 1.7.2", 189 | ] 190 | 191 | [[package]] 192 | name = "core-foundation" 193 | version = "0.7.0" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" 196 | dependencies = [ 197 | "core-foundation-sys", 198 | "libc", 199 | ] 200 | 201 | [[package]] 202 | name = "core-foundation-sys" 203 | version = "0.7.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" 206 | 207 | [[package]] 208 | name = "crc32fast" 209 | version = "1.2.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" 212 | dependencies = [ 213 | "cfg-if", 214 | ] 215 | 216 | [[package]] 217 | name = "crossbeam-deque" 218 | version = "0.7.3" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" 221 | dependencies = [ 222 | "crossbeam-epoch", 223 | "crossbeam-utils", 224 | "maybe-uninit", 225 | ] 226 | 227 | [[package]] 228 | name = "crossbeam-epoch" 229 | version = "0.8.2" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" 232 | dependencies = [ 233 | "autocfg 1.0.1", 234 | "cfg-if", 235 | "crossbeam-utils", 236 | "lazy_static", 237 | "maybe-uninit", 238 | "memoffset", 239 | "scopeguard", 240 | ] 241 | 242 | [[package]] 243 | name = "crossbeam-queue" 244 | version = "0.2.3" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" 247 | dependencies = [ 248 | "cfg-if", 249 | "crossbeam-utils", 250 | "maybe-uninit", 251 | ] 252 | 253 | [[package]] 254 | name = "crossbeam-utils" 255 | version = "0.7.2" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 258 | dependencies = [ 259 | "autocfg 1.0.1", 260 | "cfg-if", 261 | "lazy_static", 262 | ] 263 | 264 | [[package]] 265 | name = "deser-hjson" 266 | version = "1.1.0" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "799b522307619917536ae2c26e60dab657998dea8f3feaf827e9dc8daeb404bf" 269 | dependencies = [ 270 | "serde", 271 | ] 272 | 273 | [[package]] 274 | name = "dtoa" 275 | version = "0.4.6" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" 278 | 279 | [[package]] 280 | name = "either" 281 | version = "1.6.0" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" 284 | 285 | [[package]] 286 | name = "encoding_rs" 287 | version = "0.8.24" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "a51b8cf747471cb9499b6d59e59b0444f4c90eba8968c4e44874e92b5b64ace2" 290 | dependencies = [ 291 | "cfg-if", 292 | ] 293 | 294 | [[package]] 295 | name = "env_logger" 296 | version = "0.5.13" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" 299 | dependencies = [ 300 | "atty", 301 | "humantime", 302 | "log", 303 | "regex", 304 | "termcolor", 305 | ] 306 | 307 | [[package]] 308 | name = "error-chain" 309 | version = "0.12.4" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" 312 | dependencies = [ 313 | "version_check", 314 | ] 315 | 316 | [[package]] 317 | name = "failure" 318 | version = "0.1.8" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" 321 | dependencies = [ 322 | "backtrace", 323 | "failure_derive", 324 | ] 325 | 326 | [[package]] 327 | name = "failure_derive" 328 | version = "0.1.8" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" 331 | dependencies = [ 332 | "proc-macro2", 333 | "quote", 334 | "syn", 335 | "synstructure", 336 | ] 337 | 338 | [[package]] 339 | name = "flate2" 340 | version = "1.0.17" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "766d0e77a2c1502169d4a93ff3b8c15a71fd946cd0126309752104e5f3c46d94" 343 | dependencies = [ 344 | "cfg-if", 345 | "crc32fast", 346 | "libc", 347 | "miniz_oxide", 348 | ] 349 | 350 | [[package]] 351 | name = "fnv" 352 | version = "1.0.7" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 355 | 356 | [[package]] 357 | name = "foreign-types" 358 | version = "0.3.2" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 361 | dependencies = [ 362 | "foreign-types-shared", 363 | ] 364 | 365 | [[package]] 366 | name = "foreign-types-shared" 367 | version = "0.1.1" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 370 | 371 | [[package]] 372 | name = "fuchsia-cprng" 373 | version = "0.1.1" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 376 | 377 | [[package]] 378 | name = "fuchsia-zircon" 379 | version = "0.3.3" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 382 | dependencies = [ 383 | "bitflags", 384 | "fuchsia-zircon-sys", 385 | ] 386 | 387 | [[package]] 388 | name = "fuchsia-zircon-sys" 389 | version = "0.3.3" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 392 | 393 | [[package]] 394 | name = "futures" 395 | version = "0.1.29" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" 398 | 399 | [[package]] 400 | name = "futures-cpupool" 401 | version = "0.1.8" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" 404 | dependencies = [ 405 | "futures", 406 | "num_cpus", 407 | ] 408 | 409 | [[package]] 410 | name = "getrandom" 411 | version = "0.1.14" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 414 | dependencies = [ 415 | "cfg-if", 416 | "libc", 417 | "wasi 0.9.0+wasi-snapshot-preview1", 418 | ] 419 | 420 | [[package]] 421 | name = "gimli" 422 | version = "0.22.0" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" 425 | 426 | [[package]] 427 | name = "h2" 428 | version = "0.1.26" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" 431 | dependencies = [ 432 | "byteorder", 433 | "bytes 0.4.12", 434 | "fnv", 435 | "futures", 436 | "http", 437 | "indexmap", 438 | "log", 439 | "slab", 440 | "string", 441 | "tokio-io", 442 | ] 443 | 444 | [[package]] 445 | name = "hashbrown" 446 | version = "0.8.2" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" 449 | dependencies = [ 450 | "autocfg 1.0.1", 451 | ] 452 | 453 | [[package]] 454 | name = "hermit-abi" 455 | version = "0.1.15" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" 458 | dependencies = [ 459 | "libc", 460 | ] 461 | 462 | [[package]] 463 | name = "http" 464 | version = "0.1.21" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" 467 | dependencies = [ 468 | "bytes 0.4.12", 469 | "fnv", 470 | "itoa", 471 | ] 472 | 473 | [[package]] 474 | name = "http-body" 475 | version = "0.1.0" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" 478 | dependencies = [ 479 | "bytes 0.4.12", 480 | "futures", 481 | "http", 482 | "tokio-buf", 483 | ] 484 | 485 | [[package]] 486 | name = "httparse" 487 | version = "1.3.4" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" 490 | 491 | [[package]] 492 | name = "humantime" 493 | version = "1.3.0" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 496 | dependencies = [ 497 | "quick-error", 498 | ] 499 | 500 | [[package]] 501 | name = "hyper" 502 | version = "0.12.35" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" 505 | dependencies = [ 506 | "bytes 0.4.12", 507 | "futures", 508 | "futures-cpupool", 509 | "h2", 510 | "http", 511 | "http-body", 512 | "httparse", 513 | "iovec", 514 | "itoa", 515 | "log", 516 | "net2", 517 | "rustc_version", 518 | "time", 519 | "tokio", 520 | "tokio-buf", 521 | "tokio-executor", 522 | "tokio-io", 523 | "tokio-reactor", 524 | "tokio-tcp", 525 | "tokio-threadpool", 526 | "tokio-timer", 527 | "want", 528 | ] 529 | 530 | [[package]] 531 | name = "hyper-tls" 532 | version = "0.3.2" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" 535 | dependencies = [ 536 | "bytes 0.4.12", 537 | "futures", 538 | "hyper", 539 | "native-tls", 540 | "tokio-io", 541 | ] 542 | 543 | [[package]] 544 | name = "idna" 545 | version = "0.1.5" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" 548 | dependencies = [ 549 | "matches", 550 | "unicode-bidi", 551 | "unicode-normalization", 552 | ] 553 | 554 | [[package]] 555 | name = "idna" 556 | version = "0.2.0" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 559 | dependencies = [ 560 | "matches", 561 | "unicode-bidi", 562 | "unicode-normalization", 563 | ] 564 | 565 | [[package]] 566 | name = "indexmap" 567 | version = "1.5.2" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | checksum = "4e47a3566dd4fd4eec714ae6ceabdee0caec795be835c223d92c2d40f1e8cf1c" 570 | dependencies = [ 571 | "autocfg 1.0.1", 572 | "hashbrown", 573 | ] 574 | 575 | [[package]] 576 | name = "iovec" 577 | version = "0.1.4" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 580 | dependencies = [ 581 | "libc", 582 | ] 583 | 584 | [[package]] 585 | name = "itoa" 586 | version = "0.4.6" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 589 | 590 | [[package]] 591 | name = "kernel32-sys" 592 | version = "0.2.2" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 595 | dependencies = [ 596 | "winapi 0.2.8", 597 | "winapi-build", 598 | ] 599 | 600 | [[package]] 601 | name = "lazy_static" 602 | version = "1.4.0" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 605 | 606 | [[package]] 607 | name = "libc" 608 | version = "0.2.76" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" 611 | 612 | [[package]] 613 | name = "lock_api" 614 | version = "0.3.4" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" 617 | dependencies = [ 618 | "scopeguard", 619 | ] 620 | 621 | [[package]] 622 | name = "log" 623 | version = "0.4.11" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 626 | dependencies = [ 627 | "cfg-if", 628 | ] 629 | 630 | [[package]] 631 | name = "matches" 632 | version = "0.1.8" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 635 | 636 | [[package]] 637 | name = "maybe-uninit" 638 | version = "2.0.0" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 641 | 642 | [[package]] 643 | name = "memchr" 644 | version = "2.5.0" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 647 | 648 | [[package]] 649 | name = "memoffset" 650 | version = "0.5.5" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" 653 | dependencies = [ 654 | "autocfg 1.0.1", 655 | ] 656 | 657 | [[package]] 658 | name = "mime" 659 | version = "0.3.16" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 662 | 663 | [[package]] 664 | name = "mime_guess" 665 | version = "2.0.3" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" 668 | dependencies = [ 669 | "mime", 670 | "unicase", 671 | ] 672 | 673 | [[package]] 674 | name = "miniz_oxide" 675 | version = "0.4.1" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "4d7559a8a40d0f97e1edea3220f698f78b1c5ab67532e49f68fde3910323b722" 678 | dependencies = [ 679 | "adler", 680 | ] 681 | 682 | [[package]] 683 | name = "mio" 684 | version = "0.6.22" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" 687 | dependencies = [ 688 | "cfg-if", 689 | "fuchsia-zircon", 690 | "fuchsia-zircon-sys", 691 | "iovec", 692 | "kernel32-sys", 693 | "libc", 694 | "log", 695 | "miow", 696 | "net2", 697 | "slab", 698 | "winapi 0.2.8", 699 | ] 700 | 701 | [[package]] 702 | name = "miow" 703 | version = "0.2.1" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 706 | dependencies = [ 707 | "kernel32-sys", 708 | "net2", 709 | "winapi 0.2.8", 710 | "ws2_32-sys", 711 | ] 712 | 713 | [[package]] 714 | name = "native-tls" 715 | version = "0.2.4" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" 718 | dependencies = [ 719 | "lazy_static", 720 | "libc", 721 | "log", 722 | "openssl", 723 | "openssl-probe", 724 | "openssl-sys", 725 | "schannel", 726 | "security-framework", 727 | "security-framework-sys", 728 | "tempfile", 729 | ] 730 | 731 | [[package]] 732 | name = "net2" 733 | version = "0.2.34" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" 736 | dependencies = [ 737 | "cfg-if", 738 | "libc", 739 | "winapi 0.3.9", 740 | ] 741 | 742 | [[package]] 743 | name = "num-integer" 744 | version = "0.1.43" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" 747 | dependencies = [ 748 | "autocfg 1.0.1", 749 | "num-traits", 750 | ] 751 | 752 | [[package]] 753 | name = "num-traits" 754 | version = "0.2.12" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 757 | dependencies = [ 758 | "autocfg 1.0.1", 759 | ] 760 | 761 | [[package]] 762 | name = "num_cpus" 763 | version = "1.13.0" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 766 | dependencies = [ 767 | "hermit-abi", 768 | "libc", 769 | ] 770 | 771 | [[package]] 772 | name = "object" 773 | version = "0.20.0" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" 776 | 777 | [[package]] 778 | name = "openssl" 779 | version = "0.10.30" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" 782 | dependencies = [ 783 | "bitflags", 784 | "cfg-if", 785 | "foreign-types", 786 | "lazy_static", 787 | "libc", 788 | "openssl-sys", 789 | ] 790 | 791 | [[package]] 792 | name = "openssl-probe" 793 | version = "0.1.2" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" 796 | 797 | [[package]] 798 | name = "openssl-sys" 799 | version = "0.9.58" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" 802 | dependencies = [ 803 | "autocfg 1.0.1", 804 | "cc", 805 | "libc", 806 | "pkg-config", 807 | "vcpkg", 808 | ] 809 | 810 | [[package]] 811 | name = "parking_lot" 812 | version = "0.9.0" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" 815 | dependencies = [ 816 | "lock_api", 817 | "parking_lot_core", 818 | "rustc_version", 819 | ] 820 | 821 | [[package]] 822 | name = "parking_lot_core" 823 | version = "0.6.2" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" 826 | dependencies = [ 827 | "cfg-if", 828 | "cloudabi", 829 | "libc", 830 | "redox_syscall", 831 | "rustc_version", 832 | "smallvec", 833 | "winapi 0.3.9", 834 | ] 835 | 836 | [[package]] 837 | name = "percent-encoding" 838 | version = "1.0.1" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 841 | 842 | [[package]] 843 | name = "percent-encoding" 844 | version = "2.1.0" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 847 | 848 | [[package]] 849 | name = "pkg-config" 850 | version = "0.3.18" 851 | source = "registry+https://github.com/rust-lang/crates.io-index" 852 | checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" 853 | 854 | [[package]] 855 | name = "ppv-lite86" 856 | version = "0.2.9" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" 859 | 860 | [[package]] 861 | name = "proc-macro2" 862 | version = "1.0.24" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 865 | dependencies = [ 866 | "unicode-xid", 867 | ] 868 | 869 | [[package]] 870 | name = "publicsuffix" 871 | version = "1.5.4" 872 | source = "registry+https://github.com/rust-lang/crates.io-index" 873 | checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" 874 | dependencies = [ 875 | "error-chain", 876 | "idna 0.2.0", 877 | "lazy_static", 878 | "regex", 879 | "url 2.1.1", 880 | ] 881 | 882 | [[package]] 883 | name = "quick-error" 884 | version = "1.2.3" 885 | source = "registry+https://github.com/rust-lang/crates.io-index" 886 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 887 | 888 | [[package]] 889 | name = "quote" 890 | version = "1.0.7" 891 | source = "registry+https://github.com/rust-lang/crates.io-index" 892 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 893 | dependencies = [ 894 | "proc-macro2", 895 | ] 896 | 897 | [[package]] 898 | name = "rand" 899 | version = "0.6.5" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 902 | dependencies = [ 903 | "autocfg 0.1.7", 904 | "libc", 905 | "rand_chacha 0.1.1", 906 | "rand_core 0.4.2", 907 | "rand_hc 0.1.0", 908 | "rand_isaac", 909 | "rand_jitter", 910 | "rand_os", 911 | "rand_pcg", 912 | "rand_xorshift", 913 | "winapi 0.3.9", 914 | ] 915 | 916 | [[package]] 917 | name = "rand" 918 | version = "0.7.3" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 921 | dependencies = [ 922 | "getrandom", 923 | "libc", 924 | "rand_chacha 0.2.2", 925 | "rand_core 0.5.1", 926 | "rand_hc 0.2.0", 927 | ] 928 | 929 | [[package]] 930 | name = "rand_chacha" 931 | version = "0.1.1" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 934 | dependencies = [ 935 | "autocfg 0.1.7", 936 | "rand_core 0.3.1", 937 | ] 938 | 939 | [[package]] 940 | name = "rand_chacha" 941 | version = "0.2.2" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 944 | dependencies = [ 945 | "ppv-lite86", 946 | "rand_core 0.5.1", 947 | ] 948 | 949 | [[package]] 950 | name = "rand_core" 951 | version = "0.3.1" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 954 | dependencies = [ 955 | "rand_core 0.4.2", 956 | ] 957 | 958 | [[package]] 959 | name = "rand_core" 960 | version = "0.4.2" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 963 | 964 | [[package]] 965 | name = "rand_core" 966 | version = "0.5.1" 967 | source = "registry+https://github.com/rust-lang/crates.io-index" 968 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 969 | dependencies = [ 970 | "getrandom", 971 | ] 972 | 973 | [[package]] 974 | name = "rand_hc" 975 | version = "0.1.0" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 978 | dependencies = [ 979 | "rand_core 0.3.1", 980 | ] 981 | 982 | [[package]] 983 | name = "rand_hc" 984 | version = "0.2.0" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 987 | dependencies = [ 988 | "rand_core 0.5.1", 989 | ] 990 | 991 | [[package]] 992 | name = "rand_isaac" 993 | version = "0.1.1" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 996 | dependencies = [ 997 | "rand_core 0.3.1", 998 | ] 999 | 1000 | [[package]] 1001 | name = "rand_jitter" 1002 | version = "0.1.4" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" 1005 | dependencies = [ 1006 | "libc", 1007 | "rand_core 0.4.2", 1008 | "winapi 0.3.9", 1009 | ] 1010 | 1011 | [[package]] 1012 | name = "rand_os" 1013 | version = "0.1.3" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 1016 | dependencies = [ 1017 | "cloudabi", 1018 | "fuchsia-cprng", 1019 | "libc", 1020 | "rand_core 0.4.2", 1021 | "rdrand", 1022 | "winapi 0.3.9", 1023 | ] 1024 | 1025 | [[package]] 1026 | name = "rand_pcg" 1027 | version = "0.1.2" 1028 | source = "registry+https://github.com/rust-lang/crates.io-index" 1029 | checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 1030 | dependencies = [ 1031 | "autocfg 0.1.7", 1032 | "rand_core 0.4.2", 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "rand_xorshift" 1037 | version = "0.1.1" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 1040 | dependencies = [ 1041 | "rand_core 0.3.1", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "rdrand" 1046 | version = "0.4.0" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 1049 | dependencies = [ 1050 | "rand_core 0.3.1", 1051 | ] 1052 | 1053 | [[package]] 1054 | name = "redis" 1055 | version = "0.21.2" 1056 | source = "registry+https://github.com/rust-lang/crates.io-index" 1057 | checksum = "202c5bf92cad3d57605c366e644a7fbf305a83f19754fc66678c6265dcc9b8b4" 1058 | dependencies = [ 1059 | "async-trait", 1060 | "combine", 1061 | "dtoa", 1062 | "itoa", 1063 | "percent-encoding 2.1.0", 1064 | "sha1", 1065 | "url 2.1.1", 1066 | ] 1067 | 1068 | [[package]] 1069 | name = "redox_syscall" 1070 | version = "0.1.57" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 1073 | 1074 | [[package]] 1075 | name = "regex" 1076 | version = "1.8.0" 1077 | source = "registry+https://github.com/rust-lang/crates.io-index" 1078 | checksum = "ac6cf59af1067a3fb53fbe5c88c053764e930f932be1d71d3ffe032cbe147f59" 1079 | dependencies = [ 1080 | "aho-corasick", 1081 | "memchr", 1082 | "regex-syntax", 1083 | ] 1084 | 1085 | [[package]] 1086 | name = "regex-syntax" 1087 | version = "0.7.0" 1088 | source = "registry+https://github.com/rust-lang/crates.io-index" 1089 | checksum = "b6868896879ba532248f33598de5181522d8b3d9d724dfd230911e1a7d4822f5" 1090 | 1091 | [[package]] 1092 | name = "remove_dir_all" 1093 | version = "0.5.3" 1094 | source = "registry+https://github.com/rust-lang/crates.io-index" 1095 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1096 | dependencies = [ 1097 | "winapi 0.3.9", 1098 | ] 1099 | 1100 | [[package]] 1101 | name = "reqwest" 1102 | version = "0.9.24" 1103 | source = "registry+https://github.com/rust-lang/crates.io-index" 1104 | checksum = "f88643aea3c1343c804950d7bf983bd2067f5ab59db6d613a08e05572f2714ab" 1105 | dependencies = [ 1106 | "base64", 1107 | "bytes 0.4.12", 1108 | "cookie", 1109 | "cookie_store", 1110 | "encoding_rs", 1111 | "flate2", 1112 | "futures", 1113 | "http", 1114 | "hyper", 1115 | "hyper-tls", 1116 | "log", 1117 | "mime", 1118 | "mime_guess", 1119 | "native-tls", 1120 | "serde", 1121 | "serde_json", 1122 | "serde_urlencoded", 1123 | "time", 1124 | "tokio", 1125 | "tokio-executor", 1126 | "tokio-io", 1127 | "tokio-threadpool", 1128 | "tokio-timer", 1129 | "url 1.7.2", 1130 | "uuid", 1131 | "winreg", 1132 | ] 1133 | 1134 | [[package]] 1135 | name = "resc" 1136 | version = "0.3.4" 1137 | dependencies = [ 1138 | "anyhow", 1139 | "chrono", 1140 | "deser-hjson", 1141 | "env_logger", 1142 | "lazy_static", 1143 | "log", 1144 | "redis", 1145 | "regex", 1146 | "reqwest", 1147 | "serde", 1148 | "serde_json", 1149 | "serde_regex", 1150 | "thiserror", 1151 | ] 1152 | 1153 | [[package]] 1154 | name = "rustc-demangle" 1155 | version = "0.1.16" 1156 | source = "registry+https://github.com/rust-lang/crates.io-index" 1157 | checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" 1158 | 1159 | [[package]] 1160 | name = "rustc_version" 1161 | version = "0.2.3" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 1164 | dependencies = [ 1165 | "semver", 1166 | ] 1167 | 1168 | [[package]] 1169 | name = "ryu" 1170 | version = "1.0.5" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1173 | 1174 | [[package]] 1175 | name = "schannel" 1176 | version = "0.1.19" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 1179 | dependencies = [ 1180 | "lazy_static", 1181 | "winapi 0.3.9", 1182 | ] 1183 | 1184 | [[package]] 1185 | name = "scopeguard" 1186 | version = "1.1.0" 1187 | source = "registry+https://github.com/rust-lang/crates.io-index" 1188 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1189 | 1190 | [[package]] 1191 | name = "security-framework" 1192 | version = "0.4.4" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" 1195 | dependencies = [ 1196 | "bitflags", 1197 | "core-foundation", 1198 | "core-foundation-sys", 1199 | "libc", 1200 | "security-framework-sys", 1201 | ] 1202 | 1203 | [[package]] 1204 | name = "security-framework-sys" 1205 | version = "0.4.3" 1206 | source = "registry+https://github.com/rust-lang/crates.io-index" 1207 | checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" 1208 | dependencies = [ 1209 | "core-foundation-sys", 1210 | "libc", 1211 | ] 1212 | 1213 | [[package]] 1214 | name = "semver" 1215 | version = "0.9.0" 1216 | source = "registry+https://github.com/rust-lang/crates.io-index" 1217 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 1218 | dependencies = [ 1219 | "semver-parser", 1220 | ] 1221 | 1222 | [[package]] 1223 | name = "semver-parser" 1224 | version = "0.7.0" 1225 | source = "registry+https://github.com/rust-lang/crates.io-index" 1226 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 1227 | 1228 | [[package]] 1229 | name = "serde" 1230 | version = "1.0.115" 1231 | source = "registry+https://github.com/rust-lang/crates.io-index" 1232 | checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" 1233 | dependencies = [ 1234 | "serde_derive", 1235 | ] 1236 | 1237 | [[package]] 1238 | name = "serde_derive" 1239 | version = "1.0.115" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" 1242 | dependencies = [ 1243 | "proc-macro2", 1244 | "quote", 1245 | "syn", 1246 | ] 1247 | 1248 | [[package]] 1249 | name = "serde_json" 1250 | version = "1.0.57" 1251 | source = "registry+https://github.com/rust-lang/crates.io-index" 1252 | checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" 1253 | dependencies = [ 1254 | "itoa", 1255 | "ryu", 1256 | "serde", 1257 | ] 1258 | 1259 | [[package]] 1260 | name = "serde_regex" 1261 | version = "1.1.0" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" 1264 | dependencies = [ 1265 | "regex", 1266 | "serde", 1267 | ] 1268 | 1269 | [[package]] 1270 | name = "serde_urlencoded" 1271 | version = "0.5.5" 1272 | source = "registry+https://github.com/rust-lang/crates.io-index" 1273 | checksum = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" 1274 | dependencies = [ 1275 | "dtoa", 1276 | "itoa", 1277 | "serde", 1278 | "url 1.7.2", 1279 | ] 1280 | 1281 | [[package]] 1282 | name = "sha1" 1283 | version = "0.6.0" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" 1286 | 1287 | [[package]] 1288 | name = "slab" 1289 | version = "0.4.2" 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" 1291 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 1292 | 1293 | [[package]] 1294 | name = "smallvec" 1295 | version = "0.6.13" 1296 | source = "registry+https://github.com/rust-lang/crates.io-index" 1297 | checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" 1298 | dependencies = [ 1299 | "maybe-uninit", 1300 | ] 1301 | 1302 | [[package]] 1303 | name = "string" 1304 | version = "0.2.1" 1305 | source = "registry+https://github.com/rust-lang/crates.io-index" 1306 | checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" 1307 | dependencies = [ 1308 | "bytes 0.4.12", 1309 | ] 1310 | 1311 | [[package]] 1312 | name = "syn" 1313 | version = "1.0.60" 1314 | source = "registry+https://github.com/rust-lang/crates.io-index" 1315 | checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" 1316 | dependencies = [ 1317 | "proc-macro2", 1318 | "quote", 1319 | "unicode-xid", 1320 | ] 1321 | 1322 | [[package]] 1323 | name = "synstructure" 1324 | version = "0.12.4" 1325 | source = "registry+https://github.com/rust-lang/crates.io-index" 1326 | checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" 1327 | dependencies = [ 1328 | "proc-macro2", 1329 | "quote", 1330 | "syn", 1331 | "unicode-xid", 1332 | ] 1333 | 1334 | [[package]] 1335 | name = "tempfile" 1336 | version = "3.1.0" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 1339 | dependencies = [ 1340 | "cfg-if", 1341 | "libc", 1342 | "rand 0.7.3", 1343 | "redox_syscall", 1344 | "remove_dir_all", 1345 | "winapi 0.3.9", 1346 | ] 1347 | 1348 | [[package]] 1349 | name = "termcolor" 1350 | version = "1.1.0" 1351 | source = "registry+https://github.com/rust-lang/crates.io-index" 1352 | checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 1353 | dependencies = [ 1354 | "winapi-util", 1355 | ] 1356 | 1357 | [[package]] 1358 | name = "thiserror" 1359 | version = "1.0.23" 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" 1361 | checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" 1362 | dependencies = [ 1363 | "thiserror-impl", 1364 | ] 1365 | 1366 | [[package]] 1367 | name = "thiserror-impl" 1368 | version = "1.0.23" 1369 | source = "registry+https://github.com/rust-lang/crates.io-index" 1370 | checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" 1371 | dependencies = [ 1372 | "proc-macro2", 1373 | "quote", 1374 | "syn", 1375 | ] 1376 | 1377 | [[package]] 1378 | name = "time" 1379 | version = "0.1.44" 1380 | source = "registry+https://github.com/rust-lang/crates.io-index" 1381 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 1382 | dependencies = [ 1383 | "libc", 1384 | "wasi 0.10.0+wasi-snapshot-preview1", 1385 | "winapi 0.3.9", 1386 | ] 1387 | 1388 | [[package]] 1389 | name = "tinyvec" 1390 | version = "0.3.4" 1391 | source = "registry+https://github.com/rust-lang/crates.io-index" 1392 | checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" 1393 | 1394 | [[package]] 1395 | name = "tokio" 1396 | version = "0.1.22" 1397 | source = "registry+https://github.com/rust-lang/crates.io-index" 1398 | checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" 1399 | dependencies = [ 1400 | "bytes 0.4.12", 1401 | "futures", 1402 | "mio", 1403 | "num_cpus", 1404 | "tokio-current-thread", 1405 | "tokio-executor", 1406 | "tokio-io", 1407 | "tokio-reactor", 1408 | "tokio-tcp", 1409 | "tokio-threadpool", 1410 | "tokio-timer", 1411 | ] 1412 | 1413 | [[package]] 1414 | name = "tokio-buf" 1415 | version = "0.1.1" 1416 | source = "registry+https://github.com/rust-lang/crates.io-index" 1417 | checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" 1418 | dependencies = [ 1419 | "bytes 0.4.12", 1420 | "either", 1421 | "futures", 1422 | ] 1423 | 1424 | [[package]] 1425 | name = "tokio-current-thread" 1426 | version = "0.1.7" 1427 | source = "registry+https://github.com/rust-lang/crates.io-index" 1428 | checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" 1429 | dependencies = [ 1430 | "futures", 1431 | "tokio-executor", 1432 | ] 1433 | 1434 | [[package]] 1435 | name = "tokio-executor" 1436 | version = "0.1.10" 1437 | source = "registry+https://github.com/rust-lang/crates.io-index" 1438 | checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" 1439 | dependencies = [ 1440 | "crossbeam-utils", 1441 | "futures", 1442 | ] 1443 | 1444 | [[package]] 1445 | name = "tokio-io" 1446 | version = "0.1.13" 1447 | source = "registry+https://github.com/rust-lang/crates.io-index" 1448 | checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" 1449 | dependencies = [ 1450 | "bytes 0.4.12", 1451 | "futures", 1452 | "log", 1453 | ] 1454 | 1455 | [[package]] 1456 | name = "tokio-reactor" 1457 | version = "0.1.12" 1458 | source = "registry+https://github.com/rust-lang/crates.io-index" 1459 | checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" 1460 | dependencies = [ 1461 | "crossbeam-utils", 1462 | "futures", 1463 | "lazy_static", 1464 | "log", 1465 | "mio", 1466 | "num_cpus", 1467 | "parking_lot", 1468 | "slab", 1469 | "tokio-executor", 1470 | "tokio-io", 1471 | "tokio-sync", 1472 | ] 1473 | 1474 | [[package]] 1475 | name = "tokio-sync" 1476 | version = "0.1.8" 1477 | source = "registry+https://github.com/rust-lang/crates.io-index" 1478 | checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" 1479 | dependencies = [ 1480 | "fnv", 1481 | "futures", 1482 | ] 1483 | 1484 | [[package]] 1485 | name = "tokio-tcp" 1486 | version = "0.1.4" 1487 | source = "registry+https://github.com/rust-lang/crates.io-index" 1488 | checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" 1489 | dependencies = [ 1490 | "bytes 0.4.12", 1491 | "futures", 1492 | "iovec", 1493 | "mio", 1494 | "tokio-io", 1495 | "tokio-reactor", 1496 | ] 1497 | 1498 | [[package]] 1499 | name = "tokio-threadpool" 1500 | version = "0.1.18" 1501 | source = "registry+https://github.com/rust-lang/crates.io-index" 1502 | checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" 1503 | dependencies = [ 1504 | "crossbeam-deque", 1505 | "crossbeam-queue", 1506 | "crossbeam-utils", 1507 | "futures", 1508 | "lazy_static", 1509 | "log", 1510 | "num_cpus", 1511 | "slab", 1512 | "tokio-executor", 1513 | ] 1514 | 1515 | [[package]] 1516 | name = "tokio-timer" 1517 | version = "0.2.13" 1518 | source = "registry+https://github.com/rust-lang/crates.io-index" 1519 | checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" 1520 | dependencies = [ 1521 | "crossbeam-utils", 1522 | "futures", 1523 | "slab", 1524 | "tokio-executor", 1525 | ] 1526 | 1527 | [[package]] 1528 | name = "try-lock" 1529 | version = "0.2.3" 1530 | source = "registry+https://github.com/rust-lang/crates.io-index" 1531 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1532 | 1533 | [[package]] 1534 | name = "try_from" 1535 | version = "0.3.2" 1536 | source = "registry+https://github.com/rust-lang/crates.io-index" 1537 | checksum = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b" 1538 | dependencies = [ 1539 | "cfg-if", 1540 | ] 1541 | 1542 | [[package]] 1543 | name = "unicase" 1544 | version = "2.6.0" 1545 | source = "registry+https://github.com/rust-lang/crates.io-index" 1546 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1547 | dependencies = [ 1548 | "version_check", 1549 | ] 1550 | 1551 | [[package]] 1552 | name = "unicode-bidi" 1553 | version = "0.3.4" 1554 | source = "registry+https://github.com/rust-lang/crates.io-index" 1555 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1556 | dependencies = [ 1557 | "matches", 1558 | ] 1559 | 1560 | [[package]] 1561 | name = "unicode-normalization" 1562 | version = "0.1.13" 1563 | source = "registry+https://github.com/rust-lang/crates.io-index" 1564 | checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" 1565 | dependencies = [ 1566 | "tinyvec", 1567 | ] 1568 | 1569 | [[package]] 1570 | name = "unicode-xid" 1571 | version = "0.2.1" 1572 | source = "registry+https://github.com/rust-lang/crates.io-index" 1573 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1574 | 1575 | [[package]] 1576 | name = "url" 1577 | version = "1.7.2" 1578 | source = "registry+https://github.com/rust-lang/crates.io-index" 1579 | checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" 1580 | dependencies = [ 1581 | "idna 0.1.5", 1582 | "matches", 1583 | "percent-encoding 1.0.1", 1584 | ] 1585 | 1586 | [[package]] 1587 | name = "url" 1588 | version = "2.1.1" 1589 | source = "registry+https://github.com/rust-lang/crates.io-index" 1590 | checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" 1591 | dependencies = [ 1592 | "idna 0.2.0", 1593 | "matches", 1594 | "percent-encoding 2.1.0", 1595 | ] 1596 | 1597 | [[package]] 1598 | name = "uuid" 1599 | version = "0.7.4" 1600 | source = "registry+https://github.com/rust-lang/crates.io-index" 1601 | checksum = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" 1602 | dependencies = [ 1603 | "rand 0.6.5", 1604 | ] 1605 | 1606 | [[package]] 1607 | name = "vcpkg" 1608 | version = "0.2.10" 1609 | source = "registry+https://github.com/rust-lang/crates.io-index" 1610 | checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" 1611 | 1612 | [[package]] 1613 | name = "version_check" 1614 | version = "0.9.2" 1615 | source = "registry+https://github.com/rust-lang/crates.io-index" 1616 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 1617 | 1618 | [[package]] 1619 | name = "want" 1620 | version = "0.2.0" 1621 | source = "registry+https://github.com/rust-lang/crates.io-index" 1622 | checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" 1623 | dependencies = [ 1624 | "futures", 1625 | "log", 1626 | "try-lock", 1627 | ] 1628 | 1629 | [[package]] 1630 | name = "wasi" 1631 | version = "0.9.0+wasi-snapshot-preview1" 1632 | source = "registry+https://github.com/rust-lang/crates.io-index" 1633 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1634 | 1635 | [[package]] 1636 | name = "wasi" 1637 | version = "0.10.0+wasi-snapshot-preview1" 1638 | source = "registry+https://github.com/rust-lang/crates.io-index" 1639 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1640 | 1641 | [[package]] 1642 | name = "winapi" 1643 | version = "0.2.8" 1644 | source = "registry+https://github.com/rust-lang/crates.io-index" 1645 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1646 | 1647 | [[package]] 1648 | name = "winapi" 1649 | version = "0.3.9" 1650 | source = "registry+https://github.com/rust-lang/crates.io-index" 1651 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1652 | dependencies = [ 1653 | "winapi-i686-pc-windows-gnu", 1654 | "winapi-x86_64-pc-windows-gnu", 1655 | ] 1656 | 1657 | [[package]] 1658 | name = "winapi-build" 1659 | version = "0.1.1" 1660 | source = "registry+https://github.com/rust-lang/crates.io-index" 1661 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1662 | 1663 | [[package]] 1664 | name = "winapi-i686-pc-windows-gnu" 1665 | version = "0.4.0" 1666 | source = "registry+https://github.com/rust-lang/crates.io-index" 1667 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1668 | 1669 | [[package]] 1670 | name = "winapi-util" 1671 | version = "0.1.5" 1672 | source = "registry+https://github.com/rust-lang/crates.io-index" 1673 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1674 | dependencies = [ 1675 | "winapi 0.3.9", 1676 | ] 1677 | 1678 | [[package]] 1679 | name = "winapi-x86_64-pc-windows-gnu" 1680 | version = "0.4.0" 1681 | source = "registry+https://github.com/rust-lang/crates.io-index" 1682 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1683 | 1684 | [[package]] 1685 | name = "winreg" 1686 | version = "0.6.2" 1687 | source = "registry+https://github.com/rust-lang/crates.io-index" 1688 | checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" 1689 | dependencies = [ 1690 | "winapi 0.3.9", 1691 | ] 1692 | 1693 | [[package]] 1694 | name = "ws2_32-sys" 1695 | version = "0.2.1" 1696 | source = "registry+https://github.com/rust-lang/crates.io-index" 1697 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 1698 | dependencies = [ 1699 | "winapi 0.2.8", 1700 | "winapi-build", 1701 | ] 1702 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "resc" 3 | version = "0.3.4" 4 | authors = ["Canop "] 5 | edition = "2018" 6 | description = "A Redis based task orchestrator" 7 | repository = "https://github.com/Canop/resc" 8 | license = "MIT" 9 | 10 | [dependencies] 11 | anyhow = "1.0" 12 | chrono = "0.4" 13 | deser-hjson = "1.1.0" 14 | env_logger = "0.5.13" 15 | lazy_static = "1.4" 16 | log = "0.4" 17 | redis = "0.21.2" 18 | regex = "1.8" 19 | reqwest = "0.9" 20 | serde = { version = "1.0", features = ["derive"] } 21 | serde_json = "1.0" 22 | serde_regex = "1.1" 23 | thiserror = "1.0" 24 | 25 | [patch.crates-io] 26 | # deser-hjson = { path = "../deser-hjson" } 27 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:latest as builder 2 | WORKDIR /usr/src/resc 3 | COPY . . 4 | RUN cargo install --path . 5 | 6 | FROM debian:buster-slim 7 | RUN apt-get update && apt-get install -y openssl 8 | COPY --from=builder /usr/local/cargo/bin/resc /usr/local/bin/resc 9 | 10 | RUN mkdir /usr/local/resc 11 | VOLUME /usr/local/resc 12 | 13 | ENTRYPOINT ["resc"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![MIT][s2]][l2] [![Latest Version][s1]][l1] 2 | 3 | [s1]: https://img.shields.io/crates/v/resc.svg 4 | [l1]: https://crates.io/crates/resc 5 | 6 | [s2]: https://img.shields.io/badge/license-MIT-blue.svg 7 | [l2]: LICENSE 8 | 9 | # Purpose 10 | 11 | Redis lists are wonderful as task queues for distributed workers. A worker can safely and atomically take a task, even when several ones watch the same queue. 12 | 13 | **Resc** is a reliable and configurable task generator for redis. 14 | 15 | Rules and Redis queues are defined in a configuration file, which can be in [JSON](https://json.org) or [Hjson](https://hjson.github.io/). 16 | 17 | It watches one or several queues for events, which can be task completion notifications or simple "root" events, and applies rules to generate tasks. 18 | 19 | It achieves this in a safe and monitorable way and takes care, for example, of avoiding duplicating tasks. 20 | 21 | Resc is written in rust for safety and performance. 22 | 23 | # How it generally works 24 | 25 | ## Queues setup 26 | 27 | Here we'll have a very simple setup, with only 4 Redis queues and only one type of tasks (the "tasks-A") and two workers ready to do them. 28 | 29 | We won't show how tasks are deduplicated, logged, or published for supervision. 30 | 31 | ### Some Event Generator pushes an event to the "global/done" input queue 32 | 33 | ![lpush event](doc/flow-01-lpush-event.png) 34 | 35 | Instead of just a "global" queue, there could be several input queues but one is often enough even with hundreds of event sources. 36 | 37 | ### Resc takes the event 38 | 39 | ![brpoplpush event](doc/flow-02-brpoplpush-event.png) 40 | 41 | `brpoplpush` is an atomic operation, the event is guaranteed to not be lost: it's either in global/done or in global/taken. 42 | 43 | ### Resc applies its rules to generate zero or more tasks 44 | 45 | 46 | ![lpush task](doc/flow-03-lpush-task.png) 47 | 48 | 49 | ### Resc removes the event from global/taken 50 | 51 | ![lrem event](doc/flow-04-lrem-event.png) 52 | 53 | It was kept here so that the rules could be played again on restore in case of crash during rules computation (some rules may imply calling a server). 54 | 55 | 56 | ### A worker takes the task 57 | 58 | ![brpoplpush task](doc/flow-05-brpoplpush-task.png) 59 | 60 | Once again, `brpoplpush` is an atomic operation, the worker may die during effort, knowing the task won't be lost 61 | 62 | ### When the job is done, the worker notifies it with an event 63 | 64 | ![lpush done](doc/flow-06-lpush-done.png) 65 | 66 | 67 | ### ... then removes the task from its taken queue 68 | 69 | ![lrem task](doc/flow-07-lrem-task.png) 70 | 71 | ## ... and it goes on 72 | 73 | Resc will now apply its rules to the `done-1` event, which may lead to new tasks, or maybe there's nothing more to do. 74 | 75 | Of course everything can happen with thousands of events, tasks and even workers. 76 | 77 | # Workers 78 | 79 | Resc, as a scheduler, assumes workers handle tasks in this very simple way: 80 | 81 | 1. pick a task in a queue and atomically move it to a "taken" list : `BRPOPLPUSH myqueue/todo myqueue/taken 0` 82 | 83 | 2. do the task 84 | 85 | 3. clean the "taken" list : `LREM myqueue/taken the-task` 86 | 87 | 4. notify the scheduler the task is done by pushing it to a "done" queue: `LPUSH global/done the-task` 88 | 89 | This scheme ensures several workers can safely work on the same queue. 90 | 91 | You often want the queues to be free of duplicates. 92 | You may still want to queue tasks while they are being processed (for example you may want a recomputation because some new info arrived). 93 | 94 | If you want deduplicating of a task queue, you declare a task set in the configuration and the worker, just after having picked the task from the queue and before executing it, remove it from the set too. 95 | 96 | Java, Go, Rust and node.js implementations of workers are provided in the examples directory. 97 | They all show how to use (or not) the deduplicating queue. 98 | 99 | # Introductory Example 100 | 101 | The complete instructions on executing this example, and a business logic explanation, are available at [examples/simple-example.md](examples/simple-example.md). 102 | 103 | ## Simple regex based task generation 104 | 105 | Here's a simple Hjson configuration file: 106 | 107 | { 108 | redis: { 109 | url: "redis://127.0.0.1/" 110 | } 111 | watchers: [ 112 | { 113 | input_queue: global/events 114 | taken_queue: global/taken 115 | rules: [ 116 | { 117 | name: TRT computation on data acquisition 118 | on: "^acq/(?P\\w+)/(?P\\w+)$" 119 | make: { 120 | task: "trt/${process_id}/${product_id}" 121 | queue: "trt/${process_id}/todo-queue" 122 | set: "trt/${process_id}/todo-set" 123 | } 124 | } 125 | ] 126 | } 127 | ] 128 | } 129 | 130 | Resc can be launched with this configuration using 131 | 132 | resc myconf.hjson 133 | 134 | Resc starts a watcher, a thread, over the specified `input_queue`. 135 | 136 | When a new event (a string in the `global/events` list) appears, it's atomically moved (using [BRPOPLPUSH](https://redis.io/commands/brpoplpush)) to the `global/taken` list and watcher's rules are executed. 137 | 138 | Assuming the coming task is `"acq/123/456"`, then the first (and unique) rule of our example will match, according to the regular expression in `"on""`. 139 | 140 | Several variables are dynamically generated and valued: 141 | 142 | process_id = 123 143 | product_id = 456 144 | 145 | Those variables are used to extrapolate the task and queue of the todo part of the rule. 146 | 147 | The task `"trt/123/456"` would then be created. 148 | 149 | If the `"trt/123/todo-set"` set doesn't contain the task already, then it's added to that set (with the time which may be used for monitoring) then to the `"trt/123/todo-queue"` queue. 150 | 151 | After having executed all rules on this task, it's cleared from the `"global/taken"` queue and the watcher goes on watching the `"global/events"` queue again for other tasks. 152 | 153 | ### Logging 154 | 155 | You don't usually want a lot of log, that's why the default log includes only warnings, but during the setup of your system you might want to see what events comes in your queues and what tasks are generated. 156 | 157 | You can see more by setting the log level to `INFO`: 158 | 159 | RUST_LOG="info" resc myconf.hjson 160 | 161 | or if you want to see what rules were activated: 162 | 163 | RUST_LOG="debug" resc myconf.hjson 164 | 165 | ## Fetching some data to compute new tasks 166 | 167 | Sometimes it might be necessary to query a web service to compute the tasks to generate in response to an event. 168 | 169 | Let's say there is a REST service returning the elements which would be logically impacted when some other one change (for example a change in a customer command might involve the recomputing of some product validity for that command). 170 | 171 | If there's certain event on product 5ab7342600000040, you want to query 172 | 173 | http://my-web-service/products/5ab7342600000040/direct-children 174 | 175 | which responds in JSON with the list of products which should be recomputed: 176 | 177 | [ 178 | {"processId":634876914,"productId":"5ab7e7dc00000040"}, 179 | {"processId":634876914,"productId":"5ab7ebe800000040"} 180 | ] 181 | 182 | and for each of those products you want to generate a new task. 183 | 184 | Then the relevant rule could be like this: 185 | 186 | { 187 | name: TRT propagation to children 188 | on: "^trt/(?P\\d+)/(?P\\w{16})$" 189 | fetch: [{ 190 | url: "http://my-web-service/products/${product_id}/direct-children" 191 | returns: child 192 | }] 193 | todo: { 194 | task: "trt/${child.processId}/${child.productId}" 195 | queue: "trt/${child.processId}/todo-queue" 196 | set: "trt/${child.processId}/todo-set" 197 | } 198 | } 199 | 200 | The `fetch` element describes the HTTP query and the namespace of the variables read in the web-service's response and used for generation of tasks, queues and sets. 201 | 202 | In our example, we'd end with two new tasks, `"trt/634876914/5ab7e7dc00000040"` (added to queue `"trt/634876914/todo-queue"`), and `"trt/634876914/5ab7ebe800000040"` (added to queue `"trt/634876914/todo-queue"`). 203 | 204 | ## Switching queues, default configuration values 205 | 206 | When you have several rules and one of them involves querying a remote service as in our example, you don't want all the rules to suffer from a possible slow-down of this remote service. 207 | 208 | That's when you may want to have another watcher, and thread, handling those specific task generations. 209 | 210 | In order to do that, you want a rule just passing the task to another queue which another watcher watches. 211 | 212 | Let's call this new queue `global/to-propagate` (you give your queues the names you want). 213 | 214 | The new configuration becomes 215 | 216 | { 217 | "redis": { 218 | "url": "redis://127.0.0.1/" 219 | }, 220 | "watchers": [ 221 | { 222 | "input_queue": "global/events", 223 | "taken_queue": "global/taken", 224 | "rules": [ 225 | { 226 | "name": "TRT computation on data acquisition", 227 | "on": "^acq/(?P\\d+)/(?P\\d+)$", 228 | "make": { 229 | "task": "trt/${process_id}/${product_id}", 230 | "queue": "trt/${process_id}/todo-queue", 231 | "set": "trt/${process_id}/todo-set" 232 | } 233 | }, 234 | { 235 | "name": "TRT propagation to children : switch queue", 236 | "on": "^trt/(?P\\d+)/(?P\\w{16})$", 237 | "make": { 238 | "queue": "global/to-propagate" 239 | } 240 | } 241 | ] 242 | }, 243 | { 244 | "input_queue": "global/to-propagate", 245 | "rules": [ 246 | { 247 | "name": "TRT propagation to children : make child tasks", 248 | "on": "^trt/(?P\\d+)/(?P\\w{16})$", 249 | "fetch": [{ 250 | "url": "http://my-web-service/products/${product_id}/direct-children", 251 | "returns": "child" 252 | }], 253 | "make": { 254 | "task": "trt/${child.processId}/${child.productId}", 255 | "queue": "trt/${child.processId}/todo-queue", 256 | "set": "trt/${child.processId}/todo-set" 257 | } 258 | } 259 | ] 260 | } 261 | ] 262 | } 263 | 264 | This way no remote service can slow down the global queue managment. 265 | 266 | You may have noticed the configuration is a little lighter than what could have been expected. It's because some settings are optional. 267 | 268 | When omitted, `taken_queue` is simply `input_queue` with `/taken` added. So here the second watcher would use as temporary queue `global/to-propagate/taken`. 269 | 270 | When `make/task` is omitted, the generated task is the same string as the input task. More precisely, the default value of `make/task` is `"${input_task}"`, `${input_task}` being a variable you can use in your task/queue/set generation. 271 | 272 | # License 273 | 274 | MIT 275 | -------------------------------------------------------------------------------- /bacon.toml: -------------------------------------------------------------------------------- 1 | 2 | # This is a configuration file for the bacon tool 3 | # More info at https://github.com/Canop/bacon 4 | 5 | default_job = "check" 6 | 7 | [jobs] 8 | 9 | [jobs.check] 10 | command = ["cargo", "check", "--color", "always"] 11 | need_stdout = false 12 | 13 | [jobs.check-all] 14 | command = ["cargo", "check", "--all-targets", "--color", "always"] 15 | need_stdout = false 16 | 17 | [jobs.light] 18 | command = ["cargo", "check", "--color", "always"] 19 | need_stdout = false 20 | 21 | [jobs.clippy] 22 | command = ["cargo", "clippy", "--color", "always"] 23 | need_stdout = false 24 | 25 | [jobs.test] 26 | command = ["cargo", "test", "--color", "always"] 27 | need_stdout = true 28 | 29 | -------------------------------------------------------------------------------- /compile-all-targets.sh: -------------------------------------------------------------------------------- 1 | # WARNING: This script is NOT meant for normal installation, it's dedicated 2 | # to the compilation of all supported targets, from a linux machine. 3 | # This is a long process and it involves specialized toolchains. 4 | # For usual compilation do 5 | # cargo build --release 6 | 7 | H1="\n\e[30;104;1m\e[2K\n\e[A" # style first header 8 | H2="\n\e[30;104m\e[1K\n\e[A" # style second header 9 | EH="\e[00m\n\e[2K" # end header 10 | 11 | version=$(sed 's/version = "\([0-9.]\{1,\}\(-[a-z]\+\)\?\)"/\1/;t;d' Cargo.toml | head -1) 12 | echo -e "${H1}Compilation of all targets for resc $version${EH}" 13 | 14 | # clean previous build 15 | rm -rf build 16 | mkdir build 17 | echo " build cleaned" 18 | 19 | # build the linux version 20 | echo -e "${H2}Compiling the linux version${EH}" 21 | cargo build --release 22 | strip target/release/resc 23 | mkdir build/x86_64-linux/ 24 | cp target/release/resc build/x86_64-linux/ 25 | 26 | -------------------------------------------------------------------------------- /doc/flow-01-lpush-event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/resc/4d67d972522304404e5469044c0fe40a4e4889e6/doc/flow-01-lpush-event.png -------------------------------------------------------------------------------- /doc/flow-02-brpoplpush-event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/resc/4d67d972522304404e5469044c0fe40a4e4889e6/doc/flow-02-brpoplpush-event.png -------------------------------------------------------------------------------- /doc/flow-03-lpush-task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/resc/4d67d972522304404e5469044c0fe40a4e4889e6/doc/flow-03-lpush-task.png -------------------------------------------------------------------------------- /doc/flow-04-lrem-event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/resc/4d67d972522304404e5469044c0fe40a4e4889e6/doc/flow-04-lrem-event.png -------------------------------------------------------------------------------- /doc/flow-05-brpoplpush-task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/resc/4d67d972522304404e5469044c0fe40a4e4889e6/doc/flow-05-brpoplpush-task.png -------------------------------------------------------------------------------- /doc/flow-06-lpush-done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/resc/4d67d972522304404e5469044c0fe40a4e4889e6/doc/flow-06-lpush-done.png -------------------------------------------------------------------------------- /doc/flow-07-lrem-task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/resc/4d67d972522304404e5469044c0fe40a4e4889e6/doc/flow-07-lrem-task.png -------------------------------------------------------------------------------- /examples/complexe-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "redis": { 3 | "url": "redis://127.0.0.1/" 4 | }, 5 | "listener_channel": "events", 6 | "watchers": [ 7 | { 8 | "input_queue": "global/events", 9 | "taken_queue": "global/taken", 10 | "rules": [ 11 | { 12 | "name": "TRT computation on data acquisition", 13 | "on": "^acq/(?P\\w+)/(?P\\d+)$", 14 | "make": { 15 | "task": "trt/${process_id}/${product_id}", 16 | "queue": "trt/${process_id}/todo-queue", 17 | "set": "trt/${process_id}/todo-set" 18 | } 19 | }, 20 | { 21 | "name": "TRT propagation to children : switch queue", 22 | "on": "^trt/(?P\\w+)/(?P\\w{16})$", 23 | "make": { 24 | "queue": "global/to-propagate" 25 | } 26 | } 27 | ] 28 | }, 29 | { 30 | "input_queue": "global/to-propagate", 31 | "rules": [ 32 | { 33 | "name": "TRT propagation to children : make child tasks", 34 | "on": "^trt/(?P\\w+)/(?P\\w{16})$", 35 | "fetch": [{ 36 | "url": "http://localhost:8080/eyeron/pub/products/${product_id}/direct-childs", 37 | "returns": "child" 38 | }], 39 | "make": { 40 | "task": "trt/${child.processId}/${child.productId}", 41 | "queue": "trt/${child.processId}/todo" 42 | } 43 | } 44 | ] 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /examples/doc/simple-example-complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/resc/4d67d972522304404e5469044c0fe40a4e4889e6/examples/doc/simple-example-complete.png -------------------------------------------------------------------------------- /examples/doc/simple-example-generated-tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/resc/4d67d972522304404e5469044c0fe40a4e4889e6/examples/doc/simple-example-generated-tasks.png -------------------------------------------------------------------------------- /examples/go-client/worker.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/garyburd/redigo/redis" 6 | "log" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | const REDIS_URL = "redis://127.0.0.1" 12 | const INPUT_QUEUE = "trt/plantA/todo-queue" 13 | const INPUT_SET = "trt/plantA/todo-set" // set to "" if you don't use a set for deduplicating 14 | const TAKEN_QUEUE = "trt/plantA/taken" 15 | const OUTPUT_QUEUE = "global/events" 16 | 17 | func handleTask(task string) { 18 | tokens := strings.Split(task, "/") 19 | if len(tokens) != 3 { 20 | log.Printf("Illegal task : %v", task) 21 | return 22 | } 23 | nature, process, product := tokens[0], tokens[1], tokens[2] 24 | fmt.Printf("Executing %s for product %s on process %s ", nature, product, process) 25 | for i := 0; i < 10; i++ { 26 | time.Sleep(time.Second) 27 | fmt.Print(".") 28 | } 29 | log.Println(" done") 30 | } 31 | 32 | func main() { 33 | con, err := redis.DialURL(REDIS_URL) 34 | if err != nil { 35 | log.Fatalf("Could not connect: %v\n", err) 36 | } 37 | defer con.Close() 38 | log.Printf("Worker listening on queue %+v\n", INPUT_QUEUE) 39 | log.Printf(" connected\n") 40 | for { 41 | task, _ := redis.String(con.Do("BRPOPLPUSH", INPUT_QUEUE, TAKEN_QUEUE, 60)) 42 | if task != "" { 43 | if INPUT_SET != "" { 44 | if _, err = con.Do("ZREM", INPUT_SET, task); err != nil { 45 | log.Fatalf("Error in LPUSH: %v\n", err) 46 | } 47 | } 48 | handleTask(task) 49 | if _, err = con.Do("LPUSH", OUTPUT_QUEUE, task); err != nil { 50 | log.Fatalf("Error in LPUSH: %v\n", err) 51 | } 52 | if _, err = con.Do("LREM", TAKEN_QUEUE, 1, task); err != nil { 53 | log.Fatalf("Error in LREM: %v\n", err) 54 | } 55 | } else { 56 | log.Println("I'm bored") 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/java-client/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'application' 4 | } 5 | 6 | version '1.0' 7 | 8 | repositories { 9 | mavenCentral() 10 | jcenter() 11 | mavenLocal() 12 | } 13 | compileJava.options.encoding = 'UTF-8' 14 | targetCompatibility = 11 15 | sourceCompatibility = 11 16 | 17 | mainClassName = "org.canop.resc.examples.SimpleWorker" 18 | 19 | dependencies { 20 | implementation "redis.clients:jedis:3.2.0" 21 | } 22 | 23 | wrapper { 24 | gradleVersion = "6.2.2" 25 | } 26 | -------------------------------------------------------------------------------- /examples/java-client/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Canop/resc/4d67d972522304404e5469044c0fe40a4e4889e6/examples/java-client/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/java-client/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /examples/java-client/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /examples/java-client/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /examples/java-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.canop.resc.client.examples 8 | java-resc-worker 9 | jar 10 | 0.1 11 | 12 | 13 | UTF-8 14 | 1.9 15 | 1.9 16 | 17 | 18 | 19 | 20 | redis.clients 21 | jedis 22 | 2.9.0 23 | jar 24 | compile 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-shade-plugin 33 | 2.1 34 | 35 | 36 | package 37 | 38 | shade 39 | 40 | 41 | 42 | 44 | org.canop.resc.examples.SimpleWorker 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /examples/java-client/src/main/java/org/canop/resc/examples/SimpleWorker.java: -------------------------------------------------------------------------------- 1 | package org.canop.resc.examples; 2 | 3 | import redis.clients.jedis.Jedis; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | /** 7 | * A simple worker for resc, listening for a queue, "doing" the tasks, 8 | * then telling redis the job is done. 9 | * 10 | * Configuration is hardcoded here. 11 | */ 12 | public class SimpleWorker { 13 | 14 | static String host = "localhost"; 15 | static String inputQueue = "trt/plantA/todo-queue"; 16 | static String inputSet = "trt/plantA/todo-set"; // set to null if not using a set 17 | static String takenQueue = "trt/plantA/taken"; 18 | static String outputQueue = "global/events"; 19 | 20 | static class Task { 21 | 22 | public final String nature; 23 | public final String process; 24 | public final String product; 25 | 26 | public Task(String name){ 27 | String[] tokens = name.split("/"); 28 | if (tokens.length!=3) throw new IllegalArgumentException("Invalid task name"); 29 | nature = tokens[0]; 30 | process = tokens[1]; 31 | product = tokens[2]; 32 | } 33 | 34 | public void execute() throws InterruptedException { 35 | System.out.printf("Executing %s for product %s on process %s ", nature, product, process); 36 | for (int i=0; i<10; i++) { 37 | System.out.print("."); 38 | TimeUnit.SECONDS.sleep(1); 39 | } 40 | System.out.println(" done"); 41 | } 42 | 43 | } 44 | 45 | /** 46 | * execute the task if possible 47 | */ 48 | private static void handleTask(String taskName) { 49 | Task task; 50 | try { 51 | task = new Task(taskName); 52 | } catch (IllegalArgumentException e) { 53 | System.out.printf("Invalid task name : \"%s\"\n", taskName); 54 | return; 55 | } 56 | try { 57 | task.execute(); 58 | } catch (Exception e) { 59 | System.out.printf("Error with task \"%s\"\n", taskName); 60 | e.printStackTrace(); 61 | } 62 | } 63 | 64 | public static void main(String[] args) { 65 | Jedis jedis = new Jedis(host); 66 | System.out.println("Recovering tasks from queue " + takenQueue); 67 | String task; 68 | do { 69 | task = jedis.rpoplpush(takenQueue, inputQueue); 70 | if (task != null) System.out.println("Recovered task " + task); 71 | else System.out.println("No more task in " + takenQueue); 72 | } while (task != null); 73 | 74 | System.out.println("Worker listening on queue " + inputQueue); 75 | for (;;) { 76 | //# Take a task on input, put it on taken 77 | String taskName = jedis.brpoplpush(inputQueue, takenQueue, 60); 78 | if (taskName != null) { 79 | if (inputSet != null) { 80 | //# remove the task from the task set 81 | long n = jedis.zrem(inputSet, taskName); 82 | if (n != 1) { 83 | System.out.printf( 84 | "Unexpected number of elements removed from set: %d\n", 85 | n 86 | ); 87 | } 88 | } 89 | //# do the job 90 | handleTask(taskName); 91 | //# notify the scheduler the job is done 92 | jedis.lpush(outputQueue, taskName); 93 | //# Remove the task from taken 94 | jedis.lrem(takenQueue, 1, taskName); 95 | } else { 96 | System.out.println("I'm bored"); 97 | } 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /examples/node-client/main.js: -------------------------------------------------------------------------------- 1 | const redis = require("redis") 2 | const Promise = require("bluebird") 3 | Promise.promisifyAll(redis) 4 | 5 | const inputQueue = "trt/plantA/todo-queue" 6 | const inputSet = "trt/plantA/todo-set" // set to null if not deduplicating 7 | const takenQueue = "trt/plantA/taken" 8 | const outputQueue = "global/events" 9 | 10 | function print(s){ 11 | process.stdout.write(s) 12 | } 13 | 14 | async function handleTask(taskName){ 15 | const [nature, process, product] = taskName.split("/") 16 | if (!product) { 17 | return console.warn("Invalid task name : " + taskName) 18 | } 19 | print(`Executing ${nature} for product ${product} on process ${process} `) 20 | for (let i=0; i<10; i++) { 21 | print(".") 22 | await Promise.delay(1000) 23 | } 24 | print(" done\n") 25 | } 26 | 27 | const client = redis.createClient() 28 | console.log(`Worker listening on queue ${inputQueue}`) 29 | ;(function loop(){ 30 | // the promisified version of brpoplpush doesn't seem to work, hence this awkward construct 31 | //# Take a task on input, put it on taken 32 | client.brpoplpush(inputQueue, takenQueue, 60, async function(err, taskName){ 33 | if (taskName) { 34 | try { 35 | //# remove the task from the set 36 | if (inputSet) { 37 | await client.zremAsync(inputSet, taskName); 38 | } 39 | 40 | //# do the job 41 | await handleTask(taskName) 42 | 43 | //# notify the scheduler the job is done 44 | await client.lpushAsync(outputQueue, taskName) 45 | 46 | //# Remove the task from taken 47 | await client.lremAsync(takenQueue, 1, taskName) 48 | } catch (e) { 49 | console.warn("There was an error while pushing the task back:", e) 50 | } 51 | } else if (err) { 52 | console.warn("There was an error:", err) 53 | } else { 54 | console.log("I'm bored") 55 | } 56 | loop() 57 | }) 58 | })() 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /examples/node-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "bluebird": "^3.5.2", 4 | "redis": "^2.8.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/node-client/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | bluebird@^3.5.2: 6 | version "3.5.2" 7 | resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.2.tgz#1be0908e054a751754549c270489c1505d4ab15a" 8 | integrity sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg== 9 | 10 | double-ended-queue@^2.1.0-0: 11 | version "2.1.0-0" 12 | resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" 13 | integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw= 14 | 15 | redis-commands@^1.2.0: 16 | version "1.4.0" 17 | resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.4.0.tgz#52f9cf99153efcce56a8f86af986bd04e988602f" 18 | integrity sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw== 19 | 20 | redis-parser@^2.6.0: 21 | version "2.6.0" 22 | resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b" 23 | integrity sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs= 24 | 25 | redis@^2.8.0: 26 | version "2.8.0" 27 | resolved "https://registry.yarnpkg.com/redis/-/redis-2.8.0.tgz#202288e3f58c49f6079d97af7a10e1303ae14b02" 28 | integrity sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A== 29 | dependencies: 30 | double-ended-queue "^2.1.0-0" 31 | redis-commands "^1.2.0" 32 | redis-parser "^2.6.0" 33 | -------------------------------------------------------------------------------- /examples/rust-client/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "async-channel" 5 | version = "1.5.1" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "59740d83946db6a5af71ae25ddf9562c2b176b2ca42cf99a455f09f4a220d6b9" 8 | dependencies = [ 9 | "concurrent-queue", 10 | "event-listener", 11 | "futures-core", 12 | ] 13 | 14 | [[package]] 15 | name = "async-executor" 16 | version = "1.4.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "eb877970c7b440ead138f6321a3b5395d6061183af779340b65e20c0fede9146" 19 | dependencies = [ 20 | "async-task", 21 | "concurrent-queue", 22 | "fastrand", 23 | "futures-lite", 24 | "once_cell", 25 | "vec-arena", 26 | ] 27 | 28 | [[package]] 29 | name = "async-global-executor" 30 | version = "2.0.2" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" 33 | dependencies = [ 34 | "async-channel", 35 | "async-executor", 36 | "async-io", 37 | "async-mutex", 38 | "blocking", 39 | "futures-lite", 40 | "num_cpus", 41 | "once_cell", 42 | ] 43 | 44 | [[package]] 45 | name = "async-io" 46 | version = "1.3.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "9315f8f07556761c3e48fec2e6b276004acf426e6dc068b2c2251854d65ee0fd" 49 | dependencies = [ 50 | "concurrent-queue", 51 | "fastrand", 52 | "futures-lite", 53 | "libc", 54 | "log", 55 | "nb-connect", 56 | "once_cell", 57 | "parking", 58 | "polling", 59 | "vec-arena", 60 | "waker-fn", 61 | "winapi 0.3.9", 62 | ] 63 | 64 | [[package]] 65 | name = "async-lock" 66 | version = "2.3.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "1996609732bde4a9988bc42125f55f2af5f3c36370e27c778d5191a4a1b63bfb" 69 | dependencies = [ 70 | "event-listener", 71 | ] 72 | 73 | [[package]] 74 | name = "async-mutex" 75 | version = "1.4.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" 78 | dependencies = [ 79 | "event-listener", 80 | ] 81 | 82 | [[package]] 83 | name = "async-std" 84 | version = "1.9.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "d9f06685bad74e0570f5213741bea82158279a4103d988e57bfada11ad230341" 87 | dependencies = [ 88 | "async-channel", 89 | "async-global-executor", 90 | "async-io", 91 | "async-lock", 92 | "crossbeam-utils", 93 | "futures-channel", 94 | "futures-core", 95 | "futures-io", 96 | "futures-lite", 97 | "gloo-timers", 98 | "kv-log-macro", 99 | "log", 100 | "memchr", 101 | "num_cpus", 102 | "once_cell", 103 | "pin-project-lite 0.2.4", 104 | "pin-utils", 105 | "slab", 106 | "wasm-bindgen-futures", 107 | ] 108 | 109 | [[package]] 110 | name = "async-task" 111 | version = "4.0.3" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" 114 | 115 | [[package]] 116 | name = "async-trait" 117 | version = "0.1.42" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" 120 | dependencies = [ 121 | "proc-macro2", 122 | "quote", 123 | "syn", 124 | ] 125 | 126 | [[package]] 127 | name = "atomic-waker" 128 | version = "1.0.0" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" 131 | 132 | [[package]] 133 | name = "autocfg" 134 | version = "1.0.1" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 137 | 138 | [[package]] 139 | name = "bitflags" 140 | version = "1.0.4" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" 143 | 144 | [[package]] 145 | name = "blocking" 146 | version = "1.0.2" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" 149 | dependencies = [ 150 | "async-channel", 151 | "async-task", 152 | "atomic-waker", 153 | "fastrand", 154 | "futures-lite", 155 | "once_cell", 156 | ] 157 | 158 | [[package]] 159 | name = "bumpalo" 160 | version = "3.6.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" 163 | 164 | [[package]] 165 | name = "bytes" 166 | version = "0.5.6" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" 169 | 170 | [[package]] 171 | name = "bytes" 172 | version = "1.0.1" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" 175 | 176 | [[package]] 177 | name = "cache-padded" 178 | version = "1.1.1" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" 181 | 182 | [[package]] 183 | name = "cc" 184 | version = "1.0.66" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" 187 | 188 | [[package]] 189 | name = "cfg-if" 190 | version = "0.1.10" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 193 | 194 | [[package]] 195 | name = "cfg-if" 196 | version = "1.0.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 199 | 200 | [[package]] 201 | name = "combine" 202 | version = "4.5.2" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "cc4369b5e4c0cddf64ad8981c0111e7df4f7078f4d6ba98fb31f2e17c4c57b7e" 205 | dependencies = [ 206 | "bytes 0.5.6", 207 | "bytes 1.0.1", 208 | "futures-util", 209 | "memchr", 210 | "pin-project-lite 0.2.4", 211 | "tokio", 212 | ] 213 | 214 | [[package]] 215 | name = "concurrent-queue" 216 | version = "1.2.2" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" 219 | dependencies = [ 220 | "cache-padded", 221 | ] 222 | 223 | [[package]] 224 | name = "crossbeam-utils" 225 | version = "0.8.1" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" 228 | dependencies = [ 229 | "autocfg", 230 | "cfg-if 1.0.0", 231 | "lazy_static", 232 | ] 233 | 234 | [[package]] 235 | name = "ctor" 236 | version = "0.1.19" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19" 239 | dependencies = [ 240 | "quote", 241 | "syn", 242 | ] 243 | 244 | [[package]] 245 | name = "dtoa" 246 | version = "0.4.7" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" 249 | 250 | [[package]] 251 | name = "event-listener" 252 | version = "2.5.1" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" 255 | 256 | [[package]] 257 | name = "fastrand" 258 | version = "1.4.0" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3" 261 | dependencies = [ 262 | "instant", 263 | ] 264 | 265 | [[package]] 266 | name = "fnv" 267 | version = "1.0.7" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 270 | 271 | [[package]] 272 | name = "form_urlencoded" 273 | version = "1.0.0" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" 276 | dependencies = [ 277 | "matches", 278 | "percent-encoding", 279 | ] 280 | 281 | [[package]] 282 | name = "fuchsia-zircon" 283 | version = "0.3.3" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 286 | dependencies = [ 287 | "bitflags", 288 | "fuchsia-zircon-sys", 289 | ] 290 | 291 | [[package]] 292 | name = "fuchsia-zircon-sys" 293 | version = "0.3.3" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 296 | 297 | [[package]] 298 | name = "futures-channel" 299 | version = "0.3.12" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" 302 | dependencies = [ 303 | "futures-core", 304 | ] 305 | 306 | [[package]] 307 | name = "futures-core" 308 | version = "0.3.12" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" 311 | 312 | [[package]] 313 | name = "futures-io" 314 | version = "0.3.12" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" 317 | 318 | [[package]] 319 | name = "futures-lite" 320 | version = "1.11.3" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "b4481d0cd0de1d204a4fa55e7d45f07b1d958abcb06714b3446438e2eff695fb" 323 | dependencies = [ 324 | "fastrand", 325 | "futures-core", 326 | "futures-io", 327 | "memchr", 328 | "parking", 329 | "pin-project-lite 0.2.4", 330 | "waker-fn", 331 | ] 332 | 333 | [[package]] 334 | name = "futures-sink" 335 | version = "0.3.12" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" 338 | 339 | [[package]] 340 | name = "futures-task" 341 | version = "0.3.12" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" 344 | dependencies = [ 345 | "once_cell", 346 | ] 347 | 348 | [[package]] 349 | name = "futures-util" 350 | version = "0.3.12" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" 353 | dependencies = [ 354 | "futures-core", 355 | "futures-io", 356 | "futures-sink", 357 | "futures-task", 358 | "memchr", 359 | "pin-project-lite 0.2.4", 360 | "pin-utils", 361 | "slab", 362 | ] 363 | 364 | [[package]] 365 | name = "gloo-timers" 366 | version = "0.2.1" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" 369 | dependencies = [ 370 | "futures-channel", 371 | "futures-core", 372 | "js-sys", 373 | "wasm-bindgen", 374 | "web-sys", 375 | ] 376 | 377 | [[package]] 378 | name = "hermit-abi" 379 | version = "0.1.18" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 382 | dependencies = [ 383 | "libc", 384 | ] 385 | 386 | [[package]] 387 | name = "idna" 388 | version = "0.2.0" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 391 | dependencies = [ 392 | "matches", 393 | "unicode-bidi", 394 | "unicode-normalization", 395 | ] 396 | 397 | [[package]] 398 | name = "instant" 399 | version = "0.1.9" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" 402 | dependencies = [ 403 | "cfg-if 1.0.0", 404 | ] 405 | 406 | [[package]] 407 | name = "iovec" 408 | version = "0.1.4" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 411 | dependencies = [ 412 | "libc", 413 | ] 414 | 415 | [[package]] 416 | name = "itoa" 417 | version = "0.4.7" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 420 | 421 | [[package]] 422 | name = "js-sys" 423 | version = "0.3.47" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" 426 | dependencies = [ 427 | "wasm-bindgen", 428 | ] 429 | 430 | [[package]] 431 | name = "kernel32-sys" 432 | version = "0.2.2" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 435 | dependencies = [ 436 | "winapi 0.2.8", 437 | "winapi-build", 438 | ] 439 | 440 | [[package]] 441 | name = "kv-log-macro" 442 | version = "1.0.7" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" 445 | dependencies = [ 446 | "log", 447 | ] 448 | 449 | [[package]] 450 | name = "lazy_static" 451 | version = "1.4.0" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 454 | 455 | [[package]] 456 | name = "libc" 457 | version = "0.2.85" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3" 460 | 461 | [[package]] 462 | name = "log" 463 | version = "0.4.14" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 466 | dependencies = [ 467 | "cfg-if 1.0.0", 468 | "value-bag", 469 | ] 470 | 471 | [[package]] 472 | name = "matches" 473 | version = "0.1.8" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 476 | 477 | [[package]] 478 | name = "memchr" 479 | version = "2.3.4" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 482 | 483 | [[package]] 484 | name = "mio" 485 | version = "0.6.23" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" 488 | dependencies = [ 489 | "cfg-if 0.1.10", 490 | "fuchsia-zircon", 491 | "fuchsia-zircon-sys", 492 | "iovec", 493 | "kernel32-sys", 494 | "libc", 495 | "log", 496 | "miow", 497 | "net2", 498 | "slab", 499 | "winapi 0.2.8", 500 | ] 501 | 502 | [[package]] 503 | name = "mio-uds" 504 | version = "0.6.8" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" 507 | dependencies = [ 508 | "iovec", 509 | "libc", 510 | "mio", 511 | ] 512 | 513 | [[package]] 514 | name = "miow" 515 | version = "0.2.2" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" 518 | dependencies = [ 519 | "kernel32-sys", 520 | "net2", 521 | "winapi 0.2.8", 522 | "ws2_32-sys", 523 | ] 524 | 525 | [[package]] 526 | name = "nb-connect" 527 | version = "1.0.2" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "8123a81538e457d44b933a02faf885d3fe8408806b23fa700e8f01c6c3a98998" 530 | dependencies = [ 531 | "libc", 532 | "winapi 0.3.9", 533 | ] 534 | 535 | [[package]] 536 | name = "net2" 537 | version = "0.2.37" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" 540 | dependencies = [ 541 | "cfg-if 0.1.10", 542 | "libc", 543 | "winapi 0.3.9", 544 | ] 545 | 546 | [[package]] 547 | name = "num_cpus" 548 | version = "1.13.0" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 551 | dependencies = [ 552 | "hermit-abi", 553 | "libc", 554 | ] 555 | 556 | [[package]] 557 | name = "once_cell" 558 | version = "1.5.2" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" 561 | 562 | [[package]] 563 | name = "parking" 564 | version = "2.0.0" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" 567 | 568 | [[package]] 569 | name = "percent-encoding" 570 | version = "2.1.0" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 573 | 574 | [[package]] 575 | name = "pin-project-lite" 576 | version = "0.1.11" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" 579 | 580 | [[package]] 581 | name = "pin-project-lite" 582 | version = "0.2.4" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" 585 | 586 | [[package]] 587 | name = "pin-utils" 588 | version = "0.1.0" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 591 | 592 | [[package]] 593 | name = "polling" 594 | version = "2.0.2" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4" 597 | dependencies = [ 598 | "cfg-if 0.1.10", 599 | "libc", 600 | "log", 601 | "wepoll-sys", 602 | "winapi 0.3.9", 603 | ] 604 | 605 | [[package]] 606 | name = "proc-macro2" 607 | version = "1.0.24" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 610 | dependencies = [ 611 | "unicode-xid", 612 | ] 613 | 614 | [[package]] 615 | name = "quote" 616 | version = "1.0.8" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" 619 | dependencies = [ 620 | "proc-macro2", 621 | ] 622 | 623 | [[package]] 624 | name = "redis" 625 | version = "0.17.0" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "95357caf2640abc54651b93c98a8df4fe1ccbf44b8e601ccdf43d5c1451f29ac" 628 | dependencies = [ 629 | "async-std", 630 | "async-trait", 631 | "bytes 0.5.6", 632 | "combine", 633 | "dtoa", 634 | "futures-util", 635 | "itoa", 636 | "percent-encoding", 637 | "pin-project-lite 0.1.11", 638 | "sha1", 639 | "tokio", 640 | "tokio-util", 641 | "url", 642 | ] 643 | 644 | [[package]] 645 | name = "rust-client" 646 | version = "0.1.0" 647 | dependencies = [ 648 | "redis", 649 | ] 650 | 651 | [[package]] 652 | name = "sha1" 653 | version = "0.6.0" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" 656 | 657 | [[package]] 658 | name = "slab" 659 | version = "0.4.2" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 662 | 663 | [[package]] 664 | name = "syn" 665 | version = "1.0.60" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" 668 | dependencies = [ 669 | "proc-macro2", 670 | "quote", 671 | "unicode-xid", 672 | ] 673 | 674 | [[package]] 675 | name = "tokio" 676 | version = "0.2.25" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" 679 | dependencies = [ 680 | "bytes 0.5.6", 681 | "fnv", 682 | "futures-core", 683 | "iovec", 684 | "lazy_static", 685 | "libc", 686 | "memchr", 687 | "mio", 688 | "mio-uds", 689 | "pin-project-lite 0.1.11", 690 | ] 691 | 692 | [[package]] 693 | name = "tokio-util" 694 | version = "0.3.1" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" 697 | dependencies = [ 698 | "bytes 0.5.6", 699 | "futures-core", 700 | "futures-sink", 701 | "log", 702 | "pin-project-lite 0.1.11", 703 | "tokio", 704 | ] 705 | 706 | [[package]] 707 | name = "unicode-bidi" 708 | version = "0.3.4" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 711 | dependencies = [ 712 | "matches", 713 | ] 714 | 715 | [[package]] 716 | name = "unicode-normalization" 717 | version = "0.1.7" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" 720 | 721 | [[package]] 722 | name = "unicode-xid" 723 | version = "0.2.1" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 726 | 727 | [[package]] 728 | name = "url" 729 | version = "2.2.0" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" 732 | dependencies = [ 733 | "form_urlencoded", 734 | "idna", 735 | "matches", 736 | "percent-encoding", 737 | ] 738 | 739 | [[package]] 740 | name = "value-bag" 741 | version = "1.0.0-alpha.6" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "6b676010e055c99033117c2343b33a40a30b91fecd6c49055ac9cd2d6c305ab1" 744 | dependencies = [ 745 | "ctor", 746 | ] 747 | 748 | [[package]] 749 | name = "vec-arena" 750 | version = "1.0.0" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d" 753 | 754 | [[package]] 755 | name = "waker-fn" 756 | version = "1.1.0" 757 | source = "registry+https://github.com/rust-lang/crates.io-index" 758 | checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" 759 | 760 | [[package]] 761 | name = "wasm-bindgen" 762 | version = "0.2.70" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" 765 | dependencies = [ 766 | "cfg-if 1.0.0", 767 | "wasm-bindgen-macro", 768 | ] 769 | 770 | [[package]] 771 | name = "wasm-bindgen-backend" 772 | version = "0.2.70" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" 775 | dependencies = [ 776 | "bumpalo", 777 | "lazy_static", 778 | "log", 779 | "proc-macro2", 780 | "quote", 781 | "syn", 782 | "wasm-bindgen-shared", 783 | ] 784 | 785 | [[package]] 786 | name = "wasm-bindgen-futures" 787 | version = "0.4.20" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94" 790 | dependencies = [ 791 | "cfg-if 1.0.0", 792 | "js-sys", 793 | "wasm-bindgen", 794 | "web-sys", 795 | ] 796 | 797 | [[package]] 798 | name = "wasm-bindgen-macro" 799 | version = "0.2.70" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" 802 | dependencies = [ 803 | "quote", 804 | "wasm-bindgen-macro-support", 805 | ] 806 | 807 | [[package]] 808 | name = "wasm-bindgen-macro-support" 809 | version = "0.2.70" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" 812 | dependencies = [ 813 | "proc-macro2", 814 | "quote", 815 | "syn", 816 | "wasm-bindgen-backend", 817 | "wasm-bindgen-shared", 818 | ] 819 | 820 | [[package]] 821 | name = "wasm-bindgen-shared" 822 | version = "0.2.70" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" 825 | 826 | [[package]] 827 | name = "web-sys" 828 | version = "0.3.47" 829 | source = "registry+https://github.com/rust-lang/crates.io-index" 830 | checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" 831 | dependencies = [ 832 | "js-sys", 833 | "wasm-bindgen", 834 | ] 835 | 836 | [[package]] 837 | name = "wepoll-sys" 838 | version = "3.0.1" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff" 841 | dependencies = [ 842 | "cc", 843 | ] 844 | 845 | [[package]] 846 | name = "winapi" 847 | version = "0.2.8" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 850 | 851 | [[package]] 852 | name = "winapi" 853 | version = "0.3.9" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 856 | dependencies = [ 857 | "winapi-i686-pc-windows-gnu", 858 | "winapi-x86_64-pc-windows-gnu", 859 | ] 860 | 861 | [[package]] 862 | name = "winapi-build" 863 | version = "0.1.1" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 866 | 867 | [[package]] 868 | name = "winapi-i686-pc-windows-gnu" 869 | version = "0.4.0" 870 | source = "registry+https://github.com/rust-lang/crates.io-index" 871 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 872 | 873 | [[package]] 874 | name = "winapi-x86_64-pc-windows-gnu" 875 | version = "0.4.0" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 878 | 879 | [[package]] 880 | name = "ws2_32-sys" 881 | version = "0.2.1" 882 | source = "registry+https://github.com/rust-lang/crates.io-index" 883 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 884 | dependencies = [ 885 | "winapi 0.2.8", 886 | "winapi-build", 887 | ] 888 | -------------------------------------------------------------------------------- /examples/rust-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-client" 3 | version = "0.1.0" 4 | edition = "2018" 5 | authors = ["Denys Séguret "] 6 | license = "MIT" 7 | 8 | [dependencies] 9 | redis = "0.17" 10 | -------------------------------------------------------------------------------- /examples/rust-client/src/main.rs: -------------------------------------------------------------------------------- 1 | use { 2 | redis::Commands, 3 | std::{ 4 | io::{self, Write}, 5 | time::Duration, 6 | }, 7 | }; 8 | 9 | /// emptying the taken queue should only be done when there's only 10 | /// one worker on that queue, or on command, after a crash 11 | const EMPTY_TAKEN_AT_LAUNCH: bool = true; 12 | 13 | const REDIS_URL: &str = "redis://127.0.0.1/"; 14 | const INPUT_QUEUE: &str = "trt/plantA/todo-queue"; 15 | const INPUT_SET: &str = "trt/plantA/todo-set"; 16 | const TAKEN_QUEUE: &str = "trt/plantA/taken"; 17 | const OUTPUT_QUEUE: &str = "global/events"; 18 | const WAIT_BETWEEN_DOTS: Duration = Duration::from_secs(1); 19 | 20 | fn handle_task(task: &str) { 21 | match task.split("/").collect::>().as_slice() { 22 | [nature, process, product] => { 23 | print!( 24 | "Executing {:?} for product {:?} on process {:?} ", 25 | nature, product, process 26 | ); 27 | for _ in 0..10 { 28 | std::thread::sleep(WAIT_BETWEEN_DOTS); 29 | print!("."); 30 | io::stdout().flush().ok(); 31 | } 32 | println!(" done"); 33 | } 34 | _ => { 35 | println!("Illegal task format!"); 36 | } 37 | } 38 | } 39 | 40 | fn main() { 41 | let client = redis::Client::open(REDIS_URL).unwrap(); 42 | let mut con = client.get_connection().unwrap(); 43 | if EMPTY_TAKEN_AT_LAUNCH { 44 | // at launch we recover the tasks remaining in the taken_queue 45 | // and we move them to the list of tasks to do 46 | loop { 47 | match con.rpoplpush::<_, String>(TAKEN_QUEUE, INPUT_QUEUE) { 48 | Ok(task) => println!("recovered task {:?}", task), 49 | Err(_) => { 50 | println!("No more tasks to recover in queue {:?}", TAKEN_QUEUE); 51 | break; 52 | }, 53 | } 54 | } 55 | } 56 | println!("Worker listening on queue {:?}", INPUT_QUEUE); 57 | loop { 58 | //# Take a task on input, put it on taken 59 | if let Ok(task) = con.brpoplpush::<_, String>(INPUT_QUEUE, TAKEN_QUEUE, 60) { 60 | //# removing the task from the task_set so that it can be pushed again 61 | match con.zrem::<_, _, i32>(INPUT_SET, &task) { 62 | Ok(1) => { 63 | println!("removed task from set"); 64 | } 65 | Ok(0) => { 66 | println!("no task found in set - might be a bad configuration"); 67 | } 68 | Ok(n) => { 69 | println!("unexpected {} tasks removed - bad configuration", n); 70 | } 71 | Err(err) => { 72 | println!("error while lpushing the task back : {:?}", err); 73 | } 74 | } 75 | //# do the real job 76 | handle_task(&task); 77 | //# notify the scheduler the job is done 78 | if let Err(err) = con.lpush::<_, _, ()>(OUTPUT_QUEUE, &task) { 79 | println!("error while lpushing the task back : {:?}", err); 80 | } 81 | //# Remove the task from taken 82 | if let Err(err) = con.lrem::<_, _, ()>(TAKEN_QUEUE, 1, &task) { 83 | println!("error while cleaning the taken list : {:?}", err); 84 | } 85 | } else { 86 | println!("I'm bored"); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /examples/simple-conf.hjson: -------------------------------------------------------------------------------- 1 | # This very simple example illustrates the propagation from 2 | # events (called "acq", as in "acquisition") 3 | # to tasks (called "trt" as in "treatment") 4 | { 5 | 6 | redis: { 7 | url: "redis://127.0.0.1/" 8 | } 9 | 10 | # This optional channel is here for observability 11 | listener_channel: "events" 12 | 13 | watchers: [ 14 | { 15 | input_queue: global/events 16 | taken_queue: global/taken 17 | rules: [ 18 | { 19 | name: TRT computation on data acquisition 20 | on: "^acq/(?P\\w+)/(?P\\w+)$" 21 | make: { 22 | task: "trt/${process_id}/${product_id}" 23 | queue: "trt/${process_id}/todo-queue" 24 | set: "trt/${process_id}/todo-set" 25 | } 26 | } 27 | ] 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /examples/simple-example.md: -------------------------------------------------------------------------------- 1 | 2 | ## Introduction 3 | 4 | This simple example shows 5 | 6 | * the resc scheduler generating tasks according to one of the simplest possible rule 7 | * one or several worker(s) picking up tasks, executing them, and signaling end of execution 8 | 9 | Java, Go, node, and Rust implementations of workers are available and can be used concurrently. 10 | 11 | The business logic here is that a source sends events informing us that some data has been received regarding a plant "plantA" and several products (the nature of this event is `"acq"`) and that we must execute some treatments. 12 | 13 | The scheduler rule here is just 14 | 15 | rules: [ 16 | { 17 | name: TRT computation on data acquisition 18 | on: "^acq/(?P\\w+)/(?P\\w+)$" 19 | make: { 20 | task: "trt/${process_id}/${product_id}" 21 | queue: "trt/${process_id}/todo-queue" 22 | set: "trt/${process_id}/todo-set" 23 | } 24 | } 25 | ] 26 | 27 | *[simple-conf.hjson](simple-conf.hjson)* 28 | 29 | 30 | So we'll manually generate events like `"acq/plantA/123"` and observe the scheduler generate tasks like `"trt/plantA/123"`, and then the worker(s) handle those tasks. 31 | 32 | ## Compilation, preparation 33 | 34 | Of course Redis must be installed and started. 35 | 36 | ### Compilation of the scheduler 37 | 38 | In the root directory of the resc project, execute 39 | 40 | cargo build --release 41 | 42 | This builds the `target/release/resc` executable. 43 | 44 | ### Compilation of the java worker 45 | 46 | You need to have a Java9 JDK and Maven installed. 47 | 48 | Move to the `examples/java-client` directory, then run 49 | 50 | mvn package 51 | 52 | This builds the `target/java-resc-worker-01.jar` jar file. 53 | 54 | ### Preparation of the node.js worker 55 | 56 | You need to have node and yarn installed 57 | 58 | Move to the `examples/node-client` directory, then run 59 | 60 | yarn 61 | 62 | ### Compilation of the Go worker 63 | 64 | You need to have a go dev environment set up. 65 | 66 | Start by fetching the official Go Redis driver: 67 | 68 | go get github.com/garyburd/redigo/redis 69 | 70 | Then compile the application: 71 | 72 | cd examples/go-client 73 | go build 74 | 75 | ### Preparation of the rust worker 76 | 77 | As this is only a demonstration, we don't need to compile as release. We do nothing here. 78 | 79 | ## Running the Simple Example 80 | 81 | All this can be done in whatever order. In order to see what happens you should have one console per program. 82 | 83 | ### Starting the redis cli 84 | 85 | Just do 86 | 87 | redis-cli 88 | 89 | At the prompt, if you previously tried a few examples, you might want to do 90 | 91 | flushall 92 | 93 | which removes everything. 94 | 95 | ### Starting the scheduler 96 | 97 | 98 | With the normal log setting, only errors and warning are displayed. 99 | If you want to see something, you should set the log level to "debug" or at least "info". In the examples directory, do 100 | 101 | RUST_LOG=debug target/release/resc examples/simple-conf.hjson 102 | 103 | ### Starting workers 104 | 105 | Launch as many workers as desired. 106 | 107 | Whatever the worker you launch, you should see 108 | 109 | listening on queue trt/plantA/todo 110 | 111 | #### Start a Java worker 112 | 113 | java -jar examples/java-client/target/java-resc-worker-0.1.jar 114 | 115 | #### Start a node worker 116 | 117 | node examples/node-client/main.js 118 | 119 | #### Start a go worker 120 | 121 | examples/go-client/go-client 122 | 123 | #### Start a rust worker 124 | 125 | cd examples/rust-client 126 | cargo run 127 | 128 | 129 | ### Generating a few root events 130 | 131 | In the redis cli do 132 | 133 | lpush global/events acq/plantA/1 acq/plantA/2 acq/plantA/3 134 | 135 | You should then see the scheduler immediately generating the 3 resulting tasks: 136 | 137 | ![generated tasks](doc/simple-example-generated-tasks.png) 138 | 139 | The workers pick tasks, execute them, and then send back the tasks as done in `global/events`. 140 | 141 | Here's what your screen should look like: 142 | 143 | ![complete run](doc/simple-example-complete.png) 144 | 145 | 146 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | # build a new release of resc 2 | # This isn't used for normal compilation (see https://dystroy.org/resc for instruction) 3 | # but for the building of the official releases 4 | version=$(sed 's/version = "\([0-9.]\{1,\}\)"/\1/;t;d' Cargo.toml | head -1) 5 | 6 | echo "Building release $version" 7 | 8 | # make the build directory and compile for all targets 9 | ./compile-all-targets.sh 10 | 11 | # add the readme and changelog in the build directory 12 | echo "This is resc. More info and installation instructions on https://github.com/Canop/resc" > build/README.md 13 | cp CHANGELOG.md build 14 | 15 | # publish version number 16 | echo "$version" > build/version 17 | 18 | # prepare the release archive 19 | rm resc_*.zip 20 | zip -r "resc_$version.zip" build/* 21 | 22 | # copy it to releases folder 23 | mkdir releases 24 | cp "resc_$version.zip" releases 25 | -------------------------------------------------------------------------------- /src/conf.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::*, 3 | serde::Deserialize, 4 | std::{ 5 | path::PathBuf, 6 | }, 7 | }; 8 | 9 | 10 | /// Redis access configuration 11 | #[derive(Debug, Deserialize)] 12 | pub struct RedisConf { 13 | pub url: String, 14 | } 15 | 16 | /// The configuration of Resc, as read from a JSON file 17 | #[derive(Debug, Deserialize)] 18 | pub struct Conf { 19 | pub redis: RedisConf, 20 | pub listener_channel: String, 21 | pub watchers: Vec, 22 | } 23 | 24 | pub fn read_file(filename: &str) -> Result { 25 | let start = std::time::Instant::now(); 26 | let conf = SerdeFormat::read_file(&PathBuf::from(&filename)); 27 | debug!("Conf read in {:?}", start.elapsed()); 28 | conf 29 | } 30 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | use { 2 | thiserror::Error, 3 | }; 4 | 5 | 6 | #[derive(Error, Debug)] 7 | pub enum RescError { 8 | 9 | #[error("conf error")] 10 | Conf(#[from] ConfError), 11 | 12 | #[error("fetch error")] 13 | Reqwest(#[from] FetchError), 14 | 15 | #[error("redis error")] 16 | Redis(#[from] redis::RedisError), 17 | 18 | } 19 | 20 | #[derive(Error, Debug)] 21 | pub enum ConfError { 22 | 23 | #[error("Unknow file extension: {0:?}")] 24 | UnknownFileExtension(String), 25 | 26 | #[error("IO error: {0}")] 27 | IO(#[from] std::io::Error), 28 | 29 | #[error("Invalid Hjson: {0}")] 30 | Hjson(#[from] deser_hjson::Error), 31 | 32 | #[error("Invalid JSON: {0}")] 33 | JSON(#[from] serde_json::Error), 34 | } 35 | 36 | 37 | #[derive(Error, Debug)] 38 | pub enum FetchError { 39 | 40 | #[error("reqwest error")] 41 | Reqwest(#[from] reqwest::Error), 42 | 43 | #[error("fetch received an error - status: {0}")] 44 | ErrorStatus(u16), 45 | 46 | #[error("unexpected response content")] 47 | UnexpectedContent, 48 | 49 | #[error("io error")] 50 | IO(#[from] std::io::Error), 51 | 52 | #[error("invalid JSON")] 53 | JSON(#[from] serde_json::Error), 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /src/fetcher.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::*, 3 | log::*, 4 | serde::Deserialize, 5 | serde_json::{self, Value}, 6 | std::{collections::HashMap, io::Read}, 7 | }; 8 | 9 | /// the data the fetcher got 10 | #[derive(Debug)] 11 | pub struct FetchResult { 12 | pub props: HashMap, 13 | } 14 | 15 | /// A Fetcher is responsible for synchronously fetching some data 16 | /// (for use in handling a rule) 17 | #[derive(Debug, Clone, Deserialize)] 18 | pub struct Fetcher { 19 | pub url: Pattern, 20 | pub returns: String, 21 | } 22 | 23 | impl Fetcher { 24 | fn returned_key(&self, key: &str) -> String { 25 | format!("{}.{}", self.returns, key) 26 | } 27 | 28 | fn get_fetch_result(&self, object_value: &serde_json::Map) -> FetchResult { 29 | let mut props = HashMap::new(); 30 | for (key, value) in object_value { 31 | match value { 32 | Value::String(string_value) => { 33 | props.insert(self.returned_key(key), string_value.to_owned()); 34 | } 35 | Value::Number(number_value) => { 36 | props.insert(self.returned_key(key), number_value.to_string()); 37 | } 38 | _ => { 39 | debug!(" ignoring property {:#?}={:#?}", key, value); 40 | } 41 | } 42 | } 43 | FetchResult { props } 44 | } 45 | 46 | pub fn results(&self, props: &HashMap) -> Result, FetchError> { 47 | let url = self.url.inject(props); 48 | info!(" querying url: {:#?}", url); 49 | let mut response = reqwest::get(&url)?; 50 | if !response.status().is_success() { 51 | return Err(FetchError::ErrorStatus(response.status().into())); 52 | } 53 | // TODO use derive for response deserialization 54 | let mut json = String::new(); 55 | response.read_to_string(&mut json)?; 56 | let mut results = Vec::new(); 57 | let value: Value = serde_json::from_str(&json)?; 58 | // we accept either a simple object, or an array of objects 59 | match value { 60 | Value::Array(returned_values) => { 61 | for returned_value in &returned_values { 62 | match returned_value { 63 | Value::Object(object_value) => { 64 | results.push(self.get_fetch_result(object_value)); 65 | } 66 | _ => { 67 | return Err(FetchError::UnexpectedContent); 68 | } 69 | } 70 | } 71 | } 72 | Value::Object(returned_value) => { 73 | results.push(self.get_fetch_result(&returned_value)); 74 | } 75 | _ => { 76 | return Err(FetchError::UnexpectedContent); 77 | } 78 | } 79 | Ok(results) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //! Resc is a task orchestrator for distributed systems 2 | //! It's based on Rust and ensures in a safe way the 3 | //! generation of deduced tasks and their availability 4 | //! for external workers 5 | //! 6 | //! Introduction and complete description in the [README](https://github.com/Canop/resc) 7 | 8 | mod conf; 9 | mod errors; 10 | mod fetcher; 11 | mod make; 12 | mod pattern; 13 | mod rule; 14 | mod ruleset; 15 | mod rule_result; 16 | mod serde_format; 17 | mod watcher; 18 | 19 | use { 20 | chrono::Local, 21 | log::*, 22 | std::{env, io::Write, thread}, 23 | }; 24 | 25 | pub use { 26 | conf::*, 27 | errors::*, 28 | fetcher::*, 29 | make::*, 30 | pattern::*, 31 | rule::*, 32 | ruleset::*, 33 | rule_result::*, 34 | serde_format::*, 35 | watcher::*, 36 | }; 37 | 38 | fn configure_logger() { 39 | let env = env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "warn"); 40 | let mut builder = env_logger::Builder::from_env(env); 41 | builder.default_format_module_path(false); 42 | // log format with millisecond for better understanding of concurrency issues 43 | builder.format(|buf, record| { 44 | writeln!( 45 | buf, 46 | "{} [{}] - {}", 47 | Local::now().format("%Y-%m-%dT%H:%M:%S%.3f"), 48 | record.level(), 49 | record.args() 50 | ) 51 | }); 52 | builder.init(); 53 | } 54 | 55 | fn main() { 56 | configure_logger(); 57 | 58 | info!("----- starting resc scheduler -----"); 59 | 60 | let args: Vec = env::args().collect(); 61 | if args.len() < 2 { 62 | panic!("no configuration file provided"); 63 | } 64 | let config_filename = &args[1]; 65 | info!("configuration read from {}", config_filename); 66 | let conf = match conf::read_file(config_filename) { 67 | Ok(conf) => conf, 68 | Err(e) => { 69 | error!("Error reading configuration: {}", &e); 70 | eprintln!("{}", e); 71 | return; 72 | } 73 | }; 74 | 75 | let mut handles = Vec::new(); 76 | for watcher_conf in &conf.watchers { 77 | let mut watcher = Watcher::new(watcher_conf, &conf).unwrap(); 78 | handles.push(thread::spawn(move || { 79 | watcher.run().unwrap(); 80 | })); 81 | } 82 | 83 | debug!("all watchers started"); 84 | 85 | for h in handles { 86 | h.join().unwrap(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/make.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::*, 3 | serde::Deserialize, 4 | std::collections::HashMap, 5 | }; 6 | 7 | 8 | #[derive(Debug, Clone, Deserialize)] 9 | pub struct Maker { 10 | 11 | /// an optional name, for logs and for documentation in formats 12 | /// not allowing comments 13 | pub name: Option, 14 | 15 | /// the output task generation pattern, defined with token 16 | /// found with on_regex or a fetcher 17 | #[serde(default = "Pattern::default_task")] 18 | pub task: Pattern, 19 | 20 | /// the queue where the generated tasks must be written 21 | pub queue: Pattern, 22 | 23 | /// the optional task set used for deduplicating 24 | pub set: Option, 25 | 26 | } 27 | impl Maker { 28 | pub fn make( 29 | &self, 30 | props: &HashMap, 31 | results: &mut Vec, 32 | ) { 33 | results.push(RuleResult { 34 | task: self.task.inject(props), 35 | queue: self.queue.inject(props), 36 | set: self.set.as_ref().map(|pattern| pattern.inject(props)), 37 | }); 38 | } 39 | } 40 | 41 | /// This mimics the configuration structure where Make 42 | /// elements can be given in an array or just single. 43 | /// For now there's no difference and a single works 44 | /// just as a 1 element array. 45 | #[derive(Debug, Clone, Deserialize)] 46 | #[serde(untagged)] 47 | pub enum Makers { 48 | 49 | Single(Maker), 50 | 51 | Multiple(Vec), 52 | 53 | } 54 | 55 | impl Makers { 56 | pub fn make( 57 | &self, 58 | props: &HashMap, 59 | results: &mut Vec, 60 | ) { 61 | match self { 62 | Self::Single(maker) => { 63 | maker.make(props, results); 64 | } 65 | Self::Multiple(vec) => { 66 | for maker in vec { 67 | maker.make(props, results); 68 | } 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/pattern.rs: -------------------------------------------------------------------------------- 1 | use { 2 | lazy_static::lazy_static, 3 | regex::{Captures, Regex}, 4 | serde::{Deserialize, Deserializer}, 5 | std::collections::HashMap, 6 | }; 7 | 8 | /// Patterns are built from strings like "bla ${some_var} ${some.otherone} bla" 9 | /// and are expanded with HashMap 10 | /// TODO use an enum, and define an identity for the simple case 11 | #[derive(Debug, Clone)] 12 | pub struct Pattern { 13 | pub src: String, 14 | } 15 | 16 | impl Pattern { 17 | pub fn inject(&self, props: &HashMap) -> String { 18 | lazy_static! { 19 | static ref OUT_GROUP_REGEX: Regex = Regex::new(r"\$\{([\w.]+)\}").unwrap(); 20 | } 21 | OUT_GROUP_REGEX 22 | .replace_all(&self.src, |caps: &Captures| { 23 | match props.get(caps.get(1).unwrap().as_str()) { 24 | Some(value) => value, 25 | None => "-missing group!-", // we'll probably panic later on 26 | } 27 | }) 28 | .to_string() 29 | } 30 | /// produce the pattern to use when the config gives none 31 | pub fn default_task() -> Self { 32 | Self { src: "${input_task}".to_owned() } 33 | } 34 | } 35 | 36 | impl<'de> Deserialize<'de> for Pattern { 37 | fn deserialize(deserializer: D) -> Result 38 | where D: Deserializer<'de> 39 | { 40 | let src = String::deserialize(deserializer)?; 41 | Ok(Self { src }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/rule.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::*, 3 | log::*, 4 | regex::Regex, 5 | serde::Deserialize, 6 | std::collections::HashMap, 7 | }; 8 | 9 | 10 | /// a rule, defined by a condition (the "on" pattern) 11 | /// and what to do with the matching tasks 12 | #[derive(Debug, Clone, Deserialize)] 13 | pub struct Rule { 14 | 15 | /// the name, unused for now, but having it in the JSON 16 | /// file helps making it clearer and it could be used in 17 | /// logging in the future, so it's mandatory 18 | #[serde(default = "Rule::default_name")] 19 | pub name: String, 20 | 21 | /// the input task parser. It checks the rule applies to 22 | /// the task and it extracts the token which will be used 23 | /// to generate the output task 24 | #[serde(with = "serde_regex", alias = "on")] 25 | pub on_regex: Regex, 26 | 27 | /// The optional fetchers which may query some additional 28 | /// token for generation of the output task 29 | #[serde(default, alias = "fetch")] 30 | pub fetchers: Vec, 31 | 32 | /// The recipe for building the output tasks when the rule 33 | /// is verified and the fetchers did their job 34 | #[serde(alias = "make")] 35 | pub makers: Makers, 36 | 37 | } 38 | 39 | impl Rule { 40 | pub fn default_name() -> String { 41 | "".into() 42 | } 43 | pub fn is_match(&self, task: &str) -> bool { 44 | self.on_regex.is_match(task) 45 | } 46 | /// Assuming the rule matches, computes the rule results 47 | /// (there's only one RuleResult when no fetcher is involved) 48 | pub fn results(&self, task: &str) -> Result, RescError> { 49 | // props will contain the token usable for generating 50 | // the task name, output queue and output set 51 | let mut props: HashMap = HashMap::new(); 52 | props.insert("input_task".to_owned(), task.to_owned()); 53 | let caps = self.on_regex.captures(task).unwrap(); 54 | let mut results = Vec::new(); 55 | for groupname in self.on_regex.capture_names().flatten() { 56 | if let Some(value) = caps.name(groupname) { 57 | props.insert(groupname.to_string(), value.as_str().to_string()); 58 | } 59 | } 60 | if !self.fetchers.is_empty() { 61 | // if there are fetchers, we'll fetch all the possible results 62 | // and generate a ruleresult per fetchresult 63 | for fetcher in &self.fetchers { 64 | let fetch_results = fetcher.results(&props)?; 65 | debug!(" -> fetch results {:#?}", &fetch_results); 66 | for mut fetch_result in fetch_results { 67 | // we inject the parent properties 68 | // This is heavy but makes the whole simpler 69 | for (key, value) in &props { 70 | fetch_result.props.insert(key.clone(), value.clone()); 71 | } 72 | trace!(" merged: {:#?}", &fetch_result.props); 73 | self.makers.make(&fetch_result.props, &mut results); 74 | } 75 | } 76 | } else { 77 | self.makers.make(&props, &mut results); 78 | } 79 | Ok(results) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/rule_result.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | /// result of applying a rule to a task 4 | #[derive(Debug)] 5 | pub struct RuleResult { 6 | 7 | /// the task to generate 8 | pub task: String, 9 | 10 | /// the queue where to write the task 11 | pub queue: String, 12 | 13 | /// the sorted set where to check the task 14 | /// isn't yet in the queue 15 | pub set: Option, 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/ruleset.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::{ 3 | rule::Rule, 4 | }, 5 | serde::Deserialize, 6 | }; 7 | 8 | /// all the rules of a watcher, that is the rules 9 | /// related to an input queue 10 | #[derive(Debug, Deserialize)] 11 | pub struct Ruleset { 12 | pub rules: Vec, 13 | } 14 | 15 | impl Ruleset { 16 | pub fn matching_rules(&self, task: &str) -> Vec<&Rule> { 17 | self.rules.iter().filter(|r| r.is_match(task)).collect() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/serde_format.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::*, 3 | serde::de::DeserializeOwned, 4 | std::{ 5 | fs, 6 | path::Path, 7 | }, 8 | }; 9 | 10 | 11 | /// Formats usable for reading configuration files 12 | #[derive(Default, PartialEq, Eq, Debug, Clone, Copy)] 13 | pub enum SerdeFormat { 14 | Hjson, 15 | #[default] 16 | Json, 17 | } 18 | 19 | pub static FORMATS: &[SerdeFormat] = &[ 20 | SerdeFormat::Hjson, 21 | SerdeFormat::Json, 22 | ]; 23 | 24 | impl SerdeFormat { 25 | pub fn key(self) -> &'static str { 26 | match self { 27 | Self::Hjson => "hjson", 28 | Self::Json => "json", 29 | } 30 | } 31 | pub fn from_key(key: &str) -> Option { 32 | match key { 33 | "hjson" => Some(SerdeFormat::Hjson), 34 | "json" => Some(SerdeFormat::Json), 35 | _ => None, 36 | } 37 | } 38 | pub fn from_path(path: &Path) -> Result { 39 | path.extension() 40 | .and_then(|os| os.to_str()) 41 | .map(|ext| ext.to_lowercase()) 42 | .and_then(|key| Self::from_key(&key)) 43 | .ok_or_else(|| ConfError::UnknownFileExtension(path.to_string_lossy().to_string())) 44 | } 45 | pub fn read_file(path: &Path) -> Result 46 | where T: DeserializeOwned 47 | { 48 | let format = Self::from_path(path)?; 49 | match format { 50 | Self::Hjson => { 51 | let file_content = fs::read_to_string(path)?; 52 | let conf = deser_hjson::from_str(&file_content); 53 | if let Err(e) = &conf { 54 | warn!("Error while deserializing conf: {:#?}", e); 55 | } 56 | Ok(conf?) 57 | } 58 | Self::Json => { 59 | Ok(serde_json::from_reader(fs::File::open(path)?)?) 60 | } 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/watcher.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::*, 3 | log::*, 4 | redis::{self, Commands, Connection}, 5 | serde::Deserialize, 6 | std::{ 7 | time::SystemTime, 8 | }, 9 | }; 10 | 11 | #[derive(Debug, Deserialize)] 12 | pub struct WatcherConf { 13 | pub input_queue: String, 14 | pub taken_queue: Option, 15 | pub rules: Vec, 16 | } 17 | 18 | /// A watcher watches the events incoming in one specific queue 19 | /// and applies rules to generate tasks 20 | pub struct Watcher { 21 | con: Connection, 22 | listener_channel: String, 23 | input_queue: String, 24 | taken_queue: String, // can't be shared between watchers 25 | ruleset: Ruleset, 26 | } 27 | 28 | impl Watcher { 29 | 30 | pub fn new( 31 | watcher_conf: &WatcherConf, 32 | global_conf: &Conf, 33 | ) -> Result { 34 | let listener_channel = global_conf.listener_channel.clone(); 35 | let input_queue = watcher_conf.input_queue.clone(); 36 | let taken_queue = match watcher_conf.taken_queue.as_ref() { 37 | Some(queue) => queue.clone(), 38 | None => format!("{}/taken", &input_queue), 39 | }; 40 | let ruleset = Ruleset { 41 | rules: watcher_conf.rules.clone(), 42 | }; 43 | let client = redis::Client::open(&*global_conf.redis.url)?; 44 | let con = client.get_connection()?; 45 | debug!("got redis connection"); 46 | Ok(Self { 47 | con, 48 | listener_channel, 49 | input_queue, 50 | taken_queue, 51 | ruleset, 52 | }) 53 | } 54 | 55 | pub fn run(&mut self) -> Result<(), RescError> { 56 | self.empty_taken_queue(); 57 | self.watch_input_queue() 58 | } 59 | 60 | /// move tasks from the taken queue to the input queue 61 | /// 62 | /// This is done on start to reschedule the tasks that 63 | /// weren't completely handled. 64 | fn empty_taken_queue(&mut self) { 65 | debug!("watcher cleans its taken queue"); 66 | let mut n = 0; 67 | while let Ok(taken) = self.con.rpoplpush::<_, String>(&self.taken_queue, &self.input_queue) { 68 | debug!( 69 | " moving {:?} from {:?} to {:?}", 70 | &taken, &self.taken_queue, &self.input_queue 71 | ); 72 | n += 1; 73 | } 74 | if n > 0 { 75 | warn!( 76 | "moved {} tasks from {:?} to {:?}", 77 | n, &self.taken_queue, &self.input_queue 78 | ); 79 | } 80 | } 81 | 82 | /// completely handle one event received on the input queue 83 | fn handle_input_event(&mut self, event: String) -> Result<(), RescError> { 84 | let now = now_secs(); 85 | info!( 86 | "<- got {:?} in queue {:?} @ {}", 87 | &event, &self.input_queue, now 88 | ); 89 | 90 | // we first compute all the rule results 91 | let mut results = Vec::new(); 92 | for rule in self.ruleset.matching_rules(&event) { 93 | debug!(" applying rule {:?}", rule.name); 94 | match rule.results(&event) { 95 | Ok(mut rule_results) => { 96 | results.append(&mut rule_results); 97 | } 98 | Err(e) => { 99 | // A possible failure reason is a fetch not possible because of 100 | // network or server condition. 101 | // TODO should we do something better ? Requeue ? 102 | error!(" Rule execution failed: {:?}", e); 103 | } 104 | } 105 | } 106 | debug!(" {} result(s)", results.len()); 107 | 108 | // we now apply the rule results, that is we push the tasks 109 | for r in results { 110 | // if the rule specifies a task_set, we check the task isn't 111 | // already present in the set 112 | let in_set_time: Option = r.set.as_ref() 113 | .and_then(|s| self.con.zscore(s, &r.task).ok()); 114 | if let Some(time) = in_set_time { 115 | info!(" task {:?} already queued @ {}", &r.task, time); 116 | continue; 117 | } 118 | info!(" -> {:?} pushed to queue {:?}", &r.task, &r.queue); 119 | if let Some(task_set) = r.set.as_ref() { 120 | // we push first to the task set, to avoid a race condition: 121 | // a worker not finding the task in the set 122 | self.con.zadd(task_set, &r.task, now)?; 123 | debug!( 124 | " {:?} pushed to task_set {:?} @ {}", 125 | &r.task, task_set, now 126 | ); 127 | } 128 | self.con.lpush(&r.queue, &r.task)?; 129 | self.con.publish( 130 | &self.listener_channel, 131 | format!("{} TRIGGER {} -> {}", &self.taken_queue, &event, &r.task), 132 | )?; 133 | } 134 | 135 | // the event can now be removed from the taken queue 136 | self.con.lrem(&self.taken_queue, 1, &event)?; 137 | self.con.publish( 138 | &self.listener_channel, 139 | format!("{} DONE {}", &self.taken_queue, &event), 140 | )?; 141 | debug!(" done with task {:?}", &event); 142 | Ok(()) 143 | } 144 | 145 | /// continuously watch the input queue an apply rules on the events 146 | /// it takes in the queue 147 | fn watch_input_queue(&mut self) -> Result<(), RescError> { 148 | info!("watcher launched on queue {:?}...", &self.input_queue); 149 | loop { 150 | match self.con.brpoplpush(&self.input_queue, &self.taken_queue, 0) { 151 | Ok(event) => { 152 | self.handle_input_event(event)? 153 | } 154 | Err(e) => { 155 | error!("BRPOPLPUSH on {:?} failed : {}", &self.input_queue, e); 156 | } 157 | } 158 | } 159 | } 160 | 161 | } 162 | 163 | /// build the Epoch related timestamp, in seconds as f64 164 | /// because we want to use in in JSON and JS. Precision 165 | /// in f64 is not lost because this number is smaller than 2^51. 166 | fn now_secs() -> f64 { 167 | SystemTime::now() 168 | .duration_since(SystemTime::UNIX_EPOCH) 169 | .unwrap() 170 | .as_secs() 171 | as f64 172 | } 173 | --------------------------------------------------------------------------------