mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 07:00:37 +02:00
Compare commits
96 Commits
Author | SHA1 | Date | |
---|---|---|---|
b93b80ccaa | |||
48128c9db6 | |||
6dafaa197d | |||
1634d8e087 | |||
7a583083b8 | |||
75156ab0c9 | |||
9fd6923821 | |||
91a929b2a9 | |||
0f8e31af06 | |||
bd71c2f34d | |||
001123dbd6 | |||
cfee151d4e | |||
fc59291191 | |||
4fc05cac56 | |||
cc4616f25b | |||
e82fbb7bcf | |||
8cd639f6a2 | |||
a8f555856a | |||
3792562046 | |||
d05c48a1d7 | |||
36cc5eb933 | |||
f9f74a0f7d | |||
77f42931ff | |||
73f62266c6 | |||
df2f3d25b0 | |||
599c43ce04 | |||
5c2199e7f4 | |||
3ad4e0348f | |||
02d5729941 | |||
ce35689d2e | |||
da81e21bf2 | |||
3b2ed7631f | |||
1a46e70dfb | |||
0fc9b6cfa2 | |||
61768aa2fd | |||
ea5bf9db36 | |||
9d24afcfe3 | |||
033df9457b | |||
d8e105fe34 | |||
d34068da18 | |||
611103d211 | |||
528c1c5fd8 | |||
f73732bf1e | |||
fd7875e572 | |||
e8bc319f08 | |||
a92ff57270 | |||
004230d02d | |||
a148c640b2 | |||
ea0205f2ff | |||
005649b6fc | |||
e09e3b01d6 | |||
fc15e0e27d | |||
b2fe5fabb1 | |||
52d69bb021 | |||
5f550a355b | |||
dbecbdccd4 | |||
734877338d | |||
2e439ca77f | |||
a853880e07 | |||
93f3ed98e1 | |||
a131eddf54 | |||
b19a7aa8a6 | |||
f5aa53c530 | |||
80f5e14512 | |||
41390cd963 | |||
0b5e131410 | |||
556596bce8 | |||
b791d1ab0d | |||
ac070ae942 | |||
111ad868a7 | |||
a7274115d0 | |||
81160bcefb | |||
2880109f31 | |||
09a1f5acb9 | |||
5fcf11fcb0 | |||
42fac722bb | |||
073e5727c6 | |||
ad1c4f5e39 | |||
dc8a68c98f | |||
e5621dea58 | |||
00acf22f5f | |||
4c09716ad8 | |||
1c941557c3 | |||
28e1a7915d | |||
4bc9d9fd3b | |||
e278ca61d1 | |||
2146ede15d | |||
e737222a5d | |||
f03f1949bf | |||
0fe6c7c558 | |||
b13202bbfc | |||
90fae903ce | |||
06b154f4b2 | |||
419a0665c8 | |||
387098fc87 | |||
c42b588782 |
322
Cargo.lock
generated
322
Cargo.lock
generated
@ -184,13 +184,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "async-attributes"
|
||||
version = "1.1.2"
|
||||
name = "as-slice"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
|
||||
checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0"
|
||||
dependencies = [
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"generic-array 0.12.4",
|
||||
"generic-array 0.13.3",
|
||||
"generic-array 0.14.4",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -281,7 +283,7 @@ checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -290,7 +292,6 @@ version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9f06685bad74e0570f5213741bea82158279a4103d988e57bfada11ad230341"
|
||||
dependencies = [
|
||||
"async-attributes",
|
||||
"async-channel",
|
||||
"async-global-executor",
|
||||
"async-io",
|
||||
@ -326,7 +327,7 @@ checksum = "36ea56748e10732c49404c153638a15ec3d6211ec5ff35d9bb20e13b93576adf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -564,9 +565,9 @@ checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
|
||||
|
||||
[[package]]
|
||||
name = "byte-unit"
|
||||
version = "4.0.9"
|
||||
version = "4.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c8758c32833faaae35b24a73d332e62d0528e89076ae841c63940e37008b153"
|
||||
checksum = "b9520900471c3a9bbcfe0fd4c7b6bcfeff41b20a76cf91c59b7474b09be1ee27"
|
||||
dependencies = [
|
||||
"utf8-width",
|
||||
]
|
||||
@ -579,9 +580,9 @@ checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.2"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
@ -780,6 +781,12 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "common-path"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101"
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "1.2.2"
|
||||
@ -1104,7 +1111,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"smallvec 1.6.1",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1114,7 +1121,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e"
|
||||
dependencies = [
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1146,7 +1153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19"
|
||||
dependencies = [
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1254,7 +1261,7 @@ checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1276,7 +1283,7 @@ checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1576,7 +1583,7 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@ -1855,7 +1862,7 @@ dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1972,6 +1979,24 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[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.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
@ -2013,7 +2038,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2045,6 +2070,12 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gjson"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "864178e25a00c41404f1728997c9a21a7b746be1faefe6ce4dc41eb48bb4234f"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
@ -2107,6 +2138,15 @@ dependencies = [
|
||||
"regex 1.4.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.9.1"
|
||||
@ -2125,6 +2165,18 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422"
|
||||
dependencies = [
|
||||
"as-slice",
|
||||
"generic-array 0.14.4",
|
||||
"hash32",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.2"
|
||||
@ -2206,7 +2258,7 @@ dependencies = [
|
||||
"markup5ever",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2382,18 +2434,6 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ichwh"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea685d38f1becb4f0a04e6cbff9256c6c2cd5e5905563b251401d1c13d12c654"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"cfg-if 0.1.10",
|
||||
"futures 0.3.13",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.2"
|
||||
@ -3075,7 +3115,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"ctrlc",
|
||||
@ -3104,6 +3144,7 @@ dependencies = [
|
||||
"nu_plugin_match",
|
||||
"nu_plugin_post",
|
||||
"nu_plugin_ps",
|
||||
"nu_plugin_query_json",
|
||||
"nu_plugin_s3",
|
||||
"nu_plugin_selector",
|
||||
"nu_plugin_start",
|
||||
@ -3120,6 +3161,15 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bd69a141e8fdfa5ac882d8b816db2b9ad138ef7e3baa7cb753a9b3789aa8c7e"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"doc-comment",
|
||||
"regex 1.4.3",
|
||||
@ -3130,7 +3180,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-cli"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"arboard",
|
||||
@ -3163,13 +3213,12 @@ dependencies = [
|
||||
"glob",
|
||||
"htmlescape",
|
||||
"ical",
|
||||
"ichwh",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"lazy_static 1.4.0",
|
||||
"log 0.4.14",
|
||||
"meval",
|
||||
"nu-ansi-term",
|
||||
"nu-ansi-term 0.31.0",
|
||||
"nu-command",
|
||||
"nu-data",
|
||||
"nu-engine",
|
||||
@ -3177,6 +3226,7 @@ dependencies = [
|
||||
"nu-json",
|
||||
"nu-parser",
|
||||
"nu-plugin",
|
||||
"nu-pretty-hex",
|
||||
"nu-protocol",
|
||||
"nu-source",
|
||||
"nu-stream",
|
||||
@ -3188,7 +3238,6 @@ dependencies = [
|
||||
"num-traits 0.2.14",
|
||||
"parking_lot 0.11.1",
|
||||
"pin-utils",
|
||||
"pretty-hex",
|
||||
"ptree",
|
||||
"query_interface",
|
||||
"quick-xml 0.21.0",
|
||||
@ -3231,12 +3280,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-command"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"arboard",
|
||||
"async-recursion",
|
||||
"async-trait",
|
||||
"base64 0.13.0",
|
||||
"bigdecimal",
|
||||
"byte-unit",
|
||||
@ -3259,14 +3306,11 @@ dependencies = [
|
||||
"filesize",
|
||||
"fs_extra",
|
||||
"futures 0.3.13",
|
||||
"futures-util",
|
||||
"futures_codec",
|
||||
"getset",
|
||||
"glob",
|
||||
"hamcrest2",
|
||||
"htmlescape",
|
||||
"ical",
|
||||
"ichwh",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"lazy_static 1.4.0",
|
||||
@ -3274,13 +3318,14 @@ dependencies = [
|
||||
"md5 0.7.0",
|
||||
"meval",
|
||||
"minus",
|
||||
"nu-ansi-term",
|
||||
"nu-ansi-term 0.31.0",
|
||||
"nu-data",
|
||||
"nu-engine",
|
||||
"nu-errors",
|
||||
"nu-json",
|
||||
"nu-parser",
|
||||
"nu-plugin",
|
||||
"nu-pretty-hex",
|
||||
"nu-protocol",
|
||||
"nu-source",
|
||||
"nu-stream",
|
||||
@ -3292,7 +3337,6 @@ dependencies = [
|
||||
"num-traits 0.2.14",
|
||||
"parking_lot 0.11.1",
|
||||
"pin-utils",
|
||||
"pretty-hex",
|
||||
"ptree",
|
||||
"query_interface",
|
||||
"quick-xml 0.21.0",
|
||||
@ -3335,18 +3379,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-data"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"byte-unit",
|
||||
"chrono",
|
||||
"common-path",
|
||||
"derive-new",
|
||||
"directories-next",
|
||||
"dirs-next",
|
||||
"getset",
|
||||
"indexmap",
|
||||
"log 0.4.14",
|
||||
"nu-ansi-term",
|
||||
"nu-ansi-term 0.31.0",
|
||||
"nu-errors",
|
||||
"nu-protocol",
|
||||
"nu-source",
|
||||
@ -3358,21 +3403,26 @@ dependencies = [
|
||||
"num-traits 0.2.14",
|
||||
"query_interface",
|
||||
"serde 1.0.124",
|
||||
"sha2 0.9.3",
|
||||
"toml",
|
||||
"users",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-engine"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"async-recursion",
|
||||
"async-trait",
|
||||
"bigdecimal",
|
||||
"bytes 0.5.6",
|
||||
"chrono",
|
||||
"codespan-reporting",
|
||||
"derive-new",
|
||||
"dirs-next",
|
||||
"dunce",
|
||||
"dyn-clone",
|
||||
"encoding_rs",
|
||||
"filesize",
|
||||
"fs_extra",
|
||||
@ -3385,7 +3435,7 @@ dependencies = [
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"log 0.4.14",
|
||||
"nu-ansi-term",
|
||||
"nu-ansi-term 0.31.0",
|
||||
"nu-data",
|
||||
"nu-errors",
|
||||
"nu-parser",
|
||||
@ -3395,6 +3445,9 @@ dependencies = [
|
||||
"nu-stream",
|
||||
"nu-test-support",
|
||||
"nu-value-ext",
|
||||
"num-bigint 0.3.2",
|
||||
"num-format",
|
||||
"num-traits 0.2.14",
|
||||
"parking_lot 0.11.1",
|
||||
"rayon",
|
||||
"serde 1.0.124",
|
||||
@ -3410,14 +3463,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-errors"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"codespan-reporting",
|
||||
"derive-new",
|
||||
"getset",
|
||||
"glob",
|
||||
"nu-ansi-term",
|
||||
"nu-ansi-term 0.31.0",
|
||||
"nu-source",
|
||||
"num-bigint 0.3.2",
|
||||
"num-traits 0.2.14",
|
||||
@ -3429,7 +3482,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-json"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"dunce",
|
||||
"lazy_static 1.4.0",
|
||||
@ -3443,7 +3496,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-parser"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"codespan-reporting",
|
||||
@ -3466,7 +3519,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-plugin"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"indexmap",
|
||||
@ -3480,9 +3533,18 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-pretty-hex"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"heapless",
|
||||
"nu-ansi-term 0.29.0",
|
||||
"rand 0.8.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-protocol"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"byte-unit",
|
||||
@ -3505,7 +3567,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-source"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"derive-new",
|
||||
"getset",
|
||||
@ -3516,7 +3578,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-stream"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"futures 0.3.13",
|
||||
"nu-errors",
|
||||
@ -3526,16 +3588,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-table"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
"nu-ansi-term 0.31.0",
|
||||
"regex 1.4.3",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-test-support"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"chrono",
|
||||
@ -3554,7 +3616,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-value-ext"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itertools",
|
||||
@ -3566,23 +3628,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_binaryview"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"crossterm 0.19.0",
|
||||
"image 0.22.5",
|
||||
"neso",
|
||||
"nu-ansi-term",
|
||||
"nu-ansi-term 0.31.0",
|
||||
"nu-errors",
|
||||
"nu-plugin",
|
||||
"nu-pretty-hex",
|
||||
"nu-protocol",
|
||||
"nu-source",
|
||||
"pretty-hex",
|
||||
"rawkey",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_chart"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"crossterm 0.19.0",
|
||||
"nu-cli",
|
||||
@ -3597,7 +3659,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_fetch"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
"futures 0.3.13",
|
||||
@ -3612,7 +3674,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_from_bson"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"bson",
|
||||
@ -3626,7 +3688,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_from_sqlite"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"nu-errors",
|
||||
@ -3641,7 +3703,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_inc"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"nu-errors",
|
||||
"nu-plugin",
|
||||
@ -3654,7 +3716,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_match"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"nu-errors",
|
||||
"nu-plugin",
|
||||
@ -3665,7 +3727,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_post"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
"futures 0.3.13",
|
||||
@ -3681,7 +3743,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_ps"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"futures 0.3.13",
|
||||
"futures-timer",
|
||||
@ -3693,9 +3755,21 @@ dependencies = [
|
||||
"sysinfo",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_query_json"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"gjson",
|
||||
"nu-errors",
|
||||
"nu-plugin",
|
||||
"nu-protocol",
|
||||
"nu-source",
|
||||
"nu-test-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_s3"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"futures 0.3.13",
|
||||
"nu-errors",
|
||||
@ -3707,7 +3781,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_selector"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"nipper",
|
||||
"nu-errors",
|
||||
@ -3719,7 +3793,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_start"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"nu-errors",
|
||||
@ -3728,11 +3802,12 @@ dependencies = [
|
||||
"nu-source",
|
||||
"open",
|
||||
"url",
|
||||
"webbrowser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_sys"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"futures 0.3.13",
|
||||
"futures-util",
|
||||
@ -3746,10 +3821,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_textview"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"bat",
|
||||
"nu-ansi-term",
|
||||
"nu-ansi-term 0.31.0",
|
||||
"nu-data",
|
||||
"nu-errors",
|
||||
"nu-plugin",
|
||||
@ -3761,7 +3836,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_to_bson"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"bson",
|
||||
"nu-errors",
|
||||
@ -3774,7 +3849,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_to_sqlite"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"hex 0.4.3",
|
||||
"nu-errors",
|
||||
@ -3789,7 +3864,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_tree"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"derive-new",
|
||||
"nu-errors",
|
||||
@ -3801,7 +3876,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_xpath"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"indexmap",
|
||||
@ -4015,9 +4090,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "open"
|
||||
version = "1.5.1"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2033f93630dd4b04768ecf5e16bcd3002a89e1e1dbef375bf290dd67e2b7a4d"
|
||||
checksum = "a7e9f1bdf15cd1f5a00cc9002a733a6ee6d0ff562491852d59652471c4a389f7"
|
||||
dependencies = [
|
||||
"which",
|
||||
"winapi 0.3.9",
|
||||
@ -4219,7 +4294,7 @@ dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4257,7 +4332,7 @@ checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4268,7 +4343,7 @@ checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4378,12 +4453,6 @@ dependencies = [
|
||||
"typed-arena",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty-hex"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.4.0"
|
||||
@ -4403,7 +4472,7 @@ dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
@ -4517,7 +4586,7 @@ checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4939,7 +5008,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"rust-embed-utils",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@ -5235,7 +5304,7 @@ checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5346,7 +5415,7 @@ checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5511,7 +5580,7 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5585,7 +5654,7 @@ dependencies = [
|
||||
"quote 1.0.9",
|
||||
"serde 1.0.124",
|
||||
"serde_derive",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5601,7 +5670,7 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha1 0.6.0",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5740,9 +5809,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.62"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512"
|
||||
checksum = "8fd9bc7ccc2688b3344c2f48b9b546648b25ce0b20fc717ee7fa7981a8ca9717"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
@ -5766,7 +5835,7 @@ checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
"unicode-xid 0.2.1",
|
||||
]
|
||||
|
||||
@ -5795,9 +5864,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.16.4"
|
||||
version = "0.16.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c280c91abd1aed2e36be1bc8f56fbc7a2acbb2b58fbcac9641510179cc72dd9"
|
||||
checksum = "567e910ef0207be81a4e1bb0491e9a8d9866cf45b20fe1a52c03d347da9ea51b"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"core-foundation-sys",
|
||||
@ -5913,7 +5982,7 @@ checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5991,7 +6060,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"standback",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6151,7 +6220,7 @@ checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6340,13 +6409,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.13"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07"
|
||||
checksum = "41768be5b9f3489491825f56f01f25290aa1d3e7cc97e182d4d34360493ba6fa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6686,7 +6755,7 @@ dependencies = [
|
||||
"log 0.4.14",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -6736,7 +6805,7 @@ checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.62",
|
||||
"syn 1.0.63",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -6757,6 +6826,17 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webbrowser"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecad156490d6b620308ed411cfee90d280b3cbd13e189ea0d3fada8acc89158a"
|
||||
dependencies = [
|
||||
"web-sys",
|
||||
"widestring",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.4"
|
||||
@ -6774,14 +6854,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.0.2"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87c14ef7e1b8b8ecfc75d5eca37949410046e66f15d185c01d70824f1f8111ef"
|
||||
checksum = "b55551e42cbdf2ce2bedd2203d0cc08dba002c27510f86dab6d0ce304cba3dfe"
|
||||
dependencies = [
|
||||
"either",
|
||||
"libc",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
|
||||
|
||||
[[package]]
|
||||
name = "wild"
|
||||
version = "2.0.4"
|
||||
|
84
Cargo.toml
84
Cargo.toml
@ -10,7 +10,7 @@ license = "MIT"
|
||||
name = "nu"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
|
||||
[workspace]
|
||||
members = ["crates/*/"]
|
||||
@ -18,35 +18,36 @@ members = ["crates/*/"]
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nu-cli = { version = "0.29.0", path = "./crates/nu-cli", default-features = false }
|
||||
nu-command = { version = "0.29.0", path = "./crates/nu-command" }
|
||||
nu-data = { version = "0.29.0", path = "./crates/nu-data" }
|
||||
nu-engine = { version = "0.29.0", path = "./crates/nu-engine" }
|
||||
nu-errors = { version = "0.29.0", path = "./crates/nu-errors" }
|
||||
nu-parser = { version = "0.29.0", path = "./crates/nu-parser" }
|
||||
nu-plugin = { version = "0.29.0", path = "./crates/nu-plugin" }
|
||||
nu-protocol = { version = "0.29.0", path = "./crates/nu-protocol" }
|
||||
nu-source = { version = "0.29.0", path = "./crates/nu-source" }
|
||||
nu-value-ext = { version = "0.29.0", path = "./crates/nu-value-ext" }
|
||||
nu-cli = { version = "0.31.0", path = "./crates/nu-cli", default-features = false }
|
||||
nu-command = { version = "0.31.0", path = "./crates/nu-command" }
|
||||
nu-data = { version = "0.31.0", path = "./crates/nu-data" }
|
||||
nu-engine = { version = "0.31.0", path = "./crates/nu-engine" }
|
||||
nu-errors = { version = "0.31.0", path = "./crates/nu-errors" }
|
||||
nu-parser = { version = "0.31.0", path = "./crates/nu-parser" }
|
||||
nu-plugin = { version = "0.31.0", path = "./crates/nu-plugin" }
|
||||
nu-protocol = { version = "0.31.0", path = "./crates/nu-protocol" }
|
||||
nu-source = { version = "0.31.0", path = "./crates/nu-source" }
|
||||
nu-value-ext = { version = "0.31.0", path = "./crates/nu-value-ext" }
|
||||
|
||||
nu_plugin_binaryview = { version = "0.29.0", path = "./crates/nu_plugin_binaryview", optional = true }
|
||||
nu_plugin_chart = { version = "0.29.0", path = "./crates/nu_plugin_chart", optional = true }
|
||||
nu_plugin_fetch = { version = "0.29.0", path = "./crates/nu_plugin_fetch", optional = true }
|
||||
nu_plugin_from_bson = { version = "0.29.0", path = "./crates/nu_plugin_from_bson", optional = true }
|
||||
nu_plugin_from_sqlite = { version = "0.29.0", path = "./crates/nu_plugin_from_sqlite", optional = true }
|
||||
nu_plugin_inc = { version = "0.29.0", path = "./crates/nu_plugin_inc", optional = true }
|
||||
nu_plugin_match = { version = "0.29.0", path = "./crates/nu_plugin_match", optional = true }
|
||||
nu_plugin_post = { version = "0.29.0", path = "./crates/nu_plugin_post", optional = true }
|
||||
nu_plugin_ps = { version = "0.29.0", path = "./crates/nu_plugin_ps", optional = true }
|
||||
nu_plugin_s3 = { version = "0.29.0", path = "./crates/nu_plugin_s3", optional = true }
|
||||
nu_plugin_selector = { version = "0.29.0", path = "./crates/nu_plugin_selector", optional = true }
|
||||
nu_plugin_start = { version = "0.29.0", path = "./crates/nu_plugin_start", optional = true }
|
||||
nu_plugin_sys = { version = "0.29.0", path = "./crates/nu_plugin_sys", optional = true }
|
||||
nu_plugin_textview = { version = "0.29.0", path = "./crates/nu_plugin_textview", optional = true }
|
||||
nu_plugin_to_bson = { version = "0.29.0", path = "./crates/nu_plugin_to_bson", optional = true }
|
||||
nu_plugin_to_sqlite = { version = "0.29.0", path = "./crates/nu_plugin_to_sqlite", optional = true }
|
||||
nu_plugin_tree = { version = "0.29.0", path = "./crates/nu_plugin_tree", optional = true }
|
||||
nu_plugin_xpath = { version = "0.29.0", path = "./crates/nu_plugin_xpath", optional = true }
|
||||
nu_plugin_binaryview = { version = "0.31.0", path = "./crates/nu_plugin_binaryview", optional = true }
|
||||
nu_plugin_chart = { version = "0.31.0", path = "./crates/nu_plugin_chart", optional = true }
|
||||
nu_plugin_fetch = { version = "0.31.0", path = "./crates/nu_plugin_fetch", optional = true }
|
||||
nu_plugin_from_bson = { version = "0.31.0", path = "./crates/nu_plugin_from_bson", optional = true }
|
||||
nu_plugin_from_sqlite = { version = "0.31.0", path = "./crates/nu_plugin_from_sqlite", optional = true }
|
||||
nu_plugin_inc = { version = "0.31.0", path = "./crates/nu_plugin_inc", optional = true }
|
||||
nu_plugin_match = { version = "0.31.0", path = "./crates/nu_plugin_match", optional = true }
|
||||
nu_plugin_post = { version = "0.31.0", path = "./crates/nu_plugin_post", optional = true }
|
||||
nu_plugin_ps = { version = "0.31.0", path = "./crates/nu_plugin_ps", optional = true }
|
||||
nu_plugin_query_json = { version = "0.31.0", path = "./crates/nu_plugin_query_json", optional = true }
|
||||
nu_plugin_s3 = { version = "0.31.0", path = "./crates/nu_plugin_s3", optional = true }
|
||||
nu_plugin_selector = { version = "0.31.0", path = "./crates/nu_plugin_selector", optional = true }
|
||||
nu_plugin_start = { version = "0.31.0", path = "./crates/nu_plugin_start", optional = true }
|
||||
nu_plugin_sys = { version = "0.31.0", path = "./crates/nu_plugin_sys", optional = true }
|
||||
nu_plugin_textview = { version = "0.31.0", path = "./crates/nu_plugin_textview", optional = true }
|
||||
nu_plugin_to_bson = { version = "0.31.0", path = "./crates/nu_plugin_to_bson", optional = true }
|
||||
nu_plugin_to_sqlite = { version = "0.31.0", path = "./crates/nu_plugin_to_sqlite", optional = true }
|
||||
nu_plugin_tree = { version = "0.31.0", path = "./crates/nu_plugin_tree", optional = true }
|
||||
nu_plugin_xpath = { version = "0.31.0", path = "./crates/nu_plugin_xpath", optional = true }
|
||||
|
||||
# Required to bootstrap the main binary
|
||||
clap = "2.33.3"
|
||||
@ -57,7 +58,7 @@ log = "0.4.14"
|
||||
pretty_env_logger = "0.4.0"
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { version = "0.29.0", path = "./crates/nu-test-support" }
|
||||
nu-test-support = { version = "0.31.0", path = "./crates/nu-test-support" }
|
||||
dunce = "1.0.1"
|
||||
serial_test = "0.5.1"
|
||||
hamcrest2 = "0.3.0"
|
||||
@ -81,9 +82,7 @@ rustyline-support = ["nu-cli/rustyline-support", "nu-command/rustyline-support"]
|
||||
term-support = ["nu-cli/term", "nu-command/term"]
|
||||
uuid-support = ["nu-cli/uuid_crate", "nu-command/uuid_crate"]
|
||||
which-support = [
|
||||
"nu-cli/ichwh",
|
||||
"nu-cli/which",
|
||||
"nu-command/ichwh",
|
||||
"nu-command/which",
|
||||
"nu-engine/which",
|
||||
]
|
||||
@ -92,14 +91,10 @@ default = [
|
||||
"nu-cli/shadow-rs",
|
||||
"sys",
|
||||
"ps",
|
||||
"textview",
|
||||
"inc",
|
||||
"directories-support",
|
||||
"ctrlc-support",
|
||||
"which-support",
|
||||
"ptree-support",
|
||||
"term-support",
|
||||
"uuid-support",
|
||||
"rustyline-support",
|
||||
"match",
|
||||
"post",
|
||||
@ -111,9 +106,13 @@ stable = ["default"]
|
||||
extra = [
|
||||
"default",
|
||||
"binaryview",
|
||||
"inc",
|
||||
"tree",
|
||||
"ptree-support",
|
||||
"textview",
|
||||
"clipboard-cli",
|
||||
"trash-support",
|
||||
"uuid-support",
|
||||
"start",
|
||||
"bson",
|
||||
"sqlite",
|
||||
@ -121,6 +120,7 @@ extra = [
|
||||
"chart",
|
||||
"xpath",
|
||||
"selector",
|
||||
"query-json",
|
||||
]
|
||||
|
||||
wasi = ["inc", "match", "ptree-support", "match", "tree", "rustyline-support"]
|
||||
@ -135,13 +135,13 @@ post = ["nu_plugin_post"]
|
||||
ps = ["nu_plugin_ps"]
|
||||
sys = ["nu_plugin_sys"]
|
||||
textview = ["nu_plugin_textview"]
|
||||
zip-support = ["nu-cli/zip", "nu-command/zip"]
|
||||
|
||||
# Extra
|
||||
binaryview = ["nu_plugin_binaryview"]
|
||||
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
|
||||
chart = ["nu_plugin_chart"]
|
||||
clipboard-cli = ["nu-cli/clipboard-cli", "nu-command/clipboard-cli"]
|
||||
query-json = ["nu_plugin_query_json"]
|
||||
s3 = ["nu_plugin_s3"]
|
||||
selector = ["nu_plugin_selector"]
|
||||
sqlite = ["nu_plugin_from_sqlite", "nu_plugin_to_sqlite"]
|
||||
@ -153,6 +153,8 @@ trash-support = [
|
||||
]
|
||||
tree = ["nu_plugin_tree"]
|
||||
xpath = ["nu_plugin_xpath"]
|
||||
zip-support = ["nu-cli/zip", "nu-command/zip"]
|
||||
|
||||
#This is disabled in extra for now
|
||||
table-pager = ["nu-command/table-pager"]
|
||||
|
||||
@ -160,7 +162,8 @@ table-pager = ["nu-command/table-pager"]
|
||||
#strip = "symbols" #Couldn't get working +nightly
|
||||
codegen-units = 1 #Reduce parallel codegen units
|
||||
lto = true #Link Time Optimization
|
||||
opt-level = 'z' #Optimize for size
|
||||
# opt-level = 'z' #Optimize for size
|
||||
# debug = true
|
||||
|
||||
# Core plugins that ship with `cargo install nu` by default
|
||||
# Currently, Cargo limits us to installing only one binary
|
||||
@ -212,6 +215,11 @@ name = "nu_plugin_extra_tree"
|
||||
path = "src/plugins/nu_plugin_extra_tree.rs"
|
||||
required-features = ["tree"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_extra_query_json"
|
||||
path = "src/plugins/nu_plugin_extra_query_json.rs"
|
||||
required-features = ["query-json"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_extra_start"
|
||||
path = "src/plugins/nu_plugin_extra_start.rs"
|
||||
|
@ -9,7 +9,7 @@ description = "Library for ANSI terminal colors and styles (bold, underline)"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-ansi-term"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![allow(missing_docs)]
|
||||
use crate::style::{Color, Style};
|
||||
use crate::write::AnyWrite;
|
||||
use std::fmt;
|
||||
|
@ -246,7 +246,7 @@ extern crate doc_comment;
|
||||
#[cfg(test)]
|
||||
doctest!("../README.md");
|
||||
|
||||
mod ansi;
|
||||
pub mod ansi;
|
||||
pub use ansi::{Infix, Prefix, Suffix};
|
||||
|
||||
mod style;
|
||||
|
@ -5,26 +5,27 @@ description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-cli"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-command = { version = "0.29.0", path = "../nu-command" }
|
||||
nu-data = { version = "0.29.0", path = "../nu-data" }
|
||||
nu-engine = { version = "0.29.0", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.29.0", path = "../nu-errors" }
|
||||
nu-json = { version = "0.29.0", path = "../nu-json" }
|
||||
nu-parser = { version = "0.29.0", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.29.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.29.0", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.29.0", path = "../nu-source" }
|
||||
nu-stream = { version = "0.29.0", path = "../nu-stream" }
|
||||
nu-table = { version = "0.29.0", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.29.0", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.29.0", path = "../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.29.0", path = "../nu-ansi-term" }
|
||||
nu-command = { version = "0.31.0", path = "../nu-command" }
|
||||
nu-data = { version = "0.31.0", path = "../nu-data" }
|
||||
nu-engine = { version = "0.31.0", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.31.0", path = "../nu-errors" }
|
||||
nu-json = { version = "0.31.0", path = "../nu-json" }
|
||||
nu-parser = { version = "0.31.0", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.31.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.31.0", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.31.0", path = "../nu-source" }
|
||||
nu-stream = { version = "0.31.0", path = "../nu-stream" }
|
||||
nu-table = { version = "0.31.0", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.31.0", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.31.0", path = "../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.31.0", path = "../nu-ansi-term" }
|
||||
nu-pretty-hex = { version = "0.31.0", path = "../nu-pretty-hex" }
|
||||
|
||||
Inflector = "0.11"
|
||||
arboard = { version = "1.1.0", optional = true }
|
||||
@ -57,7 +58,6 @@ getset = "0.1.1"
|
||||
glob = "0.3.0"
|
||||
htmlescape = "0.3.1"
|
||||
ical = "0.7.0"
|
||||
ichwh = { version = "0.3.4", optional = true }
|
||||
indexmap = { version = "1.6.1", features = ["serde-1"] }
|
||||
itertools = "0.10.0"
|
||||
lazy_static = "1.*"
|
||||
@ -68,7 +68,6 @@ num-format = { version = "0.4.0", features = ["with-num-bigint"] }
|
||||
num-traits = "0.2.14"
|
||||
parking_lot = "0.11.1"
|
||||
pin-utils = "0.1.0"
|
||||
pretty-hex = "0.2.1"
|
||||
ptree = { version = "0.3.1", optional = true }
|
||||
query_interface = "0.3.5"
|
||||
quick-xml = "0.21.0"
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::line_editor::configure_ctrl_c;
|
||||
use nu_command::commands::default_context::create_default_context;
|
||||
use nu_engine::{evaluation_context, run_block, script::run_script_standalone, EvaluationContext};
|
||||
use nu_ansi_term::Color;
|
||||
use nu_engine::{maybe_print_errors, run_block, script::run_script_standalone, EvaluationContext};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_engine::script::{process_script, LineResult};
|
||||
@ -13,8 +13,7 @@ use crate::line_editor::{
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use nu_data::config;
|
||||
use nu_data::config::{Conf, NuConfig};
|
||||
use nu_source::{AnchorLocation, Tag, Text};
|
||||
use nu_source::{Tag, Text};
|
||||
use nu_stream::InputStream;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
#[allow(unused_imports)]
|
||||
@ -23,10 +22,9 @@ use std::sync::atomic::Ordering;
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
use rustyline::{self, error::ReadlineError};
|
||||
|
||||
use crate::EnvironmentSyncer;
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::ParserScope;
|
||||
use nu_protocol::{hir::ExternalRedirection, UntaggedValue, Value};
|
||||
use nu_protocol::{hir::ExternalRedirection, ConfigPath, UntaggedValue, Value};
|
||||
|
||||
use log::trace;
|
||||
use std::error::Error;
|
||||
@ -35,10 +33,9 @@ use std::path::PathBuf;
|
||||
|
||||
pub struct Options {
|
||||
pub config: Option<OsString>,
|
||||
pub history: Option<PathBuf>,
|
||||
pub save_history: bool,
|
||||
pub stdin: bool,
|
||||
pub scripts: Vec<NuScript>,
|
||||
pub save_history: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
@ -51,20 +48,9 @@ impl Options {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
config: None,
|
||||
history: None,
|
||||
save_history: true,
|
||||
stdin: false,
|
||||
scripts: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn history(&self, block: impl FnOnce(&std::path::Path)) {
|
||||
if !self.save_history {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(file) = &self.history {
|
||||
block(&file)
|
||||
save_history: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -137,106 +123,38 @@ pub fn search_paths() -> Vec<std::path::PathBuf> {
|
||||
search_paths
|
||||
}
|
||||
|
||||
pub async fn run_script_file(mut options: Options) -> Result<(), Box<dyn Error>> {
|
||||
let mut context = create_default_context(false)?;
|
||||
let mut syncer = create_environment_syncer(&context, &mut options);
|
||||
let config = syncer.get_config();
|
||||
pub fn run_script_file(context: EvaluationContext, options: Options) -> Result<(), Box<dyn Error>> {
|
||||
if let Some(cfg) = options.config {
|
||||
load_cfg_as_global_cfg(&context, PathBuf::from(cfg));
|
||||
} else {
|
||||
load_global_cfg(&context);
|
||||
}
|
||||
|
||||
context.configure(&config, |_, ctx| {
|
||||
syncer.load_environment();
|
||||
syncer.sync_env_vars(ctx);
|
||||
syncer.sync_path_vars(ctx);
|
||||
|
||||
if let Err(reason) = syncer.autoenv(ctx) {
|
||||
ctx.with_host(|host| host.print_err(reason, &Text::from("")));
|
||||
}
|
||||
|
||||
let _ = register_plugins(ctx);
|
||||
let _ = configure_ctrl_c(ctx);
|
||||
});
|
||||
|
||||
let _ = run_startup_commands(&mut context, &config).await;
|
||||
let _ = register_plugins(&context);
|
||||
let _ = configure_ctrl_c(&context);
|
||||
|
||||
let script = options
|
||||
.scripts
|
||||
.get(0)
|
||||
.ok_or_else(|| ShellError::unexpected("Nu source code not available"))?;
|
||||
|
||||
run_script_standalone(script.get_code().to_string(), options.stdin, &context, true).await?;
|
||||
run_script_standalone(script.get_code().to_string(), options.stdin, &context, true)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_environment_syncer(
|
||||
context: &EvaluationContext,
|
||||
options: &mut Options,
|
||||
) -> EnvironmentSyncer {
|
||||
let configuration = match &options.config {
|
||||
Some(config_file) => {
|
||||
let location = Some(AnchorLocation::File(
|
||||
config_file.to_string_lossy().to_string(),
|
||||
));
|
||||
|
||||
let tag = Tag::unknown().anchored(location);
|
||||
|
||||
context.scope.add_var(
|
||||
"config-path",
|
||||
UntaggedValue::filepath(PathBuf::from(&config_file)).into_value(tag),
|
||||
);
|
||||
|
||||
NuConfig::with(Some(config_file.into()))
|
||||
}
|
||||
None => NuConfig::new(),
|
||||
};
|
||||
|
||||
let history_path = configuration.history_path();
|
||||
options.history = Some(history_path.clone());
|
||||
|
||||
let location = Some(AnchorLocation::File(
|
||||
history_path.to_string_lossy().to_string(),
|
||||
));
|
||||
|
||||
let tag = Tag::unknown().anchored(location);
|
||||
|
||||
context.scope.add_var(
|
||||
"history-path",
|
||||
UntaggedValue::filepath(history_path).into_value(tag),
|
||||
);
|
||||
|
||||
EnvironmentSyncer::with_config(Box::new(configuration))
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub async fn cli(
|
||||
mut context: EvaluationContext,
|
||||
mut options: Options,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut syncer = create_environment_syncer(&context, &mut options);
|
||||
pub fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn Error>> {
|
||||
let _ = configure_ctrl_c(&context);
|
||||
|
||||
let configuration = syncer.get_config();
|
||||
|
||||
let mut rl = default_rustyline_editor_configuration();
|
||||
|
||||
context.configure(&configuration, |config, ctx| {
|
||||
syncer.load_environment();
|
||||
syncer.sync_env_vars(ctx);
|
||||
syncer.sync_path_vars(ctx);
|
||||
|
||||
if let Err(reason) = syncer.autoenv(ctx) {
|
||||
ctx.with_host(|host| host.print_err(reason, &Text::from("")));
|
||||
}
|
||||
|
||||
let _ = configure_ctrl_c(ctx);
|
||||
let _ = configure_rustyline_editor(&mut rl, config);
|
||||
|
||||
let helper = Some(nu_line_editor_helper(ctx, config));
|
||||
rl.set_helper(helper);
|
||||
});
|
||||
|
||||
// start time for command duration
|
||||
// start time for running startup scripts (this metric includes loading of the cfg, but w/e)
|
||||
let startup_commands_start_time = std::time::Instant::now();
|
||||
// run the startup commands
|
||||
let _ = run_startup_commands(&mut context, &configuration).await;
|
||||
|
||||
if let Some(cfg) = options.config {
|
||||
load_cfg_as_global_cfg(&context, PathBuf::from(cfg));
|
||||
} else {
|
||||
load_global_cfg(&context);
|
||||
}
|
||||
// Store cmd duration in an env var
|
||||
context.scope.add_env_var(
|
||||
"CMD_DURATION",
|
||||
@ -247,20 +165,43 @@ pub async fn cli(
|
||||
startup_commands_start_time.elapsed()
|
||||
);
|
||||
|
||||
//Configure rustyline
|
||||
let mut rl = default_rustyline_editor_configuration();
|
||||
let history_path = if let Some(cfg) = &context.configs.lock().global_config {
|
||||
let _ = configure_rustyline_editor(&mut rl, cfg);
|
||||
let helper = Some(nu_line_editor_helper(&context, cfg));
|
||||
rl.set_helper(helper);
|
||||
nu_data::config::path::history_path_or_default(cfg)
|
||||
} else {
|
||||
nu_data::config::path::default_history_path()
|
||||
};
|
||||
|
||||
// Don't load history if it's not necessary
|
||||
if options.save_history {
|
||||
let _ = rl.load_history(&history_path);
|
||||
}
|
||||
|
||||
//set vars from cfg if present
|
||||
let (skip_welcome_message, prompt) = if let Some(cfg) = &context.configs.lock().global_config {
|
||||
(
|
||||
cfg.var("skip_welcome_message")
|
||||
.map(|x| x.is_true())
|
||||
.unwrap_or(false),
|
||||
cfg.var("prompt"),
|
||||
)
|
||||
} else {
|
||||
(false, None)
|
||||
};
|
||||
|
||||
//Check whether dir we start in contains local cfg file and if so load it.
|
||||
load_local_cfg_if_present(&context);
|
||||
|
||||
// Give ourselves a scope to work in
|
||||
context.scope.enter_scope();
|
||||
|
||||
options.history(|file| {
|
||||
let _ = rl.load_history(&file);
|
||||
});
|
||||
|
||||
let mut session_text = String::new();
|
||||
let mut line_start: usize = 0;
|
||||
|
||||
let skip_welcome_message = configuration
|
||||
.var("skip_welcome_message")
|
||||
.map(|x| x.is_true())
|
||||
.unwrap_or(false);
|
||||
if !skip_welcome_message {
|
||||
println!(
|
||||
"Welcome to Nushell {} (type 'help' for more info)",
|
||||
@ -284,31 +225,38 @@ pub async fn cli(
|
||||
let cwd = context.shell_manager.path();
|
||||
|
||||
let colored_prompt = {
|
||||
if let Some(prompt) = configuration.var("prompt") {
|
||||
if let Some(prompt) = &prompt {
|
||||
let prompt_line = prompt.as_string()?;
|
||||
|
||||
context.scope.enter_scope();
|
||||
|
||||
let (mut prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
|
||||
|
||||
prompt_block.set_redirect(ExternalRedirection::Stdout);
|
||||
let (prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
|
||||
|
||||
if err.is_some() {
|
||||
context.scope.exit_scope();
|
||||
|
||||
format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch())
|
||||
format!(
|
||||
"{}{}{}{}{}{}> ",
|
||||
Color::Green.bold().prefix().to_string(),
|
||||
cwd,
|
||||
nu_ansi_term::ansi::RESET,
|
||||
Color::Cyan.bold().prefix().to_string(),
|
||||
current_branch(),
|
||||
nu_ansi_term::ansi::RESET
|
||||
)
|
||||
} else {
|
||||
let run_result = run_block(&prompt_block, &context, InputStream::empty()).await;
|
||||
let run_result = run_block(
|
||||
&prompt_block,
|
||||
&context,
|
||||
InputStream::empty(),
|
||||
ExternalRedirection::Stdout,
|
||||
);
|
||||
context.scope.exit_scope();
|
||||
|
||||
match run_result {
|
||||
Ok(result) => match result.collect_string(Tag::unknown()).await {
|
||||
Ok(result) => match result.collect_string(Tag::unknown()) {
|
||||
Ok(string_result) => {
|
||||
let errors = context.get_errors();
|
||||
evaluation_context::maybe_print_errors(
|
||||
&context,
|
||||
Text::from(prompt_line),
|
||||
);
|
||||
maybe_print_errors(&context, Text::from(prompt_line));
|
||||
context.clear_errors();
|
||||
|
||||
if !errors.is_empty() {
|
||||
@ -333,7 +281,15 @@ pub async fn cli(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch())
|
||||
format!(
|
||||
"{}{}{}{}{}{}> ",
|
||||
Color::Green.bold().prefix().to_string(),
|
||||
cwd,
|
||||
nu_ansi_term::ansi::RESET,
|
||||
Color::Cyan.bold().prefix().to_string(),
|
||||
current_branch(),
|
||||
nu_ansi_term::ansi::RESET
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@ -345,7 +301,9 @@ pub async fn cli(
|
||||
}
|
||||
};
|
||||
|
||||
rl.helper_mut().expect("No helper").colored_prompt = colored_prompt;
|
||||
if let Some(helper) = rl.helper_mut() {
|
||||
helper.colored_prompt = colored_prompt;
|
||||
}
|
||||
let mut initial_command = Some(String::new());
|
||||
let mut readline = Err(ReadlineError::Eof);
|
||||
while let Some(ref cmd) = initial_command {
|
||||
@ -363,16 +321,13 @@ pub async fn cli(
|
||||
let cmd_start_time = std::time::Instant::now();
|
||||
|
||||
let line = match convert_rustyline_result_to_string(readline) {
|
||||
LineResult::Success(_) => {
|
||||
process_script(
|
||||
&session_text[line_start..],
|
||||
&context,
|
||||
false,
|
||||
line_start,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
}
|
||||
LineResult::Success(_) => process_script(
|
||||
&session_text[line_start..],
|
||||
&context,
|
||||
false,
|
||||
line_start,
|
||||
true,
|
||||
),
|
||||
x => x,
|
||||
};
|
||||
|
||||
@ -381,54 +336,50 @@ pub async fn cli(
|
||||
.scope
|
||||
.add_env_var("CMD_DURATION", format!("{:?}", cmd_start_time.elapsed()));
|
||||
|
||||
// Check the config to see if we need to update the path
|
||||
// TODO: make sure config is cached so we don't path this load every call
|
||||
// FIXME: we probably want to be a bit more graceful if we can't set the environment
|
||||
|
||||
context.configure(&configuration, |config, ctx| {
|
||||
if syncer.did_config_change() {
|
||||
syncer.reload();
|
||||
syncer.sync_env_vars(ctx);
|
||||
syncer.sync_path_vars(ctx);
|
||||
}
|
||||
|
||||
if let Err(reason) = syncer.autoenv(ctx) {
|
||||
ctx.with_host(|host| host.print_err(reason, &Text::from("")));
|
||||
}
|
||||
|
||||
let _ = configure_rustyline_editor(&mut rl, config);
|
||||
});
|
||||
|
||||
match line {
|
||||
LineResult::Success(line) => {
|
||||
options.history(|file| {
|
||||
if options.save_history && !line.trim().is_empty() {
|
||||
rl.add_history_entry(&line);
|
||||
let _ = rl.save_history(&file);
|
||||
});
|
||||
|
||||
evaluation_context::maybe_print_errors(&context, Text::from(session_text.clone()));
|
||||
let _ = rl.append_history(&history_path);
|
||||
}
|
||||
maybe_print_errors(&context, Text::from(session_text.clone()));
|
||||
}
|
||||
|
||||
LineResult::ClearHistory => {
|
||||
options.history(|file| {
|
||||
if options.save_history {
|
||||
rl.clear_history();
|
||||
let _ = rl.save_history(&file);
|
||||
});
|
||||
let _ = rl.append_history(&history_path);
|
||||
}
|
||||
}
|
||||
|
||||
LineResult::Error(line, reason) => {
|
||||
options.history(|file| {
|
||||
LineResult::Error(line, err) => {
|
||||
if options.save_history && !line.trim().is_empty() {
|
||||
rl.add_history_entry(&line);
|
||||
let _ = rl.save_history(&file);
|
||||
});
|
||||
let _ = rl.append_history(&history_path);
|
||||
}
|
||||
|
||||
context.with_host(|host| host.print_err(reason, &Text::from(session_text.clone())));
|
||||
context
|
||||
.host
|
||||
.lock()
|
||||
.print_err(err, &Text::from(session_text.clone()));
|
||||
|
||||
// I am not so sure, we don't need maybe_print_errors here (as we printed an err
|
||||
// above), because maybe_print_errors also clears the errors.
|
||||
// TODO Analyze where above err comes from, and whether we need to clear
|
||||
// context.errors here
|
||||
// Or just be consistent and return errors always in context.errors...
|
||||
maybe_print_errors(&context, Text::from(session_text.clone()));
|
||||
}
|
||||
|
||||
LineResult::CtrlC => {
|
||||
let config_ctrlc_exit = configuration
|
||||
.var("ctrlc_exit")
|
||||
.map(|s| s.value.is_true())
|
||||
let config_ctrlc_exit = context
|
||||
.configs
|
||||
.lock()
|
||||
.global_config
|
||||
.as_ref()
|
||||
.map(|cfg| cfg.var("ctrlc_exit"))
|
||||
.flatten()
|
||||
.map(|ctrl_c| ctrl_c.is_true())
|
||||
.unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells
|
||||
|
||||
if !config_ctrlc_exit {
|
||||
@ -436,10 +387,9 @@ pub async fn cli(
|
||||
}
|
||||
|
||||
if ctrlcbreak {
|
||||
options.history(|file| {
|
||||
let _ = rl.save_history(&file);
|
||||
});
|
||||
|
||||
if options.save_history {
|
||||
let _ = rl.append_history(&history_path);
|
||||
}
|
||||
std::process::exit(0);
|
||||
} else {
|
||||
context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)"));
|
||||
@ -463,14 +413,49 @@ pub async fn cli(
|
||||
}
|
||||
|
||||
// we are ok if we can not save history
|
||||
options.history(|file| {
|
||||
let _ = rl.save_history(&file);
|
||||
});
|
||||
if options.save_history {
|
||||
let _ = rl.append_history(&history_path);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn register_plugins(context: &mut EvaluationContext) -> Result<(), ShellError> {
|
||||
pub fn load_local_cfg_if_present(context: &EvaluationContext) {
|
||||
trace!("Loading local cfg if present");
|
||||
match config::loadable_cfg_exists_in_dir(PathBuf::from(context.shell_manager.path())) {
|
||||
Ok(Some(cfg_path)) => {
|
||||
if let Err(err) = context.load_config(&ConfigPath::Local(cfg_path)) {
|
||||
context.host.lock().print_err(err, &Text::from(""))
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
//Report error while checking for local cfg file
|
||||
context.host.lock().print_err(e, &Text::from(""))
|
||||
}
|
||||
Ok(None) => {
|
||||
//No local cfg file present in start dir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_cfg_as_global_cfg(context: &EvaluationContext, path: PathBuf) {
|
||||
if let Err(err) = context.load_config(&ConfigPath::Global(path)) {
|
||||
context.host.lock().print_err(err, &Text::from(""));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_global_cfg(context: &EvaluationContext) {
|
||||
match config::default_path() {
|
||||
Ok(path) => {
|
||||
load_cfg_as_global_cfg(context, path);
|
||||
}
|
||||
Err(e) => {
|
||||
context.host.lock().print_err(e, &Text::from(""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_plugins(context: &EvaluationContext) -> Result<(), ShellError> {
|
||||
if let Ok(plugins) = nu_engine::plugin::build_plugin::scan(search_paths()) {
|
||||
context.add_commands(
|
||||
plugins
|
||||
@ -483,35 +468,7 @@ pub fn register_plugins(context: &mut EvaluationContext) -> Result<(), ShellErro
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_startup_commands(
|
||||
context: &mut EvaluationContext,
|
||||
config: &dyn nu_data::config::Conf,
|
||||
) -> Result<(), ShellError> {
|
||||
if let Some(commands) = config.var("startup") {
|
||||
match commands {
|
||||
Value {
|
||||
value: UntaggedValue::Table(pipelines),
|
||||
..
|
||||
} => {
|
||||
let mut script_file = String::new();
|
||||
for pipeline in pipelines {
|
||||
script_file.push_str(&pipeline.as_string()?);
|
||||
script_file.push('\n');
|
||||
}
|
||||
let _ = run_script_standalone(script_file, false, context, false).await;
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::untagged_runtime_error(
|
||||
"expected a table of pipeline strings as startup commands",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, ShellError> {
|
||||
pub fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, ShellError> {
|
||||
// FIXME: do we still need this?
|
||||
let line = if let Some(s) = line.strip_suffix('\n') {
|
||||
s
|
||||
@ -528,13 +485,16 @@ pub async fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<Strin
|
||||
}
|
||||
|
||||
let input_stream = InputStream::empty();
|
||||
let env = ctx.get_env();
|
||||
ctx.scope.add_env(env);
|
||||
|
||||
let result = run_block(&classified_block, ctx, input_stream).await;
|
||||
let result = run_block(
|
||||
&classified_block,
|
||||
ctx,
|
||||
input_stream,
|
||||
ExternalRedirection::Stdout,
|
||||
);
|
||||
ctx.scope.exit_scope();
|
||||
|
||||
result?.collect_string(Tag::unknown()).await.map(|x| x.item)
|
||||
result?.collect_string(Tag::unknown()).map(|x| x.item)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -562,7 +522,7 @@ mod tests {
|
||||
let (tokens, err) = nu_parser::lex(&data, 0);
|
||||
let (lite_block, err2) = nu_parser::parse_block(tokens);
|
||||
if err.is_none() && err2.is_none() {
|
||||
let context = EvaluationContext::basic().unwrap();
|
||||
let context = EvaluationContext::basic();
|
||||
let _ = nu_parser::classify_block(&lite_block, &context.scope);
|
||||
}
|
||||
true
|
||||
|
@ -254,6 +254,8 @@ pub fn completion_location(line: &str, block: &Block, pos: usize) -> Vec<Complet
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::*;
|
||||
|
||||
use nu_parser::{classify_block, lex, parse_block, ParserScope};
|
||||
@ -285,9 +287,9 @@ mod tests {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn add_definition(&self, _block: Block) {}
|
||||
fn add_definition(&self, _block: Arc<Block>) {}
|
||||
|
||||
fn get_definitions(&self) -> Vec<Block> {
|
||||
fn get_definitions(&self) -> Vec<Arc<Block>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ impl matchers::Matcher for Matcher {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// TODO: check some unicode matches if this becomes relevant
|
||||
// TODO: check some Unicode matches if this becomes relevant
|
||||
|
||||
// FIXME: could work exhaustively through ['-', '--'. ''] in a loop for each test
|
||||
#[test]
|
||||
|
@ -1,3 +0,0 @@
|
||||
pub(crate) mod directory_specific_environment;
|
||||
pub(crate) mod environment;
|
||||
pub(crate) mod environment_syncer;
|
@ -1,259 +0,0 @@
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use nu_command::commands::autoenv;
|
||||
use nu_errors::ShellError;
|
||||
use serde::Deserialize;
|
||||
use std::env::*;
|
||||
use std::process::Command;
|
||||
|
||||
use std::{
|
||||
ffi::OsString,
|
||||
fmt::Debug,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
//Tests reside in /nushell/tests/shell/pipeline/commands/internal.rs
|
||||
|
||||
type EnvKey = String;
|
||||
type EnvVal = OsString;
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DirectorySpecificEnvironment {
|
||||
pub last_seen_directory: PathBuf,
|
||||
//If an environment var has been added from a .nu in a directory, we track it here so we can remove it when the user leaves the directory.
|
||||
//If setting the var overwrote some value, we save the old value in an option so we can restore it later.
|
||||
added_vars: IndexMap<PathBuf, IndexMap<EnvKey, Option<EnvVal>>>,
|
||||
|
||||
//We track directories that we have read .nu-env from. This is different from the keys in added_vars since sometimes a file only wants to run scripts.
|
||||
visited_dirs: IndexSet<PathBuf>,
|
||||
exitscripts: IndexMap<PathBuf, Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
pub struct NuEnvDoc {
|
||||
pub env: Option<IndexMap<String, String>>,
|
||||
pub scriptvars: Option<IndexMap<String, String>>,
|
||||
pub scripts: Option<IndexMap<String, Vec<String>>>,
|
||||
pub entryscripts: Option<Vec<String>>,
|
||||
pub exitscripts: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl DirectorySpecificEnvironment {
|
||||
pub fn new() -> DirectorySpecificEnvironment {
|
||||
let root_dir = if cfg!(target_os = "windows") {
|
||||
PathBuf::from("c:\\")
|
||||
} else {
|
||||
PathBuf::from("/")
|
||||
};
|
||||
DirectorySpecificEnvironment {
|
||||
last_seen_directory: root_dir,
|
||||
added_vars: IndexMap::new(),
|
||||
visited_dirs: IndexSet::new(),
|
||||
exitscripts: IndexMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn toml_if_trusted(&mut self, nu_env_file: &Path) -> Result<NuEnvDoc, ShellError> {
|
||||
let content = std::fs::read(&nu_env_file)?;
|
||||
|
||||
if autoenv::file_is_trusted(&nu_env_file, &content)? {
|
||||
let mut doc: NuEnvDoc = toml::de::from_slice(&content)
|
||||
.map_err(|e| ShellError::untagged_runtime_error(format!("{:?}", e)))?;
|
||||
|
||||
if let Some(scripts) = doc.scripts.as_ref() {
|
||||
for (k, v) in scripts {
|
||||
if k == "entryscripts" {
|
||||
doc.entryscripts = Some(v.clone());
|
||||
} else if k == "exitscripts" {
|
||||
doc.exitscripts = Some(v.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(doc);
|
||||
}
|
||||
Err(ShellError::untagged_runtime_error(
|
||||
format!("{:?} is untrusted. Run 'autoenv trust {:?}' to trust it.\nThis needs to be done after each change to the file.", nu_env_file, nu_env_file.parent().unwrap_or_else(|| &Path::new("")))))
|
||||
}
|
||||
|
||||
pub fn maintain_autoenv(&mut self) -> Result<(), ShellError> {
|
||||
let mut dir = current_dir()?;
|
||||
|
||||
if self.last_seen_directory == dir {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
//We track which keys we set as we go up the directory hierarchy, so that we don't overwrite a value we set in a subdir.
|
||||
let mut added_keys = IndexSet::new();
|
||||
|
||||
let mut new_visited_dirs = IndexSet::new();
|
||||
let mut popped = true;
|
||||
while popped {
|
||||
let nu_env_file = dir.join(".nu-env");
|
||||
if nu_env_file.exists() && !self.visited_dirs.contains(&dir) {
|
||||
let nu_env_doc = self.toml_if_trusted(&nu_env_file)?;
|
||||
|
||||
//add regular variables from the [env section]
|
||||
if let Some(env) = nu_env_doc.env {
|
||||
for (env_key, env_val) in env {
|
||||
self.maybe_add_key(&mut added_keys, &dir, &env_key, &env_val);
|
||||
}
|
||||
}
|
||||
|
||||
//Add variables that need to evaluate scripts to run, from [scriptvars] section
|
||||
if let Some(sv) = nu_env_doc.scriptvars {
|
||||
for (key, script) in sv {
|
||||
self.maybe_add_key(
|
||||
&mut added_keys,
|
||||
&dir,
|
||||
&key,
|
||||
value_from_script(&script)?.as_str(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(es) = nu_env_doc.entryscripts {
|
||||
for s in es {
|
||||
run(s.as_str(), None)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(es) = nu_env_doc.exitscripts {
|
||||
self.exitscripts.insert(dir.clone(), es);
|
||||
}
|
||||
}
|
||||
new_visited_dirs.insert(dir.clone());
|
||||
popped = dir.pop();
|
||||
}
|
||||
|
||||
//Time to clear out vars set by directories that we have left.
|
||||
let mut new_vars = IndexMap::new();
|
||||
for (dir, dirmap) in self.added_vars.drain(..) {
|
||||
if new_visited_dirs.contains(&dir) {
|
||||
new_vars.insert(dir, dirmap);
|
||||
} else {
|
||||
for (k, v) in dirmap {
|
||||
if let Some(v) = v {
|
||||
std::env::set_var(k, v);
|
||||
} else {
|
||||
std::env::remove_var(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Run exitscripts, can not be done in same loop as new vars as some files can contain only exitscripts
|
||||
let mut new_exitscripts = IndexMap::new();
|
||||
for (dir, scripts) in self.exitscripts.drain(..) {
|
||||
if new_visited_dirs.contains(&dir) {
|
||||
new_exitscripts.insert(dir, scripts);
|
||||
} else {
|
||||
for s in scripts {
|
||||
run(s.as_str(), Some(&dir))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.visited_dirs = new_visited_dirs;
|
||||
self.exitscripts = new_exitscripts;
|
||||
self.added_vars = new_vars;
|
||||
self.last_seen_directory = current_dir()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn maybe_add_key(
|
||||
&mut self,
|
||||
seen_vars: &mut IndexSet<EnvKey>,
|
||||
dir: &Path,
|
||||
key: &str,
|
||||
val: &str,
|
||||
) {
|
||||
//This condition is to make sure variables in parent directories don't overwrite variables set by subdirectories.
|
||||
if !seen_vars.contains(key) {
|
||||
seen_vars.insert(key.to_string());
|
||||
self.added_vars
|
||||
.entry(PathBuf::from(dir))
|
||||
.or_insert(IndexMap::new())
|
||||
.insert(key.to_string(), var_os(key));
|
||||
|
||||
std::env::set_var(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
// If the user recently ran autoenv untrust on a file, we clear the environment variables it set and make sure to not run any possible exitscripts.
|
||||
pub fn clear_recently_untrusted_file(&mut self) -> Result<(), ShellError> {
|
||||
// Figure out which file was untrusted
|
||||
// Remove all vars set by it
|
||||
let current_trusted_files: IndexSet<PathBuf> = autoenv::read_trusted()?
|
||||
.files
|
||||
.iter()
|
||||
.map(|(k, _)| PathBuf::from(k))
|
||||
.collect();
|
||||
|
||||
// We figure out which file(s) the user untrusted by taking the set difference of current trusted files in .config/nu/nu-env.toml and the files tracked by self.added_env_vars
|
||||
// If a file is in self.added_env_vars but not in nu-env.toml, it was just untrusted.
|
||||
let untrusted_files: IndexSet<PathBuf> = self
|
||||
.added_vars
|
||||
.iter()
|
||||
.filter_map(|(path, _)| {
|
||||
if !current_trusted_files.contains(path) {
|
||||
return Some(path.clone());
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect();
|
||||
|
||||
for path in untrusted_files {
|
||||
if let Some(added_keys) = self.added_vars.get(&path) {
|
||||
for (key, _) in added_keys {
|
||||
remove_var(key);
|
||||
}
|
||||
}
|
||||
self.exitscripts.remove(&path);
|
||||
self.added_vars.remove(&path);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn run(cmd: &str, dir: Option<&PathBuf>) -> Result<(), ShellError> {
|
||||
if cfg!(target_os = "windows") {
|
||||
if let Some(dir) = dir {
|
||||
let command = format!("cd {} & {}", dir.to_string_lossy(), cmd);
|
||||
Command::new("cmd")
|
||||
.args(&["/C", command.as_str()])
|
||||
.output()?
|
||||
} else {
|
||||
Command::new("cmd").args(&["/C", cmd]).output()?
|
||||
}
|
||||
} else if let Some(dir) = dir {
|
||||
// FIXME: When nu scripting is added, cding like might not be a good idea. If nu decides to execute entryscripts when entering the dir this way, it will cause troubles.
|
||||
// For now only standard shell scripts are used, so this is an issue for the future.
|
||||
Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(format!("cd {:?}; {}", dir, cmd))
|
||||
.output()?
|
||||
} else {
|
||||
Command::new("sh").arg("-c").arg(&cmd).output()?
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
fn value_from_script(cmd: &str) -> Result<String, ShellError> {
|
||||
let command = if cfg!(target_os = "windows") {
|
||||
Command::new("cmd").args(&["/C", cmd]).output()?
|
||||
} else {
|
||||
Command::new("sh").arg("-c").arg(&cmd).output()?
|
||||
};
|
||||
if command.stdout.is_empty() {
|
||||
return Err(ShellError::untagged_runtime_error(format!(
|
||||
"{:?} did not return any output",
|
||||
cmd
|
||||
)));
|
||||
}
|
||||
let response = std::str::from_utf8(&command.stdout[..command.stdout.len()]).map_err(|e| {
|
||||
ShellError::untagged_runtime_error(format!(
|
||||
"Couldn't parse stdout from command {:?}: {:?}",
|
||||
command, e
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(response.trim().to_string())
|
||||
}
|
274
crates/nu-cli/src/env/environment.rs
vendored
274
crates/nu-cli/src/env/environment.rs
vendored
@ -1,274 +0,0 @@
|
||||
use crate::env::directory_specific_environment::*;
|
||||
use indexmap::{indexmap, IndexSet};
|
||||
use nu_data::config::Conf;
|
||||
use nu_engine::Env;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{UntaggedValue, Value};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Environment {
|
||||
environment_vars: Option<Value>,
|
||||
path_vars: Option<Value>,
|
||||
pub autoenv: DirectorySpecificEnvironment,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
pub fn new() -> Environment {
|
||||
Environment {
|
||||
environment_vars: None,
|
||||
path_vars: None,
|
||||
autoenv: DirectorySpecificEnvironment::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_config<T: Conf>(configuration: &T) -> Environment {
|
||||
let env = configuration.env();
|
||||
let path = configuration.path();
|
||||
Environment {
|
||||
environment_vars: env,
|
||||
path_vars: path,
|
||||
autoenv: DirectorySpecificEnvironment::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn autoenv(&mut self, reload_trusted: bool) -> Result<(), ShellError> {
|
||||
self.autoenv.maintain_autoenv()?;
|
||||
if reload_trusted {
|
||||
self.autoenv.clear_recently_untrusted_file()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn morph<T: Conf>(&mut self, configuration: &T) {
|
||||
self.environment_vars = configuration.env();
|
||||
self.path_vars = configuration.path();
|
||||
}
|
||||
}
|
||||
|
||||
impl Env for Environment {
|
||||
fn env(&self) -> Option<Value> {
|
||||
if let Some(vars) = &self.environment_vars {
|
||||
return Some(vars.clone());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn path(&self) -> Option<Value> {
|
||||
if let Some(vars) = &self.path_vars {
|
||||
return Some(vars.clone());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn add_env(&mut self, key: &str, value: &str) {
|
||||
let value = UntaggedValue::string(value);
|
||||
|
||||
let new_envs = {
|
||||
if let Some(Value {
|
||||
value: UntaggedValue::Row(ref envs),
|
||||
ref tag,
|
||||
}) = self.environment_vars
|
||||
{
|
||||
let mut new_envs = envs.clone();
|
||||
|
||||
if !new_envs.contains_key(key) {
|
||||
new_envs.insert_data_at_key(key, value.into_value(tag.clone()));
|
||||
}
|
||||
|
||||
Value {
|
||||
value: UntaggedValue::Row(new_envs),
|
||||
tag: tag.clone(),
|
||||
}
|
||||
} else {
|
||||
UntaggedValue::Row(indexmap! { key.into() => value.into_untagged_value() }.into())
|
||||
.into_untagged_value()
|
||||
}
|
||||
};
|
||||
|
||||
self.environment_vars = Some(new_envs);
|
||||
}
|
||||
|
||||
fn add_path(&mut self, paths: std::ffi::OsString) {
|
||||
let new_paths = {
|
||||
if let Some(Value {
|
||||
value: UntaggedValue::Table(ref current_paths),
|
||||
ref tag,
|
||||
}) = self.path_vars
|
||||
{
|
||||
let mut new_paths = current_paths.clone();
|
||||
|
||||
let new_path_candidates = std::env::split_paths(&paths).map(|path| {
|
||||
UntaggedValue::string(path.to_string_lossy()).into_value(tag.clone())
|
||||
});
|
||||
|
||||
new_paths.extend(new_path_candidates);
|
||||
|
||||
let paths: IndexSet<Value> = new_paths.into_iter().collect();
|
||||
|
||||
Value {
|
||||
value: UntaggedValue::Table(paths.into_iter().collect()),
|
||||
tag: tag.clone(),
|
||||
}
|
||||
} else {
|
||||
let p = paths.into_string().unwrap_or_else(|_| String::from(""));
|
||||
let p = UntaggedValue::string(p).into_untagged_value();
|
||||
UntaggedValue::Table(vec![p]).into_untagged_value()
|
||||
}
|
||||
};
|
||||
|
||||
self.path_vars = Some(new_paths);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Env, Environment};
|
||||
use nu_data::config::{tests::FakeConfig, Conf};
|
||||
use nu_protocol::UntaggedValue;
|
||||
use nu_test_support::fs::Stub::FileWithContent;
|
||||
use nu_test_support::playground::Playground;
|
||||
|
||||
#[test]
|
||||
fn picks_up_environment_variables_from_configuration() {
|
||||
Playground::setup("environment_test_1", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
mosquetero_1 = "Andrés N. Robalino"
|
||||
mosquetero_2 = "Jonathan Turner"
|
||||
mosquetero_3 = "Yehuda katz"
|
||||
mosquetero_4 = "Jason Gedge"
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let actual = Environment::from_config(&fake_config);
|
||||
|
||||
assert_eq!(actual.env(), fake_config.env());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn picks_up_path_variables_from_configuration() {
|
||||
Playground::setup("environment_test_2", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
path = ["/Users/andresrobalino/.volta/bin", "/users/mosqueteros/bin"]
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let actual = Environment::from_config(&fake_config);
|
||||
|
||||
assert_eq!(actual.path(), fake_config.path());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn updates_env_variable() {
|
||||
Playground::setup("environment_test_3", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
SHELL = "/usr/bin/you_already_made_the_nu_choice"
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = Environment::from_config(&fake_config);
|
||||
|
||||
actual.add_env("USER", "NUNO");
|
||||
|
||||
assert_eq!(
|
||||
actual.env(),
|
||||
Some(
|
||||
UntaggedValue::row(
|
||||
indexmap! {
|
||||
"USER".into() => UntaggedValue::string("NUNO").into_untagged_value(),
|
||||
"SHELL".into() => UntaggedValue::string("/usr/bin/you_already_made_the_nu_choice").into_untagged_value(),
|
||||
}
|
||||
).into_untagged_value()
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_update_env_variable_if_it_exists() {
|
||||
Playground::setup("environment_test_4", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
SHELL = "/usr/bin/you_already_made_the_nu_choice"
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = Environment::from_config(&fake_config);
|
||||
|
||||
actual.add_env("SHELL", "/usr/bin/sh");
|
||||
|
||||
assert_eq!(
|
||||
actual.env(),
|
||||
Some(
|
||||
UntaggedValue::row(
|
||||
indexmap! {
|
||||
"SHELL".into() => UntaggedValue::string("/usr/bin/you_already_made_the_nu_choice").into_untagged_value(),
|
||||
}
|
||||
).into_untagged_value()
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn updates_path_variable() {
|
||||
Playground::setup("environment_test_5", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
path = ["/Users/andresrobalino/.volta/bin", "/users/mosqueteros/bin"]
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = Environment::from_config(&fake_config);
|
||||
|
||||
actual.add_path(std::ffi::OsString::from("/path/to/be/added"));
|
||||
|
||||
assert_eq!(
|
||||
actual.path(),
|
||||
Some(
|
||||
UntaggedValue::table(&[
|
||||
UntaggedValue::string("/Users/andresrobalino/.volta/bin")
|
||||
.into_untagged_value(),
|
||||
UntaggedValue::string("/users/mosqueteros/bin").into_untagged_value(),
|
||||
UntaggedValue::string("/path/to/be/added").into_untagged_value(),
|
||||
])
|
||||
.into_untagged_value()
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
617
crates/nu-cli/src/env/environment_syncer.rs
vendored
617
crates/nu-cli/src/env/environment_syncer.rs
vendored
@ -1,617 +0,0 @@
|
||||
use crate::env::environment::Environment;
|
||||
use nu_data::config::{Conf, NuConfig};
|
||||
use nu_engine::Env;
|
||||
use nu_engine::EvaluationContext;
|
||||
use nu_errors::ShellError;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::{atomic::Ordering, Arc};
|
||||
|
||||
pub struct EnvironmentSyncer {
|
||||
pub env: Arc<Mutex<Box<Environment>>>,
|
||||
pub config: Arc<Mutex<Box<dyn Conf>>>,
|
||||
}
|
||||
|
||||
impl Default for EnvironmentSyncer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl EnvironmentSyncer {
|
||||
pub fn with_config(config: Box<dyn Conf>) -> Self {
|
||||
EnvironmentSyncer {
|
||||
env: Arc::new(Mutex::new(Box::new(Environment::new()))),
|
||||
config: Arc::new(Mutex::new(config)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> EnvironmentSyncer {
|
||||
EnvironmentSyncer {
|
||||
env: Arc::new(Mutex::new(Box::new(Environment::new()))),
|
||||
config: Arc::new(Mutex::new(Box::new(NuConfig::new()))),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn set_config(&mut self, config: Box<dyn Conf>) {
|
||||
self.config = Arc::new(Mutex::new(config));
|
||||
}
|
||||
|
||||
pub fn get_config(&self) -> Box<dyn Conf> {
|
||||
let config = self.config.lock();
|
||||
|
||||
config.clone_box()
|
||||
}
|
||||
|
||||
pub fn load_environment(&mut self) {
|
||||
let config = self.config.lock();
|
||||
|
||||
self.env = Arc::new(Mutex::new(Box::new(Environment::from_config(&*config))));
|
||||
}
|
||||
|
||||
pub fn did_config_change(&mut self) -> bool {
|
||||
let config = self.config.lock();
|
||||
config.is_modified().unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn reload(&mut self) {
|
||||
let mut config = self.config.lock();
|
||||
config.reload();
|
||||
|
||||
let mut environment = self.env.lock();
|
||||
environment.morph(&*config);
|
||||
}
|
||||
|
||||
pub fn autoenv(&self, ctx: &mut EvaluationContext) -> Result<(), ShellError> {
|
||||
let mut environment = self.env.lock();
|
||||
let recently_used = ctx
|
||||
.user_recently_used_autoenv_untrust
|
||||
.load(Ordering::SeqCst);
|
||||
let auto = environment.autoenv(recently_used);
|
||||
ctx.user_recently_used_autoenv_untrust
|
||||
.store(false, Ordering::SeqCst);
|
||||
auto
|
||||
}
|
||||
|
||||
pub fn sync_env_vars(&mut self, ctx: &mut EvaluationContext) {
|
||||
let mut environment = self.env.lock();
|
||||
|
||||
if environment.env().is_some() {
|
||||
for (name, value) in ctx.with_host(|host| host.vars()) {
|
||||
if name != "path" && name != "PATH" {
|
||||
// account for new env vars present in the current session
|
||||
// that aren't loaded from config.
|
||||
environment.add_env(&name, &value);
|
||||
|
||||
// clear the env var from the session
|
||||
// we are about to replace them
|
||||
ctx.with_host(|host| host.env_rm(std::ffi::OsString::from(name)));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(variables) = environment.env() {
|
||||
for var in variables.row_entries() {
|
||||
if let Ok(string) = var.1.as_string() {
|
||||
ctx.with_host(|host| {
|
||||
host.env_set(
|
||||
std::ffi::OsString::from(var.0),
|
||||
std::ffi::OsString::from(&string),
|
||||
)
|
||||
});
|
||||
|
||||
ctx.scope.add_env_var_to_base(var.0, string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sync_path_vars(&mut self, ctx: &mut EvaluationContext) {
|
||||
let mut environment = self.env.lock();
|
||||
|
||||
if environment.path().is_some() {
|
||||
let native_paths = ctx.with_host(|host| host.env_get(std::ffi::OsString::from("PATH")));
|
||||
|
||||
if let Some(native_paths) = native_paths {
|
||||
environment.add_path(native_paths);
|
||||
|
||||
ctx.with_host(|host| {
|
||||
host.env_rm(std::ffi::OsString::from("PATH"));
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(new_paths) = environment.path() {
|
||||
let prepared = std::env::join_paths(
|
||||
new_paths
|
||||
.table_entries()
|
||||
.map(|p| p.as_string())
|
||||
.filter_map(Result::ok),
|
||||
);
|
||||
|
||||
if let Ok(paths_ready) = prepared {
|
||||
ctx.with_host(|host| {
|
||||
host.env_set(
|
||||
std::ffi::OsString::from("PATH"),
|
||||
std::ffi::OsString::from(&paths_ready),
|
||||
);
|
||||
});
|
||||
|
||||
ctx.scope
|
||||
.add_env_var_to_base("PATH", paths_ready.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn clear_env_vars(&mut self, ctx: &mut EvaluationContext) {
|
||||
for (key, _value) in ctx.with_host(|host| host.vars()) {
|
||||
if key != "path" && key != "PATH" {
|
||||
ctx.with_host(|host| host.env_rm(std::ffi::OsString::from(key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn clear_path_var(&mut self, ctx: &mut EvaluationContext) {
|
||||
ctx.with_host(|host| host.env_rm(std::ffi::OsString::from("PATH")));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::EnvironmentSyncer;
|
||||
use indexmap::IndexMap;
|
||||
use nu_data::config::tests::FakeConfig;
|
||||
use nu_engine::Env;
|
||||
use nu_engine::EvaluationContext;
|
||||
use nu_errors::ShellError;
|
||||
use nu_test_support::fs::Stub::FileWithContent;
|
||||
use nu_test_support::playground::Playground;
|
||||
use parking_lot::Mutex;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
// This test fails on Linux.
|
||||
// It's possible it has something to do with the fake configuration
|
||||
// TODO: More tests.
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[test]
|
||||
fn syncs_env_if_new_env_entry_is_added_to_an_existing_configuration() -> Result<(), ShellError>
|
||||
{
|
||||
let mut ctx = EvaluationContext::basic()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let mut expected = IndexMap::new();
|
||||
expected.insert(
|
||||
"SHELL".to_string(),
|
||||
"/usr/bin/you_already_made_the_nu_choice".to_string(),
|
||||
);
|
||||
|
||||
Playground::setup("syncs_env_from_config_updated_test_1", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![
|
||||
FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
SHELL = "/usr/bin/you_already_made_the_nu_choice"
|
||||
"#,
|
||||
),
|
||||
FileWithContent(
|
||||
"updated_configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
SHELL = "/usr/bin/you_already_made_the_nu_choice"
|
||||
USER = "NUNO"
|
||||
"#,
|
||||
),
|
||||
]);
|
||||
|
||||
let file = dirs.test().join("configuration.toml");
|
||||
let new_file = dirs.test().join("updated_configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = EnvironmentSyncer::with_config(Box::new(fake_config));
|
||||
|
||||
// Here, the environment variables from the current session
|
||||
// are cleared since we will load and set them from the
|
||||
// configuration file
|
||||
actual.clear_env_vars(&mut ctx);
|
||||
|
||||
// Nu loads the environment variables from the configuration file
|
||||
actual.load_environment();
|
||||
actual.sync_env_vars(&mut ctx);
|
||||
|
||||
{
|
||||
let environment = actual.env.lock();
|
||||
let mut vars = IndexMap::new();
|
||||
environment
|
||||
.env()
|
||||
.expect("No variables in the environment.")
|
||||
.row_entries()
|
||||
.for_each(|(name, value)| {
|
||||
vars.insert(
|
||||
name.to_string(),
|
||||
value.as_string().expect("Couldn't convert to string"),
|
||||
);
|
||||
});
|
||||
|
||||
for k in expected.keys() {
|
||||
assert!(vars.contains_key(k));
|
||||
}
|
||||
}
|
||||
|
||||
assert!(!actual.did_config_change());
|
||||
|
||||
// Replacing the newer configuration file to the existing one.
|
||||
let new_config_contents = std::fs::read_to_string(new_file).expect("Failed");
|
||||
std::fs::write(&file, &new_config_contents).expect("Failed");
|
||||
|
||||
// A change has happened
|
||||
assert!(actual.did_config_change());
|
||||
|
||||
// Syncer should reload and add new envs
|
||||
actual.reload();
|
||||
actual.sync_env_vars(&mut ctx);
|
||||
|
||||
expected.insert("USER".to_string(), "NUNO".to_string());
|
||||
|
||||
{
|
||||
let environment = actual.env.lock();
|
||||
let mut vars = IndexMap::new();
|
||||
environment
|
||||
.env()
|
||||
.expect("No variables in the environment.")
|
||||
.row_entries()
|
||||
.for_each(|(name, value)| {
|
||||
vars.insert(
|
||||
name.to_string(),
|
||||
value.as_string().expect("Couldn't convert to string"),
|
||||
);
|
||||
});
|
||||
|
||||
for k in expected.keys() {
|
||||
assert!(vars.contains_key(k));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syncs_env_if_new_env_entry_in_session_is_not_in_configuration_file() -> Result<(), ShellError>
|
||||
{
|
||||
let mut ctx = EvaluationContext::basic()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let mut expected = IndexMap::new();
|
||||
expected.insert(
|
||||
"SHELL".to_string(),
|
||||
"/usr/bin/you_already_made_the_nu_choice".to_string(),
|
||||
);
|
||||
expected.insert("USER".to_string(), "NUNO".to_string());
|
||||
|
||||
Playground::setup("syncs_env_test_1", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
SHELL = "/usr/bin/you_already_made_the_nu_choice"
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = EnvironmentSyncer::new();
|
||||
actual.set_config(Box::new(fake_config));
|
||||
|
||||
// Here, the environment variables from the current session
|
||||
// are cleared since we will load and set them from the
|
||||
// configuration file (if any)
|
||||
actual.clear_env_vars(&mut ctx);
|
||||
|
||||
// We explicitly simulate and add the USER variable to the current
|
||||
// session's environment variables with the value "NUNO".
|
||||
ctx.with_host(|test_host| {
|
||||
test_host.env_set(
|
||||
std::ffi::OsString::from("USER"),
|
||||
std::ffi::OsString::from("NUNO"),
|
||||
)
|
||||
});
|
||||
|
||||
// Nu loads the environment variables from the configuration file (if any)
|
||||
actual.load_environment();
|
||||
|
||||
// By this point, Nu has already loaded the environment variables
|
||||
// stored in the configuration file. Before continuing we check
|
||||
// if any new environment variables have been added from the ones loaded
|
||||
// in the configuration file.
|
||||
//
|
||||
// Nu sees the missing "USER" variable and accounts for it.
|
||||
actual.sync_env_vars(&mut ctx);
|
||||
|
||||
// Confirms session environment variables are replaced from Nu configuration file
|
||||
// including the newer one accounted for.
|
||||
ctx.with_host(|test_host| {
|
||||
let var_user = test_host
|
||||
.env_get(std::ffi::OsString::from("USER"))
|
||||
.expect("Couldn't get USER var from host.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
let var_shell = test_host
|
||||
.env_get(std::ffi::OsString::from("SHELL"))
|
||||
.expect("Couldn't get SHELL var from host.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
let mut found = IndexMap::new();
|
||||
found.insert("SHELL".to_string(), var_shell);
|
||||
found.insert("USER".to_string(), var_user);
|
||||
|
||||
for k in found.keys() {
|
||||
assert!(expected.contains_key(k));
|
||||
}
|
||||
});
|
||||
|
||||
// Now confirm in-memory environment variables synced appropriately
|
||||
// including the newer one accounted for.
|
||||
let environment = actual.env.lock();
|
||||
|
||||
let mut vars = IndexMap::new();
|
||||
environment
|
||||
.env()
|
||||
.expect("No variables in the environment.")
|
||||
.row_entries()
|
||||
.for_each(|(name, value)| {
|
||||
vars.insert(
|
||||
name.to_string(),
|
||||
value.as_string().expect("Couldn't convert to string"),
|
||||
);
|
||||
});
|
||||
for k in expected.keys() {
|
||||
assert!(vars.contains_key(k));
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nu_envs_have_higher_priority_and_does_not_get_overwritten() -> Result<(), ShellError> {
|
||||
let mut ctx = EvaluationContext::basic()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let mut expected = IndexMap::new();
|
||||
expected.insert(
|
||||
"SHELL".to_string(),
|
||||
"/usr/bin/you_already_made_the_nu_choice".to_string(),
|
||||
);
|
||||
|
||||
Playground::setup("syncs_env_test_2", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
SHELL = "/usr/bin/you_already_made_the_nu_choice"
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = EnvironmentSyncer::new();
|
||||
actual.set_config(Box::new(fake_config));
|
||||
|
||||
actual.clear_env_vars(&mut ctx);
|
||||
|
||||
ctx.with_host(|test_host| {
|
||||
test_host.env_set(
|
||||
std::ffi::OsString::from("SHELL"),
|
||||
std::ffi::OsString::from("/usr/bin/sh"),
|
||||
)
|
||||
});
|
||||
|
||||
actual.load_environment();
|
||||
actual.sync_env_vars(&mut ctx);
|
||||
|
||||
ctx.with_host(|test_host| {
|
||||
let var_shell = test_host
|
||||
.env_get(std::ffi::OsString::from("SHELL"))
|
||||
.expect("Couldn't get SHELL var from host.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
let mut found = IndexMap::new();
|
||||
found.insert("SHELL".to_string(), var_shell);
|
||||
|
||||
for k in found.keys() {
|
||||
assert!(expected.contains_key(k));
|
||||
}
|
||||
});
|
||||
|
||||
let environment = actual.env.lock();
|
||||
|
||||
let mut vars = IndexMap::new();
|
||||
environment
|
||||
.env()
|
||||
.expect("No variables in the environment.")
|
||||
.row_entries()
|
||||
.for_each(|(name, value)| {
|
||||
vars.insert(
|
||||
name.to_string(),
|
||||
value.as_string().expect("couldn't convert to string"),
|
||||
);
|
||||
});
|
||||
for k in expected.keys() {
|
||||
assert!(vars.contains_key(k));
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syncs_path_if_new_path_entry_in_session_is_not_in_configuration_file(
|
||||
) -> Result<(), ShellError> {
|
||||
let mut ctx = EvaluationContext::basic()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let expected = std::env::join_paths(vec![
|
||||
PathBuf::from("/Users/andresrobalino/.volta/bin"),
|
||||
PathBuf::from("/Users/mosqueteros/bin"),
|
||||
PathBuf::from("/path/to/be/added"),
|
||||
])
|
||||
.expect("Couldn't join paths.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
Playground::setup("syncs_path_test_1", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
path = ["/Users/andresrobalino/.volta/bin", "/Users/mosqueteros/bin"]
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = EnvironmentSyncer::new();
|
||||
actual.set_config(Box::new(fake_config));
|
||||
|
||||
// Here, the environment variables from the current session
|
||||
// are cleared since we will load and set them from the
|
||||
// configuration file (if any)
|
||||
actual.clear_path_var(&mut ctx);
|
||||
|
||||
// We explicitly simulate and add the PATH variable to the current
|
||||
// session with the path "/path/to/be/added".
|
||||
ctx.with_host(|test_host| {
|
||||
test_host.env_set(
|
||||
std::ffi::OsString::from("PATH"),
|
||||
std::env::join_paths(vec![PathBuf::from("/path/to/be/added")])
|
||||
.expect("Couldn't join paths."),
|
||||
)
|
||||
});
|
||||
|
||||
// Nu loads the path variables from the configuration file (if any)
|
||||
actual.load_environment();
|
||||
|
||||
// By this point, Nu has already loaded environment path variable
|
||||
// stored in the configuration file. Before continuing we check
|
||||
// if any new paths have been added from the ones loaded in the
|
||||
// configuration file.
|
||||
//
|
||||
// Nu sees the missing "/path/to/be/added" and accounts for it.
|
||||
actual.sync_path_vars(&mut ctx);
|
||||
|
||||
ctx.with_host(|test_host| {
|
||||
let actual = test_host
|
||||
.env_get(std::ffi::OsString::from("PATH"))
|
||||
.expect("Couldn't get PATH var from host.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
});
|
||||
|
||||
let environment = actual.env.lock();
|
||||
|
||||
let paths = std::env::join_paths(
|
||||
&environment
|
||||
.path()
|
||||
.expect("No path variable in the environment.")
|
||||
.table_entries()
|
||||
.map(|value| value.as_string().expect("Couldn't convert to string"))
|
||||
.map(PathBuf::from)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.expect("Couldn't join paths.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
assert_eq!(paths, expected);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nu_paths_have_higher_priority_and_new_paths_get_appended_to_the_end(
|
||||
) -> Result<(), ShellError> {
|
||||
let mut ctx = EvaluationContext::basic()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let expected = std::env::join_paths(vec![
|
||||
PathBuf::from("/Users/andresrobalino/.volta/bin"),
|
||||
PathBuf::from("/Users/mosqueteros/bin"),
|
||||
PathBuf::from("/path/to/be/added"),
|
||||
])
|
||||
.expect("Couldn't join paths.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
Playground::setup("syncs_path_test_2", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
path = ["/Users/andresrobalino/.volta/bin", "/Users/mosqueteros/bin"]
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = EnvironmentSyncer::new();
|
||||
actual.set_config(Box::new(fake_config));
|
||||
|
||||
actual.clear_path_var(&mut ctx);
|
||||
|
||||
ctx.with_host(|test_host| {
|
||||
test_host.env_set(
|
||||
std::ffi::OsString::from("PATH"),
|
||||
std::env::join_paths(vec![PathBuf::from("/path/to/be/added")])
|
||||
.expect("Couldn't join paths."),
|
||||
)
|
||||
});
|
||||
|
||||
actual.load_environment();
|
||||
actual.sync_path_vars(&mut ctx);
|
||||
|
||||
ctx.with_host(|test_host| {
|
||||
let actual = test_host
|
||||
.env_get(std::ffi::OsString::from("PATH"))
|
||||
.expect("Couldn't get PATH var from host.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
});
|
||||
|
||||
let environment = actual.env.lock();
|
||||
|
||||
let paths = std::env::join_paths(
|
||||
&environment
|
||||
.path()
|
||||
.expect("No path variable in the environment.")
|
||||
.table_entries()
|
||||
.map(|value| value.as_string().expect("Couldn't convert to string"))
|
||||
.map(PathBuf::from)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.expect("Couldn't join paths.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
assert_eq!(paths, expected);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,9 +1,5 @@
|
||||
#![recursion_limit = "2048"]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate indexmap;
|
||||
|
||||
#[macro_use]
|
||||
mod prelude;
|
||||
|
||||
@ -16,7 +12,6 @@ extern crate quickcheck_macros;
|
||||
mod cli;
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
mod completion;
|
||||
mod env;
|
||||
mod format;
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
mod keybinding;
|
||||
@ -30,13 +25,12 @@ pub use crate::cli::cli;
|
||||
pub use crate::cli::{parse_and_eval, register_plugins, run_script_file};
|
||||
pub use crate::cli::{NuScript, Options};
|
||||
|
||||
pub use crate::env::environment_syncer::EnvironmentSyncer;
|
||||
pub use nu_command::commands::default_context::create_default_context;
|
||||
pub use nu_data::config;
|
||||
pub use nu_data::dict::TaggedListBuilder;
|
||||
pub use nu_data::primitive;
|
||||
pub use nu_data::value;
|
||||
pub use nu_stream::{InputStream, InterruptibleStream, OutputStream};
|
||||
pub use nu_stream::{ActionStream, InputStream, InterruptibleStream};
|
||||
pub use nu_value_ext::ValueExt;
|
||||
pub use num_traits::cast::ToPrimitive;
|
||||
|
||||
|
@ -202,7 +202,7 @@ pub fn configure_rustyline_editor(
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub fn nu_line_editor_helper(
|
||||
context: &mut EvaluationContext,
|
||||
context: &EvaluationContext,
|
||||
config: &dyn nu_data::config::Conf,
|
||||
) -> crate::shell::Helper {
|
||||
let hinter = rustyline_hinter(config);
|
||||
@ -224,7 +224,7 @@ pub fn rustyline_hinter(
|
||||
Some(rustyline::hint::HistoryHinter {})
|
||||
}
|
||||
|
||||
pub fn configure_ctrl_c(_context: &mut EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
pub fn configure_ctrl_c(_context: &EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
#[cfg(feature = "ctrlc")]
|
||||
{
|
||||
let cc = _context.ctrl_c.clone();
|
||||
|
@ -8,25 +8,10 @@ macro_rules! return_err {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! stream {
|
||||
($($expr:expr),*) => {{
|
||||
let mut v = VecDeque::new();
|
||||
|
||||
$(
|
||||
v.push_back($expr);
|
||||
)*
|
||||
|
||||
v
|
||||
}}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! trace_out_stream {
|
||||
(target: $target:tt, $desc:tt = $expr:expr) => {{
|
||||
if log::log_enabled!(target: $target, log::Level::Trace) {
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
let objects = $expr.inspect(move |o| {
|
||||
trace!(
|
||||
target: $target,
|
||||
@ -46,13 +31,12 @@ macro_rules! trace_out_stream {
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) use futures::{Stream, StreamExt};
|
||||
pub(crate) use nu_engine::Host;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_errors::ShellError;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_protocol::outln;
|
||||
pub(crate) use nu_stream::OutputStream;
|
||||
pub(crate) use nu_stream::ActionStream;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_value_ext::ValueExt;
|
||||
#[allow(unused_imports)]
|
||||
@ -60,33 +44,16 @@ pub(crate) use std::sync::atomic::Ordering;
|
||||
|
||||
#[allow(clippy::clippy::wrong_self_convention)]
|
||||
pub trait FromInputStream {
|
||||
fn from_input_stream(self) -> OutputStream;
|
||||
fn from_input_stream(self) -> ActionStream;
|
||||
}
|
||||
|
||||
impl<T> FromInputStream for T
|
||||
where
|
||||
T: Stream<Item = nu_protocol::Value> + Send + 'static,
|
||||
T: Iterator<Item = nu_protocol::Value> + Send + Sync + 'static,
|
||||
{
|
||||
fn from_input_stream(self) -> OutputStream {
|
||||
OutputStream {
|
||||
values: self.map(nu_protocol::ReturnSuccess::value).boxed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::clippy::wrong_self_convention)]
|
||||
pub trait ToOutputStream {
|
||||
fn to_output_stream(self) -> OutputStream;
|
||||
}
|
||||
|
||||
impl<T, U> ToOutputStream for T
|
||||
where
|
||||
T: Stream<Item = U> + Send + 'static,
|
||||
U: Into<nu_protocol::ReturnValue>,
|
||||
{
|
||||
fn to_output_stream(self) -> OutputStream {
|
||||
OutputStream {
|
||||
values: self.map(|item| item.into()).boxed(),
|
||||
fn from_input_stream(self) -> ActionStream {
|
||||
ActionStream {
|
||||
values: Box::new(self.map(nu_protocol::ReturnSuccess::value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::completion;
|
||||
use crate::shell::completer::NuCompleter;
|
||||
use nu_ansi_term::Color;
|
||||
use nu_engine::{DefaultPalette, EvaluationContext, Painter};
|
||||
use nu_source::{Tag, Tagged};
|
||||
use std::borrow::Cow::{self, Owned};
|
||||
@ -79,7 +80,7 @@ impl rustyline::highlight::Highlighter for Helper {
|
||||
}
|
||||
|
||||
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
|
||||
Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
|
||||
Owned(Color::DarkGray.prefix().to_string() + hint + nu_ansi_term::ansi::RESET)
|
||||
}
|
||||
|
||||
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
|
||||
@ -149,6 +150,7 @@ impl rustyline::Helper for Helper {}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nu_engine::EvaluationContext;
|
||||
use rustyline::completion::Completer;
|
||||
use rustyline::line_buffer::LineBuffer;
|
||||
|
||||
@ -162,7 +164,7 @@ mod tests {
|
||||
buffer.insert_str(0, text);
|
||||
buffer.set_pos(text.len() - 1);
|
||||
|
||||
let helper = Helper::new(EvaluationContext::basic().unwrap(), None);
|
||||
let helper = Helper::new(EvaluationContext::basic(), None);
|
||||
|
||||
helper.update(&mut buffer, "cd ".len(), &replacement);
|
||||
|
||||
@ -182,7 +184,7 @@ mod tests {
|
||||
buffer.insert_str(0, text);
|
||||
buffer.set_pos(text.len() - 30);
|
||||
|
||||
let helper = Helper::new(EvaluationContext::basic().unwrap(), None);
|
||||
let helper = Helper::new(EvaluationContext::basic(), None);
|
||||
|
||||
helper.update(&mut buffer, "cd ".len(), &replacement);
|
||||
|
||||
|
@ -285,7 +285,7 @@ fn get_shape_of_expr(expr: &SpannedExpression) -> Option<SyntaxShape> {
|
||||
nu_protocol::hir::Literal::Bare(_) => Some(SyntaxShape::String),
|
||||
}
|
||||
}
|
||||
//Synthetic are expressions that are generated by the parser and not inputed by the user
|
||||
//Synthetic are expressions that are generated by the parser and not inputted by the user
|
||||
//ExternalWord is anything sent to external commands (?)
|
||||
Expression::ExternalWord => Some(SyntaxShape::String),
|
||||
Expression::Synthetic(_) => Some(SyntaxShape::String),
|
||||
@ -377,17 +377,14 @@ impl VarSyntaxShapeDeductor {
|
||||
.iter()
|
||||
.map(|decl| {
|
||||
let usage: VarUsage = decl.into();
|
||||
let deduction = match deducer.inferences.get(&usage) {
|
||||
Some(vec) => Some(vec.clone()),
|
||||
None => None,
|
||||
};
|
||||
let deduction = deducer.inferences.get(&usage).cloned();
|
||||
(decl.clone(), deduction)
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn infer_shape(&mut self, block: &Block, scope: &Scope) -> Result<(), ShellError> {
|
||||
trace!("Infering vars in shape");
|
||||
trace!("Inferring vars in shape");
|
||||
for group in &block.block {
|
||||
for pipeline in &group.pipelines {
|
||||
self.infer_pipeline(pipeline, scope)?;
|
||||
@ -397,7 +394,7 @@ impl VarSyntaxShapeDeductor {
|
||||
}
|
||||
|
||||
pub fn infer_pipeline(&mut self, pipeline: &Pipeline, scope: &Scope) -> Result<(), ShellError> {
|
||||
trace!("Infering vars in pipeline");
|
||||
trace!("Inferring vars in pipeline");
|
||||
for (cmd_pipeline_idx, classified) in pipeline.list.iter().enumerate() {
|
||||
match &classified {
|
||||
ClassifiedCommand::Internal(internal) => {
|
||||
@ -429,7 +426,7 @@ impl VarSyntaxShapeDeductor {
|
||||
}
|
||||
}
|
||||
if let Some(named) = &internal.args.named {
|
||||
trace!("Infering vars in named exprs");
|
||||
trace!("Inferring vars in named exprs");
|
||||
for (_name, val) in named.iter() {
|
||||
if let NamedValue::Value(_, named_expr) = val {
|
||||
self.infer_shapes_in_expr(
|
||||
@ -443,7 +440,7 @@ impl VarSyntaxShapeDeductor {
|
||||
}
|
||||
ClassifiedCommand::Expr(spanned_expr) => {
|
||||
trace!(
|
||||
"Infering shapes in ClassifiedCommand::Expr: {:?}",
|
||||
"Inferring shapes in ClassifiedCommand::Expr: {:?}",
|
||||
spanned_expr
|
||||
);
|
||||
self.infer_shapes_in_expr((cmd_pipeline_idx, pipeline), spanned_expr, scope)?;
|
||||
@ -459,7 +456,7 @@ impl VarSyntaxShapeDeductor {
|
||||
positionals: &[SpannedExpression],
|
||||
signature: &Signature,
|
||||
) -> Result<(), ShellError> {
|
||||
trace!("Infering vars in positionals");
|
||||
trace!("Inferring vars in positionals");
|
||||
//TODO currently correct inference for optional positionals is not implemented.
|
||||
// See https://github.com/nushell/nushell/pull/2486 for a discussion about this
|
||||
// For now we assume every variable in an optional positional is used as this optional
|
||||
@ -500,7 +497,7 @@ impl VarSyntaxShapeDeductor {
|
||||
named: &NamedArguments,
|
||||
signature: &Signature,
|
||||
) -> Result<(), ShellError> {
|
||||
trace!("Infering vars in named");
|
||||
trace!("Inferring vars in named");
|
||||
for (name, val) in named.iter() {
|
||||
if let NamedValue::Value(span, spanned_expr) = &val {
|
||||
if let Expression::Variable(var_name, _) = &spanned_expr.expr {
|
||||
@ -534,15 +531,15 @@ impl VarSyntaxShapeDeductor {
|
||||
) -> Result<(), ShellError> {
|
||||
match &spanned_expr.expr {
|
||||
Expression::Binary(_) => {
|
||||
trace!("Infering vars in bin expr");
|
||||
trace!("Inferring vars in bin expr");
|
||||
self.infer_shapes_in_binary_expr((pipeline_idx, pipeline), spanned_expr, scope)?;
|
||||
}
|
||||
Expression::Block(b) => {
|
||||
trace!("Infering vars in block");
|
||||
trace!("Inferring vars in block");
|
||||
self.infer_shape(&b, scope)?;
|
||||
}
|
||||
Expression::Path(path) => {
|
||||
trace!("Infering vars in path");
|
||||
trace!("Inferring vars in path");
|
||||
match &path.head.expr {
|
||||
//PathMember can't be var yet (?)
|
||||
//TODO Iterate over path parts and find var when implemented
|
||||
@ -560,7 +557,7 @@ impl VarSyntaxShapeDeductor {
|
||||
}
|
||||
}
|
||||
Expression::Range(range) => {
|
||||
trace!("Infering vars in range");
|
||||
trace!("Inferring vars in range");
|
||||
if let Some(range_left) = &range.left {
|
||||
if let Expression::Variable(var_name, _) = &range_left.expr {
|
||||
self.checked_insert(
|
||||
@ -585,13 +582,13 @@ impl VarSyntaxShapeDeductor {
|
||||
}
|
||||
}
|
||||
Expression::List(inner_exprs) => {
|
||||
trace!("Infering vars in list");
|
||||
trace!("Inferring vars in list");
|
||||
for expr in inner_exprs {
|
||||
self.infer_shapes_in_expr((pipeline_idx, pipeline), expr, scope)?;
|
||||
}
|
||||
}
|
||||
Expression::Invocation(invoc) => {
|
||||
trace!("Infering vars in invocation: {:?}", invoc);
|
||||
trace!("Inferring vars in invocation: {:?}", invoc);
|
||||
self.infer_shape(invoc, scope)?;
|
||||
}
|
||||
Expression::Table(header, _rows) => {
|
||||
@ -738,7 +735,7 @@ impl VarSyntaxShapeDeductor {
|
||||
(pipeline_idx, pipeline): (usize, &Pipeline),
|
||||
scope: &Scope,
|
||||
) -> Result<(), ShellError> {
|
||||
trace!("Infering shapes between var {:?} and expr {:?}", var, expr);
|
||||
trace!("Inferring shapes between var {:?} and expr {:?}", var, expr);
|
||||
let bin = spanned_to_binary(bin_spanned);
|
||||
if let Expression::Literal(Literal::Operator(op)) = bin.op.expr {
|
||||
match &op {
|
||||
@ -1023,7 +1020,7 @@ impl VarSyntaxShapeDeductor {
|
||||
Some(combination)
|
||||
}
|
||||
})
|
||||
.filter_map(|elem| elem)
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
//No any's intersection of both is result
|
||||
@ -1044,7 +1041,7 @@ impl VarSyntaxShapeDeductor {
|
||||
Some(combination)
|
||||
}
|
||||
})
|
||||
.filter_map(|elem| elem)
|
||||
.flatten()
|
||||
.collect();
|
||||
if intersection.is_empty() {
|
||||
//TODO pass all labels somehow
|
||||
|
@ -5,30 +5,29 @@ description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-command"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-data = { version = "0.29.0", path = "../nu-data" }
|
||||
nu-engine = { version = "0.29.0", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.29.0", path = "../nu-errors" }
|
||||
nu-json = { version = "0.29.0", path = "../nu-json" }
|
||||
nu-parser = { version = "0.29.0", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.29.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.29.0", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.29.0", path = "../nu-source" }
|
||||
nu-stream = { version = "0.29.0", path = "../nu-stream" }
|
||||
nu-table = { version = "0.29.0", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.29.0", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.29.0", path = "../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.29.0", path = "../nu-ansi-term" }
|
||||
nu-data = { version = "0.31.0", path = "../nu-data" }
|
||||
nu-engine = { version = "0.31.0", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.31.0", path = "../nu-errors" }
|
||||
nu-json = { version = "0.31.0", path = "../nu-json" }
|
||||
nu-parser = { version = "0.31.0", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.31.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.31.0", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.31.0", path = "../nu-source" }
|
||||
nu-stream = { version = "0.31.0", path = "../nu-stream" }
|
||||
nu-table = { version = "0.31.0", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.31.0", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.31.0", path = "../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.31.0", path = "../nu-ansi-term" }
|
||||
nu-pretty-hex = { version = "0.31.0", path = "../nu-pretty-hex" }
|
||||
|
||||
Inflector = "0.11"
|
||||
arboard = { version = "1.1.0", optional = true }
|
||||
async-recursion = "0.3.2"
|
||||
async-trait = "0.1.42"
|
||||
base64 = "0.13.0"
|
||||
bigdecimal = { version = "0.2.0", features = ["serde"] }
|
||||
byte-unit = "4.0.9"
|
||||
@ -51,13 +50,10 @@ encoding_rs = "0.8.28"
|
||||
filesize = "0.2.0"
|
||||
fs_extra = "1.2.0"
|
||||
futures = { version = "0.3.12", features = ["compat", "io-compat"] }
|
||||
futures-util = "0.3.12"
|
||||
futures_codec = "0.4.1"
|
||||
getset = "0.1.1"
|
||||
glob = "0.3.0"
|
||||
htmlescape = "0.3.1"
|
||||
ical = "0.7.0"
|
||||
ichwh = { version = "0.3.4", optional = true }
|
||||
indexmap = { version = "1.6.1", features = ["serde-1"] }
|
||||
itertools = "0.10.0"
|
||||
lazy_static = "1.*"
|
||||
@ -70,7 +66,6 @@ num-format = { version = "0.4.0", features = ["with-num-bigint"] }
|
||||
num-traits = "0.2.14"
|
||||
parking_lot = "0.11.1"
|
||||
pin-utils = "0.1.0"
|
||||
pretty-hex = "0.2.1"
|
||||
ptree = { version = "0.3.1", optional = true }
|
||||
query_interface = "0.3.5"
|
||||
quick-xml = "0.21.0"
|
||||
@ -101,7 +96,7 @@ trash = { version = "1.3.0", optional = true }
|
||||
unicode-segmentation = "1.7.1"
|
||||
url = "2.2.0"
|
||||
uuid_crate = { package = "uuid", version = "0.8.2", features = ["v4"], optional = true }
|
||||
which = { version = "4.0.2", optional = true }
|
||||
which = { version = "4.1.0", optional = true }
|
||||
zip = { version = "0.5.9", optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
|
@ -4,7 +4,9 @@ pub(crate) mod macros;
|
||||
mod from_delimited_data;
|
||||
mod to_delimited_data;
|
||||
|
||||
pub(crate) mod all;
|
||||
pub(crate) mod ansi;
|
||||
pub(crate) mod any;
|
||||
pub(crate) mod append;
|
||||
pub(crate) mod args;
|
||||
pub mod autoenv;
|
||||
@ -68,7 +70,7 @@ pub(crate) mod histogram;
|
||||
pub(crate) mod history;
|
||||
pub(crate) mod if_;
|
||||
pub(crate) mod insert;
|
||||
pub(crate) mod into_int;
|
||||
pub(crate) mod into;
|
||||
pub(crate) mod keep;
|
||||
pub(crate) mod last;
|
||||
pub(crate) mod length;
|
||||
@ -169,6 +171,10 @@ pub(crate) use each::EachWindow;
|
||||
pub(crate) use echo::Echo;
|
||||
pub(crate) use empty::Command as Empty;
|
||||
pub(crate) use if_::If;
|
||||
pub(crate) use into::Into;
|
||||
pub(crate) use into::IntoBinary;
|
||||
pub(crate) use into::IntoInt;
|
||||
pub(crate) use into::IntoString;
|
||||
pub(crate) use nu::NuPlugin;
|
||||
pub(crate) use update::Command as Update;
|
||||
pub(crate) mod kill;
|
||||
@ -176,6 +182,8 @@ pub(crate) use kill::Kill;
|
||||
pub(crate) mod clear;
|
||||
pub(crate) use clear::Clear;
|
||||
pub(crate) mod touch;
|
||||
pub(crate) use all::Command as All;
|
||||
pub(crate) use any::Command as Any;
|
||||
pub(crate) use enter::Enter;
|
||||
pub(crate) use every::Every;
|
||||
pub(crate) use exec::Exec;
|
||||
@ -208,7 +216,6 @@ pub(crate) use help::Help;
|
||||
pub(crate) use histogram::Histogram;
|
||||
pub(crate) use history::History;
|
||||
pub(crate) use insert::Command as Insert;
|
||||
pub(crate) use into_int::IntoInt;
|
||||
pub(crate) use keep::{Keep, KeepUntil, KeepWhile};
|
||||
pub(crate) use last::Last;
|
||||
pub(crate) use length::Length;
|
||||
@ -218,7 +225,8 @@ pub(crate) use lines::Lines;
|
||||
pub(crate) use ls::Ls;
|
||||
pub(crate) use math::{
|
||||
Math, MathAbs, MathAverage, MathCeil, MathEval, MathFloor, MathMaximum, MathMedian,
|
||||
MathMinimum, MathMode, MathProduct, MathRound, MathStddev, MathSummation, MathVariance,
|
||||
MathMinimum, MathMode, MathProduct, MathRound, MathSqrt, MathStddev, MathSummation,
|
||||
MathVariance,
|
||||
};
|
||||
pub(crate) use merge::Merge;
|
||||
pub(crate) use mkdir::Mkdir;
|
||||
@ -228,8 +236,8 @@ pub(crate) use nth::Nth;
|
||||
pub(crate) use open::Open;
|
||||
pub(crate) use parse::Parse;
|
||||
pub(crate) use path::{
|
||||
PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathExtension, PathFilestem,
|
||||
PathJoin, PathType,
|
||||
PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathJoin, PathParse, PathSplit,
|
||||
PathType,
|
||||
};
|
||||
pub(crate) use pivot::Pivot;
|
||||
pub(crate) use prepend::Prepend;
|
||||
@ -285,7 +293,7 @@ pub(crate) use touch::Touch;
|
||||
pub(crate) use uniq::Uniq;
|
||||
pub(crate) use url_::{UrlCommand, UrlHost, UrlPath, UrlQuery, UrlScheme};
|
||||
pub(crate) use version::Version;
|
||||
pub(crate) use where_::Where;
|
||||
pub(crate) use where_::Command as Where;
|
||||
pub(crate) use which_::Which;
|
||||
pub(crate) use with_env::WithEnv;
|
||||
pub(crate) use wrap::Wrap;
|
||||
|
136
crates/nu-command/src/commands/all.rs
Normal file
136
crates/nu-command/src/commands/all.rs
Normal file
@ -0,0 +1,136 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::evaluate_baseline_expr;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
struct AllArgs {
|
||||
predicate: CapturedBlock,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"all?"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("all?").required(
|
||||
"condition",
|
||||
SyntaxShape::RowCondition,
|
||||
"the condition that must match",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Find if the table rows matches the condition."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
all(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
use nu_protocol::Value;
|
||||
|
||||
vec![
|
||||
Example {
|
||||
description: "Find if services are running",
|
||||
example: "echo [[status]; [UP] [UP]] | all? status == UP",
|
||||
result: Some(vec![Value::from(true)]),
|
||||
},
|
||||
Example {
|
||||
description: "Check that all values are even",
|
||||
example: "echo [2 4 6 8] | all? $(= $it mod 2) == 0",
|
||||
result: Some(vec![Value::from(true)]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn all(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let args = args.evaluate_once()?;
|
||||
let all_args = AllArgs {
|
||||
predicate: args.req(0)?,
|
||||
};
|
||||
|
||||
let err = Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
args.call_info.name_tag.clone(),
|
||||
));
|
||||
|
||||
//This seems a little odd. Can't we have predicates with pipelines/multiple statements?
|
||||
let condition = {
|
||||
if all_args.predicate.block.block.len() != 1 {
|
||||
return err;
|
||||
}
|
||||
match all_args.predicate.block.block[0].pipelines.get(0) {
|
||||
Some(item) => match item.list.get(0) {
|
||||
Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
|
||||
_ => {
|
||||
return err;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let scope = args.scope();
|
||||
|
||||
let init = Ok(InputStream::one(
|
||||
UntaggedValue::boolean(true).into_value(&tag),
|
||||
));
|
||||
|
||||
// Variables in nu are immutable. Having the same variable accross invocations
|
||||
// of evaluate_baseline_expr does not mutate the variables and those each
|
||||
// invocations are independent of each other!
|
||||
scope.enter_scope();
|
||||
scope.add_vars(&all_args.predicate.captured.entries);
|
||||
let result = args.input.fold(init, move |acc, row| {
|
||||
let condition = condition.clone();
|
||||
let ctx = ctx.clone();
|
||||
ctx.scope.add_var("$it", row);
|
||||
|
||||
let condition = evaluate_baseline_expr(&condition, &ctx);
|
||||
|
||||
let curr = acc?.drain_vec();
|
||||
let curr = curr
|
||||
.get(0)
|
||||
.ok_or_else(|| ShellError::unexpected("No value to check with"))?;
|
||||
let cond = curr.as_bool()?;
|
||||
|
||||
match condition {
|
||||
Ok(condition) => match condition.as_bool() {
|
||||
Ok(b) => Ok(InputStream::one(
|
||||
UntaggedValue::boolean(cond && b).into_value(&curr.tag),
|
||||
)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
});
|
||||
scope.exit_scope();
|
||||
|
||||
Ok(result?.to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Command;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Command {})
|
||||
}
|
||||
}
|
@ -2,19 +2,11 @@ use crate::prelude::*;
|
||||
use nu_ansi_term::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AnsiArgs {
|
||||
code: Value,
|
||||
escape: Option<Tagged<String>>,
|
||||
osc: Option<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"ansi"
|
||||
@ -120,8 +112,12 @@ Format: #
|
||||
]
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (AnsiArgs { code, escape, osc }, _) = args.process().await?;
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let code: Option<Tagged<String>> = args.opt(0)?;
|
||||
let escape: Option<Tagged<String>> = args.get_flag("escape")?;
|
||||
let osc: Option<Tagged<String>> = args.get_flag("osc")?;
|
||||
|
||||
if let Some(e) = escape {
|
||||
let esc_vec: Vec<char> = e.item.chars().collect();
|
||||
@ -133,9 +129,9 @@ Format: #
|
||||
));
|
||||
}
|
||||
let output = format!("\x1b[{}", e.item);
|
||||
return Ok(OutputStream::one(ReturnSuccess::value(
|
||||
return Ok(OutputStream::one(
|
||||
UntaggedValue::string(output).into_value(e.tag()),
|
||||
)));
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(o) = osc {
|
||||
@ -151,30 +147,37 @@ Format: #
|
||||
//Operating system command aka osc ESC ] <- note the right brace, not left brace for osc
|
||||
// OCS's need to end with a bell '\x07' char
|
||||
let output = format!("\x1b]{};", o.item);
|
||||
return Ok(OutputStream::one(ReturnSuccess::value(
|
||||
return Ok(OutputStream::one(
|
||||
UntaggedValue::string(output).into_value(o.tag()),
|
||||
)));
|
||||
));
|
||||
}
|
||||
|
||||
let code_string = code.as_string()?;
|
||||
let ansi_code = str_to_ansi(code_string);
|
||||
if let Some(code) = code {
|
||||
let ansi_code = str_to_ansi(&code.item);
|
||||
|
||||
if let Some(output) = ansi_code {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(code.tag()),
|
||||
)))
|
||||
if let Some(output) = ansi_code {
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(output).into_value(code.tag()),
|
||||
))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Unknown ansi code",
|
||||
"unknown ansi code",
|
||||
code.tag(),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Unknown ansi code",
|
||||
"unknown ansi code",
|
||||
code.tag(),
|
||||
"Expected ansi code",
|
||||
"expect ansi code",
|
||||
args.call_info.name_tag.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn str_to_ansi(s: String) -> Option<String> {
|
||||
match s.as_str() {
|
||||
pub fn str_to_ansi(s: &str) -> Option<String> {
|
||||
match s {
|
||||
"g" | "green" => Some(Color::Green.prefix().to_string()),
|
||||
"gb" | "green_bold" => Some(Color::Green.bold().prefix().to_string()),
|
||||
"gu" | "green_underline" => Some(Color::Green.underline().prefix().to_string()),
|
||||
@ -330,7 +333,7 @@ pub fn str_to_ansi(s: String) -> Option<String> {
|
||||
|
||||
// Ansi RGB - Needs to be 32;2;r;g;b or 48;2;r;g;b
|
||||
// assuming the rgb will be passed via command and no here
|
||||
"rgb_fg" => Some("\x1b[32;2;".to_string()),
|
||||
"rgb_fg" => Some("\x1b[38;2;".to_string()),
|
||||
"rgb_bg" => Some("\x1b[48;2;".to_string()),
|
||||
|
||||
// Ansi color index - Needs 38;5;idx or 48;5;idx where idx = 0 to 255
|
||||
|
@ -2,20 +2,12 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::ShellTypeName;
|
||||
use nu_protocol::{
|
||||
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
use strip_ansi_escapes::strip;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Arguments {
|
||||
rest: Vec<ColumnPath>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"ansi strip"
|
||||
@ -32,8 +24,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"strip ansi escape sequences from string"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
operate(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
operate(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -45,14 +37,16 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { rest }, input) = args.process().await?;
|
||||
let column_paths: Vec<_> = rest;
|
||||
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
Ok(input
|
||||
let column_paths: Vec<_> = args.rest_args()?;
|
||||
|
||||
let result: Vec<Value> = args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
ReturnSuccess::value(action(&v, v.tag())?)
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
|
||||
@ -63,10 +57,12 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
)?;
|
||||
}
|
||||
|
||||
ReturnSuccess::value(ret)
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
.collect::<Result<Vec<Value>, _>>()?;
|
||||
|
||||
Ok(OutputStream::from_stream(result.into_iter()))
|
||||
}
|
||||
|
||||
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
|
137
crates/nu-command/src/commands/any.rs
Normal file
137
crates/nu-command/src/commands/any.rs
Normal file
@ -0,0 +1,137 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::evaluate_baseline_expr;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
struct AnyArgs {
|
||||
predicate: CapturedBlock,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"any?"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("any?").required(
|
||||
"condition",
|
||||
SyntaxShape::RowCondition,
|
||||
"the condition that must match",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Find if the table rows matches the condition."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
any(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
use nu_protocol::Value;
|
||||
|
||||
vec![
|
||||
Example {
|
||||
description: "Find if a service is not running",
|
||||
example: "echo [[status]; [UP] [DOWN] [UP]] | any? status == DOWN",
|
||||
result: Some(vec![Value::from(true)]),
|
||||
},
|
||||
Example {
|
||||
description: "Check if any of the values is odd",
|
||||
example: "echo [2 4 1 6 8] | any? $(= $it mod 2) == 1",
|
||||
result: Some(vec![Value::from(true)]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn any(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let args = args.evaluate_once()?;
|
||||
let any_args = AnyArgs {
|
||||
predicate: args.req(0)?,
|
||||
};
|
||||
|
||||
let err = Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
args.call_info.name_tag.clone(),
|
||||
));
|
||||
|
||||
//This seems a little odd. Can't we have predicates with pipelines/multiple statements?
|
||||
let condition = {
|
||||
if any_args.predicate.block.block.len() != 1 {
|
||||
return err;
|
||||
}
|
||||
match any_args.predicate.block.block[0].pipelines.get(0) {
|
||||
Some(item) => match item.list.get(0) {
|
||||
Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
|
||||
_ => {
|
||||
return err;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let scope = args.scope();
|
||||
|
||||
let init = Ok(InputStream::one(
|
||||
UntaggedValue::boolean(false).into_value(&tag),
|
||||
));
|
||||
|
||||
// Variables in nu are immutable. Having the same variable accross invocations
|
||||
// of evaluate_baseline_expr does not mutate the variables and thus each
|
||||
// invocations are independent of each other!
|
||||
scope.enter_scope();
|
||||
scope.add_vars(&any_args.predicate.captured.entries);
|
||||
|
||||
let result = args.input.fold(init, move |acc, row| {
|
||||
let condition = condition.clone();
|
||||
let ctx = ctx.clone();
|
||||
ctx.scope.add_var("$it", row);
|
||||
|
||||
let condition = evaluate_baseline_expr(&condition, &ctx);
|
||||
|
||||
let curr = acc?.drain_vec();
|
||||
let curr = curr
|
||||
.get(0)
|
||||
.ok_or_else(|| ShellError::unexpected("No value to check with"))?;
|
||||
let cond = curr.as_bool()?;
|
||||
|
||||
match condition {
|
||||
Ok(condition) => match condition.as_bool() {
|
||||
Ok(b) => Ok(InputStream::one(
|
||||
UntaggedValue::boolean(cond || b).into_value(&curr.tag),
|
||||
)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
});
|
||||
scope.exit_scope();
|
||||
|
||||
Ok(result?.to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Command;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Command {})
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Arguments {
|
||||
@ -10,7 +10,6 @@ struct Arguments {
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"append"
|
||||
@ -28,13 +27,14 @@ impl WholeStreamCommand for Command {
|
||||
"Append a row to the table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { mut value }, input) = args.process().await?;
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { mut value }, mut input) = args.process()?;
|
||||
|
||||
let input: Vec<Value> = input.collect().await;
|
||||
let mut prepend = vec![];
|
||||
|
||||
if let Some(first) = input.get(0) {
|
||||
if let Some(first) = input.next() {
|
||||
value.tag = first.tag();
|
||||
prepend.push(first);
|
||||
}
|
||||
|
||||
// Checks if we are trying to append a row literal
|
||||
@ -48,18 +48,13 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(futures::stream::iter(
|
||||
input
|
||||
.into_iter()
|
||||
.chain(vec![value])
|
||||
.map(ReturnSuccess::value),
|
||||
)
|
||||
.to_output_stream())
|
||||
Ok(prepend
|
||||
.into_iter()
|
||||
.chain(input.into_iter().chain(vec![value]))
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
use nu_protocol::row;
|
||||
|
||||
vec![
|
||||
Example {
|
||||
description: "Add values to the end of the table",
|
||||
|
@ -2,50 +2,8 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
pub struct Autoenv;
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Default)]
|
||||
pub struct Trusted {
|
||||
pub files: IndexMap<String, Vec<u8>>,
|
||||
}
|
||||
impl Trusted {
|
||||
pub fn new() -> Self {
|
||||
Trusted {
|
||||
files: IndexMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn file_is_trusted(nu_env_file: &Path, content: &[u8]) -> Result<bool, ShellError> {
|
||||
let contentdigest = Sha256::digest(&content).as_slice().to_vec();
|
||||
let nufile = std::fs::canonicalize(nu_env_file)?;
|
||||
|
||||
let trusted = read_trusted()?;
|
||||
|
||||
Ok(trusted.files.get(&nufile.to_string_lossy().to_string()) == Some(&contentdigest))
|
||||
}
|
||||
|
||||
pub fn read_trusted() -> Result<Trusted, ShellError> {
|
||||
let config_path = config::default_path_for(&Some(PathBuf::from("nu-env.toml")))?;
|
||||
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(config_path)
|
||||
.map_err(|_| ShellError::untagged_runtime_error("Couldn't open nu-env.toml"))?;
|
||||
let mut doc = String::new();
|
||||
file.read_to_string(&mut doc)?;
|
||||
|
||||
let allowed = toml::de::from_str(doc.as_str()).unwrap_or_else(|_| Trusted::new());
|
||||
Ok(allowed)
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Autoenv {
|
||||
fn name(&self) -> &str {
|
||||
"autoenv"
|
||||
@ -56,19 +14,20 @@ impl WholeStreamCommand for Autoenv {
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
// "Mark a .nu-env file in a directory as trusted. Needs to be re-run after each change to the file or its filepath."
|
||||
r#"Create a file called .nu-env in any directory and run 'autoenv trust' to let nushell read it when entering the directory.
|
||||
The file can contain several optional sections:
|
||||
env: environment variables to set when visiting the directory. The variables are unset after leaving the directory and any overwritten values are restored.
|
||||
scriptvars: environment variables that should be set to the return value of a script. After they have been set, they behave in the same way as variables set in the env section.
|
||||
scripts: scripts to run when entering the directory or leaving it."#
|
||||
r#"Create a file called .nu-env in any directory and run 'autoenv trust' to let nushell load it when entering the directory.
|
||||
The .nu-env file has the same format as your $HOME/nu/config.toml file. By loading a .nu-env file the following applies:
|
||||
- environment variables (section \"[env]\") are loaded from the .nu-env file. Those env variables only exist in this directory (and children directories)
|
||||
- the \"startup\" commands are run when entering the directory
|
||||
- the \"on_exit\" commands are run when leaving the directory
|
||||
"#
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("autoenv")
|
||||
}
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(&Autoenv, &args.scope)).into_value(Tag::unknown()),
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(&Autoenv, args.scope())).into_value(Tag::unknown()),
|
||||
)))
|
||||
}
|
||||
|
||||
@ -76,15 +35,12 @@ The file can contain several optional sections:
|
||||
vec![Example {
|
||||
description: "Example .nu-env file",
|
||||
example: r#"cat .nu-env
|
||||
startup = ["echo ...entering the directory", "echo 1 2 3"]
|
||||
on_exit = ["echo ...leaving the directory"]
|
||||
|
||||
[env]
|
||||
mykey = "myvalue"
|
||||
|
||||
[scriptvars]
|
||||
myscript = "echo myval"
|
||||
|
||||
[scripts]
|
||||
entryscripts = ["touch hello.txt", "touch hello2.txt"]
|
||||
exitscripts = ["touch bye.txt"]"#,
|
||||
"#,
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::autoenv::read_trusted;
|
||||
use crate::prelude::*;
|
||||
use nu_data::config::read_trusted;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::SyntaxShape;
|
||||
@ -8,7 +8,6 @@ use sha2::{Digest, Sha256};
|
||||
use std::{fs, path::PathBuf};
|
||||
pub struct AutoenvTrust;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for AutoenvTrust {
|
||||
fn name(&self) -> &str {
|
||||
"autoenv trust"
|
||||
@ -22,11 +21,11 @@ impl WholeStreamCommand for AutoenvTrust {
|
||||
"Trust a .nu-env file in the current or given directory"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
|
||||
let file_to_trust = match args.call_info.evaluate(&ctx).await?.args.nth(0) {
|
||||
let file_to_trust = match args.call_info.evaluate(&ctx)?.args.nth(0) {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
||||
tag: _,
|
||||
@ -56,7 +55,7 @@ impl WholeStreamCommand for AutoenvTrust {
|
||||
})?;
|
||||
fs::write(config_path, tomlstr).expect("Couldn't write to toml file");
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(".nu-env trusted!").into_value(tag),
|
||||
)))
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::autoenv::Trusted;
|
||||
use crate::prelude::*;
|
||||
use nu_data::config::Trusted;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::SyntaxShape;
|
||||
@ -8,7 +8,6 @@ use std::io::Read;
|
||||
use std::{fs, path::PathBuf};
|
||||
pub struct AutoenvUnTrust;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for AutoenvUnTrust {
|
||||
fn name(&self) -> &str {
|
||||
"autoenv untrust"
|
||||
@ -26,10 +25,10 @@ impl WholeStreamCommand for AutoenvUnTrust {
|
||||
"Untrust a .nu-env file in the current or given directory"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let file_to_untrust = match args.call_info.evaluate(&ctx).await?.args.nth(0) {
|
||||
let file_to_untrust = match args.call_info.evaluate(&ctx)?.args.nth(0) {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
||||
tag: _,
|
||||
@ -80,7 +79,7 @@ impl WholeStreamCommand for AutoenvUnTrust {
|
||||
})?;
|
||||
fs::write(config_path, tomlstr).expect("Couldn't write to toml file");
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(".nu-env untrusted!").into_value(tag),
|
||||
)))
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::commands::autoview::options::{ConfigExtensions, NuConfig as AutoViewConfiguration};
|
||||
use crate::commands::autoview::options::ConfigExtensions;
|
||||
use crate::prelude::*;
|
||||
use crate::primitive::get_color_config;
|
||||
use nu_data::value::format_leaf;
|
||||
@ -7,12 +7,9 @@ use nu_errors::ShellError;
|
||||
use nu_protocol::hir::{self, Expression, ExternalRedirection, Literal, SpannedExpression};
|
||||
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
|
||||
use nu_table::TextStyle;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"autoview"
|
||||
@ -26,17 +23,8 @@ impl WholeStreamCommand for Command {
|
||||
"View the contents of the pipeline as a table or list."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
autoview(RunnableContext {
|
||||
input: args.input,
|
||||
scope: args.scope.clone(),
|
||||
shell_manager: args.shell_manager,
|
||||
host: args.host,
|
||||
ctrl_c: args.ctrl_c,
|
||||
current_errors: args.current_errors,
|
||||
name: args.call_info.name_tag,
|
||||
})
|
||||
.await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
autoview(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -55,57 +43,29 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RunnableContextWithoutInput {
|
||||
pub shell_manager: ShellManager,
|
||||
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub scope: Scope,
|
||||
pub name: Tag,
|
||||
}
|
||||
pub fn autoview(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let configuration = args.configs().lock().global_config();
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
impl RunnableContextWithoutInput {
|
||||
pub fn convert(context: RunnableContext) -> (InputStream, RunnableContextWithoutInput) {
|
||||
let new_context = RunnableContextWithoutInput {
|
||||
shell_manager: context.shell_manager,
|
||||
host: context.host,
|
||||
ctrl_c: context.ctrl_c,
|
||||
current_errors: context.current_errors,
|
||||
scope: context.scope,
|
||||
name: context.name,
|
||||
};
|
||||
(context.input, new_context)
|
||||
}
|
||||
}
|
||||
let binary = args.scope().get_command("binaryview");
|
||||
let text = args.scope().get_command("textview");
|
||||
let table = args.scope().get_command("table");
|
||||
let context = args.context;
|
||||
let mut input_stream = args.input;
|
||||
|
||||
pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
let configuration = AutoViewConfiguration::new();
|
||||
|
||||
let binary = context.get_command("binaryview");
|
||||
let text = context.get_command("textview");
|
||||
let table = context.get_command("table");
|
||||
|
||||
let pivot_mode = configuration.pivot_mode();
|
||||
|
||||
let (mut input_stream, context) = RunnableContextWithoutInput::convert(context);
|
||||
let term_width = context.host.lock().width();
|
||||
let color_hm = get_color_config();
|
||||
|
||||
if let Some(x) = input_stream.next().await {
|
||||
match input_stream.next().await {
|
||||
if let Some(x) = input_stream.next() {
|
||||
match input_stream.next() {
|
||||
Some(y) => {
|
||||
let ctrl_c = context.ctrl_c.clone();
|
||||
let xy = vec![x, y];
|
||||
let xy_stream = futures::stream::iter(xy)
|
||||
.chain(input_stream)
|
||||
.interruptible(ctrl_c);
|
||||
let xy_stream = xy.into_iter().chain(input_stream).interruptible(ctrl_c);
|
||||
|
||||
let stream = InputStream::from_stream(xy_stream);
|
||||
|
||||
if let Some(table) = table {
|
||||
let command_args = create_default_command_args(&context).with_input(stream);
|
||||
let result = table.run(command_args).await?;
|
||||
result.collect::<Vec<_>>().await;
|
||||
let command_args = create_default_command_args(&context, stream, tag);
|
||||
let result = table.run(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@ -115,14 +75,15 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
||||
tag: Tag { anchor, span },
|
||||
} if anchor.is_some() => {
|
||||
if let Some(text) = text {
|
||||
let mut stream = VecDeque::new();
|
||||
stream.push_back(
|
||||
UntaggedValue::string(s).into_value(Tag { anchor, span }),
|
||||
let command_args = create_default_command_args(
|
||||
&context,
|
||||
InputStream::one(
|
||||
UntaggedValue::string(s).into_value(Tag { anchor, span }),
|
||||
),
|
||||
tag,
|
||||
);
|
||||
let command_args =
|
||||
create_default_command_args(&context).with_input(stream);
|
||||
let result = text.run(command_args).await?;
|
||||
result.collect::<Vec<_>>().await;
|
||||
let result = text.run_with_actions(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
} else {
|
||||
out!("{}", s);
|
||||
}
|
||||
@ -199,14 +160,12 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
||||
..
|
||||
} => {
|
||||
if let Some(binary) = binary {
|
||||
let mut stream = VecDeque::new();
|
||||
stream.push_back(x);
|
||||
let command_args =
|
||||
create_default_command_args(&context).with_input(stream);
|
||||
let result = binary.run(command_args).await?;
|
||||
result.collect::<Vec<_>>().await;
|
||||
create_default_command_args(&context, InputStream::one(x), tag);
|
||||
let result = binary.run_with_actions(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
} else {
|
||||
use pretty_hex::*;
|
||||
use nu_pretty_hex::*;
|
||||
out!("{:?}", b.hex_dump());
|
||||
}
|
||||
}
|
||||
@ -219,41 +178,55 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
||||
}
|
||||
|
||||
Value {
|
||||
value: UntaggedValue::Row(row),
|
||||
value: UntaggedValue::Row(ref row),
|
||||
..
|
||||
} if pivot_mode.is_always()
|
||||
|| (pivot_mode.is_auto()
|
||||
&& (row
|
||||
.entries
|
||||
.iter()
|
||||
.map(|(_, v)| v.convert_to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.iter()
|
||||
.fold(0usize, |acc, len| acc + len.len())
|
||||
+ row.entries.iter().count() * 2)
|
||||
> term_width) =>
|
||||
{
|
||||
let mut entries = vec![];
|
||||
for (key, value) in row.entries.iter() {
|
||||
entries.push(vec![
|
||||
nu_table::StyledString::new(
|
||||
key.to_string(),
|
||||
TextStyle::new()
|
||||
.alignment(nu_table::Alignment::Left)
|
||||
.fg(nu_ansi_term::Color::Green)
|
||||
.bold(Some(true)),
|
||||
),
|
||||
nu_table::StyledString::new(
|
||||
format_leaf(value).plain_string(100_000),
|
||||
nu_table::TextStyle::basic_left(),
|
||||
),
|
||||
]);
|
||||
} => {
|
||||
let pivot_mode = configuration.pivot_mode();
|
||||
|
||||
let term_width = context.host.lock().width();
|
||||
if pivot_mode.is_always()
|
||||
|| (pivot_mode.is_auto()
|
||||
&& (row
|
||||
.entries
|
||||
.iter()
|
||||
.map(|(_, v)| v.convert_to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.iter()
|
||||
.fold(0usize, |acc, len| acc + len.len())
|
||||
+ row.entries.iter().count() * 2)
|
||||
> term_width)
|
||||
{
|
||||
let mut entries = vec![];
|
||||
for (key, value) in row.entries.iter() {
|
||||
entries.push(vec![
|
||||
nu_table::StyledString::new(
|
||||
key.to_string(),
|
||||
TextStyle::new()
|
||||
.alignment(nu_table::Alignment::Left)
|
||||
.fg(nu_ansi_term::Color::Green)
|
||||
.bold(Some(true)),
|
||||
),
|
||||
nu_table::StyledString::new(
|
||||
format_leaf(value).plain_string(100_000),
|
||||
nu_table::TextStyle::basic_left(),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
let color_hm = get_color_config(&configuration);
|
||||
|
||||
let table =
|
||||
nu_table::Table::new(vec![], entries, nu_table::Theme::compact());
|
||||
|
||||
println!("{}", nu_table::draw_table(&table, term_width, &color_hm));
|
||||
} else if let Some(table) = table {
|
||||
let command_args =
|
||||
create_default_command_args(&context, InputStream::one(x), tag);
|
||||
let result = table.run(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
} else {
|
||||
out!("{:?}", row);
|
||||
}
|
||||
|
||||
let table =
|
||||
nu_table::Table::new(vec![], entries, nu_table::Theme::compact());
|
||||
|
||||
println!("{}", nu_table::draw_table(&table, term_width, &color_hm));
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Nothing),
|
||||
@ -265,12 +238,10 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
||||
value: ref item, ..
|
||||
} => {
|
||||
if let Some(table) = table {
|
||||
let mut stream = VecDeque::new();
|
||||
stream.push_back(x);
|
||||
let command_args =
|
||||
create_default_command_args(&context).with_input(stream);
|
||||
let result = table.run(command_args).await?;
|
||||
result.collect::<Vec<_>>().await;
|
||||
create_default_command_args(&context, InputStream::one(x), tag);
|
||||
let result = table.run(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
} else {
|
||||
out!("{:?}", item);
|
||||
}
|
||||
@ -280,16 +251,17 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
||||
}
|
||||
}
|
||||
|
||||
Ok(OutputStream::empty())
|
||||
Ok(InputStream::empty())
|
||||
}
|
||||
|
||||
fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawCommandArgs {
|
||||
let span = context.name.span;
|
||||
RawCommandArgs {
|
||||
host: context.host.clone(),
|
||||
ctrl_c: context.ctrl_c.clone(),
|
||||
current_errors: context.current_errors.clone(),
|
||||
shell_manager: context.shell_manager.clone(),
|
||||
fn create_default_command_args(
|
||||
context: &EvaluationContext,
|
||||
input: InputStream,
|
||||
tag: Tag,
|
||||
) -> CommandArgs {
|
||||
let span = tag.span;
|
||||
CommandArgs {
|
||||
context: context.clone(),
|
||||
call_info: UnevaluatedCallInfo {
|
||||
args: hir::Call {
|
||||
head: Box::new(SpannedExpression::new(
|
||||
@ -301,9 +273,9 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
|
||||
span,
|
||||
external_redirection: ExternalRedirection::Stdout,
|
||||
},
|
||||
name_tag: context.name.clone(),
|
||||
name_tag: tag,
|
||||
},
|
||||
scope: Scope::new(),
|
||||
input,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,10 @@ use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::{Block, CapturedBlock, ClassifiedCommand, Group, InternalCommand, Pipeline},
|
||||
hir::{
|
||||
Block, CapturedBlock, ClassifiedCommand, ExternalRedirection, Group, InternalCommand,
|
||||
Pipeline,
|
||||
},
|
||||
Dictionary, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use rand::{
|
||||
@ -23,7 +26,6 @@ struct BenchmarkArgs {
|
||||
passthrough: Option<CapturedBlock>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Benchmark {
|
||||
fn name(&self) -> &str {
|
||||
"benchmark"
|
||||
@ -48,8 +50,8 @@ impl WholeStreamCommand for Benchmark {
|
||||
"Runs a block and returns the time it took to execute it."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
benchmark(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
benchmark(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -68,11 +70,16 @@ impl WholeStreamCommand for Benchmark {
|
||||
}
|
||||
}
|
||||
|
||||
async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = raw_args.call_info.args.span;
|
||||
let mut context = EvaluationContext::from_args(&raw_args);
|
||||
let scope = raw_args.scope.clone();
|
||||
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process().await?;
|
||||
fn benchmark(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.args.span;
|
||||
let mut context = EvaluationContext::from_args(&args);
|
||||
let scope = args.scope().clone();
|
||||
|
||||
let args = args.evaluate_once()?;
|
||||
let cmd_args = BenchmarkArgs {
|
||||
block: args.req(0)?,
|
||||
passthrough: args.get_flag("passthrough")?,
|
||||
};
|
||||
|
||||
let env = scope.get_env_vars();
|
||||
let name = generate_free_name(&env);
|
||||
@ -82,15 +89,21 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let start_time = Instant::now();
|
||||
|
||||
// #[cfg(feature = "rich-benchmark")]
|
||||
// let start = time().await;
|
||||
// let start = time();
|
||||
|
||||
context.scope.enter_scope();
|
||||
let result = run_block(&block.block, &context, input).await;
|
||||
let result = run_block(
|
||||
&cmd_args.block.block,
|
||||
&context,
|
||||
args.input,
|
||||
ExternalRedirection::StdoutAndStderr,
|
||||
);
|
||||
|
||||
context.scope.exit_scope();
|
||||
let output = result?.into_vec().await;
|
||||
let output = result?.into_vec();
|
||||
|
||||
// #[cfg(feature = "rich-benchmark")]
|
||||
// let end = time().await;
|
||||
// let end = time();
|
||||
|
||||
let end_time = Instant::now();
|
||||
context.clear_errors();
|
||||
@ -102,7 +115,7 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let real_time = into_big_int(end_time - start_time);
|
||||
indexmap.insert("real time".to_string(), real_time);
|
||||
benchmark_output(indexmap, output, passthrough, &tag, &mut context).await
|
||||
benchmark_output(indexmap, output, cmd_args.passthrough, &tag, &mut context)
|
||||
}
|
||||
// return advanced stats
|
||||
// #[cfg(feature = "rich-benchmark")]
|
||||
@ -121,7 +134,7 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
// let idle_time = into_big_int(end.idle() - start.idle());
|
||||
// indexmap.insert("idle time".to_string(), idle_time);
|
||||
|
||||
// benchmark_output(indexmap, output, passthrough, &tag, &mut context).await
|
||||
// benchmark_output(indexmap, output, passthrough, &tag, &mut context)
|
||||
// } else {
|
||||
// Err(ShellError::untagged_runtime_error(
|
||||
// "Could not retrieve CPU time",
|
||||
@ -129,7 +142,7 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
// }
|
||||
}
|
||||
|
||||
async fn benchmark_output<T, Output>(
|
||||
fn benchmark_output<T, Output>(
|
||||
indexmap: IndexMap<String, BigInt>,
|
||||
block_output: Output,
|
||||
passthrough: Option<CapturedBlock>,
|
||||
@ -155,7 +168,12 @@ where
|
||||
let time_block = add_implicit_autoview(time_block.block);
|
||||
|
||||
context.scope.enter_scope();
|
||||
let result = run_block(&time_block, context, benchmark_output).await;
|
||||
let result = run_block(
|
||||
&time_block,
|
||||
context,
|
||||
benchmark_output,
|
||||
ExternalRedirection::StdoutAndStderr,
|
||||
);
|
||||
context.scope.exit_scope();
|
||||
result?;
|
||||
context.clear_errors();
|
||||
@ -167,21 +185,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn add_implicit_autoview(mut block: Block) -> Block {
|
||||
if block.block.is_empty() {
|
||||
let group = Group::new(
|
||||
vec![{
|
||||
let mut commands = Pipeline::new(block.span);
|
||||
commands.push(ClassifiedCommand::Internal(InternalCommand::new(
|
||||
"autoview".to_string(),
|
||||
block.span,
|
||||
block.span,
|
||||
)));
|
||||
commands
|
||||
}],
|
||||
block.span,
|
||||
);
|
||||
block.push(group);
|
||||
fn add_implicit_autoview(mut block: Arc<Block>) -> Arc<Block> {
|
||||
if let Some(block) = std::sync::Arc::<nu_protocol::hir::Block>::get_mut(&mut block) {
|
||||
if block.block.is_empty() {
|
||||
let group = Group::new(
|
||||
vec![{
|
||||
let mut commands = Pipeline::new(block.span);
|
||||
commands.push(ClassifiedCommand::Internal(InternalCommand::new(
|
||||
"autoview".to_string(),
|
||||
block.span,
|
||||
block.span,
|
||||
)));
|
||||
commands
|
||||
}],
|
||||
block.span,
|
||||
);
|
||||
block.push(group);
|
||||
}
|
||||
}
|
||||
block
|
||||
}
|
||||
|
@ -3,16 +3,10 @@ use nu_errors::ShellError;
|
||||
|
||||
use nu_data::value::format_leaf;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BuildStringArgs {
|
||||
rest: Vec<Value>,
|
||||
}
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct BuildString;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for BuildString {
|
||||
fn name(&self) -> &str {
|
||||
"build-string"
|
||||
@ -27,9 +21,10 @@ impl WholeStreamCommand for BuildString {
|
||||
"Builds a string from the arguments."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (BuildStringArgs { rest }, _) = args.process().await?;
|
||||
let args = args.evaluate_once()?;
|
||||
let rest: Vec<Value> = args.rest(0)?;
|
||||
|
||||
let mut output_string = String::new();
|
||||
|
||||
@ -37,9 +32,9 @@ impl WholeStreamCommand for BuildString {
|
||||
output_string.push_str(&format_leaf(&r).plain_string(100_000))
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(output_string).into_value(tag),
|
||||
)))
|
||||
))
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -1,13 +1,12 @@
|
||||
use crate::prelude::*;
|
||||
use chrono::{Datelike, Local, NaiveDate};
|
||||
use indexmap::IndexMap;
|
||||
use nu_engine::{EvaluatedWholeStreamCommandArgs, WholeStreamCommand};
|
||||
use nu_engine::{EvaluatedCommandArgs, WholeStreamCommand};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct Cal;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Cal {
|
||||
fn name(&self) -> &str {
|
||||
"cal"
|
||||
@ -41,8 +40,8 @@ impl WholeStreamCommand for Cal {
|
||||
"Display a calendar."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
cal(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
cal(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -66,8 +65,8 @@ impl WholeStreamCommand for Cal {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
pub fn cal(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let mut calendar_vec_deque = VecDeque::new();
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
@ -76,7 +75,7 @@ pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let mut selected_year: i32 = current_year;
|
||||
let mut current_day_option: Option<u32> = Some(current_day);
|
||||
|
||||
let month_range = if let Some(full_year_value) = args.get("full-year") {
|
||||
let month_range = if let Some(full_year_value) = args.call_info.args.get("full-year") {
|
||||
if let Ok(year_u64) = full_year_value.as_u64() {
|
||||
selected_year = year_u64 as i32;
|
||||
|
||||
@ -102,7 +101,7 @@ pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
current_day_option,
|
||||
)?;
|
||||
|
||||
Ok(futures::stream::iter(calendar_vec_deque).to_output_stream())
|
||||
Ok(calendar_vec_deque.into_iter().to_action_stream())
|
||||
}
|
||||
|
||||
fn get_invalid_year_shell_error(year_tag: &Tag) -> ShellError {
|
||||
@ -166,7 +165,7 @@ fn get_current_date() -> (i32, u32, u32) {
|
||||
}
|
||||
|
||||
fn add_months_of_year_to_table(
|
||||
args: &EvaluatedWholeStreamCommandArgs,
|
||||
args: &EvaluatedCommandArgs,
|
||||
mut calendar_vec_deque: &mut VecDeque<Value>,
|
||||
tag: &Tag,
|
||||
selected_year: i32,
|
||||
@ -199,7 +198,7 @@ fn add_months_of_year_to_table(
|
||||
}
|
||||
|
||||
fn add_month_to_table(
|
||||
args: &EvaluatedWholeStreamCommandArgs,
|
||||
args: &EvaluatedCommandArgs,
|
||||
calendar_vec_deque: &mut VecDeque<Value>,
|
||||
tag: &Tag,
|
||||
selected_year: i32,
|
||||
@ -210,7 +209,7 @@ fn add_month_to_table(
|
||||
|
||||
let month_helper = match month_helper_result {
|
||||
Ok(month_helper) => month_helper,
|
||||
Err(()) => match args.get("full-year") {
|
||||
Err(()) => match args.call_info.args.get("full-year") {
|
||||
Some(full_year_value) => {
|
||||
return Err(get_invalid_year_shell_error(&full_year_value.tag()))
|
||||
}
|
||||
@ -236,7 +235,7 @@ fn add_month_to_table(
|
||||
|
||||
let mut week_start_day = days_of_the_week[0].to_string();
|
||||
|
||||
if let Some(week_start_value) = args.get("week-start") {
|
||||
if let Some(week_start_value) = args.call_info.args.get("week-start") {
|
||||
if let Ok(day) = week_start_value.as_string() {
|
||||
if days_of_the_week.contains(&day.as_str()) {
|
||||
week_start_day = day;
|
||||
@ -265,10 +264,10 @@ fn add_month_to_table(
|
||||
let mut day_number: u32 = 1;
|
||||
let day_limit: u32 = total_start_offset + month_helper.number_of_days_in_month;
|
||||
|
||||
let should_show_year_column = args.has("year");
|
||||
let should_show_quarter_column = args.has("quarter");
|
||||
let should_show_month_column = args.has("month");
|
||||
let should_show_month_names = args.has("month-names");
|
||||
let should_show_year_column = args.has_flag("year");
|
||||
let should_show_quarter_column = args.has_flag("quarter");
|
||||
let should_show_month_column = args.has_flag("month");
|
||||
let should_show_month_names = args.has_flag("month-names");
|
||||
|
||||
while day_number <= day_limit {
|
||||
let mut indexmap = IndexMap::new();
|
||||
|
@ -7,7 +7,6 @@ use nu_protocol::{Signature, SyntaxShape};
|
||||
|
||||
pub struct Cd;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Cd {
|
||||
fn name(&self) -> &str {
|
||||
"cd"
|
||||
@ -25,10 +24,10 @@ impl WholeStreamCommand for Cd {
|
||||
"Change to a new path."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
let (args, _): (CdArgs, _) = args.process().await?;
|
||||
let shell_manager = args.shell_manager();
|
||||
let (args, _): (CdArgs, _) = args.process()?;
|
||||
shell_manager.cd(args, name)
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,11 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_engine::{FromValue, WholeStreamCommand};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Char;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CharArgs {
|
||||
name: Tagged<String>,
|
||||
rest: Vec<Tagged<String>>,
|
||||
unicode: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Char {
|
||||
fn name(&self) -> &str {
|
||||
"char"
|
||||
@ -26,8 +18,8 @@ impl WholeStreamCommand for Char {
|
||||
SyntaxShape::Any,
|
||||
"the name of the character to output",
|
||||
)
|
||||
.rest(SyntaxShape::String, "multiple unicode bytes")
|
||||
.switch("unicode", "unicode string i.e. 1f378", Some('u'))
|
||||
.rest(SyntaxShape::String, "multiple Unicode bytes")
|
||||
.switch("unicode", "Unicode string i.e. 1f378", Some('u'))
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -51,12 +43,12 @@ impl WholeStreamCommand for Char {
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Output unicode character",
|
||||
description: "Output Unicode character",
|
||||
example: r#"char -u 1f378"#,
|
||||
result: Some(vec![Value::from("\u{1f378}")]),
|
||||
},
|
||||
Example {
|
||||
description: "Output multi-byte unicode character",
|
||||
description: "Output multi-byte Unicode character",
|
||||
example: r#"char -u 1F468 200D 1F466 200D 1F466"#,
|
||||
result: Some(vec![Value::from(
|
||||
"\u{1F468}\u{200D}\u{1F466}\u{200D}\u{1F466}",
|
||||
@ -65,19 +57,16 @@ impl WholeStreamCommand for Char {
|
||||
]
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (
|
||||
CharArgs {
|
||||
name,
|
||||
rest,
|
||||
unicode,
|
||||
},
|
||||
_,
|
||||
) = args.process().await?;
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let name: Tagged<String> = args.req(0)?;
|
||||
let rest: Vec<Value> = args.rest(1)?;
|
||||
let unicode = args.has_flag("unicode");
|
||||
|
||||
if unicode {
|
||||
if !rest.is_empty() {
|
||||
// Setup a new buffer to put all the unicode bytes in
|
||||
// Setup a new buffer to put all the Unicode bytes in
|
||||
let mut multi_byte = String::new();
|
||||
// Get the first byte
|
||||
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
|
||||
@ -87,25 +76,26 @@ impl WholeStreamCommand for Char {
|
||||
}
|
||||
// Get the rest of the bytes
|
||||
for byte_part in rest {
|
||||
let byte_part: Tagged<String> = FromValue::from_value(&byte_part)?;
|
||||
let decoded_char = string_to_unicode_char(&byte_part, &byte_part.tag);
|
||||
match decoded_char {
|
||||
Ok(ch) => multi_byte.push(ch),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(multi_byte).into_value(name.tag),
|
||||
)))
|
||||
} else {
|
||||
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
|
||||
if let Ok(ch) = decoded_char {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(ch).into_value(name.tag()),
|
||||
)))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"error decoding unicode character",
|
||||
"error decoding unicode character",
|
||||
"error decoding Unicode character",
|
||||
"error decoding Unicode character",
|
||||
name.tag(),
|
||||
))
|
||||
}
|
||||
@ -113,7 +103,7 @@ impl WholeStreamCommand for Char {
|
||||
} else {
|
||||
let special_character = str_to_character(&name.item);
|
||||
if let Some(output) = special_character {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(name.tag()),
|
||||
)))
|
||||
} else {
|
||||
@ -136,8 +126,8 @@ fn string_to_unicode_char(s: &str, t: &Tag) -> Result<char, ShellError> {
|
||||
Ok(ch)
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"error decoding unicode character",
|
||||
"error decoding unicode character",
|
||||
"error decoding Unicode character",
|
||||
"error decoding Unicode character",
|
||||
t,
|
||||
))
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
#[derive(Clone)]
|
||||
pub struct Chart;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Chart {
|
||||
fn name(&self) -> &str {
|
||||
"chart"
|
||||
@ -20,15 +19,15 @@ impl WholeStreamCommand for Chart {
|
||||
"Displays charts."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
if args.scope.get_command("chart bar").is_none() {
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
if args.scope().get_command("chart bar").is_none() {
|
||||
return Err(ShellError::untagged_runtime_error(
|
||||
"nu_plugin_chart not installed.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(get_full_help(&Chart, &args.scope)).into_value(Tag::unknown()),
|
||||
Ok(ActionStream::one(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(get_full_help(&Chart, args.scope())).into_value(Tag::unknown()),
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,14 @@
|
||||
use crate::futures::ThreadedReceiver;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::evaluate_baseline_expr;
|
||||
use nu_engine::{evaluate_baseline_expr, BufCodecReader};
|
||||
use nu_engine::{MaybeTextCodec, StringOrBinary};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
use std::ops::Deref;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::mpsc;
|
||||
use std::{borrow::Cow, io::BufReader};
|
||||
|
||||
use futures::executor::block_on_stream;
|
||||
use futures_codec::FramedRead;
|
||||
use log::trace;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
@ -18,9 +16,8 @@ use nu_protocol::hir::Expression;
|
||||
use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
|
||||
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
use nu_stream::trace_stream;
|
||||
|
||||
pub(crate) async fn run_external_command(
|
||||
pub(crate) fn run_external_command(
|
||||
command: ExternalCommand,
|
||||
context: &mut EvaluationContext,
|
||||
input: InputStream,
|
||||
@ -28,18 +25,19 @@ pub(crate) async fn run_external_command(
|
||||
) -> Result<InputStream, ShellError> {
|
||||
trace!(target: "nu::run::external", "-> {}", command.name);
|
||||
|
||||
context.sync_path_to_env();
|
||||
if !context.host.lock().is_external_cmd(&command.name) {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Command not found",
|
||||
"command not found",
|
||||
format!("command {} not found", &command.name),
|
||||
&command.name_tag,
|
||||
));
|
||||
}
|
||||
|
||||
run_with_stdin(command, context, input, external_redirection).await
|
||||
run_with_stdin(command, context, input, external_redirection)
|
||||
}
|
||||
|
||||
async fn run_with_stdin(
|
||||
fn run_with_stdin(
|
||||
command: ExternalCommand,
|
||||
context: &mut EvaluationContext,
|
||||
input: InputStream,
|
||||
@ -47,12 +45,10 @@ async fn run_with_stdin(
|
||||
) -> Result<InputStream, ShellError> {
|
||||
let path = context.shell_manager.path();
|
||||
|
||||
let input = trace_stream!(target: "nu::trace_stream::external::stdin", "input" = input);
|
||||
|
||||
let mut command_args = vec![];
|
||||
for arg in command.args.iter() {
|
||||
let is_literal = matches!(arg.expr, Expression::Literal(_));
|
||||
let value = evaluate_baseline_expr(arg, context).await?;
|
||||
let value = evaluate_baseline_expr(arg, context)?;
|
||||
|
||||
// Skip any arguments that don't really exist, treating them as optional
|
||||
// FIXME: we may want to preserve the gap in the future, though it's hard to say
|
||||
@ -203,243 +199,274 @@ fn spawn(
|
||||
trace!(target: "nu::run::external", "built command {:?}", process);
|
||||
|
||||
// TODO Switch to async_std::process once it's stabilized
|
||||
if let Ok(mut child) = process.spawn() {
|
||||
let (tx, rx) = mpsc::sync_channel(0);
|
||||
match process.spawn() {
|
||||
Ok(mut child) => {
|
||||
let (tx, rx) = mpsc::sync_channel(0);
|
||||
|
||||
let mut stdin = child.stdin.take();
|
||||
let mut stdin = child.stdin.take();
|
||||
|
||||
let stdin_write_tx = tx.clone();
|
||||
let stdout_read_tx = tx;
|
||||
let stdin_name_tag = command.name_tag.clone();
|
||||
let stdout_name_tag = command.name_tag;
|
||||
let stdin_write_tx = tx.clone();
|
||||
let stdout_read_tx = tx;
|
||||
let stdin_name_tag = command.name_tag.clone();
|
||||
let stdout_name_tag = command.name_tag;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
if !input.is_empty() {
|
||||
let mut stdin_write = stdin
|
||||
.take()
|
||||
.expect("Internal error: could not get stdin pipe for external command");
|
||||
std::thread::spawn(move || {
|
||||
if !input.is_empty() {
|
||||
let mut stdin_write = stdin
|
||||
.take()
|
||||
.expect("Internal error: could not get stdin pipe for external command");
|
||||
|
||||
for value in block_on_stream(input) {
|
||||
match &value.value {
|
||||
UntaggedValue::Primitive(Primitive::Nothing) => continue,
|
||||
UntaggedValue::Primitive(Primitive::String(s)) => {
|
||||
if stdin_write.write(s.as_bytes()).is_err() {
|
||||
// Other side has closed, so exit
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
UntaggedValue::Primitive(Primitive::Binary(b)) => {
|
||||
if stdin_write.write(b).is_err() {
|
||||
// Other side has closed, so exit
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
unsupported => {
|
||||
println!("Unsupported: {:?}", unsupported);
|
||||
let _ = stdin_write_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!(
|
||||
"Received unexpected type from pipeline ({})",
|
||||
unsupported.type_name()
|
||||
),
|
||||
"expected a string",
|
||||
stdin_name_tag.clone(),
|
||||
)),
|
||||
tag: stdin_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
std::thread::spawn(move || {
|
||||
if external_redirection == ExternalRedirection::Stdout
|
||||
|| external_redirection == ExternalRedirection::StdoutAndStderr
|
||||
{
|
||||
let stdout = if let Some(stdout) = child.stdout.take() {
|
||||
stdout
|
||||
} else {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"Can't redirect the stdout for external command",
|
||||
"can't redirect stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
};
|
||||
|
||||
let file = futures::io::AllowStdIo::new(stdout);
|
||||
let stream = FramedRead::new(file, MaybeTextCodec::default());
|
||||
|
||||
for line in block_on_stream(stream) {
|
||||
match line {
|
||||
Ok(line) => match line {
|
||||
StringOrBinary::String(s) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(s.clone())),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
for value in input {
|
||||
match &value.value {
|
||||
UntaggedValue::Primitive(Primitive::Nothing) => continue,
|
||||
UntaggedValue::Primitive(Primitive::String(s)) => {
|
||||
if stdin_write.write(s.as_bytes()).is_err() {
|
||||
// Other side has closed, so exit
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
StringOrBinary::Binary(b) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Binary(
|
||||
b.into_iter().collect(),
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
UntaggedValue::Primitive(Primitive::Binary(b)) => {
|
||||
if stdin_write.write(b).is_err() {
|
||||
// Other side has closed, so exit
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
// If there's an exit status, it makes sense that we may error when
|
||||
// trying to read from its stdout pipe (likely been closed). In that
|
||||
// case, don't emit an error.
|
||||
let should_error = match child.wait() {
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
Err(_) => true,
|
||||
};
|
||||
|
||||
if should_error {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
unsupported => {
|
||||
println!("Unsupported: {:?}", unsupported);
|
||||
let _ = stdin_write_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!("Unable to read from stdout ({})", e),
|
||||
"unable to read from stdout",
|
||||
&stdout_name_tag,
|
||||
format!(
|
||||
"Received unexpected type from pipeline ({})",
|
||||
unsupported.type_name()
|
||||
),
|
||||
"expected a string",
|
||||
stdin_name_tag.clone(),
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
tag: stdin_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if external_redirection == ExternalRedirection::Stderr
|
||||
|| external_redirection == ExternalRedirection::StdoutAndStderr
|
||||
{
|
||||
let stderr = if let Some(stderr) = child.stderr.take() {
|
||||
stderr
|
||||
} else {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"Can't redirect the stderr for external command",
|
||||
"can't redirect stderr",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
};
|
||||
|
||||
let file = futures::io::AllowStdIo::new(stderr);
|
||||
let stream = FramedRead::new(file, MaybeTextCodec::default());
|
||||
Ok(())
|
||||
});
|
||||
|
||||
for line in block_on_stream(stream) {
|
||||
match line {
|
||||
Ok(line) => match line {
|
||||
StringOrBinary::String(s) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error(s),
|
||||
),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
StringOrBinary::Binary(_) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error("<binary stderr>"),
|
||||
),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
// If there's an exit status, it makes sense that we may error when
|
||||
// trying to read from its stdout pipe (likely been closed). In that
|
||||
// case, don't emit an error.
|
||||
let should_error = match child.wait() {
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
Err(_) => true,
|
||||
};
|
||||
|
||||
if should_error {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!("Unable to read from stdout ({})", e),
|
||||
"unable to read from stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can give an error when we see a non-zero exit code, but this is different
|
||||
// than what other shells will do.
|
||||
let external_failed = match child.wait() {
|
||||
Err(_) => true,
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
};
|
||||
|
||||
if external_failed {
|
||||
let cfg = nu_data::config::config(Tag::unknown());
|
||||
if let Ok(cfg) = cfg {
|
||||
if cfg.contains_key("nonzero_exit_errors") {
|
||||
std::thread::spawn(move || {
|
||||
if external_redirection == ExternalRedirection::Stdout
|
||||
|| external_redirection == ExternalRedirection::StdoutAndStderr
|
||||
{
|
||||
let stdout = if let Some(stdout) = child.stdout.take() {
|
||||
stdout
|
||||
} else {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"External command failed",
|
||||
"command failed",
|
||||
"Can't redirect the stdout for external command",
|
||||
"can't redirect stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
};
|
||||
|
||||
// let file = futures::io::AllowStdIo::new(stdout);
|
||||
// let stream = FramedRead::new(file, MaybeTextCodec::default());
|
||||
let buf_read = BufReader::new(stdout);
|
||||
let buf_codec = BufCodecReader::new(buf_read, MaybeTextCodec::default());
|
||||
|
||||
for line in buf_codec {
|
||||
match line {
|
||||
Ok(line) => match line {
|
||||
StringOrBinary::String(s) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(
|
||||
s.clone(),
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
StringOrBinary::Binary(b) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Binary(
|
||||
b.into_iter().collect(),
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
// If there's an exit status, it makes sense that we may error when
|
||||
// trying to read from its stdout pipe (likely been closed). In that
|
||||
// case, don't emit an error.
|
||||
let should_error = match child.wait() {
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
Err(_) => true,
|
||||
};
|
||||
|
||||
if should_error {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!("Unable to read from stdout ({})", e),
|
||||
"unable to read from stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::external_non_zero()),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
}
|
||||
if external_redirection == ExternalRedirection::Stderr
|
||||
|| external_redirection == ExternalRedirection::StdoutAndStderr
|
||||
{
|
||||
let stderr = if let Some(stderr) = child.stderr.take() {
|
||||
stderr
|
||||
} else {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"Can't redirect the stderr for external command",
|
||||
"can't redirect stderr",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
};
|
||||
|
||||
Ok(())
|
||||
});
|
||||
// let file = futures::io::AllowStdIo::new(stderr);
|
||||
// let stream = FramedRead::new(file, MaybeTextCodec::default());
|
||||
let buf_reader = BufReader::new(stderr);
|
||||
let buf_codec = BufCodecReader::new(buf_reader, MaybeTextCodec::default());
|
||||
|
||||
let stream = ThreadedReceiver::new(rx);
|
||||
Ok(stream.to_input_stream())
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Failed to spawn process",
|
||||
for line in buf_codec {
|
||||
match line {
|
||||
Ok(line) => match line {
|
||||
StringOrBinary::String(s) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error(s),
|
||||
),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
StringOrBinary::Binary(_) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error("<binary stderr>"),
|
||||
),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
// If there's an exit status, it makes sense that we may error when
|
||||
// trying to read from its stdout pipe (likely been closed). In that
|
||||
// case, don't emit an error.
|
||||
let should_error = match child.wait() {
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
Err(_) => true,
|
||||
};
|
||||
|
||||
if should_error {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!("Unable to read from stdout ({})", e),
|
||||
"unable to read from stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can give an error when we see a non-zero exit code, but this is different
|
||||
// than what other shells will do.
|
||||
let external_failed = match child.wait() {
|
||||
Err(_) => true,
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
};
|
||||
|
||||
if external_failed {
|
||||
let cfg = nu_data::config::config(Tag::unknown());
|
||||
if let Ok(cfg) = cfg {
|
||||
if cfg.contains_key("nonzero_exit_errors") {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"External command failed",
|
||||
"command failed",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::external_non_zero()),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let stream = ChannelReceiver::new(rx);
|
||||
Ok(stream.to_input_stream())
|
||||
}
|
||||
Err(e) => Err(ShellError::labeled_error(
|
||||
format!("{}", e),
|
||||
"failed to spawn",
|
||||
&command.name_tag,
|
||||
))
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
struct ChannelReceiver {
|
||||
rx: Arc<Mutex<mpsc::Receiver<Result<Value, ShellError>>>>,
|
||||
}
|
||||
|
||||
impl ChannelReceiver {
|
||||
pub fn new(rx: mpsc::Receiver<Result<Value, ShellError>>) -> Self {
|
||||
Self {
|
||||
rx: Arc::new(Mutex::new(rx)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ChannelReceiver {
|
||||
type Item = Result<Value, ShellError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let rx = self.rx.lock();
|
||||
match rx.recv() {
|
||||
Ok(v) => Some(v),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -506,16 +533,13 @@ mod tests {
|
||||
#[cfg(feature = "which")]
|
||||
use super::{run_external_command, InputStream};
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use futures::executor::block_on;
|
||||
#[cfg(feature = "which")]
|
||||
use nu_engine::EvaluationContext;
|
||||
#[cfg(feature = "which")]
|
||||
use nu_errors::ShellError;
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use nu_test_support::commands::ExternalBuilder;
|
||||
// async fn read(mut stream: OutputStream) -> Option<Value> {
|
||||
// match stream.try_next().await {
|
||||
// fn read(mut stream: OutputStream) -> Option<Value> {
|
||||
// match stream.try_next() {
|
||||
// Ok(val) => {
|
||||
// if let Some(val) = val {
|
||||
// val.raw_value()
|
||||
@ -528,32 +552,25 @@ mod tests {
|
||||
// }
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
async fn non_existent_run() -> Result<(), ShellError> {
|
||||
fn non_existent_run() {
|
||||
use nu_protocol::hir::ExternalRedirection;
|
||||
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
|
||||
|
||||
let input = InputStream::empty();
|
||||
let mut ctx =
|
||||
EvaluationContext::basic().expect("There was a problem creating a basic context.");
|
||||
let mut ctx = EvaluationContext::basic();
|
||||
|
||||
assert!(
|
||||
run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout)
|
||||
.await
|
||||
.is_err()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
assert!(run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout).is_err());
|
||||
}
|
||||
|
||||
// async fn failure_run() -> Result<(), ShellError> {
|
||||
// fn failure_run() -> Result<(), ShellError> {
|
||||
// let cmd = ExternalBuilder::for_name("fail").build();
|
||||
|
||||
// let mut ctx = crate::cli::EvaluationContext::basic().expect("There was a problem creating a basic context.");
|
||||
// let stream = run_external_command(cmd, &mut ctx, None, false)
|
||||
// .await?
|
||||
// ?
|
||||
// .expect("There was a problem running the external command.");
|
||||
|
||||
// match read(stream.into()).await {
|
||||
// match read(stream.into()) {
|
||||
// Some(Value {
|
||||
// value: UntaggedValue::Error(_),
|
||||
// ..
|
||||
@ -571,8 +588,8 @@ mod tests {
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
#[test]
|
||||
fn identifies_command_not_found() -> Result<(), ShellError> {
|
||||
block_on(non_existent_run())
|
||||
fn identifies_command_not_found() {
|
||||
non_existent_run()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -6,7 +6,6 @@ use std::process::Command;
|
||||
|
||||
pub struct Clear;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Clear {
|
||||
fn name(&self) -> &str {
|
||||
"clear"
|
||||
@ -20,7 +19,7 @@ impl WholeStreamCommand for Clear {
|
||||
"Clears the terminal."
|
||||
}
|
||||
|
||||
async fn run(&self, _: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run(&self, _: CommandArgs) -> Result<InputStream, ShellError> {
|
||||
if cfg!(windows) {
|
||||
Command::new("cmd")
|
||||
.args(&["/C", "cls"])
|
||||
@ -32,7 +31,7 @@ impl WholeStreamCommand for Clear {
|
||||
.status()
|
||||
.expect("failed to execute process");
|
||||
}
|
||||
Ok(OutputStream::empty())
|
||||
Ok(InputStream::empty())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::prelude::*;
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, Value};
|
||||
@ -8,7 +8,6 @@ use arboard::Clipboard;
|
||||
|
||||
pub struct Clip;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Clip {
|
||||
fn name(&self) -> &str {
|
||||
"clip"
|
||||
@ -22,8 +21,8 @@ impl WholeStreamCommand for Clip {
|
||||
"Copy the contents of the pipeline to the copy/paste buffer."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
clip(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
clip(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -42,10 +41,10 @@ impl WholeStreamCommand for Clip {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn clip(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn clip(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let input = args.input;
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let values: Vec<Value> = input.collect().await;
|
||||
let name = args.call_info.name_tag;
|
||||
let values: Vec<Value> = input.collect();
|
||||
|
||||
if let Ok(mut clip_context) = Clipboard::new() {
|
||||
let mut new_copy_data = String::new();
|
||||
@ -89,7 +88,7 @@ pub async fn clip(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
name,
|
||||
));
|
||||
}
|
||||
Ok(OutputStream::empty())
|
||||
Ok(ActionStream::empty())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,19 +1,16 @@
|
||||
use crate::prelude::*;
|
||||
use futures::future;
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Compact;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CompactArgs {
|
||||
rest: Vec<Tagged<String>>,
|
||||
columns: Vec<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Compact {
|
||||
fn name(&self) -> &str {
|
||||
"compact"
|
||||
@ -27,8 +24,8 @@ impl WholeStreamCommand for Compact {
|
||||
"Creates a table with non-empty rows."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
compact(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
compact(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -40,34 +37,28 @@ impl WholeStreamCommand for Compact {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn compact(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (CompactArgs { rest: columns }, input) = args.process().await?;
|
||||
pub fn compact(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (args, input) = args.extract(|params| {
|
||||
Ok(CompactArgs {
|
||||
columns: params.rest(0)?,
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(input
|
||||
.filter_map(move |item| {
|
||||
future::ready(if columns.is_empty() {
|
||||
if !item.is_empty() {
|
||||
Some(ReturnSuccess::value(item))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.filter(move |item| {
|
||||
if args.columns.is_empty() {
|
||||
!item.is_empty()
|
||||
} else if let Value {
|
||||
value: UntaggedValue::Row(ref r),
|
||||
..
|
||||
} = item
|
||||
{
|
||||
args.columns
|
||||
.iter()
|
||||
.all(|field| r.get_data(field).borrow().is_some())
|
||||
} else {
|
||||
match item {
|
||||
Value {
|
||||
value: UntaggedValue::Row(ref r),
|
||||
..
|
||||
} => {
|
||||
if columns
|
||||
.iter()
|
||||
.all(|field| r.get_data(field).borrow().is_some())
|
||||
{
|
||||
Some(ReturnSuccess::value(item))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
false
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config clear"
|
||||
@ -19,8 +18,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"clear the config"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
clear(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
clear(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -32,24 +31,23 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn clear(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_span = args.call_info.name_tag.clone();
|
||||
pub fn clear(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
|
||||
let path = match args.scope.get_var("config-path") {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
|
||||
..
|
||||
}) => Some(path),
|
||||
_ => nu_data::config::default_path().ok(),
|
||||
let result = if let Some(global_cfg) = &mut args.configs().lock().global_config {
|
||||
global_cfg.vars.clear();
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Row(global_cfg.vars.clone().into()).into_value(args.call_info.name_tag),
|
||||
)))
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
};
|
||||
|
||||
let mut result = nu_data::config::read(name_span, &path)?;
|
||||
|
||||
result.clear();
|
||||
|
||||
config::write(&result, &path)?;
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(args.call_info.name_tag),
|
||||
)))
|
||||
result
|
||||
}
|
||||
|
@ -2,12 +2,11 @@ use crate::prelude::*;
|
||||
use nu_engine::CommandArgs;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
||||
use nu_stream::OutputStream;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use nu_stream::ActionStream;
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"config"
|
||||
@ -21,21 +20,22 @@ impl WholeStreamCommand for Command {
|
||||
"Configuration management."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
|
||||
let path = match args.scope.get_var("config-path") {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
|
||||
..
|
||||
}) => Some(path),
|
||||
_ => nu_data::config::default_path().ok(),
|
||||
};
|
||||
let result = nu_data::config::read(&name, &path)?;
|
||||
|
||||
Ok(futures::stream::iter(vec![ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(name),
|
||||
)])
|
||||
.to_output_stream())
|
||||
if let Some(global_cfg) = &args.configs().lock().global_config {
|
||||
let result = global_cfg.vars.clone();
|
||||
Ok(vec![ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(name),
|
||||
)]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
@ -12,7 +10,6 @@ pub struct Arguments {
|
||||
column_path: ColumnPath,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config get"
|
||||
@ -30,8 +27,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Gets a value from the config"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
get(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
get(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -43,35 +40,29 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn get(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let scope = args.scope.clone();
|
||||
let (Arguments { column_path }, _) = args.process().await?;
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
|
||||
let path = match scope.get_var("config-path") {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
|
||||
..
|
||||
}) => Some(path),
|
||||
_ => nu_data::config::default_path().ok(),
|
||||
let (Arguments { column_path }, _) = args.process()?;
|
||||
|
||||
let result = if let Some(global_cfg) = &ctx.configs.lock().global_config {
|
||||
let result = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
|
||||
let value = crate::commands::get::get_column_path(&column_path, &result)?;
|
||||
Ok(match value {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => list.into_iter().to_action_stream(),
|
||||
x => ActionStream::one(ReturnSuccess::value(x)),
|
||||
})
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
};
|
||||
|
||||
let result = UntaggedValue::row(nu_data::config::read(&name, &path)?).into_value(&name);
|
||||
|
||||
let value = crate::commands::get::get_column_path(&column_path, &result)?;
|
||||
|
||||
Ok(match value {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => {
|
||||
let list: Vec<_> = list
|
||||
.iter()
|
||||
.map(|x| ReturnSuccess::value(x.clone()))
|
||||
.collect();
|
||||
|
||||
futures::stream::iter(list).to_output_stream()
|
||||
}
|
||||
x => OutputStream::one(ReturnSuccess::value(x)),
|
||||
})
|
||||
result
|
||||
}
|
||||
|
@ -13,3 +13,9 @@ pub use path::SubCommand as ConfigPath;
|
||||
pub use remove::SubCommand as ConfigRemove;
|
||||
pub use set::SubCommand as ConfigSet;
|
||||
pub use set_into::SubCommand as ConfigSetInto;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
|
||||
pub fn err_no_global_cfg_present() -> ShellError {
|
||||
ShellError::untagged_runtime_error("No global config found!")
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config path"
|
||||
@ -19,8 +18,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"return the path to the config file"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
path(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
path(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -32,19 +31,16 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn path(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
match args.scope.get_var("config-path") {
|
||||
Some(
|
||||
path
|
||||
@
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::FilePath(_)),
|
||||
..
|
||||
},
|
||||
) => path,
|
||||
_ => UntaggedValue::Primitive(Primitive::FilePath(nu_data::config::default_path()?))
|
||||
.into_value(args.call_info.name_tag),
|
||||
},
|
||||
)))
|
||||
pub fn path(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
if let Some(global_cfg) = &mut args.configs().lock().global_config {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Primitive(Primitive::FilePath(global_cfg.file_path.clone())),
|
||||
)))
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct SubCommand;
|
||||
@ -11,7 +11,6 @@ pub struct Arguments {
|
||||
remove: Tagged<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config remove"
|
||||
@ -29,8 +28,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Removes a value from the config"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
remove(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
remove(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -42,35 +41,36 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_span = args.call_info.name_tag.clone();
|
||||
let scope = args.scope.clone();
|
||||
let (Arguments { remove }, _) = args.process().await?;
|
||||
|
||||
let path = match scope.get_var("config-path") {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
|
||||
..
|
||||
}) => Some(path),
|
||||
_ => nu_data::config::default_path().ok(),
|
||||
};
|
||||
|
||||
let mut result = nu_data::config::read(name_span, &path)?;
|
||||
pub fn remove(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let (Arguments { remove }, _) = args.process()?;
|
||||
|
||||
let key = remove.to_string();
|
||||
|
||||
if result.contains_key(&key) {
|
||||
result.swap_remove(&key);
|
||||
config::write(&result, &path)?;
|
||||
Ok(futures::stream::iter(vec![ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(remove.tag()),
|
||||
)])
|
||||
.to_output_stream())
|
||||
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
|
||||
if global_cfg.vars.contains_key(&key) {
|
||||
global_cfg.vars.swap_remove(&key);
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
Ok(vec![ReturnSuccess::value(
|
||||
UntaggedValue::row(global_cfg.vars.clone()).into_value(remove.tag()),
|
||||
)]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Key does not exist in config",
|
||||
"key",
|
||||
remove.tag(),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Key does not exist in config",
|
||||
"key",
|
||||
remove.tag(),
|
||||
))
|
||||
}
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
@ -13,7 +11,6 @@ pub struct Arguments {
|
||||
value: Value,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config set"
|
||||
@ -29,8 +26,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Sets a value in the config"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
set(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
set(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -59,46 +56,49 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn set(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let scope = args.scope.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let (
|
||||
Arguments {
|
||||
column_path,
|
||||
mut value,
|
||||
},
|
||||
_,
|
||||
) = args.process().await?;
|
||||
) = args.process()?;
|
||||
|
||||
let path = match scope.get_var("config-path") {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
|
||||
..
|
||||
}) => Some(path),
|
||||
_ => nu_data::config::default_path().ok(),
|
||||
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
|
||||
let configuration = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
|
||||
|
||||
if let UntaggedValue::Table(rows) = &value.value {
|
||||
if rows.len() == 1 && rows[0].is_row() {
|
||||
value = rows[0].clone();
|
||||
}
|
||||
}
|
||||
|
||||
match configuration.forgiving_insert_data_at_column_path(&column_path, value) {
|
||||
Ok(Value {
|
||||
value: UntaggedValue::Row(changes),
|
||||
..
|
||||
}) => {
|
||||
global_cfg.vars = changes.entries;
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
|
||||
)))
|
||||
}
|
||||
Ok(_) => Ok(ActionStream::empty()),
|
||||
Err(reason) => Err(reason),
|
||||
}
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
};
|
||||
|
||||
let raw_entries = nu_data::config::read(&name, &path)?;
|
||||
let configuration = UntaggedValue::row(raw_entries).into_value(&name);
|
||||
|
||||
if let UntaggedValue::Table(rows) = &value.value {
|
||||
if rows.len() == 1 && rows[0].is_row() {
|
||||
value = rows[0].clone();
|
||||
}
|
||||
}
|
||||
|
||||
match configuration.forgiving_insert_data_at_column_path(&column_path, value) {
|
||||
Ok(Value {
|
||||
value: UntaggedValue::Row(changes),
|
||||
..
|
||||
}) => {
|
||||
config::write(&changes.entries, &path)?;
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Row(changes).into_value(name),
|
||||
)))
|
||||
}
|
||||
Ok(_) => Ok(OutputStream::empty()),
|
||||
Err(reason) => Err(reason),
|
||||
}
|
||||
result
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct SubCommand;
|
||||
@ -11,7 +11,6 @@ pub struct Arguments {
|
||||
set_into: Tagged<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config set_into"
|
||||
@ -29,8 +28,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Sets a value in the config"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
set_into(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
set_into(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -42,51 +41,46 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn set_into(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let scope = args.scope.clone();
|
||||
let (Arguments { set_into: v }, input) = args.process().await?;
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let (Arguments { set_into: v }, input) = args.process()?;
|
||||
|
||||
let path = match scope.get_var("config-path") {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
|
||||
..
|
||||
}) => Some(path),
|
||||
_ => nu_data::config::default_path().ok(),
|
||||
};
|
||||
|
||||
let mut result = nu_data::config::read(&name, &path)?;
|
||||
|
||||
let rows: Vec<Value> = input.collect().await;
|
||||
let rows: Vec<Value> = input.collect();
|
||||
let key = v.to_string();
|
||||
|
||||
Ok(if rows.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"No values given for set_into",
|
||||
"needs value(s) from pipeline",
|
||||
v.tag(),
|
||||
));
|
||||
} else if rows.len() == 1 {
|
||||
// A single value
|
||||
let value = &rows[0];
|
||||
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
|
||||
if rows.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"No values given for set_into",
|
||||
"needs value(s) from pipeline",
|
||||
v.tag(),
|
||||
));
|
||||
} else if rows.len() == 1 {
|
||||
// A single value
|
||||
let value = &rows[0];
|
||||
|
||||
result.insert(key, value.clone());
|
||||
global_cfg.vars.insert(key, value.clone());
|
||||
} else {
|
||||
// Take in the pipeline as a table
|
||||
let value = UntaggedValue::Table(rows).into_value(name.clone());
|
||||
|
||||
config::write(&result, &path)?;
|
||||
global_cfg.vars.insert(key, value);
|
||||
}
|
||||
|
||||
OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(name),
|
||||
))
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
|
||||
)))
|
||||
} else {
|
||||
// Take in the pipeline as a table
|
||||
let value = UntaggedValue::Table(rows).into_value(name.clone());
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
};
|
||||
|
||||
result.insert(key, value);
|
||||
|
||||
config::write(&result, &path)?;
|
||||
|
||||
OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(name),
|
||||
))
|
||||
})
|
||||
result
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ use nu_protocol::{Signature, SyntaxShape};
|
||||
|
||||
pub struct Cpy;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Cpy {
|
||||
fn name(&self) -> &str {
|
||||
"cp"
|
||||
@ -26,10 +25,10 @@ impl WholeStreamCommand for Cpy {
|
||||
"Copy files."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let shell_manager = args.shell_manager();
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (args, _) = args.process().await?;
|
||||
let (args, _) = args.process()?;
|
||||
shell_manager.cp(args, name)
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"date"
|
||||
@ -19,9 +18,9 @@ impl WholeStreamCommand for Command {
|
||||
"Apply date function."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(&Command, &args.scope)).into_value(Tag::unknown()),
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ pub struct FormatArgs {
|
||||
table: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date format"
|
||||
@ -31,8 +30,8 @@ impl WholeStreamCommand for Date {
|
||||
"Format a given date using the given format string."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
format(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
format(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -51,9 +50,9 @@ impl WholeStreamCommand for Date {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn format(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (FormatArgs { format, table }, input) = args.process().await?;
|
||||
let (FormatArgs { format, table }, input) = args.process()?;
|
||||
|
||||
Ok(input
|
||||
.map(move |value| match value {
|
||||
@ -92,7 +91,7 @@ pub async fn format(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
&tag,
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -7,7 +7,6 @@ use nu_protocol::{Dictionary, ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date list-timezone"
|
||||
@ -21,8 +20,8 @@ impl WholeStreamCommand for Date {
|
||||
"List supported time zones."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
list_timezone(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
list_timezone(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -41,8 +40,8 @@ impl WholeStreamCommand for Date {
|
||||
}
|
||||
}
|
||||
|
||||
async fn list_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn list_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let list = TZ_VARIANTS.iter().map(move |tz| {
|
||||
@ -58,7 +57,7 @@ async fn list_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
))
|
||||
});
|
||||
|
||||
Ok(futures::stream::iter(list).to_output_stream())
|
||||
Ok(list.into_iter().to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -6,7 +6,6 @@ use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date now"
|
||||
@ -20,20 +19,20 @@ impl WholeStreamCommand for Date {
|
||||
"Get the current date."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
now(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
now(args)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn now(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
pub fn now(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let now: DateTime<Local> = Local::now();
|
||||
|
||||
let value = UntaggedValue::date(now.with_timezone(now.offset())).into_value(&tag);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
Ok(ActionStream::one(value))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -7,7 +7,6 @@ use nu_protocol::{Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date to-table"
|
||||
@ -21,8 +20,8 @@ impl WholeStreamCommand for Date {
|
||||
"Print the date in a structured table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
to_table(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
to_table(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -34,8 +33,8 @@ impl WholeStreamCommand for Date {
|
||||
}
|
||||
}
|
||||
|
||||
async fn to_table(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn to_table(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let input = args.input;
|
||||
|
||||
@ -88,7 +87,7 @@ async fn to_table(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
&tag,
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -12,7 +12,6 @@ struct DateToTimeZoneArgs {
|
||||
timezone: Tagged<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date to-timezone"
|
||||
@ -34,8 +33,8 @@ impl WholeStreamCommand for Date {
|
||||
"Use 'date list-timezone' to list all supported time zones."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
to_timezone(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
to_timezone(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -59,9 +58,9 @@ impl WholeStreamCommand for Date {
|
||||
}
|
||||
}
|
||||
|
||||
async fn to_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn to_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (DateToTimeZoneArgs { timezone }, input) = args.process().await?;
|
||||
let (DateToTimeZoneArgs { timezone }, input) = args.process()?;
|
||||
|
||||
Ok(input
|
||||
.map(move |value| match value {
|
||||
@ -86,7 +85,7 @@ async fn to_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
&tag,
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
fn error_message(err: ParseErrorKind) -> &'static str {
|
||||
|
@ -8,7 +8,6 @@ use nu_protocol::Signature;
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date utc"
|
||||
@ -22,13 +21,13 @@ impl WholeStreamCommand for Date {
|
||||
"return the current date in utc."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
utc(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
utc(args)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn utc(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
pub fn utc(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let no_fmt = "".to_string();
|
||||
|
@ -10,7 +10,6 @@ pub struct DebugArgs {
|
||||
raw: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Debug {
|
||||
fn name(&self) -> &str {
|
||||
"debug"
|
||||
@ -24,13 +23,13 @@ impl WholeStreamCommand for Debug {
|
||||
"Print the Rust debug representation of the values."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
debug_value(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
debug_value(args)
|
||||
}
|
||||
}
|
||||
|
||||
async fn debug_value(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (DebugArgs { raw }, input) = args.process().await?;
|
||||
fn debug_value(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (DebugArgs { raw }, input) = args.process()?;
|
||||
Ok(input
|
||||
.map(move |v| {
|
||||
if raw {
|
||||
@ -41,7 +40,7 @@ async fn debug_value(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
ReturnSuccess::debug_value(v)
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -14,7 +14,6 @@ pub struct DefArgs {
|
||||
pub block: CapturedBlock,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Def {
|
||||
fn name(&self) -> &str {
|
||||
"def"
|
||||
@ -35,11 +34,11 @@ impl WholeStreamCommand for Def {
|
||||
"Create a command and set it to a definition."
|
||||
}
|
||||
|
||||
async fn run(&self, _args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, _args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
// Currently, we don't do anything here because we should have already
|
||||
// installed the definition as we entered the scope
|
||||
// We just create a command so that we can get proper coloring
|
||||
Ok(OutputStream::empty())
|
||||
Ok(ActionStream::empty())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -13,7 +13,6 @@ struct DefaultArgs {
|
||||
|
||||
pub struct Default;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Default {
|
||||
fn name(&self) -> &str {
|
||||
"default"
|
||||
@ -33,8 +32,8 @@ impl WholeStreamCommand for Default {
|
||||
"Sets a default row's column if missing."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
default(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
default(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -46,8 +45,8 @@ impl WholeStreamCommand for Default {
|
||||
}
|
||||
}
|
||||
|
||||
async fn default(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (DefaultArgs { column, value }, input) = args.process().await?;
|
||||
fn default(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (DefaultArgs { column, value }, input) = args.process()?;
|
||||
|
||||
Ok(input
|
||||
.map(move |item| {
|
||||
@ -68,7 +67,7 @@ async fn default(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
ReturnSuccess::value(item)
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::whole_stream_command;
|
||||
use nu_engine::EvaluationContext;
|
||||
use std::error::Error;
|
||||
|
||||
pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Box<dyn Error>> {
|
||||
let context = EvaluationContext::basic()?;
|
||||
let context = EvaluationContext::basic();
|
||||
|
||||
{
|
||||
use crate::commands::*;
|
||||
@ -120,9 +120,14 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(Get),
|
||||
whole_stream_command(Update),
|
||||
whole_stream_command(Insert),
|
||||
whole_stream_command(Into),
|
||||
whole_stream_command(IntoBinary),
|
||||
whole_stream_command(IntoInt),
|
||||
whole_stream_command(IntoString),
|
||||
whole_stream_command(SplitBy),
|
||||
// Row manipulation
|
||||
whole_stream_command(All),
|
||||
whole_stream_command(Any),
|
||||
whole_stream_command(Reverse),
|
||||
whole_stream_command(Append),
|
||||
whole_stream_command(Prepend),
|
||||
@ -187,6 +192,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(MathRound),
|
||||
whole_stream_command(MathFloor),
|
||||
whole_stream_command(MathCeil),
|
||||
whole_stream_command(MathSqrt),
|
||||
// File format output
|
||||
whole_stream_command(To),
|
||||
whole_stream_command(ToCsv),
|
||||
@ -232,9 +238,9 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(PathDirname),
|
||||
whole_stream_command(PathExists),
|
||||
whole_stream_command(PathExpand),
|
||||
whole_stream_command(PathExtension),
|
||||
whole_stream_command(PathFilestem),
|
||||
whole_stream_command(PathJoin),
|
||||
whole_stream_command(PathParse),
|
||||
whole_stream_command(PathSplit),
|
||||
whole_stream_command(PathType),
|
||||
// Url
|
||||
whole_stream_command(UrlCommand),
|
||||
|
@ -9,7 +9,6 @@ pub struct Describe;
|
||||
#[derive(Deserialize)]
|
||||
pub struct DescribeArgs {}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Describe {
|
||||
fn name(&self) -> &str {
|
||||
"describe"
|
||||
@ -23,12 +22,12 @@ impl WholeStreamCommand for Describe {
|
||||
"Describes the objects in the stream."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
describe(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
describe(args)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn describe(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn describe(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(args
|
||||
.input
|
||||
.map(|row| {
|
||||
@ -37,7 +36,7 @@ pub async fn describe(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
UntaggedValue::string(name).into_value(Tag::unknown_anchor(row.tag.span)),
|
||||
)
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -12,7 +12,6 @@ struct DoArgs {
|
||||
ignore_errors: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Do {
|
||||
fn name(&self) -> &str {
|
||||
"do"
|
||||
@ -32,8 +31,8 @@ impl WholeStreamCommand for Do {
|
||||
"Runs a block, optionally ignoring errors."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
do_(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
do_(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -52,17 +51,17 @@ impl WholeStreamCommand for Do {
|
||||
}
|
||||
}
|
||||
|
||||
async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn do_(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
||||
|
||||
let context = EvaluationContext::from_args(&raw_args);
|
||||
let (
|
||||
DoArgs {
|
||||
ignore_errors,
|
||||
mut block,
|
||||
block,
|
||||
},
|
||||
input,
|
||||
) = raw_args.process().await?;
|
||||
) = raw_args.process()?;
|
||||
|
||||
let block_redirection = match external_redirection {
|
||||
ExternalRedirection::None => {
|
||||
@ -82,9 +81,8 @@ async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
x => x,
|
||||
};
|
||||
|
||||
block.block.set_redirect(block_redirection);
|
||||
context.scope.enter_scope();
|
||||
let result = run_block(&block.block, &context, input).await;
|
||||
let result = run_block(&block.block, &context, input, block_redirection);
|
||||
context.scope.exit_scope();
|
||||
|
||||
if ignore_errors {
|
||||
@ -93,14 +91,14 @@ async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
match result {
|
||||
Ok(mut stream) => {
|
||||
let output = stream.drain_vec().await;
|
||||
let output = stream.drain_vec();
|
||||
context.clear_errors();
|
||||
Ok(futures::stream::iter(output).to_output_stream())
|
||||
Ok(output.into_iter().to_action_stream())
|
||||
}
|
||||
Err(_) => Ok(OutputStream::empty()),
|
||||
Err(_) => Ok(ActionStream::empty()),
|
||||
}
|
||||
} else {
|
||||
result.map(|x| x.to_output_stream())
|
||||
result.map(|x| x.to_action_stream())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ pub struct Arguments {
|
||||
columns: Option<Tagged<u64>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"drop column"
|
||||
@ -30,12 +29,12 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Remove the last number of columns. If you want to remove columns by name, try 'reject'."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
drop(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
drop(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
use nu_protocol::{row, Value};
|
||||
use nu_protocol::Value;
|
||||
|
||||
vec![Example {
|
||||
description: "Remove the last column of a table",
|
||||
@ -48,8 +47,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { columns }, input) = args.process().await?;
|
||||
fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { columns }, input) = args.process()?;
|
||||
|
||||
let to_drop = if let Some(quantity) = columns {
|
||||
*quantity as usize
|
||||
@ -70,7 +69,7 @@ async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
select_fields(&item, descs, item.tag())
|
||||
})
|
||||
.map(ReturnSuccess::value)
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -11,7 +11,6 @@ pub struct Arguments {
|
||||
rows: Option<Tagged<u64>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"drop"
|
||||
@ -29,8 +28,8 @@ impl WholeStreamCommand for Command {
|
||||
"Remove the last number of rows or columns."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
drop(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
drop(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -52,9 +51,9 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { rows }, input) = args.process().await?;
|
||||
let v: Vec<_> = input.into_vec().await;
|
||||
fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { rows }, input) = args.process()?;
|
||||
let v: Vec<_> = input.into_vec();
|
||||
|
||||
let rows_to_drop = if let Some(quantity) = rows {
|
||||
*quantity as usize
|
||||
@ -63,7 +62,7 @@ async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
};
|
||||
|
||||
Ok(if rows_to_drop == 0 {
|
||||
futures::stream::iter(v).to_output_stream()
|
||||
v.into_iter().to_action_stream()
|
||||
} else {
|
||||
let k = if v.len() < rows_to_drop {
|
||||
0
|
||||
@ -73,6 +72,6 @@ async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let iter = v.into_iter().take(k);
|
||||
|
||||
futures::stream::iter(iter).to_output_stream()
|
||||
iter.to_action_stream()
|
||||
})
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ pub struct DuArgs {
|
||||
min_size: Option<Tagged<u64>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Du {
|
||||
fn name(&self) -> &str {
|
||||
NAME
|
||||
@ -71,8 +70,8 @@ impl WholeStreamCommand for Du {
|
||||
"Find disk usage sizes of specified items."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
du(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
du(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -84,12 +83,12 @@ impl WholeStreamCommand for Du {
|
||||
}
|
||||
}
|
||||
|
||||
async fn du(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn du(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let ctrl_c = args.ctrl_c.clone();
|
||||
let ctrl_c = args.ctrl_c();
|
||||
let ctrl_c_copy = ctrl_c.clone();
|
||||
|
||||
let (args, _): (DuArgs, _) = args.process().await?;
|
||||
let (args, _): (DuArgs, _) = args.process()?;
|
||||
let exclude = args.exclude.map_or(Ok(None), move |x| {
|
||||
Pattern::new(&x.item)
|
||||
.map(Option::Some)
|
||||
@ -131,7 +130,7 @@ async fn du(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
all,
|
||||
};
|
||||
|
||||
let inp = futures::stream::iter(paths);
|
||||
let inp = paths;
|
||||
|
||||
Ok(inp
|
||||
.flat_map(move |path| match path {
|
||||
@ -146,12 +145,12 @@ async fn du(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
output.push(Ok(ReturnSuccess::Value(v.into())));
|
||||
}
|
||||
}
|
||||
futures::stream::iter(output)
|
||||
output
|
||||
}
|
||||
Err(e) => futures::stream::iter(vec![Err(e)]),
|
||||
Err(e) => vec![Err(e)],
|
||||
})
|
||||
.interruptible(ctrl_c_copy)
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
fn glob_err_into(e: GlobError) -> ShellError {
|
||||
|
@ -2,22 +2,14 @@ use crate::prelude::*;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use futures::stream::once;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
hir::{CapturedBlock, ExternalRedirection},
|
||||
Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Each;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EachArgs {
|
||||
block: CapturedBlock,
|
||||
numbered: Tagged<bool>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Each {
|
||||
fn name(&self) -> &str {
|
||||
"each"
|
||||
@ -37,8 +29,8 @@ impl WholeStreamCommand for Each {
|
||||
"Run a block on each row of the table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
each(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
each(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -67,10 +59,11 @@ impl WholeStreamCommand for Each {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn process_row(
|
||||
pub fn process_row(
|
||||
captured_block: Arc<Box<CapturedBlock>>,
|
||||
context: Arc<EvaluationContext>,
|
||||
input: Value,
|
||||
external_redirection: ExternalRedirection,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let input_clone = input.clone();
|
||||
// When we process a row, we need to know whether the block wants to have the contents of the row as
|
||||
@ -80,7 +73,7 @@ pub async fn process_row(
|
||||
let input_stream = if !captured_block.block.params.positional.is_empty() {
|
||||
InputStream::empty()
|
||||
} else {
|
||||
once(async { Ok(input_clone) }).to_input_stream()
|
||||
vec![Ok(input_clone)].into_iter().to_input_stream()
|
||||
};
|
||||
|
||||
context.scope.enter_scope();
|
||||
@ -95,11 +88,16 @@ pub async fn process_row(
|
||||
context.scope.add_var("$it", input);
|
||||
}
|
||||
|
||||
let result = run_block(&captured_block.block, &*context, input_stream).await;
|
||||
let result = run_block(
|
||||
&captured_block.block,
|
||||
&*context,
|
||||
input_stream,
|
||||
external_redirection,
|
||||
);
|
||||
|
||||
context.scope.exit_scope();
|
||||
|
||||
Ok(result?.to_output_stream())
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
|
||||
@ -110,40 +108,42 @@ pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
|
||||
dict.into_value()
|
||||
}
|
||||
|
||||
async fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
||||
let args = raw_args.evaluate_once()?;
|
||||
|
||||
let (each_args, input): (EachArgs, _) = raw_args.process().await?;
|
||||
let block = Arc::new(Box::new(each_args.block));
|
||||
let block: CapturedBlock = args.req(0)?;
|
||||
let numbered: bool = args.has_flag("numbered");
|
||||
|
||||
if each_args.numbered.item {
|
||||
Ok(input
|
||||
let block = Arc::new(Box::new(block));
|
||||
|
||||
if numbered {
|
||||
Ok(args
|
||||
.input
|
||||
.enumerate()
|
||||
.then(move |input| {
|
||||
.map(move |input| {
|
||||
let block = block.clone();
|
||||
let context = context.clone();
|
||||
let row = make_indexed_item(input.0, input.1);
|
||||
|
||||
async {
|
||||
match process_row(block, context, row).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
match process_row(block, context, row, external_redirection) {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Value::error(e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
} else {
|
||||
Ok(input
|
||||
.then(move |input| {
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |input| {
|
||||
let block = block.clone();
|
||||
let context = context.clone();
|
||||
|
||||
async {
|
||||
match process_row(block, context, input).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
match process_row(block, context, input, external_redirection) {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Value::error(e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
|
@ -3,7 +3,8 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
hir::{CapturedBlock, ExternalRedirection},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use serde::Deserialize;
|
||||
@ -17,7 +18,6 @@ pub struct EachGroupArgs {
|
||||
//numbered: Tagged<bool>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for EachGroup {
|
||||
fn name(&self) -> &str {
|
||||
"each group"
|
||||
@ -45,16 +45,58 @@ impl WholeStreamCommand for EachGroup {
|
||||
}]
|
||||
}
|
||||
|
||||
async fn run(&self, raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
let (each_args, input): (EachGroupArgs, _) = raw_args.process().await?;
|
||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
||||
let (each_args, input): (EachGroupArgs, _) = raw_args.process()?;
|
||||
let block = Arc::new(Box::new(each_args.block));
|
||||
|
||||
Ok(input
|
||||
.chunks(each_args.group_size.item)
|
||||
.then(move |input| run_block_on_vec(input, block.clone(), context.clone()))
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
let each_group_iterator = EachGroupIterator {
|
||||
block,
|
||||
context,
|
||||
group_size: each_args.group_size.item,
|
||||
input,
|
||||
external_redirection,
|
||||
};
|
||||
|
||||
Ok(each_group_iterator.flatten().to_action_stream())
|
||||
}
|
||||
}
|
||||
|
||||
struct EachGroupIterator {
|
||||
block: Arc<Box<CapturedBlock>>,
|
||||
context: Arc<EvaluationContext>,
|
||||
group_size: usize,
|
||||
input: InputStream,
|
||||
external_redirection: ExternalRedirection,
|
||||
}
|
||||
|
||||
impl Iterator for EachGroupIterator {
|
||||
type Item = OutputStream;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut group = vec![];
|
||||
let mut current_count = 0;
|
||||
|
||||
while let Some(next) = self.input.next() {
|
||||
group.push(next);
|
||||
|
||||
current_count += 1;
|
||||
if current_count >= self.group_size {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if group.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(run_block_on_vec(
|
||||
group,
|
||||
self.block.clone(),
|
||||
self.context.clone(),
|
||||
self.external_redirection,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,43 +104,33 @@ pub(crate) fn run_block_on_vec(
|
||||
input: Vec<Value>,
|
||||
block: Arc<Box<CapturedBlock>>,
|
||||
context: Arc<EvaluationContext>,
|
||||
) -> impl Future<Output = OutputStream> {
|
||||
external_redirection: ExternalRedirection,
|
||||
) -> OutputStream {
|
||||
let value = Value {
|
||||
value: UntaggedValue::Table(input),
|
||||
tag: Tag::unknown(),
|
||||
};
|
||||
|
||||
async {
|
||||
match process_row(block, context, value).await {
|
||||
Ok(s) => {
|
||||
// We need to handle this differently depending on whether process_row
|
||||
// returned just 1 value or if it returned multiple as a stream.
|
||||
let vec = s.collect::<Vec<_>>().await;
|
||||
match process_row(block, context, value, external_redirection) {
|
||||
Ok(s) => {
|
||||
// We need to handle this differently depending on whether process_row
|
||||
// returned just 1 value or if it returned multiple as a stream.
|
||||
let vec = s.collect::<Vec<_>>();
|
||||
|
||||
// If it returned just one value, just take that value
|
||||
if vec.len() == 1 {
|
||||
return OutputStream::one(vec.into_iter().next().expect(
|
||||
"This should be impossible, we just checked that vec.len() == 1.",
|
||||
));
|
||||
}
|
||||
|
||||
// If it returned multiple values, we need to put them into a table and
|
||||
// return that.
|
||||
let result = vec.into_iter().collect::<Result<Vec<ReturnSuccess>, _>>();
|
||||
let result_table = match result {
|
||||
Ok(t) => t,
|
||||
Err(e) => return OutputStream::one(Err(e)),
|
||||
};
|
||||
|
||||
let table = result_table
|
||||
.into_iter()
|
||||
.filter_map(|x| x.raw_value())
|
||||
.collect();
|
||||
|
||||
OutputStream::one(Ok(ReturnSuccess::Value(UntaggedValue::Table(table).into())))
|
||||
// If it returned just one value, just take that value
|
||||
if vec.len() == 1 {
|
||||
return OutputStream::one(
|
||||
vec.into_iter()
|
||||
.next()
|
||||
.expect("This should be impossible, we just checked that vec.len() == 1."),
|
||||
);
|
||||
}
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
|
||||
// If it returned multiple values, we need to put them into a table and
|
||||
// return that.
|
||||
OutputStream::one(UntaggedValue::Table(vec).into_untagged_value())
|
||||
}
|
||||
Err(e) => OutputStream::one(Value::error(e)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@ pub struct EachWindowArgs {
|
||||
stride: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for EachWindow {
|
||||
fn name(&self) -> &str {
|
||||
"each window"
|
||||
@ -50,16 +49,16 @@ impl WholeStreamCommand for EachWindow {
|
||||
}]
|
||||
}
|
||||
|
||||
async fn run(&self, raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
let (each_args, mut input): (EachWindowArgs, _) = raw_args.process().await?;
|
||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
||||
let (each_args, mut input): (EachWindowArgs, _) = raw_args.process()?;
|
||||
let block = Arc::new(Box::new(each_args.block));
|
||||
|
||||
let mut window: Vec<_> = input
|
||||
.by_ref()
|
||||
.take(*each_args.window_size - 1)
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// `window` must start with dummy values, which will be removed on the first iteration
|
||||
let stride = each_args.stride.map(|x| *x).unwrap_or(1);
|
||||
@ -67,7 +66,7 @@ impl WholeStreamCommand for EachWindow {
|
||||
|
||||
Ok(input
|
||||
.enumerate()
|
||||
.then(move |(i, input)| {
|
||||
.map(move |(i, input)| {
|
||||
// This would probably be more efficient if `last` was a VecDeque
|
||||
// But we can't have that because it needs to be put into a Table
|
||||
window.remove(0);
|
||||
@ -77,17 +76,20 @@ impl WholeStreamCommand for EachWindow {
|
||||
let context = context.clone();
|
||||
let local_window = window.clone();
|
||||
|
||||
async move {
|
||||
if i % stride == 0 {
|
||||
Some(run_block_on_vec(local_window, block, context).await)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if i % stride == 0 {
|
||||
Some(run_block_on_vec(
|
||||
local_window,
|
||||
block,
|
||||
context,
|
||||
external_redirection,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(|x| async { x })
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
.flatten()
|
||||
.to_action_stream())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,12 @@
|
||||
use crate::prelude::*;
|
||||
use bigdecimal::Zero;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::Operator;
|
||||
use nu_protocol::{
|
||||
Primitive, Range, RangeInclusion, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{Primitive, Range, RangeInclusion, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct Echo;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct EchoArgs {
|
||||
pub rest: Vec<Value>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Echo {
|
||||
fn name(&self) -> &str {
|
||||
"echo"
|
||||
@ -27,8 +20,8 @@ impl WholeStreamCommand for Echo {
|
||||
"Echo the arguments back to the user."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
echo(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<InputStream, ShellError> {
|
||||
echo(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -47,36 +40,37 @@ impl WholeStreamCommand for Echo {
|
||||
}
|
||||
}
|
||||
|
||||
async fn echo(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (args, _): (EchoArgs, _) = args.process().await?;
|
||||
fn echo(args: CommandArgs) -> Result<InputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let rest: Vec<Value> = args.rest(0)?;
|
||||
|
||||
let stream = args.rest.into_iter().map(|i| match i.as_string() {
|
||||
Ok(s) => OutputStream::one(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(s).into_value(i.tag.clone()),
|
||||
))),
|
||||
let stream = rest.into_iter().map(|i| match i.as_string() {
|
||||
Ok(s) => InputStream::one(UntaggedValue::string(s).into_value(i.tag.clone())),
|
||||
_ => match i {
|
||||
Value {
|
||||
value: UntaggedValue::Table(table),
|
||||
..
|
||||
} => futures::stream::iter(table.into_iter().map(ReturnSuccess::value))
|
||||
.to_output_stream(),
|
||||
} => InputStream::from_stream(table.into_iter()),
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Range(range)),
|
||||
tag,
|
||||
} => futures::stream::iter(RangeIterator::new(*range, tag)).to_output_stream(),
|
||||
x => OutputStream::one(Ok(ReturnSuccess::Value(x))),
|
||||
} => InputStream::from_stream(RangeIterator::new(*range, tag)),
|
||||
x => InputStream::one(x),
|
||||
},
|
||||
});
|
||||
|
||||
Ok(futures::stream::iter(stream).flatten().to_output_stream())
|
||||
Ok(InputStream::from_stream(stream.flatten()))
|
||||
}
|
||||
|
||||
struct RangeIterator {
|
||||
curr: Primitive,
|
||||
end: Primitive,
|
||||
curr: UntaggedValue,
|
||||
end: UntaggedValue,
|
||||
tag: Tag,
|
||||
is_end_inclusive: bool,
|
||||
moves_up: bool,
|
||||
one: UntaggedValue,
|
||||
negative_one: UntaggedValue,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
impl RangeIterator {
|
||||
@ -93,98 +87,104 @@ impl RangeIterator {
|
||||
|
||||
RangeIterator {
|
||||
moves_up: start <= end,
|
||||
curr: start,
|
||||
end,
|
||||
curr: UntaggedValue::Primitive(start),
|
||||
end: UntaggedValue::Primitive(end),
|
||||
tag,
|
||||
is_end_inclusive: matches!(range.to.1, RangeInclusion::Inclusive),
|
||||
one: UntaggedValue::int(1),
|
||||
negative_one: UntaggedValue::int(-1),
|
||||
done: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for RangeIterator {
|
||||
type Item = Result<ReturnSuccess, ShellError>;
|
||||
type Item = Value;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let ordering = if self.end == Primitive::Nothing {
|
||||
use std::cmp::Ordering;
|
||||
if self.done {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ordering = if self.end == UntaggedValue::Primitive(Primitive::Nothing) {
|
||||
Ordering::Less
|
||||
} else {
|
||||
let result =
|
||||
nu_data::base::coerce_compare_primitive(&self.curr, &self.end).map_err(|_| {
|
||||
ShellError::labeled_error(
|
||||
"Cannot create range",
|
||||
"unsupported range",
|
||||
self.tag.span,
|
||||
)
|
||||
});
|
||||
|
||||
if let Err(result) = result {
|
||||
return Some(Err(result));
|
||||
match (&self.curr, &self.end) {
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Int(x)),
|
||||
UntaggedValue::Primitive(Primitive::Int(y)),
|
||||
) => x.cmp(y),
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Decimal(x)),
|
||||
UntaggedValue::Primitive(Primitive::Decimal(y)),
|
||||
) => x.cmp(y),
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Decimal(x)),
|
||||
UntaggedValue::Primitive(Primitive::Int(y)),
|
||||
) => x.cmp(&(BigDecimal::zero() + y)),
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Int(x)),
|
||||
UntaggedValue::Primitive(Primitive::Decimal(y)),
|
||||
) => (BigDecimal::zero() + x).cmp(y),
|
||||
_ => {
|
||||
self.done = true;
|
||||
return Some(
|
||||
UntaggedValue::Error(ShellError::labeled_error(
|
||||
"Cannot create range",
|
||||
"unsupported range",
|
||||
self.tag.span,
|
||||
))
|
||||
.into_untagged_value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let result = result
|
||||
.expect("Internal error: the error case was already protected, but that failed");
|
||||
|
||||
result.compare()
|
||||
};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
if self.moves_up
|
||||
&& (ordering == Ordering::Less || self.is_end_inclusive && ordering == Ordering::Equal)
|
||||
{
|
||||
let output = UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone());
|
||||
let next_value = nu_data::value::compute_values(Operator::Plus, &self.curr, &self.one);
|
||||
|
||||
let next_value = nu_data::value::compute_values(
|
||||
Operator::Plus,
|
||||
&UntaggedValue::Primitive(self.curr.clone()),
|
||||
&UntaggedValue::int(1),
|
||||
);
|
||||
let mut next = match next_value {
|
||||
Ok(result) => result,
|
||||
|
||||
self.curr = match next_value {
|
||||
Ok(result) => match result {
|
||||
UntaggedValue::Primitive(p) => p,
|
||||
_ => {
|
||||
return Some(Err(ShellError::unimplemented(
|
||||
"Internal error: expected a primitive result from increment",
|
||||
)));
|
||||
}
|
||||
},
|
||||
Err((left_type, right_type)) => {
|
||||
return Some(Err(ShellError::coerce_error(
|
||||
left_type.spanned(self.tag.span),
|
||||
right_type.spanned(self.tag.span),
|
||||
)));
|
||||
self.done = true;
|
||||
return Some(
|
||||
UntaggedValue::Error(ShellError::coerce_error(
|
||||
left_type.spanned(self.tag.span),
|
||||
right_type.spanned(self.tag.span),
|
||||
))
|
||||
.into_untagged_value(),
|
||||
);
|
||||
}
|
||||
};
|
||||
Some(ReturnSuccess::value(output))
|
||||
std::mem::swap(&mut self.curr, &mut next);
|
||||
|
||||
Some(next.into_value(self.tag.clone()))
|
||||
} else if !self.moves_up
|
||||
&& (ordering == Ordering::Greater
|
||||
|| self.is_end_inclusive && ordering == Ordering::Equal)
|
||||
{
|
||||
let output = UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone());
|
||||
let next_value =
|
||||
nu_data::value::compute_values(Operator::Plus, &self.curr, &self.negative_one);
|
||||
|
||||
let next_value = nu_data::value::compute_values(
|
||||
Operator::Plus,
|
||||
&UntaggedValue::Primitive(self.curr.clone()),
|
||||
&UntaggedValue::int(-1),
|
||||
);
|
||||
|
||||
self.curr = match next_value {
|
||||
Ok(result) => match result {
|
||||
UntaggedValue::Primitive(p) => p,
|
||||
_ => {
|
||||
return Some(Err(ShellError::unimplemented(
|
||||
"Internal error: expected a primitive result from increment",
|
||||
)));
|
||||
}
|
||||
},
|
||||
let mut next = match next_value {
|
||||
Ok(result) => result,
|
||||
Err((left_type, right_type)) => {
|
||||
return Some(Err(ShellError::coerce_error(
|
||||
left_type.spanned(self.tag.span),
|
||||
right_type.spanned(self.tag.span),
|
||||
)));
|
||||
self.done = true;
|
||||
return Some(
|
||||
UntaggedValue::Error(ShellError::coerce_error(
|
||||
left_type.spanned(self.tag.span),
|
||||
right_type.spanned(self.tag.span),
|
||||
))
|
||||
.into_untagged_value(),
|
||||
);
|
||||
}
|
||||
};
|
||||
Some(ReturnSuccess::value(output))
|
||||
std::mem::swap(&mut self.curr, &mut next);
|
||||
|
||||
Some(next.into_value(self.tag.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -3,12 +3,11 @@ use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape,
|
||||
UntaggedValue, Value,
|
||||
hir::CapturedBlock, hir::ExternalRedirection, ColumnPath, Primitive, ReturnSuccess, Signature,
|
||||
SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use crate::utils::arguments::arguments;
|
||||
use futures::stream::once;
|
||||
use nu_value_ext::{as_string, ValueExt};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -18,7 +17,6 @@ pub struct Arguments {
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"empty?"
|
||||
@ -35,8 +33,8 @@ impl WholeStreamCommand for Command {
|
||||
"Check for empty values."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
is_empty(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
is_empty(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -81,81 +79,80 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
async fn is_empty(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn is_empty(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let name_tag = Arc::new(args.call_info.name_tag.clone());
|
||||
let context = Arc::new(EvaluationContext::from_args(&args));
|
||||
let (Arguments { mut rest }, input) = args.process().await?;
|
||||
let (Arguments { mut rest }, input) = args.process()?;
|
||||
let (columns, default_block): (Vec<ColumnPath>, Option<Box<CapturedBlock>>) =
|
||||
arguments(&mut rest)?;
|
||||
let default_block = Arc::new(default_block);
|
||||
|
||||
if input.is_empty() {
|
||||
let stream = futures::stream::iter(vec![
|
||||
UntaggedValue::Primitive(Primitive::Nothing).into_value(tag)
|
||||
]);
|
||||
let stream = vec![UntaggedValue::Primitive(Primitive::Nothing).into_value(tag)].into_iter();
|
||||
|
||||
return Ok(InputStream::from_stream(stream)
|
||||
.then(move |input| {
|
||||
.map(move |input| {
|
||||
let tag = name_tag.clone();
|
||||
let context = context.clone();
|
||||
let block = default_block.clone();
|
||||
let columns = vec![];
|
||||
|
||||
async {
|
||||
match process_row(context, input, block, columns, tag).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
match process_row(context, input, block, columns, tag) {
|
||||
Ok(s) => s,
|
||||
Err(e) => ActionStream::one(Err(e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream());
|
||||
.to_action_stream());
|
||||
}
|
||||
|
||||
Ok(input
|
||||
.then(move |input| {
|
||||
.map(move |input| {
|
||||
let tag = name_tag.clone();
|
||||
let context = context.clone();
|
||||
let block = default_block.clone();
|
||||
let columns = columns.clone();
|
||||
|
||||
async {
|
||||
match process_row(context, input, block, columns, tag).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
match process_row(context, input, block, columns, tag) {
|
||||
Ok(s) => s,
|
||||
Err(e) => ActionStream::one(Err(e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
async fn process_row(
|
||||
fn process_row(
|
||||
context: Arc<EvaluationContext>,
|
||||
input: Value,
|
||||
default_block: Arc<Option<Box<CapturedBlock>>>,
|
||||
column_paths: Vec<ColumnPath>,
|
||||
tag: Arc<Tag>,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
) -> Result<ActionStream, ShellError> {
|
||||
let _tag = &*tag;
|
||||
let mut out = Arc::new(None);
|
||||
let results = Arc::make_mut(&mut out);
|
||||
|
||||
if let Some(default_block) = &*default_block {
|
||||
let for_block = input.clone();
|
||||
let input_stream = once(async { Ok(for_block) }).to_input_stream();
|
||||
let input_stream = vec![Ok(for_block)].into_iter().to_input_stream();
|
||||
|
||||
context.scope.enter_scope();
|
||||
context.scope.add_vars(&default_block.captured.entries);
|
||||
context.scope.add_var("$it", input.clone());
|
||||
|
||||
let stream = run_block(&default_block.block, &*context, input_stream).await;
|
||||
let stream = run_block(
|
||||
&default_block.block,
|
||||
&*context,
|
||||
input_stream,
|
||||
ExternalRedirection::Stdout,
|
||||
);
|
||||
context.scope.exit_scope();
|
||||
|
||||
let mut stream = stream?;
|
||||
*results = Some({
|
||||
let values = stream.drain_vec().await;
|
||||
let values = stream.drain_vec();
|
||||
|
||||
let errors = context.get_errors();
|
||||
|
||||
@ -186,7 +183,7 @@ async fn process_row(
|
||||
ref tag,
|
||||
} => {
|
||||
if column_paths.is_empty() {
|
||||
Ok(OutputStream::one(ReturnSuccess::value({
|
||||
Ok(ActionStream::one(ReturnSuccess::value({
|
||||
let is_empty = input.is_empty();
|
||||
|
||||
if default_block.is_some() {
|
||||
@ -229,10 +226,10 @@ async fn process_row(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(obj)))
|
||||
Ok(ActionStream::one(ReturnSuccess::value(obj)))
|
||||
}
|
||||
}
|
||||
other => Ok(OutputStream::one(ReturnSuccess::value({
|
||||
other => Ok(ActionStream::one(ReturnSuccess::value({
|
||||
if other.is_empty() {
|
||||
results
|
||||
.clone()
|
||||
|
@ -17,7 +17,6 @@ pub struct EnterArgs {
|
||||
encoding: Option<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Enter {
|
||||
fn name(&self) -> &str {
|
||||
"enter"
|
||||
@ -51,8 +50,8 @@ For a more complete list of encodings please refer to the encoding_rs
|
||||
documentation link at https://docs.rs/encoding_rs/0.8.28/encoding_rs/#statics"#
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
enter(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
enter(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -76,64 +75,40 @@ documentation link at https://docs.rs/encoding_rs/0.8.28/encoding_rs/#statics"#
|
||||
}
|
||||
}
|
||||
|
||||
async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let scope = raw_args.scope.clone();
|
||||
let shell_manager = raw_args.shell_manager.clone();
|
||||
let head = raw_args.call_info.args.head.clone();
|
||||
let ctrl_c = raw_args.ctrl_c.clone();
|
||||
let current_errors = raw_args.current_errors.clone();
|
||||
let host = raw_args.host.clone();
|
||||
let tag = raw_args.call_info.name_tag.clone();
|
||||
let (EnterArgs { location, encoding }, _) = raw_args.process().await?;
|
||||
fn enter(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let head = args.call_info.args.head.clone();
|
||||
let context = args.context.clone();
|
||||
let scope = args.scope().clone();
|
||||
let path = args.context.shell_manager.path();
|
||||
let (EnterArgs { location, encoding }, _) = args.process()?;
|
||||
let location_string = location.display().to_string();
|
||||
let location_clone = location_string.clone();
|
||||
|
||||
if location_string.starts_with("help") {
|
||||
let spec = location_string.split(':').collect::<Vec<&str>>();
|
||||
|
||||
if spec.len() == 2 {
|
||||
let (_, command) = (spec[0], spec[1]);
|
||||
|
||||
if scope.has_command(command) {
|
||||
return Ok(OutputStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterHelpShell(
|
||||
UntaggedValue::string(command).into_value(Tag::unknown()),
|
||||
),
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(OutputStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterHelpShell(UntaggedValue::nothing().into_value(Tag::unknown())),
|
||||
)))
|
||||
} else if location.is_dir() {
|
||||
Ok(OutputStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterShell(location_clone),
|
||||
if location.is_dir() {
|
||||
Ok(ActionStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterShell(location_string),
|
||||
)))
|
||||
} else {
|
||||
// If it's a file, attempt to open the file as a value and enter it
|
||||
let cwd = shell_manager.path();
|
||||
let cwd = path;
|
||||
|
||||
let full_path = std::path::PathBuf::from(cwd);
|
||||
let span = location.span();
|
||||
|
||||
let (file_extension, tagged_contents) = crate::commands::open::fetch(
|
||||
&full_path,
|
||||
&PathBuf::from(location_clone),
|
||||
&PathBuf::from(location_string),
|
||||
span,
|
||||
encoding,
|
||||
)
|
||||
.await?;
|
||||
)?;
|
||||
|
||||
match tagged_contents.value {
|
||||
UntaggedValue::Primitive(Primitive::String(_)) => {
|
||||
if let Some(extension) = file_extension {
|
||||
let command_name = format!("from {}", extension);
|
||||
if let Some(converter) = scope.get_command(&command_name) {
|
||||
let new_args = RawCommandArgs {
|
||||
host,
|
||||
ctrl_c,
|
||||
current_errors,
|
||||
shell_manager,
|
||||
let tag = tagged_contents.tag.clone();
|
||||
let new_args = CommandArgs {
|
||||
context,
|
||||
call_info: UnevaluatedCallInfo {
|
||||
args: nu_protocol::hir::Call {
|
||||
head,
|
||||
@ -144,38 +119,34 @@ async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
},
|
||||
name_tag: tag.clone(),
|
||||
},
|
||||
scope: scope.clone(),
|
||||
input: InputStream::one(tagged_contents),
|
||||
};
|
||||
let tag = tagged_contents.tag.clone();
|
||||
let mut result = converter
|
||||
.run(new_args.with_input(vec![tagged_contents]))
|
||||
.await?;
|
||||
let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
|
||||
result.drain_vec().await;
|
||||
Ok(futures::stream::iter(result_vec.into_iter().map(
|
||||
move |res| match res {
|
||||
Ok(ReturnSuccess::Value(Value { value, .. })) => Ok(
|
||||
ReturnSuccess::Action(CommandAction::EnterValueShell(Value {
|
||||
let mut result = converter.run(new_args)?;
|
||||
let result_vec: Vec<Value> = result.drain_vec();
|
||||
Ok(result_vec
|
||||
.into_iter()
|
||||
.map(move |res| {
|
||||
let Value { value, .. } = res;
|
||||
Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
|
||||
Value {
|
||||
value,
|
||||
tag: tag.clone(),
|
||||
})),
|
||||
),
|
||||
x => x,
|
||||
},
|
||||
))
|
||||
.to_output_stream())
|
||||
},
|
||||
)))
|
||||
})
|
||||
.to_action_stream())
|
||||
} else {
|
||||
Ok(OutputStream::one(ReturnSuccess::action(
|
||||
Ok(ActionStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterValueShell(tagged_contents),
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Ok(OutputStream::one(ReturnSuccess::action(
|
||||
Ok(ActionStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterValueShell(tagged_contents),
|
||||
)))
|
||||
}
|
||||
}
|
||||
_ => Ok(OutputStream::one(ReturnSuccess::action(
|
||||
_ => Ok(ActionStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterValueShell(tagged_contents),
|
||||
))),
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ pub struct EveryArgs {
|
||||
skip: Tagged<bool>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Every {
|
||||
fn name(&self) -> &str {
|
||||
"every"
|
||||
@ -36,8 +35,8 @@ impl WholeStreamCommand for Every {
|
||||
"Show (or skip) every n-th row, starting from the first one."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
every(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
every(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -63,15 +62,15 @@ impl WholeStreamCommand for Every {
|
||||
}
|
||||
}
|
||||
|
||||
async fn every(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (EveryArgs { stride, skip }, input) = args.process().await?;
|
||||
fn every(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (EveryArgs { stride, skip }, input) = args.process()?;
|
||||
|
||||
let stride = stride.item;
|
||||
let skip = skip.item;
|
||||
|
||||
Ok(input
|
||||
.enumerate()
|
||||
.filter_map(move |(i, value)| async move {
|
||||
.filter_map(move |(i, value)| {
|
||||
let stride_desired = if stride < 1 { 1 } else { stride } as usize;
|
||||
let should_include = skip == (i % stride_desired != 0);
|
||||
|
||||
@ -81,7 +80,7 @@ async fn every(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
None
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -13,7 +13,6 @@ pub struct ExecArgs {
|
||||
pub rest: Vec<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Exec {
|
||||
fn name(&self) -> &str {
|
||||
"exec"
|
||||
@ -32,8 +31,8 @@ impl WholeStreamCommand for Exec {
|
||||
"Execute command."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
exec(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
exec(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -53,12 +52,12 @@ impl WholeStreamCommand for Exec {
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn exec(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn exec(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process::Command;
|
||||
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (args, _): (ExecArgs, _) = args.process().await?;
|
||||
let (args, _): (ExecArgs, _) = args.process()?;
|
||||
|
||||
let mut command = Command::new(args.command.item);
|
||||
for tagged_arg in args.rest {
|
||||
@ -75,7 +74,7 @@ async fn exec(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
async fn exec(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn exec(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Err(ShellError::labeled_error(
|
||||
"Error on exec",
|
||||
"exec is not supported on your platform",
|
||||
|
@ -4,7 +4,6 @@ use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape};
|
||||
|
||||
pub struct Exit;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Exit {
|
||||
fn name(&self) -> &str {
|
||||
"exit"
|
||||
@ -24,8 +23,8 @@ impl WholeStreamCommand for Exit {
|
||||
"Exit the current shell (or all shells)."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
exit(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
exit(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -44,8 +43,8 @@ impl WholeStreamCommand for Exit {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn exit(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
pub fn exit(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let code = if let Some(value) = args.call_info.args.nth(0) {
|
||||
value.as_i32()?
|
||||
@ -59,7 +58,7 @@ pub async fn exit(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
CommandAction::LeaveShell(code)
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::action(command_action)))
|
||||
Ok(ActionStream::one(ReturnSuccess::action(command_action)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -11,7 +11,6 @@ pub struct FirstArgs {
|
||||
rows: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for First {
|
||||
fn name(&self) -> &str {
|
||||
"first"
|
||||
@ -29,8 +28,8 @@ impl WholeStreamCommand for First {
|
||||
"Show only the first number of rows."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
first(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
first(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -52,15 +51,15 @@ impl WholeStreamCommand for First {
|
||||
}
|
||||
}
|
||||
|
||||
async fn first(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (FirstArgs { rows }, input) = args.process().await?;
|
||||
fn first(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (FirstArgs { rows }, input) = args.process()?;
|
||||
let rows_desired = if let Some(quantity) = rows {
|
||||
*quantity
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
Ok(input.take(rows_desired).to_output_stream())
|
||||
Ok(input.take(rows_desired).to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -13,7 +13,6 @@ pub struct Arguments {
|
||||
rest: Vec<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"flatten"
|
||||
@ -27,8 +26,8 @@ impl WholeStreamCommand for Command {
|
||||
"Flatten the table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
flatten(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
flatten(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -52,14 +51,14 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
async fn flatten(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn flatten(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (Arguments { rest: columns }, input) = args.process().await?;
|
||||
let (Arguments { rest: columns }, input) = args.process()?;
|
||||
|
||||
Ok(input
|
||||
.map(move |item| futures::stream::iter(flat_value(&columns, &item, &tag).into_iter()))
|
||||
.map(move |item| flat_value(&columns, &item, &tag).into_iter())
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
enum TableInside<'a> {
|
||||
|
@ -13,7 +13,6 @@ pub struct FormatArgs {
|
||||
pattern: Tagged<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Format {
|
||||
fn name(&self) -> &str {
|
||||
"format"
|
||||
@ -31,8 +30,8 @@ impl WholeStreamCommand for Format {
|
||||
"Format columns into a string using a simple pattern."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
format_command(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
format_command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -44,51 +43,48 @@ impl WholeStreamCommand for Format {
|
||||
}
|
||||
}
|
||||
|
||||
async fn format_command(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn format_command(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let ctx = Arc::new(EvaluationContext::from_args(&args));
|
||||
let (FormatArgs { pattern }, input) = args.process().await?;
|
||||
let (FormatArgs { pattern }, input) = args.process()?;
|
||||
|
||||
let format_pattern = format(&pattern);
|
||||
let commands = Arc::new(format_pattern);
|
||||
|
||||
Ok(input
|
||||
.then(move |value| {
|
||||
.map(move |value| {
|
||||
let mut output = String::new();
|
||||
let commands = commands.clone();
|
||||
let ctx = ctx.clone();
|
||||
|
||||
async move {
|
||||
for command in &*commands {
|
||||
match command {
|
||||
FormatCommand::Text(s) => {
|
||||
output.push_str(&s);
|
||||
}
|
||||
FormatCommand::Column(c) => {
|
||||
// FIXME: use the correct spans
|
||||
let full_column_path = nu_parser::parse_full_column_path(
|
||||
&(c.to_string()).spanned(Span::unknown()),
|
||||
&ctx.scope,
|
||||
);
|
||||
for command in &*commands {
|
||||
match command {
|
||||
FormatCommand::Text(s) => {
|
||||
output.push_str(&s);
|
||||
}
|
||||
FormatCommand::Column(c) => {
|
||||
// FIXME: use the correct spans
|
||||
let full_column_path = nu_parser::parse_full_column_path(
|
||||
&(c.to_string()).spanned(Span::unknown()),
|
||||
&ctx.scope,
|
||||
);
|
||||
|
||||
ctx.scope.enter_scope();
|
||||
ctx.scope.add_var("$it", value.clone());
|
||||
let result = evaluate_baseline_expr(&full_column_path.0, &*ctx).await;
|
||||
ctx.scope.exit_scope();
|
||||
ctx.scope.enter_scope();
|
||||
ctx.scope.add_var("$it", value.clone());
|
||||
let result = evaluate_baseline_expr(&full_column_path.0, &*ctx);
|
||||
ctx.scope.exit_scope();
|
||||
|
||||
if let Ok(c) = result {
|
||||
output
|
||||
.push_str(&value::format_leaf(c.borrow()).plain_string(100_000))
|
||||
} else {
|
||||
// That column doesn't match, so don't emit anything
|
||||
}
|
||||
if let Ok(c) = result {
|
||||
output.push_str(&value::format_leaf(c.borrow()).plain_string(100_000))
|
||||
} else {
|
||||
// That column doesn't match, so don't emit anything
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReturnSuccess::value(UntaggedValue::string(output).into_untagged_value())
|
||||
}
|
||||
|
||||
ReturnSuccess::value(UntaggedValue::string(output).into_untagged_value())
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -16,7 +16,6 @@ pub struct Arguments {
|
||||
format: Tagged<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FileSize {
|
||||
fn name(&self) -> &str {
|
||||
"format filesize"
|
||||
@ -40,8 +39,8 @@ impl WholeStreamCommand for FileSize {
|
||||
"Converts a column of filesizes to some specified format"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
filesize(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
filesize(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -60,11 +59,11 @@ impl WholeStreamCommand for FileSize {
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_row(
|
||||
fn process_row(
|
||||
input: Value,
|
||||
format: Tagged<String>,
|
||||
field: Arc<ColumnPath>,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
) -> Result<ActionStream, ShellError> {
|
||||
Ok({
|
||||
let replace_for = get_data_by_column_path(&input, &field, move |_, _, error| error);
|
||||
match replace_for {
|
||||
@ -76,7 +75,7 @@ async fn process_row(
|
||||
{
|
||||
let byte_format = InlineShape::format_bytes(&fs, Some(&format.item));
|
||||
let byte_value = Value::from(byte_format.1);
|
||||
OutputStream::one(ReturnSuccess::value(
|
||||
ActionStream::one(ReturnSuccess::value(
|
||||
input.replace_data_at_column_path(&field, byte_value).expect("Given that the existence check was already done, this shouldn't trigger never"),
|
||||
))
|
||||
} else {
|
||||
@ -87,29 +86,27 @@ async fn process_row(
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
Err(e) => ActionStream::one(Err(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn filesize(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { field, format }, input) = raw_args.process().await?;
|
||||
fn filesize(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { field, format }, input) = raw_args.process()?;
|
||||
let field = Arc::new(field);
|
||||
|
||||
Ok(input
|
||||
.then(move |input| {
|
||||
.map(move |input| {
|
||||
let format = format.clone();
|
||||
let field = field.clone();
|
||||
|
||||
async {
|
||||
match process_row(input, format, field).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
match process_row(input, format, field) {
|
||||
Ok(s) => s,
|
||||
Err(e) => ActionStream::one(Err(e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct From;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for From {
|
||||
fn name(&self) -> &str {
|
||||
"from"
|
||||
@ -19,10 +18,10 @@ impl WholeStreamCommand for From {
|
||||
"Parse content (string or binary) as a table (input format based on subcommand, like csv, ini, json, toml)."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(&From, &args.scope)).into_value(Tag::unknown()),
|
||||
)))
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(get_full_help(&From, args.scope())).into_value(Tag::unknown()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,13 +6,6 @@ use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct FromCsv;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromCsvArgs {
|
||||
noheaders: bool,
|
||||
separator: Option<Value>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromCsv {
|
||||
fn name(&self) -> &str {
|
||||
"from csv"
|
||||
@ -37,8 +30,8 @@ impl WholeStreamCommand for FromCsv {
|
||||
"Parse text as .csv and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_csv(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_csv(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -67,16 +60,14 @@ impl WholeStreamCommand for FromCsv {
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let noheaders = args.has_flag("noheaders");
|
||||
let separator: Option<Value> = args.get_flag("separator")?;
|
||||
let input = args.input;
|
||||
|
||||
let (
|
||||
FromCsvArgs {
|
||||
noheaders,
|
||||
separator,
|
||||
},
|
||||
input,
|
||||
) = args.process().await?;
|
||||
let sep = match separator {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(s)),
|
||||
@ -100,7 +91,7 @@ async fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
_ => ',',
|
||||
};
|
||||
|
||||
from_delimited_data(noheaders, sep, "CSV", input, name).await
|
||||
from_delimited_data(noheaders, sep, "CSV", input, name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -45,7 +45,7 @@ fn from_delimited_string_to_value(
|
||||
Ok(UntaggedValue::Table(rows).into_value(&tag))
|
||||
}
|
||||
|
||||
pub async fn from_delimited_data(
|
||||
pub fn from_delimited_data(
|
||||
noheaders: bool,
|
||||
sep: char,
|
||||
format_name: &'static str,
|
||||
@ -53,7 +53,7 @@ pub async fn from_delimited_data(
|
||||
name: Tag,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let name_tag = name;
|
||||
let concat_string = input.collect_string(name_tag.clone()).await?;
|
||||
let concat_string = input.collect_string(name_tag.clone())?;
|
||||
let sample_lines = concat_string.item.lines().take(3).collect_vec().join("\n");
|
||||
|
||||
match from_delimited_string_to_value(concat_string.item, noheaders, sep, name_tag.clone()) {
|
||||
@ -61,7 +61,7 @@ pub async fn from_delimited_data(
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => Ok(futures::stream::iter(list).to_output_stream()),
|
||||
} => Ok(list.into_iter().to_output_stream()),
|
||||
x => Ok(OutputStream::one(x)),
|
||||
},
|
||||
Err(err) => {
|
||||
@ -80,7 +80,7 @@ pub async fn from_delimited_data(
|
||||
Err(ShellError::labeled_error_with_secondary(
|
||||
line_one,
|
||||
line_two,
|
||||
name_tag.clone(),
|
||||
name_tag,
|
||||
"value originates from here",
|
||||
concat_string.tag,
|
||||
))
|
||||
|
@ -3,20 +3,13 @@ use ::eml_parser::eml::*;
|
||||
use ::eml_parser::EmlParser;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_protocol::{Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct FromEml;
|
||||
|
||||
const DEFAULT_BODY_PREVIEW: usize = 50;
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct FromEmlArgs {
|
||||
#[serde(rename(deserialize = "preview-body"))]
|
||||
preview_body: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromEml {
|
||||
fn name(&self) -> &str {
|
||||
"from eml"
|
||||
@ -35,8 +28,8 @@ impl WholeStreamCommand for FromEml {
|
||||
"Parse text as .eml and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_eml(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_eml(args)
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,15 +66,15 @@ fn headerfieldvalue_to_value(tag: &Tag, value: &HeaderFieldValue) -> UntaggedVal
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_eml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_eml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (eml_args, input): (FromEmlArgs, _) = args.process().await?;
|
||||
let value = input.collect_string(tag.clone()).await?;
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let body_preview = eml_args
|
||||
.preview_body
|
||||
.map(|b| b.item)
|
||||
.unwrap_or(DEFAULT_BODY_PREVIEW);
|
||||
let preview_body: Option<Tagged<usize>> = args.get_flag("preview-body")?;
|
||||
|
||||
let value = args.input.collect_string(tag.clone())?;
|
||||
|
||||
let body_preview = preview_body.map(|b| b.item).unwrap_or(DEFAULT_BODY_PREVIEW);
|
||||
|
||||
let eml = EmlParser::from_string(value.item)
|
||||
.with_body_preview(body_preview)
|
||||
@ -116,7 +109,7 @@ async fn from_eml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
dict.insert_untagged("Body", UntaggedValue::string(body));
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(dict.into_value())))
|
||||
Ok(OutputStream::one(dict.into_value()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -4,12 +4,11 @@ use ical::parser::ical::component::*;
|
||||
use ical::property::Property;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use std::io::BufReader;
|
||||
|
||||
pub struct FromIcs;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromIcs {
|
||||
fn name(&self) -> &str {
|
||||
"from ics"
|
||||
@ -23,17 +22,17 @@ impl WholeStreamCommand for FromIcs {
|
||||
"Parse text as .ics and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ics(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ics(args)
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_ics(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn from_ics(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let input_string = input.collect_string(tag.clone()).await?.item;
|
||||
let input_string = input.collect_string(tag.clone())?.item;
|
||||
let input_bytes = input_string.as_bytes();
|
||||
let buf_reader = BufReader::new(input_bytes);
|
||||
let parser = ical::IcalParser::new(buf_reader);
|
||||
@ -44,8 +43,8 @@ async fn from_ics(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
for calendar in parser {
|
||||
match calendar {
|
||||
Ok(c) => output.push(ReturnSuccess::value(calendar_to_value(c, tag.clone()))),
|
||||
Err(_) => output.push(Err(ShellError::labeled_error(
|
||||
Ok(c) => output.push(calendar_to_value(c, tag.clone())),
|
||||
Err(_) => output.push(Value::error(ShellError::labeled_error(
|
||||
"Could not parse as .ics",
|
||||
"input cannot be parsed as .ics",
|
||||
tag.clone(),
|
||||
@ -53,7 +52,7 @@ async fn from_ics(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(futures::stream::iter(output).to_output_stream())
|
||||
Ok(output.into_iter().to_output_stream())
|
||||
}
|
||||
|
||||
fn calendar_to_value(calendar: IcalCalendar, tag: Tag) -> Value {
|
||||
|
@ -6,7 +6,6 @@ use std::collections::HashMap;
|
||||
|
||||
pub struct FromIni;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromIni {
|
||||
fn name(&self) -> &str {
|
||||
"from ini"
|
||||
@ -20,8 +19,8 @@ impl WholeStreamCommand for FromIni {
|
||||
"Parse text as .ini and create table"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ini(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ini(args)
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,18 +59,18 @@ pub fn from_ini_string_to_value(
|
||||
Ok(convert_ini_top_to_nu_value(&v, tag))
|
||||
}
|
||||
|
||||
async fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
let concat_string = input.collect_string(tag.clone()).await?;
|
||||
let concat_string = input.collect_string(tag.clone())?;
|
||||
|
||||
match from_ini_string_to_value(concat_string.item, tag.clone()) {
|
||||
Ok(x) => match x {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => Ok(futures::stream::iter(list).to_output_stream()),
|
||||
} => Ok(list.into_iter().to_output_stream()),
|
||||
x => Ok(OutputStream::one(x)),
|
||||
},
|
||||
Err(_) => Err(ShellError::labeled_error_with_secondary(
|
||||
|
@ -1,16 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
|
||||
pub struct FromJson;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromJsonArgs {
|
||||
objects: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromJson {
|
||||
fn name(&self) -> &str {
|
||||
"from json"
|
||||
@ -28,8 +22,8 @@ impl WholeStreamCommand for FromJson {
|
||||
"Parse text as .json and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_json(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_json(args)
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,29 +62,32 @@ pub fn from_json_string_to_value(s: String, tag: impl Into<Tag>) -> nu_json::Res
|
||||
Ok(convert_json_value_to_nu_value(&v, tag))
|
||||
}
|
||||
|
||||
async fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (FromJsonArgs { objects }, input) = args.process().await?;
|
||||
let concat_string = input.collect_string(name_tag.clone()).await?;
|
||||
let args = args.evaluate_once()?;
|
||||
let objects = args.has_flag("objects");
|
||||
|
||||
let concat_string = args.input.collect_string(name_tag.clone())?;
|
||||
|
||||
let string_clone: Vec<_> = concat_string.item.lines().map(|x| x.to_string()).collect();
|
||||
|
||||
if objects {
|
||||
Ok(
|
||||
futures::stream::iter(string_clone.into_iter().filter_map(move |json_str| {
|
||||
Ok(string_clone
|
||||
.into_iter()
|
||||
.filter_map(move |json_str| {
|
||||
if json_str.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
match from_json_string_to_value(json_str, &name_tag) {
|
||||
Ok(x) => Some(ReturnSuccess::value(x)),
|
||||
Ok(x) => Some(x),
|
||||
Err(e) => {
|
||||
let mut message = "Could not parse as JSON (".to_string();
|
||||
message.push_str(&e.to_string());
|
||||
message.push(')');
|
||||
|
||||
Some(Err(ShellError::labeled_error_with_secondary(
|
||||
Some(Value::error(ShellError::labeled_error_with_secondary(
|
||||
message,
|
||||
"input cannot be parsed as JSON",
|
||||
name_tag.clone(),
|
||||
@ -99,27 +96,24 @@ async fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
)))
|
||||
}
|
||||
}
|
||||
}))
|
||||
.to_output_stream(),
|
||||
)
|
||||
})
|
||||
.to_output_stream())
|
||||
} else {
|
||||
match from_json_string_to_value(concat_string.item, name_tag.clone()) {
|
||||
Ok(x) => match x {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => Ok(
|
||||
futures::stream::iter(list.into_iter().map(ReturnSuccess::value))
|
||||
.to_output_stream(),
|
||||
),
|
||||
x => Ok(OutputStream::one(ReturnSuccess::value(x))),
|
||||
} => Ok(list.into_iter().to_output_stream()),
|
||||
|
||||
x => Ok(OutputStream::one(x)),
|
||||
},
|
||||
Err(e) => {
|
||||
let mut message = "Could not parse as JSON (".to_string();
|
||||
message.push_str(&e.to_string());
|
||||
message.push(')');
|
||||
|
||||
Ok(OutputStream::one(Err(
|
||||
Ok(OutputStream::one(Value::error(
|
||||
ShellError::labeled_error_with_secondary(
|
||||
message,
|
||||
"input cannot be parsed as JSON",
|
||||
|
@ -3,50 +3,34 @@ use calamine::*;
|
||||
use nu_data::TaggedListBuilder;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
|
||||
use std::io::Cursor;
|
||||
|
||||
pub struct FromOds;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromOdsArgs {
|
||||
noheaders: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromOds {
|
||||
fn name(&self) -> &str {
|
||||
"from ods"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from ods").switch(
|
||||
"noheaders",
|
||||
"don't treat the first row as column names",
|
||||
Some('n'),
|
||||
)
|
||||
Signature::build("from ods")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Parse OpenDocument Spreadsheet(.ods) data and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ods(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ods(args)
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_ods(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_ods(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let span = tag.span;
|
||||
|
||||
let (
|
||||
FromOdsArgs {
|
||||
noheaders: _noheaders,
|
||||
},
|
||||
input,
|
||||
) = args.process().await?;
|
||||
let bytes = input.collect_binary(tag.clone()).await?;
|
||||
let bytes = args.input.collect_binary(tag.clone())?;
|
||||
let buf: Cursor<Vec<u8>> = Cursor::new(bytes.item);
|
||||
let mut ods = Ods::<_>::new(buf).map_err(|_| {
|
||||
ShellError::labeled_error("Could not load ods file", "could not load ods file", &tag)
|
||||
@ -88,7 +72,7 @@ async fn from_ods(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(dict.into_value())))
|
||||
Ok(OutputStream::one(dict.into_value()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,26 +1,14 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
Primitive, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{Primitive, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct FromSsv;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromSsvArgs {
|
||||
noheaders: bool,
|
||||
#[serde(rename(deserialize = "aligned-columns"))]
|
||||
aligned_columns: bool,
|
||||
#[serde(rename(deserialize = "minimum-spaces"))]
|
||||
minimum_spaces: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
const STRING_REPRESENTATION: &str = "from ssv";
|
||||
const DEFAULT_MINIMUM_SPACES: usize = 2;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromSsv {
|
||||
fn name(&self) -> &str {
|
||||
STRING_REPRESENTATION
|
||||
@ -46,8 +34,8 @@ impl WholeStreamCommand for FromSsv {
|
||||
"Parse text as space-separated values and create a table. The default minimum number of spaces counted as a separator is 2."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ssv(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ssv(args)
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,17 +235,15 @@ fn from_ssv_string_to_value(
|
||||
UntaggedValue::Table(rows).into_value(&tag)
|
||||
}
|
||||
|
||||
async fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (
|
||||
FromSsvArgs {
|
||||
noheaders,
|
||||
aligned_columns,
|
||||
minimum_spaces,
|
||||
},
|
||||
input,
|
||||
) = args.process().await?;
|
||||
let concat_string = input.collect_string(name.clone()).await?;
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let noheaders = args.has_flag("noheaders");
|
||||
let aligned_columns = args.has_flag("aligned-columns");
|
||||
let minimum_spaces: Option<Tagged<usize>> = args.get_flag("minimum-spaces")?;
|
||||
|
||||
let concat_string = args.input.collect_string(name.clone())?;
|
||||
let split_at = match minimum_spaces {
|
||||
Some(number) => number.item,
|
||||
None => DEFAULT_MINIMUM_SPACES,
|
||||
@ -269,15 +255,13 @@ async fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
noheaders,
|
||||
aligned_columns,
|
||||
split_at,
|
||||
name.clone(),
|
||||
name,
|
||||
) {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => {
|
||||
futures::stream::iter(list.into_iter().map(ReturnSuccess::value)).to_output_stream()
|
||||
}
|
||||
x => OutputStream::one(ReturnSuccess::value(x)),
|
||||
} => list.into_iter().to_output_stream(),
|
||||
x => OutputStream::one(x),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
|
||||
pub struct FromToml;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromToml {
|
||||
fn name(&self) -> &str {
|
||||
"from toml"
|
||||
@ -19,8 +18,8 @@ impl WholeStreamCommand for FromToml {
|
||||
"Parse text as .toml and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_toml(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_toml(args)
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,21 +60,20 @@ pub fn from_toml_string_to_value(s: String, tag: impl Into<Tag>) -> Result<Value
|
||||
Ok(convert_toml_value_to_nu_value(&v, tag))
|
||||
}
|
||||
|
||||
pub async fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
pub fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let concat_string = input.collect_string(tag.clone()).await?;
|
||||
let concat_string = input.collect_string(tag.clone())?;
|
||||
Ok(
|
||||
match from_toml_string_to_value(concat_string.item, tag.clone()) {
|
||||
Ok(x) => match x {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => futures::stream::iter(list.into_iter().map(ReturnSuccess::value))
|
||||
.to_output_stream(),
|
||||
x => OutputStream::one(ReturnSuccess::value(x)),
|
||||
} => list.into_iter().to_output_stream(),
|
||||
x => OutputStream::one(x),
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
|
@ -6,12 +6,6 @@ use nu_protocol::Signature;
|
||||
|
||||
pub struct FromTsv;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromTsvArgs {
|
||||
noheaders: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromTsv {
|
||||
fn name(&self) -> &str {
|
||||
"from tsv"
|
||||
@ -29,16 +23,18 @@ impl WholeStreamCommand for FromTsv {
|
||||
"Parse text as .tsv and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_tsv(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_tsv(args)
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (FromTsvArgs { noheaders }, input) = args.process().await?;
|
||||
let args = args.evaluate_once()?;
|
||||
let noheaders = args.has_flag("noheaders");
|
||||
let input = args.input;
|
||||
|
||||
from_delimited_data(noheaders, '\t', "TSV", input, name).await
|
||||
from_delimited_data(noheaders, '\t', "TSV", input, name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
|
||||
|
||||
pub struct FromUrl;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromUrl {
|
||||
fn name(&self) -> &str {
|
||||
"from url"
|
||||
@ -19,17 +18,17 @@ impl WholeStreamCommand for FromUrl {
|
||||
"Parse url-encoded string as a table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_url(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_url(args)
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let concat_string = input.collect_string(tag.clone()).await?;
|
||||
let concat_string = input.collect_string(tag.clone())?;
|
||||
|
||||
let result = serde_urlencoded::from_str::<Vec<(String, String)>>(&concat_string.item);
|
||||
|
||||
@ -41,7 +40,7 @@ async fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
row.insert_untagged(k, UntaggedValue::string(v));
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(row.into_value())))
|
||||
Ok(OutputStream::one(row.into_value()))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"String not compatible with url-encoding",
|
||||
|
@ -1,14 +1,12 @@
|
||||
extern crate ical;
|
||||
use crate::prelude::*;
|
||||
use ical::parser::vcard::component::*;
|
||||
use ical::property::Property;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
|
||||
pub struct FromVcf;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromVcf {
|
||||
fn name(&self) -> &str {
|
||||
"from vcf"
|
||||
@ -22,31 +20,33 @@ impl WholeStreamCommand for FromVcf {
|
||||
"Parse text as .vcf and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_vcf(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_vcf(args)
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_vcf(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn from_vcf(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let input_string = input.collect_string(tag.clone()).await?.item;
|
||||
let input_string = input.collect_string(tag.clone())?.item;
|
||||
let input_bytes = input_string.into_bytes();
|
||||
let cursor = std::io::Cursor::new(input_bytes);
|
||||
let parser = ical::VcardParser::new(cursor);
|
||||
|
||||
let iter = parser.map(move |contact| match contact {
|
||||
Ok(c) => ReturnSuccess::value(contact_to_value(c, tag.clone())),
|
||||
Err(_) => Err(ShellError::labeled_error(
|
||||
Ok(c) => contact_to_value(c, tag.clone()),
|
||||
Err(_) => Value::error(ShellError::labeled_error(
|
||||
"Could not parse as .vcf",
|
||||
"input cannot be parsed as .vcf",
|
||||
tag.clone(),
|
||||
)),
|
||||
});
|
||||
|
||||
Ok(futures::stream::iter(iter).to_output_stream())
|
||||
let collected: Vec<_> = iter.collect();
|
||||
|
||||
Ok(collected.into_iter().to_output_stream())
|
||||
}
|
||||
|
||||
fn contact_to_value(contact: VcardContact, tag: Tag) -> Value {
|
||||
|
@ -3,17 +3,11 @@ use calamine::*;
|
||||
use nu_data::TaggedListBuilder;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
|
||||
use std::io::Cursor;
|
||||
|
||||
pub struct FromXlsx;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromXlsxArgs {
|
||||
noheaders: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromXlsx {
|
||||
fn name(&self) -> &str {
|
||||
"from xlsx"
|
||||
@ -31,21 +25,16 @@ impl WholeStreamCommand for FromXlsx {
|
||||
"Parse binary Excel(.xlsx) data and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_xlsx(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_xlsx(args)
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_xlsx(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_xlsx(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let span = tag.span;
|
||||
let (
|
||||
FromXlsxArgs {
|
||||
noheaders: _noheaders,
|
||||
},
|
||||
input,
|
||||
) = args.process().await?;
|
||||
let value = input.collect_binary(tag.clone()).await?;
|
||||
|
||||
let value = args.input.collect_binary(tag.clone())?;
|
||||
|
||||
let buf: Cursor<Vec<u8>> = Cursor::new(value.item);
|
||||
let mut xls = Xlsx::<_>::new(buf).map_err(|_| {
|
||||
@ -88,7 +77,7 @@ async fn from_xlsx(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(dict.into_value())))
|
||||
Ok(OutputStream::one(dict.into_value()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
|
||||
pub struct FromXml;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromXml {
|
||||
fn name(&self) -> &str {
|
||||
"from xml"
|
||||
@ -19,8 +18,8 @@ impl WholeStreamCommand for FromXml {
|
||||
"Parse text as .xml and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_xml(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_xml(args)
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,12 +94,12 @@ pub fn from_xml_string_to_value(s: String, tag: impl Into<Tag>) -> Result<Value,
|
||||
Ok(from_document_to_value(&parsed, tag))
|
||||
}
|
||||
|
||||
async fn from_xml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn from_xml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let concat_string = input.collect_string(tag.clone()).await?;
|
||||
let concat_string = input.collect_string(tag.clone())?;
|
||||
|
||||
Ok(
|
||||
match from_xml_string_to_value(concat_string.item, tag.clone()) {
|
||||
@ -108,9 +107,8 @@ async fn from_xml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => futures::stream::iter(list.into_iter().map(ReturnSuccess::value))
|
||||
.to_output_stream(),
|
||||
x => OutputStream::one(ReturnSuccess::value(x)),
|
||||
} => list.into_iter().to_output_stream(),
|
||||
x => OutputStream::one(x),
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
|
@ -5,7 +5,6 @@ use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value}
|
||||
|
||||
pub struct FromYaml;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromYaml {
|
||||
fn name(&self) -> &str {
|
||||
"from yaml"
|
||||
@ -19,14 +18,13 @@ impl WholeStreamCommand for FromYaml {
|
||||
"Parse text as .yaml/.yml and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_yaml(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_yaml(args)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FromYml;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromYml {
|
||||
fn name(&self) -> &str {
|
||||
"from yml"
|
||||
@ -40,8 +38,8 @@ impl WholeStreamCommand for FromYml {
|
||||
"Parse text as .yaml/.yml and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_yaml(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_yaml(args)
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,19 +131,19 @@ pub fn from_yaml_string_to_value(s: String, tag: impl Into<Tag>) -> Result<Value
|
||||
convert_yaml_value_to_nu_value(&v, tag)
|
||||
}
|
||||
|
||||
async fn from_yaml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn from_yaml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let concat_string = input.collect_string(tag.clone()).await?;
|
||||
let concat_string = input.collect_string(tag.clone())?;
|
||||
|
||||
match from_yaml_string_to_value(concat_string.item, tag.clone()) {
|
||||
Ok(x) => match x {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => Ok(futures::stream::iter(list).to_output_stream()),
|
||||
} => Ok(list.into_iter().to_output_stream()),
|
||||
x => Ok(OutputStream::one(x)),
|
||||
},
|
||||
Err(_) => Err(ShellError::labeled_error_with_secondary(
|
||||
|
@ -18,7 +18,6 @@ pub struct Arguments {
|
||||
rest: Vec<Value>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"get"
|
||||
@ -35,8 +34,8 @@ impl WholeStreamCommand for Command {
|
||||
"Open given cells as text."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
get(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
get(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -55,29 +54,31 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { mut rest }, mut input) = args.process().await?;
|
||||
pub fn get(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { mut rest }, mut input) = args.process()?;
|
||||
let (column_paths, _) = arguments(&mut rest)?;
|
||||
|
||||
if column_paths.is_empty() {
|
||||
let vec = input.drain_vec().await;
|
||||
let vec = input.drain_vec();
|
||||
|
||||
let descs = nu_protocol::merge_descriptors(&vec);
|
||||
|
||||
Ok(futures::stream::iter(descs.into_iter().map(ReturnSuccess::value)).to_output_stream())
|
||||
Ok(descs
|
||||
.into_iter()
|
||||
.map(ReturnSuccess::value)
|
||||
.to_action_stream())
|
||||
} else {
|
||||
trace!("get {:?}", column_paths);
|
||||
let output_stream = input
|
||||
.map(move |item| {
|
||||
let output = column_paths
|
||||
column_paths
|
||||
.iter()
|
||||
.map(move |path| get_output(&item, path))
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
futures::stream::iter(output)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream();
|
||||
.to_action_stream();
|
||||
Ok(output_stream)
|
||||
}
|
||||
}
|
||||
@ -227,8 +228,8 @@ pub fn get_column_from_row_error(
|
||||
} => {
|
||||
let primary_label = format!("There isn't a column named '{}'", &column);
|
||||
|
||||
if let Some(suggestions) = did_you_mean(&obj_source, column_path_tried.as_string()) {
|
||||
Some(ShellError::labeled_error_with_secondary(
|
||||
did_you_mean(&obj_source, column_path_tried.as_string()).map(|suggestions| {
|
||||
ShellError::labeled_error_with_secondary(
|
||||
"Unknown column",
|
||||
primary_label,
|
||||
column_path_tried.span,
|
||||
@ -238,10 +239,8 @@ pub fn get_column_from_row_error(
|
||||
&obj_source.data_descriptors().join(", ")
|
||||
),
|
||||
column_path_tried.span.since(path_members_span),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
PathMember {
|
||||
unspanned: UnspannedPathMember::Int(idx),
|
||||
|
@ -2,7 +2,8 @@ use crate::prelude::*;
|
||||
use crate::utils::suggestions::suggestions;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::hir::ExternalRedirection;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use nu_value_ext::as_string;
|
||||
|
||||
@ -13,7 +14,6 @@ pub struct Arguments {
|
||||
grouper: Option<Value>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"group-by"
|
||||
@ -31,8 +31,8 @@ impl WholeStreamCommand for Command {
|
||||
"Create a new table grouped."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
group_by(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
group_by(args)
|
||||
}
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
@ -128,12 +128,12 @@ enum Grouper {
|
||||
ByBlock,
|
||||
}
|
||||
|
||||
pub async fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let context = Arc::new(EvaluationContext::from_args(&args));
|
||||
let (Arguments { grouper }, input) = args.process().await?;
|
||||
let (Arguments { grouper }, input) = args.process()?;
|
||||
|
||||
let values: Vec<Value> = input.collect().await;
|
||||
let values: Vec<Value> = input.collect();
|
||||
let mut keys: Vec<Result<String, ShellError>> = vec![];
|
||||
let mut group_strategy = Grouper::ByColumn(None);
|
||||
|
||||
@ -149,10 +149,14 @@ pub async fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let run = block.clone();
|
||||
let context = context.clone();
|
||||
|
||||
match crate::commands::each::process_row(run, context, value.clone()).await {
|
||||
match crate::commands::each::process_row(
|
||||
run,
|
||||
context,
|
||||
value.clone(),
|
||||
ExternalRedirection::Stdout,
|
||||
) {
|
||||
Ok(mut s) => {
|
||||
let collection: Vec<Result<ReturnSuccess, ShellError>> =
|
||||
s.drain_vec().await;
|
||||
let collection: Vec<Value> = s.drain_vec();
|
||||
|
||||
if collection.len() > 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
@ -163,14 +167,12 @@ pub async fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
}
|
||||
|
||||
let value = match collection.get(0) {
|
||||
Some(Ok(return_value)) => {
|
||||
return_value.raw_value().unwrap_or_else(|| {
|
||||
UntaggedValue::string(error_key).into_value(&name)
|
||||
})
|
||||
}
|
||||
Some(Err(_)) | None => {
|
||||
UntaggedValue::string(error_key).into_value(&name)
|
||||
}
|
||||
Some(Value {
|
||||
value: UntaggedValue::Error(_),
|
||||
..
|
||||
})
|
||||
| None => UntaggedValue::string(error_key).into_value(&name),
|
||||
Some(return_value) => return_value.clone(),
|
||||
};
|
||||
|
||||
keys.push(as_string(&value));
|
||||
@ -209,7 +211,7 @@ pub async fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let group_value = match group_strategy {
|
||||
Grouper::ByBlock => {
|
||||
let map = keys.clone();
|
||||
let map = keys;
|
||||
|
||||
let block = Box::new(move |idx: usize, row: &Value| match map.get(idx) {
|
||||
Some(Ok(key)) => Ok(key.clone()),
|
||||
@ -222,7 +224,7 @@ pub async fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Grouper::ByColumn(column_name) => group(&column_name, &values, &name),
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(group_value?)))
|
||||
Ok(OutputStream::one(group_value?))
|
||||
}
|
||||
|
||||
pub fn group(
|
||||
|
@ -13,7 +13,6 @@ pub struct GroupByDateArgs {
|
||||
format: Option<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for GroupByDate {
|
||||
fn name(&self) -> &str {
|
||||
"group-by date"
|
||||
@ -38,8 +37,8 @@ impl WholeStreamCommand for GroupByDate {
|
||||
"creates a table grouped by date."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
group_by_date(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
group_by_date(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -59,7 +58,7 @@ enum GroupByColumn {
|
||||
Name(Option<Tagged<String>>),
|
||||
}
|
||||
|
||||
pub async fn group_by_date(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn group_by_date(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (
|
||||
GroupByDateArgs {
|
||||
@ -67,8 +66,8 @@ pub async fn group_by_date(args: CommandArgs) -> Result<OutputStream, ShellError
|
||||
format,
|
||||
},
|
||||
input,
|
||||
) = args.process().await?;
|
||||
let values: Vec<Value> = input.collect().await;
|
||||
) = args.process()?;
|
||||
let values: Vec<Value> = input.collect();
|
||||
|
||||
if values.is_empty() {
|
||||
Err(ShellError::labeled_error(
|
||||
@ -126,7 +125,7 @@ pub async fn group_by_date(args: CommandArgs) -> Result<OutputStream, ShellError
|
||||
}
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(value_result?)))
|
||||
Ok(ActionStream::one(ReturnSuccess::value(value_result?)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,6 @@ pub enum ActionType {
|
||||
}
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"hash base64"
|
||||
@ -65,8 +64,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"base64 encode or decode a value"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
operate(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
operate(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -96,8 +95,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_tag = &args.call_info.name_tag.clone();
|
||||
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name_tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (
|
||||
Arguments {
|
||||
@ -107,10 +106,10 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
rest,
|
||||
},
|
||||
input,
|
||||
) = args.process().await?;
|
||||
) = args.process()?;
|
||||
|
||||
if encode.item && decode.item {
|
||||
return Ok(OutputStream::one(Err(ShellError::labeled_error(
|
||||
return Ok(ActionStream::one(Err(ShellError::labeled_error(
|
||||
"only one of --decode and --encode flags can be used",
|
||||
"conflicting flags",
|
||||
name_tag,
|
||||
@ -155,7 +154,7 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
ReturnSuccess::value(ret)
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
fn action(
|
||||
|
@ -5,7 +5,6 @@ use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"hash"
|
||||
@ -22,9 +21,9 @@ impl WholeStreamCommand for Command {
|
||||
"Apply hash function."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(&Command, &args.scope)).into_value(Tag::unknown()),
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ pub struct Arguments {
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"hash md5"
|
||||
@ -31,8 +30,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"md5 encode a value"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
operate(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
operate(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -57,8 +56,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { rest }, input) = args.process().await?;
|
||||
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { rest }, input) = args.process()?;
|
||||
|
||||
let column_paths: Vec<_> = rest;
|
||||
|
||||
@ -79,7 +78,7 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
ReturnSuccess::value(ret)
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::prelude::*;
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
@ -8,7 +8,6 @@ use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
||||
|
||||
pub struct Headers;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Headers {
|
||||
fn name(&self) -> &str {
|
||||
"headers"
|
||||
@ -22,8 +21,8 @@ impl WholeStreamCommand for Headers {
|
||||
"Use the first row of the table as column names."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
headers(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
headers(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -42,9 +41,9 @@ impl WholeStreamCommand for Headers {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn headers(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn headers(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let input = args.input;
|
||||
let rows: Vec<Value> = input.collect().await;
|
||||
let rows: Vec<Value> = input.collect();
|
||||
|
||||
if rows.is_empty() {
|
||||
return Err(ShellError::untagged_runtime_error(
|
||||
@ -77,8 +76,10 @@ pub async fn headers(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
)),
|
||||
}?;
|
||||
|
||||
Ok(
|
||||
futures::stream::iter(rows.into_iter().skip(1).map(move |r| {
|
||||
Ok(rows
|
||||
.into_iter()
|
||||
.skip(1)
|
||||
.map(move |r| {
|
||||
//Each row is a dictionary with the headers as keys
|
||||
match &r.value {
|
||||
UntaggedValue::Row(d) => {
|
||||
@ -100,9 +101,8 @@ pub async fn headers(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
r.tag.span,
|
||||
)),
|
||||
}
|
||||
}))
|
||||
.to_output_stream(),
|
||||
)
|
||||
})
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user