├── .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 | 
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 | 
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 | 
47 |
48 |
49 | ### Resc removes the event from global/taken
50 |
51 | 
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 | 
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 | 
65 |
66 |
67 | ### ... then removes the task from its taken queue
68 |
69 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------