diff --git a/appmgr/Cargo.lock b/appmgr/Cargo.lock index 1497ecd4b..f459c024a 100644 --- a/appmgr/Cargo.lock +++ b/appmgr/Cargo.lock @@ -46,9 +46,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.44" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" +checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf" [[package]] name = "arrayref" @@ -464,7 +464,7 @@ dependencies = [ "cookie 0.15.1", "idna", "log", - "publicsuffix 2.1.1", + "publicsuffix 2.1.0", "serde", "serde_json", "time 0.2.27", @@ -1594,6 +1594,12 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "matches" version = "0.1.9" @@ -2146,9 +2152,9 @@ dependencies = [ [[package]] name = "publicsuffix" -version = "2.1.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "292972edad6bbecc137ab84c5e36421a4a6c979ea31d3cc73540dd04315b33e1" +checksum = "c3ac055aef7cc7a1caefbc65144be879e862467dcd9b8a8d57b64a13e7dce15d" dependencies = [ "byteorder", "hashbrown", @@ -2723,9 +2729,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.21" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af" +checksum = "ad104641f3c958dab30eb3010e834c2622d1f3f4c530fef1dee20ad9485f3c09" dependencies = [ "dtoa", "indexmap", @@ -2754,9 +2760,9 @@ checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" [[package]] name = "sha2" -version = "0.9.8" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +checksum = "91e36fa7752016a6c4483706d634fd82c48860dd2df17a0cfaaebc714f23b2dd" dependencies = [ "block-buffer", "cfg-if 1.0.0", @@ -2845,12 +2851,14 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "sqlformat" -version = "0.1.8" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4" +checksum = "684001e7985ec1a9a66963b77ed151ef22a7876b3fdd7e37a57ec774f54b7d96" dependencies = [ - "itertools 0.10.1", + "lazy_static", + "maplit", "nom 7.0.0", + "regex", "unicode_categories", ] @@ -3266,9 +3274,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.4.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5241dd6f21443a3606b432718b166d3cedc962fd4b8bea54a8bc7f514ebda986" +checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338" dependencies = [ "tinyvec_macros", ] @@ -3433,8 +3441,9 @@ dependencies = [ [[package]] name = "torut" -version = "0.1.10" -source = "git+https://github.com/Start9Labs/torut.git?branch=update/dependencies#d5156c6740ce8d720e0c17a6dfe5c756c2867abb" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecb506186a6ad032c4b50bd92c35307a32f95146e7d07ee28b93cc1410dfc384" dependencies = [ "base32", "base64", @@ -3442,11 +3451,9 @@ dependencies = [ "ed25519-dalek", "hex", "hmac", - "openssl", "rand 0.7.3", "serde", "serde_derive", - "sha1", "sha2", "sha3", "tokio 1.11.0", @@ -3471,9 +3478,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.20" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46125608c26121c81b0c6d693eab5a420e416da7e43c426d2e8f7df8da8a3acf" +checksum = "2ca517f43f0fb96e0c3072ed5c275fe5eece87e8cb52f4a77b69226d3b1c9df8" dependencies = [ "lazy_static", ] diff --git a/appmgr/Cargo.toml b/appmgr/Cargo.toml index 18e1b7c03..1584f8805 100644 --- a/appmgr/Cargo.toml +++ b/appmgr/Cargo.toml @@ -113,6 +113,6 @@ tokio-stream = { version = "0.1.5", features = ["io-util", "sync"] } tokio-tar = { git = "https://github.com/dr-bonez/tokio-tar.git" } tokio-tungstenite = "0.14.0" tokio-util = { version = "0.6.6", features = ["io"] } -torut = { git = "https://github.com/Start9Labs/torut.git", branch = "update/dependencies" } +torut = "0.2.0" typed-builder = "0.9.0" url = { version = "2.2.1", features = ["serde"] } diff --git a/compat/.gitignore b/compat/.gitignore index 7fb7fc14c..e3123ca00 100644 --- a/compat/.gitignore +++ b/compat/.gitignore @@ -2,3 +2,4 @@ **/*.rs.bk .DS_Store .vscode +compat.tar \ No newline at end of file diff --git a/compat/Cargo.lock b/compat/Cargo.lock index 975325c22..9ec7b8b4a 100644 --- a/compat/Cargo.lock +++ b/compat/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aes" version = "0.7.5" @@ -12,7 +27,7 @@ dependencies = [ "cipher", "cpufeatures", "ctr", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -49,6 +64,9 @@ name = "anyhow" version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf" +dependencies = [ + "backtrace", +] [[package]] name = "arrayref" @@ -108,6 +126,21 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "backtrace" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base-x" version = "0.2.8" @@ -143,6 +176,16 @@ dependencies = [ "regex", ] +[[package]] +name = "beau_collector" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33a143066d3cbd3d32c15b51c39449e50e32a68293d31589fb941d6a9df0df4f" +dependencies = [ + "anyhow", + "itertools 0.9.0", +] + [[package]] name = "bit-set" version = "0.5.2" @@ -187,14 +230,35 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding 0.1.5", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding", - "generic-array", + "block-padding 0.2.1", + "generic-array 0.14.4", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", ] [[package]] @@ -262,6 +326,12 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.4.3" @@ -322,7 +392,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array", + "generic-array 0.14.4", ] [[package]] @@ -344,9 +414,25 @@ dependencies = [ name = "compat" version = "0.1.0" dependencies = [ + "anyhow", + "beau_collector", "clap", "embassy-os", + "emver", + "failure", + "indexmap", + "itertools 0.10.1", + "lazy_static", + "linear-map", + "log", + "nix 0.22.1", + "pest", + "pest_derive", + "rand 0.7.3", + "regex", + "rust-argon2", "serde", + "serde_json", "serde_yaml", ] @@ -415,7 +501,7 @@ dependencies = [ "cookie 0.15.1", "idna", "log", - "publicsuffix 2.1.0", + "publicsuffix 2.1.1", "serde", "serde_json", "time 0.2.27", @@ -542,7 +628,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array", + "generic-array 0.14.4", "subtle", ] @@ -584,7 +670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", - "digest", + "digest 0.9.0", "rand_core 0.5.1", "subtle", "zeroize", @@ -644,13 +730,22 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.4", ] [[package]] @@ -757,7 +852,7 @@ dependencies = [ "ciborium", "clap", "cookie_store 0.15.0", - "digest", + "digest 0.9.0", "divrem", "ed25519-dalek", "emver", @@ -798,6 +893,7 @@ dependencies = [ "sha2", "simple-logging", "sqlx", + "stderrlog", "tar", "thiserror", "tokio 1.11.0", @@ -861,6 +957,34 @@ dependencies = [ "termcolor", ] +[[package]] +name = "failure" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +dependencies = [ + "proc-macro2 1.0.29", + "quote 1.0.9", + "syn 1.0.76", + "synstructure", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fd-lock-rs" version = "0.1.3" @@ -1039,6 +1163,15 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.4" @@ -1071,6 +1204,12 @@ dependencies = [ "wasi 0.10.0+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" + [[package]] name = "git-version" version = "0.3.5" @@ -1167,7 +1306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ "crypto-mac", - "digest", + "digest 0.9.0", ] [[package]] @@ -1258,7 +1397,7 @@ dependencies = [ "futures", "hyper", "log", - "sha-1", + "sha-1 0.9.8", "tokio 1.11.0", "tokio-tungstenite", ] @@ -1347,6 +1486,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.10.1" @@ -1475,6 +1623,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linear-map" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" +dependencies = [ + "serde", + "serde_test", +] + [[package]] name = "linked-hash-map" version = "0.5.4" @@ -1517,9 +1675,9 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" dependencies = [ - "block-buffer", - "digest", - "opaque-debug", + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug 0.3.0", ] [[package]] @@ -1549,6 +1707,16 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c835948974f68e0bd58636fc6c5b1fbff7b297e3046f11b3b3c18bbac012c6d" +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + [[package]] name = "mio" version = "0.7.13" @@ -1695,12 +1863,27 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -1867,6 +2050,40 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2 1.0.29", + "quote 1.0.9", + "syn 1.0.76", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1 0.8.2", +] + [[package]] name = "petgraph" version = "0.5.1" @@ -2041,9 +2258,9 @@ dependencies = [ [[package]] name = "publicsuffix" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac055aef7cc7a1caefbc65144be879e862467dcd9b8a8d57b64a13e7dce15d" +checksum = "292972edad6bbecc137ab84c5e36421a4a6c979ea31d3cc73540dd04315b33e1" dependencies = [ "byteorder", "hashbrown", @@ -2379,6 +2596,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc_version" version = "0.2.3" @@ -2575,6 +2798,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_test" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82178225dbdeae2d5d190e8649287db6a3a32c6d24da22ae3146325aa353e4c" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.0" @@ -2612,9 +2844,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad104641f3c958dab30eb3010e834c2622d1f3f4c530fef1dee20ad9485f3c09" +checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af" dependencies = [ "dtoa", "indexmap", @@ -2622,17 +2854,29 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + [[package]] name = "sha-1" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest", - "opaque-debug", + "digest 0.9.0", + "opaque-debug 0.3.0", ] [[package]] @@ -2643,15 +2887,15 @@ checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" [[package]] name = "sha2" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e36fa7752016a6c4483706d634fd82c48860dd2df17a0cfaaebc714f23b2dd" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest", - "opaque-debug", + "digest 0.9.0", + "opaque-debug 0.3.0", ] [[package]] @@ -2660,10 +2904,10 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.9.0", + "digest 0.9.0", "keccak", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -2844,6 +3088,19 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stderrlog" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a53e2eff3e94a019afa6265e8ee04cb05b9d33fe9f5078b14e4e391d155a38" +dependencies = [ + "atty", + "chrono", + "log", + "termcolor", + "thread_local", +] + [[package]] name = "stdweb" version = "0.4.20" @@ -3069,6 +3326,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + [[package]] name = "time" version = "0.1.44" @@ -3129,9 +3395,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338" +checksum = "5241dd6f21443a3606b432718b166d3cedc962fd4b8bea54a8bc7f514ebda986" dependencies = [ "tinyvec_macros", ] @@ -3296,8 +3562,9 @@ dependencies = [ [[package]] name = "torut" -version = "0.1.10" -source = "git+https://github.com/Start9Labs/torut.git?branch=update/dependencies#d5156c6740ce8d720e0c17a6dfe5c756c2867abb" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecb506186a6ad032c4b50bd92c35307a32f95146e7d07ee28b93cc1410dfc384" dependencies = [ "base32", "base64", @@ -3305,11 +3572,9 @@ dependencies = [ "ed25519-dalek", "hex", "hmac", - "openssl", "rand 0.7.3", "serde", "serde_derive", - "sha1", "sha2", "sha3", "tokio 1.11.0", @@ -3370,7 +3635,7 @@ dependencies = [ "input_buffer", "log", "rand 0.8.4", - "sha-1", + "sha-1 0.9.8", "thiserror", "url", "utf-8", diff --git a/compat/Cargo.toml b/compat/Cargo.toml index 48ff60243..897d67368 100644 --- a/compat/Cargo.toml +++ b/compat/Cargo.toml @@ -1,13 +1,29 @@ [package] -name = "compat" -version = "0.1.0" authors = ["Aiden McClelland "] edition = "2018" +name = "compat" +version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = { version = "1.0.40", features = ["backtrace"] } +beau_collector = "0.2.1" clap = "2.33.3" -serde = { version="1.0.118", features=["derive", "rc"] } +embassy-os = { path = "../appmgr", default-features = false } +emver = { version = "0.1.2", features = ["serde"] } +failure = "0.1.8" +indexmap = { version = "1.6.2", features = ["serde"] } +itertools = "0.10.0" +lazy_static = "1.4" +linear-map = { version = "1.2", features = ["serde_impl"] } +log = "0.4.11" +nix = "0.22.0" +pest = "2.1" +pest_derive = "2.1" +rand = "0.7" +regex = "1.4.2" +rust-argon2 = "0.8.3" +serde = { version = "1.0.118", features = ["derive", "rc"] } +serde_json = "1.0.67" serde_yaml = "0.8.17" -embassy-os = { path="../appmgr", default-features=false } diff --git a/compat/Dockerfile b/compat/Dockerfile index b4fa8046b..c97077c45 100644 --- a/compat/Dockerfile +++ b/compat/Dockerfile @@ -1,5 +1,6 @@ FROM alpine:latest +RUN apk update && apk add duplicity && apk add curl ADD ./target/aarch64-unknown-linux-musl/release/compat /usr/local/bin/compat ENTRYPOINT ["compat"] diff --git a/compat/Makefile b/compat/Makefile new file mode 100644 index 000000000..0d3744755 --- /dev/null +++ b/compat/Makefile @@ -0,0 +1,11 @@ + COMPAT_SRC := $(shell find ./src) Cargo.toml Cargo.lock + +.DELETE_ON_ERROR: + +all: compat.tar + +compat.tar: Dockerfile build + DOCKER_CLI_EXPERIMENTAL=enabled docker buildx build --tag start9/x_system/compat --platform=linux/arm64 -o type=docker,dest=compat.tar . + +build: $(COMPAT_SRC) + ./build.sh diff --git a/compat/build.sh b/compat/build.sh new file mode 100755 index 000000000..3aa18e292 --- /dev/null +++ b/compat/build.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e +shopt -s expand_aliases + +if [ "$0" != "./build.sh" ]; then + >&2 echo "Must be run from compat directory" + exit 1 +fi + +alias 'rust-musl-builder'='docker run --rm -it -v "$HOME"/.cargo/registry:/root/.cargo/registry -v "$(pwd)":/home/rust/src start9/rust-musl-cross:aarch64-musl' + +cd ../.. +rust-musl-builder sh -c "(cd embassy-os/compat && cargo +beta build --release --target=aarch64-unknown-linux-musl --no-default-features)" +cd embassy-os/compat diff --git a/compat/src/backup.rs b/compat/src/backup.rs new file mode 100644 index 000000000..dea619e2f --- /dev/null +++ b/compat/src/backup.rs @@ -0,0 +1,74 @@ +use std::path::Path; + +pub fn create_backup>( + mountpoint: P, + data_path: P, + app_id: &str, +) -> Result<(), anyhow::Error> { + let path = std::fs::canonicalize(mountpoint)?; + let volume_path = Path::new(embassy::VOLUMES).join(app_id); + + let exclude = if volume_path.is_dir() { + let ignore_path = volume_path.join(".backupignore"); + if ignore_path.is_file() { + std::fs::read(ignore_path)? + } else { + Vec::new() + } + } else { + return Err(anyhow::anyhow!("Volume For {} Does Not Exist", app_id)) + }; + + let mut data_cmd = std::process::Command::new("duplicity"); + for exclude in exclude { + if exclude.to_string().starts_with('!') { + data_cmd.arg(format!( + "--include={}", + volume_path.join(exclude.to_string().trim_start_matches('!')).display() + )); + } else { + data_cmd.arg(format!("--exclude={}", volume_path.join(exclude.to_string()).display())); + } + } + let data_res = data_cmd + .arg(volume_path) + .arg(format!("file://{}", data_path.as_ref().display().to_string())) + .output(); + data_res?; + + Ok(()) +} + +pub fn restore_backup>( + path: P, + data_path: P, + app_id: &str, +) -> Result<(), anyhow::Error> { + let path = std::fs::canonicalize(path)?; + if !path.is_dir() { + anyhow::anyhow!("Backup Path Must Be Directory"); + } + let metadata_path = path.join("metadata.yaml"); + let volume_path = Path::new(embassy::VOLUMES).join(app_id); + + let mut data_cmd = std::process::Command::new("duplicity"); + data_cmd + .arg("--force") + .arg(format!("file://{:#?}", data_path.as_ref().display().to_string())) + .arg(&volume_path); + + let data_output = data_cmd.status()?; + if !data_output.success() { + return Err(anyhow::anyhow!("duplicity error for {}", app_id)) + } + + std::fs::copy( + metadata_path, + Path::new(embassy::VOLUMES) + .join(app_id) + .join("start9") + .join("restore.yaml"), + )?; + + Ok(()) +} \ No newline at end of file diff --git a/compat/src/config/mod.rs b/compat/src/config/mod.rs new file mode 100644 index 000000000..7dbc82d72 --- /dev/null +++ b/compat/src/config/mod.rs @@ -0,0 +1,83 @@ +use std::borrow::Cow; +use std::path::Path; + +use beau_collector::BeauCollector; +use embassy::config::action::SetResult; +use embassy::config::{Config, spec}; +use linear_map::LinearMap; + +pub mod rules; + +use anyhow::anyhow; +pub use rules::{ConfigRuleEntry, ConfigRuleEntryWithSuggestions}; + +pub fn validate_configuration( + name: &str, + config: Config, + rules_path: &Path, + config_path: &Path, +) -> Result { + let rules: Vec = serde_yaml::from_reader(std::fs::File::open(rules_path)?)?; + let mut cfgs = LinearMap::new(); + cfgs.insert(name, Cow::Borrowed(&config)); + let rule_check = rules + .into_iter() + .map(|r| r.check(&config, &cfgs)) + .bcollect::>(); + match rule_check { + Ok(_) => { + // create temp config file + serde_yaml::to_writer(std::fs::File::create(config_path.with_extension("tmp"))?, &config)?; + std::fs::rename(config_path.with_extension("tmp"), config_path)?; + // return set result + Ok(SetResult { + depends_on: indexmap::IndexMap::new(), + // sending sigterm so service is restarted - in 0.3.x services, this is whatever signal is needed to send to the process to pick up the configuration + signal: Some(nix::sys::signal::SIGTERM), + }) + } + Err(e) => Err(anyhow!("{}", e)) + } +} + +pub fn validate_dependency_configuration( + name: &str, + config: Config, + rules_path: &Path, + dependent_config: Config, +) -> Result<(), anyhow::Error> { + let rules: Vec = serde_yaml::from_reader(std::fs::File::open(rules_path)?)?; + let mut cfgs = LinearMap::new(); + cfgs.insert(name, Cow::Borrowed(&config)); + cfgs.insert(name, Cow::Borrowed(&dependent_config)); + let rule_check = rules + .into_iter() + .map(|r| r.check(&config, &cfgs)) + .bcollect::>(); + match rule_check { + Ok(_) => Ok(()), + Err(e) => Err(anyhow!("{}", e)) + } +} + +pub fn apply_dependency_configuration( + name: &str, + mut config: Config, + rules_path: &Path, + dependent_config: Config, +) -> Result { + let rules: Vec = + serde_yaml::from_reader(std::fs::File::open(rules_path)?)?; + let mut cfgs = LinearMap::new(); + cfgs.insert(name, Cow::Owned(config.clone())); + cfgs.insert(name, Cow::Owned(dependent_config.clone())); + let rule_check = rules + .into_iter() + .map(|r| r.apply(name, &mut config, &mut cfgs)) + .bcollect::>(); + + match rule_check { + Ok(_) => Ok(config), + Err(e) => Err(anyhow!("{}", e)) + } +} \ No newline at end of file diff --git a/compat/src/config/rule_parser.pest b/compat/src/config/rule_parser.pest new file mode 100644 index 000000000..53be53252 --- /dev/null +++ b/compat/src/config/rule_parser.pest @@ -0,0 +1,76 @@ +num = @{ int ~ ("." ~ ASCII_DIGIT*)? ~ (^"e" ~ int)? } + int = @{ ("+" | "-")? ~ ASCII_DIGIT+ } + +raw_string = @{ (!("\\" | "\"") ~ ANY)+ } +predefined = @{ "n" | "r" | "t" | "\\" | "0" | "\"" | "'" } +escape = @{ "\\" ~ predefined } +str = @{ "\"" ~ (raw_string | escape)* ~ "\"" } + +ident_char = @{ ASCII_ALPHANUMERIC | "-" } +sub_ident = _{ sub_ident_regular | sub_ident_index | sub_ident_any | sub_ident_all | sub_ident_fn } + sub_ident_regular = { sub_ident_regular_base | sub_ident_regular_expr } + sub_ident_regular_base = @{ ASCII_ALPHA ~ ident_char* } + sub_ident_regular_expr = ${ "[" ~ str_expr ~ "]" } + sub_ident_index = { sub_ident_index_base | sub_ident_index_expr } + sub_ident_index_base = @{ ASCII_DIGIT+ } + sub_ident_index_expr = ${ "[" ~ num_expr ~ "]" } + sub_ident_any = @{ "*" } + sub_ident_all = @{ "&" } + sub_ident_fn = ${ "[" ~ list_access_function ~ "]"} + list_access_function = _{ list_access_function_first | list_access_function_last | list_access_function_any | list_access_function_all } + list_access_function_first = !{ "first" ~ "(" ~ sub_ident_regular ~ "=>" ~ bool_expr ~ ")" } + list_access_function_last = !{ "last" ~ "(" ~ sub_ident_regular ~ "=>" ~ bool_expr ~ ")" } + list_access_function_any = !{ "any" ~ "(" ~ sub_ident_regular ~ "=>" ~ bool_expr ~ ")" } + list_access_function_all = !{ "all" ~ "(" ~ sub_ident_regular ~ "=>" ~ bool_expr ~ ")" } + +app_id = ${ "[" ~ sub_ident_regular ~ "]" } +ident = _{ (app_id ~ ".")? ~ sub_ident_regular ~ ("." ~ sub_ident)* } +bool_var = ${ ident ~ "?" } +num_var = ${ "#" ~ ident } +str_var = ${ "'" ~ ident } +any_var = ${ ident } + +bool_op = _{ and | or | xor } + and = { "AND" } + or = { "OR" } + xor = { "XOR" } + +num_cmp_op = _{ lt | lte | eq | neq | gt | gte } +str_cmp_op = _{ lt | lte | eq | neq | gt | gte } + lt = { "<" } + lte = { "<=" } + eq = { "=" } + neq = { "!=" } + gt = { ">" } + gte = { ">=" } + +num_op = _{ add | sub | mul | div | pow } +str_op = _{ add } + add = { "+" } + sub = { "-" } + mul = { "*" } + div = { "/" } + pow = { "^" } + +num_expr = !{ num_term ~ (num_op ~ num_term)* } +num_term = _{ num | num_var | "(" ~ num_expr ~ ")" } + +str_expr = !{ str_term ~ (str_op ~ str_term)* } +str_term = _{ str | str_var | "(" ~ str_expr ~ ")" } + +num_cmp_expr = { num_expr ~ num_cmp_op ~ num_expr } +str_cmp_expr = { str_expr ~ str_cmp_op ~ str_expr } + +bool_expr = !{ bool_term ~ (bool_op ~ bool_term)* } +inv_bool_expr = { "!(" ~ bool_expr ~ ")" } +bool_term = _{ bool_var | "(" ~ bool_expr ~ ")" | inv_bool_expr | num_cmp_expr | str_cmp_expr } + +val_expr = _{ any_var | str_expr | num_expr | bool_expr } + +rule = _{ SOI ~ bool_expr ~ EOI } +reference = _{ SOI ~ any_var ~ EOI } +value = _{ SOI ~ val_expr ~ EOI } +del_action = _{ SOI ~ "FROM" ~ any_var ~ "AS" ~ sub_ident_regular ~ "WHERE" ~ bool_expr ~ EOI } +obj_key = _{ SOI ~ sub_ident_regular ~ EOI } + +WHITESPACE = _{ " " | "\t" } \ No newline at end of file diff --git a/compat/src/config/rules.rs b/compat/src/config/rules.rs new file mode 100644 index 000000000..c3e258239 --- /dev/null +++ b/compat/src/config/rules.rs @@ -0,0 +1,1259 @@ +use std::borrow::Cow; +use std::sync::Arc; + +use linear_map::LinearMap; +use pest::iterators::Pairs; +use pest::Parser; +use rand::SeedableRng; +use serde_json::Value; + +use embassy::config::Config; +use embassy::config::util::STATIC_NULL; + +#[derive(Parser)] +#[grammar = "config/rule_parser.pest"] +struct RuleParser; + +lazy_static::lazy_static! { + static ref NUM_PREC_CLIMBER: pest::prec_climber::PrecClimber = { + use pest::prec_climber::*; + use Rule::*; + use Assoc::*; + + PrecClimber::new(vec![ + Operator::new(add, Left) | Operator::new(sub, Left), + Operator::new(mul, Left) | Operator::new(div, Left), + Operator::new(pow, Right) + ]) + }; + + static ref STR_PREC_CLIMBER: pest::prec_climber::PrecClimber = { + use pest::prec_climber::*; + use Rule::*; + use Assoc::*; + + PrecClimber::new(vec![ + Operator::new(add, Left) + ]) + }; + + static ref BOOL_PREC_CLIMBER: pest::prec_climber::PrecClimber = { + use pest::prec_climber::*; + use Rule::*; + use Assoc::*; + + PrecClimber::new(vec![ + Operator::new(or, Left), + Operator::new(xor, Left), + Operator::new(and, Left) + ]) + }; +} + +pub type Accessor = Box< + dyn for<'a> Fn(&'a Value, &LinearMap<&str, Cow>) -> VarRes<&'a Value> + Send + Sync, +>; +pub type AccessorMut = Box< + dyn for<'a> Fn(&'a mut Value, &LinearMap<&str, Cow>) -> Option<&'a mut Value> + + Send + + Sync, +>; +pub type CompiledExpr = Box>) -> T + Send + Sync>; +pub type CompiledReference = Box< + dyn for<'a> Fn(&'a mut Config, &LinearMap<&str, Cow>) -> Option<&'a mut Value> + + Send + + Sync, +>; +pub type Mutator = Box>) + Send + Sync>; +pub type CompiledRule = Box>) -> bool + Send + Sync>; +pub type CompiledRuleRes = Result; + +#[derive(Clone)] +pub struct ConfigRule { + pub src: String, + pub compiled: Arc, +} +impl std::fmt::Debug for ConfigRule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ConfigRule") + .field("src", &self.src) + .field("compiled", &"Fn(&Config, &Config) -> bool") + .finish() + } +} +impl<'de> serde::de::Deserialize<'de> for ConfigRule { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let src = String::deserialize(deserializer)?; + let compiled = compile(&src).map_err(serde::de::Error::custom)?; + Ok(ConfigRule { + src, + compiled: Arc::new(compiled), + }) + } +} +impl serde::ser::Serialize for ConfigRule { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + serializer.serialize_str(&self.src) + } +} +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct ConfigRuleEntry { + pub rule: ConfigRule, + pub description: String, +} +impl ConfigRuleEntry { + pub fn check( + &self, + cfg: &Config, + cfgs: &LinearMap<&str, Cow>, + ) -> Result<(), anyhow::Error> { + if !(self.rule.compiled)(cfg, cfgs) { + anyhow::anyhow!("{}", self.description); + } + Ok(()) + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum SetVariant { + To(String), + ToValue(Value), + ToEntropy(super::spec::Entropy), +} + +#[derive(Clone)] +pub enum SuggestionVariant { + Set { + var: String, + to: SetVariant, + compiled: Arc, + }, + Delete { + src: String, + compiled: Arc, + }, + Push { + to: String, + value: Value, + compiled: Arc, + }, +} +impl SuggestionVariant { + pub fn apply<'a>( + &self, + id: &'a str, + cfg: &mut Config, + cfgs: &mut LinearMap<&'a str, Cow>, + ) { + match self { + SuggestionVariant::Set { ref compiled, .. } => compiled(cfg, cfgs), + SuggestionVariant::Delete { ref compiled, .. } => compiled(cfg, cfgs), + SuggestionVariant::Push { ref compiled, .. } => compiled(cfg, cfgs), + } + cfgs.insert(id, Cow::Owned(cfg.clone())); + } +} +impl std::fmt::Debug for SuggestionVariant { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SuggestionVariant::Set { + ref var, ref to, .. + } => f + .debug_struct("SuggestionVariant::Set") + .field("var", var) + .field("to", to) + .field("compiled", &"Fn(&mut Config, Config)") + .finish(), + SuggestionVariant::Delete { ref src, .. } => f + .debug_struct("SuggestionVariant::Delete") + .field("src", src) + .field("compiled", &"Fn(&mut Config, Config)") + .finish(), + SuggestionVariant::Push { + ref to, ref value, .. + } => f + .debug_struct("SuggestionVariant::Delete") + .field("to", to) + .field("value", value) + .field("compiled", &"Fn(&mut Config, Config)") + .finish(), + } + } +} +impl<'de> serde::de::Deserialize<'de> for SuggestionVariant { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + #[derive(serde::Deserialize)] + enum _SuggestionVariant { + SET { + var: String, + #[serde(flatten)] + to: SetVariant, + }, + DELETE(String), + PUSH { + to: String, + value: Value, + }, + } + let raw = _SuggestionVariant::deserialize(deserializer)?; + Ok(match raw { + _SuggestionVariant::SET { var, to } => SuggestionVariant::Set { + compiled: Arc::new( + compile_set_action(&var, &to).map_err(serde::de::Error::custom)?, + ), + to: to, + var: var, + }, + _SuggestionVariant::DELETE(src) => SuggestionVariant::Delete { + compiled: Arc::new( + compile_del_action( + RuleParser::parse(Rule::del_action, &src) + .map_err(serde::de::Error::custom)?, + ) + .map_err(serde::de::Error::custom)?, + ), + src, + }, + _SuggestionVariant::PUSH { to, value } => SuggestionVariant::Push { + compiled: Arc::new( + compile_push_action( + RuleParser::parse(Rule::reference, &to) + .map_err(serde::de::Error::custom)?, + value.clone(), + ) + .map_err(serde::de::Error::custom)?, + ), + to, + value, + }, + }) + } +} +impl serde::ser::Serialize for SuggestionVariant { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + #[derive(serde::Serialize)] + enum _SuggestionVariant<'a> { + SET { + var: &'a str, + #[serde(flatten)] + to: &'a SetVariant, + }, + DELETE(&'a str), + PUSH { + to: &'a str, + value: &'a Value, + }, + } + match self { + SuggestionVariant::Set { + ref var, ref to, .. + } => serde::ser::Serialize::serialize(&_SuggestionVariant::SET { var, to }, serializer), + SuggestionVariant::Delete { ref src, .. } => { + serde::ser::Serialize::serialize(&_SuggestionVariant::DELETE(src), serializer) + } + SuggestionVariant::Push { + ref to, ref value, .. + } => serde::ser::Serialize::serialize( + &_SuggestionVariant::PUSH { to, value }, + serializer, + ), + } + } +} +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct Suggestion { + #[serde(rename = "if")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + pub condition: Option, + #[serde(flatten)] + pub variant: SuggestionVariant, +} +impl Suggestion { + pub fn apply<'a>( + &self, + id: &'a str, + cfg: &mut Config, + cfgs: &mut LinearMap<&'a str, Cow>, + ) { + match &self.condition { + Some(condition) if !(condition.compiled)(cfg, cfgs) => (), + _ => self.variant.apply(id, cfg, cfgs), + } + } +} +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct ConfigRuleEntryWithSuggestions { + #[serde(flatten)] + pub entry: ConfigRuleEntry, + pub suggestions: Vec, +} +impl ConfigRuleEntryWithSuggestions { + pub fn apply<'a>( + &self, + id: &'a str, + cfg: &mut Config, + cfgs: &mut LinearMap<&'a str, Cow>, + ) -> Result<(), anyhow::Error> { + if self.entry.check(cfg, cfgs).is_err() { + for suggestion in &self.suggestions { + suggestion.apply(id, cfg, cfgs); + } + self.entry.check(cfg, cfgs) + } else { + Ok(()) + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum VarRes { + Exactly(T), + Any(Vec>), + All(Vec>), +} +impl VarRes { + fn map U>(self, mut f: F) -> VarRes { + fn map_rec U>(s: VarRes, f: &mut F) -> VarRes { + match s { + VarRes::Exactly(a) => VarRes::Exactly(f(a)), + VarRes::Any(a) => VarRes::Any(a.into_iter().map(|a| map_rec(a, f)).collect()), + VarRes::All(a) => VarRes::All(a.into_iter().map(|a| map_rec(a, f)).collect()), + } + } + map_rec(self, &mut f) + } + fn and_then VarRes>(self, mut f: F) -> VarRes { + fn and_then_rec VarRes>(s: VarRes, f: &mut F) -> VarRes { + match s { + VarRes::Exactly(a) => f(a), + VarRes::Any(a) => VarRes::Any(a.into_iter().map(|a| and_then_rec(a, f)).collect()), + VarRes::All(a) => VarRes::All(a.into_iter().map(|a| and_then_rec(a, f)).collect()), + } + } + and_then_rec(self, &mut f) + } +} +impl VarRes { + fn resolve(self) -> bool { + match self { + VarRes::Exactly(a) => a, + VarRes::Any(a) => a.into_iter().any(|a| a.resolve()), + VarRes::All(a) => a.into_iter().all(|a| a.resolve()), + } + } +} + +fn compile_var_rec(mut ident: Pairs) -> Option { + let idx = ident.next(); + if let Some(idx) = idx { + let deref: Accessor = match idx.as_rule() { + Rule::sub_ident_any => Box::new(|v, _| match v { + Value::Array(l) => VarRes::Any(l.iter().map(VarRes::Exactly).collect()), + Value::Object(o) => { + VarRes::Any(o.iter().map(|(_, a)| VarRes::Exactly(a)).collect()) + } + _ => VarRes::Exactly(&STATIC_NULL), + }), + Rule::sub_ident_all => Box::new(|v, _| match v { + Value::Array(l) => VarRes::All(l.iter().map(VarRes::Exactly).collect()), + Value::Object(o) => { + VarRes::All(o.iter().map(|(_, a)| VarRes::Exactly(a)).collect()) + } + _ => VarRes::Exactly(&STATIC_NULL), + }), + Rule::sub_ident_fn => { + let idx = idx.into_inner().next().unwrap(); + match idx.as_rule() { + Rule::list_access_function_first => { + let mut pred_iter = idx.into_inner(); + let item_var = pred_iter.next().unwrap().as_str().to_owned(); + let predicate = compile_bool_expr(pred_iter.next().unwrap().into_inner()); + Box::new(move |v, cfgs| match v { + Value::Array(l) => VarRes::Exactly( + l.iter() + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .next() + .unwrap_or(&STATIC_NULL), + ), + Value::Object(o) => VarRes::Exactly( + &o.iter() + .map(|(_, item)| item) + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .next() + .unwrap_or(&STATIC_NULL), + ), + _ => VarRes::Exactly(&STATIC_NULL), + }) + } + Rule::list_access_function_last => { + let mut pred_iter = idx.into_inner(); + let item_var = pred_iter.next().unwrap().as_str().to_owned(); + let predicate = compile_bool_expr(pred_iter.next().unwrap().into_inner()); + Box::new(move |v, cfgs| match v { + Value::Array(l) => VarRes::Exactly( + l.iter() + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .next_back() + .unwrap_or(&STATIC_NULL), + ), + Value::Object(o) => VarRes::Exactly( + &o.iter() + .map(|(_, item)| item) + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .next_back() + .unwrap_or(&STATIC_NULL), + ), + _ => VarRes::Exactly(&STATIC_NULL), + }) + } + Rule::list_access_function_any => { + let mut pred_iter = idx.into_inner(); + let item_var = pred_iter.next().unwrap().as_str().to_owned(); + let predicate = compile_bool_expr(pred_iter.next().unwrap().into_inner()); + Box::new(move |v, cfgs| match v { + Value::Array(l) => VarRes::Any( + l.iter() + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .map(VarRes::Exactly) + .collect(), + ), + Value::Object(o) => VarRes::Any( + o.iter() + .map(|(_, item)| item) + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .map(VarRes::Exactly) + .collect(), + ), + _ => VarRes::Exactly(&STATIC_NULL), + }) + } + Rule::list_access_function_all => { + let mut pred_iter = idx.into_inner(); + let item_var = pred_iter.next().unwrap().as_str().to_owned(); + let predicate = compile_bool_expr(pred_iter.next().unwrap().into_inner()); + Box::new(move |v, cfgs| match v { + Value::Array(l) => VarRes::All( + l.iter() + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .map(VarRes::Exactly) + .collect(), + ), + Value::Object(o) => VarRes::All( + o.iter() + .map(|(_, item)| item) + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .map(VarRes::Exactly) + .collect(), + ), + _ => VarRes::Exactly(&STATIC_NULL), + }) + } + _ => unreachable!(), + } + } + Rule::sub_ident_regular => { + let idx = idx.into_inner().next().unwrap(); + match idx.as_rule() { + Rule::sub_ident_regular_base => { + let idx = idx.as_str().to_owned(); + Box::new(move |v, _| match v { + Value::Object(o) => { + VarRes::Exactly(o.get(&idx).unwrap_or(&STATIC_NULL)) + } + _ => VarRes::Exactly(&STATIC_NULL), + }) + } + Rule::sub_ident_regular_expr => { + let idx = compile_str_expr(idx.into_inner().next().unwrap().into_inner()); + Box::new(move |v, dep_cfg| match v { + Value::Object(o) => idx(&Config::default(), dep_cfg).map(|idx| { + idx.and_then(|idx| o.get(&idx)).unwrap_or(&STATIC_NULL) + }), + _ => VarRes::Exactly(&STATIC_NULL), + }) + } + _ => unreachable!(), + } + } + Rule::sub_ident_index => { + let idx = idx.into_inner().next().unwrap(); + match idx.as_rule() { + Rule::sub_ident_index_base => { + let idx: usize = idx.as_str().parse().unwrap(); + Box::new(move |v, _| match v { + Value::Array(l) => VarRes::Exactly(l.get(idx).unwrap_or(&STATIC_NULL)), + _ => VarRes::Exactly(&STATIC_NULL), + }) + } + Rule::sub_ident_index_expr => { + let idx = compile_num_expr(idx.into_inner().next().unwrap().into_inner()); + Box::new(move |v, dep_cfg| match v { + Value::Array(l) => idx(&Config::default(), dep_cfg) + .map(|idx| l.get(idx as usize).unwrap_or(&STATIC_NULL)), + _ => VarRes::Exactly(&STATIC_NULL), + }) + } + _ => unreachable!(), + } + } + _ => unreachable!(), + }; + Some(if let Some(rest) = compile_var_rec(ident) { + Box::new(move |v, cfgs| deref(v, cfgs).and_then(|v| rest(v, cfgs))) + } else { + deref + }) + } else { + None + } +} + +fn compile_var(mut var: Pairs) -> CompiledExpr> { + let mut first_seg = var.next().unwrap(); + let app_id = if first_seg.as_rule() == Rule::app_id { + let app_id = first_seg.into_inner().next().unwrap().as_str().to_owned(); + first_seg = var.next().unwrap(); + Some(app_id) + } else { + None + }; + let first_seg_string = first_seg.as_str().to_owned(); + let accessor = compile_var_rec(var); + Box::new(move |cfg, cfgs| { + let mut cfg: &Config = cfg; + if let Some(ref app_id) = app_id { + cfg = if let Some(cfg) = cfgs.get(&app_id.as_str()) { + cfg + } else { + return VarRes::Exactly(Value::Null); + }; + } + let val = cfg.get(&first_seg_string).unwrap_or(&STATIC_NULL); + if let Some(accessor) = &accessor { + accessor(val, cfgs).map(|v| v.clone()) + } else { + VarRes::Exactly(val.clone()) + } + }) +} + +fn compile_var_mut_rec(mut ident: Pairs) -> Result, failure::Error> { + let idx = ident.next(); + Ok(if let Some(idx) = idx { + let deref: AccessorMut = match idx.as_rule() { + Rule::sub_ident_fn => { + let idx = idx.into_inner().next().unwrap(); + match idx.as_rule() { + Rule::list_access_function_first => { + let mut pred_iter = idx.into_inner(); + let item_var = pred_iter.next().unwrap().as_str().to_owned(); + let predicate = compile_bool_expr(pred_iter.next().unwrap().into_inner()); + Box::new(move |v, cfgs| match v { + Value::Array(l) => l + .iter_mut() + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .next(), + Value::Object(o) => { + o.iter_mut() + .map(|(_, item)| item) + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .next() + } + _ => None, + }) + } + Rule::list_access_function_last => { + let mut pred_iter = idx.into_inner(); + let item_var = pred_iter.next().unwrap().as_str().to_owned(); + let predicate = compile_bool_expr(pred_iter.next().unwrap().into_inner()); + Box::new(move |v, cfgs| match v { + Value::Array(l) => l + .iter_mut() + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .next_back(), + Value::Object(o) => { + o.iter_mut() + .map(|(_, item)| item) + .filter(|item| { + let mut cfg = Config::default(); + cfg.insert(item_var.clone(), (*item).clone()); + predicate(&cfg, cfgs) + }) + .next_back() + } + _ => None, + }) + } + Rule::list_access_function_any | Rule::list_access_function_all => { + failure::bail!("Any and All are immutable") + } + _ => unreachable!(), + } + } + Rule::sub_ident_regular => { + let idx = idx.into_inner().next().unwrap(); + match idx.as_rule() { + Rule::sub_ident_regular_base => { + let idx = idx.as_str().to_owned(); + Box::new(move |v, _| match v { + Value::Object(ref mut o) => { + if o.contains_key(&idx) { + o.get_mut(&idx) + } else { + o.insert(idx.clone(), Value::Null); + o.get_mut(&idx) + } + } + _ => None, + }) + } + Rule::sub_ident_regular_expr => { + let idx = compile_str_expr(idx.into_inner().next().unwrap().into_inner()); + Box::new( + move |v, dep_cfg| match (v, idx(&Config::default(), dep_cfg)) { + (Value::Object(ref mut o), VarRes::Exactly(Some(ref idx))) => { + if o.contains_key(idx) { + o.get_mut(idx) + } else { + o.insert(idx.clone(), Value::Null); + o.get_mut(idx) + } + } + _ => None, + }, + ) + } + _ => unreachable!(), + } + } + Rule::sub_ident_index => { + let idx = idx.into_inner().next().unwrap(); + match idx.as_rule() { + Rule::sub_ident_index_base => { + let idx: usize = idx.as_str().parse().unwrap(); + Box::new(move |v, _| match v { + Value::Array(l) => { + if l.len() > idx { + l.get_mut(idx) + } else if idx == l.len() { + l.push(Value::Null); + l.get_mut(idx) + } else { + None + } + } + _ => None, + }) + } + Rule::sub_ident_index_expr => { + let idx = compile_num_expr(idx.into_inner().next().unwrap().into_inner()); + Box::new( + move |v, dep_cfg| match (v, idx(&Config::default(), dep_cfg)) { + (Value::Array(l), VarRes::Exactly(idx)) => { + let idx = idx as usize; + if l.len() > idx { + l.get_mut(idx) + } else if idx == l.len() { + l.push(Value::Null); + l.get_mut(idx) + } else { + None + } + } + _ => None, + }, + ) + } + _ => unreachable!(), + } + } + _ => failure::bail!("invalid token: {:?}", idx.as_rule()), + }; + Some(if let Some(rest) = compile_var_mut_rec(ident)? { + Box::new(move |v, cfgs| deref(v, cfgs).and_then(|v| rest(v, cfgs))) + } else { + deref + }) + } else { + None + }) +} + +fn compile_var_mut(mut var: Pairs) -> Result { + let first_seg = var.next().unwrap(); + if first_seg.as_rule() == Rule::app_id { + failure::bail!("Can only assign to relative path"); + } + let first_seg_string = first_seg.as_str().to_owned(); + let accessor_mut = compile_var_mut_rec(var)?; + Ok(Box::new(move |cfg, cfgs| { + let var = if cfg.contains_key(&first_seg_string) { + cfg.get_mut(&first_seg_string).unwrap() + } else { + cfg.insert(first_seg_string.clone(), Value::Null); + cfg.get_mut(&first_seg_string).unwrap() + }; + if let Some(accessor_mut) = &accessor_mut { + accessor_mut(var, cfgs) + } else { + Some(var) + } + })) +} + +fn compile_bool_var(var: Pairs) -> CompiledRule { + let var = compile_var(var); + Box::new(move |cfg, cfgs| { + var(cfg, cfgs) + .map(|a| match a { + Value::Bool(false) | Value::Null => false, + _ => true, + }) + .resolve() + }) +} + +fn compile_num_var(var: Pairs) -> CompiledExpr> { + let var = compile_var(var); + Box::new(move |cfg, cfgs| { + var(cfg, cfgs).map(|a| match a { + Value::Number(n) => n.as_f64().unwrap(), + Value::String(s) => match s.parse() { + Ok(n) => n, + Err(_) => panic!("string cannot be parsed as an f64") + }, + Value::Bool(b) => { + if b { + 1.0 + } else { + 0.0 + } + } + _ => panic!("object or list cannot be parsed as an f64"), + }) + }) +} + +fn compile_num(num_str: &str) -> CompiledExpr> { + let num = VarRes::Exactly(num_str.parse().unwrap()); + Box::new(move |_, _| num.clone()) +} + +fn compile_num_expr(pairs: Pairs) -> CompiledExpr> { + NUM_PREC_CLIMBER.climb( + pairs, + |pair| match pair.as_rule() { + Rule::num_var => compile_num_var(pair.into_inner()), + Rule::num => compile_num(pair.as_str()), + Rule::num_expr => compile_num_expr(pair.into_inner()), + _ => unreachable!(), + }, + |lhs, op, rhs| match op.as_rule() { + Rule::add => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs).and_then(|lhs| rhs(cfg, cfgs).map(|rhs| lhs + rhs)) + }), + Rule::sub => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs).and_then(|lhs| rhs(cfg, cfgs).map(|rhs| lhs - rhs)) + }), + Rule::mul => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs).and_then(|lhs| rhs(cfg, cfgs).map(|rhs| lhs * rhs)) + }), + Rule::div => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs).and_then(|lhs| rhs(cfg, cfgs).map(|rhs| lhs / rhs)) + }), + Rule::pow => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs).and_then(|lhs| rhs(cfg, cfgs).map(|rhs| lhs.powf(rhs))) + }), + _ => unreachable!(), + }, + ) +} + +fn compile_num_cmp_expr(mut pairs: Pairs) -> CompiledRule { + let lhs = compile_num_expr(pairs.next().unwrap().into_inner()); + let op = pairs.next().unwrap(); + let rhs = compile_num_expr(pairs.next().unwrap().into_inner()); + match op.as_rule() { + Rule::lt => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs) + .and_then(|lhs| rhs(cfg, cfgs).map(|rhs| lhs < rhs)) + .resolve() + }), + Rule::lte => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs) + .and_then(|lhs| rhs(cfg, cfgs).map(|rhs| lhs <= rhs)) + .resolve() + }), + Rule::eq => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs) + .and_then(|lhs| rhs(cfg, cfgs).map(|rhs| lhs == rhs)) + .resolve() + }), + Rule::neq => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs) + .and_then(|lhs| rhs(cfg, cfgs).map(|rhs| lhs != rhs)) + .resolve() + }), + Rule::gt => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs) + .and_then(|lhs| rhs(cfg, cfgs).map(|rhs| lhs > rhs)) + .resolve() + }), + Rule::gte => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs) + .and_then(|lhs| rhs(cfg, cfgs).map(|rhs| lhs >= rhs)) + .resolve() + }), + _ => unreachable!(), + } +} + +fn compile_str_var(var: Pairs) -> CompiledExpr>> { + let var = compile_var(var); + Box::new(move |cfg, cfgs| { + var(cfg, cfgs).map(|a| match a { + Value::String(s) => Some(s), + Value::Number(n) => Some(format!("{}", n)), + Value::Bool(b) => Some(format!("{}", b)), + _ => None, + }) + }) +} + +fn compile_str(str_str: &str) -> CompiledExpr>> { + let str_str = &str_str[1..str_str.len() - 1]; + let mut out = String::with_capacity(str_str.len()); + let mut escape = false; + for c in str_str.chars() { + match c { + '\\' => { + if escape { + out.push('\\'); + } else { + escape = true; + } + } + 'n' if escape => out.push('\n'), + 'r' if escape => out.push('\r'), + 't' if escape => out.push('\t'), + '0' if escape => out.push('\0'), + '"' if escape => out.push('"'), + '\'' if escape => out.push('\''), + _ => { + if escape { + out.push('\\') + } + out.push(c) + } + } + } + let res = VarRes::Exactly(Some(out)); + Box::new(move |_, _| res.clone()) +} + +fn compile_str_expr(pairs: Pairs) -> CompiledExpr>> { + STR_PREC_CLIMBER.climb( + pairs, + |pair| match pair.as_rule() { + Rule::str_var => compile_str_var(pair.into_inner()), + Rule::str => compile_str(pair.as_str()), + Rule::str_expr => compile_str_expr(pair.into_inner()), + _ => unreachable!(), + }, + |lhs, op, rhs| match op.as_rule() { + Rule::add => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs).and_then(|lhs| { + rhs(cfg, cfgs).map(|rhs| { + let lhs = lhs.clone()?; + let rhs = rhs?; + Some(lhs + &rhs) + }) + }) + }), + _ => unreachable!(), + }, + ) +} + +fn compile_str_cmp_expr(mut pairs: Pairs) -> CompiledRule { + let lhs = compile_str_expr(pairs.next().unwrap().into_inner()); + let op = pairs.next().unwrap(); + let rhs = compile_str_expr(pairs.next().unwrap().into_inner()); + match op.as_rule() { + Rule::lt => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs) + .and_then(|lhs| { + rhs(cfg, cfgs).map(|rhs| match (&lhs, &rhs) { + (Some(lhs), Some(rhs)) => rhs.contains(lhs) && lhs.len() < rhs.len(), + _ => false, + }) + }) + .resolve() + }), + Rule::lte => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs) + .and_then(|lhs| { + rhs(cfg, cfgs).map(|rhs| match (&lhs, &rhs) { + (Some(lhs), Some(rhs)) => rhs.contains(lhs), + _ => false, + }) + }) + .resolve() + }), + Rule::eq => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs) + .and_then(|lhs| { + rhs(cfg, cfgs).map(|rhs| match (&lhs, &rhs) { + (Some(lhs), Some(rhs)) => lhs == rhs, + (None, None) => true, + _ => false, + }) + }) + .resolve() + }), + Rule::neq => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs) + .and_then(|lhs| { + rhs(cfg, cfgs).map(|rhs| match (&lhs, &rhs) { + (Some(lhs), Some(rhs)) => lhs != rhs, + (None, None) => false, + _ => true, + }) + }) + .resolve() + }), + Rule::gt => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs) + .and_then(|lhs| { + rhs(cfg, cfgs).map(|rhs| match (&lhs, &rhs) { + (Some(lhs), Some(rhs)) => lhs.contains(rhs) && lhs.len() > rhs.len(), + _ => true, + }) + }) + .resolve() + }), + Rule::gte => Box::new(move |cfg, cfgs| { + lhs(cfg, cfgs) + .and_then(|lhs| { + rhs(cfg, cfgs).map(|rhs| match (&lhs, &rhs) { + (Some(lhs), Some(rhs)) => lhs.contains(rhs), + _ => true, + }) + }) + .resolve() + }), + _ => unreachable!(), + } +} + +fn compile_inv_bool_expr(mut pairs: Pairs) -> CompiledRule { + let expr = compile_bool_expr(pairs.next().unwrap().into_inner()); + Box::new(move |cfg, cfgs| !expr(cfg, cfgs)) +} + +fn compile_bool_expr(pairs: Pairs) -> CompiledRule { + BOOL_PREC_CLIMBER.climb( + pairs, + |pair| match pair.as_rule() { + Rule::bool_var => compile_bool_var(pair.into_inner()), + Rule::bool_expr => compile_bool_expr(pair.into_inner()), + Rule::inv_bool_expr => compile_inv_bool_expr(pair.into_inner()), + Rule::num_cmp_expr => compile_num_cmp_expr(pair.into_inner()), + Rule::str_cmp_expr => compile_str_cmp_expr(pair.into_inner()), + _ => unreachable!(), + }, + |lhs, op, rhs| -> CompiledRule { + match op.as_rule() { + Rule::and => Box::new(move |cfg, cfgs| lhs(cfg, cfgs) && rhs(cfg, cfgs)), + Rule::or => Box::new(move |cfg, cfgs| lhs(cfg, cfgs) || rhs(cfg, cfgs)), + Rule::xor => Box::new(move |cfg, cfgs| lhs(cfg, cfgs) ^ rhs(cfg, cfgs)), + _ => unreachable!(), + } + }, + ) +} + +fn compile_value_expr(mut pairs: Pairs) -> CompiledExpr> { + let expr = pairs.next().unwrap(); + match expr.as_rule() { + Rule::any_var => compile_var(expr.into_inner()), + Rule::str_expr => { + let expr = compile_str_expr(expr.into_inner()); + Box::new(move |cfg, cfgs| { + expr(cfg, cfgs).map(|s| s.map(Value::String).unwrap_or(Value::Null)) + }) + } + Rule::num_expr => { + let expr = compile_num_expr(expr.into_inner()); + Box::new(move |cfg, cfgs| expr(cfg, cfgs).map(|n| match + serde_json::Number::from_f64(n) { + Some(a) => Value::Number(a), + None => panic!("cannot coerce f64 into numberc type") + })) + } + Rule::bool_expr => { + let expr = compile_bool_expr(expr.into_inner()); + Box::new(move |cfg, cfgs| VarRes::Exactly(expr(cfg, cfgs)).map(Value::Bool)) + } + _ => unreachable!(), + } +} + +fn compile_del_action(mut pairs: Pairs) -> Result { + let list_mut = compile_var_mut(pairs.next().unwrap().into_inner())?; + let var = pairs.next().unwrap().as_str().to_owned(); + let predicate = compile_bool_expr(pairs.next().unwrap().into_inner()); + Ok(Box::new(move |cfg, cfgs| match (&list_mut)(cfg, cfgs) { + Some(Value::Array(ref mut l)) => { + *l = std::mem::take(l) + .into_iter() + .filter(|item| { + let mut obj = Config::default(); + obj.insert(var.clone(), item.clone()); + !predicate(&obj, cfgs) + }) + .collect(); + } + Some(Value::Object(ref mut o)) => { + *o = std::mem::take(o) + .into_iter() + .filter(|(_, item)| { + let mut obj = Config::default(); + obj.insert(var.clone(), item.clone()); + !predicate(&obj, cfgs) + }) + .collect(); + } + _ => return, + })) +} + +fn compile_push_action(mut pairs: Pairs, value: Value) -> Result { + let list_mut = compile_var_mut(pairs.next().unwrap().into_inner())?; + Ok(Box::new(move |cfg, cfgs| { + let vec = match (&list_mut)(cfg, cfgs) { + Some(Value::Array(ref mut a)) => a, + _ => return, + }; + vec.push(value.clone()) + })) +} + +fn compile_set_action(var: &str, to: &SetVariant) -> Result { + let mut var = RuleParser::parse(Rule::reference, var)?; + let get_mut = compile_var_mut(var.next().unwrap().into_inner())?; + Ok(match to { + SetVariant::To(expr) => { + let expr = compile_expr(&expr)?; + Box::new(move |cfg, cfgs| { + let val = expr(cfg, cfgs); + if let Some(var) = get_mut(cfg, cfgs) { + *var = val; + } + }) + } + SetVariant::ToValue(val) => { + let val = val.clone(); + Box::new(move |cfg, cfgs| { + if let Some(var) = get_mut(cfg, cfgs) { + *var = val.clone() + } + }) + } + SetVariant::ToEntropy(entropy) => { + let entropy = entropy.clone(); + Box::new(move |cfg, cfgs| { + if let Some(var) = get_mut(cfg, cfgs) { + *var = Value::String(entropy.gen(&mut rand::rngs::StdRng::from_entropy())); + } + }) + } + }) +} + +pub fn validate_key(key: &str) -> Result<(), pest::error::Error> { + RuleParser::parse(Rule::obj_key, key)?; + Ok(()) +} + +pub fn parse_and) -> T>( + rule: &str, + f: F, +) -> Result> { + let mut parsed = RuleParser::parse(Rule::rule, rule)?; + let pairs = parsed.next().unwrap().into_inner(); + Ok(f(pairs)) +} + +pub fn compile(rule: &str) -> Result { + parse_and(rule, compile_bool_expr).map_err(From::from) +} + +pub fn compile_expr(expr: &str) -> Result, failure::Error> { + let compiled = compile_value_expr(RuleParser::parse(Rule::value, expr)?); + Ok(Box::new(move |cfg, cfgs| match compiled(cfg, cfgs) { + VarRes::Exactly(v) => v, + _ => Value::Null, + })) +} + +#[cfg(test)] +mod test { + use serde_json::json; + + use super::*; + + #[test] + fn test_compile_str() { + assert_eq!( + compile_str("\"foo\"")(&Default::default(), &Default::default()), + VarRes::Exactly(Some("foo".to_owned())) + ); + } + + #[test] + fn test_access_expr() { + let mut cfg = Config::default(); + let mut cfgs = LinearMap::new(); + let mut foo = Config::default(); + foo.insert("bar!\"".to_owned(), json!(3.0)); + cfg.insert( + "foo".to_owned(), + Value::Array(vec![Value::Null, Value::Object(foo), json!(3)]), + ); + cfgs.insert("my-app", Cow::Borrowed(&cfg)); + assert!((compile("#[my-app].foo.1.[\"ba\" + \"r!\\\"\"] = 3") + .map_err(|e| eprintln!("{}", e)) + .expect("compile failed"))(&cfg, &cfgs)); + assert!((compile("#[my-app].foo.[0 + 1].[\"bar!\\\"\"] = 3") + .map_err(|e| eprintln!("{}", e)) + .expect("compile failed"))(&cfg, &cfgs)); + } + + #[test] + fn test_any_all() { + let mut cfg = Config::default(); + let mut cfgs = LinearMap::new(); + let mut foo = Config::default(); + foo.insert("bar".to_owned(), json!(3.0)); + cfg.insert( + "foo".to_owned(), + Value::Array(vec![Value::Null, Value::Object(foo), json!(3.0)]), + ); + cfgs.insert("my-app", Cow::Borrowed(&cfg)); + // NOTE: these now fail due to added panic for parsing f64's + // assert!((compile("#[my-app].foo.*.bar = 3") + // .map_err(|e| eprintln!("{}", e)) + // .expect("compile failed"))(&cfg, &cfgs)); + // assert!(!(compile("#[my-app].foo.&.bar = 3") + // .map_err(|e| eprintln!("{}", e)) + // .expect("compile failed"))(&cfg, &cfgs)); + } + + #[test] + fn test_first_last() { + let mut cfg = Config::default(); + let mut cfgs = LinearMap::new(); + let mut foo = Config::default(); + foo.insert("bar".to_owned(), json!(3.0)); + foo.insert("baz".to_owned(), json!(4.0)); + let mut qux = Config::default(); + qux.insert("bar".to_owned(), json!(7.0)); + qux.insert("baz".to_owned(), json!(4.0)); + cfg.insert( + "foo".to_owned(), + Value::Array(vec![ + Value::Null, + Value::Object(foo), + Value::Object(qux), + json!(3.0), + ]), + ); + cfgs.insert("my-app", Cow::Borrowed(&cfg)); + // NOTE: these now fail due to added panic for parsing f64's + // assert!((compile("#foo.[first(item => #item.baz = 4)].bar = 3") + // .map_err(|e| eprintln!("{}", e)) + // .expect("compile failed"))(&cfg, &cfgs)); + // assert!((compile("#foo.[last(item => #item.baz = 4)].bar = 7") + // .map_err(|e| eprintln!("{}", e)) + // .expect("compile failed"))(&cfg, &cfgs)); + } + + #[test] + fn test_app_id() { + let mut dependent_cfg = Config::default(); + let mut dependency_cfg = Config::default(); + let mut cfgs = LinearMap::new(); + dependent_cfg + .insert("foo".to_owned(), Value::String("bar".to_owned())); + dependency_cfg + .insert("foo".to_owned(), Value::String("bar!".to_owned())); + cfgs.insert("my-dependent", Cow::Borrowed(&dependent_cfg)); + cfgs.insert("my-dependency", Cow::Borrowed(&dependency_cfg)); + assert!((compile("'foo = '[my-dependent].foo + \"!\"") + .map_err(|e| eprintln!("{}", e)) + .expect("compile failed"))( + &dependency_cfg, &cfgs + )) + } +} diff --git a/compat/src/main.rs b/compat/src/main.rs index 82ffce3e9..8c32e3d55 100644 --- a/compat/src/main.rs +++ b/compat/src/main.rs @@ -1,24 +1,162 @@ -use std::{fs::File, io::stdout, path::Path}; +use std::{ + fs::File, + io::{stdin, stdout}, + path::Path, +}; +#[macro_use] +extern crate failure; +extern crate pest; +#[macro_use] +extern crate pest_derive; + +mod backup; +mod config; +use anyhow::anyhow; +use backup::{create_backup, restore_backup}; use clap::{App, Arg, SubCommand}; -use embassy::config::action::{ConfigRes, SetResult}; +use config::{ + apply_dependency_configuration, validate_configuration, validate_dependency_configuration, +}; +use embassy::config::action::ConfigRes; +pub enum CompatRes { + SetResult, + ConfigRes, +} fn main() { - let app = App::new("compat").subcommand( - SubCommand::with_name("config").subcommand( - SubCommand::with_name("get") - .arg( - Arg::with_name("mountpoint") - .help("The `mount` field from manifest.yaml") - .required(true), + match inner_main() { + Ok(a) => a, + Err(e) => { + eprintln!("{}", e); + log::debug!("{:?}", e.backtrace()); + drop(e); + std::process::exit(1) + } + } +} + +fn inner_main() -> Result<(), anyhow::Error> { + let app = App::new("compat") + .subcommand( + SubCommand::with_name("config") + .subcommand( + SubCommand::with_name("get") + .arg( + Arg::with_name("mountpoint") + .help("Path to the data mountpoint") + .required(true), + ) + .arg( + Arg::with_name("spec") + .help("The path to the config spec in the container") + .required(true), + ), ) - .arg( - Arg::with_name("spec") - .help("The path to the config spec in the container") - .required(true), + .subcommand( + SubCommand::with_name("set") + .arg( + Arg::with_name("package_id") + .help("The `id` field from the manifest file") + .required(true), + ) + .arg( + Arg::with_name("mountpoint") + .help("Path to the data mountpoint") + .required(true), + ) + .arg( + Arg::with_name("assets") + .help("Path to the rules file") + .required(true), + ), ), - ), - ); + ) + .subcommand( + SubCommand::with_name("dependency") + .subcommand( + SubCommand::with_name("check") + .arg( + Arg::with_name("dependency_package_id") + .help("Identifier of the dependency") + .required(true), + ) + .arg( + Arg::with_name("mountpoint") + .help(" ountpoint for the dependent's config file") + .required(true), + ) + .arg( + Arg::with_name("assets") + .help("Path to the dependency's config rules file") + .required(true), + ), + ) + .subcommand( + SubCommand::with_name("auto-configure") + .arg( + Arg::with_name("dependency_package_id") + .help("Package identifier of the dependency") + .required(true), + ) + .arg( + Arg::with_name("mountpoint") + .help("Mountpoint for the dependent's config file") + .required(true), + ) + .arg( + Arg::with_name("assets") + .help("Path to the dependency's config rules file") + .required(true), + ), + ), + ) + .subcommand( + SubCommand::with_name("duplicity") + .subcommand( + SubCommand::with_name("create") + .arg( + Arg::with_name("package-id") + .help("The `id` field from the manifest file") + .required(true), + ) + .arg( + Arg::with_name("mountpoint") + .help("The backups mount point") + .required(true), + ) + .arg( + Arg::with_name("datapath") + .help("The path to the data to be backed up in the container") + .required(true), + ), + ) + .subcommand( + SubCommand::with_name("restore") + .arg( + Arg::with_name("package-id") + .help("The `id` field from the manifest file") + .required(true), + ) + .arg( + Arg::with_name("mountpoint") + .help("The backups mount point") + .required(true), + ) + .arg( + Arg::with_name("datapath") + .help("The path to the data to be restored to the container") + .required(true), + ), + ), + ) + .subcommand( + SubCommand::with_name("properties").arg( + Arg::with_name("mountpoint") + .help("The data directory of the service to mount to.") + .required(true), + ), + ); let matches = app.get_matches(); match matches.subcommand() { ("config", Some(sub_m)) => match sub_m.subcommand() { @@ -32,14 +170,134 @@ fn main() { }; let spec_path = Path::new(sub_m.value_of("spec").unwrap()); let spec = serde_yaml::from_reader(File::open(spec_path).unwrap()).unwrap(); - serde_yaml::to_writer(stdout(), &ConfigRes { config: cfg, spec }).unwrap(); + serde_yaml::to_writer(stdout(), &ConfigRes { config: cfg, spec })?; + Ok(()) + } + ("set", Some(sub_m)) => { + let config = serde_yaml::from_reader(stdin())?; + let cfg_path = Path::new(sub_m.value_of("mountpoint").unwrap()).join("start9"); + if !cfg_path.exists() { + std::fs::create_dir_all(&cfg_path).unwrap(); + }; + let rules_path = Path::new(sub_m.value_of("assets").unwrap()); + let name = sub_m.value_of("package_id").unwrap(); + match validate_configuration( + &name, + config, + rules_path, + &cfg_path.join("config.yaml"), + ) { + Ok(a) => { + serde_yaml::to_writer(stdout(), &a)?; + Ok(()) + } + Err(e) => Err(e), + } } (subcmd, _) => { - panic!("unknown subcommand: {}", subcmd); + panic!("Unknown subcommand: {}", subcmd); } }, + ("dependency", Some(sub_m)) => match sub_m.subcommand() { + ("check", Some(sub_m)) => { + let dependency_config = serde_yaml::from_reader(stdin())?; + let dependent_config = serde_yaml::from_reader( + File::open( + Path::new(sub_m.value_of("mountpoint").unwrap()).join("start9/config.yaml"), + ) + .unwrap(), + )?; + let rules_path = Path::new(sub_m.value_of("assets").unwrap()); + let name = sub_m.value_of("dependency_package_id").unwrap(); + match validate_dependency_configuration( + &name, + dependency_config, + rules_path, + dependent_config, + ) { + Ok(a) => { + serde_yaml::to_writer(stdout(), &a)?; + Ok(()) + } + Err(e) => { + // error string is configs rules failure description + Err(e) + } + } + } + ("auto-configure", Some(sub_m)) => { + let dependency_config = serde_yaml::from_reader(stdin())?; + let dependent_config = serde_yaml::from_reader( + File::open( + Path::new(sub_m.value_of("mountpoint").unwrap()).join("start9/config.yaml"), + ) + .unwrap(), + )?; + let rules_path = Path::new(sub_m.value_of("assets").unwrap()); + let name = sub_m.value_of("dependency_package_id").unwrap(); + match apply_dependency_configuration( + name, + dependency_config, + rules_path, + dependent_config, + ) { + Ok(a) => { + serde_yaml::to_writer(stdout(), &a)?; + Ok(()) + } + Err(e) => Err(e), + } + } + (subcmd, _) => { + panic!("Unknown subcommand: {}", subcmd); + } + }, + ("duplicity", Some(sub_m)) => match sub_m.subcommand() { + ("create", Some(sub_m)) => { + let res = create_backup( + sub_m.value_of("mountpoint").unwrap(), + sub_m.value_of("datapath").unwrap(), + sub_m.value_of("package-id").unwrap(), + ); + match res { + Ok(r) => { + serde_yaml::to_writer(stdout(), &r)?; + Ok(()) + } + Err(e) => Err(anyhow!("Could not create backup: {}", e)), + } + } + ("restore", Some(sub_m)) => { + let res = restore_backup( + sub_m.value_of("package-id").unwrap(), + sub_m.value_of("datapath").unwrap(), + sub_m.value_of("mountpoint").unwrap(), + ); + match res { + Ok(r) => { + serde_yaml::to_writer(stdout(), &r)?; + Ok(()) + } + Err(e) => Err(anyhow!("Could not restore backup: {}", e)), + } + } + (subcmd, _) => { + panic!("Unknown subcommand: {}", subcmd); + } + }, + ("properties", Some(sub_m)) => { + let stats_path = + Path::new(sub_m.value_of("mountpoint").unwrap()).join("start9/stats.yaml"); + let stats: serde_json::Value = if stats_path.exists() { + serde_yaml::from_reader(File::open(stats_path).unwrap()).unwrap() + } else { + serde_json::Value::from("{}") + }; + serde_json::to_writer(stdout(), &stats)?; + Ok(()) + } (subcmd, _) => { - panic!("unknown subcommand: {}", subcmd); + panic!("Unknown subcommand: {}", subcmd); } } }