Start creating command line interface with `check`, `build`, `graph` and `templates` commandstags/v0.0.0-test-pr-120
| @@ -389,6 +389,12 @@ version = "1.0.73" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" | |||
| [[package]] | |||
| name = "cesu8" | |||
| version = "1.1.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" | |||
| [[package]] | |||
| name = "cfg-if" | |||
| version = "1.0.0" | |||
| @@ -427,26 +433,54 @@ dependencies = [ | |||
| [[package]] | |||
| name = "clap" | |||
| version = "3.1.12" | |||
| version = "3.2.20" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "7c167e37342afc5f33fd87bbc870cedd020d2a6dffa05d45ccd9241fbdd146db" | |||
| checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd" | |||
| dependencies = [ | |||
| "atty", | |||
| "bitflags", | |||
| "clap_derive", | |||
| "clap_lex", | |||
| "clap_derive 3.2.18", | |||
| "clap_lex 0.2.4", | |||
| "indexmap", | |||
| "lazy_static", | |||
| "once_cell", | |||
| "strsim 0.10.0", | |||
| "termcolor", | |||
| "textwrap 0.15.0", | |||
| ] | |||
| [[package]] | |||
| name = "clap" | |||
| version = "4.0.3" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "3943ff31339d6d148b4e9cfb9b1eb0f814989ed21dfede3c2d2e11f3d9497f60" | |||
| dependencies = [ | |||
| "atty", | |||
| "bitflags", | |||
| "clap_derive 4.0.1", | |||
| "clap_lex 0.3.0", | |||
| "once_cell", | |||
| "strsim 0.10.0", | |||
| "termcolor", | |||
| ] | |||
| [[package]] | |||
| name = "clap_derive" | |||
| version = "3.2.18" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" | |||
| dependencies = [ | |||
| "heck 0.4.0", | |||
| "proc-macro-error", | |||
| "proc-macro2", | |||
| "quote", | |||
| "syn", | |||
| ] | |||
| [[package]] | |||
| name = "clap_derive" | |||
| version = "3.1.7" | |||
| version = "4.0.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1" | |||
| checksum = "ca689d7434ce44517a12a89456b2be4d1ea1cafcd8f581978c03d45f5a5c12a7" | |||
| dependencies = [ | |||
| "heck 0.4.0", | |||
| "proc-macro-error", | |||
| @@ -457,9 +491,18 @@ dependencies = [ | |||
| [[package]] | |||
| name = "clap_lex" | |||
| version = "0.1.1" | |||
| version = "0.2.4" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" | |||
| dependencies = [ | |||
| "os_str_bytes", | |||
| ] | |||
| [[package]] | |||
| name = "clap_lex" | |||
| version = "0.3.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "189ddd3b5d32a70b35e7686054371742a937b0d99128e76dde6340210e966669" | |||
| checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" | |||
| dependencies = [ | |||
| "os_str_bytes", | |||
| ] | |||
| @@ -474,6 +517,16 @@ dependencies = [ | |||
| "unicode-width", | |||
| ] | |||
| [[package]] | |||
| name = "combine" | |||
| version = "4.6.6" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" | |||
| dependencies = [ | |||
| "bytes", | |||
| "memchr", | |||
| ] | |||
| [[package]] | |||
| name = "communication-layer-pub-sub" | |||
| version = "0.1.0" | |||
| @@ -660,6 +713,12 @@ dependencies = [ | |||
| "syn", | |||
| ] | |||
| [[package]] | |||
| name = "cty" | |||
| version = "0.2.2" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" | |||
| [[package]] | |||
| name = "cxx" | |||
| version = "1.0.73" | |||
| @@ -728,6 +787,41 @@ dependencies = [ | |||
| "syn", | |||
| ] | |||
| [[package]] | |||
| name = "darling" | |||
| version = "0.13.4" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" | |||
| dependencies = [ | |||
| "darling_core", | |||
| "darling_macro", | |||
| ] | |||
| [[package]] | |||
| name = "darling_core" | |||
| version = "0.13.4" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" | |||
| dependencies = [ | |||
| "fnv", | |||
| "ident_case", | |||
| "proc-macro2", | |||
| "quote", | |||
| "strsim 0.10.0", | |||
| "syn", | |||
| ] | |||
| [[package]] | |||
| name = "darling_macro" | |||
| version = "0.13.4" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" | |||
| dependencies = [ | |||
| "darling_core", | |||
| "quote", | |||
| "syn", | |||
| ] | |||
| [[package]] | |||
| name = "dashmap" | |||
| version = "4.0.2" | |||
| @@ -787,12 +881,24 @@ dependencies = [ | |||
| "winapi", | |||
| ] | |||
| [[package]] | |||
| name = "dora-cli" | |||
| version = "0.1.0" | |||
| dependencies = [ | |||
| "clap 4.0.3", | |||
| "dora-core", | |||
| "eyre", | |||
| "serde_yaml 0.9.11", | |||
| "tempfile", | |||
| "webbrowser", | |||
| ] | |||
| [[package]] | |||
| name = "dora-coordinator" | |||
| version = "0.1.0" | |||
| dependencies = [ | |||
| "bincode", | |||
| "clap 3.1.12", | |||
| "clap 3.2.20", | |||
| "dora-core", | |||
| "dora-message", | |||
| "dora-node-api", | |||
| @@ -801,7 +907,7 @@ dependencies = [ | |||
| "futures-concurrency", | |||
| "rand", | |||
| "serde", | |||
| "serde_yaml", | |||
| "serde_yaml 0.8.23", | |||
| "time", | |||
| "tokio", | |||
| "tokio-stream", | |||
| @@ -818,6 +924,7 @@ dependencies = [ | |||
| "dora-node-api", | |||
| "eyre", | |||
| "serde", | |||
| "serde_yaml 0.9.11", | |||
| ] | |||
| [[package]] | |||
| @@ -860,7 +967,7 @@ dependencies = [ | |||
| "flume", | |||
| "once_cell", | |||
| "serde", | |||
| "serde_yaml", | |||
| "serde_yaml 0.8.23", | |||
| "thiserror", | |||
| "tokio", | |||
| "tracing", | |||
| @@ -886,7 +993,7 @@ dependencies = [ | |||
| "eyre", | |||
| "flume", | |||
| "pyo3", | |||
| "serde_yaml", | |||
| "serde_yaml 0.8.23", | |||
| ] | |||
| [[package]] | |||
| @@ -926,7 +1033,7 @@ dependencies = [ | |||
| name = "dora-runtime" | |||
| version = "0.1.0" | |||
| dependencies = [ | |||
| "clap 3.1.12", | |||
| "clap 3.2.20", | |||
| "dora-core", | |||
| "dora-message", | |||
| "dora-node-api", | |||
| @@ -938,7 +1045,7 @@ dependencies = [ | |||
| "futures-concurrency", | |||
| "libloading", | |||
| "pyo3", | |||
| "serde_yaml", | |||
| "serde_yaml 0.8.23", | |||
| "tokio", | |||
| "tokio-stream", | |||
| "tracing", | |||
| @@ -1294,9 +1401,9 @@ dependencies = [ | |||
| [[package]] | |||
| name = "hashbrown" | |||
| version = "0.11.2" | |||
| version = "0.12.3" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" | |||
| checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" | |||
| [[package]] | |||
| name = "heck" | |||
| @@ -1468,6 +1575,12 @@ dependencies = [ | |||
| "thiserror", | |||
| ] | |||
| [[package]] | |||
| name = "ident_case" | |||
| version = "1.0.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" | |||
| [[package]] | |||
| name = "idna" | |||
| version = "0.2.3" | |||
| @@ -1487,9 +1600,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" | |||
| [[package]] | |||
| name = "indexmap" | |||
| version = "1.8.0" | |||
| version = "1.9.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" | |||
| checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" | |||
| dependencies = [ | |||
| "autocfg 1.1.0", | |||
| "hashbrown", | |||
| @@ -1565,6 +1678,26 @@ version = "1.0.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" | |||
| [[package]] | |||
| name = "jni" | |||
| version = "0.19.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" | |||
| dependencies = [ | |||
| "cesu8", | |||
| "combine", | |||
| "jni-sys", | |||
| "log", | |||
| "thiserror", | |||
| "walkdir", | |||
| ] | |||
| [[package]] | |||
| name = "jni-sys" | |||
| version = "0.3.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" | |||
| [[package]] | |||
| name = "js-sys" | |||
| version = "0.3.56" | |||
| @@ -1681,6 +1814,15 @@ version = "0.1.2" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "f26a8d2502d5aa4d411ef494ba7470eb299f05725179ce3b5de77aa01a9ffdea" | |||
| [[package]] | |||
| name = "malloc_buf" | |||
| version = "0.0.6" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" | |||
| dependencies = [ | |||
| "libc", | |||
| ] | |||
| [[package]] | |||
| name = "maplit" | |||
| version = "1.0.2" | |||
| @@ -1858,6 +2000,64 @@ name = "napi-sys" | |||
| version = "1.0.0" | |||
| source = "git+https://github.com/getditto/napi-rs?branch=ditto/closure-into-jsfunction#da095cc3f1af133344083b525d7e9763b347e249" | |||
| [[package]] | |||
| name = "ndk" | |||
| version = "0.7.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" | |||
| dependencies = [ | |||
| "bitflags", | |||
| "jni-sys", | |||
| "ndk-sys", | |||
| "num_enum", | |||
| "raw-window-handle", | |||
| "thiserror", | |||
| ] | |||
| [[package]] | |||
| name = "ndk-context" | |||
| version = "0.1.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" | |||
| [[package]] | |||
| name = "ndk-glue" | |||
| version = "0.7.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" | |||
| dependencies = [ | |||
| "libc", | |||
| "log", | |||
| "ndk", | |||
| "ndk-context", | |||
| "ndk-macro", | |||
| "ndk-sys", | |||
| "once_cell", | |||
| "parking_lot", | |||
| ] | |||
| [[package]] | |||
| name = "ndk-macro" | |||
| version = "0.3.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" | |||
| dependencies = [ | |||
| "darling", | |||
| "proc-macro-crate", | |||
| "proc-macro2", | |||
| "quote", | |||
| "syn", | |||
| ] | |||
| [[package]] | |||
| name = "ndk-sys" | |||
| version = "0.4.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "21d83ec9c63ec5bf950200a8e508bdad6659972187b625469f58ef8c08e29046" | |||
| dependencies = [ | |||
| "jni-sys", | |||
| ] | |||
| [[package]] | |||
| name = "nix" | |||
| version = "0.22.3" | |||
| @@ -1952,6 +2152,27 @@ dependencies = [ | |||
| "libc", | |||
| ] | |||
| [[package]] | |||
| name = "num_enum" | |||
| version = "0.5.7" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" | |||
| dependencies = [ | |||
| "num_enum_derive", | |||
| ] | |||
| [[package]] | |||
| name = "num_enum_derive" | |||
| version = "0.5.7" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" | |||
| dependencies = [ | |||
| "proc-macro-crate", | |||
| "proc-macro2", | |||
| "quote", | |||
| "syn", | |||
| ] | |||
| [[package]] | |||
| name = "num_threads" | |||
| version = "0.1.5" | |||
| @@ -1961,6 +2182,15 @@ dependencies = [ | |||
| "libc", | |||
| ] | |||
| [[package]] | |||
| name = "objc" | |||
| version = "0.2.7" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" | |||
| dependencies = [ | |||
| "malloc_buf", | |||
| ] | |||
| [[package]] | |||
| name = "once_cell" | |||
| version = "1.13.0" | |||
| @@ -2361,6 +2591,17 @@ dependencies = [ | |||
| "syn", | |||
| ] | |||
| [[package]] | |||
| name = "proc-macro-crate" | |||
| version = "1.2.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" | |||
| dependencies = [ | |||
| "once_cell", | |||
| "thiserror", | |||
| "toml", | |||
| ] | |||
| [[package]] | |||
| name = "proc-macro-error" | |||
| version = "1.0.4" | |||
| @@ -2605,6 +2846,15 @@ dependencies = [ | |||
| "getrandom", | |||
| ] | |||
| [[package]] | |||
| name = "raw-window-handle" | |||
| version = "0.5.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" | |||
| dependencies = [ | |||
| "cty", | |||
| ] | |||
| [[package]] | |||
| name = "rayon" | |||
| version = "1.5.1" | |||
| @@ -2864,6 +3114,15 @@ dependencies = [ | |||
| "syn", | |||
| ] | |||
| [[package]] | |||
| name = "same-file" | |||
| version = "1.0.6" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" | |||
| dependencies = [ | |||
| "winapi-util", | |||
| ] | |||
| [[package]] | |||
| name = "schannel" | |||
| version = "0.1.19" | |||
| @@ -2937,18 +3196,18 @@ checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" | |||
| [[package]] | |||
| name = "serde" | |||
| version = "1.0.136" | |||
| version = "1.0.144" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" | |||
| checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" | |||
| dependencies = [ | |||
| "serde_derive", | |||
| ] | |||
| [[package]] | |||
| name = "serde_derive" | |||
| version = "1.0.136" | |||
| version = "1.0.144" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" | |||
| checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" | |||
| dependencies = [ | |||
| "proc-macro2", | |||
| "quote", | |||
| @@ -2978,6 +3237,19 @@ dependencies = [ | |||
| "yaml-rust", | |||
| ] | |||
| [[package]] | |||
| name = "serde_yaml" | |||
| version = "0.9.11" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "89f31df3f50926cdf2855da5fd8812295c34752cb20438dae42a67f79e021ac3" | |||
| dependencies = [ | |||
| "indexmap", | |||
| "itoa", | |||
| "ryu", | |||
| "serde", | |||
| "unsafe-libyaml", | |||
| ] | |||
| [[package]] | |||
| name = "sha-1" | |||
| version = "0.8.2" | |||
| @@ -3367,6 +3639,15 @@ dependencies = [ | |||
| "tracing", | |||
| ] | |||
| [[package]] | |||
| name = "toml" | |||
| version = "0.5.9" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" | |||
| dependencies = [ | |||
| "serde", | |||
| ] | |||
| [[package]] | |||
| name = "tonic" | |||
| version = "0.6.2" | |||
| @@ -3588,6 +3869,12 @@ version = "0.1.8" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8" | |||
| [[package]] | |||
| name = "unsafe-libyaml" | |||
| version = "0.2.2" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "931179334a56395bcf64ba5e0ff56781381c1a5832178280c7d7f91d1679aeb0" | |||
| [[package]] | |||
| name = "untrusted" | |||
| version = "0.7.1" | |||
| @@ -3716,6 +4003,17 @@ version = "1.1.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" | |||
| [[package]] | |||
| name = "walkdir" | |||
| version = "2.3.2" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" | |||
| dependencies = [ | |||
| "same-file", | |||
| "winapi", | |||
| "winapi-util", | |||
| ] | |||
| [[package]] | |||
| name = "want" | |||
| version = "0.3.0" | |||
| @@ -3816,6 +4114,22 @@ dependencies = [ | |||
| "wasm-bindgen", | |||
| ] | |||
| [[package]] | |||
| name = "webbrowser" | |||
| version = "0.8.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "01d62aa75495ab67cdc273d0b95cc76bcedfea2ba28338a4cf9b4137949dfac5" | |||
| dependencies = [ | |||
| "jni", | |||
| "ndk-glue", | |||
| "objc", | |||
| "raw-window-handle", | |||
| "url", | |||
| "web-sys", | |||
| "widestring", | |||
| "winapi", | |||
| ] | |||
| [[package]] | |||
| name = "webpki" | |||
| version = "0.21.4" | |||
| @@ -3865,6 +4179,12 @@ dependencies = [ | |||
| "libc", | |||
| ] | |||
| [[package]] | |||
| name = "widestring" | |||
| version = "1.0.2" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" | |||
| [[package]] | |||
| name = "winapi" | |||
| version = "0.3.9" | |||
| @@ -4059,7 +4379,7 @@ dependencies = [ | |||
| "num_cpus", | |||
| "serde", | |||
| "serde_json", | |||
| "serde_yaml", | |||
| "serde_yaml 0.8.23", | |||
| "validated_struct", | |||
| "zenoh-cfg-properties", | |||
| "zenoh-core", | |||
| @@ -0,0 +1,18 @@ | |||
| [package] | |||
| name = "dora-cli" | |||
| version = "0.1.0" | |||
| edition = "2021" | |||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
| [[bin]] | |||
| name = "dora" | |||
| path = "src/main.rs" | |||
| [dependencies] | |||
| clap = { version = "4.0.3", features = ["derive"] } | |||
| eyre = "0.6.8" | |||
| dora-core = { path = "../../libraries/core" } | |||
| serde_yaml = "0.9.11" | |||
| tempfile = "3.3.0" | |||
| webbrowser = "0.8.0" | |||
| @@ -0,0 +1,73 @@ | |||
| use crate::graph; | |||
| use dora_core::descriptor::{OperatorId, SINGLE_OPERATOR_DEFAULT_ID}; | |||
| use eyre::{eyre, Context}; | |||
| use std::{path::Path, process::Command}; | |||
| pub fn build(dataflow: &Path) -> eyre::Result<()> { | |||
| let descriptor = graph::read_descriptor(dataflow)?; | |||
| let dataflow_absolute = if dataflow.is_relative() { | |||
| std::env::current_dir().unwrap().join(dataflow) | |||
| } else { | |||
| dataflow.to_owned() | |||
| }; | |||
| let working_dir = dataflow_absolute.parent().unwrap(); | |||
| let default_op_id = OperatorId::from(SINGLE_OPERATOR_DEFAULT_ID.to_string()); | |||
| for node in &descriptor.nodes { | |||
| match &node.kind { | |||
| dora_core::descriptor::NodeKind::Runtime(runtime_node) => { | |||
| for operator in &runtime_node.operators { | |||
| run_build_command(operator.config.build.as_deref(), &working_dir) | |||
| .with_context(|| { | |||
| format!( | |||
| "build command failed for operator `{}/{}`", | |||
| node.id, operator.id | |||
| ) | |||
| })?; | |||
| } | |||
| } | |||
| dora_core::descriptor::NodeKind::Custom(custom_node) => { | |||
| run_build_command(custom_node.build.as_deref(), &working_dir).with_context( | |||
| || format!("build command failed for custom node `{}`", node.id), | |||
| )? | |||
| } | |||
| dora_core::descriptor::NodeKind::Operator(operator) => { | |||
| run_build_command(operator.config.build.as_deref(), &working_dir).with_context( | |||
| || { | |||
| format!( | |||
| "build command failed for operator `{}/{}`", | |||
| node.id, | |||
| operator.id.as_ref().unwrap_or(&default_op_id) | |||
| ) | |||
| }, | |||
| )? | |||
| } | |||
| } | |||
| } | |||
| Ok(()) | |||
| } | |||
| fn run_build_command(build: Option<&str>, working_dir: &Path) -> eyre::Result<()> { | |||
| if let Some(build) = build { | |||
| let mut split = build.split_whitespace(); | |||
| let mut cmd = Command::new( | |||
| split | |||
| .next() | |||
| .ok_or_else(|| eyre!("build command is empty"))?, | |||
| ); | |||
| cmd.args(split); | |||
| cmd.current_dir(working_dir); | |||
| let exit_status = cmd | |||
| .status() | |||
| .wrap_err_with(|| format!("failed to run `{}`", build))?; | |||
| if exit_status.success() { | |||
| Ok(()) | |||
| } else { | |||
| Err(eyre!("build command returned an error code")) | |||
| } | |||
| } else { | |||
| Ok(()) | |||
| } | |||
| } | |||
| @@ -0,0 +1,166 @@ | |||
| use crate::graph::read_descriptor; | |||
| use dora_core::{ | |||
| adjust_shared_library_path, | |||
| descriptor::{self, CoreNodeKind, InputMapping, OperatorSource, UserInputMapping}, | |||
| }; | |||
| use eyre::{bail, eyre, Context}; | |||
| use std::{env::consts::EXE_EXTENSION, path::Path}; | |||
| pub fn check(dataflow_path: &Path, runtime: &Path) -> eyre::Result<()> { | |||
| let runtime = runtime.with_extension(EXE_EXTENSION); | |||
| let descriptor = read_descriptor(&dataflow_path).wrap_err_with(|| { | |||
| format!( | |||
| "failed to read dataflow descriptor at {}", | |||
| dataflow_path.display() | |||
| ) | |||
| })?; | |||
| let base = dataflow_path | |||
| .canonicalize() | |||
| .unwrap() | |||
| .parent() | |||
| .unwrap() | |||
| .to_owned(); | |||
| let nodes = descriptor.resolve_aliases(); | |||
| if nodes | |||
| .iter() | |||
| .any(|n| matches!(n.kind, CoreNodeKind::Runtime(_))) | |||
| && !runtime.is_file() | |||
| { | |||
| bail!( | |||
| "There is no runtime at {}, or it is not a file", | |||
| runtime.display() | |||
| ); | |||
| } | |||
| // check that nodes and operators exist | |||
| for node in &nodes { | |||
| match &node.kind { | |||
| descriptor::CoreNodeKind::Custom(node) => { | |||
| let mut args = node.run.split_ascii_whitespace(); | |||
| let raw = Path::new( | |||
| args.next() | |||
| .ok_or_else(|| eyre!("`run` field must not be empty"))?, | |||
| ); | |||
| let path = if raw.extension().is_none() { | |||
| raw.with_extension(EXE_EXTENSION) | |||
| } else { | |||
| raw.to_owned() | |||
| }; | |||
| base.join(&path) | |||
| .canonicalize() | |||
| .wrap_err_with(|| format!("no node exists at `{}`", path.display()))?; | |||
| } | |||
| descriptor::CoreNodeKind::Runtime(node) => { | |||
| for operator_definition in &node.operators { | |||
| match &operator_definition.config.source { | |||
| OperatorSource::SharedLibrary(path) => { | |||
| let path = adjust_shared_library_path(path)?; | |||
| if !base.join(&path).exists() { | |||
| bail!("no shared library at `{}`", path.display()); | |||
| } | |||
| } | |||
| OperatorSource::Python(path) => { | |||
| if !base.join(&path).exists() { | |||
| bail!("no Python library at `{}`", path.display()); | |||
| } | |||
| } | |||
| OperatorSource::Wasm(path) => { | |||
| if !base.join(&path).exists() { | |||
| bail!("no WASM library at `{}`", path.display()); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| // check that all inputs mappings point to an existing output | |||
| for node in &nodes { | |||
| match &node.kind { | |||
| descriptor::CoreNodeKind::Custom(custom_node) => { | |||
| for (input_id, mapping) in &custom_node.run_config.inputs { | |||
| check_input(mapping, &nodes, &format!("{}/{input_id}", node.id))?; | |||
| } | |||
| } | |||
| descriptor::CoreNodeKind::Runtime(runtime_node) => { | |||
| for operator_definition in &runtime_node.operators { | |||
| for (input_id, mapping) in &operator_definition.config.inputs { | |||
| check_input( | |||
| mapping, | |||
| &nodes, | |||
| &format!("{}/{}/{input_id}", operator_definition.id, node.id), | |||
| )?; | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| } | |||
| Ok(()) | |||
| } | |||
| fn check_input( | |||
| mapping: &InputMapping, | |||
| nodes: &[dora_core::descriptor::ResolvedNode], | |||
| input_id_str: &str, | |||
| ) -> Result<(), eyre::ErrReport> { | |||
| Ok(match mapping { | |||
| InputMapping::Timer { interval: _ } => {} | |||
| InputMapping::User(UserInputMapping { | |||
| source, | |||
| operator, | |||
| output, | |||
| }) => { | |||
| let source_node = nodes.iter().find(|n| &n.id == source).ok_or_else(|| { | |||
| eyre!("source node `{source}` mapped to input `{input_id_str}` does not exist",) | |||
| })?; | |||
| if let Some(operator_id) = operator { | |||
| let operator = match &source_node.kind { | |||
| CoreNodeKind::Runtime(runtime) => { | |||
| let operator = runtime.operators.iter().find(|o| &o.id == operator_id); | |||
| operator.ok_or_else(|| { | |||
| eyre!( | |||
| "source operator `{source}/{operator_id}` used \ | |||
| for input `{input_id_str}` does not exist", | |||
| ) | |||
| })? | |||
| } | |||
| CoreNodeKind::Custom(_) => { | |||
| bail!( | |||
| "input `{input_id_str}` references operator \ | |||
| `{source}/{operator_id}`, but `{source}` is a \ | |||
| custom node", | |||
| ); | |||
| } | |||
| }; | |||
| if !operator.config.outputs.contains(output) { | |||
| bail!( | |||
| "output `{source}/{operator_id}/{output}` mapped to \ | |||
| input `{input_id_str}` does not exist", | |||
| ); | |||
| } | |||
| } else { | |||
| match &source_node.kind { | |||
| CoreNodeKind::Runtime(_) => bail!( | |||
| "input `{input_id_str}` references output \ | |||
| `{source}/{output}`, but `{source}` is a \ | |||
| runtime node", | |||
| ), | |||
| CoreNodeKind::Custom(custom_node) => { | |||
| if !custom_node.run_config.outputs.contains(output) { | |||
| bail!( | |||
| "output `{source}/{output}` mapped to \ | |||
| input `{input_id_str}` does not exist", | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| }) | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="utf-8"> | |||
| </head> | |||
| <body> | |||
| <div class="mermaid"> | |||
| ____insert____ | |||
| </div> | |||
| <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script> | |||
| <script>mermaid.initialize({ startOnLoad: true, securityLevel: 'loose' }); | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,28 @@ | |||
| use std::{fs, path::Path}; | |||
| use dora_core::descriptor::Descriptor; | |||
| use eyre::Context; | |||
| const MERMAID_TEMPLATE: &str = include_str!("mermaid-template.html"); | |||
| pub fn visualize_as_html(dataflow: &Path) -> eyre::Result<String> { | |||
| let mermaid = visualize_as_mermaid(dataflow)?; | |||
| Ok(MERMAID_TEMPLATE.replacen("____insert____", &mermaid, 1)) | |||
| } | |||
| pub fn visualize_as_mermaid(dataflow: &Path) -> eyre::Result<String> { | |||
| let descriptor = read_descriptor(&dataflow) | |||
| .with_context(|| format!("failed to read dataflow at `{}`", dataflow.display()))?; | |||
| let visualized = descriptor | |||
| .visualize_as_mermaid() | |||
| .context("failed to visualize descriptor")?; | |||
| Ok(visualized) | |||
| } | |||
| pub fn read_descriptor(file: &Path) -> eyre::Result<Descriptor> { | |||
| let descriptor_file = fs::read(file).context("failed to open given file")?; | |||
| let descriptor: Descriptor = | |||
| serde_yaml::from_slice(&descriptor_file).context("failed to parse given descriptor")?; | |||
| Ok(descriptor) | |||
| } | |||
| @@ -0,0 +1,127 @@ | |||
| use clap::Parser; | |||
| use eyre::Context; | |||
| use std::{io::Write, path::PathBuf}; | |||
| use tempfile::NamedTempFile; | |||
| mod build; | |||
| mod check; | |||
| mod graph; | |||
| mod template; | |||
| #[derive(Debug, clap::Parser)] | |||
| #[clap(version)] | |||
| struct Args { | |||
| #[clap(subcommand)] | |||
| command: Command, | |||
| } | |||
| #[derive(Debug, clap::Subcommand)] | |||
| enum Command { | |||
| Check { | |||
| dataflow: PathBuf, | |||
| runtime_path: PathBuf, | |||
| }, | |||
| Graph { | |||
| dataflow: PathBuf, | |||
| #[clap(long, action)] | |||
| mermaid: bool, | |||
| #[clap(long, action)] | |||
| open: bool, | |||
| }, | |||
| Build { | |||
| dataflow: PathBuf, | |||
| }, | |||
| New { | |||
| #[clap(flatten)] | |||
| args: CommandNew, | |||
| }, | |||
| Dashboard, | |||
| Start, | |||
| Stop, | |||
| Logs, | |||
| Metrics, | |||
| Stats, | |||
| List, | |||
| Get, | |||
| Upgrade, | |||
| } | |||
| #[derive(Debug, clap::Args)] | |||
| pub struct CommandNew { | |||
| #[clap(long, value_enum, default_value_t = Kind::Operator)] | |||
| kind: Kind, | |||
| #[clap(long, value_enum, default_value_t = Lang::Rust)] | |||
| lang: Lang, | |||
| name: String, | |||
| path: Option<PathBuf>, | |||
| } | |||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)] | |||
| enum Kind { | |||
| Operator, | |||
| CustomNode, | |||
| } | |||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)] | |||
| enum Lang { | |||
| Rust, | |||
| Python, | |||
| C, | |||
| Cxx, | |||
| } | |||
| fn main() -> eyre::Result<()> { | |||
| let args = Args::parse(); | |||
| match args.command { | |||
| Command::Check { | |||
| dataflow, | |||
| runtime_path, | |||
| } => check::check(&dataflow, &runtime_path)?, | |||
| Command::Graph { | |||
| dataflow, | |||
| mermaid, | |||
| open, | |||
| } => { | |||
| if mermaid { | |||
| let visualized = graph::visualize_as_mermaid(&dataflow)?; | |||
| println!("{visualized}"); | |||
| println!( | |||
| "Paste the above output on https://mermaid.live/ or in a \ | |||
| ```mermaid code block on GitHub to display it." | |||
| ); | |||
| } else { | |||
| let html = graph::visualize_as_html(&dataflow)?; | |||
| let mut file = NamedTempFile::new().context("failed to create temp file")?; | |||
| file.as_file_mut().write_all(html.as_bytes())?; | |||
| let path = file.path().to_owned(); | |||
| file.keep()?; | |||
| println!( | |||
| "View graph by opening the following in your browser:\n file://{}", | |||
| path.display() | |||
| ); | |||
| if open { | |||
| webbrowser::open(path.as_os_str().to_str().unwrap())?; | |||
| } | |||
| } | |||
| } | |||
| Command::Build { dataflow } => { | |||
| build::build(&dataflow)?; | |||
| } | |||
| Command::New { args } => template::create(args)?, | |||
| Command::Dashboard => todo!(), | |||
| Command::Start => todo!(), | |||
| Command::Stop => todo!(), | |||
| Command::Logs => todo!(), | |||
| Command::Metrics => todo!(), | |||
| Command::Stats => todo!(), | |||
| Command::List => todo!(), | |||
| Command::Get => todo!(), | |||
| Command::Upgrade => todo!(), | |||
| } | |||
| Ok(()) | |||
| } | |||
| @@ -0,0 +1,85 @@ | |||
| use eyre::{bail, Context}; | |||
| use std::{ | |||
| fs, | |||
| path::{Path, PathBuf}, | |||
| }; | |||
| pub fn create(args: crate::CommandNew) -> eyre::Result<()> { | |||
| let crate::CommandNew { | |||
| kind, | |||
| lang: _, | |||
| name, | |||
| path, | |||
| } = args; | |||
| match kind { | |||
| crate::Kind::Operator => create_operator(name, path), | |||
| crate::Kind::CustomNode => create_custom_node(name, path), | |||
| } | |||
| } | |||
| fn create_operator(name: String, path: Option<PathBuf>) -> Result<(), eyre::ErrReport> { | |||
| const OPERATOR: &str = include_str!("operator/operator-template.c"); | |||
| const HEADER: &str = include_str!("../../../../../apis/c/operator/operator_api.h"); | |||
| if name.contains('/') { | |||
| bail!("operator name must not contain `/` separators"); | |||
| } | |||
| if !name.is_ascii() { | |||
| bail!("operator name must be ASCII"); | |||
| } | |||
| // create directories | |||
| let root = path.as_deref().unwrap_or(Path::new(&name)); | |||
| fs::create_dir(&root) | |||
| .with_context(|| format!("failed to create directory `{}`", root.display()))?; | |||
| let operator_path = root.join("operator.c"); | |||
| fs::write(&operator_path, OPERATOR) | |||
| .with_context(|| format!("failed to write `{}`", operator_path.display()))?; | |||
| let header_path = root.join("operator_api.h"); | |||
| fs::write(&header_path, HEADER) | |||
| .with_context(|| format!("failed to write `{}`", header_path.display()))?; | |||
| // TODO: Makefile? | |||
| println!( | |||
| "Created new C operator `{name}` at {}", | |||
| Path::new(".").join(root).display() | |||
| ); | |||
| Ok(()) | |||
| } | |||
| fn create_custom_node(name: String, path: Option<PathBuf>) -> Result<(), eyre::ErrReport> { | |||
| const NODE: &str = include_str!("node/node-template.c"); | |||
| const HEADER: &str = include_str!("../../../../../apis/c/node/node_api.h"); | |||
| if name.contains('/') { | |||
| bail!("node name must not contain `/` separators"); | |||
| } | |||
| if !name.is_ascii() { | |||
| bail!("node name must be ASCII"); | |||
| } | |||
| // create directories | |||
| let root = path.as_deref().unwrap_or(Path::new(&name)); | |||
| fs::create_dir(&root) | |||
| .with_context(|| format!("failed to create directory `{}`", root.display()))?; | |||
| let node_path = root.join("node.c"); | |||
| fs::write(&node_path, NODE) | |||
| .with_context(|| format!("failed to write `{}`", node_path.display()))?; | |||
| let header_path = root.join("node_api.h"); | |||
| fs::write(&header_path, HEADER) | |||
| .with_context(|| format!("failed to write `{}`", header_path.display()))?; | |||
| // TODO: Makefile? | |||
| println!( | |||
| "Created new C custom node `{name}` at {}", | |||
| Path::new(".").join(root).display() | |||
| ); | |||
| Ok(()) | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <assert.h> | |||
| #include "node_api.h" | |||
| // sleep | |||
| #ifdef _WIN32 | |||
| #include <Windows.h> | |||
| #else | |||
| #include <unistd.h> | |||
| #endif | |||
| int main() | |||
| { | |||
| void *dora_context = init_dora_context_from_env(); | |||
| if (dora_context == NULL) | |||
| { | |||
| fprintf(stderr, "failed to init dora context\n"); | |||
| return -1; | |||
| } | |||
| while (1) | |||
| { | |||
| void *input = dora_next_input(dora_context); | |||
| if (input == NULL) | |||
| { | |||
| // end of input | |||
| break; | |||
| } | |||
| char *id; | |||
| size_t id_len; | |||
| read_dora_input_id(input, &id, &id_len); | |||
| char *data; | |||
| size_t data_len; | |||
| read_dora_input_data(input, &data, &data_len); | |||
| char out_id[] = "foo"; | |||
| char out_data[] = "bar"; | |||
| dora_send_output(dora_context, out_id, strlen(out_id), out_data, strlen(out_data)); | |||
| free_dora_input(input); // do not use `id` or `data` pointer after freeing | |||
| } | |||
| free_dora_context(dora_context); | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,60 @@ | |||
| #include "operator_api.h" | |||
| #include <assert.h> | |||
| #include <string.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| DoraInitResult_t dora_init_operator(void) | |||
| { | |||
| // allocate memory for storing context across function calls (optional) | |||
| void *context = malloc(10); | |||
| // TODO initialize context memory | |||
| DoraInitResult_t result = {.operator_context = context}; | |||
| return result; | |||
| } | |||
| DoraResult_t dora_drop_operator(void *operator_context) | |||
| { | |||
| free(operator_context); | |||
| DoraResult_t result = {}; | |||
| return result; | |||
| } | |||
| OnInputResult_t dora_on_input( | |||
| const Input_t *input, | |||
| const SendOutput_t *send_output, | |||
| void *operator_context) | |||
| { | |||
| char id[input->id.len + 1]; | |||
| memcpy(id, input->id.ptr, input->id.len); | |||
| id[input->id.len] = 0; | |||
| // example for matching on input name | |||
| if (strcmp(id, "foo") == 0) | |||
| { | |||
| char *out_id = "bar"; | |||
| char *out_id_heap = strdup(out_id); | |||
| int data_alloc_size = 10; | |||
| void *out_data = malloc(data_alloc_size); | |||
| // TODO intialize out_data | |||
| Output_t output = {.id = { | |||
| .ptr = (uint8_t *)out_id_heap, | |||
| .len = strlen(out_id_heap), | |||
| .cap = strlen(out_id_heap) + 1, | |||
| }, | |||
| .data = {.ptr = (uint8_t *)out_data, .len = strlen(out_data), .cap = data_alloc_size}}; | |||
| DoraResult_t res = (send_output->send_output.call)(send_output->send_output.env_ptr, output); | |||
| OnInputResult_t result = {.result = res, .status = DORA_STATUS_CONTINUE}; | |||
| return result; | |||
| } | |||
| else | |||
| { | |||
| OnInputResult_t result = {.status = DORA_STATUS_CONTINUE}; | |||
| return result; | |||
| } | |||
| } | |||
| @@ -0,0 +1 @@ | |||
| @@ -0,0 +1,13 @@ | |||
| mod c; | |||
| mod cxx; | |||
| mod python; | |||
| mod rust; | |||
| pub fn create(args: crate::CommandNew) -> eyre::Result<()> { | |||
| match args.lang { | |||
| crate::Lang::Rust => rust::create(args), | |||
| crate::Lang::Python => python::create(args), | |||
| crate::Lang::C => c::create(args), | |||
| crate::Lang::Cxx => todo!(), | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| use std::path::PathBuf; | |||
| pub fn create(args: crate::CommandNew) -> eyre::Result<()> { | |||
| let crate::CommandNew { | |||
| kind, | |||
| lang: _, | |||
| name, | |||
| path, | |||
| } = args; | |||
| match kind { | |||
| crate::Kind::Operator => create_operator(name, path), | |||
| crate::Kind::CustomNode => create_custom_node(name, path), | |||
| } | |||
| } | |||
| fn create_operator(name: String, path: Option<PathBuf>) -> Result<(), eyre::ErrReport> { | |||
| todo!() | |||
| } | |||
| fn create_custom_node(name: String, path: Option<PathBuf>) -> Result<(), eyre::ErrReport> { | |||
| todo!() | |||
| } | |||
| @@ -0,0 +1,91 @@ | |||
| use eyre::{bail, Context}; | |||
| use std::{ | |||
| fs, | |||
| path::{Path, PathBuf}, | |||
| }; | |||
| pub fn create(args: crate::CommandNew) -> eyre::Result<()> { | |||
| let crate::CommandNew { | |||
| kind, | |||
| lang: _, | |||
| name, | |||
| path, | |||
| } = args; | |||
| match kind { | |||
| crate::Kind::Operator => create_operator(name, path), | |||
| crate::Kind::CustomNode => create_custom_node(name, path), | |||
| } | |||
| } | |||
| fn create_operator(name: String, path: Option<PathBuf>) -> Result<(), eyre::ErrReport> { | |||
| const CARGO_TOML: &str = include_str!("operator/Cargo-template.toml"); | |||
| const LIB_RS: &str = include_str!("operator/lib-template.rs"); | |||
| if name.contains('/') { | |||
| bail!("operator name must not contain `/` separators"); | |||
| } | |||
| if !name.is_ascii() { | |||
| bail!("operator name must be ASCII"); | |||
| } | |||
| // create directories | |||
| let root = path.as_deref().unwrap_or(Path::new(&name)); | |||
| fs::create_dir(&root) | |||
| .with_context(|| format!("failed to create directory `{}`", root.display()))?; | |||
| let src = root.join("src"); | |||
| fs::create_dir(&src) | |||
| .with_context(|| format!("failed to create directory `{}`", src.display()))?; | |||
| let cargo_toml = CARGO_TOML.replace("___name___", &name); | |||
| let cargo_toml_path = root.join("Cargo.toml"); | |||
| fs::write(&cargo_toml_path, &cargo_toml) | |||
| .with_context(|| format!("failed to write `{}`", cargo_toml_path.display()))?; | |||
| let lib_rs_path = src.join("lib.rs"); | |||
| fs::write(&lib_rs_path, LIB_RS) | |||
| .with_context(|| format!("failed to write `{}`", lib_rs_path.display()))?; | |||
| println!( | |||
| "Created new Rust operator `{name}` at {}", | |||
| Path::new(".").join(root).display() | |||
| ); | |||
| Ok(()) | |||
| } | |||
| fn create_custom_node(name: String, path: Option<PathBuf>) -> Result<(), eyre::ErrReport> { | |||
| const CARGO_TOML: &str = include_str!("node/Cargo-template.toml"); | |||
| const MAIN_RS: &str = include_str!("node/main-template.rs"); | |||
| if name.contains('/') { | |||
| bail!("node name must not contain `/` separators"); | |||
| } | |||
| if !name.is_ascii() { | |||
| bail!("node name must be ASCII"); | |||
| } | |||
| // create directories | |||
| let root = path.as_deref().unwrap_or(Path::new(&name)); | |||
| fs::create_dir(&root) | |||
| .with_context(|| format!("failed to create directory `{}`", root.display()))?; | |||
| let src = root.join("src"); | |||
| fs::create_dir(&src) | |||
| .with_context(|| format!("failed to create directory `{}`", src.display()))?; | |||
| let cargo_toml = CARGO_TOML.replace("___name___", &name); | |||
| let cargo_toml_path = root.join("Cargo.toml"); | |||
| fs::write(&cargo_toml_path, &cargo_toml) | |||
| .with_context(|| format!("failed to write `{}`", cargo_toml_path.display()))?; | |||
| let main_rs_path = src.join("main.rs"); | |||
| fs::write(&main_rs_path, MAIN_RS) | |||
| .with_context(|| format!("failed to write `{}`", main_rs_path.display()))?; | |||
| println!( | |||
| "Created new Rust custom node `{name}` at {}", | |||
| Path::new(".").join(root).display() | |||
| ); | |||
| Ok(()) | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| [package] | |||
| name = "___name___" | |||
| version = "0.1.0" | |||
| edition = "2021" | |||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
| [dependencies] | |||
| dora-node-api = "0.1.0" | |||
| @@ -0,0 +1,15 @@ | |||
| use dora_node_api::{self, config::DataId, DoraNode}; | |||
| use std::error::Error; | |||
| fn main() -> Result<(), Box<dyn Error>> { | |||
| let mut node = DoraNode::init_from_env()?; | |||
| let inputs = node.inputs()?; | |||
| while let Ok(input) = inputs.recv() { | |||
| match input.id.as_str() { | |||
| other => eprintln!("Ignoring unexpected input `{other}`"), | |||
| } | |||
| } | |||
| Ok(()) | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| [package] | |||
| name = "___name___" | |||
| version = "0.1.0" | |||
| edition = "2021" | |||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
| [lib] | |||
| crate-type = ["cdylib"] | |||
| [dependencies] | |||
| dora-operator-api = "0.1.0" | |||
| @@ -0,0 +1,23 @@ | |||
| use dora_operator_api::{register_operator, DoraOperator, DoraOutputSender, DoraStatus}; | |||
| use std::time::{Duration, Instant}; | |||
| register_operator!(ExampleOperator); | |||
| #[derive(Debug, Default)] | |||
| struct ExampleOperator { | |||
| example_field: u32, | |||
| } | |||
| impl DoraOperator for ExampleOperator { | |||
| fn on_input( | |||
| &mut self, | |||
| id: &str, | |||
| data: &[u8], | |||
| output_sender: &mut DoraOutputSender, | |||
| ) -> Result<DoraStatus, String> { | |||
| match id { | |||
| other => eprintln!("ignoring unexpected input {other}"), | |||
| } | |||
| Ok(DoraStatus::Continue) | |||
| } | |||
| } | |||
| @@ -14,8 +14,6 @@ use tokio_stream::wrappers::IntervalStream; | |||
| #[derive(Debug, Clone, clap::Parser)] | |||
| #[clap(about = "Dora coordinator")] | |||
| pub enum Command { | |||
| #[clap(about = "Print Graph")] | |||
| Visualize { dataflow: PathBuf }, | |||
| #[clap(about = "Run dataflow pipeline")] | |||
| Run { | |||
| dataflow: PathBuf, | |||
| @@ -25,17 +23,6 @@ pub enum Command { | |||
| pub async fn run(command: Command) -> eyre::Result<()> { | |||
| match command { | |||
| Command::Visualize { dataflow: file } => { | |||
| let descriptor = read_descriptor(&file).await?; | |||
| let visualized = descriptor | |||
| .visualize_as_mermaid() | |||
| .context("failed to visualize descriptor")?; | |||
| println!("{visualized}"); | |||
| println!( | |||
| "Paste the above output on https://mermaid.live/ or in a \ | |||
| ```mermaid code block on GitHub to display it." | |||
| ); | |||
| } | |||
| Command::Run { dataflow, runtime } => { | |||
| let runtime_path = runtime.unwrap_or_else(|| { | |||
| std::env::args() | |||
| @@ -1,4 +1,5 @@ | |||
| use super::OperatorEvent; | |||
| use dora_core::adjust_shared_library_path; | |||
| use dora_node_api::{communication::Publisher, config::DataId}; | |||
| use dora_operator_api_types::{ | |||
| safer_ffi::closure::ArcDynFn1, DoraDropOperator, DoraInitOperator, DoraInitResult, DoraOnInput, | |||
| @@ -24,18 +25,7 @@ pub fn spawn( | |||
| inputs: Receiver<dora_node_api::Input>, | |||
| publishers: HashMap<DataId, Box<dyn Publisher>>, | |||
| ) -> eyre::Result<()> { | |||
| let file_name = path | |||
| .file_name() | |||
| .ok_or_else(|| eyre!("shared library path has no file name"))? | |||
| .to_str() | |||
| .ok_or_else(|| eyre!("shared library file name is not valid UTF8"))?; | |||
| if file_name.starts_with("lib") { | |||
| bail!("Shared library file name must not start with `lib`, prefix is added automatically"); | |||
| } | |||
| if path.extension().is_some() { | |||
| bail!("Shared library file name must have no extension, it is added automatically"); | |||
| } | |||
| let path = path.with_file_name(libloading::library_filename(file_name)); | |||
| let path = adjust_shared_library_path(path)?; | |||
| let library = unsafe { | |||
| libloading::Library::new(&path) | |||
| @@ -5,6 +5,7 @@ communication: | |||
| nodes: | |||
| - id: rust-node | |||
| custom: | |||
| build: cargo build -p rust-dataflow-example-node | |||
| run: ../../target/debug/rust-dataflow-example-node | |||
| inputs: | |||
| tick: dora/timer/millis/300 | |||
| @@ -13,6 +14,7 @@ nodes: | |||
| - id: runtime-node | |||
| operators: | |||
| - id: rust-operator | |||
| build: cargo build -p rust-dataflow-example-operator | |||
| shared-library: ../../target/debug/rust_dataflow_example_operator | |||
| inputs: | |||
| tick: dora/timer/millis/100 | |||
| @@ -21,6 +23,7 @@ nodes: | |||
| - status | |||
| - id: rust-sink | |||
| custom: | |||
| build: cargo build -p rust-dataflow-example-sink | |||
| run: ../../target/debug/rust-dataflow-example-sink | |||
| inputs: | |||
| message: runtime-node/rust-operator/status | |||
| @@ -7,13 +7,12 @@ async fn main() -> eyre::Result<()> { | |||
| std::env::set_current_dir(root.join(file!()).parent().unwrap()) | |||
| .wrap_err("failed to set working dir")?; | |||
| build_package("rust-dataflow-example-node").await?; | |||
| build_package("rust-dataflow-example-operator").await?; | |||
| build_package("rust-dataflow-example-sink").await?; | |||
| let dataflow = Path::new("dataflow.yml"); | |||
| build_dataflow(dataflow).await?; | |||
| build_package("dora-runtime").await?; | |||
| dora_coordinator::run(dora_coordinator::Command::Run { | |||
| dataflow: Path::new("dataflow.yml").to_owned(), | |||
| dataflow: dataflow.to_owned(), | |||
| runtime: Some(root.join("target").join("debug").join("dora-runtime")), | |||
| }) | |||
| .await?; | |||
| @@ -21,6 +20,18 @@ async fn main() -> eyre::Result<()> { | |||
| Ok(()) | |||
| } | |||
| async fn build_dataflow(dataflow: &Path) -> eyre::Result<()> { | |||
| let cargo = std::env::var("CARGO").unwrap(); | |||
| let mut cmd = tokio::process::Command::new(&cargo); | |||
| cmd.arg("run"); | |||
| cmd.arg("--package").arg("dora-cli"); | |||
| cmd.arg("--").arg("build").arg(dataflow); | |||
| if !cmd.status().await?.success() { | |||
| bail!("failed to build dataflow"); | |||
| }; | |||
| Ok(()) | |||
| } | |||
| async fn build_package(package: &str) -> eyre::Result<()> { | |||
| let cargo = std::env::var("CARGO").unwrap(); | |||
| let mut cmd = tokio::process::Command::new(&cargo); | |||
| @@ -10,3 +10,4 @@ license = "Apache-2.0" | |||
| dora-node-api = { version = "0.1.0", path = "../../apis/rust/node" } | |||
| eyre = "0.6.8" | |||
| serde = { version = "1.0.136", features = ["derive"] } | |||
| serde_yaml = "0.9.11" | |||
| @@ -1,6 +1,5 @@ | |||
| use dora_node_api::config::{ | |||
| CommunicationConfig, DataId, InputMapping, NodeId, NodeRunConfig, OperatorId, | |||
| }; | |||
| use dora_node_api::config::{CommunicationConfig, NodeRunConfig}; | |||
| pub use dora_node_api::config::{DataId, InputMapping, NodeId, OperatorId, UserInputMapping}; | |||
| use serde::{Deserialize, Serialize}; | |||
| use std::collections::HashMap; | |||
| use std::fmt; | |||
| @@ -15,13 +14,16 @@ mod visualize; | |||
| #[derive(Debug, Serialize, Deserialize)] | |||
| #[serde(deny_unknown_fields)] | |||
| pub struct Descriptor { | |||
| // see https://github.com/dtolnay/serde-yaml/issues/298 | |||
| #[serde(with = "serde_yaml::with::singleton_map")] | |||
| pub communication: CommunicationConfig, | |||
| pub nodes: Vec<Node>, | |||
| } | |||
| pub const SINGLE_OPERATOR_DEFAULT_ID: &str = "op"; | |||
| impl Descriptor { | |||
| pub fn resolve_aliases(&self) -> Vec<ResolvedNode> { | |||
| let default_op_id = OperatorId::from("op".to_string()); | |||
| let default_op_id = OperatorId::from(SINGLE_OPERATOR_DEFAULT_ID.to_string()); | |||
| let single_operator_nodes: HashMap<_, _> = self | |||
| .nodes | |||
| @@ -156,6 +158,9 @@ pub struct OperatorConfig { | |||
| #[serde(flatten)] | |||
| pub source: OperatorSource, | |||
| #[serde(default, skip_serializing_if = "Option::is_none")] | |||
| pub build: Option<String>, | |||
| } | |||
| #[derive(Debug, Serialize, Deserialize, Clone)] | |||
| @@ -192,6 +197,8 @@ pub struct CustomNode { | |||
| pub run: String, | |||
| pub env: Option<BTreeMap<String, EnvValue>>, | |||
| pub working_directory: Option<BTreeMap<String, EnvValue>>, | |||
| #[serde(default, skip_serializing_if = "Option::is_none")] | |||
| pub build: Option<String>, | |||
| #[serde(flatten)] | |||
| pub run_config: NodeRunConfig, | |||
| @@ -1 +1,27 @@ | |||
| use std::{ | |||
| env::consts::{DLL_PREFIX, DLL_SUFFIX}, | |||
| path::Path, | |||
| }; | |||
| use eyre::{bail, eyre}; | |||
| pub mod descriptor; | |||
| pub fn adjust_shared_library_path(path: &Path) -> Result<std::path::PathBuf, eyre::ErrReport> { | |||
| let file_name = path | |||
| .file_name() | |||
| .ok_or_else(|| eyre!("shared library path has no file name"))? | |||
| .to_str() | |||
| .ok_or_else(|| eyre!("shared library file name is not valid UTF8"))?; | |||
| if file_name.starts_with("lib") { | |||
| bail!("Shared library file name must not start with `lib`, prefix is added automatically"); | |||
| } | |||
| if path.extension().is_some() { | |||
| bail!("Shared library file name must have no extension, it is added automatically"); | |||
| } | |||
| let library_filename = format!("{DLL_PREFIX}{file_name}{DLL_SUFFIX}"); | |||
| let path = path.with_file_name(library_filename); | |||
| Ok(path) | |||
| } | |||