mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 15:11:52 +02:00
Compare commits
117 Commits
Author | SHA1 | Date | |
---|---|---|---|
2df8775b48 | |||
e02b4f1443 | |||
194782215f | |||
df17d28c0f | |||
5f43c8f024 | |||
1a18734f9a | |||
4a70c1ff4f | |||
770e5d89f2 | |||
cfac8e84dd | |||
43d90c1745 | |||
38bdb053d2 | |||
95e61773a5 | |||
4e931fa73f | |||
2573441e28 | |||
5770b15270 | |||
6817b472d0 | |||
2b076369e0 | |||
973a8ee8f3 | |||
1159d3365a | |||
152ba32eb7 | |||
153320ef33 | |||
ff236da72c | |||
54326869e4 | |||
f14f4e39c5 | |||
a18b2702ca | |||
93410c470e | |||
5d945ef869 | |||
df07be6a42 | |||
3c32d4947c | |||
2ea5235aea | |||
c096f031ce | |||
ae1d4bdb4c | |||
0adf2accdd | |||
4201f48be5 | |||
b076e375ca | |||
2f1016d44f | |||
ddf9d61346 | |||
f0b7ab5ecc | |||
e4c6336bd4 | |||
66061192f8 | |||
b7bc4c1f80 | |||
a56abb6502 | |||
892a416211 | |||
f45adecd01 | |||
bd015e82dc | |||
cf43b74f26 | |||
18909ec14a | |||
ed243c88d2 | |||
cb7723f423 | |||
9dc88f8a95 | |||
0a439fe52f | |||
a8b65e35ec | |||
bd9e598bf0 | |||
75f8247af1 | |||
e8ec5027ff | |||
ebba89ea31 | |||
8388afc9d9 | |||
b133724b38 | |||
09429d08aa | |||
9b577b8679 | |||
7a595827f1 | |||
332e12ded0 | |||
a508e15efe | |||
a5b6bb6209 | |||
1882a32b83 | |||
798766b4b5 | |||
193c4cc6d5 | |||
422b6ca871 | |||
2b13ac3856 | |||
4c10351579 | |||
dd27aaef1b | |||
6eb4a0e87b | |||
15f3a545f0 | |||
365f76ad19 | |||
df2845a9b4 | |||
8453261211 | |||
1dc8f3300e | |||
10d4edc7af | |||
50cbf91bc5 | |||
d05f9b3b1e | |||
f5fad393d0 | |||
d19a5f4c2f | |||
04451af776 | |||
232aca76a4 | |||
0178b53289 | |||
e05e6b42fe | |||
dd79afb503 | |||
599bb9797d | |||
c355585112 | |||
45f32c9541 | |||
7528094e12 | |||
dcfa135ab9 | |||
e9bb4f25eb | |||
0f7a9bbd31 | |||
73e65df5f6 | |||
a63a5adafa | |||
2eb4f8d28a | |||
d9ae66791a | |||
2c5939dc7d | |||
3150e70fc7 | |||
c9ffd6afc0 | |||
986b427038 | |||
c973850571 | |||
5a725f9651 | |||
79cc725aff | |||
e2cbc4e853 | |||
b5a27f0ccb | |||
bdb12f4bff | |||
c9c29f9e4c | |||
32951f1161 | |||
56f85b3108 | |||
16f85f32a2 | |||
2ae2f2ea9d | |||
4696c9069b | |||
1ffbb66e64 | |||
8dc7b8a7cd | |||
666e6a7b57 |
@ -71,7 +71,7 @@ steps:
|
||||
- bash: RUSTFLAGS="-D warnings" cargo clippy --all -- -D clippy::unwrap_used
|
||||
condition: eq(variables['style'], 'canary')
|
||||
displayName: Check clippy lints
|
||||
- bash: RUSTFLAGS="-D warnings" cargo test --all --no-default-features
|
||||
- bash: RUSTFLAGS="-D warnings" cargo test --all --no-default-features --features=rustyline-support
|
||||
condition: eq(variables['style'], 'minimal')
|
||||
displayName: Run tests
|
||||
- bash: RUSTFLAGS="-D warnings" cargo test --all --features=extra
|
||||
|
6
.cargo/config.toml
Normal file
6
.cargo/config.toml
Normal file
@ -0,0 +1,6 @@
|
||||
# use LLD as linker on Windows because it could be faster
|
||||
# for full and incremental builds compared to default
|
||||
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
#linker = "lld-link.exe"
|
||||
rustflags = ["-C", "link-args=-stack:10000000"]
|
@ -31,6 +31,11 @@ cargo build
|
||||
cargo build --release && cargo run --release
|
||||
```
|
||||
|
||||
- Build and run with extra features:
|
||||
```shell
|
||||
cargo build --release --features=extra && cargo run --release --features=extra
|
||||
```
|
||||
|
||||
- Run Clippy on Nushell:
|
||||
|
||||
```shell
|
||||
@ -60,3 +65,11 @@ cargo build
|
||||
```shell
|
||||
cargo fmt --all
|
||||
```
|
||||
|
||||
### Debugging Tips
|
||||
|
||||
- To view verbose logs when developing, enable the `trace` log level.
|
||||
|
||||
```shell
|
||||
cargo build --release --features=extra && cargo run --release --features=extra -- --loglevel trace
|
||||
```
|
||||
|
804
Cargo.lock
generated
804
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
134
Cargo.toml
134
Cargo.toml
@ -10,7 +10,7 @@ license = "MIT"
|
||||
name = "nu"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
version = "0.19.0"
|
||||
version = "0.21.0"
|
||||
|
||||
[workspace]
|
||||
members = ["crates/*/"]
|
||||
@ -18,51 +18,57 @@ members = ["crates/*/"]
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nu-cli = {version = "0.19.0", path = "./crates/nu-cli"}
|
||||
nu-data = {version = "0.19.0", path = "./crates/nu-data"}
|
||||
nu-errors = {version = "0.19.0", path = "./crates/nu-errors"}
|
||||
nu-parser = {version = "0.19.0", path = "./crates/nu-parser"}
|
||||
nu-plugin = {version = "0.19.0", path = "./crates/nu-plugin"}
|
||||
nu-protocol = {version = "0.19.0", path = "./crates/nu-protocol"}
|
||||
nu-source = {version = "0.19.0", path = "./crates/nu-source"}
|
||||
nu-value-ext = {version = "0.19.0", path = "./crates/nu-value-ext"}
|
||||
nu-cli = {version = "0.21.0", path = "./crates/nu-cli"}
|
||||
nu-data = {version = "0.21.0", path = "./crates/nu-data"}
|
||||
nu-errors = {version = "0.21.0", path = "./crates/nu-errors"}
|
||||
nu-parser = {version = "0.21.0", path = "./crates/nu-parser"}
|
||||
nu-plugin = {version = "0.21.0", path = "./crates/nu-plugin"}
|
||||
nu-protocol = {version = "0.21.0", path = "./crates/nu-protocol"}
|
||||
nu-source = {version = "0.21.0", path = "./crates/nu-source"}
|
||||
nu-value-ext = {version = "0.21.0", path = "./crates/nu-value-ext"}
|
||||
|
||||
nu_plugin_binaryview = {version = "0.19.0", path = "./crates/nu_plugin_binaryview", optional = true}
|
||||
nu_plugin_fetch = {version = "0.19.0", path = "./crates/nu_plugin_fetch", optional = true}
|
||||
nu_plugin_from_bson = {version = "0.19.0", path = "./crates/nu_plugin_from_bson", optional = true}
|
||||
nu_plugin_from_sqlite = {version = "0.19.0", path = "./crates/nu_plugin_from_sqlite", optional = true}
|
||||
nu_plugin_inc = {version = "0.19.0", path = "./crates/nu_plugin_inc", optional = true}
|
||||
nu_plugin_match = {version = "0.19.0", path = "./crates/nu_plugin_match", optional = true}
|
||||
nu_plugin_post = {version = "0.19.0", path = "./crates/nu_plugin_post", optional = true}
|
||||
nu_plugin_ps = {version = "0.19.0", path = "./crates/nu_plugin_ps", optional = true}
|
||||
nu_plugin_s3 = {version = "0.19.0", path = "./crates/nu_plugin_s3", optional = true}
|
||||
nu_plugin_start = {version = "0.19.0", path = "./crates/nu_plugin_start", optional = true}
|
||||
nu_plugin_sys = {version = "0.19.0", path = "./crates/nu_plugin_sys", optional = true}
|
||||
nu_plugin_textview = {version = "0.19.0", path = "./crates/nu_plugin_textview", optional = true}
|
||||
nu_plugin_to_bson = {version = "0.19.0", path = "./crates/nu_plugin_to_bson", optional = true}
|
||||
nu_plugin_to_sqlite = {version = "0.19.0", path = "./crates/nu_plugin_to_sqlite", optional = true}
|
||||
nu_plugin_tree = {version = "0.19.0", path = "./crates/nu_plugin_tree", optional = true}
|
||||
nu_plugin_binaryview = {version = "0.21.0", path = "./crates/nu_plugin_binaryview", optional = true}
|
||||
nu_plugin_chart = {version = "0.21.0", path = "./crates/nu_plugin_chart", optional = true}
|
||||
nu_plugin_fetch = {version = "0.21.0", path = "./crates/nu_plugin_fetch", optional = true}
|
||||
nu_plugin_from_bson = {version = "0.21.0", path = "./crates/nu_plugin_from_bson", optional = true}
|
||||
nu_plugin_from_sqlite = {version = "0.21.0", path = "./crates/nu_plugin_from_sqlite", optional = true}
|
||||
nu_plugin_inc = {version = "0.21.0", path = "./crates/nu_plugin_inc", optional = true}
|
||||
nu_plugin_match = {version = "0.21.0", path = "./crates/nu_plugin_match", optional = true}
|
||||
nu_plugin_post = {version = "0.21.0", path = "./crates/nu_plugin_post", optional = true}
|
||||
nu_plugin_ps = {version = "0.21.0", path = "./crates/nu_plugin_ps", optional = true}
|
||||
nu_plugin_s3 = {version = "0.21.0", path = "./crates/nu_plugin_s3", optional = true}
|
||||
nu_plugin_start = {version = "0.21.0", path = "./crates/nu_plugin_start", optional = true}
|
||||
nu_plugin_sys = {version = "0.21.0", path = "./crates/nu_plugin_sys", optional = true}
|
||||
nu_plugin_textview = {version = "0.21.0", path = "./crates/nu_plugin_textview", optional = true}
|
||||
nu_plugin_to_bson = {version = "0.21.0", path = "./crates/nu_plugin_to_bson", optional = true}
|
||||
nu_plugin_to_sqlite = {version = "0.21.0", path = "./crates/nu_plugin_to_sqlite", optional = true}
|
||||
nu_plugin_tree = {version = "0.21.0", path = "./crates/nu_plugin_tree", optional = true}
|
||||
nu_plugin_xpath = {version = "0.21.0", path = "./crates/nu_plugin_xpath", optional = true}
|
||||
|
||||
crossterm = {version = "0.17.5", optional = true}
|
||||
semver = {version = "0.10.0", optional = true}
|
||||
url = {version = "2.1.1", optional = true}
|
||||
|
||||
clap = "2.33.1"
|
||||
ctrlc = "3.1.4"
|
||||
dunce = "1.0.1"
|
||||
futures = {version = "0.3", features = ["compat", "io-compat"]}
|
||||
log = "0.4.8"
|
||||
# Required to bootstrap the main binary
|
||||
clap = "2.33.3"
|
||||
ctrlc = {version = "3.1.6", optional = true}
|
||||
futures = {version = "0.3.5", features = ["compat", "io-compat"]}
|
||||
log = "0.4.11"
|
||||
pretty_env_logger = "0.4.0"
|
||||
quick-xml = "0.18.1"
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = {version = "0.19.0", path = "./crates/nu-test-support"}
|
||||
dunce = "1.0.1"
|
||||
nu-test-support = {version = "0.21.0", path = "./crates/nu-test-support"}
|
||||
|
||||
[build-dependencies]
|
||||
serde = {version = "1.0.110", features = ["derive"]}
|
||||
toml = "0.5.6"
|
||||
|
||||
[features]
|
||||
ctrlc-support = ["nu-cli/ctrlc"]
|
||||
directories-support = ["nu-cli/directories", "nu-cli/dirs", "nu-data/directories", "nu-data/dirs"]
|
||||
git-support = ["nu-cli/git2"]
|
||||
ptree-support = ["nu-cli/ptree"]
|
||||
rich-benchmark = ["nu-cli/rich-benchmark"]
|
||||
rustyline-support = ["nu-cli/rustyline-support"]
|
||||
term-support = ["nu-cli/term"]
|
||||
uuid-support = ["nu-cli/uuid_crate"]
|
||||
which-support = ["nu-cli/ichwh", "nu-cli/which"]
|
||||
|
||||
default = [
|
||||
"sys",
|
||||
"ps",
|
||||
@ -75,40 +81,39 @@ default = [
|
||||
"ptree-support",
|
||||
"term-support",
|
||||
"uuid-support",
|
||||
"rustyline-support",
|
||||
"match",
|
||||
"post",
|
||||
"fetch",
|
||||
"rich-benchmark",
|
||||
]
|
||||
extra = ["default", "binaryview", "tree", "clipboard-cli", "trash-support", "start", "bson", "sqlite", "s3"]
|
||||
extra = ["default", "binaryview", "tree", "clipboard-cli", "trash-support", "start", "bson", "sqlite", "s3", "chart", "xpath"]
|
||||
stable = ["default"]
|
||||
|
||||
# Default
|
||||
inc = ["semver", "nu_plugin_inc"]
|
||||
ps = ["nu_plugin_ps"]
|
||||
sys = ["nu_plugin_sys"]
|
||||
textview = ["crossterm", "url", "nu_plugin_textview"]
|
||||
wasi = ["inc", "match", "directories-support", "ptree-support", "match", "tree", "rustyline-support"]
|
||||
|
||||
# Stable
|
||||
binaryview = ["nu_plugin_binaryview"]
|
||||
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
|
||||
trace = ["nu-parser/trace"]
|
||||
|
||||
# Stable (Default)
|
||||
fetch = ["nu_plugin_fetch"]
|
||||
inc = ["nu_plugin_inc"]
|
||||
match = ["nu_plugin_match"]
|
||||
post = ["nu_plugin_post"]
|
||||
ps = ["nu_plugin_ps"]
|
||||
sys = ["nu_plugin_sys"]
|
||||
textview = ["nu_plugin_textview"]
|
||||
|
||||
# Extra
|
||||
binaryview = ["nu_plugin_binaryview"]
|
||||
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
|
||||
chart = ["nu_plugin_chart"]
|
||||
clipboard-cli = ["nu-cli/clipboard-cli"]
|
||||
s3 = ["nu_plugin_s3"]
|
||||
sqlite = ["nu_plugin_from_sqlite", "nu_plugin_to_sqlite"]
|
||||
start = ["nu_plugin_start"]
|
||||
trace = ["nu-parser/trace"]
|
||||
tree = ["nu_plugin_tree"]
|
||||
|
||||
clipboard-cli = ["nu-cli/clipboard-cli"]
|
||||
ctrlc-support = ["nu-cli/ctrlc"]
|
||||
directories-support = ["nu-cli/directories", "nu-cli/dirs", "nu-data/directories", "nu-data/dirs"]
|
||||
git-support = ["nu-cli/git2"]
|
||||
ptree-support = ["nu-cli/ptree"]
|
||||
term-support = ["nu-cli/term"]
|
||||
trash-support = ["nu-cli/trash-support"]
|
||||
uuid-support = ["nu-cli/uuid_crate"]
|
||||
which-support = ["nu-cli/ichwh", "nu-cli/which"]
|
||||
tree = ["nu_plugin_tree"]
|
||||
xpath = ["nu_plugin_xpath"]
|
||||
|
||||
# Core plugins that ship with `cargo install nu` by default
|
||||
# Currently, Cargo limits us to installing only one binary
|
||||
@ -170,6 +175,21 @@ name = "nu_plugin_extra_s3"
|
||||
path = "src/plugins/nu_plugin_extra_s3.rs"
|
||||
required-features = ["s3"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_extra_chart_bar"
|
||||
path = "src/plugins/nu_plugin_extra_chart_bar.rs"
|
||||
required-features = ["chart"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_extra_chart_line"
|
||||
path = "src/plugins/nu_plugin_extra_chart_line.rs"
|
||||
required-features = ["chart"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_extra_xpath"
|
||||
path = "src/plugins/nu_plugin_extra_xpath.rs"
|
||||
required-features = ["xpath"]
|
||||
|
||||
# Main nu binary
|
||||
[[bin]]
|
||||
name = "nu"
|
||||
|
@ -4,103 +4,103 @@ description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-cli"
|
||||
version = "0.19.0"
|
||||
version = "0.21.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-data = {version = "0.19.0", path = "../nu-data"}
|
||||
nu-errors = {version = "0.19.0", path = "../nu-errors"}
|
||||
nu-parser = {version = "0.19.0", path = "../nu-parser"}
|
||||
nu-plugin = {version = "0.19.0", path = "../nu-plugin"}
|
||||
nu-protocol = {version = "0.19.0", path = "../nu-protocol"}
|
||||
nu-source = {version = "0.19.0", path = "../nu-source"}
|
||||
nu-table = {version = "0.19.0", path = "../nu-table"}
|
||||
nu-test-support = {version = "0.19.0", path = "../nu-test-support"}
|
||||
nu-value-ext = {version = "0.19.0", path = "../nu-value-ext"}
|
||||
nu-data = {version = "0.21.0", path = "../nu-data"}
|
||||
nu-errors = {version = "0.21.0", path = "../nu-errors"}
|
||||
nu-parser = {version = "0.21.0", path = "../nu-parser"}
|
||||
nu-plugin = {version = "0.21.0", path = "../nu-plugin"}
|
||||
nu-protocol = {version = "0.21.0", path = "../nu-protocol"}
|
||||
nu-source = {version = "0.21.0", path = "../nu-source"}
|
||||
nu-table = {version = "0.21.0", path = "../nu-table"}
|
||||
nu-test-support = {version = "0.21.0", path = "../nu-test-support"}
|
||||
nu-value-ext = {version = "0.21.0", path = "../nu-value-ext"}
|
||||
|
||||
ansi_term = "0.12.1"
|
||||
app_dirs = {version = "2", package = "app_dirs2"}
|
||||
async-recursion = "0.3.1"
|
||||
async-trait = "0.1.36"
|
||||
async-trait = "0.1.40"
|
||||
base64 = "0.12.3"
|
||||
bigdecimal = {version = "0.1.2", features = ["serde"]}
|
||||
byte-unit = "3.1.3"
|
||||
bytes = "0.5.5"
|
||||
calamine = "0.16"
|
||||
chrono = {version = "0.4.11", features = ["serde"]}
|
||||
clap = "2.33.1"
|
||||
bigdecimal = {version = "0.2.0", features = ["serde"]}
|
||||
byte-unit = "4.0.9"
|
||||
bytes = "0.5.6"
|
||||
calamine = "0.16.1"
|
||||
chrono = {version = "0.4.15", features = ["serde"]}
|
||||
clap = "2.33.3"
|
||||
codespan-reporting = "0.9.5"
|
||||
csv = "1.1"
|
||||
ctrlc = {version = "3.1.4", optional = true}
|
||||
csv = "1.1.3"
|
||||
ctrlc = {version = "3.1.6", optional = true}
|
||||
derive-new = "0.5.8"
|
||||
directories = {version = "2.0.2", optional = true}
|
||||
dirs = {version = "2.0.2", optional = true}
|
||||
dtparse = "1.1.0"
|
||||
directories = {version = "3.0.1", optional = true}
|
||||
dirs = {version = "3.0.1", optional = true}
|
||||
dtparse = "1.2.0"
|
||||
dunce = "1.0.1"
|
||||
eml-parser = "0.1.0"
|
||||
filesize = "0.2.0"
|
||||
futures = {version = "0.3", features = ["compat", "io-compat"]}
|
||||
fs_extra = "1.2.0"
|
||||
futures = {version = "0.3.5", features = ["compat", "io-compat"]}
|
||||
futures-util = "0.3.5"
|
||||
futures_codec = "0.4"
|
||||
futures_codec = "0.4.1"
|
||||
getset = "0.1.1"
|
||||
git2 = {version = "0.13.6", default_features = false, optional = true}
|
||||
git2 = {version = "0.13.11", default_features = false, optional = true}
|
||||
glob = "0.3.0"
|
||||
hex = "0.4"
|
||||
heim = {version = "0.1.0-beta.3", optional = true}
|
||||
htmlescape = "0.3.1"
|
||||
ical = "0.6.*"
|
||||
ical = "0.6.0"
|
||||
ichwh = {version = "0.3.4", optional = true}
|
||||
indexmap = {version = "1.4.0", features = ["serde-1"]}
|
||||
indexmap = {version = "1.6.0", features = ["serde-1"]}
|
||||
itertools = "0.9.0"
|
||||
log = "0.4.8"
|
||||
meval = "0.2"
|
||||
natural = "0.5.0"
|
||||
num-bigint = {version = "0.2.6", features = ["serde"]}
|
||||
num-format = {version = "0.4", features = ["with-num-bigint"]}
|
||||
num-traits = "0.2.11"
|
||||
log = "0.4.11"
|
||||
meval = "0.2.0"
|
||||
num-bigint = {version = "0.3.0", features = ["serde"]}
|
||||
num-format = {version = "0.4.0", features = ["with-num-bigint"]}
|
||||
num-traits = "0.2.12"
|
||||
parking_lot = "0.11.0"
|
||||
pin-utils = "0.1.0"
|
||||
pretty-hex = "0.1.1"
|
||||
pretty_env_logger = "0.4.0"
|
||||
ptree = {version = "0.2", optional = true}
|
||||
pretty-hex = "0.2.0"
|
||||
ptree = {version = "0.3.0", optional = true}
|
||||
query_interface = "0.3.5"
|
||||
rand = "0.7"
|
||||
regex = "1"
|
||||
quick-xml = "0.18.1"
|
||||
rand = "0.7.3"
|
||||
regex = "1.3.9"
|
||||
roxmltree = "0.13.0"
|
||||
rust-embed = "5.6.0"
|
||||
rustyline = "6.2.0"
|
||||
serde = {version = "1.0.114", features = ["derive"]}
|
||||
rustyline = {version = "6.3.0", optional = true}
|
||||
serde = {version = "1.0.115", features = ["derive"]}
|
||||
serde-hjson = "0.9.1"
|
||||
serde_bytes = "0.11.5"
|
||||
serde_ini = "0.2.0"
|
||||
serde_json = "1.0.55"
|
||||
serde_urlencoded = "0.6.1"
|
||||
serde_yaml = "0.8"
|
||||
serde_json = "1.0.57"
|
||||
serde_urlencoded = "0.7.0"
|
||||
serde_yaml = "0.8.13"
|
||||
sha2 = "0.9.1"
|
||||
shellexpand = "2.0.0"
|
||||
strip-ansi-escapes = "0.1.0"
|
||||
sxd-document = "0.3.2"
|
||||
sxd-xpath = "0.4.2"
|
||||
tempfile = "3.1.0"
|
||||
term = {version = "0.5.2", optional = true}
|
||||
term = {version = "0.6.1", optional = true}
|
||||
term_size = "0.3.2"
|
||||
termcolor = "1.1.0"
|
||||
toml = "0.5.6"
|
||||
typetag = "0.1.5"
|
||||
umask = "1.0.0"
|
||||
unicode-xid = "0.2.1"
|
||||
unicode-segmentation = "1.6.0"
|
||||
uom = {version = "0.28.0", features = ["f64", "try-from"]}
|
||||
uuid_crate = {package = "uuid", version = "0.8.1", features = ["v4"], optional = true}
|
||||
which = {version = "4.0.2", optional = true}
|
||||
zip = {version = "0.5.6", optional = true}
|
||||
zip = {version = "0.5.7", optional = true}
|
||||
|
||||
clipboard = {version = "0.5", optional = true}
|
||||
encoding_rs = "0.8.23"
|
||||
quick-xml = "0.18.1"
|
||||
rayon = "1.3.1"
|
||||
trash = {version = "1.0.1", optional = true}
|
||||
url = {version = "2.1.1"}
|
||||
Inflector = "0.11"
|
||||
clipboard = {version = "0.5.0", optional = true}
|
||||
encoding_rs = "0.8.24"
|
||||
rayon = "1.4.0"
|
||||
trash = {version = "1.1.1", optional = true}
|
||||
url = "2.1.1"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
umask = "1.0.0"
|
||||
users = "0.10.0"
|
||||
|
||||
# TODO this will be possible with new dependency resolver
|
||||
@ -112,17 +112,18 @@ users = "0.10.0"
|
||||
[dependencies.rusqlite]
|
||||
features = ["bundled", "blob"]
|
||||
optional = true
|
||||
version = "0.23.1"
|
||||
version = "0.24.0"
|
||||
|
||||
[build-dependencies]
|
||||
git2 = {version = "0.13", optional = true}
|
||||
|
||||
git2 = {version = "0.13.11", optional = true}
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = "0.9"
|
||||
quickcheck_macros = "0.9"
|
||||
quickcheck = "0.9.2"
|
||||
quickcheck_macros = "0.9.1"
|
||||
|
||||
[features]
|
||||
clipboard-cli = ["clipboard"]
|
||||
rich-benchmark = ["heim"]
|
||||
rustyline-support = ["rustyline"]
|
||||
stable = []
|
||||
trash-support = ["trash"]
|
||||
|
File diff suppressed because it is too large
Load Diff
64
crates/nu-cli/src/command_registry.rs
Normal file
64
crates/nu-cli/src/command_registry.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use crate::commands::Command;
|
||||
use indexmap::IndexMap;
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::SignatureRegistry;
|
||||
use nu_protocol::Signature;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CommandRegistry {
|
||||
registry: Arc<Mutex<IndexMap<String, Command>>>,
|
||||
}
|
||||
|
||||
impl SignatureRegistry for CommandRegistry {
|
||||
fn has(&self, name: &str) -> bool {
|
||||
let registry = self.registry.lock();
|
||||
registry.contains_key(name)
|
||||
}
|
||||
fn get(&self, name: &str) -> Option<Signature> {
|
||||
let registry = self.registry.lock();
|
||||
registry.get(name).map(|command| command.signature())
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn SignatureRegistry> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRegistry {
|
||||
pub fn new() -> CommandRegistry {
|
||||
CommandRegistry {
|
||||
registry: Arc::new(Mutex::new(IndexMap::default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandRegistry {
|
||||
pub fn get_command(&self, name: &str) -> Option<Command> {
|
||||
let registry = self.registry.lock();
|
||||
|
||||
registry.get(name).cloned()
|
||||
}
|
||||
|
||||
pub fn expect_command(&self, name: &str) -> Result<Command, ShellError> {
|
||||
self.get_command(name).ok_or_else(|| {
|
||||
ShellError::untagged_runtime_error(format!("Could not load command: {}", name))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has(&self, name: &str) -> bool {
|
||||
let registry = self.registry.lock();
|
||||
|
||||
registry.contains_key(name)
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, name: impl Into<String>, command: Command) {
|
||||
let mut registry = self.registry.lock();
|
||||
registry.insert(name.into(), command);
|
||||
}
|
||||
|
||||
pub fn names(&self) -> Vec<String> {
|
||||
let registry = self.registry.lock();
|
||||
registry.keys().cloned().collect()
|
||||
}
|
||||
}
|
@ -17,8 +17,9 @@ pub(crate) mod build_string;
|
||||
pub(crate) mod cal;
|
||||
pub(crate) mod cd;
|
||||
pub(crate) mod char_;
|
||||
pub(crate) mod chart;
|
||||
pub(crate) mod classified;
|
||||
#[cfg(feature = "clipboard")]
|
||||
#[cfg(feature = "clipboard-cli")]
|
||||
pub(crate) mod clip;
|
||||
pub(crate) mod command;
|
||||
pub(crate) mod compact;
|
||||
@ -29,13 +30,16 @@ pub(crate) mod cp;
|
||||
pub(crate) mod date;
|
||||
pub(crate) mod debug;
|
||||
pub(crate) mod default;
|
||||
pub(crate) mod describe;
|
||||
pub(crate) mod do_;
|
||||
pub(crate) mod drop;
|
||||
pub(crate) mod du;
|
||||
pub(crate) mod each;
|
||||
pub(crate) mod echo;
|
||||
pub(crate) mod empty;
|
||||
pub(crate) mod enter;
|
||||
pub(crate) mod every;
|
||||
pub(crate) mod exec;
|
||||
pub(crate) mod exit;
|
||||
pub(crate) mod first;
|
||||
pub(crate) mod format;
|
||||
@ -64,7 +68,6 @@ pub(crate) mod history;
|
||||
pub(crate) mod if_;
|
||||
pub(crate) mod insert;
|
||||
pub(crate) mod into_int;
|
||||
pub(crate) mod is_empty;
|
||||
pub(crate) mod keep;
|
||||
pub(crate) mod last;
|
||||
pub(crate) mod lines;
|
||||
@ -75,6 +78,7 @@ pub(crate) mod mkdir;
|
||||
pub(crate) mod move_;
|
||||
pub(crate) mod next;
|
||||
pub(crate) mod nth;
|
||||
pub(crate) mod nu;
|
||||
pub(crate) mod open;
|
||||
pub(crate) mod parse;
|
||||
pub(crate) mod path;
|
||||
@ -114,12 +118,10 @@ pub(crate) mod to_tsv;
|
||||
pub(crate) mod to_url;
|
||||
pub(crate) mod to_xml;
|
||||
pub(crate) mod to_yaml;
|
||||
pub(crate) mod trim;
|
||||
pub(crate) mod uniq;
|
||||
pub(crate) mod update;
|
||||
pub(crate) mod url_;
|
||||
pub(crate) mod version;
|
||||
pub(crate) mod what;
|
||||
pub(crate) mod where_;
|
||||
pub(crate) mod which_;
|
||||
pub(crate) mod with_env;
|
||||
@ -133,7 +135,7 @@ pub(crate) use command::{
|
||||
|
||||
pub(crate) use alias::Alias;
|
||||
pub(crate) use ansi::Ansi;
|
||||
pub(crate) use append::Append;
|
||||
pub(crate) use append::Command as Append;
|
||||
pub(crate) use autoenv::Autoenv;
|
||||
pub(crate) use autoenv_trust::AutoenvTrust;
|
||||
pub(crate) use autoenv_untrust::AutoenvUnTrust;
|
||||
@ -141,6 +143,7 @@ pub(crate) use benchmark::Benchmark;
|
||||
pub(crate) use build_string::BuildString;
|
||||
pub(crate) use cal::Cal;
|
||||
pub(crate) use char_::Char;
|
||||
pub(crate) use chart::Chart;
|
||||
pub(crate) use compact::Compact;
|
||||
pub(crate) use config::{
|
||||
Config, ConfigClear, ConfigGet, ConfigLoad, ConfigPath, ConfigRemove, ConfigSet, ConfigSetInto,
|
||||
@ -150,14 +153,18 @@ pub(crate) use cp::Cpy;
|
||||
pub(crate) use date::{Date, DateFormat, DateNow, DateUTC};
|
||||
pub(crate) use debug::Debug;
|
||||
pub(crate) use default::Default;
|
||||
pub(crate) use describe::Describe;
|
||||
pub(crate) use do_::Do;
|
||||
pub(crate) use drop::Drop;
|
||||
pub(crate) use du::Du;
|
||||
pub(crate) use each::Each;
|
||||
pub(crate) use each::EachGroup;
|
||||
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 is_empty::IsEmpty;
|
||||
pub(crate) use update::Update;
|
||||
pub(crate) use nu::NuPlugin;
|
||||
pub(crate) use update::Command as Update;
|
||||
pub(crate) mod kill;
|
||||
pub(crate) use kill::Kill;
|
||||
pub(crate) mod clear;
|
||||
@ -165,6 +172,7 @@ pub(crate) use clear::Clear;
|
||||
pub(crate) mod touch;
|
||||
pub(crate) use enter::Enter;
|
||||
pub(crate) use every::Every;
|
||||
pub(crate) use exec::Exec;
|
||||
pub(crate) use exit::Exit;
|
||||
pub(crate) use first::First;
|
||||
pub(crate) use format::Format;
|
||||
@ -185,13 +193,13 @@ pub(crate) use from_xml::FromXML;
|
||||
pub(crate) use from_yaml::FromYAML;
|
||||
pub(crate) use from_yaml::FromYML;
|
||||
pub(crate) use get::Get;
|
||||
pub(crate) use group_by::GroupBy;
|
||||
pub(crate) use group_by::Command as GroupBy;
|
||||
pub(crate) use group_by_date::GroupByDate;
|
||||
pub(crate) use headers::Headers;
|
||||
pub(crate) use help::Help;
|
||||
pub(crate) use histogram::Histogram;
|
||||
pub(crate) use history::History;
|
||||
pub(crate) use insert::Insert;
|
||||
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;
|
||||
@ -218,7 +226,7 @@ pub(crate) use prev::Previous;
|
||||
pub(crate) use pwd::Pwd;
|
||||
#[cfg(feature = "uuid_crate")]
|
||||
pub(crate) use random::RandomUUID;
|
||||
pub(crate) use random::{Random, RandomBool, RandomDice};
|
||||
pub(crate) use random::{Random, RandomBool, RandomDice, RandomInteger};
|
||||
pub(crate) use range::Range;
|
||||
pub(crate) use reduce::Reduce;
|
||||
pub(crate) use reject::Reject;
|
||||
@ -238,9 +246,9 @@ pub(crate) use split::{Split, SplitChars, SplitColumn, SplitRow};
|
||||
pub(crate) use split_by::SplitBy;
|
||||
pub(crate) use str_::{
|
||||
Str, StrCamelCase, StrCapitalize, StrCollect, StrContains, StrDowncase, StrEndsWith,
|
||||
StrFindReplace, StrFrom, StrIndexOf, StrKebabCase, StrLength, StrPascalCase, StrReverse,
|
||||
StrScreamingSnakeCase, StrSet, StrSnakeCase, StrStartsWith, StrSubstring, StrToDatetime,
|
||||
StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
|
||||
StrFindReplace, StrFrom, StrIndexOf, StrKebabCase, StrLPad, StrLength, StrPascalCase, StrRPad,
|
||||
StrReverse, StrScreamingSnakeCase, StrSet, StrSnakeCase, StrStartsWith, StrSubstring,
|
||||
StrToDatetime, StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
|
||||
};
|
||||
pub(crate) use table::Table;
|
||||
pub(crate) use tags::Tags;
|
||||
@ -255,12 +263,46 @@ pub(crate) use to_url::ToURL;
|
||||
pub(crate) use to_xml::ToXML;
|
||||
pub(crate) use to_yaml::ToYAML;
|
||||
pub(crate) use touch::Touch;
|
||||
pub(crate) use trim::Trim;
|
||||
pub(crate) use uniq::Uniq;
|
||||
pub(crate) use url_::{UrlCommand, UrlHost, UrlPath, UrlQuery, UrlScheme};
|
||||
pub(crate) use version::Version;
|
||||
pub(crate) use what::What;
|
||||
pub(crate) use where_::Where;
|
||||
pub(crate) use which_::Which;
|
||||
pub(crate) use with_env::WithEnv;
|
||||
pub(crate) use wrap::Wrap;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::commands::whole_stream_command;
|
||||
use crate::examples::{test_anchors, test_examples};
|
||||
use nu_errors::ShellError;
|
||||
|
||||
fn commands() -> Vec<Command> {
|
||||
vec![
|
||||
whole_stream_command(Append),
|
||||
whole_stream_command(GroupBy),
|
||||
whole_stream_command(Insert),
|
||||
whole_stream_command(Update),
|
||||
whole_stream_command(Empty),
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
for cmd in commands() {
|
||||
test_examples(cmd)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tracks_metadata() -> Result<(), ShellError> {
|
||||
for cmd in commands() {
|
||||
test_anchors(cmd)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_data::config;
|
||||
use nu_errors::ShellError;
|
||||
@ -340,11 +340,12 @@ fn find_block_shapes(block: &Block, registry: &CommandRegistry) -> Result<ShapeM
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Alias;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Alias {})
|
||||
Ok(test_examples(Alias {})?)
|
||||
}
|
||||
}
|
||||
|
@ -135,11 +135,12 @@ pub fn str_to_ansi_color(s: String) -> Option<String> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Ansi;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Ansi {})
|
||||
Ok(test_examples(Ansi {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AppendArgs {
|
||||
struct Arguments {
|
||||
row: Value,
|
||||
}
|
||||
|
||||
pub struct Append;
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Append {
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"append"
|
||||
}
|
||||
@ -34,11 +34,18 @@ impl WholeStreamCommand for Append {
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let (AppendArgs { row }, input) = args.process(registry).await?;
|
||||
let (Arguments { mut row }, input) = args.process(registry).await?;
|
||||
|
||||
let eos = futures::stream::iter(vec![row]);
|
||||
let input: Vec<Value> = input.collect().await;
|
||||
|
||||
Ok(input.chain(eos).to_output_stream())
|
||||
if let Some(first) = input.get(0) {
|
||||
row.tag = first.tag();
|
||||
}
|
||||
|
||||
Ok(
|
||||
futures::stream::iter(input.into_iter().chain(vec![row]).map(ReturnSuccess::value))
|
||||
.to_output_stream(),
|
||||
)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -54,15 +61,3 @@ impl WholeStreamCommand for Append {
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Append;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Append {})
|
||||
}
|
||||
}
|
||||
|
@ -318,7 +318,7 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
|
||||
external_redirection: ExternalRedirection::Stdout,
|
||||
},
|
||||
name_tag: context.name.clone(),
|
||||
scope: Scope::new(),
|
||||
scope: Scope::create(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -326,11 +326,12 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Command;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Command {})
|
||||
Ok(test_examples(Command {})?)
|
||||
}
|
||||
}
|
||||
|
@ -10,25 +10,16 @@ pub enum AutoPivotMode {
|
||||
|
||||
impl AutoPivotMode {
|
||||
pub fn is_auto(&self) -> bool {
|
||||
match &self {
|
||||
AutoPivotMode::Auto => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, AutoPivotMode::Auto)
|
||||
}
|
||||
|
||||
pub fn is_always(&self) -> bool {
|
||||
match &self {
|
||||
AutoPivotMode::Always => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, AutoPivotMode::Always)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn is_never(&self) -> bool {
|
||||
match &self {
|
||||
AutoPivotMode::Never => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, AutoPivotMode::Never)
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,7 +28,7 @@ pub trait ConfigExtensions: Debug + Send {
|
||||
}
|
||||
|
||||
pub fn pivot_mode(config: &NuConfig) -> AutoPivotMode {
|
||||
let vars = config.vars.lock();
|
||||
let vars = &config.vars;
|
||||
|
||||
if let Some(mode) = vars.get("pivot_mode") {
|
||||
let mode = match mode.as_string() {
|
||||
|
@ -1,18 +1,26 @@
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
#[cfg(feature = "rich-benchmark")]
|
||||
use heim::cpu::time;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::Block, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
hir::{Block, ClassifiedCommand, Commands, InternalCommand},
|
||||
Dictionary, Scope, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use chrono::prelude::*;
|
||||
use rand::{
|
||||
distributions::Alphanumeric,
|
||||
prelude::{thread_rng, Rng},
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
pub struct Benchmark;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct BenchmarkArgs {
|
||||
block: Block,
|
||||
passthrough: Option<Block>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -22,15 +30,22 @@ impl WholeStreamCommand for Benchmark {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("benchmark").required(
|
||||
"block",
|
||||
SyntaxShape::Block,
|
||||
"the block to run and benchmark",
|
||||
)
|
||||
Signature::build("benchmark")
|
||||
.required(
|
||||
"block",
|
||||
SyntaxShape::Block,
|
||||
"the block to run and benchmark",
|
||||
)
|
||||
.named(
|
||||
"passthrough",
|
||||
SyntaxShape::Block,
|
||||
"Display the benchmark results and pass through the block's output",
|
||||
Some('p'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Runs a block and return the time it took to do execute it. Eg) benchmark { echo $nu.env.NAME }"
|
||||
"Runs a block and returns the time it took to execute it"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
@ -40,6 +55,21 @@ impl WholeStreamCommand for Benchmark {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
benchmark(args, registry).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Benchmarks a command within a block",
|
||||
example: "benchmark { sleep 500ms }",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Benchmarks a command within a block and passes its output through",
|
||||
example: "echo 45 | benchmark { sleep 500ms } --passthrough {}",
|
||||
result: Some(vec![UntaggedValue::int(45).into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
async fn benchmark(
|
||||
@ -48,31 +78,135 @@ async fn benchmark(
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
|
||||
let mut context = Context::from_raw(&raw_args, ®istry);
|
||||
let tag = raw_args.call_info.args.span;
|
||||
let mut context = EvaluationContext::from_raw(&raw_args, ®istry);
|
||||
let scope = raw_args.call_info.scope.clone();
|
||||
let (BenchmarkArgs { block }, input) = raw_args.process(®istry).await?;
|
||||
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process(®istry).await?;
|
||||
|
||||
let start_time: chrono::DateTime<_> = Utc::now();
|
||||
let env = scope.env();
|
||||
let name = generate_free_name(&env);
|
||||
let mut env = IndexMap::new();
|
||||
env.insert(name, generate_random_env_value());
|
||||
let scope = Scope::append_env(scope, env);
|
||||
|
||||
let result = run_block(
|
||||
&block,
|
||||
&mut context,
|
||||
input,
|
||||
&scope.it,
|
||||
&scope.vars,
|
||||
&scope.env,
|
||||
)
|
||||
.await;
|
||||
let start_time = Instant::now();
|
||||
|
||||
let _ = result?.drain_vec().await;
|
||||
let run_duration: chrono::Duration = Utc::now().signed_duration_since(start_time);
|
||||
#[cfg(feature = "rich-benchmark")]
|
||||
let start = time().await;
|
||||
|
||||
let result = run_block(&block, &mut context, input, scope.clone()).await;
|
||||
let output = result?.into_vec().await;
|
||||
|
||||
#[cfg(feature = "rich-benchmark")]
|
||||
let end = time().await;
|
||||
|
||||
let end_time = Instant::now();
|
||||
context.clear_errors();
|
||||
|
||||
let output = Ok(ReturnSuccess::Value(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::from(run_duration)),
|
||||
tag: Tag::from(block.span),
|
||||
}));
|
||||
// return basic runtime
|
||||
#[cfg(not(feature = "rich-benchmark"))]
|
||||
{
|
||||
let mut indexmap = IndexMap::with_capacity(1);
|
||||
|
||||
Ok(OutputStream::from(vec![output]))
|
||||
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, scope).await
|
||||
}
|
||||
// return advanced stats
|
||||
#[cfg(feature = "rich-benchmark")]
|
||||
if let (Ok(start), Ok(end)) = (start, end) {
|
||||
let mut indexmap = IndexMap::with_capacity(4);
|
||||
|
||||
let real_time = into_big_int(end_time - start_time);
|
||||
indexmap.insert("real time".to_string(), real_time);
|
||||
|
||||
let user_time = into_big_int(end.user() - start.user());
|
||||
indexmap.insert("user time".to_string(), user_time);
|
||||
|
||||
let system_time = into_big_int(end.system() - start.system());
|
||||
indexmap.insert("system time".to_string(), system_time);
|
||||
|
||||
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, scope).await
|
||||
} else {
|
||||
Err(ShellError::untagged_runtime_error(
|
||||
"Could not retreive CPU time",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
async fn benchmark_output<T, Output>(
|
||||
indexmap: IndexMap<String, BigInt>,
|
||||
block_output: Output,
|
||||
passthrough: Option<Block>,
|
||||
tag: T,
|
||||
context: &mut EvaluationContext,
|
||||
scope: Arc<Scope>,
|
||||
) -> Result<OutputStream, ShellError>
|
||||
where
|
||||
T: Into<Tag> + Copy,
|
||||
Output: Into<OutputStream>,
|
||||
{
|
||||
let value = UntaggedValue::Row(Dictionary::from(
|
||||
indexmap
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, UntaggedValue::duration(v).into_value(tag)))
|
||||
.collect::<IndexMap<String, Value>>(),
|
||||
))
|
||||
.into_value(tag);
|
||||
|
||||
if let Some(time_block) = passthrough {
|
||||
let benchmark_output = InputStream::one(value);
|
||||
|
||||
// add autoview for an empty block
|
||||
let time_block = add_implicit_autoview(time_block);
|
||||
|
||||
let _ = run_block(&time_block, context, benchmark_output, scope).await?;
|
||||
context.clear_errors();
|
||||
|
||||
Ok(block_output.into())
|
||||
} else {
|
||||
let benchmark_output = OutputStream::one(value);
|
||||
Ok(benchmark_output)
|
||||
}
|
||||
}
|
||||
|
||||
fn add_implicit_autoview(mut block: Block) -> Block {
|
||||
if block.block.is_empty() {
|
||||
block.push({
|
||||
let mut commands = Commands::new(block.span);
|
||||
commands.push(ClassifiedCommand::Internal(InternalCommand::new(
|
||||
"autoview".to_string(),
|
||||
block.span,
|
||||
block.span,
|
||||
)));
|
||||
commands
|
||||
});
|
||||
}
|
||||
block
|
||||
}
|
||||
|
||||
fn into_big_int<T: TryInto<Duration>>(time: T) -> BigInt {
|
||||
time.try_into()
|
||||
.unwrap_or_else(|_| Duration::new(0, 0))
|
||||
.as_nanos()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn generate_random_env_value() -> String {
|
||||
let mut thread_rng = thread_rng();
|
||||
let len = thread_rng.gen_range(1, 16 * 1024);
|
||||
thread_rng.sample_iter(&Alphanumeric).take(len).collect()
|
||||
}
|
||||
|
||||
fn generate_free_name(env: &indexmap::IndexMap<String, String>) -> String {
|
||||
let mut thread_rng = thread_rng();
|
||||
loop {
|
||||
let candidate_name = format!("NU_RANDOM_VALUE_{}", thread_rng.gen::<usize>());
|
||||
if !env.contains_key(&candidate_name) {
|
||||
return candidate_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -339,11 +339,12 @@ fn add_month_to_table(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Cal;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Cal {})
|
||||
Ok(test_examples(Cal {})?)
|
||||
}
|
||||
}
|
||||
|
@ -72,11 +72,12 @@ impl WholeStreamCommand for Cd {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Cd;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Cd {})
|
||||
Ok(test_examples(Cd {})?)
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +94,35 @@ fn str_to_character(s: &str) -> Option<String> {
|
||||
"high_voltage_sign" | "elevated" => Some('\u{26a1}'.to_string()), // ⚡
|
||||
"tilde" | "twiddle" | "squiggly" | "home" => Some("~".into()), // ~
|
||||
"hash" | "hashtag" | "pound_sign" | "sharp" | "root" => Some("#".into()), // #
|
||||
|
||||
// Weather symbols
|
||||
"sun" => Some("\x1b[33;1m\u{2600}\x1b[0m".to_string()), // Yellow Bold ☀
|
||||
"moon" => Some("\x1b[36m\u{263d}\x1b[0m".to_string()), // Cyan ☽
|
||||
"clouds" => Some("\x1b[37;1m\u{2601}\x1b[0m".to_string()), // White Bold ☁
|
||||
"rain" => Some("\x1b[37;1m\u{2614}\x1b[0m".to_string()), // White Bold ☔
|
||||
"fog" => Some("\x1b[37;1m\u{2592}\x1b[0m".to_string()), // White Bold ▒
|
||||
"mist" => Some("\x1b[34m\u{2591}\x1b[0m".to_string()), // Blue ░
|
||||
"haze" => Some("\x1b[33m\u{2591}\x1b[0m".to_string()), // Yellow ░
|
||||
"snow" => Some("\x1b[37;1m\u{2744}\x1b[0m".to_string()), // White Bold ❄
|
||||
"thunderstorm" => Some("\x1b[33;1m\u{26a1}\x1b[0m".to_string()), // Yellow Bold ⚡
|
||||
|
||||
// Reference for ansi codes https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
|
||||
// Another good reference http://ascii-table.com/ansi-escape-sequences.php
|
||||
|
||||
// For setting title like `echo [$(char title) $(pwd) $(char bel)] | str collect`
|
||||
"title" => Some("\x1b]2;".to_string()), // ESC]2; xterm sets window title
|
||||
"bel" => Some('\x07'.to_string()), // Terminal Bell
|
||||
"backspace" => Some('\x08'.to_string()), // Backspace
|
||||
|
||||
// Ansi Erase Sequences
|
||||
"clear_screen" => Some("\x1b[J".to_string()), // clears the screen
|
||||
"clear_screen_from_cursor_to_end" => Some("\x1b[0J".to_string()), // clears from cursor until end of screen
|
||||
"clear_screen_from_cursor_to_beginning" => Some("\x1b[1J".to_string()), // clears from cursor to beginning of screen
|
||||
"cls" | "clear_entire_screen" => Some("\x1b[2J".to_string()), // clears the entire screen
|
||||
"erase_line" => Some("\x1b[K".to_string()), // clears the current line
|
||||
"erase_line_from_cursor_to_end" => Some("\x1b[0K".to_string()), // clears from cursor to end of line
|
||||
"erase_line_from_cursor_to_beginning" => Some("\x1b[1K".to_string()), // clears from cursor to start of line
|
||||
"erase_entire_line" => Some("\x1b[2K".to_string()), // clears entire line
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -101,11 +130,12 @@ fn str_to_character(s: &str) -> Option<String> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Char;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Char {})
|
||||
Ok(test_examples(Char {})?)
|
||||
}
|
||||
}
|
||||
|
53
crates/nu-cli/src/commands/chart.rs
Normal file
53
crates/nu-cli/src/commands/chart.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Chart;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Chart {
|
||||
fn name(&self) -> &str {
|
||||
"chart"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("chart")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Displays charts."
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
_args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
if registry.get_command("chart bar").is_none() {
|
||||
return Err(ShellError::untagged_runtime_error(
|
||||
"nu_plugin_chart not installed.",
|
||||
));
|
||||
}
|
||||
|
||||
let registry = registry.clone();
|
||||
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(crate::commands::help::get_help(&Chart, ®istry))
|
||||
.into_value(Tag::unknown()),
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Chart;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Chart {})?)
|
||||
}
|
||||
}
|
@ -1,21 +1,19 @@
|
||||
use crate::commands::classified::expr::run_expression_block;
|
||||
use crate::commands::classified::internal::run_internal_command;
|
||||
use crate::context::Context;
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use crate::prelude::*;
|
||||
use crate::stream::InputStream;
|
||||
use futures::stream::TryStreamExt;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::{Block, ClassifiedCommand, Commands};
|
||||
use nu_protocol::{ReturnSuccess, UntaggedValue, Value};
|
||||
use nu_protocol::{ReturnSuccess, Scope, UntaggedValue, Value};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
pub(crate) async fn run_block(
|
||||
block: &Block,
|
||||
ctx: &mut Context,
|
||||
ctx: &mut EvaluationContext,
|
||||
mut input: InputStream,
|
||||
it: &Value,
|
||||
vars: &IndexMap<String, Value>,
|
||||
env: &IndexMap<String, String>,
|
||||
scope: Arc<Scope>,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
let mut output: Result<InputStream, ShellError> = Ok(InputStream::empty());
|
||||
for pipeline in &block.block {
|
||||
@ -54,7 +52,7 @@ pub(crate) async fn run_block(
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
output = run_pipeline(pipeline, ctx, input, it, vars, env).await;
|
||||
output = run_pipeline(pipeline, ctx, input, scope.clone()).await;
|
||||
|
||||
input = InputStream::empty();
|
||||
}
|
||||
@ -64,11 +62,9 @@ pub(crate) async fn run_block(
|
||||
|
||||
async fn run_pipeline(
|
||||
commands: &Commands,
|
||||
ctx: &mut Context,
|
||||
ctx: &mut EvaluationContext,
|
||||
mut input: InputStream,
|
||||
it: &Value,
|
||||
vars: &IndexMap<String, Value>,
|
||||
env: &IndexMap<String, String>,
|
||||
scope: Arc<Scope>,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
for item in commands.list.clone() {
|
||||
input = match item {
|
||||
@ -77,13 +73,13 @@ async fn run_pipeline(
|
||||
}
|
||||
|
||||
ClassifiedCommand::Expr(expr) => {
|
||||
run_expression_block(*expr, ctx, it, vars, env).await?
|
||||
run_expression_block(*expr, ctx, scope.clone()).await?
|
||||
}
|
||||
|
||||
ClassifiedCommand::Error(err) => return Err(err.into()),
|
||||
|
||||
ClassifiedCommand::Internal(left) => {
|
||||
run_internal_command(left, ctx, input, it, vars, env).await?
|
||||
run_internal_command(left, ctx, input, scope.clone()).await?
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -6,14 +6,12 @@ use log::{log_enabled, trace};
|
||||
use futures::stream::once;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::SpannedExpression;
|
||||
use nu_protocol::Value;
|
||||
use nu_protocol::Scope;
|
||||
|
||||
pub(crate) async fn run_expression_block(
|
||||
expr: SpannedExpression,
|
||||
context: &mut Context,
|
||||
it: &Value,
|
||||
vars: &IndexMap<String, Value>,
|
||||
env: &IndexMap<String, String>,
|
||||
context: &mut EvaluationContext,
|
||||
scope: Arc<Scope>,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
if log_enabled!(log::Level::Trace) {
|
||||
trace!(target: "nu::run::expr", "->");
|
||||
@ -21,7 +19,7 @@ pub(crate) async fn run_expression_block(
|
||||
}
|
||||
|
||||
let registry = context.registry().clone();
|
||||
let output = evaluate_baseline_expr(&expr, ®istry, it, vars, env).await?;
|
||||
let output = evaluate_baseline_expr(&expr, ®istry, scope).await?;
|
||||
|
||||
Ok(once(async { Ok(output) }).to_input_stream())
|
||||
}
|
||||
|
@ -19,9 +19,9 @@ use nu_source::Tag;
|
||||
|
||||
pub(crate) async fn run_external_command(
|
||||
command: ExternalCommand,
|
||||
context: &mut Context,
|
||||
context: &mut EvaluationContext,
|
||||
input: InputStream,
|
||||
scope: &Scope,
|
||||
scope: Arc<Scope>,
|
||||
external_redirection: ExternalRedirection,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
trace!(target: "nu::run::external", "-> {}", command.name);
|
||||
@ -39,9 +39,9 @@ pub(crate) async fn run_external_command(
|
||||
|
||||
async fn run_with_stdin(
|
||||
command: ExternalCommand,
|
||||
context: &mut Context,
|
||||
context: &mut EvaluationContext,
|
||||
input: InputStream,
|
||||
scope: &Scope,
|
||||
scope: Arc<Scope>,
|
||||
external_redirection: ExternalRedirection,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
let path = context.shell_manager.path();
|
||||
@ -50,9 +50,7 @@ async fn run_with_stdin(
|
||||
|
||||
let mut command_args = vec![];
|
||||
for arg in command.args.iter() {
|
||||
let value =
|
||||
evaluate_baseline_expr(arg, &context.registry, &scope.it, &scope.vars, &scope.env)
|
||||
.await?;
|
||||
let value = evaluate_baseline_expr(arg, &context.registry, scope.clone()).await?;
|
||||
|
||||
// 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
|
||||
@ -138,7 +136,7 @@ fn spawn(
|
||||
args: &[String],
|
||||
input: InputStream,
|
||||
external_redirection: ExternalRedirection,
|
||||
scope: &Scope,
|
||||
scope: Arc<Scope>,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
let command = command.clone();
|
||||
|
||||
@ -169,7 +167,7 @@ fn spawn(
|
||||
trace!(target: "nu::run::external", "cwd = {:?}", &path);
|
||||
|
||||
process.env_clear();
|
||||
process.envs(scope.env.iter());
|
||||
process.envs(scope.env());
|
||||
|
||||
// We want stdout regardless of what
|
||||
// we are doing ($it case or pipe stdin)
|
||||
@ -543,7 +541,7 @@ mod tests {
|
||||
add_quotes, argument_contains_whitespace, argument_is_quoted, expand_tilde, remove_quotes,
|
||||
};
|
||||
#[cfg(feature = "which")]
|
||||
use super::{run_external_command, Context, InputStream};
|
||||
use super::{run_external_command, EvaluationContext, InputStream};
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use futures::executor::block_on;
|
||||
@ -573,13 +571,14 @@ mod tests {
|
||||
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
|
||||
|
||||
let input = InputStream::empty();
|
||||
let mut ctx = Context::basic().expect("There was a problem creating a basic context.");
|
||||
let mut ctx =
|
||||
EvaluationContext::basic().expect("There was a problem creating a basic context.");
|
||||
|
||||
assert!(run_external_command(
|
||||
cmd,
|
||||
&mut ctx,
|
||||
input,
|
||||
&Scope::new(),
|
||||
Scope::create(),
|
||||
ExternalRedirection::Stdout
|
||||
)
|
||||
.await
|
||||
@ -591,7 +590,7 @@ mod tests {
|
||||
// async fn failure_run() -> Result<(), ShellError> {
|
||||
// let cmd = ExternalBuilder::for_name("fail").build();
|
||||
|
||||
// let mut ctx = Context::basic().expect("There was a problem creating a basic context.");
|
||||
// let mut ctx = 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.");
|
||||
|
@ -9,22 +9,15 @@ use nu_protocol::{CommandAction, Primitive, ReturnSuccess, Scope, UntaggedValue,
|
||||
|
||||
pub(crate) async fn run_internal_command(
|
||||
command: InternalCommand,
|
||||
context: &mut Context,
|
||||
context: &mut EvaluationContext,
|
||||
input: InputStream,
|
||||
it: &Value,
|
||||
vars: &IndexMap<String, Value>,
|
||||
env: &IndexMap<String, String>,
|
||||
scope: Arc<Scope>,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
if log_enabled!(log::Level::Trace) {
|
||||
trace!(target: "nu::run::internal", "->");
|
||||
trace!(target: "nu::run::internal", "{}", command.name);
|
||||
}
|
||||
|
||||
let scope = Scope {
|
||||
it: it.clone(),
|
||||
vars: vars.clone(),
|
||||
env: env.clone(),
|
||||
};
|
||||
let objects: InputStream = trace_stream!(target: "nu::trace_stream::internal", "input" = input);
|
||||
let internal_command = context.expect_command(&command.name);
|
||||
|
||||
@ -38,7 +31,7 @@ pub(crate) async fn run_internal_command(
|
||||
internal_command?,
|
||||
Tag::unknown_anchor(command.name_span),
|
||||
command.args.clone(),
|
||||
&scope,
|
||||
scope.clone(),
|
||||
objects,
|
||||
)
|
||||
.await?
|
||||
@ -48,8 +41,6 @@ pub(crate) async fn run_internal_command(
|
||||
//let context = Arc::new(context.clone());
|
||||
let context = context.clone();
|
||||
let command = Arc::new(command);
|
||||
let scope = Arc::new(scope);
|
||||
// let scope = scope.clone();
|
||||
|
||||
Ok(InputStream::from_stream(
|
||||
result
|
||||
@ -90,7 +81,7 @@ pub(crate) async fn run_internal_command(
|
||||
external_redirection: ExternalRedirection::Stdout,
|
||||
},
|
||||
name_tag: Tag::unknown_anchor(command.name_span),
|
||||
scope: (&*scope).clone(),
|
||||
scope,
|
||||
},
|
||||
};
|
||||
let result = converter
|
||||
@ -200,6 +191,28 @@ pub(crate) async fn run_internal_command(
|
||||
)]);
|
||||
InputStream::from_stream(futures::stream::iter(vec![]))
|
||||
}
|
||||
CommandAction::AddPlugins(path) => {
|
||||
match crate::plugin::scan(vec![std::path::PathBuf::from(path)]) {
|
||||
Ok(plugins) => {
|
||||
context.add_commands(
|
||||
plugins
|
||||
.into_iter()
|
||||
.filter(|p| {
|
||||
!context.is_command_registered(p.name())
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
|
||||
InputStream::from_stream(futures::stream::iter(vec![]))
|
||||
}
|
||||
Err(reason) => {
|
||||
context.error(reason.clone());
|
||||
InputStream::one(
|
||||
UntaggedValue::Error(reason).into_untagged_value(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandAction::PreviousShell => {
|
||||
context.shell_manager.prev();
|
||||
InputStream::from_stream(futures::stream::iter(vec![]))
|
||||
|
@ -130,7 +130,7 @@ async fn run_filter(
|
||||
UntaggedValue::Primitive(Primitive::EndOfStream).into_untagged_value()
|
||||
]);
|
||||
|
||||
let args = args.evaluate_once_with_scope(®istry, &scope).await?;
|
||||
let args = args.evaluate_once_with_scope(®istry, scope).await?;
|
||||
|
||||
let real_path = Path::new(&path);
|
||||
let ext = real_path.extension();
|
||||
|
@ -17,7 +17,7 @@ impl WholeStreamCommand for Clear {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"clears the terminal"
|
||||
"Clears the terminal"
|
||||
}
|
||||
|
||||
async fn run(&self, _: CommandArgs, _: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
@ -43,15 +43,3 @@ impl WholeStreamCommand for Clear {
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Clear;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Clear {})
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use futures::stream::StreamExt;
|
||||
use nu_errors::ShellError;
|
||||
@ -32,11 +32,18 @@ impl WholeStreamCommand for Clip {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Save text to the clipboard",
|
||||
example: "echo 'secret value' | clip",
|
||||
result: None,
|
||||
}]
|
||||
vec![
|
||||
Example {
|
||||
description: "Save text to the clipboard",
|
||||
example: "echo 'secret value' | clip",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Save numbers to the clipboard",
|
||||
example: "random integer 10000000..99999999 | clip",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,16 +68,14 @@ pub async fn clip(
|
||||
first = false;
|
||||
}
|
||||
|
||||
let string: String = match i.as_string() {
|
||||
Ok(string) => string.to_string(),
|
||||
Err(_) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Given non-string data",
|
||||
"expected strings from pipeline",
|
||||
name,
|
||||
))
|
||||
}
|
||||
};
|
||||
let string: String = i.convert_to_string();
|
||||
if string.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unable to convert to string",
|
||||
"Unable to convert to string",
|
||||
name,
|
||||
));
|
||||
}
|
||||
|
||||
new_copy_data.push_str(&string);
|
||||
}
|
||||
@ -99,11 +104,12 @@ pub async fn clip(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Clip;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Clip {})
|
||||
Ok(test_examples(Clip {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::help::get_help;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::deserializer::ConfigDeserializer;
|
||||
use crate::evaluate::evaluate_args::evaluate_args;
|
||||
use crate::prelude::*;
|
||||
@ -17,27 +17,12 @@ use std::sync::atomic::AtomicBool;
|
||||
pub struct UnevaluatedCallInfo {
|
||||
pub args: hir::Call,
|
||||
pub name_tag: Tag,
|
||||
pub scope: Scope,
|
||||
pub scope: Arc<Scope>,
|
||||
}
|
||||
|
||||
impl UnevaluatedCallInfo {
|
||||
pub async fn evaluate(self, registry: &CommandRegistry) -> Result<CallInfo, ShellError> {
|
||||
let args = evaluate_args(&self.args, registry, &self.scope).await?;
|
||||
|
||||
Ok(CallInfo {
|
||||
args,
|
||||
name_tag: self.name_tag,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn evaluate_with_new_it(
|
||||
self,
|
||||
registry: &CommandRegistry,
|
||||
it: &Value,
|
||||
) -> Result<CallInfo, ShellError> {
|
||||
let mut scope = self.scope.clone();
|
||||
scope.it = it.clone();
|
||||
let args = evaluate_args(&self.args, registry, &scope).await?;
|
||||
let args = evaluate_args(&self.args, registry, self.scope.clone()).await?;
|
||||
|
||||
Ok(CallInfo {
|
||||
args,
|
||||
@ -115,7 +100,7 @@ impl CommandArgs {
|
||||
pub async fn evaluate_once_with_scope(
|
||||
self,
|
||||
registry: &CommandRegistry,
|
||||
scope: &Scope,
|
||||
scope: Arc<Scope>,
|
||||
) -> Result<EvaluatedWholeStreamCommandArgs, ShellError> {
|
||||
let host = self.host.clone();
|
||||
let ctrl_c = self.ctrl_c.clone();
|
||||
@ -215,37 +200,6 @@ impl EvaluatedWholeStreamCommandArgs {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Getters)]
|
||||
#[get = "pub"]
|
||||
pub struct EvaluatedFilterCommandArgs {
|
||||
args: EvaluatedCommandArgs,
|
||||
}
|
||||
|
||||
impl Deref for EvaluatedFilterCommandArgs {
|
||||
type Target = EvaluatedCommandArgs;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.args
|
||||
}
|
||||
}
|
||||
|
||||
impl EvaluatedFilterCommandArgs {
|
||||
pub fn new(
|
||||
host: Arc<parking_lot::Mutex<dyn Host>>,
|
||||
ctrl_c: Arc<AtomicBool>,
|
||||
shell_manager: ShellManager,
|
||||
call_info: CallInfo,
|
||||
) -> EvaluatedFilterCommandArgs {
|
||||
EvaluatedFilterCommandArgs {
|
||||
args: EvaluatedCommandArgs {
|
||||
host,
|
||||
ctrl_c,
|
||||
shell_manager,
|
||||
call_info,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Getters, new)]
|
||||
#[get = "pub(crate)"]
|
||||
pub struct EvaluatedCommandArgs {
|
||||
@ -303,6 +257,11 @@ pub trait WholeStreamCommand: Send + Sync {
|
||||
false
|
||||
}
|
||||
|
||||
// Commands that are not meant to be run by users
|
||||
fn is_internal(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
Vec::new()
|
||||
}
|
||||
@ -367,74 +326,15 @@ impl Command {
|
||||
self.0.is_binary()
|
||||
}
|
||||
|
||||
pub fn is_internal(&self) -> bool {
|
||||
self.0.is_internal()
|
||||
}
|
||||
|
||||
pub fn stream_command(&self) -> &dyn WholeStreamCommand {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FnFilterCommand {
|
||||
name: String,
|
||||
func: fn(EvaluatedFilterCommandArgs) -> Result<OutputStream, ShellError>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FnFilterCommand {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"usage"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
CommandArgs {
|
||||
host,
|
||||
ctrl_c,
|
||||
shell_manager,
|
||||
call_info,
|
||||
input,
|
||||
..
|
||||
}: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = Arc::new(registry.clone());
|
||||
let func = self.func;
|
||||
|
||||
Ok(input
|
||||
.then(move |it| {
|
||||
let host = host.clone();
|
||||
let registry = registry.clone();
|
||||
let ctrl_c = ctrl_c.clone();
|
||||
let shell_manager = shell_manager.clone();
|
||||
let call_info = call_info.clone();
|
||||
async move {
|
||||
let call_info = match call_info.evaluate_with_new_it(&*registry, &it).await {
|
||||
Err(err) => {
|
||||
return OutputStream::one(Err(err));
|
||||
}
|
||||
Ok(args) => args,
|
||||
};
|
||||
|
||||
let args = EvaluatedFilterCommandArgs::new(
|
||||
host.clone(),
|
||||
ctrl_c.clone(),
|
||||
shell_manager.clone(),
|
||||
call_info,
|
||||
);
|
||||
|
||||
match func(args) {
|
||||
Err(err) => return OutputStream::one(Err(err)),
|
||||
Ok(stream) => stream,
|
||||
}
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn whole_stream_command(command: impl WholeStreamCommand + 'static) -> Command {
|
||||
Command(Arc::new(command))
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use futures::future;
|
||||
use futures::stream::StreamExt;
|
||||
@ -37,22 +37,11 @@ impl WholeStreamCommand for Compact {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Filter out all null entries in a list",
|
||||
example: "echo [1 2 $null 3 $null $null] | compact",
|
||||
result: Some(vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Filter out all directory entries having no 'target'",
|
||||
example: "ls -la | compact target",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
vec![Example {
|
||||
description: "Filter out all directory entries having no 'target'",
|
||||
example: "ls -la | compact target",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,11 +84,12 @@ pub async fn compact(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Compact;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Compact {})
|
||||
Ok(test_examples(Compact {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
@ -1,15 +1,14 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GetArgs {
|
||||
get: Tagged<String>,
|
||||
path: ColumnPath,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -21,7 +20,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("config get").required(
|
||||
"get",
|
||||
SyntaxShape::Any,
|
||||
SyntaxShape::ColumnPath,
|
||||
"value to get from the config",
|
||||
)
|
||||
}
|
||||
@ -51,17 +50,14 @@ pub async fn get(
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let name_span = args.call_info.name_tag.clone();
|
||||
let (GetArgs { get }, _) = args.process(®istry).await?;
|
||||
let name_tag = args.call_info.name_tag.clone();
|
||||
let (GetArgs { path }, _) = args.process(®istry).await?;
|
||||
|
||||
// NOTE: None because we are not loading a new config file, we just want to read from the
|
||||
// existing config
|
||||
let result = nu_data::config::read(name_span, &None)?;
|
||||
let result = UntaggedValue::row(nu_data::config::read(&name_tag, &None)?).into_value(&name_tag);
|
||||
|
||||
let key = get.to_string();
|
||||
let value = result
|
||||
.get(&key)
|
||||
.ok_or_else(|| ShellError::labeled_error("Missing key in config", "key", get.tag()))?;
|
||||
let value = crate::commands::get::get_column_path(&path, &result)?;
|
||||
|
||||
Ok(match value {
|
||||
Value {
|
||||
@ -75,9 +71,6 @@ pub async fn get(
|
||||
|
||||
futures::stream::iter(list).to_output_stream()
|
||||
}
|
||||
x => {
|
||||
let x = x.clone();
|
||||
OutputStream::one(ReturnSuccess::value(x))
|
||||
}
|
||||
x => OutputStream::one(ReturnSuccess::value(x)),
|
||||
})
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
|
@ -1,15 +1,14 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SetArgs {
|
||||
key: Tagged<String>,
|
||||
path: ColumnPath,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
@ -21,7 +20,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("config set")
|
||||
.required("key", SyntaxShape::String, "variable name to set")
|
||||
.required("key", SyntaxShape::ColumnPath, "variable name to set")
|
||||
.required("value", SyntaxShape::Any, "value to use")
|
||||
}
|
||||
|
||||
@ -38,11 +37,28 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Set nonzero_exit_errors to true",
|
||||
example: "config set nonzero_exit_errors $true",
|
||||
result: None,
|
||||
}]
|
||||
vec![
|
||||
Example {
|
||||
description: "Set auto pivoting",
|
||||
example: "config set pivot_mode always",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Set line editor options",
|
||||
example: "config set line_editor [[edit_mode, completion_type]; [emacs circular]]",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Set coloring options",
|
||||
example: "config set color_config [[header_align header_bold]; [left $true]]",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Set nested options",
|
||||
example: "config set color_config.header_color white",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,18 +66,32 @@ pub async fn set(
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let name_span = args.call_info.name_tag.clone();
|
||||
let (SetArgs { key, value }, _) = args.process(®istry).await?;
|
||||
let name_tag = args.call_info.name_tag.clone();
|
||||
let (SetArgs { path, mut value }, _) = args.process(®istry).await?;
|
||||
|
||||
// NOTE: None because we are not loading a new config file, we just want to read from the
|
||||
// existing config
|
||||
let mut result = nu_data::config::read(name_span, &None)?;
|
||||
let raw_entries = nu_data::config::read(&name_tag, &None)?;
|
||||
let configuration = UntaggedValue::row(raw_entries).into_value(&name_tag);
|
||||
|
||||
result.insert(key.to_string(), value.clone());
|
||||
if let UntaggedValue::Table(rows) = &value.value {
|
||||
if rows.len() == 1 && rows[0].is_row() {
|
||||
value = rows[0].clone();
|
||||
}
|
||||
}
|
||||
|
||||
config::write(&result, &None)?;
|
||||
match configuration.forgiving_insert_data_at_column_path(&path, value) {
|
||||
Ok(Value {
|
||||
value: UntaggedValue::Row(changes),
|
||||
..
|
||||
}) => {
|
||||
config::write(&changes.entries, &None)?;
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(&value.tag),
|
||||
)))
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Row(changes).into_value(name_tag),
|
||||
)))
|
||||
}
|
||||
Ok(_) => Ok(OutputStream::empty()),
|
||||
Err(reason) => Err(reason),
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use futures::stream::StreamExt;
|
||||
use nu_errors::ShellError;
|
||||
@ -80,11 +80,12 @@ impl WholeStreamCommand for Count {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Count;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Count {})
|
||||
Ok(test_examples(Count {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
@ -66,11 +66,12 @@ impl WholeStreamCommand for Cpy {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Cpy;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Cpy {})
|
||||
Ok(test_examples(Cpy {})?)
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Work with dates."
|
||||
"Apply date function"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
@ -36,11 +36,12 @@ impl WholeStreamCommand for Command {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Command;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Command {})
|
||||
Ok(test_examples(Command {})?)
|
||||
}
|
||||
}
|
||||
|
@ -61,3 +61,16 @@ pub async fn format(
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Date;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Date {})?)
|
||||
}
|
||||
}
|
||||
|
@ -48,3 +48,16 @@ pub async fn now(
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Date;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Date {})?)
|
||||
}
|
||||
}
|
||||
|
@ -48,3 +48,16 @@ pub async fn utc(
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Date;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Date {})?)
|
||||
}
|
||||
}
|
||||
|
@ -55,11 +55,12 @@ async fn debug_value(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Debug;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Debug {})
|
||||
Ok(test_examples(Debug {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
@ -83,11 +83,12 @@ async fn default(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Default;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Default {})
|
||||
Ok(test_examples(Default {})?)
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,13 @@ use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct What;
|
||||
pub struct Describe;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WhatArgs {}
|
||||
pub struct DescribeArgs {}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for What {
|
||||
impl WholeStreamCommand for Describe {
|
||||
fn name(&self) -> &str {
|
||||
"describe"
|
||||
}
|
||||
@ -28,11 +28,11 @@ impl WholeStreamCommand for What {
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
what(args, registry).await
|
||||
describe(args, registry).await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn what(
|
||||
pub async fn describe(
|
||||
args: CommandArgs,
|
||||
_registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
@ -49,12 +49,13 @@ pub async fn what(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::What;
|
||||
use super::Describe;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(What {})
|
||||
Ok(test_examples(Describe {})?)
|
||||
}
|
||||
}
|
@ -65,7 +65,7 @@ async fn do_(
|
||||
let registry = registry.clone();
|
||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
||||
|
||||
let mut context = Context::from_raw(&raw_args, ®istry);
|
||||
let mut context = EvaluationContext::from_raw(&raw_args, ®istry);
|
||||
let scope = raw_args.call_info.scope.clone();
|
||||
let (
|
||||
DoArgs {
|
||||
@ -95,15 +95,7 @@ async fn do_(
|
||||
|
||||
block.set_redirect(block_redirection);
|
||||
|
||||
let result = run_block(
|
||||
&block,
|
||||
&mut context,
|
||||
input,
|
||||
&scope.it,
|
||||
&scope.vars,
|
||||
&scope.env,
|
||||
)
|
||||
.await;
|
||||
let result = run_block(&block, &mut context, input, scope).await;
|
||||
|
||||
if ignore_errors {
|
||||
// To properly ignore errors we need to redirect stderr, consume it, and remove
|
||||
@ -125,11 +117,12 @@ async fn do_(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Do;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Do {})
|
||||
Ok(test_examples(Do {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||
@ -85,11 +85,12 @@ async fn drop(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Drop;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Drop {})
|
||||
Ok(test_examples(Drop {})?)
|
||||
}
|
||||
}
|
||||
|
@ -427,11 +427,12 @@ impl From<FileInfo> for Value {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Du;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Du {})
|
||||
Ok(test_examples(Du {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
|
||||
use futures::stream::once;
|
||||
@ -84,7 +84,7 @@ pub async fn process_row(
|
||||
block: Arc<Block>,
|
||||
scope: Arc<Scope>,
|
||||
head: Arc<Box<SpannedExpression>>,
|
||||
mut context: Arc<Context>,
|
||||
mut context: Arc<EvaluationContext>,
|
||||
input: Value,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let input_clone = input.clone();
|
||||
@ -97,9 +97,7 @@ pub async fn process_row(
|
||||
&block,
|
||||
Arc::make_mut(&mut context),
|
||||
input_stream,
|
||||
&input,
|
||||
&scope.vars,
|
||||
&scope.env,
|
||||
Scope::append_it(scope, input),
|
||||
)
|
||||
.await?
|
||||
.to_output_stream())
|
||||
@ -119,8 +117,8 @@ async fn each(
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let head = Arc::new(raw_args.call_info.args.head.clone());
|
||||
let scope = Arc::new(raw_args.call_info.scope.clone());
|
||||
let context = Arc::new(Context::from_raw(&raw_args, ®istry));
|
||||
let scope = raw_args.call_info.scope.clone();
|
||||
let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry));
|
||||
let (each_args, input): (EachArgs, _) = raw_args.process(®istry).await?;
|
||||
let block = Arc::new(each_args.block);
|
||||
|
||||
@ -166,11 +164,12 @@ async fn each(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Each;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Each {})
|
||||
Ok(test_examples(Each {})?)
|
||||
}
|
||||
}
|
134
crates/nu-cli/src/commands/each/group.rs
Normal file
134
crates/nu-cli/src/commands/each/group.rs
Normal file
@ -0,0 +1,134 @@
|
||||
use crate::commands::each::process_row;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::Block, hir::SpannedExpression, ReturnSuccess, Scope, Signature, SyntaxShape,
|
||||
UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub struct EachGroup;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EachGroupArgs {
|
||||
group_size: Tagged<usize>,
|
||||
block: Block,
|
||||
//numbered: Tagged<bool>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for EachGroup {
|
||||
fn name(&self) -> &str {
|
||||
"each group"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("each group")
|
||||
.required("group_size", SyntaxShape::Int, "the size of each group")
|
||||
.required(
|
||||
"block",
|
||||
SyntaxShape::Block,
|
||||
"the block to run on each group",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Runs a block on groups of `group_size` rows of a table at a time."
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Echo the sum of each pair",
|
||||
example: "echo [1 2 3 4] | each group 2 { echo $it | math sum }",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
raw_args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let head = Arc::new(raw_args.call_info.args.head.clone());
|
||||
let scope = raw_args.call_info.scope.clone();
|
||||
let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry));
|
||||
let (each_args, input): (EachGroupArgs, _) = raw_args.process(®istry).await?;
|
||||
let block = Arc::new(each_args.block);
|
||||
|
||||
Ok(input
|
||||
.chunks(each_args.group_size.item)
|
||||
.then(move |input| {
|
||||
run_block_on_vec(
|
||||
input,
|
||||
block.clone(),
|
||||
scope.clone(),
|
||||
head.clone(),
|
||||
context.clone(),
|
||||
)
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn run_block_on_vec(
|
||||
input: Vec<Value>,
|
||||
block: Arc<Block>,
|
||||
scope: Arc<Scope>,
|
||||
head: Arc<Box<SpannedExpression>>,
|
||||
context: Arc<EvaluationContext>,
|
||||
) -> impl Future<Output = OutputStream> {
|
||||
let value = Value {
|
||||
value: UntaggedValue::Table(input),
|
||||
tag: Tag::unknown(),
|
||||
};
|
||||
|
||||
async {
|
||||
match process_row(block, scope, head, 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;
|
||||
|
||||
// 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())))
|
||||
}
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::EachGroup;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(EachGroup {})?)
|
||||
}
|
||||
}
|
9
crates/nu-cli/src/commands/each/mod.rs
Normal file
9
crates/nu-cli/src/commands/each/mod.rs
Normal file
@ -0,0 +1,9 @@
|
||||
pub mod command;
|
||||
pub mod group;
|
||||
pub mod window;
|
||||
|
||||
pub(crate) use command::make_indexed_item;
|
||||
pub use command::process_row;
|
||||
pub use command::Each;
|
||||
pub use group::EachGroup;
|
||||
pub use window::EachWindow;
|
114
crates/nu-cli/src/commands/each/window.rs
Normal file
114
crates/nu-cli/src/commands/each/window.rs
Normal file
@ -0,0 +1,114 @@
|
||||
use crate::commands::each::group::run_block_on_vec;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
//use itertools::Itertools;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::Block, Primitive, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub struct EachWindow;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EachWindowArgs {
|
||||
window_size: Tagged<usize>,
|
||||
block: Block,
|
||||
stride: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for EachWindow {
|
||||
fn name(&self) -> &str {
|
||||
"each window"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("each window")
|
||||
.required("window_size", SyntaxShape::Int, "the size of each window")
|
||||
.named(
|
||||
"stride",
|
||||
SyntaxShape::Int,
|
||||
"the number of rows to slide over between windows",
|
||||
Some('s'),
|
||||
)
|
||||
.required(
|
||||
"block",
|
||||
SyntaxShape::Block,
|
||||
"the block to run on each group",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Runs a block on sliding windows of `window_size` rows of a table at a time."
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Echo the sum of each window",
|
||||
example: "echo [1 2 3 4] | each window 2 { echo $it | math sum }",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
raw_args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let head = Arc::new(raw_args.call_info.args.head.clone());
|
||||
let scope = raw_args.call_info.scope.clone();
|
||||
let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry));
|
||||
let (each_args, mut input): (EachWindowArgs, _) = raw_args.process(®istry).await?;
|
||||
let block = Arc::new(each_args.block);
|
||||
|
||||
let mut window: Vec<_> = input
|
||||
.by_ref()
|
||||
.take(*each_args.window_size - 1)
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
|
||||
// `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);
|
||||
window.insert(0, UntaggedValue::Primitive(Primitive::Nothing).into());
|
||||
|
||||
Ok(input
|
||||
.enumerate()
|
||||
.then(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);
|
||||
window.push(input);
|
||||
|
||||
let block = block.clone();
|
||||
let scope = scope.clone();
|
||||
let head = head.clone();
|
||||
let context = context.clone();
|
||||
let local_window = window.clone();
|
||||
|
||||
async move {
|
||||
if i % stride == 0 {
|
||||
Some(run_block_on_vec(local_window, block, scope, head, context).await)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.filter_map(|x| async { x })
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::EachWindow;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(EachWindow {})?)
|
||||
}
|
||||
}
|
@ -81,17 +81,20 @@ struct RangeIterator {
|
||||
end: Primitive,
|
||||
tag: Tag,
|
||||
is_end_inclusive: bool,
|
||||
is_done: bool,
|
||||
}
|
||||
|
||||
impl RangeIterator {
|
||||
pub fn new(range: Range, tag: Tag) -> RangeIterator {
|
||||
let start = match range.from.0.item {
|
||||
Primitive::Nothing => Primitive::Int(0.into()),
|
||||
x => x,
|
||||
};
|
||||
|
||||
RangeIterator {
|
||||
curr: range.from.0.item,
|
||||
curr: start,
|
||||
end: range.to.0.item,
|
||||
tag,
|
||||
is_end_inclusive: matches!(range.to.1, RangeInclusion::Inclusive),
|
||||
is_done: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,14 +102,40 @@ impl RangeIterator {
|
||||
impl Iterator for RangeIterator {
|
||||
type Item = Result<ReturnSuccess, ShellError>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.curr != self.end {
|
||||
let ordering = if self.end == 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));
|
||||
}
|
||||
|
||||
let result = result
|
||||
.expect("Internal error: the error case was already protected, but that failed");
|
||||
|
||||
result.compare()
|
||||
};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
if (ordering == Ordering::Less) || (self.is_end_inclusive && ordering == Ordering::Equal) {
|
||||
let output = UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone());
|
||||
|
||||
self.curr = match nu_data::value::compute_values(
|
||||
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,
|
||||
_ => {
|
||||
@ -123,11 +152,6 @@ impl Iterator for RangeIterator {
|
||||
}
|
||||
};
|
||||
Some(ReturnSuccess::value(output))
|
||||
} else if self.is_end_inclusive && !self.is_done {
|
||||
self.is_done = true;
|
||||
Some(ReturnSuccess::value(
|
||||
UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone()),
|
||||
))
|
||||
} else {
|
||||
// TODO: add inclusive/exclusive ranges
|
||||
None
|
||||
@ -138,11 +162,12 @@ impl Iterator for RangeIterator {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Echo;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Echo {})
|
||||
Ok(test_examples(Echo {})?)
|
||||
}
|
||||
}
|
||||
|
288
crates/nu-cli/src/commands/empty.rs
Normal file
288
crates/nu-cli/src/commands/empty.rs
Normal file
@ -0,0 +1,288 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::Block, ColumnPath, Primitive, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue,
|
||||
Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use nu_value_ext::{as_string, ValueExt};
|
||||
|
||||
use futures::stream::once;
|
||||
use indexmap::indexmap;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
rest: Vec<Value>,
|
||||
}
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"empty?"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("empty?").rest(
|
||||
SyntaxShape::Any,
|
||||
"the names of the columns to check emptiness. Pass an optional block to replace if empty",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Check for empty values"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
is_empty(args, registry).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Check if a value is empty",
|
||||
example: "echo '' | empty?",
|
||||
result: Some(vec![UntaggedValue::boolean(true).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "more than one column",
|
||||
example: "echo [[meal size]; [arepa small] [taco '']] | empty? meal size",
|
||||
result: Some(
|
||||
vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"meal".to_string() => Value::from(false),
|
||||
"size".to_string() => Value::from(false),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"meal".to_string() => Value::from(false),
|
||||
"size".to_string() => Value::from(true),
|
||||
})
|
||||
.into(),
|
||||
],
|
||||
),
|
||||
},Example {
|
||||
description: "use a block if setting the empty cell contents is wanted",
|
||||
example: "echo [[2020/04/16 2020/07/10 2020/11/16]; ['' [27] [37]]] | empty? 2020/04/16 { = [33 37] }",
|
||||
result: Some(
|
||||
vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"2020/04/16".to_string() => UntaggedValue::table(&[UntaggedValue::int(33).into(), UntaggedValue::int(37).into()]).into(),
|
||||
"2020/07/10".to_string() => UntaggedValue::table(&[UntaggedValue::int(27).into()]).into(),
|
||||
"2020/11/16".to_string() => UntaggedValue::table(&[UntaggedValue::int(37).into()]).into(),
|
||||
})
|
||||
.into(),
|
||||
],
|
||||
),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
async fn is_empty(
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, 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_raw(&args, ®istry));
|
||||
let scope = args.call_info.scope.clone();
|
||||
let (Arguments { rest }, input) = args.process(®istry).await?;
|
||||
let (columns, default_block): (Vec<ColumnPath>, Option<Block>) = arguments(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)
|
||||
]);
|
||||
|
||||
return Ok(InputStream::from_stream(stream)
|
||||
.then(move |input| {
|
||||
let tag = name_tag.clone();
|
||||
let scope = scope.clone();
|
||||
let context = context.clone();
|
||||
let block = default_block.clone();
|
||||
let columns = vec![];
|
||||
|
||||
async {
|
||||
match process_row(scope, context, input, block, columns, tag).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream());
|
||||
}
|
||||
|
||||
Ok(input
|
||||
.then(move |input| {
|
||||
let tag = name_tag.clone();
|
||||
let scope = scope.clone();
|
||||
let context = context.clone();
|
||||
let block = default_block.clone();
|
||||
let columns = columns.clone();
|
||||
|
||||
async {
|
||||
match process_row(scope, context, input, block, columns, tag).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
fn arguments(rest: Vec<Value>) -> Result<(Vec<ColumnPath>, Option<Block>), ShellError> {
|
||||
let mut rest = rest;
|
||||
let mut columns = vec![];
|
||||
let mut default = None;
|
||||
|
||||
let last_argument = rest.pop();
|
||||
|
||||
match last_argument {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Block(call),
|
||||
..
|
||||
}) => default = Some(call),
|
||||
Some(other) => {
|
||||
let Tagged { item: path, .. } = other.as_column_path()?;
|
||||
|
||||
columns = vec![path];
|
||||
}
|
||||
None => {}
|
||||
};
|
||||
|
||||
for argument in rest {
|
||||
let Tagged { item: path, .. } = argument.as_column_path()?;
|
||||
|
||||
columns.push(path);
|
||||
}
|
||||
|
||||
Ok((columns, default))
|
||||
}
|
||||
|
||||
async fn process_row(
|
||||
scope: Arc<Scope>,
|
||||
mut context: Arc<EvaluationContext>,
|
||||
input: Value,
|
||||
default_block: Arc<Option<Block>>,
|
||||
column_paths: Vec<ColumnPath>,
|
||||
tag: Arc<Tag>,
|
||||
) -> Result<OutputStream, 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 scope = Scope::append_it(scope, input.clone());
|
||||
|
||||
let mut stream = run_block(
|
||||
&default_block,
|
||||
Arc::make_mut(&mut context),
|
||||
input_stream,
|
||||
scope,
|
||||
)
|
||||
.await?;
|
||||
|
||||
*results = Some({
|
||||
let values = stream.drain_vec().await;
|
||||
|
||||
let errors = context.get_errors();
|
||||
|
||||
if let Some(error) = errors.first() {
|
||||
return Err(error.clone());
|
||||
}
|
||||
|
||||
if values.len() == 1 {
|
||||
let value = values
|
||||
.get(0)
|
||||
.ok_or_else(|| ShellError::unexpected("No value."))?;
|
||||
|
||||
Value {
|
||||
value: value.value.clone(),
|
||||
tag: input.tag.clone(),
|
||||
}
|
||||
} else if values.is_empty() {
|
||||
UntaggedValue::nothing().into_value(&input.tag)
|
||||
} else {
|
||||
UntaggedValue::table(&values).into_value(&input.tag)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
match input {
|
||||
Value {
|
||||
value: UntaggedValue::Row(ref r),
|
||||
ref tag,
|
||||
} => {
|
||||
if column_paths.is_empty() {
|
||||
Ok(OutputStream::one(ReturnSuccess::value({
|
||||
let is_empty = input.is_empty();
|
||||
|
||||
if default_block.is_some() {
|
||||
if is_empty {
|
||||
results
|
||||
.clone()
|
||||
.unwrap_or_else(|| UntaggedValue::boolean(true).into_value(tag))
|
||||
} else {
|
||||
input.clone()
|
||||
}
|
||||
} else {
|
||||
UntaggedValue::boolean(is_empty).into_value(tag)
|
||||
}
|
||||
})))
|
||||
} else {
|
||||
let mut obj = input.clone();
|
||||
|
||||
for column in column_paths.clone() {
|
||||
let path = UntaggedValue::Primitive(Primitive::ColumnPath(column.clone()))
|
||||
.into_value(tag);
|
||||
let data = r.get_data(&as_string(&path)?).borrow().clone();
|
||||
let is_empty = data.is_empty();
|
||||
|
||||
let default = if default_block.is_some() {
|
||||
if is_empty {
|
||||
results
|
||||
.clone()
|
||||
.unwrap_or_else(|| UntaggedValue::boolean(true).into_value(tag))
|
||||
} else {
|
||||
data.clone()
|
||||
}
|
||||
} else {
|
||||
UntaggedValue::boolean(is_empty).into_value(tag)
|
||||
};
|
||||
|
||||
if let Ok(value) =
|
||||
obj.swap_data_by_column_path(&column, Box::new(move |_| Ok(default)))
|
||||
{
|
||||
obj = value;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(obj)))
|
||||
}
|
||||
}
|
||||
other => Ok(OutputStream::one(ReturnSuccess::value({
|
||||
if other.is_empty() {
|
||||
results
|
||||
.clone()
|
||||
.unwrap_or_else(|| UntaggedValue::boolean(true).into_value(other.tag))
|
||||
} else {
|
||||
UntaggedValue::boolean(false).into_value(other.tag)
|
||||
}
|
||||
}))),
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::UnevaluatedCallInfo;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::ExternalRedirection;
|
||||
@ -191,11 +191,12 @@ async fn enter(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Enter;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Enter {})
|
||||
Ok(test_examples(Enter {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
@ -93,11 +93,12 @@ async fn every(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Every;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Every {})
|
||||
Ok(test_examples(Every {})?)
|
||||
}
|
||||
}
|
||||
|
87
crates/nu-cli/src/commands/exec.rs
Normal file
87
crates/nu-cli/src/commands/exec.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct Exec;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ExecArgs {
|
||||
pub command: Tagged<PathBuf>,
|
||||
pub rest: Vec<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Exec {
|
||||
fn name(&self) -> &str {
|
||||
"exec"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("exec")
|
||||
.required("command", SyntaxShape::Path, "the command to execute")
|
||||
.rest(SyntaxShape::Pattern, "any additional arguments for command")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Execute command"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
exec(args, registry).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Execute 'ps aux'",
|
||||
example: "exec ps aux",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Execute 'nautilus'",
|
||||
example: "exec nautilus",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn exec(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process::Command;
|
||||
|
||||
let registry = registry.clone();
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (args, _): (ExecArgs, _) = args.process(®istry).await?;
|
||||
|
||||
let mut command = Command::new(args.command.item);
|
||||
for tagged_arg in args.rest {
|
||||
command.arg(tagged_arg.item);
|
||||
}
|
||||
|
||||
let err = command.exec(); // this replaces our process, should not return
|
||||
|
||||
Err(ShellError::labeled_error(
|
||||
"Error on exec",
|
||||
format!("{}", err),
|
||||
&name,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
async fn exec(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
Err(ShellError::labeled_error(
|
||||
"Error on exec",
|
||||
"exec is not supported on your platform",
|
||||
&args.call_info.name_tag,
|
||||
))
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::command::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CommandAction, ReturnSuccess, Signature};
|
||||
@ -63,11 +63,12 @@ pub async fn exit(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Exit;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Exit {})
|
||||
Ok(test_examples(Exit {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||
@ -72,11 +72,12 @@ async fn first(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::First;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(First {})
|
||||
Ok(test_examples(First {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::evaluate::evaluate_baseline_expr;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_protocol::{ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
use std::borrow::Borrow;
|
||||
|
||||
@ -54,7 +54,7 @@ async fn format_command(
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = Arc::new(registry.clone());
|
||||
let scope = Arc::new(args.call_info.scope.clone());
|
||||
let scope = args.call_info.scope.clone();
|
||||
let (FormatArgs { pattern }, input) = args.process(®istry).await?;
|
||||
|
||||
let format_pattern = format(&pattern);
|
||||
@ -83,9 +83,7 @@ async fn format_command(
|
||||
let result = evaluate_baseline_expr(
|
||||
&full_column_path.0,
|
||||
®istry,
|
||||
&value,
|
||||
&scope.vars,
|
||||
&scope.env,
|
||||
Scope::append_it(scope.clone(), value.clone()),
|
||||
)
|
||||
.await;
|
||||
|
||||
@ -153,11 +151,12 @@ fn format(input: &str) -> Vec<FormatCommand> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Format;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Format {})
|
||||
Ok(test_examples(Format {})?)
|
||||
}
|
||||
}
|
||||
|
@ -35,11 +35,12 @@ impl WholeStreamCommand for From {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::From;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(From {})
|
||||
Ok(test_examples(From {})?)
|
||||
}
|
||||
}
|
||||
|
@ -109,11 +109,12 @@ async fn from_csv(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromCSV;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromCSV {})
|
||||
Ok(test_examples(FromCSV {})?)
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ fn from_delimited_string_to_value(
|
||||
.delimiter(separator as u8)
|
||||
.from_reader(s.as_bytes());
|
||||
let tag = tag.into();
|
||||
let span = tag.span;
|
||||
|
||||
let headers = if headerless {
|
||||
(1..=reader.headers()?.len())
|
||||
@ -30,7 +31,10 @@ fn from_delimited_string_to_value(
|
||||
if let Ok(i) = value.parse::<i64>() {
|
||||
tagged_row.insert_value(header, UntaggedValue::int(i).into_value(&tag))
|
||||
} else if let Ok(f) = value.parse::<f64>() {
|
||||
tagged_row.insert_value(header, UntaggedValue::decimal(f).into_value(&tag))
|
||||
tagged_row.insert_value(
|
||||
header,
|
||||
UntaggedValue::decimal_from_float(f, span).into_value(&tag),
|
||||
)
|
||||
} else {
|
||||
tagged_row.insert_value(header, UntaggedValue::string(value).into_value(&tag))
|
||||
}
|
||||
|
@ -130,11 +130,12 @@ async fn from_eml(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromEML;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromEML {})
|
||||
Ok(test_examples(FromEML {})?)
|
||||
}
|
||||
}
|
||||
|
@ -249,11 +249,12 @@ fn params_to_value(params: Vec<(String, Vec<String>)>, tag: Tag) -> Value {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromIcs;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromIcs {})
|
||||
Ok(test_examples(FromIcs {})?)
|
||||
}
|
||||
}
|
||||
|
@ -95,11 +95,12 @@ async fn from_ini(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromINI;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromINI {})
|
||||
Ok(test_examples(FromINI {})?)
|
||||
}
|
||||
}
|
||||
|
@ -39,11 +39,12 @@ impl WholeStreamCommand for FromJSON {
|
||||
|
||||
fn convert_json_value_to_nu_value(v: &serde_hjson::Value, tag: impl Into<Tag>) -> Value {
|
||||
let tag = tag.into();
|
||||
let span = tag.span;
|
||||
|
||||
match v {
|
||||
serde_hjson::Value::Null => UntaggedValue::Primitive(Primitive::Nothing).into_value(&tag),
|
||||
serde_hjson::Value::Bool(b) => UntaggedValue::boolean(*b).into_value(&tag),
|
||||
serde_hjson::Value::F64(n) => UntaggedValue::decimal(*n).into_value(&tag),
|
||||
serde_hjson::Value::F64(n) => UntaggedValue::decimal_from_float(*n, span).into_value(&tag),
|
||||
serde_hjson::Value::U64(n) => UntaggedValue::int(*n).into_value(&tag),
|
||||
serde_hjson::Value::I64(n) => UntaggedValue::int(*n).into_value(&tag),
|
||||
serde_hjson::Value::String(s) => {
|
||||
@ -143,11 +144,12 @@ async fn from_json(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromJSON;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromJSON {})
|
||||
Ok(test_examples(FromJSON {})?)
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ async fn from_ods(
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let span = tag.span;
|
||||
let registry = registry.clone();
|
||||
|
||||
let (
|
||||
@ -73,7 +74,7 @@ async fn from_ods(
|
||||
let value = match cell {
|
||||
DataType::Empty => UntaggedValue::nothing(),
|
||||
DataType::String(s) => UntaggedValue::string(s),
|
||||
DataType::Float(f) => UntaggedValue::decimal(*f),
|
||||
DataType::Float(f) => UntaggedValue::decimal_from_float(*f, span),
|
||||
DataType::Int(i) => UntaggedValue::int(*i),
|
||||
DataType::Bool(b) => UntaggedValue::boolean(*b),
|
||||
_ => UntaggedValue::nothing(),
|
||||
@ -101,11 +102,12 @@ async fn from_ods(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromODS;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromODS {})
|
||||
Ok(test_examples(FromODS {})?)
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ fn parse_aligned_columns<'a>(
|
||||
.flat_map(|s| find_indices(*s))
|
||||
.collect::<Vec<usize>>();
|
||||
|
||||
indices.sort();
|
||||
indices.sort_unstable();
|
||||
indices.dedup();
|
||||
|
||||
let headers: Vec<(String, usize)> = indices
|
||||
@ -302,7 +302,9 @@ async fn from_ssv(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::*;
|
||||
|
||||
fn owned(x: &str, y: &str) -> (String, String) {
|
||||
(String::from(x), String::from(y))
|
||||
}
|
||||
@ -504,10 +506,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use super::FromSSV;
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromSSV {})
|
||||
Ok(test_examples(FromSSV {})?)
|
||||
}
|
||||
}
|
||||
|
@ -30,11 +30,12 @@ impl WholeStreamCommand for FromTOML {
|
||||
|
||||
pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into<Tag>) -> Value {
|
||||
let tag = tag.into();
|
||||
let span = tag.span;
|
||||
|
||||
match v {
|
||||
toml::Value::Boolean(b) => UntaggedValue::boolean(*b).into_value(tag),
|
||||
toml::Value::Integer(n) => UntaggedValue::int(*n).into_value(tag),
|
||||
toml::Value::Float(n) => UntaggedValue::decimal(*n).into_value(tag),
|
||||
toml::Value::Float(n) => UntaggedValue::decimal_from_float(*n, span).into_value(tag),
|
||||
toml::Value::String(s) => {
|
||||
UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(tag)
|
||||
}
|
||||
@ -100,11 +101,12 @@ pub async fn from_toml(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromTOML;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromTOML {})
|
||||
Ok(test_examples(FromTOML {})?)
|
||||
}
|
||||
}
|
||||
|
@ -52,11 +52,12 @@ async fn from_tsv(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromTSV;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromTSV {})
|
||||
Ok(test_examples(FromTSV {})?)
|
||||
}
|
||||
}
|
||||
|
@ -64,11 +64,12 @@ async fn from_url(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromURL;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromURL {})
|
||||
Ok(test_examples(FromURL {})?)
|
||||
}
|
||||
}
|
||||
|
@ -104,11 +104,12 @@ fn params_to_value(params: Vec<(String, Vec<String>)>, tag: Tag) -> Value {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromVcf;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromVcf {})
|
||||
Ok(test_examples(FromVcf {})?)
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ async fn from_xlsx(
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let span = tag.span;
|
||||
let registry = registry.clone();
|
||||
let (
|
||||
FromXLSXArgs {
|
||||
@ -73,7 +74,7 @@ async fn from_xlsx(
|
||||
let value = match cell {
|
||||
DataType::Empty => UntaggedValue::nothing(),
|
||||
DataType::String(s) => UntaggedValue::string(s),
|
||||
DataType::Float(f) => UntaggedValue::decimal(*f),
|
||||
DataType::Float(f) => UntaggedValue::decimal_from_float(*f, span),
|
||||
DataType::Int(i) => UntaggedValue::int(*i),
|
||||
DataType::Bool(b) => UntaggedValue::boolean(*b),
|
||||
_ => UntaggedValue::nothing(),
|
||||
@ -101,11 +102,12 @@ async fn from_xlsx(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromXLSX;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromXLSX {})
|
||||
Ok(test_examples(FromXLSX {})?)
|
||||
}
|
||||
}
|
||||
|
@ -135,6 +135,7 @@ async fn from_xml(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use crate::commands::from_xml;
|
||||
use indexmap::IndexMap;
|
||||
use nu_protocol::{UntaggedValue, Value};
|
||||
@ -304,10 +305,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use super::FromXML;
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromXML {})
|
||||
Ok(test_examples(FromXML {})?)
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ fn convert_yaml_value_to_nu_value(
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
let span = tag.span;
|
||||
|
||||
let err_not_compatible_number = ShellError::labeled_error(
|
||||
"Expected a compatible number",
|
||||
@ -69,10 +70,11 @@ fn convert_yaml_value_to_nu_value(
|
||||
serde_yaml::Value::Number(n) if n.is_i64() => {
|
||||
UntaggedValue::int(n.as_i64().ok_or_else(|| err_not_compatible_number)?).into_value(tag)
|
||||
}
|
||||
serde_yaml::Value::Number(n) if n.is_f64() => {
|
||||
UntaggedValue::decimal(n.as_f64().ok_or_else(|| err_not_compatible_number)?)
|
||||
.into_value(tag)
|
||||
}
|
||||
serde_yaml::Value::Number(n) if n.is_f64() => UntaggedValue::decimal_from_float(
|
||||
n.as_f64().ok_or_else(|| err_not_compatible_number)?,
|
||||
span,
|
||||
)
|
||||
.into_value(tag),
|
||||
serde_yaml::Value::String(s) => UntaggedValue::string(s).into_value(tag),
|
||||
serde_yaml::Value::Sequence(a) => {
|
||||
let result: Result<Vec<Value>, ShellError> = a
|
||||
@ -171,15 +173,16 @@ async fn from_yaml(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::*;
|
||||
use nu_plugin::row;
|
||||
use nu_plugin::test_helpers::value::string;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromYAML {})
|
||||
Ok(test_examples(FromYAML {})?)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -4,8 +4,8 @@ use indexmap::set::IndexSet;
|
||||
use log::trace;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
did_you_mean, ColumnPath, PathMember, Primitive, ReturnSuccess, Signature, SyntaxShape,
|
||||
UnspannedPathMember, UntaggedValue, Value,
|
||||
did_you_mean, ColumnPath, Dictionary, PathMember, Primitive, ReturnSuccess, Signature,
|
||||
SyntaxShape, UnspannedPathMember, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::HasFallibleSpan;
|
||||
use nu_value_ext::get_data_by_column_path;
|
||||
@ -58,206 +58,221 @@ impl WholeStreamCommand for Get {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_column_path(path: &ColumnPath, obj: &Value) -> Result<Value, ShellError> {
|
||||
let fields = path.clone();
|
||||
|
||||
get_data_by_column_path(
|
||||
obj,
|
||||
path,
|
||||
Box::new(move |(obj_source, column_path_tried, error)| {
|
||||
let path_members_span = fields.maybe_span().unwrap_or_else(Span::unknown);
|
||||
|
||||
match &obj_source.value {
|
||||
UntaggedValue::Table(rows) => match column_path_tried {
|
||||
PathMember {
|
||||
unspanned: UnspannedPathMember::String(column),
|
||||
..
|
||||
} => {
|
||||
let primary_label = format!("There isn't a column named '{}'", &column);
|
||||
|
||||
let suggestions: IndexSet<_> = rows
|
||||
.iter()
|
||||
.filter_map(|r| did_you_mean(&r, &column_path_tried))
|
||||
.map(|s| s[0].1.to_owned())
|
||||
.collect();
|
||||
let mut existing_columns: IndexSet<_> = IndexSet::default();
|
||||
let mut names: Vec<String> = vec![];
|
||||
|
||||
for row in rows {
|
||||
for field in row.data_descriptors() {
|
||||
if !existing_columns.contains(&field[..]) {
|
||||
existing_columns.insert(field.clone());
|
||||
names.push(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if names.is_empty() {
|
||||
return ShellError::labeled_error_with_secondary(
|
||||
"Unknown column",
|
||||
primary_label,
|
||||
column_path_tried.span,
|
||||
"Appears to contain rows. Try indexing instead.",
|
||||
column_path_tried.span.since(path_members_span),
|
||||
);
|
||||
} else {
|
||||
return ShellError::labeled_error_with_secondary(
|
||||
"Unknown column",
|
||||
primary_label,
|
||||
column_path_tried.span,
|
||||
format!(
|
||||
"Perhaps you meant '{}'? Columns available: {}",
|
||||
suggestions
|
||||
.iter()
|
||||
.map(|x| x.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join(","),
|
||||
names.join(",")
|
||||
),
|
||||
column_path_tried.span.since(path_members_span),
|
||||
);
|
||||
};
|
||||
}
|
||||
PathMember {
|
||||
unspanned: UnspannedPathMember::Int(idx),
|
||||
..
|
||||
} => {
|
||||
let total = rows.len();
|
||||
|
||||
let secondary_label = if total == 1 {
|
||||
"The table only has 1 row".to_owned()
|
||||
} else {
|
||||
format!("The table only has {} rows (0 to {})", total, total - 1)
|
||||
};
|
||||
|
||||
return ShellError::labeled_error_with_secondary(
|
||||
"Row not found",
|
||||
format!("There isn't a row indexed at {}", idx),
|
||||
column_path_tried.span,
|
||||
secondary_label,
|
||||
column_path_tried.span.since(path_members_span),
|
||||
);
|
||||
}
|
||||
},
|
||||
UntaggedValue::Row(columns) => match column_path_tried {
|
||||
PathMember {
|
||||
unspanned: UnspannedPathMember::String(column),
|
||||
..
|
||||
} => {
|
||||
let primary_label = format!("There isn't a column named '{}'", &column);
|
||||
|
||||
if let Some(suggestions) = did_you_mean(&obj_source, column_path_tried) {
|
||||
return ShellError::labeled_error_with_secondary(
|
||||
"Unknown column",
|
||||
primary_label,
|
||||
column_path_tried.span,
|
||||
format!(
|
||||
"Perhaps you meant '{}'? Columns available: {}",
|
||||
suggestions[0].1,
|
||||
&obj_source.data_descriptors().join(",")
|
||||
),
|
||||
column_path_tried.span.since(path_members_span),
|
||||
);
|
||||
}
|
||||
}
|
||||
PathMember {
|
||||
unspanned: UnspannedPathMember::Int(idx),
|
||||
..
|
||||
} => {
|
||||
return ShellError::labeled_error_with_secondary(
|
||||
"No rows available",
|
||||
format!("A row at '{}' can't be indexed.", &idx),
|
||||
column_path_tried.span,
|
||||
format!(
|
||||
"Appears to contain columns. Columns available: {}",
|
||||
columns.keys().join(",")
|
||||
),
|
||||
column_path_tried.span.since(path_members_span),
|
||||
)
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(suggestions) = did_you_mean(&obj_source, column_path_tried) {
|
||||
return ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
format!("did you mean '{}'?", suggestions[0].1),
|
||||
column_path_tried.span.since(path_members_span),
|
||||
);
|
||||
}
|
||||
|
||||
error
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let (GetArgs { rest: mut fields }, mut input) = args.process(®istry).await?;
|
||||
if fields.is_empty() {
|
||||
let (GetArgs { rest: column_paths }, mut input) = args.process(®istry).await?;
|
||||
if column_paths.is_empty() {
|
||||
let vec = input.drain_vec().await;
|
||||
|
||||
let descs = nu_protocol::merge_descriptors(&vec);
|
||||
|
||||
Ok(futures::stream::iter(descs.into_iter().map(ReturnSuccess::value)).to_output_stream())
|
||||
} else {
|
||||
let member = fields.remove(0);
|
||||
trace!("get {:?} {:?}", member, fields);
|
||||
|
||||
Ok(input
|
||||
trace!("get {:?}", column_paths);
|
||||
let output_stream = input
|
||||
.map(move |item| {
|
||||
let member = vec![member.clone()];
|
||||
|
||||
let column_paths = vec![&member, &fields]
|
||||
.into_iter()
|
||||
let output = column_paths
|
||||
.iter()
|
||||
.map(move |path| get_output(&item, path))
|
||||
.flatten()
|
||||
.collect::<Vec<&ColumnPath>>();
|
||||
|
||||
let mut output = vec![];
|
||||
for path in column_paths {
|
||||
let res = get_column_path(&path, &item);
|
||||
|
||||
match res {
|
||||
Ok(got) => match got {
|
||||
Value {
|
||||
value: UntaggedValue::Table(rows),
|
||||
..
|
||||
} => {
|
||||
for item in rows {
|
||||
output.push(ReturnSuccess::value(item.clone()));
|
||||
}
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Nothing),
|
||||
..
|
||||
} => {}
|
||||
other => output.push(ReturnSuccess::value(other.clone())),
|
||||
},
|
||||
Err(reason) => output.push(ReturnSuccess::value(
|
||||
UntaggedValue::Error(reason).into_untagged_value(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
.collect::<Vec<_>>();
|
||||
futures::stream::iter(output)
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
.to_output_stream();
|
||||
Ok(output_stream)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_output(item: &Value, path: &ColumnPath) -> Vec<Result<ReturnSuccess, ShellError>> {
|
||||
match get_column_path(path, item) {
|
||||
Ok(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Nothing),
|
||||
..
|
||||
}) => vec![],
|
||||
Ok(Value {
|
||||
value: UntaggedValue::Table(rows),
|
||||
..
|
||||
}) => rows.into_iter().map(ReturnSuccess::value).collect(),
|
||||
Ok(other) => vec![ReturnSuccess::value(other)],
|
||||
Err(reason) => vec![ReturnSuccess::value(
|
||||
UntaggedValue::Error(reason).into_untagged_value(),
|
||||
)],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_column_path(path: &ColumnPath, obj: &Value) -> Result<Value, ShellError> {
|
||||
get_data_by_column_path(obj, path, move |obj_source, column_path_tried, error| {
|
||||
let path_members_span = path.maybe_span().unwrap_or_else(Span::unknown);
|
||||
|
||||
match &obj_source.value {
|
||||
UntaggedValue::Table(rows) => {
|
||||
return get_column_path_from_table_error(
|
||||
rows,
|
||||
column_path_tried,
|
||||
&path_members_span,
|
||||
);
|
||||
}
|
||||
UntaggedValue::Row(columns) => {
|
||||
if let Some(error) = get_column_from_row_error(
|
||||
columns,
|
||||
column_path_tried,
|
||||
&path_members_span,
|
||||
obj_source,
|
||||
) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(suggestions) = did_you_mean(&obj_source, column_path_tried.as_string()) {
|
||||
ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
format!("did you mean '{}'?", suggestions[0]),
|
||||
column_path_tried.span.since(path_members_span),
|
||||
)
|
||||
} else {
|
||||
error
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_column_path_from_table_error(
|
||||
rows: &[Value],
|
||||
column_path_tried: &PathMember,
|
||||
path_members_span: &Span,
|
||||
) -> ShellError {
|
||||
match column_path_tried {
|
||||
PathMember {
|
||||
unspanned: UnspannedPathMember::String(column),
|
||||
..
|
||||
} => {
|
||||
let primary_label = format!("There isn't a column named '{}'", &column);
|
||||
|
||||
let suggestions: IndexSet<_> = rows
|
||||
.iter()
|
||||
.filter_map(|r| did_you_mean(&r, column_path_tried.as_string()))
|
||||
.map(|s| s[0].to_owned())
|
||||
.collect();
|
||||
let mut existing_columns: IndexSet<_> = IndexSet::default();
|
||||
let mut names: Vec<String> = vec![];
|
||||
|
||||
for row in rows {
|
||||
for field in row.data_descriptors() {
|
||||
if !existing_columns.contains(&field[..]) {
|
||||
existing_columns.insert(field.clone());
|
||||
names.push(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if names.is_empty() {
|
||||
ShellError::labeled_error_with_secondary(
|
||||
"Unknown column",
|
||||
primary_label,
|
||||
column_path_tried.span,
|
||||
"Appears to contain rows. Try indexing instead.",
|
||||
column_path_tried.span.since(path_members_span),
|
||||
)
|
||||
} else {
|
||||
ShellError::labeled_error_with_secondary(
|
||||
"Unknown column",
|
||||
primary_label,
|
||||
column_path_tried.span,
|
||||
format!(
|
||||
"Perhaps you meant '{}'? Columns available: {}",
|
||||
suggestions
|
||||
.iter()
|
||||
.map(|x| x.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join(","),
|
||||
names.join(", ")
|
||||
),
|
||||
column_path_tried.span.since(path_members_span),
|
||||
)
|
||||
}
|
||||
}
|
||||
PathMember {
|
||||
unspanned: UnspannedPathMember::Int(idx),
|
||||
..
|
||||
} => {
|
||||
let total = rows.len();
|
||||
|
||||
let secondary_label = if total == 1 {
|
||||
"The table only has 1 row".to_owned()
|
||||
} else {
|
||||
format!("The table only has {} rows (0 to {})", total, total - 1)
|
||||
};
|
||||
|
||||
ShellError::labeled_error_with_secondary(
|
||||
"Row not found",
|
||||
format!("There isn't a row indexed at {}", idx),
|
||||
column_path_tried.span,
|
||||
secondary_label,
|
||||
column_path_tried.span.since(path_members_span),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_column_from_row_error(
|
||||
columns: &Dictionary,
|
||||
column_path_tried: &PathMember,
|
||||
path_members_span: &Span,
|
||||
obj_source: &Value,
|
||||
) -> Option<ShellError> {
|
||||
match column_path_tried {
|
||||
PathMember {
|
||||
unspanned: UnspannedPathMember::String(column),
|
||||
..
|
||||
} => {
|
||||
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(
|
||||
"Unknown column",
|
||||
primary_label,
|
||||
column_path_tried.span,
|
||||
format!(
|
||||
"Perhaps you meant '{}'? Columns available: {}",
|
||||
suggestions[0],
|
||||
&obj_source.data_descriptors().join(", ")
|
||||
),
|
||||
column_path_tried.span.since(path_members_span),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
PathMember {
|
||||
unspanned: UnspannedPathMember::Int(idx),
|
||||
..
|
||||
} => Some(ShellError::labeled_error_with_secondary(
|
||||
"No rows available",
|
||||
format!("A row at '{}' can't be indexed.", &idx),
|
||||
column_path_tried.span,
|
||||
format!(
|
||||
"Appears to contain columns. Columns available: {}",
|
||||
columns.keys().join(", ")
|
||||
),
|
||||
column_path_tried.span.since(path_members_span),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Get;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Get {})
|
||||
Ok(test_examples(Get {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,21 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use crate::utils::suggestions::suggestions;
|
||||
use indexmap::indexmap;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use nu_value_ext::as_string;
|
||||
|
||||
pub struct GroupBy;
|
||||
pub struct Command;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GroupByArgs {
|
||||
pub struct Arguments {
|
||||
grouper: Option<Value>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for GroupBy {
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"group-by"
|
||||
}
|
||||
@ -28,7 +29,7 @@ impl WholeStreamCommand for GroupBy {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"create a new table grouped."
|
||||
"Create a new table grouped."
|
||||
}
|
||||
|
||||
async fn run(
|
||||
@ -39,17 +40,45 @@ impl WholeStreamCommand for GroupBy {
|
||||
group_by(args, registry).await
|
||||
}
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
use nu_data::value::date_naive_from_str as date;
|
||||
|
||||
vec![
|
||||
Example {
|
||||
description: "group items by column named \"type\"",
|
||||
example: r#"ls | group-by type"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "blocks can be used for generating a grouping key (same as above)",
|
||||
example: r#"ls | group-by { get type }"#,
|
||||
result: None,
|
||||
result: Some(vec![UntaggedValue::row(indexmap! {
|
||||
"File".to_string() => UntaggedValue::Table(vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"modified".to_string() => date("2019-07-23".tagged_unknown()).unwrap().into(),
|
||||
"name".to_string() => UntaggedValue::string("Andrés.txt").into(),
|
||||
"type".to_string() => UntaggedValue::string("File").into(),
|
||||
"chickens".to_string() => UntaggedValue::int(10).into(),
|
||||
}).into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"modified".to_string() => date("2019-09-24".tagged_unknown()).unwrap().into(),
|
||||
"name".to_string() => UntaggedValue::string("Andrés.txt").into(),
|
||||
"type".to_string() => UntaggedValue::string("File").into(),
|
||||
"chickens".to_string() => UntaggedValue::int(20).into(),
|
||||
}).into(),
|
||||
]).into(),
|
||||
"Dir".to_string() => UntaggedValue::Table(vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"modified".to_string() => date("2019-07-23".tagged_unknown()).unwrap().into(),
|
||||
"name".to_string() => UntaggedValue::string("Jonathan").into(),
|
||||
"type".to_string() => UntaggedValue::string("Dir").into(),
|
||||
"chickens".to_string() => UntaggedValue::int(5).into(),
|
||||
}).into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"modified".to_string() => date("2019-09-24".tagged_unknown()).unwrap().into(),
|
||||
"name".to_string() => UntaggedValue::string("Yehuda").into(),
|
||||
"type".to_string() => UntaggedValue::string("Dir").into(),
|
||||
"chickens".to_string() => UntaggedValue::int(4).into(),
|
||||
}).into(),
|
||||
]).into(),
|
||||
})
|
||||
.into()]),
|
||||
},
|
||||
Example {
|
||||
description: "you can also group by raw values by leaving out the argument",
|
||||
@ -74,10 +103,26 @@ impl WholeStreamCommand for GroupBy {
|
||||
.into()]),
|
||||
},
|
||||
Example {
|
||||
description: "write pipelines for a more involved grouping key",
|
||||
example:
|
||||
"echo [1 3 1 3 2 1 1] | group-by { echo `({{$it}} - 1) % 3` | calc | str from }",
|
||||
result: None,
|
||||
description:
|
||||
"use the block form to generate a grouping key when each row gets processed",
|
||||
example: "echo [1 3 1 3 2 1 1] | group-by { = ($it - 1) mod 3 }",
|
||||
result: Some(vec![UntaggedValue::row(indexmap! {
|
||||
"0".to_string() => UntaggedValue::Table(vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
|
||||
]).into(),
|
||||
"2".to_string() => UntaggedValue::Table(vec![
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
]).into(),
|
||||
"1".to_string() => UntaggedValue::Table(vec![
|
||||
UntaggedValue::int(2).into(),
|
||||
]).into(),
|
||||
})
|
||||
.into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -95,9 +140,9 @@ pub async fn group_by(
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let registry = registry.clone();
|
||||
let head = Arc::new(args.call_info.args.head.clone());
|
||||
let scope = Arc::new(args.call_info.scope.clone());
|
||||
let context = Arc::new(Context::from_raw(&args, ®istry));
|
||||
let (GroupByArgs { grouper }, input) = args.process(®istry).await?;
|
||||
let scope = args.call_info.scope.clone();
|
||||
let context = Arc::new(EvaluationContext::from_raw(&args, ®istry));
|
||||
let (Arguments { grouper }, input) = args.process(®istry).await?;
|
||||
|
||||
let values: Vec<Value> = input.collect().await;
|
||||
let mut keys: Vec<Result<String, ShellError>> = vec![];
|
||||
@ -167,6 +212,14 @@ pub async fn group_by(
|
||||
));
|
||||
}
|
||||
|
||||
let first = values[0].clone();
|
||||
|
||||
let name = if first.tag.anchor().is_some() {
|
||||
first.tag
|
||||
} else {
|
||||
name
|
||||
};
|
||||
|
||||
let values = UntaggedValue::table(&values).into_value(&name);
|
||||
|
||||
let group_value = match group_strategy {
|
||||
@ -179,39 +232,14 @@ pub async fn group_by(
|
||||
None => as_string(row),
|
||||
});
|
||||
|
||||
nu_data::utils::group(&values, &Some(block), &name)
|
||||
nu_data::utils::group(&values, &Some(block), name)
|
||||
}
|
||||
Grouper::ByColumn(column_name) => group(&column_name, &values, name),
|
||||
Grouper::ByColumn(column_name) => group(&column_name, &values, &name),
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(group_value?)))
|
||||
}
|
||||
|
||||
pub fn suggestions(tried: Tagged<&str>, for_value: &Value) -> ShellError {
|
||||
let possibilities = for_value.data_descriptors();
|
||||
|
||||
let mut possible_matches: Vec<_> = possibilities
|
||||
.iter()
|
||||
.map(|x| (natural::distance::levenshtein_distance(x, &tried), x))
|
||||
.collect();
|
||||
|
||||
possible_matches.sort();
|
||||
|
||||
if !possible_matches.is_empty() {
|
||||
ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
format!("did you mean '{}'?", possible_matches[0].1),
|
||||
tried.tag(),
|
||||
)
|
||||
} else {
|
||||
ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
"row does not contain this column",
|
||||
tried.tag(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn group(
|
||||
column_name: &Option<Tagged<String>>,
|
||||
values: &Value,
|
||||
@ -311,12 +339,4 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use super::GroupBy;
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(GroupBy {})
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use crate::utils::suggestions::suggestions;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
@ -137,39 +138,15 @@ pub async fn group_by_date(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suggestions(tried: Tagged<&str>, for_value: &Value) -> ShellError {
|
||||
let possibilities = for_value.data_descriptors();
|
||||
|
||||
let mut possible_matches: Vec<_> = possibilities
|
||||
.iter()
|
||||
.map(|x| (natural::distance::levenshtein_distance(x, &tried), x))
|
||||
.collect();
|
||||
|
||||
possible_matches.sort();
|
||||
|
||||
if !possible_matches.is_empty() {
|
||||
ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
format!("did you mean '{}'?", possible_matches[0].1),
|
||||
tried.tag(),
|
||||
)
|
||||
} else {
|
||||
ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
"row does not contain this column",
|
||||
tried.tag(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::GroupByDate;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(GroupByDate {})
|
||||
Ok(test_examples(GroupByDate {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use futures::stream::StreamExt;
|
||||
use indexmap::IndexMap;
|
||||
@ -116,11 +116,12 @@ pub async fn headers(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Headers;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Headers {})
|
||||
Ok(test_examples(Headers {})?)
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use nu_data::command::signature_dict;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_source::{SpannedItem, Tagged};
|
||||
use nu_value_ext::get_data_by_key;
|
||||
use nu_value_ext::ValueExt;
|
||||
|
||||
pub struct Help;
|
||||
|
||||
@ -63,54 +63,115 @@ async fn help(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
||||
let mut sorted_names = registry.names();
|
||||
sorted_names.sort();
|
||||
|
||||
Ok(
|
||||
futures::stream::iter(sorted_names.into_iter().filter_map(move |cmd| {
|
||||
// If it's a subcommand, don't list it during the commands list
|
||||
if cmd.contains(' ') {
|
||||
return None;
|
||||
}
|
||||
let mut short_desc = TaggedDictBuilder::new(name.clone());
|
||||
let document_tag = rest[0].tag.clone();
|
||||
let value = command_dict(
|
||||
match registry.get_command(&cmd).ok_or_else(|| {
|
||||
let (mut subcommand_names, command_names) = sorted_names
|
||||
.into_iter()
|
||||
// Internal only commands shouldn't be displayed
|
||||
.filter(|cmd_name| {
|
||||
registry
|
||||
.get_command(&cmd_name)
|
||||
.filter(|command| !command.is_internal())
|
||||
.is_some()
|
||||
})
|
||||
.partition::<Vec<_>, _>(|cmd_name| cmd_name.contains(' '));
|
||||
|
||||
fn process_name(
|
||||
dict: &mut TaggedDictBuilder,
|
||||
cmd_name: &str,
|
||||
registry: CommandRegistry,
|
||||
rest: Vec<Tagged<String>>,
|
||||
name: Tag,
|
||||
) -> Result<(), ShellError> {
|
||||
let document_tag = rest[0].tag.clone();
|
||||
let value = command_dict(
|
||||
registry.get_command(&cmd_name).ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
format!("Could not load {}", cmd_name),
|
||||
"could not load command",
|
||||
document_tag,
|
||||
)
|
||||
})?,
|
||||
name,
|
||||
);
|
||||
|
||||
dict.insert_untagged("name", cmd_name);
|
||||
dict.insert_untagged(
|
||||
"description",
|
||||
value
|
||||
.get_data_by_key("usage".spanned_unknown())
|
||||
.ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
format!("Could not load {}", cmd),
|
||||
"could not load command",
|
||||
document_tag,
|
||||
"Expected a usage key",
|
||||
"expected a 'usage' key",
|
||||
&value.tag,
|
||||
)
|
||||
}) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => return Some(Err(err)),
|
||||
},
|
||||
name.clone(),
|
||||
);
|
||||
})?
|
||||
.as_string()?,
|
||||
);
|
||||
|
||||
short_desc.insert_untagged("name", cmd);
|
||||
short_desc.insert_untagged(
|
||||
"description",
|
||||
match match get_data_by_key(&value, "usage".spanned_unknown()).ok_or_else(
|
||||
|| {
|
||||
ShellError::labeled_error(
|
||||
"Expected a usage key",
|
||||
"expected a 'usage' key",
|
||||
&value.tag,
|
||||
)
|
||||
},
|
||||
) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => return Some(Err(err)),
|
||||
}
|
||||
.as_string()
|
||||
{
|
||||
Ok(ok) => ok,
|
||||
Err(err) => return Some(Err(err)),
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Some(ReturnSuccess::value(short_desc.into_value()))
|
||||
}))
|
||||
.to_output_stream(),
|
||||
)
|
||||
fn make_subcommands_table(
|
||||
subcommand_names: &mut Vec<String>,
|
||||
cmd_name: &str,
|
||||
registry: CommandRegistry,
|
||||
rest: Vec<Tagged<String>>,
|
||||
name: Tag,
|
||||
) -> Result<Value, ShellError> {
|
||||
let (matching, not_matching) =
|
||||
subcommand_names.drain(..).partition(|subcommand_name| {
|
||||
subcommand_name.starts_with(&format!("{} ", cmd_name))
|
||||
});
|
||||
*subcommand_names = not_matching;
|
||||
Ok(if !matching.is_empty() {
|
||||
UntaggedValue::table(
|
||||
&(matching
|
||||
.into_iter()
|
||||
.map(|cmd_name: String| -> Result<_, ShellError> {
|
||||
let mut short_desc = TaggedDictBuilder::new(name.clone());
|
||||
process_name(
|
||||
&mut short_desc,
|
||||
&cmd_name,
|
||||
registry.clone(),
|
||||
rest.clone(),
|
||||
name.clone(),
|
||||
)?;
|
||||
Ok(short_desc.into_value())
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?[..]),
|
||||
)
|
||||
.into_value(name)
|
||||
} else {
|
||||
UntaggedValue::nothing().into_value(name)
|
||||
})
|
||||
}
|
||||
|
||||
let iterator =
|
||||
command_names
|
||||
.into_iter()
|
||||
.map(move |cmd_name| -> Result<_, ShellError> {
|
||||
let mut short_desc = TaggedDictBuilder::new(name.clone());
|
||||
process_name(
|
||||
&mut short_desc,
|
||||
&cmd_name,
|
||||
registry.clone(),
|
||||
rest.clone(),
|
||||
name.clone(),
|
||||
)?;
|
||||
short_desc.insert_value(
|
||||
"subcommands",
|
||||
make_subcommands_table(
|
||||
&mut subcommand_names,
|
||||
&cmd_name,
|
||||
registry.clone(),
|
||||
rest.clone(),
|
||||
name.clone(),
|
||||
)?,
|
||||
);
|
||||
ReturnSuccess::value(short_desc.into_value())
|
||||
});
|
||||
|
||||
Ok(futures::stream::iter(iterator).to_output_stream())
|
||||
} else if rest[0].item == "generate_docs" {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(generate_docs(
|
||||
®istry,
|
||||
@ -174,11 +235,12 @@ pub fn get_help(cmd: &dyn WholeStreamCommand, registry: &CommandRegistry) -> Str
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Help;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Help {})
|
||||
Ok(test_examples(Help {})?)
|
||||
}
|
||||
}
|
||||
|
@ -112,8 +112,9 @@ pub async fn histogram(
|
||||
nu_data::utils::Operation {
|
||||
grouper: Some(Box::new(move |_, _| Ok(String::from("frequencies")))),
|
||||
splitter: Some(splitter(column_grouper)),
|
||||
format: None,
|
||||
format: &None,
|
||||
eval: &evaluate_with,
|
||||
reduction: &nu_data::utils::Reduction::Count,
|
||||
},
|
||||
&name,
|
||||
)?;
|
||||
@ -123,17 +124,33 @@ pub async fn histogram(
|
||||
|
||||
Ok(futures::stream::iter(
|
||||
results
|
||||
.percentages
|
||||
.data
|
||||
.table_entries()
|
||||
.map(move |value| {
|
||||
let values = value.table_entries().cloned().collect::<Vec<_>>();
|
||||
let occurrences = values.len();
|
||||
|
||||
(occurrences, values[occurrences - 1].clone())
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.map(move |(occurrences, value)| {
|
||||
.zip(
|
||||
results
|
||||
.percentages
|
||||
.table_entries()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter(),
|
||||
)
|
||||
.map(move |(counts, percentages)| {
|
||||
let percentage = percentages
|
||||
.table_entries()
|
||||
.cloned()
|
||||
.last()
|
||||
.unwrap_or_else(|| {
|
||||
UntaggedValue::decimal_from_float(0.0, name.span).into_value(&name)
|
||||
});
|
||||
let value = counts
|
||||
.table_entries()
|
||||
.cloned()
|
||||
.last()
|
||||
.unwrap_or_else(|| UntaggedValue::int(0).into_value(&name));
|
||||
|
||||
let mut fact = TaggedDictBuilder::new(&name);
|
||||
let column_value = labels
|
||||
.get(idx)
|
||||
@ -147,19 +164,19 @@ pub async fn histogram(
|
||||
.clone();
|
||||
|
||||
fact.insert_value(&column.item, column_value);
|
||||
fact.insert_untagged("occurrences", UntaggedValue::int(occurrences));
|
||||
fact.insert_untagged("count", value);
|
||||
|
||||
let percentage = format!(
|
||||
let fmt_percentage = format!(
|
||||
"{}%",
|
||||
// Some(2) < the number of digits
|
||||
// true < group the digits
|
||||
crate::commands::str_::from::action(&value, &name, Some(2), true)?
|
||||
crate::commands::str_::from::action(&percentage, &name, Some(2), true)?
|
||||
.as_string()?
|
||||
);
|
||||
fact.insert_untagged("percentage", UntaggedValue::string(percentage));
|
||||
fact.insert_untagged("percentage", UntaggedValue::string(fmt_percentage));
|
||||
|
||||
let string = std::iter::repeat("*")
|
||||
.take(value.as_u64().map_err(|_| {
|
||||
.take(percentage.as_u64().map_err(|_| {
|
||||
ShellError::labeled_error("expected a number", "expected a number", &name)
|
||||
})? as usize)
|
||||
.collect::<String>();
|
||||
@ -178,11 +195,7 @@ fn evaluator(by: ColumnPath) -> Box<dyn Fn(usize, &Value) -> Result<Value, Shell
|
||||
Box::new(move |_: usize, value: &Value| {
|
||||
let path = by.clone();
|
||||
|
||||
let eval = nu_value_ext::get_data_by_column_path(
|
||||
value,
|
||||
&path,
|
||||
Box::new(move |(_, _, error)| error),
|
||||
);
|
||||
let eval = nu_value_ext::get_data_by_column_path(value, &path, move |_, _, error| error);
|
||||
|
||||
match eval {
|
||||
Ok(with_value) => Ok(with_value),
|
||||
@ -214,11 +227,12 @@ fn splitter(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Histogram;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Histogram {})
|
||||
Ok(test_examples(Histogram {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_data::config::NuConfig;
|
||||
use nu_data::config::{Conf, NuConfig};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use std::fs::File;
|
||||
@ -9,9 +9,7 @@ use std::path::PathBuf;
|
||||
|
||||
const DEFAULT_LOCATION: &str = "history.txt";
|
||||
|
||||
pub fn history_path(config: &NuConfig) -> PathBuf {
|
||||
let vars = config.vars.lock();
|
||||
|
||||
pub fn history_path(config: &dyn Conf) -> PathBuf {
|
||||
let default_path = nu_data::config::user_data()
|
||||
.map(|mut p| {
|
||||
p.push(DEFAULT_LOCATION);
|
||||
@ -19,7 +17,8 @@ pub fn history_path(config: &NuConfig) -> PathBuf {
|
||||
})
|
||||
.unwrap_or_else(|_| PathBuf::from(DEFAULT_LOCATION));
|
||||
|
||||
vars.get("history-path")
|
||||
config
|
||||
.var("history-path")
|
||||
.map_or(default_path.clone(), |custom_path| {
|
||||
match custom_path.as_string() {
|
||||
Ok(path) => PathBuf::from(path),
|
||||
@ -54,7 +53,7 @@ impl WholeStreamCommand for History {
|
||||
}
|
||||
|
||||
fn history(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let config = NuConfig::new();
|
||||
let config: Box<dyn Conf> = Box::new(NuConfig::new());
|
||||
let tag = args.call_info.name_tag;
|
||||
let path = history_path(&config);
|
||||
let file = File::open(path);
|
||||
@ -80,11 +79,12 @@ fn history(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStrea
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::History;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(History {})
|
||||
Ok(test_examples(History {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::evaluate::evaluate_baseline_expr;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::Block, hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_protocol::{
|
||||
hir::Block, hir::ClassifiedCommand, Scope, Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
pub struct If;
|
||||
|
||||
@ -72,9 +74,9 @@ async fn if_command(
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = Arc::new(registry.clone());
|
||||
let scope = Arc::new(raw_args.call_info.scope.clone());
|
||||
let scope = raw_args.call_info.scope.clone();
|
||||
let tag = raw_args.call_info.name_tag.clone();
|
||||
let context = Arc::new(Context::from_raw(&raw_args, ®istry));
|
||||
let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry));
|
||||
|
||||
let (
|
||||
IfArgs {
|
||||
@ -119,14 +121,12 @@ async fn if_command(
|
||||
let then_case = then_case.clone();
|
||||
let else_case = else_case.clone();
|
||||
let registry = registry.clone();
|
||||
let scope = scope.clone();
|
||||
let scope = Scope::append_it(scope.clone(), input);
|
||||
let mut context = context.clone();
|
||||
|
||||
async move {
|
||||
//FIXME: should we use the scope that's brought in as well?
|
||||
let condition =
|
||||
evaluate_baseline_expr(&condition, &*registry, &input, &scope.vars, &scope.env)
|
||||
.await;
|
||||
let condition = evaluate_baseline_expr(&condition, &*registry, scope.clone()).await;
|
||||
|
||||
match condition {
|
||||
Ok(condition) => match condition.as_bool() {
|
||||
@ -136,9 +136,7 @@ async fn if_command(
|
||||
&then_case,
|
||||
Arc::make_mut(&mut context),
|
||||
InputStream::empty(),
|
||||
&input,
|
||||
&scope.vars,
|
||||
&scope.env,
|
||||
scope,
|
||||
)
|
||||
.await
|
||||
{
|
||||
@ -151,9 +149,7 @@ async fn if_command(
|
||||
&else_case,
|
||||
Arc::make_mut(&mut context),
|
||||
InputStream::empty(),
|
||||
&input,
|
||||
&scope.vars,
|
||||
&scope.env,
|
||||
scope,
|
||||
)
|
||||
.await
|
||||
{
|
||||
@ -178,11 +174,12 @@ async fn if_command(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::If;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(If {})
|
||||
Ok(test_examples(If {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
@ -9,16 +9,18 @@ use nu_protocol::{
|
||||
use nu_value_ext::ValueExt;
|
||||
|
||||
use futures::stream::once;
|
||||
pub struct Insert;
|
||||
use indexmap::indexmap;
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct InsertArgs {
|
||||
pub struct Arguments {
|
||||
column: ColumnPath,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Insert {
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"insert"
|
||||
}
|
||||
@ -44,11 +46,33 @@ impl WholeStreamCommand for Insert {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
insert(args, registry).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Insert a column with a value",
|
||||
example: "echo [[author, commits]; ['Andrés', 1]] | insert branches 5",
|
||||
result: Some(vec![UntaggedValue::row(indexmap! {
|
||||
"author".to_string() => Value::from("Andrés"),
|
||||
"commits".to_string() => UntaggedValue::int(1).into(),
|
||||
"branches".to_string() => UntaggedValue::int(5).into(),
|
||||
})
|
||||
.into()]),
|
||||
},Example {
|
||||
description: "Use in block form for more involved insertion logic",
|
||||
example: "echo [[author, lucky_number]; ['Yehuda', 4]] | insert success { = $it.lucky_number * 10 }",
|
||||
result: Some(vec![UntaggedValue::row(indexmap! {
|
||||
"author".to_string() => Value::from("Yehuda"),
|
||||
"lucky_number".to_string() => UntaggedValue::int(4).into(),
|
||||
"success".to_string() => UntaggedValue::int(40).into(),
|
||||
})
|
||||
.into()]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_row(
|
||||
scope: Arc<Scope>,
|
||||
mut context: Arc<Context>,
|
||||
mut context: Arc<EvaluationContext>,
|
||||
input: Value,
|
||||
mut value: Arc<Value>,
|
||||
field: Arc<ColumnPath>,
|
||||
@ -63,15 +87,9 @@ async fn process_row(
|
||||
let for_block = input.clone();
|
||||
let input_stream = once(async { Ok(for_block) }).to_input_stream();
|
||||
|
||||
let result = run_block(
|
||||
&block,
|
||||
Arc::make_mut(&mut context),
|
||||
input_stream,
|
||||
&input,
|
||||
&scope.vars,
|
||||
&scope.env,
|
||||
)
|
||||
.await;
|
||||
let scope = Scope::append_it(scope, input.clone());
|
||||
|
||||
let result = run_block(&block, Arc::make_mut(&mut context), input_stream, scope).await;
|
||||
|
||||
match result {
|
||||
Ok(mut stream) => {
|
||||
@ -85,13 +103,16 @@ async fn process_row(
|
||||
let result = if values.len() == 1 {
|
||||
let value = values
|
||||
.get(0)
|
||||
.ok_or_else(|| ShellError::unexpected("No value to insert with"))?;
|
||||
.ok_or_else(|| ShellError::unexpected("No value to insert with."))?;
|
||||
|
||||
value.clone()
|
||||
Value {
|
||||
value: value.value.clone(),
|
||||
tag: input.tag.clone(),
|
||||
}
|
||||
} else if values.is_empty() {
|
||||
UntaggedValue::nothing().into_untagged_value()
|
||||
UntaggedValue::nothing().into_value(&input.tag)
|
||||
} else {
|
||||
UntaggedValue::table(&values).into_untagged_value()
|
||||
UntaggedValue::table(&values).into_value(&input.tag)
|
||||
};
|
||||
|
||||
match input {
|
||||
@ -118,7 +139,11 @@ async fn process_row(
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Nothing),
|
||||
..
|
||||
} => match scope.it.insert_data_at_column_path(&field, value.clone()) {
|
||||
} => match scope
|
||||
.it()
|
||||
.unwrap_or_else(|| UntaggedValue::nothing().into_untagged_value())
|
||||
.insert_data_at_column_path(&field, value.clone())
|
||||
{
|
||||
Ok(v) => OutputStream::one(ReturnSuccess::value(v)),
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
},
|
||||
@ -135,9 +160,9 @@ async fn insert(
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let scope = Arc::new(raw_args.call_info.scope.clone());
|
||||
let context = Arc::new(Context::from_raw(&raw_args, ®istry));
|
||||
let (InsertArgs { column, value }, input) = raw_args.process(®istry).await?;
|
||||
let scope = raw_args.call_info.scope.clone();
|
||||
let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry));
|
||||
let (Arguments { column, value }, input) = raw_args.process(®istry).await?;
|
||||
let value = Arc::new(value);
|
||||
let column = Arc::new(column);
|
||||
|
||||
@ -158,15 +183,3 @@ async fn insert(
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Insert;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Insert {})
|
||||
}
|
||||
}
|
||||
|
@ -78,11 +78,12 @@ async fn into_int(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::IntoInt;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(IntoInt {})
|
||||
Ok(test_examples(IntoInt {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,211 +0,0 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use nu_value_ext::ValueExt;
|
||||
|
||||
enum IsEmptyFor {
|
||||
Value,
|
||||
RowWithFieldsAndFallback(Vec<Tagged<ColumnPath>>, Value),
|
||||
RowWithField(Tagged<ColumnPath>),
|
||||
RowWithFieldAndFallback(Box<Tagged<ColumnPath>>, Value),
|
||||
}
|
||||
|
||||
pub struct IsEmpty;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct IsEmptyArgs {
|
||||
rest: Vec<Value>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for IsEmpty {
|
||||
fn name(&self) -> &str {
|
||||
"empty?"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("empty?").rest(
|
||||
SyntaxShape::Any,
|
||||
"the names of the columns to check emptiness followed by the replacement value.",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Checks emptiness. The last value is the replacement value for any empty column(s) given to check against the table."
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
is_empty(args, registry).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn is_empty(
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let (IsEmptyArgs { rest }, input) = args.process(®istry).await?;
|
||||
|
||||
Ok(input
|
||||
.map(move |value| {
|
||||
let value_tag = value.tag();
|
||||
|
||||
let action = if rest.len() <= 2 {
|
||||
let field = rest.get(0);
|
||||
let replacement_if_true = rest.get(1);
|
||||
|
||||
match (field, replacement_if_true) {
|
||||
(Some(field), Some(replacement_if_true)) => {
|
||||
IsEmptyFor::RowWithFieldAndFallback(
|
||||
Box::new(field.as_column_path()?),
|
||||
replacement_if_true.clone(),
|
||||
)
|
||||
}
|
||||
(Some(field), None) => IsEmptyFor::RowWithField(field.as_column_path()?),
|
||||
(_, _) => IsEmptyFor::Value,
|
||||
}
|
||||
} else {
|
||||
// let no_args = vec![];
|
||||
let mut arguments = rest.iter().rev();
|
||||
let replacement_if_true = match arguments.next() {
|
||||
Some(arg) => arg.clone(),
|
||||
None => UntaggedValue::boolean(value.is_empty()).into_value(&value_tag),
|
||||
};
|
||||
|
||||
IsEmptyFor::RowWithFieldsAndFallback(
|
||||
arguments
|
||||
.map(|a| a.as_column_path())
|
||||
.filter_map(Result::ok)
|
||||
.collect(),
|
||||
replacement_if_true,
|
||||
)
|
||||
};
|
||||
|
||||
match action {
|
||||
IsEmptyFor::Value => Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::boolean(value.is_empty()).into_value(value_tag),
|
||||
)),
|
||||
IsEmptyFor::RowWithFieldsAndFallback(fields, default) => {
|
||||
let mut out = value;
|
||||
|
||||
for field in fields.iter() {
|
||||
let val = crate::commands::get::get_column_path(&field, &out)?;
|
||||
|
||||
let emptiness_value = match out {
|
||||
obj
|
||||
@
|
||||
Value {
|
||||
value: UntaggedValue::Row(_),
|
||||
..
|
||||
} => {
|
||||
if val.is_empty() {
|
||||
obj.replace_data_at_column_path(&field, default.clone())
|
||||
.ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
"empty? could not find place to check emptiness",
|
||||
"column name",
|
||||
&field.tag,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Ok(obj)
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Unrecognized type in stream",
|
||||
"original value",
|
||||
&value_tag,
|
||||
)),
|
||||
};
|
||||
|
||||
out = emptiness_value?;
|
||||
}
|
||||
|
||||
Ok(ReturnSuccess::Value(out))
|
||||
}
|
||||
IsEmptyFor::RowWithField(field) => {
|
||||
let val = crate::commands::get::get_column_path(&field, &value)?;
|
||||
|
||||
match &value {
|
||||
obj
|
||||
@
|
||||
Value {
|
||||
value: UntaggedValue::Row(_),
|
||||
..
|
||||
} => {
|
||||
if val.is_empty() {
|
||||
match obj.replace_data_at_column_path(
|
||||
&field,
|
||||
UntaggedValue::boolean(true).into_value(&value_tag),
|
||||
) {
|
||||
Some(v) => Ok(ReturnSuccess::Value(v)),
|
||||
None => Err(ShellError::labeled_error(
|
||||
"empty? could not find place to check emptiness",
|
||||
"column name",
|
||||
&field.tag,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Ok(ReturnSuccess::Value(value))
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Unrecognized type in stream",
|
||||
"original value",
|
||||
&value_tag,
|
||||
)),
|
||||
}
|
||||
}
|
||||
IsEmptyFor::RowWithFieldAndFallback(field, default) => {
|
||||
let val = crate::commands::get::get_column_path(&field, &value)?;
|
||||
|
||||
match &value {
|
||||
obj
|
||||
@
|
||||
Value {
|
||||
value: UntaggedValue::Row(_),
|
||||
..
|
||||
} => {
|
||||
if val.is_empty() {
|
||||
match obj.replace_data_at_column_path(&field, default) {
|
||||
Some(v) => Ok(ReturnSuccess::Value(v)),
|
||||
None => Err(ShellError::labeled_error(
|
||||
"empty? could not find place to check emptiness",
|
||||
"column name",
|
||||
&field.tag,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Ok(ReturnSuccess::Value(value))
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Unrecognized type in stream",
|
||||
"original value",
|
||||
&value_tag,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::IsEmpty;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(IsEmpty {})
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||
@ -74,11 +74,12 @@ async fn keep(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Command;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Command {})
|
||||
Ok(test_examples(Command {})?)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use crate::evaluate::evaluate_baseline_expr;
|
||||
use crate::prelude::*;
|
||||
use log::trace;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{hir::ClassifiedCommand, Scope, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
@ -33,7 +33,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = Arc::new(registry.clone());
|
||||
let scope = Arc::new(args.call_info.scope.clone());
|
||||
let scope = args.call_info.scope.clone();
|
||||
|
||||
let call_info = args.evaluate_once(®istry).await?;
|
||||
|
||||
@ -85,19 +85,11 @@ impl WholeStreamCommand for SubCommand {
|
||||
.take_while(move |item| {
|
||||
let condition = condition.clone();
|
||||
let registry = registry.clone();
|
||||
let scope = scope.clone();
|
||||
let item = item.clone();
|
||||
let scope = Scope::append_it(scope.clone(), item.clone());
|
||||
trace!("ITEM = {:?}", item);
|
||||
|
||||
async move {
|
||||
let result = evaluate_baseline_expr(
|
||||
&*condition,
|
||||
®istry,
|
||||
&item,
|
||||
&scope.vars,
|
||||
&scope.env,
|
||||
)
|
||||
.await;
|
||||
let result = evaluate_baseline_expr(&*condition, ®istry, scope).await;
|
||||
trace!("RESULT = {:?}", result);
|
||||
|
||||
!matches!(result, Ok(ref v) if v.is_true())
|
||||
@ -109,12 +101,13 @@ impl WholeStreamCommand for SubCommand {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use crate::evaluate::evaluate_baseline_expr;
|
||||
use crate::prelude::*;
|
||||
use log::trace;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{hir::ClassifiedCommand, Scope, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
@ -33,7 +33,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = Arc::new(registry.clone());
|
||||
let scope = Arc::new(args.call_info.scope.clone());
|
||||
let scope = args.call_info.scope.clone();
|
||||
let call_info = args.evaluate_once(®istry).await?;
|
||||
|
||||
let block = call_info.args.expect_nth(0)?.clone();
|
||||
@ -84,20 +84,11 @@ impl WholeStreamCommand for SubCommand {
|
||||
.take_while(move |item| {
|
||||
let condition = condition.clone();
|
||||
let registry = registry.clone();
|
||||
let scope = scope.clone();
|
||||
let item = item.clone();
|
||||
|
||||
let scope = Scope::append_it(scope.clone(), item.clone());
|
||||
trace!("ITEM = {:?}", item);
|
||||
|
||||
async move {
|
||||
let result = evaluate_baseline_expr(
|
||||
&*condition,
|
||||
®istry,
|
||||
&item,
|
||||
&scope.vars,
|
||||
&scope.env,
|
||||
)
|
||||
.await;
|
||||
let result = evaluate_baseline_expr(&*condition, ®istry, scope).await;
|
||||
trace!("RESULT = {:?}", result);
|
||||
|
||||
matches!(result, Ok(ref v) if v.is_true())
|
||||
@ -109,12 +100,13 @@ impl WholeStreamCommand for SubCommand {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
@ -121,11 +121,12 @@ async fn kill(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Kill;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Kill {})
|
||||
Ok(test_examples(Kill {})?)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
@ -83,11 +83,12 @@ async fn last(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Last;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Last {})
|
||||
Ok(test_examples(Last {})?)
|
||||
}
|
||||
}
|
||||
|
@ -75,9 +75,9 @@ async fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
||||
} => {
|
||||
let mut leftover_string = leftover_string.lock();
|
||||
|
||||
let st = (&*leftover_string).clone() + &st;
|
||||
|
||||
let mut lines: Vec<String> = st.lines().map(|x| x.to_string()).collect();
|
||||
let lo_lines = (&*leftover_string).lines().map(|x| x.to_string());
|
||||
let st_lines = st.lines().map(|x| x.to_string());
|
||||
let mut lines: Vec<String> = lo_lines.chain(st_lines).collect();
|
||||
|
||||
leftover_string.clear();
|
||||
|
||||
@ -125,11 +125,12 @@ async fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Lines;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Lines {})
|
||||
Ok(test_examples(Lines {})?)
|
||||
}
|
||||
}
|
||||
|
@ -89,11 +89,12 @@ impl WholeStreamCommand for Ls {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Ls;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Ls {})
|
||||
Ok(test_examples(Ls {})?)
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,11 @@ impl WholeStreamCommand for SubCommand {
|
||||
vec![Example {
|
||||
description: "Get the average of a list of numbers",
|
||||
example: "echo [-50 100.0 25] | math avg",
|
||||
result: Some(vec![UntaggedValue::decimal(25).into()]),
|
||||
result: Some(vec![UntaggedValue::decimal_from_float(
|
||||
25.0,
|
||||
Span::unknown(),
|
||||
)
|
||||
.into()]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -137,6 +141,28 @@ pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
|
||||
)),
|
||||
}
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Duration(duration)),
|
||||
..
|
||||
} => {
|
||||
let left = UntaggedValue::from(Primitive::Duration(duration));
|
||||
let result = nu_data::value::compute_values(Operator::Divide, &left, &total_rows);
|
||||
|
||||
match result {
|
||||
Ok(UntaggedValue::Primitive(Primitive::Duration(result))) => {
|
||||
Ok(UntaggedValue::duration(result).into_value(name))
|
||||
}
|
||||
Ok(_) => Err(ShellError::labeled_error(
|
||||
"could not calculate average of non-integer or unrelated types",
|
||||
"source",
|
||||
name,
|
||||
)),
|
||||
Err((left_type, right_type)) => Err(ShellError::coerce_error(
|
||||
left_type.spanned(name.span),
|
||||
right_type.spanned(name.span),
|
||||
)),
|
||||
}
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(other),
|
||||
..
|
||||
@ -162,12 +188,13 @@ pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
}
|
||||
|
@ -39,15 +39,15 @@ mod tests {
|
||||
sum::summation, utils::calculate, utils::MathFunction, variance::variance,
|
||||
};
|
||||
use nu_plugin::row;
|
||||
use nu_plugin::test_helpers::value::{decimal, int, table};
|
||||
use nu_plugin::test_helpers::value::{decimal, decimal_from_float, int, table};
|
||||
use nu_protocol::Value;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Command {})
|
||||
Ok(test_examples(Command {})?)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -98,17 +98,17 @@ mod tests {
|
||||
},
|
||||
TestCase {
|
||||
description: "Mixed Values",
|
||||
values: vec![int(10), decimal(26.5), decimal(26.5)],
|
||||
values: vec![int(10), decimal_from_float(26.5), decimal_from_float(26.5)],
|
||||
expected_err: None,
|
||||
expected_res: vec![
|
||||
Ok(decimal(21)),
|
||||
Ok(int(10)),
|
||||
Ok(decimal(26.5)),
|
||||
Ok(decimal(26.5)),
|
||||
Ok(table(&[decimal(26.5)])),
|
||||
Ok(decimal_from_float(26.5)),
|
||||
Ok(decimal_from_float(26.5)),
|
||||
Ok(table(&[decimal_from_float(26.5)])),
|
||||
Ok(decimal(BigDecimal::from_str("7.77817459305202276840928798315333943213319531457321440247173855894902863154158871367713143880202865").expect("Could not convert to decimal from string"))),
|
||||
Ok(decimal(63)),
|
||||
Ok(decimal(60.5)),
|
||||
Ok(decimal_from_float(60.5)),
|
||||
],
|
||||
},
|
||||
TestCase {
|
||||
@ -128,14 +128,14 @@ mod tests {
|
||||
},
|
||||
TestCase {
|
||||
description: "Mixed Negative Values",
|
||||
values: vec![decimal(-13.5), decimal(-11.5), int(10)],
|
||||
values: vec![decimal_from_float(-13.5), decimal_from_float(-11.5), int(10)],
|
||||
expected_err: None,
|
||||
expected_res: vec![
|
||||
Ok(decimal(-5)),
|
||||
Ok(decimal(-13.5)),
|
||||
Ok(decimal_from_float(-13.5)),
|
||||
Ok(int(10)),
|
||||
Ok(decimal(-11.5)),
|
||||
Ok(table(&[decimal(-13.5), decimal(-11.5), int(10)])),
|
||||
Ok(decimal_from_float(-11.5)),
|
||||
Ok(table(&[decimal_from_float(-13.5), decimal_from_float(-11.5), int(10)])),
|
||||
Ok(decimal(BigDecimal::from_str("10.63798226482196513098036125801342585449179971588207816421068645273754903468375890632981926875247027").expect("Could not convert to decimal from string"))),
|
||||
Ok(decimal(-15)),
|
||||
Ok(decimal(BigDecimal::from_str("113.1666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667").expect("Could not convert to decimal from string"))),
|
||||
@ -151,10 +151,10 @@ mod tests {
|
||||
],
|
||||
expected_err: None,
|
||||
expected_res: vec![
|
||||
Ok(row!["col1".to_owned() => decimal(2.5), "col2".to_owned() => decimal(6.5)]),
|
||||
Ok(row!["col1".to_owned() => decimal_from_float(2.5), "col2".to_owned() => decimal_from_float(6.5)]),
|
||||
Ok(row!["col1".to_owned() => int(1), "col2".to_owned() => int(5)]),
|
||||
Ok(row!["col1".to_owned() => int(4), "col2".to_owned() => int(8)]),
|
||||
Ok(row!["col1".to_owned() => decimal(2.5), "col2".to_owned() => decimal(6.5)]),
|
||||
Ok(row!["col1".to_owned() => decimal_from_float(2.5), "col2".to_owned() => decimal_from_float(6.5)]),
|
||||
Ok(row![
|
||||
"col1".to_owned() => table(&[int(1), int(2), int(3), int(4)]),
|
||||
"col2".to_owned() => table(&[int(5), int(6), int(7), int(8)])
|
||||
@ -164,7 +164,7 @@ mod tests {
|
||||
"col2".to_owned() => decimal(BigDecimal::from_str("1.118033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137").expect("Could not convert to decimal from string"))
|
||||
]),
|
||||
Ok(row!["col1".to_owned() => int(10), "col2".to_owned() => int(26)]),
|
||||
Ok(row!["col1".to_owned() => decimal(1.25), "col2".to_owned() => decimal(1.25)]),
|
||||
Ok(row!["col1".to_owned() => decimal_from_float(1.25), "col2".to_owned() => decimal_from_float(1.25)]),
|
||||
],
|
||||
},
|
||||
// TODO-Uncomment once Issue: https://github.com/nushell/nushell/issues/1883 is resolved
|
||||
|
@ -41,7 +41,11 @@ impl WholeStreamCommand for SubCommand {
|
||||
vec![Example {
|
||||
description: "Evalulate math in the pipeline",
|
||||
example: "echo '10 / 4' | math eval",
|
||||
result: Some(vec![UntaggedValue::decimal(2.5).into()]),
|
||||
result: Some(vec![UntaggedValue::decimal_from_float(
|
||||
2.5,
|
||||
Span::unknown(),
|
||||
)
|
||||
.into()]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -96,12 +100,13 @@ pub fn parse<T: Into<Tag>>(math_expression: &str, tag: T) -> Result<Value, Strin
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
}
|
||||
|
@ -58,12 +58,13 @@ pub fn maximum(values: &[Value], _name: &Tag) -> Result<Value, ShellError> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,11 @@ impl WholeStreamCommand for SubCommand {
|
||||
vec![Example {
|
||||
description: "Get the median of a list of numbers",
|
||||
example: "echo [3 8 9 12 12 15] | math median",
|
||||
result: Some(vec![UntaggedValue::decimal(10.5).into()]),
|
||||
result: Some(vec![UntaggedValue::decimal_from_float(
|
||||
10.5,
|
||||
Span::unknown(),
|
||||
)
|
||||
.into()]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -182,12 +186,13 @@ fn compute_average(values: &[Value], name: impl Into<Tag>) -> Result<Value, Shel
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
}
|
||||
|
@ -58,12 +58,13 @@ pub fn minimum(values: &[Value], _name: &Tag) -> Result<Value, ShellError> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user