commit b624dc3b3405186682124423d1ea6989872338e7 Author: Joe Bellus Date: Sat Nov 13 00:29:29 2021 -0500 Sync commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..291450a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,779 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3720d0064a0ce5c0de7bd93bdb0a6caebab2a9b5668746145d7b3b0c5da02914" +dependencies = [ + "actix-rt", + "actix_derive", + "bitflags", + "bytes", + "crossbeam-channel", + "futures-core", + "futures-sink", + "futures-task", + "futures-util", + "log", + "once_cell", + "parking_lot", + "pin-project-lite", + "smallvec", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-rt" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0c218d0a17c120f10ee0c69c9f0c45d87319e8f66b1f065e8412b612fc3e24" +dependencies = [ + "actix-macros", + "futures-core", + "tokio", +] + +[[package]] +name = "actix_derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d44b8fee1ced9671ba043476deddef739dd0959bf77030b26b738cc591737a7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "arkham" +version = "0.1.1" +dependencies = [ + "console", +] + +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-io" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +dependencies = [ + "concurrent-queue", + "futures-lite", + "libc", + "log", + "once_cell", + "parking", + "polling", + "slab", + "socket2", + "waker-fn", + "winapi", +] + +[[package]] +name = "async-process" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83137067e3a2a6a06d67168e49e68a0957d215410473a740cea95a2425c0b7c6" +dependencies = [ + "async-io", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "libc", + "once_cell", + "signal-hook", + "winapi", +] + +[[package]] +name = "async-task" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "blocking" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046e47d4b2d391b1f6f8b407b1deb8dee56c1852ccd868becf2710f601b5f427" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cache-padded" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "conductor" +version = "0.1.0" +dependencies = [ + "actix", + "actix-rt", + "arkham", + "async-process", + "expand_str", + "futures", + "serde", + "serde_yaml", +] + +[[package]] +name = "console" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "regex", + "terminal_size", + "unicode-width", + "winapi", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "event-listener" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" + +[[package]] +name = "expand_str" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7bfbc9fbd454fca65e24c398c860da7bf0b76d0d4e62eb89e2e72d69e18a0e4" + +[[package]] +name = "fastrand" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" +dependencies = [ + "instant", +] + +[[package]] +name = "futures" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" + +[[package]] +name = "futures-executor" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" +dependencies = [ + "autocfg", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" + +[[package]] +name = "futures-task" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" + +[[package]] +name = "futures-util" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" +dependencies = [ + "autocfg", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "polling" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +dependencies = [ + "cfg-if", + "libc", + "log", + "wepoll-ffi", + "winapi", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_yaml" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af" +dependencies = [ + "dtoa", + "indexmap", + "serde", + "yaml-rust", +] + +[[package]] +name = "signal-hook" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + +[[package]] +name = "socket2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tokio" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "winapi", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1f7ac10 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "conductor" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0.130", features = ["derive"] } +serde_yaml = "0.8.21" +expand_str = "0.1.1" +actix = "0.12.0" +actix-rt = "2.4.0" +async-process = "1.3.0" +futures = "0.3.17" +arkham = { path = "../arkham" } + + diff --git a/src/definition.rs b/src/definition.rs new file mode 100644 index 0000000..41f3840 --- /dev/null +++ b/src/definition.rs @@ -0,0 +1,356 @@ +use crate::job::{Job, Jobs}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Serialize, Deserialize, Default)] +#[serde(default)] +pub struct Project { + pub components: Vec, + pub groups: Vec, + pub env: HashMap, + pub tasks: Vec, +} + +impl Project { + fn get_absolute_task(&self, name: &str) -> Option { + self.tasks + .iter() + .find(|t| t.name == name) + .map(|task| { + let job = self.build_project_task_job(task); + let mut jobs = task + .before + .iter() + .map(|name| self.get_absolute_task(name)) + .flatten() + .map(|jobs| jobs.to_vec()) + .flatten() + .collect::>(); + jobs.push(job); + Jobs::new(jobs) + }) + .or_else(|| { + self.components.iter().find_map(|component| { + component + .tasks + .iter() + .find(|t| format!("{}:{}", component.name, t.name) == name) + .map(|task| { + let job = self.build_component_task_job(component, task); + let mut jobs = task + .before + .iter() + .map(|name| self.get_absolute_task(name)) + .flatten() + .map(|jobs| jobs.to_vec()) + .flatten() + .collect::>(); + jobs.push(job); + Jobs::new(jobs) + }) + }) + }) + } + + fn get_relative_task(&self, task_name: &str, component_name: &str) -> Option { + if let Some(component) = self.components.iter().find(|c| c.name == component_name) { + component + .tasks + .iter() + .find(|t| t.name == task_name) + .map(|task| { + let job = self.build_component_task_job(component, task); + let mut jobs = task + .before + .iter() + .map(|name| self.get_by_name(name)) + .flatten() + .map(|jobs| jobs.to_vec()) + .flatten() + .collect::>(); + jobs.push(job); + Jobs::new(jobs) + }) + } else { + None + } + } + + fn get_component(&self, name: &str) -> Option { + self.components.iter().find(|c| c.name == name).map(|c| { + let component_job = self.build_component_job(c); + let mut absolute_tasks = c + .before + .iter() + .map(|name| self.get_absolute_task(name)) + .flatten() + .map(|jobs| jobs.to_vec()) + .flatten() + .collect::>(); + let mut relative_tasks = c + .before + .iter() + .map(|name| self.get_relative_task(name, &c.name)) + .flatten() + .map(|jobs| jobs.to_vec()) + .flatten() + .collect::>(); + + absolute_tasks.append(&mut relative_tasks); + absolute_tasks.push(component_job); + Jobs::new(absolute_tasks) + }) + } + + fn build_component_job(&self, c: &Component) -> Job { + let mut env = self.env.clone(); + env.extend(c.env.clone()); + Job { + name: c.name.clone(), + env, + ..Job::default() + } + } + + fn build_project_task_job(&self, task: &TaskDefinition) -> Job { + let mut env = self.env.clone(); + env.extend(task.env.clone()); + Job { + name: task.name.clone(), + ..Job::default() + } + } + + fn build_component_task_job(&self, component: &Component, task: &TaskDefinition) -> Job { + let mut env = self.env.clone(); + env.extend(component.env.clone()); + env.extend(task.env.clone()); + Job { + name: format!("{}:{}", component.name.clone(), task.name.clone()), + env, + ..Job::default() + } + } + + pub fn get_by_name(&self, name: &str) -> Option { + self.get_component(name) + .or_else(|| self.get_absolute_task(name)) + } + + pub fn from_str(s: &str) -> Self { + serde_yaml::from_str(s).unwrap() + } +} + +#[derive(Serialize, Deserialize, Default)] +#[serde(default)] +pub struct Group { + name: String, + components: Vec, + env: HashMap, +} + +#[derive(Serialize, Deserialize)] +#[serde(default)] +pub struct Component { + pub env: HashMap, + pub name: String, + pub command: String, + pub path: Option, + pub retry: bool, + pub keep_alive: bool, + pub retry_delay: u64, + pub before: Vec, + pub tasks: Vec, +} + +impl Default for Component { + fn default() -> Self { + Self { + env: HashMap::new(), + name: "UNNAMED".to_string(), + command: String::new(), + path: None, + retry: false, + keep_alive: true, + retry_delay: 2, + before: vec![], + tasks: vec![], + } + } +} + +#[derive(Serialize, Deserialize, Default)] +#[serde(default)] +pub struct TaskDefinition { + pub env: HashMap, + pub name: String, + pub command: String, + pub path: Option, + pub retry: bool, + pub keep_alive: bool, + pub retry_delay: u64, + pub before: Vec, +} + +#[cfg(test)] +mod tests { + use super::Project; + + #[test] + fn component_by_name() { + let project = Project::from_str( + r#" + components: + - name: test-component + "#, + ); + + let jobs = project.get_by_name("test-component").unwrap(); + assert_eq!(jobs.len(), 1); + } + + #[test] + fn project_task() { + let project = Project::from_str( + r#" + tasks: + - name: task1 + "#, + ); + + let jobs = project.get_by_name("task1").unwrap(); + assert_eq!(jobs.len(), 1); + } + + #[test] + fn component_task() { + let project = Project::from_str( + r#" + components: + - name: c1 + tasks: + - name: task1 + "#, + ); + + let jobs = project.get_by_name("c1:task1").unwrap(); + assert_eq!(jobs.len(), 1); + } + + #[test] + fn component_dependent_absolute_component_task() { + let project = Project::from_str( + r#" + components: + - name: main-cmp + before: + - main-cmp:sub-task + tasks: + - name: sub-task + "#, + ); + + let jobs = project.get_by_name("main-cmp").unwrap(); + assert_eq!(jobs.len(), 2); + assert_eq!(jobs.first().unwrap().name, "main-cmp:sub-task"); + } + + #[test] + fn component_dependent_relative_component_task() { + let project = Project::from_str( + r#" + components: + - name: main-cmp + before: + - sub-task + tasks: + - name: sub-task + "#, + ); + + let jobs = project.get_by_name("main-cmp").unwrap(); + assert_eq!(jobs.len(), 2); + assert_eq!(jobs.first().unwrap().name, "main-cmp:sub-task"); + } + + #[test] + fn complicated_dependencies() { + let project = Project::from_str( + r#" + components: + - name: ui + before: + - build-ui + tasks: + - name: build-ui + - name: server + before: + - setup + tasks: + - name: setup + - name: build + before: + - server:setup + tasks: + - name: build + before: + - ui:build-ui + - server:build + "#, + ); + + let jobs = project.get_by_name("build").unwrap(); + assert_eq!(jobs.get(0).unwrap().name, "ui:build-ui"); + assert_eq!(jobs.get(1).unwrap().name, "server:setup"); + assert_eq!(jobs.get(2).unwrap().name, "server:build"); + assert_eq!(jobs.get(3).unwrap().name, "build"); + assert_eq!(jobs.len(), 4); + } + + #[test] + fn component_env() { + let project = Project::from_str( + r#" + env: + foo: one + sub: two + components: + - name: main-cmp + env: + sub: three + "#, + ); + + let jobs = project.get_by_name("main-cmp").unwrap(); + let job = jobs.first().unwrap(); + assert_eq!(job.env.get("foo"), Some(&String::from("one"))); + assert_eq!(job.env.get("sub"), Some(&String::from("three"))); + } + + #[test] + fn task_env() { + let project = Project::from_str( + r#" + env: + root: ten + foo: one + sub: two + components: + - name: main-cmp + env: + sub: three + tasks: + - name: t + env: + foo: four + "#, + ); + + let jobs = project.get_by_name("main-cmp:t").unwrap(); + let job = jobs.first().unwrap(); + assert_eq!(job.env.get("foo"), Some(&String::from("four"))); + assert_eq!(job.env.get("sub"), Some(&String::from("three"))); + assert_eq!(job.env.get("root"), Some(&String::from("ten"))); + } +} diff --git a/src/job.rs b/src/job.rs new file mode 100644 index 0000000..5b5545c --- /dev/null +++ b/src/job.rs @@ -0,0 +1,66 @@ +use actix::Message; +use std::{ + collections::HashMap, + ops::{Deref, DerefMut}, +}; + +#[derive(Clone, Message)] +#[rtype(result = "()")] +pub struct Jobs(Vec); + +impl Jobs { + pub fn new(tasks: Vec) -> Self { + Self(tasks) + } +} + +impl std::fmt::Debug for Jobs { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Jobs") + .field( + "list", + &self.iter().map(|j| j.name.clone()).collect::>(), + ) + .finish() + } +} + +impl Deref for Jobs { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Jobs { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[derive(Clone, Debug, Message)] +#[rtype(result = "()")] +pub struct Job { + pub name: String, + pub command: String, + pub path: String, + pub env: HashMap, + pub retry: bool, + pub keep_alive: bool, + pub retry_delay: u64, +} + +impl Default for Job { + fn default() -> Self { + Job { + name: "Unnamed".to_string(), + command: "".to_string(), + path: ".".to_string(), + env: HashMap::new(), + retry: true, + keep_alive: true, + retry_delay: 2, + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b6b2d94 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,29 @@ +use crate::definition::Project; +use actix::prelude::*; +use runner::Manager; + +mod definition; +mod job; +mod runner; + +#[actix_rt::main] +async fn main() { + let project = Project::from_str( + r#" + components: + - name: ls + commands: + - ls + - name: currentdir + commands: + - pwd + "#, + ); + let jobs = project.get_by_name("ls").unwrap(); + let manager = Manager::new().start(); + manager.do_send(jobs); + + actix_rt::signal::ctrl_c() + .await + .expect("failed to listen for event"); +} diff --git a/src/runner.rs b/src/runner.rs new file mode 100644 index 0000000..0df7f0e --- /dev/null +++ b/src/runner.rs @@ -0,0 +1,189 @@ +use crate::job::{Job, Jobs}; +use std::collections::HashMap; +use std::process::exit; +use std::process::Stdio; +use std::time::Duration; + +use actix::prelude::*; + +use async_process::Child; +use async_process::Command; +use futures::io::AsyncBufReadExt; +use futures::io::BufReader; + +#[derive(Clone, Message, Debug)] +#[rtype(result = "()")] +pub enum Event { + StartedTask(i64, String), + Output(i64, String, String), + Error(i64, String, String), + FinishedTask(i64, String), + Completed(i64), +} + +#[derive(Debug)] +pub struct Runner { + pub id: i64, + pub tasks: Jobs, + pub manager: Recipient, + child: Option, + pub current_job: Option, +} + +impl Runner { + pub fn new(id: i64, tasks: Jobs, manager: Recipient) -> Self { + Self { + tasks, + manager, + id, + current_job: None, + child: None, + } + } +} + +impl Actor for Runner { + type Context = Context; + + fn started(&mut self, ctx: &mut Self::Context) { + if let Some(task) = self.tasks.pop() { + ctx.notify(task); + } else { + ctx.stop(); + } + } +} + +impl Handler for Runner { + type Result = (); + fn handle(&mut self, job: Job, ctx: &mut Self::Context) -> Self::Result { + self.current_job = Some(job.clone()); + let _ = self + .manager + .do_send(Event::StartedTask(self.id, job.name.clone())); + + let mut child = Command::new("sh") + .arg("-c") + .arg(&job.command) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap(); + + let stdout = child + .stdout + .take() + .expect("child did not have a handle to stdout"); + let reader = BufReader::new(stdout).lines(); + let fut = async move { + child + .status() + .await + .expect("child process encountered an error") + }; + let fut = actix::fut::wrap_future::<_, Self>(fut).map(move |status, _, ctx| { + if job.keep_alive && status.code() == Some(0) { + let delay = Duration::from_secs(job.retry_delay); + arkham::vox::header(format!("Restarting {} in {}s", job.name, delay.as_secs())); + ctx.notify_later(job, delay); + } + }); + + ctx.spawn(fut); + ctx.add_stream(reader); + } +} + +impl StreamHandler> for Runner { + fn handle(&mut self, item: Result, _: &mut Self::Context) { + let ev = match item { + Ok(v) => Event::Output( + self.id, + self.current_job + .as_ref() + .map(|i| i.name.clone()) + .unwrap_or_else(|| String::from("UNKNOWN")), + v, + ), + Err(e) => Event::Error( + self.id, + self.current_job.as_ref().unwrap().name.clone(), + e.to_string(), + ), + }; + let _ = self.manager.do_send(ev); + } + + fn finished(&mut self, ctx: &mut Self::Context) { + let _ = self.manager.do_send(Event::FinishedTask( + self.id, + self.current_job.as_ref().unwrap().name.clone(), + )); + if let Some(task) = self.tasks.pop() { + self.current_job = None; + ctx.notify(task); + } else if !self + .current_job + .as_ref() + .map(|i| i.keep_alive) + .unwrap_or(false) + { + let _ = self.manager.do_send(Event::Completed(self.id)); + ctx.stop(); + } + } +} + +#[derive(Default)] +pub struct Manager { + id_count: i64, + runners: HashMap>, +} + +impl Manager { + pub fn new() -> Self { + Self::default() + } +} + +impl Actor for Manager { + type Context = Context; +} + +impl Handler for Manager { + type Result = (); + + fn handle(&mut self, msg: Event, ctx: &mut Self::Context) -> Self::Result { + match msg { + Event::StartedTask(_, name) => { + arkham::vox::header(format!("{} - Started", name)); + } + Event::Output(_, name, v) => { + println!("[{}] {}", name, v) + } + Event::Error(_, v, name) => { + println!("[{}] Error: {}", name, v); + } + Event::FinishedTask(_, name) => { + arkham::vox::header(format!("{} - Finished", name)); + } + Event::Completed(id) => { + self.runners.remove(&id); + if self.runners.is_empty() { + ctx.stop(); + System::current().stop(); + exit(0) + } + } + } + } +} + +impl Handler for Manager { + type Result = (); + fn handle(&mut self, msg: Jobs, ctx: &mut Self::Context) -> Self::Result { + self.id_count += 1; + let addr = Runner::new(self.id_count, msg, ctx.address().recipient()).start(); + self.runners.insert(self.id_count, addr); + } +}