diff --git a/Cargo.lock b/Cargo.lock index 1f719cab7..27f3b6a13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,7 @@ dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -77,6 +77,16 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "async-trait" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "atty" version = "0.2.11" @@ -570,7 +580,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -609,7 +619,7 @@ dependencies = [ "ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -622,7 +632,7 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -632,7 +642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling_core 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -642,7 +652,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -661,7 +671,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -673,7 +683,7 @@ dependencies = [ "derive_builder_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -684,7 +694,7 @@ dependencies = [ "darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -697,7 +707,7 @@ dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -844,7 +854,7 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive_internals 0.24.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -898,7 +908,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -961,16 +971,37 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "futures-channel-preview" -version = "0.3.0-alpha.16" +name = "futures-async-stream" +version = "0.1.0-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-async-stream-macro 0.1.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-async-stream-macro" +version = "0.1.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-channel-preview" +version = "0.3.0-alpha.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-core-preview" -version = "0.3.0-alpha.16" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -984,56 +1015,51 @@ dependencies = [ [[package]] name = "futures-executor-preview" -version = "0.3.0-alpha.16" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-io-preview" -version = "0.3.0-alpha.16" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "futures-preview" -version = "0.3.0-alpha.16" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-executor-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-sink-preview" -version = "0.3.0-alpha.16" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-util-preview" -version = "0.3.0-alpha.16" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1042,11 +1068,11 @@ dependencies = [ [[package]] name = "futures_codec" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1065,7 +1091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1445,7 +1471,7 @@ dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1648,6 +1674,7 @@ dependencies = [ "adhoc_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "async-trait 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "byte-unit 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1664,9 +1691,10 @@ dependencies = [ "dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "enum-utils 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "enum_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures_codec 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-async-stream 0.1.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures_codec 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "getset 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1697,7 +1725,6 @@ dependencies = [ "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde_ini 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1721,7 +1748,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2000,6 +2027,16 @@ dependencies = [ "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pin-project" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pin-utils" version = "0.1.0-alpha.4" @@ -2636,7 +2673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2645,7 +2682,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2781,7 +2818,7 @@ dependencies = [ [[package]] name = "syn" -version = "0.15.37" +version = "0.15.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2796,7 +2833,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3115,7 +3152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3287,7 +3324,7 @@ dependencies = [ "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-shared 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3307,7 +3344,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-backend 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-shared 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3431,6 +3468,7 @@ dependencies = [ "checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" +"checksum async-trait 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "41f6b40ac5df0ab7ef35112d7a593428ca62bcaabcc7de20f88b506cf798928d" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" "checksum backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)" = "ada4c783bb7e7443c14e0480f429ae2cc99da95065aeab7ee1b81ada0419404f" @@ -3532,15 +3570,17 @@ dependencies = [ "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "a2037ec1c6c1c4f79557762eab1f7eae1f64f6cb418ace90fae88f0942b60139" -"checksum futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4cd523712fc272e9b714669165a2832debee5a5b7e409bfccdc7c0d5cd0cf07a" -"checksum futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "719770f328642b657b849856bb5a607db9538dd5bb3000122e5ead55d0a58c36" +"checksum futures-async-stream 0.1.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c4aecac975d38ff13b935de313e12fded407231b0d563e7493fac40463272a2f" +"checksum futures-async-stream-macro 0.1.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4bbd6c677284a4bb3c043193ad20017f06c61cca34327ce5b0682691f92a182e" +"checksum futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "21c71ed547606de08e9ae744bb3c6d80f5627527ef31ecf2a7210d0e67bc8fae" +"checksum futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4b141ccf9b7601ef987f36f1c0d9522f76df3bba1cf2e63bfacccc044c4558f5" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "315dc58c908535d059576a329b86cd185933433382cfcd394fb2fa353330de03" -"checksum futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cca0bf7a1f39c9d32b797b0def93d5932aa71796236aad6b549bac6f7df159a3" -"checksum futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfeac5f016a4b5835bb93eb7961f50a64f0e001207562703d9ddf4109d7b263" -"checksum futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "49dcfdacd6b5974ca0b9b78bc38ffd1071da0206179735c3df82e279f5b784e4" -"checksum futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "f7a0451b9c5047c2b9ab93425ffd0793165511e93c04b977cd45fbd41c6e34b2" -"checksum futures_codec 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b60f48aa03e365df015d2fbf0b79f17b440350c268a5e20305da17b394adcc1e" +"checksum futures-executor-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "87ba260fe51080ba37f063ad5b0732c4ff1f737ea18dcb67833d282cdc2c6f14" +"checksum futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "082e402605fcb8b1ae1e5ba7d7fdfd3e31ef510e2a8367dd92927bb41ae41b3a" +"checksum futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "bf25f91c8a9a1f64c451e91b43ba269ed359b9f52d35ed4b3ce3f9c842435867" +"checksum futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4309a25a1069a1f3c10647b227b9afe6722b67a030d3f00a9cbdc171fc038de4" +"checksum futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "af8198c48b222f02326940ce2b3aa9e6e91a32886eeaad7ca3b8e4c70daa3f4e" +"checksum futures_codec 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "36552cd31353fd135114510d53b8d120758120c36aa636a9341970f9efb1e4a0" "checksum getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e65cce4e5084b14874c4e7097f38cab54f47ee554f9194673456ea379dcc4c55" "checksum getset 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "19fbde0fad0c1c1f9474694b1f5c9ba22b09f2f74f74e6d2bd19c43f6656e2cb" "checksum gif 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "86c2f2b597d6e05c86ee5947b2223bda468fe8dad3e88e2a6520869322aaf568" @@ -3634,6 +3674,7 @@ dependencies = [ "checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" "checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" +"checksum pin-project 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2714268752963de91be73ea2689c3c63d2389b14fad04d033923e20cb3a1d99a" "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum plist 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a9f075f6394100e7c105ed1af73fb1859d6fd14e49d4290d578120beb167f" @@ -3723,7 +3764,7 @@ dependencies = [ "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "28fc0f40f0c0da73339d347aa7d6d2b90341a95683a47722bc4eebed71ff3c00" -"checksum syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e11410033fd5cf69a1cf2084604e011190c56f11e08ffc53df880f5f65f1c6e4" +"checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e80b8831c5a543192ffc3727f01cf0e57579c6ac15558e3048bfb5708892167b" "checksum sys-info 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "76d6cf7b349b6a6daaf7a3797227e2f4108c8dd398e0aca7e29b9fb239948541" diff --git a/Cargo.toml b/Cargo.toml index c7efe3f55..c5b7a5d32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,18 +28,19 @@ chrono-humanize = "0.0.11" byte-unit = "2.1.0" ordered-float = {version = "1.0.2", features = ["serde"]} prettyprint = "0.7.0" -futures-preview = { version = "=0.3.0-alpha.16", features = ["compat", "io-compat"] } -futures-sink-preview = "=0.3.0-alpha.16" -futures_codec = "0.2.2" +futures-preview = { version = "=0.3.0-alpha.17", features = ["compat", "io-compat"] } +futures-sink-preview = "=0.3.0-alpha.17" +futures-async-stream = "0.1.0-alpha.1" +async-trait = "" +futures_codec = "0.2.5" term = "0.5.2" bytes = "0.4.12" log = "0.4.7" pretty_env_logger = "0.3.0" -serde = "1.0.94" +serde = { version = "1.0.94", features = ["derive"] } serde_json = "1.0.40" serde-hjson = "0.9.0" serde_yaml = "0.8" -serde_derive = "1.0.94" serde_bytes = "0.11.1" getset = "0.0.7" logos = "0.10.0-rc2" diff --git a/src/cli.rs b/src/cli.rs index a568b9e29..02dd5de13 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,18 +1,17 @@ use crate::commands::autoview; -use crate::commands::classified::SinkCommand; use crate::commands::classified::{ ClassifiedCommand, ClassifiedInputStream, ClassifiedPipeline, ExternalCommand, InternalCommand, StreamNext, }; -use crate::commands::command::sink; use crate::commands::plugin::JsonRpc; -use crate::commands::plugin::{PluginCommand, PluginSink}; +use crate::commands::plugin::PluginCommand; +use crate::commands::{static_command, Command}; use crate::context::Context; crate use crate::errors::ShellError; use crate::git::current_branch; use crate::object::Value; use crate::parser::parse::span::Spanned; -use crate::parser::registry::CommandConfig; +use crate::parser::registry::Signature; use crate::parser::{hir, Pipeline, PipelineElement, TokenNode}; use crate::prelude::*; @@ -61,8 +60,7 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel let mut input = String::new(); match reader.read_line(&mut input) { Ok(_) => { - let response = - serde_json::from_str::>>(&input); + let response = serde_json::from_str::>>(&input); match response { Ok(jrpc) => match jrpc.params { Ok(params) => { @@ -70,15 +68,10 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel if params.is_filter { let fname = fname.to_string(); let name = params.name.clone(); - context.add_commands(vec![Arc::new(PluginCommand::new( + context.add_commands(vec![static_command(PluginCommand::new( name, fname, params, ))]); Ok(()) - } else if params.is_sink { - let fname = fname.to_string(); - let name = params.name.clone(); - context.add_sinks(vec![Arc::new(PluginSink::new(name, fname, params))]); - Ok(()) } else { Ok(()) } @@ -158,10 +151,8 @@ pub async fn cli() -> Result<(), Box> { command("ps", Box::new(ps::ps)), command("ls", Box::new(ls::ls)), command("sysinfo", Box::new(sysinfo::sysinfo)), - command("cd", Box::new(cd::cd)), command("size", Box::new(size::size)), command("from-yaml", Box::new(from_yaml::from_yaml)), - command("get", Box::new(get::get)), command("exit", Box::new(exit::exit)), command("lines", Box::new(lines::lines)), command("split-column", Box::new(split_column::split_column)), @@ -175,19 +166,18 @@ pub async fn cli() -> Result<(), Box> { command("to-toml", Box::new(to_toml::to_toml)), command("to-yaml", Box::new(to_yaml::to_yaml)), command("sort-by", Box::new(sort_by::sort_by)), - Arc::new(Remove), - Arc::new(Open), - Arc::new(Where), - Arc::new(Config), - Arc::new(SkipWhile), - ]); - - context.add_sinks(vec![ - sink("autoview", Box::new(autoview::autoview)), - sink("clip", Box::new(clip::clip)), - sink("save", Box::new(save::save)), - sink("table", Box::new(table::table)), - sink("vtable", Box::new(vtable::vtable)), + static_command(Get), + static_command(Cd), + static_command(Remove), + static_command(Open), + static_command(Where), + static_command(Config), + static_command(SkipWhile), + static_command(Clip), + static_command(Autoview), + // command("save", Box::new(save::save)), + // command("table", Box::new(table::table)), + // command("vtable", Box::new(vtable::vtable)), ]); } let _ = load_plugins(&mut context); @@ -341,17 +331,19 @@ async fn process_line(readline: Result, ctx: &mut Context .map_err(|err| (line.clone(), err))?; match pipeline.commands.last() { - Some(ClassifiedCommand::Sink(_)) => {} Some(ClassifiedCommand::External(_)) => {} - _ => pipeline.commands.push(ClassifiedCommand::Sink(SinkCommand { - command: sink("autoview", Box::new(autoview::autoview)), - name_span: None, - args: hir::Call::new( - Box::new(hir::Expression::synthetic_string("autoview")), - None, - None, - ), - })), + _ => pipeline + .commands + .push(ClassifiedCommand::Internal(InternalCommand { + command: static_command(autoview::Autoview), + name_span: None, + source_map: ctx.source_map, + args: hir::Call::new( + Box::new(hir::Expression::synthetic_string("autoview")), + None, + None, + ), + })), } let mut input = ClassifiedInputStream::new(); @@ -379,18 +371,6 @@ async fn process_line(readline: Result, ctx: &mut Context ) } - (Some(ClassifiedCommand::Sink(SinkCommand { name_span, .. })), Some(_)) => { - return LineResult::Error(line.clone(), ShellError::maybe_labeled_error("Commands like table, save, and autoview must come last in the pipeline", "must come last", name_span)); - } - - (Some(ClassifiedCommand::Sink(left)), None) => { - let input_vec: Vec> = input.objects.into_vec().await; - if let Err(err) = left.run(ctx, input_vec, &Text::from(line)) { - return LineResult::Error(line.clone(), err); - } - break; - } - ( Some(ClassifiedCommand::Internal(left)), Some(ClassifiedCommand::External(_)), @@ -485,7 +465,7 @@ fn classify_command( match context.has_command(name) { true => { let command = context.get_command(name); - let config = command.config(); + let config = command.signature(); trace!(target: "nu::build_pipeline", "classifying {:?}", config); @@ -498,20 +478,8 @@ fn classify_command( args, })) } - false => match context.has_sink(name) { - true => { - let command = context.get_sink(name); - let config = command.config(); - - let args = config.parse_args(call, context.registry(), source)?; - - Ok(ClassifiedCommand::Sink(SinkCommand { - command, - name_span: Some(head.span().clone()), - args, - })) - } - false => { + false => match context.get_command(name).as_ref() { + Command::Static(command) => { let arg_list_strings: Vec> = match call.children() { //Some(args) => args.iter().map(|i| i.as_external_arg(source)).collect(), Some(args) => args diff --git a/src/commands.rs b/src/commands.rs index 9c60aabf1..1d0c5663b 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -42,8 +42,15 @@ crate mod trim; crate mod vtable; crate mod where_; -crate use command::{command, EvaluatedStaticCommandArgs}; +crate use autoview::Autoview; +crate use cd::Cd; +crate use clip::Clip; +crate use command::{ + command, static_command, CallInfo, Command, CommandArgs, EvaluatedStaticCommandArgs, + StaticCommand, UnevaluatedCallInfo, +}; crate use config::Config; +crate use get::Get; crate use open::Open; crate use rm::Remove; crate use skip_while::SkipWhile; diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index 9a708615b..d580294bc 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -1,11 +1,31 @@ -use crate::commands::command::SinkCommandArgs; +use crate::commands::StaticCommand; use crate::context::{SourceMap, SpanSource}; use crate::errors::ShellError; use crate::format::GenericView; use crate::prelude::*; use std::path::Path; -pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { +pub struct Autoview; + +impl StaticCommand for Autoview { + fn name(&self) -> &str { + "autoview" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, autoview)?.run() + } + + fn signature(&self) -> Signature { + Signature::build("autoview").sink() + } +} + +pub fn autoview(args: (), context: RunnableContext) -> Result { if args.input.len() > 0 { if let Spanned { item: Value::Binary(_), @@ -27,7 +47,7 @@ pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { } } - Ok(()) + Ok(OutputStream::empty()) } fn equal_shapes(input: &Vec>) -> bool { diff --git a/src/commands/cd.rs b/src/commands/cd.rs index 06fd327fa..41b462d1d 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -1,27 +1,54 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::prelude::*; use std::env; +use std::path::PathBuf; -pub fn cd(args: CommandArgs, registry: &CommandRegistry) -> Result { - let env = args.env.clone(); - let env = env.lock().unwrap(); - let args = args.evaluate_once(registry)?; - let cwd = env.path().to_path_buf(); +pub struct Cd; - let path = match args.nth(0) { +#[derive(Deserialize)] +pub struct CdArgs { + target: Option>, +} + +impl StaticCommand for Cd { + fn name(&self) -> &str { + "cd" + } + + fn signature(&self) -> Signature { + Signature::build("cd") + .optional("target", SyntaxType::Path) + .filter() + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, cd)?.run() + // cd(args, registry) + } +} + +pub fn cd(CdArgs { target }: CdArgs, context: RunnableContext) -> Result { + let cwd = context.cwd().to_path_buf(); + + let path = match &target { None => match dirs::home_dir() { Some(o) => o, _ => { return Err(ShellError::maybe_labeled_error( "Can not change to home directory", "can not go to home", - args.name_span(), + context.name, )) } }, Some(v) => { - let target = v.as_string()?; - match dunce::canonicalize(cwd.join(target).as_path()) { + // let target = v.item.as_string()?; + match dunce::canonicalize(cwd.join(&v.item()).as_path()) { Ok(p) => p, Err(_) => { return Err(ShellError::labeled_error( @@ -38,11 +65,11 @@ pub fn cd(args: CommandArgs, registry: &CommandRegistry) -> Result {} Err(_) => { - if args.len() > 0 { + if let Some(path) = target { return Err(ShellError::labeled_error( "Can not change to directory", "directory not found", - args.nth(0).unwrap().span.clone(), + path.span, )); } else { return Err(ShellError::string("Can not change to directory")); diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 9abb7570e..254415b52 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -1,6 +1,5 @@ -use crate::commands::command::Sink; +use crate::commands::Command; use crate::context::SourceMap; -use crate::evaluate::Scope; use crate::parser::{hir, Span, Spanned, TokenNode}; use crate::prelude::*; use bytes::{BufMut, BytesMut}; @@ -83,7 +82,6 @@ crate enum ClassifiedCommand { #[allow(unused)] Expr(TokenNode), Internal(InternalCommand), - Sink(SinkCommand), External(ExternalCommand), } @@ -93,34 +91,13 @@ impl ClassifiedCommand { match self { ClassifiedCommand::Expr(token) => token.span(), ClassifiedCommand::Internal(internal) => internal.name_span.into(), - ClassifiedCommand::Sink(sink) => sink.name_span.into(), ClassifiedCommand::External(external) => external.name_span.into(), } } } -crate struct SinkCommand { - crate command: Arc, - crate name_span: Option, - crate args: hir::Call, -} - -impl SinkCommand { - crate fn run( - self, - context: &mut Context, - input: Vec>, - source: &Text, - ) -> Result<(), ShellError> { - let args = self - .args - .evaluate(context.registry(), &Scope::empty(), source)?; - context.run_sink(self.command, self.name_span.clone(), args, input) - } -} - crate struct InternalCommand { - crate command: Arc, + crate command: Arc, crate name_span: Option, crate source_map: SourceMap, crate args: hir::Call, @@ -142,14 +119,16 @@ impl InternalCommand { let objects: InputStream = trace_stream!(target: "nu::trace_stream::internal", "input" = input.objects); - let result = context.run_command( - self.command, - self.name_span.clone(), - self.source_map, - self.args, - source, - objects, - )?; + let result = context + .run_command( + self.command, + self.name_span.clone(), + self.source_map, + self.args, + source, + objects, + ) + .await?; let mut result = result.values; diff --git a/src/commands/clip.rs b/src/commands/clip.rs index c2527234d..0c07e8081 100644 --- a/src/commands/clip.rs +++ b/src/commands/clip.rs @@ -1,25 +1,71 @@ -use crate::commands::command::SinkCommandArgs; +use crate::commands::{CommandArgs, StaticCommand}; +use crate::context::CommandRegistry; use crate::errors::{labelled, ShellError}; +use crate::prelude::*; use clipboard::{ClipboardContext, ClipboardProvider}; +use futures::stream::StreamExt; +use futures_async_stream::async_stream_block; -pub fn clip(args: SinkCommandArgs) -> Result<(), ShellError> { +pub struct Clip; + +#[derive(Deserialize)] +pub struct ClipArgs {} + +impl StaticCommand for Clip { + fn name(&self) -> &str { + "clip" + } + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, clip)?.run() + } + + fn signature(&self) -> Signature { + Signature::build("clip").sink() + } +} + +pub fn clip( + ClipArgs {}: ClipArgs, + RunnableContext { input, name, .. }: RunnableContext, +) -> Result { + let stream = async_stream_block! { + let values: Vec> = input.values.collect().await; + + inner_clip(values, name); + }; + + let stream: BoxStream<'static, ReturnValue> = stream.boxed(); + + Ok(OutputStream::from(stream)) +} + +async fn inner_clip(input: Vec>, name: Option) -> OutputStream { let mut clip_context: ClipboardContext = ClipboardProvider::new().unwrap(); let mut new_copy_data = String::new(); - if args.input.len() > 0 { + if input.len() > 0 { let mut first = true; - for i in args.input.iter() { + for i in input.iter() { if !first { new_copy_data.push_str("\n"); } else { first = false; } - let string = i.as_string().map_err(labelled( - args.name_span(), + let s = i.as_string().map_err(labelled( + name, "Given non-string data", "expected strings from pipeline", - ))?; + )); + + let string: String = match s { + Ok(string) => string, + Err(err) => return OutputStream::one(Err(err)), + }; new_copy_data.push_str(&string); } @@ -27,5 +73,5 @@ pub fn clip(args: SinkCommandArgs) -> Result<(), ShellError> { clip_context.set_contents(new_copy_data).unwrap(); - Ok(()) + OutputStream::empty() } diff --git a/src/commands/command.rs b/src/commands/command.rs index d3cd6ba86..b1622b7b4 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -3,8 +3,9 @@ use crate::errors::ShellError; use crate::evaluate::Scope; use crate::object::Value; use crate::parser::hir; -use crate::parser::{registry, Span, Spanned}; +use crate::parser::{registry, ConfigDeserializer, Span, Spanned}; use crate::prelude::*; +use derive_new::new; use getset::Getters; use serde::{Deserialize, Serialize}; use std::fmt; @@ -80,6 +81,67 @@ impl CommandArgs { pub fn name_span(&self) -> Option { self.call_info.name_span } + + pub fn process<'de, T: Deserialize<'de>>( + self, + registry: &CommandRegistry, + callback: fn(T, RunnableContext) -> Result, + ) -> Result, ShellError> { + let env = self.env.clone(); + let args = self.evaluate_once(registry)?; + let (input, args) = args.split(); + let name_span = args.call_info.name_span; + let mut deserializer = ConfigDeserializer::from_call_node(args); + + Ok(RunnableArgs { + args: T::deserialize(&mut deserializer)?, + context: RunnableContext { + input: input, + env, + name: name_span, + }, + callback, + }) + } +} + +pub struct SinkContext { + pub input: Vec>, + pub env: Arc>, + pub name: Option, +} + +pub struct SinkArgs { + args: T, + context: SinkContext, + callback: fn(T, SinkContext) -> Result<(), ShellError>, +} + +pub struct RunnableContext { + pub input: InputStream, + pub env: Arc>, + pub name: Option, +} + +impl RunnableContext { + pub fn cwd(&self) -> PathBuf { + let env = self.env.clone(); + let env = env.lock().unwrap(); + + env.path.clone() + } +} + +pub struct RunnableArgs { + args: T, + context: RunnableContext, + callback: fn(T, RunnableContext) -> Result, +} + +impl RunnableArgs { + pub fn run(self) -> Result { + (self.callback)(self.args, self.context) + } } pub struct EvaluatedStaticCommandArgs { @@ -120,6 +182,12 @@ impl EvaluatedStaticCommandArgs { (input, args.call_info.args) } + + pub fn split(self) -> (InputStream, EvaluatedCommandArgs) { + let EvaluatedStaticCommandArgs { args, input } = self; + + (input, args) + } } #[derive(Getters)] @@ -155,7 +223,7 @@ impl EvaluatedFilterCommandArgs { } } -#[derive(Getters)] +#[derive(Getters, new)] #[get = "crate"] pub struct EvaluatedCommandArgs { pub host: Arc>, @@ -184,24 +252,21 @@ impl EvaluatedCommandArgs { self.call_info.args.get(name) } + pub fn slice_from(&self, from: usize) -> Vec> { + let positional = &self.call_info.args.positional; + + match positional { + None => vec![], + Some(list) => list[from..].to_vec(), + } + } + #[allow(unused)] pub fn has(&self, name: &str) -> bool { self.call_info.args.has(name) } } -pub struct SinkCommandArgs { - pub ctx: Context, - pub call_info: CallInfo, - pub input: Vec>, -} - -impl SinkCommandArgs { - pub fn name_span(&self) -> Option { - self.call_info.name_span - } -} - #[derive(Debug, Serialize, Deserialize)] pub enum CommandAction { ChangePath(PathBuf), @@ -241,38 +306,56 @@ impl ReturnSuccess { } } -pub trait Command: Send + Sync { +pub trait StaticCommand: Send + Sync { + fn name(&self) -> &str; + fn run( &self, args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result; - fn name(&self) -> &str; - fn config(&self) -> registry::CommandConfig { - registry::CommandConfig { + fn signature(&self) -> Signature { + Signature { name: self.name().to_string(), positional: vec![], rest_positional: true, named: indexmap::IndexMap::new(), is_filter: true, - is_sink: false, } } } -pub trait Sink { - fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError>; - fn name(&self) -> &str; +pub enum Command { + Static(Arc), +} - fn config(&self) -> registry::CommandConfig { - registry::CommandConfig { - name: self.name().to_string(), - positional: vec![], - rest_positional: true, - named: indexmap::IndexMap::new(), - is_filter: false, - is_sink: true, +impl Command { + pub fn name(&self) -> &str { + match self { + Command::Static(command) => command.name(), + } + } + + pub fn is_sink(&self) -> bool { + match self { + Command::Static(..) => false, + } + } + + pub fn signature(&self) -> Signature { + match self { + Command::Static(command) => command.signature(), + } + } + + pub async fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result { + match self { + Command::Static(command) => command.run(args, registry), } } } @@ -283,7 +366,7 @@ pub struct FnFilterCommand { func: fn(EvaluatedFilterCommandArgs) -> Result, } -impl Command for FnFilterCommand { +impl StaticCommand for FnFilterCommand { fn name(&self) -> &str { &self.name } @@ -339,7 +422,11 @@ pub struct FnRawCommand { >, } -impl Command for FnRawCommand { +impl StaticCommand for FnRawCommand { + fn name(&self) -> &str { + &self.name + } + fn run( &self, args: CommandArgs, @@ -347,10 +434,6 @@ impl Command for FnRawCommand { ) -> Result { (self.func)(args, registry) } - - fn name(&self) -> &str { - &self.name - } } pub fn command( @@ -360,45 +443,24 @@ pub fn command( + Send + Sync, >, -) -> Arc { - Arc::new(FnRawCommand { +) -> Arc { + Arc::new(Command::Static(Arc::new(FnRawCommand { name: name.to_string(), func, - }) + }))) +} + +pub fn static_command(command: impl StaticCommand + 'static) -> Arc { + Arc::new(Command::Static(Arc::new(command))) } #[allow(unused)] pub fn filter( name: &str, func: fn(EvaluatedFilterCommandArgs) -> Result, -) -> Arc { - Arc::new(FnFilterCommand { +) -> Arc { + Arc::new(Command::Static(Arc::new(FnFilterCommand { name: name.to_string(), func, - }) -} - -pub struct FnSink { - name: String, - func: Box Result<(), ShellError>>, -} - -impl Sink for FnSink { - fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError> { - (self.func)(args) - } - - fn name(&self) -> &str { - &self.name - } -} - -pub fn sink( - name: &str, - func: Box Result<(), ShellError>>, -) -> Arc { - Arc::new(FnSink { - name: name.to_string(), - func, - }) + }))) } diff --git a/src/commands/config.rs b/src/commands/config.rs index 6daf06209..c0d833402 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,57 +1,61 @@ use crate::prelude::*; -use crate::commands::EvaluatedStaticCommandArgs; +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::object::{config, Value}; use crate::parser::hir::SyntaxType; -use crate::parser::registry::{self, CommandConfig, NamedType}; -use indexmap::IndexMap; -use log::trace; +use crate::parser::registry::{self}; use std::iter::FromIterator; pub struct Config; -impl Command for Config { +#[derive(Deserialize)] +pub struct ConfigArgs { + set: Option<(Spanned, Spanned)>, + get: Option>, + clear: Spanned, + remove: Option>, + path: Spanned, +} + +impl StaticCommand for Config { + fn name(&self) -> &str { + "config" + } + + fn signature(&self) -> Signature { + Signature::build("config") + .named("set", SyntaxType::Any) + .named("get", SyntaxType::Any) + .named("remove", SyntaxType::Any) + .switch("clear") + .switch("path") + .sink() + } + fn run( &self, args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result { - let args = args.evaluate_once(registry)?; - config(args) - } - - fn name(&self) -> &str { - "config" - } - - fn config(&self) -> CommandConfig { - let mut named: IndexMap = IndexMap::new(); - named.insert("set".to_string(), NamedType::Optional(SyntaxType::Any)); - named.insert("get".to_string(), NamedType::Optional(SyntaxType::Any)); - named.insert("clear".to_string(), NamedType::Switch); - - named.insert("remove".to_string(), NamedType::Optional(SyntaxType::Any)); - - CommandConfig { - name: self.name().to_string(), - positional: vec![], - rest_positional: false, - named, - is_sink: true, - is_filter: false, - } + args.process(registry, config)?.run() } } -pub fn config(args: EvaluatedStaticCommandArgs) -> Result { - let mut result = crate::object::config::config(args.name_span())?; +pub fn config( + ConfigArgs { + set, + get, + clear, + remove, + path, + }: ConfigArgs, + RunnableContext { name, .. }: RunnableContext, +) -> Result { + let mut result = crate::object::config::config(name)?; - trace!("{:#?}", args.call_args().positional); - trace!("{:#?}", args.call_args().named); - - if let Some(v) = args.get("get") { - let key = v.as_string()?; + if let Some(v) = get { + let key = v.to_string(); let value = result .get(&key) .ok_or_else(|| ShellError::string(&format!("Missing key {} in config", key)))?; @@ -61,31 +65,38 @@ pub fn config(args: EvaluatedStaticCommandArgs) -> Result Result) -> Spanned { +pub fn convert_toml_value_to_nu_value(v: &toml::Value, span: impl Into) -> Spanned { let span = span.into(); match v { diff --git a/src/commands/get.rs b/src/commands/get.rs index ed5d65635..b90ddd837 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -1,9 +1,33 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::object::Value; -use crate::parser::Span; use crate::prelude::*; -fn get_member(path: &str, span: Span, obj: &Spanned) -> Result, ShellError> { +pub struct Get; + +#[derive(Deserialize)] +pub struct GetArgs { + rest: Vec>, +} + +impl StaticCommand for Get { + fn name(&self) -> &str { + "get" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, get)?.run() + } + fn signature(&self) -> Signature { + Signature::build("get").rest() + } +} + +fn get_member(path: &Spanned, obj: &Spanned) -> Result, ShellError> { let mut current = obj; for p in path.split(".") { match current.get_data_by_key(p) { @@ -12,7 +36,7 @@ fn get_member(path: &str, span: Span, obj: &Spanned) -> Result) -> Result Result { - let args = args.evaluate_once(registry)?; - let span = args.name_span(); - let len = args.len(); - - if len == 0 { - return Err(ShellError::maybe_labeled_error( - "Get requires a field or field path", - "needs parameter", - span, - )); - } - - let amount = args.expect_nth(0)?.as_i64(); - let (input, args) = args.parts(); - let positional = args.positional; - +pub fn get( + GetArgs { rest: fields }: GetArgs, + RunnableContext { input, .. }: RunnableContext, +) -> Result { // If it's a number, get the row instead of the column - if let Ok(amount) = amount { - return Ok(input.values.skip(amount as u64).take(1).from_input_stream()); - } - - let fields: Result, _> = positional - .iter() - .flatten() - .map(|a| (a.as_string().map(|x| (x, a.span)))) - .collect(); - - let fields = fields?; + // if let Some(amount) = amount { + // return Ok(input.values.skip(amount as u64).take(1).from_input_stream()); + // } let stream = input .values .map(move |item| { let mut result = VecDeque::new(); for field in &fields { - match get_member(&field.0, field.1, &item) { + match get_member(field, &item) { Ok(Spanned { item: Value::List(l), .. diff --git a/src/commands/macros.rs b/src/commands/macros.rs index 0fe491e32..8e148de5f 100644 --- a/src/commands/macros.rs +++ b/src/commands/macros.rs @@ -12,7 +12,7 @@ macro_rules! command { Named { $export:tt $args:ident $body:block } Positional { $($number:tt)* } Rest {} - CommandConfig { + Signature { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], optional_positional: vec![ $($optional_positional:tt)* ], @@ -52,8 +52,8 @@ macro_rules! command { stringify!($config_name) } - fn config(&self) -> $crate::parser::registry::CommandConfig { - $crate::parser::registry::CommandConfig { + fn config(&self) -> $crate::parser::registry::Signature { + $crate::parser::registry::Signature { name: self.name().to_string(), positional: vec![$($mandatory_positional)*], rest_positional: false, @@ -82,7 +82,7 @@ macro_rules! command { Named { $export:tt $args:ident $body:block } Positional { $($positional_count:tt)* } Rest { -- $param_name:ident : Switch , $($rest:tt)* } - CommandConfig { + Signature { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], optional_positional: vec![ $($optional_positional:tt)* ], @@ -102,7 +102,7 @@ macro_rules! command { Named { $export $args $body } Positional { $($positional_count)* + 1 } Rest { $($rest)* } - CommandConfig { + Signature { name: $config_name, mandatory_positional: vec![ $($mandatory_positional)* ], optional_positional: vec![ $($optional_positional)* ], @@ -132,7 +132,7 @@ macro_rules! command { Named { $export:tt $args:ident $body:block } Positional { $($positional_count:tt)* } Rest { -- $param_name:ident : $param_kind:ty , $($rest:tt)* } - CommandConfig { + Signature { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], optional_positional: vec![ $($optional_positional:tt)* ], @@ -152,7 +152,7 @@ macro_rules! command { Named { $export $args $body } Positional { $($positional_count)* + 1 } Rest { $($rest)* } - CommandConfig { + Signature { name: $config_name, mandatory_positional: vec![ $($mandatory_positional)* ], optional_positional: vec![ $($optional_positional)* ], @@ -182,7 +182,7 @@ macro_rules! command { Named { $export:tt $args:ident $body:block } Positional { $($positional_count:tt)* } Rest { -- $param_name:ident ? : $param_kind:ty , $($rest:tt)* } - CommandConfig { + Signature { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], optional_positional: vec![ $($optional_positional:tt)* ], @@ -202,7 +202,7 @@ macro_rules! command { Named { $export $args $body } Positional { $($positional_count)* + 1 } Rest { $($rest)* } - CommandConfig { + Signature { name: $config_name, mandatory_positional: vec![ $($mandatory_positional)* ], optional_positional: vec![ $($optional_positional)* ], @@ -232,7 +232,7 @@ macro_rules! command { Named { $export:ident $args:ident $body:block } Positional { $($positional_count:tt)* } Rest { $param_name:ident : Block , $($rest:tt)* } - CommandConfig { + Signature { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], optional_positional: vec![ $($optional_positional:tt)* ], @@ -255,7 +255,7 @@ macro_rules! command { Named { $export $args $body } Positional { $($positional_count)* + 1 } Rest { $($rest)* } - CommandConfig { + Signature { name: $config_name, mandatory_positional: vec![ $($mandatory_positional)* $crate::parser::registry::PositionalType::mandatory_block( stringify!($param_name) @@ -287,7 +287,7 @@ macro_rules! command { Named { $export:ident $args:ident $body:block } Positional { $($positional_count:tt)* } Rest { $param_name:ident : $param_kind:ty , $($rest:tt)* } - CommandConfig { + Signature { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], optional_positional: vec![ $($optional_positional:tt)* ], @@ -310,7 +310,7 @@ macro_rules! command { Named { $export $args $body } Positional { $($positional_count)* + 1 } Rest { $($rest)* } - CommandConfig { + Signature { name: $config_name, mandatory_positional: vec![ $($mandatory_positional)* $crate::parser::registry::PositionalType::mandatory( stringify!($param_name), <$param_kind>::syntax_type() @@ -341,7 +341,7 @@ macro_rules! command { Named { $export $args $body } Positional { 0 } Rest { $($command_rest)* } - CommandConfig { + Signature { name: $config_name, mandatory_positional: vec![], optional_positional: vec![], @@ -377,11 +377,11 @@ macro_rules! command { // stringify!($name) // } - // fn config(&self) -> CommandConfig { + // fn config(&self) -> Signature { // let mut named: IndexMap = IndexMap::new(); // named.insert(stringify!($param).to_string(), NamedType::$kind); - // CommandConfig { + // Signature { // name: self.name().to_string(), // mandatory_positional: vec![], // optional_positional: vec![], diff --git a/src/commands/open.rs b/src/commands/open.rs index 172526e3e..3739e46b1 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -1,9 +1,10 @@ +use crate::commands::StaticCommand; use crate::context::SpanSource; use crate::errors::ShellError; use crate::object::{Primitive, Value}; use crate::parser::hir::SyntaxType; use crate::parser::parse::span::Span; -use crate::parser::registry::{self, CommandConfig, NamedType}; +use crate::parser::registry::{self, Signature}; use crate::prelude::*; use mime::Mime; use std::path::{Path, PathBuf}; @@ -12,75 +13,81 @@ use uuid::Uuid; pub struct Open; -impl Command for Open { +#[derive(Deserialize)] +pub struct OpenArgs { + path: Spanned, + raw: bool, +} + +impl StaticCommand for Open { + fn name(&self) -> &str { + "open" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) + .required("path", SyntaxType::Block) + .switch("raw") + .sink() + } + fn run( &self, args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result { - let env = args.env.clone(); - let args = args.evaluate_once(registry)?; - let path = >::extract(args.expect_nth(0)?)?; - let raw = args.has("raw"); + args.process(registry, run)?.run() + } +} - let span = args.name_span(); +fn run( + OpenArgs { raw, path }: OpenArgs, + RunnableContext { env, name, .. }: RunnableContext, +) -> Result { + let cwd = env.lock().unwrap().path().to_path_buf(); + let full_path = PathBuf::from(cwd); - let cwd = env.lock().unwrap().path().to_path_buf(); - let full_path = PathBuf::from(cwd); + let path_str = path.to_str().ok_or(ShellError::type_error( + "Path", + "invalid path".spanned(path.span), + ))?; - let path_str = path.to_str().ok_or(ShellError::type_error( - "Path", - "invalid path".spanned(path.span), - ))?; + let (file_extension, contents, contents_span, span_source) = + fetch(&full_path, path_str, path.span)?; - let (file_extension, contents, contents_span, span_source) = - fetch(&full_path, path_str, path.span)?; + let file_extension = if raw { None } else { file_extension }; - let file_extension = if raw { None } else { file_extension }; + let mut stream = VecDeque::new(); - let mut stream = VecDeque::new(); + if let Some(uuid) = contents_span.source { + // If we have loaded something, track its source + stream.push_back(ReturnSuccess::action(CommandAction::AddSpanSource( + uuid, + span_source, + ))) + } - if let Some(uuid) = contents_span.source { - // If we have loaded something, track its source - stream.push_back(ReturnSuccess::action(CommandAction::AddSpanSource( - uuid, - span_source, - ))) + match contents { + Value::Primitive(Primitive::String(string)) => { + let value = parse_as_value(file_extension, string, contents_span, name)?; + + match value { + Spanned { + item: Value::List(list), + .. + } => { + for elem in list { + stream.push_back(ReturnSuccess::value(elem)); + } + } + x => stream.push_back(ReturnSuccess::value(x)), + } } - match contents { - Value::Primitive(Primitive::String(string)) => { - let value = parse_as_value(file_extension, string, contents_span, span)?; + other => stream.push_back(ReturnSuccess::value(other.spanned(contents_span))), + }; - match value { - Spanned { - item: Value::List(list), - .. - } => { - for elem in list { - stream.push_back(ReturnSuccess::value(elem)); - } - } - x => stream.push_back(ReturnSuccess::value(x)), - } - } - - other => stream.push_back(ReturnSuccess::value(other.spanned(contents_span))), - }; - - Ok(stream.boxed().to_output_stream()) - } - - fn name(&self) -> &str { - "open" - } - - fn config(&self) -> CommandConfig { - CommandConfig::new(self.name()) - .required("path", SyntaxType::Block) - .named("raw", NamedType::Switch) - .sink() - } + Ok(stream.boxed().to_output_stream()) } // command! { diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index 0b9b060f7..902f4bbef 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -1,7 +1,9 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::parser::registry; use crate::prelude::*; use derive_new::new; +use futures_async_stream::async_stream_block; use serde::{self, Deserialize, Serialize}; use std::io::prelude::*; use std::io::BufReader; @@ -37,10 +39,18 @@ pub enum NuResult { pub struct PluginCommand { name: String, path: String, - config: registry::CommandConfig, + config: registry::Signature, } -impl Command for PluginCommand { +impl StaticCommand for PluginCommand { + fn name(&self) -> &str { + &self.name + } + + fn signature(&self) -> registry::Signature { + self.config.clone() + } + fn run( &self, args: CommandArgs, @@ -48,29 +58,36 @@ impl Command for PluginCommand { ) -> Result { filter_plugin(self.path.clone(), args, registry) } - fn name(&self) -> &str { - &self.name - } - fn config(&self) -> registry::CommandConfig { - self.config.clone() - } } #[derive(new)] pub struct PluginSink { - name: String, path: String, - config: registry::CommandConfig, + config: registry::Signature, } -impl Sink for PluginSink { - fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError> { - sink_plugin(self.path.clone(), args) +impl StaticCommand for PluginSink { + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + let path = self.path.clone(); + + let stream = async_stream_block! { + sink_plugin(path, args).await; + }; + + let stream: BoxStream<'static, ReturnValue> = stream.boxed(); + + Ok(OutputStream::from(stream)) } + fn name(&self) -> &str { - &self.name + &self.config.name } - fn config(&self) -> registry::CommandConfig { + + fn signature(&self) -> registry::Signature { self.config.clone() } } @@ -191,9 +208,10 @@ pub fn filter_plugin( Ok(stream.to_output_stream()) } -pub fn sink_plugin(path: String, args: SinkCommandArgs) -> Result<(), ShellError> { +pub async fn sink_plugin(path: String, args: CommandArgs) -> Result { //use subprocess::Exec; - let request = JsonRpc::new("sink", (args.call_info, args.input)); + let input: Vec> = args.input.values.collect().await; + let request = JsonRpc::new("sink", (args.call_info, input)); let request_raw = serde_json::to_string(&request).unwrap(); let mut tmpfile = tempfile::NamedTempFile::new()?; let _ = writeln!(tmpfile, "{}", request_raw); @@ -206,5 +224,5 @@ pub fn sink_plugin(path: String, args: SinkCommandArgs) -> Result<(), ShellError let _ = child.wait(); - Ok(()) + Ok(OutputStream::empty()) } diff --git a/src/commands/rm.rs b/src/commands/rm.rs index 5a4ca7cac..44403a078 100644 --- a/src/commands/rm.rs +++ b/src/commands/rm.rs @@ -1,63 +1,57 @@ -use crate::commands::EvaluatedStaticCommandArgs; +use crate::commands::{EvaluatedStaticCommandArgs, StaticCommand}; use crate::errors::ShellError; use crate::parser::hir::SyntaxType; -use crate::parser::registry::{CommandConfig, NamedType, PositionalType}; +use crate::parser::registry::{NamedType, PositionalType}; use crate::prelude::*; use indexmap::IndexMap; +use std::path::PathBuf; pub struct Remove; -impl Command for Remove { +#[derive(Deserialize)] +pub struct RemoveArgs { + path: Spanned, + recursive: bool, +} + +impl StaticCommand for Remove { + fn name(&self) -> &str { + "rm" + } + + fn signature(&self) -> Signature { + Signature::build("rm") + .required("path", SyntaxType::Path) + .switch("recursive") + .sink() + } + fn run( &self, args: CommandArgs, registry: &CommandRegistry, ) -> Result { - let env = args.env.clone(); - rm(args.evaluate_once(registry)?, env) - } - - fn name(&self) -> &str { - "rm" - } - - fn config(&self) -> CommandConfig { - let mut named: IndexMap = IndexMap::new(); - named.insert("recursive".to_string(), NamedType::Switch); - - CommandConfig { - name: self.name().to_string(), - positional: vec![PositionalType::mandatory("file", SyntaxType::Path)], - rest_positional: false, - named, - is_sink: true, - is_filter: false, - } + args.process(registry, rm)?.run() } } pub fn rm( - args: EvaluatedStaticCommandArgs, - env: Arc>, + RemoveArgs { path, recursive }: RemoveArgs, + context: RunnableContext, ) -> Result { - let mut full_path = env.lock().unwrap().path().to_path_buf(); + let mut full_path = context.cwd(); - match args - .nth(0) - .ok_or_else(|| ShellError::string(&format!("No file or directory specified")))? - .as_string()? - .as_str() - { + match path.item.to_str().unwrap() { "." | ".." => return Err(ShellError::string("\".\" and \"..\" may not be removed")), file => full_path.push(file), } if full_path.is_dir() { - if !args.has("recursive") { - return Err(ShellError::labeled_error( + if !recursive { + return Err(ShellError::maybe_labeled_error( "is a directory", "", - args.name_span().unwrap(), + context.name, )); } std::fs::remove_dir_all(&full_path).expect("can not remove directory"); diff --git a/src/commands/save.rs b/src/commands/save.rs index 4b3152987..47b838923 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -1,100 +1,119 @@ -use crate::commands::command::SinkCommandArgs; use crate::commands::to_csv::{to_string as to_csv_to_string, value_to_csv_value}; use crate::commands::to_json::value_to_json_value; use crate::commands::to_toml::value_to_toml_value; use crate::commands::to_yaml::value_to_yaml_value; +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::object::{Primitive, Value}; use crate::parser::Spanned; +use crate::prelude::*; +use futures_async_stream::async_stream_block; use std::path::{Path, PathBuf}; -pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> { - if args.call_info.args.positional.is_none() { - return Err(ShellError::maybe_labeled_error( - "Save requires a filepath", - "needs path", - args.name_span(), - )); - } +pub struct Save; - let positional = match args.call_info.args.positional { - None => return Err(ShellError::string("save requires a filepath")), - Some(p) => p, - }; - - let cwd = args.ctx.env.lock().unwrap().path().to_path_buf(); - let mut full_path = PathBuf::from(cwd); - match &(positional[0].item) { - Value::Primitive(Primitive::String(s)) => full_path.push(Path::new(s)), - _ => {} - } - - let save_raw = match positional.get(1) { - Some(Spanned { - item: Value::Primitive(Primitive::String(s)), - .. - }) if s == "--raw" => true, - _ => false, - }; - - let contents = match full_path.extension() { - Some(x) if x == "csv" && !save_raw => { - if args.input.len() != 1 { - return Err(ShellError::string( - "saving to csv requires a single object (or use --raw)", - )); - } - to_csv_to_string(&value_to_csv_value(&args.input[0])).unwrap() - } - Some(x) if x == "toml" && !save_raw => { - if args.input.len() != 1 { - return Err(ShellError::string( - "saving to toml requires a single object (or use --raw)", - )); - } - toml::to_string(&value_to_toml_value(&args.input[0])).unwrap() - } - Some(x) if x == "json" && !save_raw => { - if args.input.len() != 1 { - return Err(ShellError::string( - "saving to json requires a single object (or use --raw)", - )); - } - serde_json::to_string(&value_to_json_value(&args.input[0])).unwrap() - } - Some(x) if x == "yml" && !save_raw => { - if args.input.len() != 1 { - return Err(ShellError::string( - "saving to yml requires a single object (or use --raw)", - )); - } - serde_yaml::to_string(&value_to_yaml_value(&args.input[0])).unwrap() - } - Some(x) if x == "yaml" && !save_raw => { - if args.input.len() != 1 { - return Err(ShellError::string( - "saving to yaml requires a single object (or use --raw)", - )); - } - serde_yaml::to_string(&value_to_yaml_value(&args.input[0])).unwrap() - } - _ => { - let mut save_data = String::new(); - if args.input.len() > 0 { - let mut first = true; - for i in args.input.iter() { - if !first { - save_data.push_str("\n"); - } else { - first = false; - } - save_data.push_str(&i.as_string().unwrap()); - } - } - save_data - } - }; - - let _ = std::fs::write(full_path, contents); - Ok(()) +#[derive(Deserialize)] +struct SaveArgs { + path: Spanned, + raw: bool, +} + +impl StaticCommand for Save { + fn name(&self) -> &str { + "save" + } + + fn signature(&self) -> Signature { + Signature::build("save") + .required("path", SyntaxType::Path) + .switch("raw") + .sink() + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, save)?.run() + } +} + +pub fn save( + SaveArgs { + path, + raw: save_raw, + }: SaveArgs, + context: RunnableContext, +) -> Result { + let mut full_path = context.cwd(); + full_path.push(path.item()); + + let stream = async_stream_block! { + let input: Vec> = context.input.values.collect().await; + + let contents = match full_path.extension() { + Some(x) if x == "csv" && !save_raw => { + if input.len() != 1 { + return Err(ShellError::string( + "saving to csv requires a single object (or use --raw)", + )); + } + to_csv_to_string(&value_to_csv_value(&input[0])).unwrap() + } + Some(x) if x == "toml" && !save_raw => { + if input.len() != 1 { + return Err(ShellError::string( + "saving to toml requires a single object (or use --raw)", + )); + } + toml::to_string(&value_to_toml_value(&input[0])).unwrap() + } + Some(x) if x == "json" && !save_raw => { + if input.len() != 1 { + return Err(ShellError::string( + "saving to json requires a single object (or use --raw)", + )); + } + serde_json::to_string(&value_to_json_value(&input[0])).unwrap() + } + Some(x) if x == "yml" && !save_raw => { + if input.len() != 1 { + return Err(ShellError::string( + "saving to yml requires a single object (or use --raw)", + )); + } + serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap() + } + Some(x) if x == "yaml" && !save_raw => { + if input.len() != 1 { + return Err(ShellError::string( + "saving to yaml requires a single object (or use --raw)", + )); + } + serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap() + } + _ => { + let mut save_data = String::new(); + if input.len() > 0 { + let mut first = true; + for i in input.iter() { + if !first { + save_data.push_str("\n"); + } else { + first = false; + } + save_data.push_str(&i.as_string().unwrap()); + } + } + save_data + } + }; + + let _ = std::fs::write(full_path, contents); + }; + + let stream: BoxStream<'static, ReturnValue> = stream.boxed(); + + Ok(OutputStream::from(stream)) } diff --git a/src/commands/skip_while.rs b/src/commands/skip_while.rs index 47df16a48..c04befd55 100644 --- a/src/commands/skip_while.rs +++ b/src/commands/skip_while.rs @@ -1,54 +1,40 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; -use crate::parser::registry::CommandConfig; -use crate::parser::registry::PositionalType; use crate::prelude::*; pub struct SkipWhile; -impl Command for SkipWhile { +#[derive(Deserialize)] +pub struct SkipWhileArgs { + condition: value::Block, +} + +impl StaticCommand for SkipWhile { + fn name(&self) -> &str { + "skip-while" + } + + fn signature(&self) -> Signature { + Signature::build("skip-while") + .required("condition", SyntaxType::Block) + .filter() + } + fn run( &self, args: CommandArgs, registry: &CommandRegistry, ) -> Result { - skip_while(args, registry) - } - fn name(&self) -> &str { - "skip-while" - } - - fn config(&self) -> CommandConfig { - CommandConfig { - name: self.name().to_string(), - positional: vec![PositionalType::mandatory_block("condition")], - rest_positional: false, - named: indexmap::IndexMap::new(), - is_filter: true, - is_sink: false, - } + args.process(registry, skip_while)?.run() } } pub fn skip_while( - args: CommandArgs, - registry: &CommandRegistry, + SkipWhileArgs { condition }: SkipWhileArgs, + RunnableContext { input, .. }: RunnableContext, ) -> Result { - let args = args.evaluate_once(registry)?; - let block = args.expect_nth(0)?.as_block()?; - let span = args.name_span(); - let len = args.len(); - let input = args.input; - - if len == 0 { - return Err(ShellError::maybe_labeled_error( - "Where requires a condition", - "needs condition", - span, - )); - } - let objects = input.values.skip_while(move |item| { - let result = block.invoke(&item); + let result = condition.invoke(&item); let return_value = match result { Ok(v) if v.is_true() => true, diff --git a/src/commands/table.rs b/src/commands/table.rs index d8e3ca7f2..02ce7cd38 100644 --- a/src/commands/table.rs +++ b/src/commands/table.rs @@ -1,9 +1,8 @@ -use crate::commands::command::SinkCommandArgs; use crate::errors::ShellError; use crate::format::TableView; use crate::prelude::*; -pub fn table(args: SinkCommandArgs) -> Result<(), ShellError> { +pub fn table(args: CommandArgs, context: RunnableContext) -> Result<(), ShellError> { if args.input.len() > 0 { let mut host = args.ctx.host.lock().unwrap(); let view = TableView::from_list(&args.input); diff --git a/src/commands/vtable.rs b/src/commands/vtable.rs index 827170a2f..deb67b9d2 100644 --- a/src/commands/vtable.rs +++ b/src/commands/vtable.rs @@ -1,9 +1,8 @@ -use crate::commands::command::SinkCommandArgs; use crate::errors::ShellError; use crate::format::VTableView; use crate::prelude::*; -pub fn vtable(args: SinkCommandArgs) -> Result<(), ShellError> { +pub fn vtable(args: CommandArgs, context: RunnableContext) -> Result<(), ShellError> { if args.input.len() > 0 { let mut host = args.ctx.host.lock().unwrap(); let view = VTableView::from_list(&args.input); diff --git a/src/commands/where_.rs b/src/commands/where_.rs index d9480aa4e..14b899c83 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -1,75 +1,58 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::object::base as value; use crate::parser::hir::SyntaxType; -use crate::parser::registry::{self, CommandConfig, PositionalType}; +use crate::parser::registry; use crate::prelude::*; use futures::future::ready; -use indexmap::IndexMap; -use log::trace; +use serde::Deserialize; pub struct Where; -impl Command for Where { +#[derive(Deserialize)] +struct WhereArgs { + condition: value::Block, +} + +impl StaticCommand for Where { + fn name(&self) -> &str { + "where" + } + + fn signature(&self) -> registry::Signature { + Signature::build("where") + .required("condition", SyntaxType::Block) + .sink() + } + fn run( &self, args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result { - let args = args.evaluate_once(registry)?; - let condition = value::Block::extract(args.expect_nth(0)?)?; - let input = args.input; - let input: InputStream = - trace_stream!(target: "nu::trace_stream::where", "where input" = input); - - Ok(input - .values - .filter_map(move |item| { - let result = condition.invoke(&item); - - let return_value = match result { - Err(err) => Some(Err(err)), - Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))), - _ => None, - }; - - ready(return_value) - }) - .boxed() - .to_output_stream()) - } - - fn name(&self) -> &str { - "where" - } - - fn config(&self) -> CommandConfig { - CommandConfig { - name: self.name().to_string(), - positional: vec![PositionalType::mandatory("condition", SyntaxType::Block)], - rest_positional: false, - named: IndexMap::default(), - is_sink: true, - is_filter: false, - } + args.process(registry, run)?.run() } } -// command! { -// Where as where(args, condition: Block,) { -// let input = args.input; -// let input: InputStream = trace_stream!(target: "nu::trace_stream::where", "where input" = input); +fn run( + WhereArgs { condition }: WhereArgs, + context: RunnableContext, +) -> Result { + Ok(context + .input + .values + .filter_map(move |item| { + let result = condition.invoke(&item); -// input.values.filter_map(move |item| { -// let result = condition.invoke(&item); + let return_value = match result { + Err(err) => Some(Err(err)), + Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))), + _ => None, + }; -// let return_value = match result { -// Err(err) => Some(Err(err)), -// Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))), -// _ => None, -// }; - -// ready(return_value) -// }) -// } -// } + ready(return_value) + }) + .boxed() + .to_output_stream()) +} diff --git a/src/context.rs b/src/context.rs index 1eed5d39b..75053f0c5 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,4 @@ -use crate::commands::command::{CallInfo, Sink, SinkCommandArgs, UnevaluatedCallInfo}; +use crate::commands::{CallInfo, Command, StaticCommand, UnevaluatedCallInfo}; use crate::parser::{hir, registry, Span}; use crate::prelude::*; @@ -37,7 +37,7 @@ impl SourceMap { #[derive(Clone, new)] pub struct CommandRegistry { #[new(value = "Arc::new(Mutex::new(IndexMap::default()))")] - registry: Arc>>>, + registry: Arc>>>, } impl CommandRegistry { @@ -47,7 +47,7 @@ impl CommandRegistry { } } - fn get_command(&self, name: &str) -> Option> { + fn get_command(&self, name: &str) -> Option> { let registry = self.registry.lock().unwrap(); registry.get(name).map(|c| c.clone()) @@ -59,7 +59,7 @@ impl CommandRegistry { registry.contains_key(name) } - fn insert(&mut self, name: impl Into, command: Arc) { + fn insert(&mut self, name: impl Into, command: Arc) { let mut registry = self.registry.lock().unwrap(); registry.insert(name.into(), command); } @@ -73,7 +73,6 @@ impl CommandRegistry { #[derive(Clone)] pub struct Context { registry: CommandRegistry, - sinks: IndexMap>, crate source_map: SourceMap, crate host: Arc>, crate env: Arc>, @@ -87,57 +86,22 @@ impl Context { crate fn basic() -> Result> { Ok(Context { registry: CommandRegistry::new(), - sinks: indexmap::IndexMap::new(), source_map: SourceMap::new(), host: Arc::new(Mutex::new(crate::env::host::BasicHost)), env: Arc::new(Mutex::new(Environment::basic()?)), }) } - pub fn add_commands(&mut self, commands: Vec>) { + pub fn add_commands(&mut self, commands: Vec>) { for command in commands { self.registry.insert(command.name().to_string(), command); } } - pub fn add_sinks(&mut self, sinks: Vec>) { - for sink in sinks { - self.sinks.insert(sink.name().to_string(), sink); - } - } - pub fn add_span_source(&mut self, uuid: Uuid, span_source: SpanSource) { self.source_map.insert(uuid, span_source); } - crate fn has_sink(&self, name: &str) -> bool { - self.sinks.contains_key(name) - } - - crate fn get_sink(&self, name: &str) -> Arc { - self.sinks.get(name).unwrap().clone() - } - - crate fn run_sink( - &mut self, - command: Arc, - name_span: Option, - args: registry::EvaluatedArgs, - input: Vec>, - ) -> Result<(), ShellError> { - let command_args = SinkCommandArgs { - ctx: self.clone(), - call_info: CallInfo { - name_span, - source_map: self.source_map.clone(), - args, - }, - input, - }; - - command.run(command_args) - } - pub fn clone_commands(&self) -> CommandRegistry { self.registry.clone() } @@ -146,13 +110,13 @@ impl Context { self.registry.has(name) } - crate fn get_command(&self, name: &str) -> Arc { + crate fn get_command(&self, name: &str) -> Arc { self.registry.get_command(name).unwrap() } - crate fn run_command( + crate async fn run_command( &mut self, - command: Arc, + command: Arc, name_span: Option, source_map: SourceMap, args: hir::Call, @@ -161,7 +125,7 @@ impl Context { ) -> Result { let command_args = self.command_args(args, input, source, source_map, name_span); - command.run(command_args, self.registry()) + command.run(command_args, self.registry()).await } fn call_info( diff --git a/src/errors.rs b/src/errors.rs index 8d9f44fd9..1e2291aa5 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -62,6 +62,15 @@ pub struct ShellError { cause: Option>, } +impl serde::de::Error for ShellError { + fn custom(msg: T) -> Self + where + T: std::fmt::Display, + { + ShellError::string(msg.to_string()) + } +} + impl ShellError { crate fn type_error( expected: impl Into, @@ -351,16 +360,6 @@ impl std::convert::From for ShellError { } } -impl std::convert::From for ShellError { - fn from(_input: futures_sink::VecSinkError) -> ShellError { - ProximateShellError::String(StringError { - title: format!("Unexpected Vec Sink Error"), - error: Value::nothing(), - }) - .start() - } -} - impl std::convert::From for ShellError { fn from(input: subprocess::PopenError) -> ShellError { ProximateShellError::String(StringError { diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 563ccb186..5507ebc2a 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -55,6 +55,16 @@ crate fn evaluate_baseline_expr( )), } } + RawExpression::List(list) => { + let mut exprs = vec![]; + + for expr in list { + let expr = evaluate_baseline_expr(expr, registry, scope, source)?; + exprs.push(expr); + } + + Ok(Value::List(exprs).spanned(expr.span())) + } RawExpression::Block(block) => Ok(Spanned::from_item( Value::Block(Block::new(block.clone(), source.clone(), *expr.span())), expr.span(), diff --git a/src/lib.rs b/src/lib.rs index 813de129b..919a809a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,15 @@ #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] #![feature(async_await)] +#![feature(generators)] #![feature(try_trait)] #![feature(bind_by_move_pattern_guards)] #![feature(box_syntax)] #![feature(type_ascription)] +#![feature(core_intrinsics)] #![feature(option_flattening)] +#![feature(specialization)] +#![feature(proc_macro_hygiene)] #[macro_use] mod prelude; @@ -37,4 +41,4 @@ pub use cli::cli; pub use errors::ShellError; pub use object::base::{Primitive, Value}; pub use parser::parse::text::Text; -pub use parser::registry::{CommandConfig, EvaluatedArgs, NamedType, PositionalType}; +pub use parser::registry::{EvaluatedArgs, NamedType, PositionalType, Signature}; diff --git a/src/object.rs b/src/object.rs index 08f660fda..f094c63a1 100644 --- a/src/object.rs +++ b/src/object.rs @@ -6,6 +6,6 @@ crate mod into; crate mod process; crate mod types; -crate use base::{Primitive, Value}; +crate use base::{Block, Primitive, Switch, Value}; crate use dict::{Dictionary, SpannedDictBuilder}; crate use files::dir_entry_dict; diff --git a/src/object/base.rs b/src/object/base.rs index 1a49f97a1..db534d46a 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -10,7 +10,7 @@ use chrono::{DateTime, Utc}; use chrono_humanize::Humanize; use derive_new::new; use ordered_float::OrderedFloat; -use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use std::fmt; use std::path::PathBuf; use std::time::SystemTime; @@ -123,42 +123,13 @@ pub struct Operation { crate right: Value, } -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new)] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Serialize, Deserialize, new)] pub struct Block { crate expressions: Vec, crate source: Text, crate span: Span, } -impl Serialize for Block { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut seq = serializer.serialize_seq(None)?; - - let list = self - .expressions - .iter() - .map(|e| e.source(&self.source.clone())); - - for item in list { - seq.serialize_element(item.as_ref())?; - } - - seq.end() - } -} - -impl Deserialize<'de> for Block { - fn deserialize(_deserializer: D) -> Result - where - D: Deserializer<'de>, - { - unimplemented!("deserialize block") - } -} - impl Block { pub fn invoke(&self, value: &Spanned) -> Result, ShellError> { let scope = Scope::new(value.clone()); @@ -260,6 +231,7 @@ impl std::convert::TryFrom<&'a Spanned> for i64 { } } +#[derive(Serialize, Deserialize)] pub enum Switch { Present, Absent, diff --git a/src/object/config.rs b/src/object/config.rs index 0860feb94..bffeff0ef 100644 --- a/src/object/config.rs +++ b/src/object/config.rs @@ -1,12 +1,15 @@ +use crate::commands::from_toml::convert_toml_value_to_nu_value; +use crate::commands::to_toml::value_to_toml_value; use crate::errors::ShellError; +use crate::object::{Dictionary, Value}; use crate::prelude::*; use app_dirs::*; use indexmap::IndexMap; use log::trace; -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use std::fs::{self, OpenOptions}; use std::io; -use std::path::Path; +use std::path::{Path, PathBuf}; const APP_INFO: AppInfo = AppInfo { name: "nu", @@ -16,7 +19,14 @@ const APP_INFO: AppInfo = AppInfo { #[derive(Deserialize, Serialize)] struct Config { #[serde(flatten)] - extra: IndexMap>, + extra: IndexMap, +} + +crate fn config_path() -> Result { + let location = app_root(AppDataType::UserConfig, &APP_INFO) + .map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?; + + Ok(location.join("config.toml")) } crate fn write_config(config: &IndexMap>) -> Result<(), ShellError> { @@ -26,9 +36,9 @@ crate fn write_config(config: &IndexMap>) -> Result<(), S let filename = location.join("config.toml"); touch(&filename)?; - let contents = toml::to_string(&Config { - extra: config.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), - })?; + let contents = value_to_toml_value(&Value::Object(Dictionary::new(config.clone()))); + + let contents = toml::to_string(&contents)?; fs::write(&filename, &contents)?; @@ -50,10 +60,18 @@ crate fn config(span: impl Into) -> Result .map(|v| v.spanned(span)) .map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?; - let parsed: Config = toml::from_str(&contents) + let parsed: toml::Value = toml::from_str(&contents) .map_err(|err| ShellError::string(&format!("Couldn't parse config file:\n{}", err)))?; - Ok(parsed.extra) + let value = convert_toml_value_to_nu_value(&parsed, span); + + match value.item { + Value::Object(Dictionary { entries }) => Ok(entries), + other => Err(ShellError::type_error( + "Dictionary", + other.type_name().spanned(value.span), + )), + } } // A simple implementation of `% touch path` (ignores existing files) diff --git a/src/object/types.rs b/src/object/types.rs index 0264a3376..5ee3784d4 100644 --- a/src/object/types.rs +++ b/src/object/types.rs @@ -1,16 +1,9 @@ use crate::object::base as value; use crate::parser::hir; use crate::prelude::*; -use derive_new::new; -use serde_derive::Deserialize; +use log::trace; use std::path::PathBuf; -pub trait Type: std::fmt::Debug + Send { - type Extractor: ExtractType; - - fn name(&self) -> &'static str; -} - pub trait ExtractType: Sized { fn extract(value: &Spanned) -> Result; fn check(value: &'value Spanned) -> Result<&'value Spanned, ShellError>; @@ -19,8 +12,120 @@ pub trait ExtractType: Sized { } } +impl ExtractType for T { + default fn extract(_value: &Spanned) -> Result { + let name = unsafe { std::intrinsics::type_name::() }; + Err(ShellError::unimplemented(format!( + " ExtractType for {}", + name + ))) + } + + default fn check(_value: &'value Spanned) -> Result<&'value Spanned, ShellError> { + Err(ShellError::unimplemented("ExtractType for T")) + } + + default fn syntax_type() -> hir::SyntaxType { + hir::SyntaxType::Any + } +} + +impl ExtractType for Vec> { + fn extract(value: &Spanned) -> Result { + let name = unsafe { std::intrinsics::type_name::() }; + trace!(" Extracting {:?} for Vec<{}>", value, name); + + match value.item() { + Value::List(items) => { + let mut out = vec![]; + + for item in items { + out.push(T::extract(item)?.spanned(item.span)); + } + + Ok(out) + } + other => Err(ShellError::type_error( + "Vec", + other.type_name().spanned(value.span), + )), + } + } + + fn check(value: &'value Spanned) -> Result<&'value Spanned, ShellError> { + match value.item() { + Value::List(_) => Ok(value), + other => Err(ShellError::type_error( + "Vec", + other.type_name().spanned(value.span), + )), + } + } + + fn syntax_type() -> hir::SyntaxType { + hir::SyntaxType::List + } +} + +impl ExtractType for (T, U) { + fn extract(value: &Spanned) -> Result<(T, U), ShellError> { + let t_name = unsafe { std::intrinsics::type_name::() }; + let u_name = unsafe { std::intrinsics::type_name::() }; + + trace!("Extracting {:?} for ({}, {})", value, t_name, u_name); + + match value.item() { + Value::List(items) => { + if items.len() == 2 { + let first = &items[0]; + let second = &items[1]; + + Ok((T::extract(first)?, U::extract(second)?)) + } else { + Err(ShellError::type_error( + "two-element-tuple", + "not-two".spanned(value.span), + )) + } + } + other => Err(ShellError::type_error( + "two-element-tuple", + other.type_name().spanned(value.span), + )), + } + } +} + +impl ExtractType for Option { + fn extract(value: &Spanned) -> Result, ShellError> { + let name = unsafe { std::intrinsics::type_name::() }; + trace!("