mirror of
https://github.com/nushell/nushell.git
synced 2025-07-03 08:03:32 +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
|
- bash: RUSTFLAGS="-D warnings" cargo clippy --all -- -D clippy::unwrap_used
|
||||||
condition: eq(variables['style'], 'canary')
|
condition: eq(variables['style'], 'canary')
|
||||||
displayName: Check clippy lints
|
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')
|
condition: eq(variables['style'], 'minimal')
|
||||||
displayName: Run tests
|
displayName: Run tests
|
||||||
- bash: RUSTFLAGS="-D warnings" cargo test --all --features=extra
|
- 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
|
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:
|
- Run Clippy on Nushell:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@ -60,3 +65,11 @@ cargo build
|
|||||||
```shell
|
```shell
|
||||||
cargo fmt --all
|
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"
|
name = "nu"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/nushell/nushell"
|
repository = "https://github.com/nushell/nushell"
|
||||||
version = "0.19.0"
|
version = "0.21.0"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["crates/*/"]
|
members = ["crates/*/"]
|
||||||
@ -18,51 +18,57 @@ members = ["crates/*/"]
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cli = {version = "0.19.0", path = "./crates/nu-cli"}
|
nu-cli = {version = "0.21.0", path = "./crates/nu-cli"}
|
||||||
nu-data = {version = "0.19.0", path = "./crates/nu-data"}
|
nu-data = {version = "0.21.0", path = "./crates/nu-data"}
|
||||||
nu-errors = {version = "0.19.0", path = "./crates/nu-errors"}
|
nu-errors = {version = "0.21.0", path = "./crates/nu-errors"}
|
||||||
nu-parser = {version = "0.19.0", path = "./crates/nu-parser"}
|
nu-parser = {version = "0.21.0", path = "./crates/nu-parser"}
|
||||||
nu-plugin = {version = "0.19.0", path = "./crates/nu-plugin"}
|
nu-plugin = {version = "0.21.0", path = "./crates/nu-plugin"}
|
||||||
nu-protocol = {version = "0.19.0", path = "./crates/nu-protocol"}
|
nu-protocol = {version = "0.21.0", path = "./crates/nu-protocol"}
|
||||||
nu-source = {version = "0.19.0", path = "./crates/nu-source"}
|
nu-source = {version = "0.21.0", path = "./crates/nu-source"}
|
||||||
nu-value-ext = {version = "0.19.0", path = "./crates/nu-value-ext"}
|
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_binaryview = {version = "0.21.0", path = "./crates/nu_plugin_binaryview", optional = true}
|
||||||
nu_plugin_fetch = {version = "0.19.0", path = "./crates/nu_plugin_fetch", optional = true}
|
nu_plugin_chart = {version = "0.21.0", path = "./crates/nu_plugin_chart", optional = true}
|
||||||
nu_plugin_from_bson = {version = "0.19.0", path = "./crates/nu_plugin_from_bson", optional = true}
|
nu_plugin_fetch = {version = "0.21.0", path = "./crates/nu_plugin_fetch", optional = true}
|
||||||
nu_plugin_from_sqlite = {version = "0.19.0", path = "./crates/nu_plugin_from_sqlite", optional = true}
|
nu_plugin_from_bson = {version = "0.21.0", path = "./crates/nu_plugin_from_bson", optional = true}
|
||||||
nu_plugin_inc = {version = "0.19.0", path = "./crates/nu_plugin_inc", optional = true}
|
nu_plugin_from_sqlite = {version = "0.21.0", path = "./crates/nu_plugin_from_sqlite", optional = true}
|
||||||
nu_plugin_match = {version = "0.19.0", path = "./crates/nu_plugin_match", optional = true}
|
nu_plugin_inc = {version = "0.21.0", path = "./crates/nu_plugin_inc", optional = true}
|
||||||
nu_plugin_post = {version = "0.19.0", path = "./crates/nu_plugin_post", optional = true}
|
nu_plugin_match = {version = "0.21.0", path = "./crates/nu_plugin_match", optional = true}
|
||||||
nu_plugin_ps = {version = "0.19.0", path = "./crates/nu_plugin_ps", optional = true}
|
nu_plugin_post = {version = "0.21.0", path = "./crates/nu_plugin_post", optional = true}
|
||||||
nu_plugin_s3 = {version = "0.19.0", path = "./crates/nu_plugin_s3", optional = true}
|
nu_plugin_ps = {version = "0.21.0", path = "./crates/nu_plugin_ps", optional = true}
|
||||||
nu_plugin_start = {version = "0.19.0", path = "./crates/nu_plugin_start", optional = true}
|
nu_plugin_s3 = {version = "0.21.0", path = "./crates/nu_plugin_s3", optional = true}
|
||||||
nu_plugin_sys = {version = "0.19.0", path = "./crates/nu_plugin_sys", optional = true}
|
nu_plugin_start = {version = "0.21.0", path = "./crates/nu_plugin_start", optional = true}
|
||||||
nu_plugin_textview = {version = "0.19.0", path = "./crates/nu_plugin_textview", optional = true}
|
nu_plugin_sys = {version = "0.21.0", path = "./crates/nu_plugin_sys", optional = true}
|
||||||
nu_plugin_to_bson = {version = "0.19.0", path = "./crates/nu_plugin_to_bson", optional = true}
|
nu_plugin_textview = {version = "0.21.0", path = "./crates/nu_plugin_textview", optional = true}
|
||||||
nu_plugin_to_sqlite = {version = "0.19.0", path = "./crates/nu_plugin_to_sqlite", optional = true}
|
nu_plugin_to_bson = {version = "0.21.0", path = "./crates/nu_plugin_to_bson", optional = true}
|
||||||
nu_plugin_tree = {version = "0.19.0", path = "./crates/nu_plugin_tree", 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}
|
# Required to bootstrap the main binary
|
||||||
semver = {version = "0.10.0", optional = true}
|
clap = "2.33.3"
|
||||||
url = {version = "2.1.1", optional = true}
|
ctrlc = {version = "3.1.6", optional = true}
|
||||||
|
futures = {version = "0.3.5", features = ["compat", "io-compat"]}
|
||||||
clap = "2.33.1"
|
log = "0.4.11"
|
||||||
ctrlc = "3.1.4"
|
|
||||||
dunce = "1.0.1"
|
|
||||||
futures = {version = "0.3", features = ["compat", "io-compat"]}
|
|
||||||
log = "0.4.8"
|
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
quick-xml = "0.18.1"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[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]
|
[build-dependencies]
|
||||||
serde = {version = "1.0.110", features = ["derive"]}
|
|
||||||
toml = "0.5.6"
|
|
||||||
|
|
||||||
[features]
|
[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 = [
|
default = [
|
||||||
"sys",
|
"sys",
|
||||||
"ps",
|
"ps",
|
||||||
@ -75,40 +81,39 @@ default = [
|
|||||||
"ptree-support",
|
"ptree-support",
|
||||||
"term-support",
|
"term-support",
|
||||||
"uuid-support",
|
"uuid-support",
|
||||||
|
"rustyline-support",
|
||||||
"match",
|
"match",
|
||||||
"post",
|
"post",
|
||||||
"fetch",
|
"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"]
|
stable = ["default"]
|
||||||
|
|
||||||
# Default
|
wasi = ["inc", "match", "directories-support", "ptree-support", "match", "tree", "rustyline-support"]
|
||||||
inc = ["semver", "nu_plugin_inc"]
|
|
||||||
ps = ["nu_plugin_ps"]
|
|
||||||
sys = ["nu_plugin_sys"]
|
|
||||||
textview = ["crossterm", "url", "nu_plugin_textview"]
|
|
||||||
|
|
||||||
# Stable
|
trace = ["nu-parser/trace"]
|
||||||
binaryview = ["nu_plugin_binaryview"]
|
|
||||||
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
|
# Stable (Default)
|
||||||
fetch = ["nu_plugin_fetch"]
|
fetch = ["nu_plugin_fetch"]
|
||||||
|
inc = ["nu_plugin_inc"]
|
||||||
match = ["nu_plugin_match"]
|
match = ["nu_plugin_match"]
|
||||||
post = ["nu_plugin_post"]
|
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"]
|
s3 = ["nu_plugin_s3"]
|
||||||
sqlite = ["nu_plugin_from_sqlite", "nu_plugin_to_sqlite"]
|
sqlite = ["nu_plugin_from_sqlite", "nu_plugin_to_sqlite"]
|
||||||
start = ["nu_plugin_start"]
|
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"]
|
trash-support = ["nu-cli/trash-support"]
|
||||||
uuid-support = ["nu-cli/uuid_crate"]
|
tree = ["nu_plugin_tree"]
|
||||||
which-support = ["nu-cli/ichwh", "nu-cli/which"]
|
xpath = ["nu_plugin_xpath"]
|
||||||
|
|
||||||
# Core plugins that ship with `cargo install nu` by default
|
# Core plugins that ship with `cargo install nu` by default
|
||||||
# Currently, Cargo limits us to installing only one binary
|
# 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"
|
path = "src/plugins/nu_plugin_extra_s3.rs"
|
||||||
required-features = ["s3"]
|
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
|
# Main nu binary
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "nu"
|
name = "nu"
|
||||||
|
@ -4,103 +4,103 @@ description = "CLI for nushell"
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.19.0"
|
version = "0.21.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-data = {version = "0.19.0", path = "../nu-data"}
|
nu-data = {version = "0.21.0", path = "../nu-data"}
|
||||||
nu-errors = {version = "0.19.0", path = "../nu-errors"}
|
nu-errors = {version = "0.21.0", path = "../nu-errors"}
|
||||||
nu-parser = {version = "0.19.0", path = "../nu-parser"}
|
nu-parser = {version = "0.21.0", path = "../nu-parser"}
|
||||||
nu-plugin = {version = "0.19.0", path = "../nu-plugin"}
|
nu-plugin = {version = "0.21.0", path = "../nu-plugin"}
|
||||||
nu-protocol = {version = "0.19.0", path = "../nu-protocol"}
|
nu-protocol = {version = "0.21.0", path = "../nu-protocol"}
|
||||||
nu-source = {version = "0.19.0", path = "../nu-source"}
|
nu-source = {version = "0.21.0", path = "../nu-source"}
|
||||||
nu-table = {version = "0.19.0", path = "../nu-table"}
|
nu-table = {version = "0.21.0", path = "../nu-table"}
|
||||||
nu-test-support = {version = "0.19.0", path = "../nu-test-support"}
|
nu-test-support = {version = "0.21.0", path = "../nu-test-support"}
|
||||||
nu-value-ext = {version = "0.19.0", path = "../nu-value-ext"}
|
nu-value-ext = {version = "0.21.0", path = "../nu-value-ext"}
|
||||||
|
|
||||||
ansi_term = "0.12.1"
|
ansi_term = "0.12.1"
|
||||||
app_dirs = {version = "2", package = "app_dirs2"}
|
|
||||||
async-recursion = "0.3.1"
|
async-recursion = "0.3.1"
|
||||||
async-trait = "0.1.36"
|
async-trait = "0.1.40"
|
||||||
base64 = "0.12.3"
|
base64 = "0.12.3"
|
||||||
bigdecimal = {version = "0.1.2", features = ["serde"]}
|
bigdecimal = {version = "0.2.0", features = ["serde"]}
|
||||||
byte-unit = "3.1.3"
|
byte-unit = "4.0.9"
|
||||||
bytes = "0.5.5"
|
bytes = "0.5.6"
|
||||||
calamine = "0.16"
|
calamine = "0.16.1"
|
||||||
chrono = {version = "0.4.11", features = ["serde"]}
|
chrono = {version = "0.4.15", features = ["serde"]}
|
||||||
clap = "2.33.1"
|
clap = "2.33.3"
|
||||||
codespan-reporting = "0.9.5"
|
codespan-reporting = "0.9.5"
|
||||||
csv = "1.1"
|
csv = "1.1.3"
|
||||||
ctrlc = {version = "3.1.4", optional = true}
|
ctrlc = {version = "3.1.6", optional = true}
|
||||||
derive-new = "0.5.8"
|
derive-new = "0.5.8"
|
||||||
directories = {version = "2.0.2", optional = true}
|
directories = {version = "3.0.1", optional = true}
|
||||||
dirs = {version = "2.0.2", optional = true}
|
dirs = {version = "3.0.1", optional = true}
|
||||||
dtparse = "1.1.0"
|
dtparse = "1.2.0"
|
||||||
dunce = "1.0.1"
|
dunce = "1.0.1"
|
||||||
eml-parser = "0.1.0"
|
eml-parser = "0.1.0"
|
||||||
filesize = "0.2.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-util = "0.3.5"
|
||||||
futures_codec = "0.4"
|
futures_codec = "0.4.1"
|
||||||
getset = "0.1.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"
|
glob = "0.3.0"
|
||||||
hex = "0.4"
|
heim = {version = "0.1.0-beta.3", optional = true}
|
||||||
htmlescape = "0.3.1"
|
htmlescape = "0.3.1"
|
||||||
ical = "0.6.*"
|
ical = "0.6.0"
|
||||||
ichwh = {version = "0.3.4", optional = true}
|
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"
|
itertools = "0.9.0"
|
||||||
log = "0.4.8"
|
log = "0.4.11"
|
||||||
meval = "0.2"
|
meval = "0.2.0"
|
||||||
natural = "0.5.0"
|
num-bigint = {version = "0.3.0", features = ["serde"]}
|
||||||
num-bigint = {version = "0.2.6", features = ["serde"]}
|
num-format = {version = "0.4.0", features = ["with-num-bigint"]}
|
||||||
num-format = {version = "0.4", features = ["with-num-bigint"]}
|
num-traits = "0.2.12"
|
||||||
num-traits = "0.2.11"
|
|
||||||
parking_lot = "0.11.0"
|
parking_lot = "0.11.0"
|
||||||
pin-utils = "0.1.0"
|
pin-utils = "0.1.0"
|
||||||
pretty-hex = "0.1.1"
|
pretty-hex = "0.2.0"
|
||||||
pretty_env_logger = "0.4.0"
|
ptree = {version = "0.3.0", optional = true}
|
||||||
ptree = {version = "0.2", optional = true}
|
|
||||||
query_interface = "0.3.5"
|
query_interface = "0.3.5"
|
||||||
rand = "0.7"
|
quick-xml = "0.18.1"
|
||||||
regex = "1"
|
rand = "0.7.3"
|
||||||
|
regex = "1.3.9"
|
||||||
roxmltree = "0.13.0"
|
roxmltree = "0.13.0"
|
||||||
rust-embed = "5.6.0"
|
rust-embed = "5.6.0"
|
||||||
rustyline = "6.2.0"
|
rustyline = {version = "6.3.0", optional = true}
|
||||||
serde = {version = "1.0.114", features = ["derive"]}
|
serde = {version = "1.0.115", features = ["derive"]}
|
||||||
serde-hjson = "0.9.1"
|
serde-hjson = "0.9.1"
|
||||||
serde_bytes = "0.11.5"
|
serde_bytes = "0.11.5"
|
||||||
serde_ini = "0.2.0"
|
serde_ini = "0.2.0"
|
||||||
serde_json = "1.0.55"
|
serde_json = "1.0.57"
|
||||||
serde_urlencoded = "0.6.1"
|
serde_urlencoded = "0.7.0"
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8.13"
|
||||||
sha2 = "0.9.1"
|
sha2 = "0.9.1"
|
||||||
shellexpand = "2.0.0"
|
shellexpand = "2.0.0"
|
||||||
strip-ansi-escapes = "0.1.0"
|
strip-ansi-escapes = "0.1.0"
|
||||||
|
sxd-document = "0.3.2"
|
||||||
|
sxd-xpath = "0.4.2"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
term = {version = "0.5.2", optional = true}
|
term = {version = "0.6.1", optional = true}
|
||||||
term_size = "0.3.2"
|
term_size = "0.3.2"
|
||||||
termcolor = "1.1.0"
|
termcolor = "1.1.0"
|
||||||
toml = "0.5.6"
|
toml = "0.5.6"
|
||||||
typetag = "0.1.5"
|
unicode-segmentation = "1.6.0"
|
||||||
umask = "1.0.0"
|
uom = {version = "0.28.0", features = ["f64", "try-from"]}
|
||||||
unicode-xid = "0.2.1"
|
|
||||||
uuid_crate = {package = "uuid", version = "0.8.1", features = ["v4"], optional = true}
|
uuid_crate = {package = "uuid", version = "0.8.1", features = ["v4"], optional = true}
|
||||||
which = {version = "4.0.2", 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"
|
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]
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
umask = "1.0.0"
|
||||||
users = "0.10.0"
|
users = "0.10.0"
|
||||||
|
|
||||||
# TODO this will be possible with new dependency resolver
|
# TODO this will be possible with new dependency resolver
|
||||||
@ -112,17 +112,18 @@ users = "0.10.0"
|
|||||||
[dependencies.rusqlite]
|
[dependencies.rusqlite]
|
||||||
features = ["bundled", "blob"]
|
features = ["bundled", "blob"]
|
||||||
optional = true
|
optional = true
|
||||||
version = "0.23.1"
|
version = "0.24.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
git2 = {version = "0.13", optional = true}
|
git2 = {version = "0.13.11", optional = true}
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
quickcheck = "0.9"
|
quickcheck = "0.9.2"
|
||||||
quickcheck_macros = "0.9"
|
quickcheck_macros = "0.9.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
clipboard-cli = ["clipboard"]
|
clipboard-cli = ["clipboard"]
|
||||||
|
rich-benchmark = ["heim"]
|
||||||
|
rustyline-support = ["rustyline"]
|
||||||
stable = []
|
stable = []
|
||||||
trash-support = ["trash"]
|
trash-support = ["trash"]
|
||||||
|
@ -1,38 +1,30 @@
|
|||||||
use crate::commands::classified::block::run_block;
|
use crate::commands::classified::block::run_block;
|
||||||
use crate::commands::classified::maybe_text_codec::{MaybeTextCodec, StringOrBinary};
|
use crate::commands::classified::maybe_text_codec::{MaybeTextCodec, StringOrBinary};
|
||||||
use crate::context::Context;
|
use crate::evaluation_context::EvaluationContext;
|
||||||
use crate::git::current_branch;
|
|
||||||
use crate::path::canonicalize;
|
use crate::path::canonicalize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
#[cfg(feature = "rustyline-support")]
|
||||||
use crate::shell::Helper;
|
use crate::shell::Helper;
|
||||||
use crate::EnvironmentSyncer;
|
use crate::EnvironmentSyncer;
|
||||||
use futures_codec::FramedRead;
|
use futures_codec::FramedRead;
|
||||||
use nu_errors::{ProximateShellError, ShellDiagnostic, ShellError};
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::hir::{ClassifiedCommand, Expression, InternalCommand, Literal, NamedArguments};
|
use nu_protocol::hir::{ClassifiedCommand, Expression, InternalCommand, Literal, NamedArguments};
|
||||||
use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value};
|
use nu_protocol::{Primitive, ReturnSuccess, Scope, UntaggedValue, Value};
|
||||||
|
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use rustyline::config::{ColorMode, CompletionType, Config};
|
#[cfg(feature = "rustyline-support")]
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::{
|
||||||
use rustyline::{self, config::Configurer, At, Cmd, Editor, KeyPress, Movement, Word};
|
self,
|
||||||
|
config::Configurer,
|
||||||
|
config::{ColorMode, CompletionType, Config},
|
||||||
|
error::ReadlineError,
|
||||||
|
At, Cmd, Editor, KeyPress, Movement, Word,
|
||||||
|
};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
fn register_plugins(context: &mut Context) -> Result<(), ShellError> {
|
|
||||||
if let Ok(plugins) = crate::plugin::scan() {
|
|
||||||
context.add_commands(
|
|
||||||
plugins
|
|
||||||
.into_iter()
|
|
||||||
.filter(|p| !context.is_command_registered(p.name()))
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn search_paths() -> Vec<std::path::PathBuf> {
|
pub fn search_paths() -> Vec<std::path::PathBuf> {
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
@ -64,21 +56,16 @@ pub fn search_paths() -> Vec<std::path::PathBuf> {
|
|||||||
search_paths
|
search_paths
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_default_context(
|
pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Box<dyn Error>> {
|
||||||
syncer: &mut crate::EnvironmentSyncer,
|
let mut context = EvaluationContext::basic()?;
|
||||||
interactive: bool,
|
|
||||||
) -> Result<Context, Box<dyn Error>> {
|
|
||||||
syncer.load_environment();
|
|
||||||
|
|
||||||
let mut context = Context::basic()?;
|
|
||||||
syncer.sync_env_vars(&mut context);
|
|
||||||
syncer.sync_path_vars(&mut context);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
use crate::commands::*;
|
use crate::commands::*;
|
||||||
|
|
||||||
context.add_commands(vec![
|
context.add_commands(vec![
|
||||||
|
whole_stream_command(NuPlugin),
|
||||||
// System/file operations
|
// System/file operations
|
||||||
|
whole_stream_command(Exec),
|
||||||
whole_stream_command(Pwd),
|
whole_stream_command(Pwd),
|
||||||
whole_stream_command(Ls),
|
whole_stream_command(Ls),
|
||||||
whole_stream_command(Du),
|
whole_stream_command(Du),
|
||||||
@ -108,7 +95,7 @@ pub fn create_default_context(
|
|||||||
whole_stream_command(Kill),
|
whole_stream_command(Kill),
|
||||||
whole_stream_command(Version),
|
whole_stream_command(Version),
|
||||||
whole_stream_command(Clear),
|
whole_stream_command(Clear),
|
||||||
whole_stream_command(What),
|
whole_stream_command(Describe),
|
||||||
whole_stream_command(Which),
|
whole_stream_command(Which),
|
||||||
whole_stream_command(Debug),
|
whole_stream_command(Debug),
|
||||||
whole_stream_command(Alias),
|
whole_stream_command(Alias),
|
||||||
@ -127,6 +114,8 @@ pub fn create_default_context(
|
|||||||
whole_stream_command(Shells),
|
whole_stream_command(Shells),
|
||||||
whole_stream_command(Enter),
|
whole_stream_command(Enter),
|
||||||
whole_stream_command(Exit),
|
whole_stream_command(Exit),
|
||||||
|
// Viz
|
||||||
|
whole_stream_command(Chart),
|
||||||
// Viewers
|
// Viewers
|
||||||
whole_stream_command(Autoview),
|
whole_stream_command(Autoview),
|
||||||
whole_stream_command(Table),
|
whole_stream_command(Table),
|
||||||
@ -136,7 +125,6 @@ pub fn create_default_context(
|
|||||||
whole_stream_command(SplitRow),
|
whole_stream_command(SplitRow),
|
||||||
whole_stream_command(SplitChars),
|
whole_stream_command(SplitChars),
|
||||||
whole_stream_command(Lines),
|
whole_stream_command(Lines),
|
||||||
whole_stream_command(Trim),
|
|
||||||
whole_stream_command(Echo),
|
whole_stream_command(Echo),
|
||||||
whole_stream_command(Parse),
|
whole_stream_command(Parse),
|
||||||
whole_stream_command(Str),
|
whole_stream_command(Str),
|
||||||
@ -159,7 +147,9 @@ pub fn create_default_context(
|
|||||||
whole_stream_command(StrEndsWith),
|
whole_stream_command(StrEndsWith),
|
||||||
whole_stream_command(StrCollect),
|
whole_stream_command(StrCollect),
|
||||||
whole_stream_command(StrLength),
|
whole_stream_command(StrLength),
|
||||||
|
whole_stream_command(StrLPad),
|
||||||
whole_stream_command(StrReverse),
|
whole_stream_command(StrReverse),
|
||||||
|
whole_stream_command(StrRPad),
|
||||||
whole_stream_command(StrCamelCase),
|
whole_stream_command(StrCamelCase),
|
||||||
whole_stream_command(StrPascalCase),
|
whole_stream_command(StrPascalCase),
|
||||||
whole_stream_command(StrKebabCase),
|
whole_stream_command(StrKebabCase),
|
||||||
@ -204,7 +194,9 @@ pub fn create_default_context(
|
|||||||
whole_stream_command(Rename),
|
whole_stream_command(Rename),
|
||||||
whole_stream_command(Uniq),
|
whole_stream_command(Uniq),
|
||||||
whole_stream_command(Each),
|
whole_stream_command(Each),
|
||||||
whole_stream_command(IsEmpty),
|
whole_stream_command(EachGroup),
|
||||||
|
whole_stream_command(EachWindow),
|
||||||
|
whole_stream_command(Empty),
|
||||||
// Table manipulation
|
// Table manipulation
|
||||||
whole_stream_command(Move),
|
whole_stream_command(Move),
|
||||||
whole_stream_command(Merge),
|
whole_stream_command(Merge),
|
||||||
@ -265,6 +257,7 @@ pub fn create_default_context(
|
|||||||
whole_stream_command(RandomDice),
|
whole_stream_command(RandomDice),
|
||||||
#[cfg(feature = "uuid_crate")]
|
#[cfg(feature = "uuid_crate")]
|
||||||
whole_stream_command(RandomUUID),
|
whole_stream_command(RandomUUID),
|
||||||
|
whole_stream_command(RandomInteger),
|
||||||
// Path
|
// Path
|
||||||
whole_stream_command(PathBasename),
|
whole_stream_command(PathBasename),
|
||||||
whole_stream_command(PathCommand),
|
whole_stream_command(PathCommand),
|
||||||
@ -282,7 +275,7 @@ pub fn create_default_context(
|
|||||||
whole_stream_command(UrlQuery),
|
whole_stream_command(UrlQuery),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
#[cfg(feature = "clipboard")]
|
#[cfg(feature = "clipboard-cli")]
|
||||||
{
|
{
|
||||||
context.add_commands(vec![whole_stream_command(crate::commands::clip::Clip)]);
|
context.add_commands(vec![whole_stream_command(crate::commands::clip::Clip)]);
|
||||||
}
|
}
|
||||||
@ -295,28 +288,300 @@ pub async fn run_vec_of_pipelines(
|
|||||||
pipelines: Vec<String>,
|
pipelines: Vec<String>,
|
||||||
redirect_stdin: bool,
|
redirect_stdin: bool,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
let mut syncer = crate::EnvironmentSyncer::new();
|
let mut syncer = EnvironmentSyncer::new();
|
||||||
let mut context = create_default_context(&mut syncer, false)?;
|
let mut context = create_default_context(false)?;
|
||||||
|
let config = syncer.get_config();
|
||||||
|
|
||||||
let _ = register_plugins(&mut context);
|
context.configure(&config, |_, ctx| {
|
||||||
|
syncer.load_environment();
|
||||||
|
syncer.sync_env_vars(ctx);
|
||||||
|
syncer.sync_path_vars(ctx);
|
||||||
|
|
||||||
|
if let Err(reason) = syncer.autoenv(ctx) {
|
||||||
|
print_err(reason, &Text::from(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = register_plugins(ctx);
|
||||||
|
let _ = configure_ctrl_c(ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
let _ = run_startup_commands(&mut context, &config).await;
|
||||||
|
|
||||||
|
for pipeline in pipelines {
|
||||||
|
run_pipeline_standalone(pipeline, redirect_stdin, &mut context, true).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rustyline-support")]
|
||||||
|
fn convert_rustyline_result_to_string(input: Result<String, ReadlineError>) -> LineResult {
|
||||||
|
match input {
|
||||||
|
Ok(s) => LineResult::Success(s),
|
||||||
|
Err(ReadlineError::Interrupted) => LineResult::CtrlC,
|
||||||
|
Err(ReadlineError::Eof) => LineResult::CtrlD,
|
||||||
|
Err(err) => {
|
||||||
|
outln!("Error: {:?}", err);
|
||||||
|
LineResult::Break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The entry point for the CLI. Will register all known internal commands, load experimental commands, load plugins, then prepare the prompt and line reader for input.
|
||||||
|
#[cfg(feature = "rustyline-support")]
|
||||||
|
pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut syncer = EnvironmentSyncer::new();
|
||||||
|
let configuration = syncer.get_config();
|
||||||
|
|
||||||
|
let mut rl = default_rustyline_editor_configuration();
|
||||||
|
|
||||||
|
context.configure(&configuration, |config, ctx| {
|
||||||
|
syncer.load_environment();
|
||||||
|
syncer.sync_env_vars(ctx);
|
||||||
|
syncer.sync_path_vars(ctx);
|
||||||
|
|
||||||
|
if let Err(reason) = syncer.autoenv(ctx) {
|
||||||
|
print_err(reason, &Text::from(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = configure_ctrl_c(ctx);
|
||||||
|
let _ = configure_rustyline_editor(&mut rl, config);
|
||||||
|
|
||||||
|
let helper = Some(nu_line_editor_helper(ctx, config));
|
||||||
|
rl.set_helper(helper);
|
||||||
|
});
|
||||||
|
|
||||||
|
let _ = run_startup_commands(&mut context, &configuration).await;
|
||||||
|
|
||||||
|
let history_path = crate::commands::history::history_path(&configuration);
|
||||||
|
let _ = rl.load_history(&history_path);
|
||||||
|
|
||||||
|
let skip_welcome_message = configuration
|
||||||
|
.var("skip_welcome_message")
|
||||||
|
.map(|x| x.is_true())
|
||||||
|
.unwrap_or(false);
|
||||||
|
if !skip_welcome_message {
|
||||||
|
println!(
|
||||||
|
"Welcome to Nushell {} (type 'help' for more info)",
|
||||||
|
clap::crate_version!()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let _ = ansi_term::enable_ansi_support();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ctrlcbreak = false;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if context.ctrl_c.load(Ordering::SeqCst) {
|
||||||
|
context.ctrl_c.store(false, Ordering::SeqCst);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cwd = context.shell_manager.path();
|
||||||
|
|
||||||
|
let colored_prompt = {
|
||||||
|
if let Some(prompt) = configuration.var("prompt") {
|
||||||
|
let prompt_line = prompt.as_string()?;
|
||||||
|
|
||||||
|
match nu_parser::lite_parse(&prompt_line, 0).map_err(ShellError::from) {
|
||||||
|
Ok(result) => {
|
||||||
|
let mut prompt_block =
|
||||||
|
nu_parser::classify_block(&result, context.registry());
|
||||||
|
|
||||||
|
let env = context.get_env();
|
||||||
|
|
||||||
|
prompt_block.block.expand_it_usage();
|
||||||
|
|
||||||
|
match run_block(
|
||||||
|
&prompt_block.block,
|
||||||
|
&mut context,
|
||||||
|
InputStream::empty(),
|
||||||
|
Scope::from_env(env),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(result) => match result.collect_string(Tag::unknown()).await {
|
||||||
|
Ok(string_result) => {
|
||||||
|
let errors = context.get_errors();
|
||||||
|
context.maybe_print_errors(Text::from(prompt_line));
|
||||||
|
context.clear_errors();
|
||||||
|
|
||||||
|
if !errors.is_empty() {
|
||||||
|
"> ".to_string()
|
||||||
|
} else {
|
||||||
|
string_result.item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
crate::cli::print_err(e, &Text::from(prompt_line));
|
||||||
|
context.clear_errors();
|
||||||
|
|
||||||
|
"> ".to_string()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
crate::cli::print_err(e, &Text::from(prompt_line));
|
||||||
|
context.clear_errors();
|
||||||
|
|
||||||
|
"> ".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
crate::cli::print_err(e, &Text::from(prompt_line));
|
||||||
|
context.clear_errors();
|
||||||
|
|
||||||
|
"> ".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
use crate::git::current_branch;
|
||||||
|
format!(
|
||||||
|
"\x1b[32m{}{}\x1b[m> ",
|
||||||
|
cwd,
|
||||||
|
match current_branch() {
|
||||||
|
Some(s) => format!("({})", s),
|
||||||
|
None => "".to_string(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let prompt = {
|
||||||
|
if let Ok(bytes) = strip_ansi_escapes::strip(&colored_prompt) {
|
||||||
|
String::from_utf8_lossy(&bytes).to_string()
|
||||||
|
} else {
|
||||||
|
"> ".to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
rl.helper_mut().expect("No helper").colored_prompt = colored_prompt;
|
||||||
|
let mut initial_command = Some(String::new());
|
||||||
|
let mut readline = Err(ReadlineError::Eof);
|
||||||
|
while let Some(ref cmd) = initial_command {
|
||||||
|
readline = rl.readline_with_initial(&prompt, (&cmd, ""));
|
||||||
|
initial_command = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let line = match convert_rustyline_result_to_string(readline) {
|
||||||
|
LineResult::Success(s) => process_line(&s, &mut context, false, true).await,
|
||||||
|
x => x,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check the config to see if we need to update the path
|
||||||
|
// TODO: make sure config is cached so we don't path this load every call
|
||||||
|
// FIXME: we probably want to be a bit more graceful if we can't set the environment
|
||||||
|
|
||||||
|
context.configure(&configuration, |config, ctx| {
|
||||||
|
if syncer.did_config_change() {
|
||||||
|
syncer.reload();
|
||||||
|
syncer.sync_env_vars(ctx);
|
||||||
|
syncer.sync_path_vars(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(reason) = syncer.autoenv(ctx) {
|
||||||
|
print_err(reason, &Text::from(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = configure_rustyline_editor(&mut rl, config);
|
||||||
|
});
|
||||||
|
|
||||||
|
match line {
|
||||||
|
LineResult::Success(line) => {
|
||||||
|
rl.add_history_entry(&line);
|
||||||
|
let _ = rl.save_history(&history_path);
|
||||||
|
context.maybe_print_errors(Text::from(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
LineResult::Error(line, err) => {
|
||||||
|
rl.add_history_entry(&line);
|
||||||
|
let _ = rl.save_history(&history_path);
|
||||||
|
|
||||||
|
context.with_host(|_host| {
|
||||||
|
print_err(err, &Text::from(line.clone()));
|
||||||
|
});
|
||||||
|
|
||||||
|
context.maybe_print_errors(Text::from(line.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
LineResult::CtrlC => {
|
||||||
|
let config_ctrlc_exit = config::config(Tag::unknown())?
|
||||||
|
.get("ctrlc_exit")
|
||||||
|
.map(|s| s.value.is_true())
|
||||||
|
.unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells
|
||||||
|
|
||||||
|
if !config_ctrlc_exit {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctrlcbreak {
|
||||||
|
let _ = rl.save_history(&history_path);
|
||||||
|
std::process::exit(0);
|
||||||
|
} else {
|
||||||
|
context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)"));
|
||||||
|
ctrlcbreak = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LineResult::CtrlD => {
|
||||||
|
context.shell_manager.remove_at_current();
|
||||||
|
if context.shell_manager.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LineResult::Break => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctrlcbreak = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we are ok if we can not save history
|
||||||
|
let _ = rl.save_history(&history_path);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_plugins(context: &mut EvaluationContext) -> Result<(), ShellError> {
|
||||||
|
if let Ok(plugins) = crate::plugin::scan(search_paths()) {
|
||||||
|
context.add_commands(
|
||||||
|
plugins
|
||||||
|
.into_iter()
|
||||||
|
.filter(|p| !context.is_command_registered(p.name()))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_ctrl_c(_context: &mut EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||||
#[cfg(feature = "ctrlc")]
|
#[cfg(feature = "ctrlc")]
|
||||||
{
|
{
|
||||||
let cc = context.ctrl_c.clone();
|
let cc = _context.ctrl_c.clone();
|
||||||
|
|
||||||
ctrlc::set_handler(move || {
|
ctrlc::set_handler(move || {
|
||||||
cc.store(true, Ordering::SeqCst);
|
cc.store(true, Ordering::SeqCst);
|
||||||
})
|
})?;
|
||||||
.expect("Error setting Ctrl-C handler");
|
|
||||||
|
|
||||||
if context.ctrl_c.load(Ordering::SeqCst) {
|
if _context.ctrl_c.load(Ordering::SeqCst) {
|
||||||
context.ctrl_c.store(false, Ordering::SeqCst);
|
_context.ctrl_c.store(false, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// before we start up, let's run our startup commands
|
Ok(())
|
||||||
if let Ok(config) = nu_data::config::config(Tag::unknown()) {
|
}
|
||||||
if let Some(commands) = config.get("startup") {
|
|
||||||
|
async fn run_startup_commands(
|
||||||
|
context: &mut EvaluationContext,
|
||||||
|
config: &dyn nu_data::config::Conf,
|
||||||
|
) -> Result<(), ShellError> {
|
||||||
|
if let Some(commands) = config.var("startup") {
|
||||||
match commands {
|
match commands {
|
||||||
Value {
|
Value {
|
||||||
value: UntaggedValue::Table(pipelines),
|
value: UntaggedValue::Table(pipelines),
|
||||||
@ -324,36 +589,29 @@ pub async fn run_vec_of_pipelines(
|
|||||||
} => {
|
} => {
|
||||||
for pipeline in pipelines {
|
for pipeline in pipelines {
|
||||||
if let Ok(pipeline_string) = pipeline.as_string() {
|
if let Ok(pipeline_string) = pipeline.as_string() {
|
||||||
let _ = run_pipeline_standalone(
|
let _ =
|
||||||
pipeline_string,
|
run_pipeline_standalone(pipeline_string, false, context, false).await;
|
||||||
false,
|
|
||||||
&mut context,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("warning: expected a table of pipeline strings as startup commands");
|
return Err(ShellError::untagged_runtime_error(
|
||||||
}
|
"expected a table of pipeline strings as startup commands",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for pipeline in pipelines {
|
|
||||||
run_pipeline_standalone(pipeline, redirect_stdin, &mut context, true).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_pipeline_standalone(
|
pub async fn run_pipeline_standalone(
|
||||||
pipeline: String,
|
pipeline: String,
|
||||||
redirect_stdin: bool,
|
redirect_stdin: bool,
|
||||||
context: &mut Context,
|
context: &mut EvaluationContext,
|
||||||
exit_on_error: bool,
|
exit_on_error: bool,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
let line = process_line(Ok(pipeline), context, redirect_stdin, false).await;
|
let line = process_line(&pipeline, context, redirect_stdin, false).await;
|
||||||
|
|
||||||
match line {
|
match line {
|
||||||
LineResult::Success(line) => {
|
LineResult::Success(line) => {
|
||||||
@ -391,7 +649,8 @@ pub async fn run_pipeline_standalone(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_rustyline_configuration() -> (Editor<Helper>, IndexMap<String, Value>) {
|
#[cfg(feature = "rustyline-support")]
|
||||||
|
fn default_rustyline_editor_configuration() -> Editor<Helper> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::Circular;
|
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::Circular;
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
@ -410,6 +669,9 @@ pub fn create_rustyline_configuration() -> (Editor<Helper>, IndexMap<String, Val
|
|||||||
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
|
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// workaround for multiline-paste hang in rustyline (see https://github.com/kkawakam/rustyline/issues/202)
|
||||||
|
rl.bind_sequence(KeyPress::BracketedPasteStart, rustyline::Cmd::Noop);
|
||||||
|
|
||||||
// Let's set the defaults up front and then override them later if the user indicates
|
// Let's set the defaults up front and then override them later if the user indicates
|
||||||
// defaults taken from here https://github.com/kkawakam/rustyline/blob/2fe886c9576c1ea13ca0e5808053ad491a6fe049/src/config.rs#L150-L167
|
// defaults taken from here https://github.com/kkawakam/rustyline/blob/2fe886c9576c1ea13ca0e5808053ad491a6fe049/src/config.rs#L150-L167
|
||||||
rl.set_max_history_size(100);
|
rl.set_max_history_size(100);
|
||||||
@ -428,23 +690,20 @@ pub fn create_rustyline_configuration() -> (Editor<Helper>, IndexMap<String, Val
|
|||||||
println!("Error loading keybindings: {:?}", e);
|
println!("Error loading keybindings: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = match config::config(Tag::unknown()) {
|
rl
|
||||||
Ok(config) => config,
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Config could not be loaded.");
|
|
||||||
if let ShellError {
|
|
||||||
error: ProximateShellError::Diagnostic(ShellDiagnostic { diagnostic }),
|
|
||||||
..
|
|
||||||
} = e
|
|
||||||
{
|
|
||||||
eprintln!("{}", diagnostic.message);
|
|
||||||
}
|
}
|
||||||
IndexMap::new()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Ok(config) = config::config(Tag::unknown()) {
|
#[cfg(feature = "rustyline-support")]
|
||||||
if let Some(line_editor_vars) = config.get("line_editor") {
|
fn configure_rustyline_editor(
|
||||||
|
rl: &mut Editor<Helper>,
|
||||||
|
config: &dyn nu_data::config::Conf,
|
||||||
|
) -> Result<(), ShellError> {
|
||||||
|
#[cfg(windows)]
|
||||||
|
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::Circular;
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
|
||||||
|
|
||||||
|
if let Some(line_editor_vars) = config.var("line_editor") {
|
||||||
for (idx, value) in line_editor_vars.row_entries() {
|
for (idx, value) in line_editor_vars.row_entries() {
|
||||||
match idx.as_ref() {
|
match idx.as_ref() {
|
||||||
"max_history_size" => {
|
"max_history_size" => {
|
||||||
@ -500,9 +759,7 @@ pub fn create_rustyline_configuration() -> (Editor<Helper>, IndexMap<String, Val
|
|||||||
"edit_mode" => {
|
"edit_mode" => {
|
||||||
let edit_mode = match value.as_string() {
|
let edit_mode = match value.as_string() {
|
||||||
Ok(s) if s.to_lowercase() == "vi" => rustyline::config::EditMode::Vi,
|
Ok(s) if s.to_lowercase() == "vi" => rustyline::config::EditMode::Vi,
|
||||||
Ok(s) if s.to_lowercase() == "emacs" => {
|
Ok(s) if s.to_lowercase() == "emacs" => rustyline::config::EditMode::Emacs,
|
||||||
rustyline::config::EditMode::Emacs
|
|
||||||
}
|
|
||||||
_ => rustyline::config::EditMode::Emacs,
|
_ => rustyline::config::EditMode::Emacs,
|
||||||
};
|
};
|
||||||
rl.set_edit_mode(edit_mode);
|
rl.set_edit_mode(edit_mode);
|
||||||
@ -521,9 +778,7 @@ pub fn create_rustyline_configuration() -> (Editor<Helper>, IndexMap<String, Val
|
|||||||
Ok(s) if s.to_lowercase() == "audible" => {
|
Ok(s) if s.to_lowercase() == "audible" => {
|
||||||
rustyline::config::BellStyle::Audible
|
rustyline::config::BellStyle::Audible
|
||||||
}
|
}
|
||||||
Ok(s) if s.to_lowercase() == "none" => {
|
Ok(s) if s.to_lowercase() == "none" => rustyline::config::BellStyle::None,
|
||||||
rustyline::config::BellStyle::None
|
|
||||||
}
|
|
||||||
Ok(s) if s.to_lowercase() == "visible" => {
|
Ok(s) if s.to_lowercase() == "visible" => {
|
||||||
rustyline::config::BellStyle::Visible
|
rustyline::config::BellStyle::Visible
|
||||||
}
|
}
|
||||||
@ -535,9 +790,7 @@ pub fn create_rustyline_configuration() -> (Editor<Helper>, IndexMap<String, Val
|
|||||||
let color_mode = match value.as_string() {
|
let color_mode = match value.as_string() {
|
||||||
Ok(s) if s.to_lowercase() == "enabled" => rustyline::ColorMode::Enabled,
|
Ok(s) if s.to_lowercase() == "enabled" => rustyline::ColorMode::Enabled,
|
||||||
Ok(s) if s.to_lowercase() == "forced" => rustyline::ColorMode::Forced,
|
Ok(s) if s.to_lowercase() == "forced" => rustyline::ColorMode::Forced,
|
||||||
Ok(s) if s.to_lowercase() == "disabled" => {
|
Ok(s) if s.to_lowercase() == "disabled" => rustyline::ColorMode::Disabled,
|
||||||
rustyline::ColorMode::Disabled
|
|
||||||
}
|
|
||||||
_ => rustyline::ColorMode::Enabled,
|
_ => rustyline::ColorMode::Enabled,
|
||||||
};
|
};
|
||||||
rl.set_color_mode(color_mode);
|
rl.set_color_mode(color_mode);
|
||||||
@ -551,246 +804,29 @@ pub fn create_rustyline_configuration() -> (Editor<Helper>, IndexMap<String, Val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
(rl, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The entry point for the CLI. Will register all known internal commands, load experimental commands, load plugins, then prepare the prompt and line reader for input.
|
|
||||||
pub async fn cli(
|
|
||||||
mut syncer: EnvironmentSyncer,
|
|
||||||
mut context: Context,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
let configuration = nu_data::config::NuConfig::new();
|
|
||||||
let history_path = crate::commands::history::history_path(&configuration);
|
|
||||||
|
|
||||||
let _ = register_plugins(&mut context);
|
|
||||||
|
|
||||||
let (mut rl, config) = create_rustyline_configuration();
|
|
||||||
|
|
||||||
// we are ok if history does not exist
|
|
||||||
let _ = rl.load_history(&history_path);
|
|
||||||
|
|
||||||
let skip_welcome_message = config
|
|
||||||
.get("skip_welcome_message")
|
|
||||||
.map(|x| x.is_true())
|
|
||||||
.unwrap_or(false);
|
|
||||||
if !skip_welcome_message {
|
|
||||||
println!(
|
|
||||||
"Welcome to Nushell {} (type 'help' for more info)",
|
|
||||||
clap::crate_version!()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
let _ = ansi_term::enable_ansi_support();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "ctrlc")]
|
|
||||||
{
|
|
||||||
let cc = context.ctrl_c.clone();
|
|
||||||
|
|
||||||
ctrlc::set_handler(move || {
|
|
||||||
cc.store(true, Ordering::SeqCst);
|
|
||||||
})
|
|
||||||
.expect("Error setting Ctrl-C handler");
|
|
||||||
}
|
|
||||||
let mut ctrlcbreak = false;
|
|
||||||
|
|
||||||
// before we start up, let's run our startup commands
|
|
||||||
if let Ok(config) = nu_data::config::config(Tag::unknown()) {
|
|
||||||
if let Some(commands) = config.get("startup") {
|
|
||||||
match commands {
|
|
||||||
Value {
|
|
||||||
value: UntaggedValue::Table(pipelines),
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
for pipeline in pipelines {
|
|
||||||
if let Ok(pipeline_string) = pipeline.as_string() {
|
|
||||||
let _ = run_pipeline_standalone(
|
|
||||||
pipeline_string,
|
|
||||||
false,
|
|
||||||
&mut context,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
println!("warning: expected a table of pipeline strings as startup commands");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if context.ctrl_c.load(Ordering::SeqCst) {
|
|
||||||
context.ctrl_c.store(false, Ordering::SeqCst);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cwd = context.shell_manager.path();
|
|
||||||
|
|
||||||
let hinter = init_hinter(&config);
|
|
||||||
|
|
||||||
rl.set_helper(Some(crate::shell::Helper::new(context.clone(), hinter)));
|
|
||||||
|
|
||||||
let colored_prompt = {
|
|
||||||
if let Some(prompt) = config.get("prompt") {
|
|
||||||
let prompt_line = prompt.as_string()?;
|
|
||||||
|
|
||||||
match nu_parser::lite_parse(&prompt_line, 0).map_err(ShellError::from) {
|
|
||||||
Ok(result) => {
|
|
||||||
let mut prompt_block =
|
|
||||||
nu_parser::classify_block(&result, context.registry());
|
|
||||||
|
|
||||||
let env = context.get_env();
|
|
||||||
|
|
||||||
prompt_block.block.expand_it_usage();
|
|
||||||
|
|
||||||
match run_block(
|
|
||||||
&prompt_block.block,
|
|
||||||
&mut context,
|
|
||||||
InputStream::empty(),
|
|
||||||
&Value::nothing(),
|
|
||||||
&IndexMap::new(),
|
|
||||||
&env,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(result) => match result.collect_string(Tag::unknown()).await {
|
|
||||||
Ok(string_result) => {
|
|
||||||
let errors = context.get_errors();
|
|
||||||
context.maybe_print_errors(Text::from(prompt_line));
|
|
||||||
context.clear_errors();
|
|
||||||
|
|
||||||
if !errors.is_empty() {
|
|
||||||
"> ".to_string()
|
|
||||||
} else {
|
|
||||||
string_result.item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
crate::cli::print_err(e, &Text::from(prompt_line));
|
|
||||||
context.clear_errors();
|
|
||||||
|
|
||||||
"> ".to_string()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
crate::cli::print_err(e, &Text::from(prompt_line));
|
|
||||||
context.clear_errors();
|
|
||||||
|
|
||||||
"> ".to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
crate::cli::print_err(e, &Text::from(prompt_line));
|
|
||||||
context.clear_errors();
|
|
||||||
|
|
||||||
"> ".to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"\x1b[32m{}{}\x1b[m> ",
|
|
||||||
cwd,
|
|
||||||
match current_branch() {
|
|
||||||
Some(s) => format!("({})", s),
|
|
||||||
None => "".to_string(),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let prompt = {
|
|
||||||
if let Ok(bytes) = strip_ansi_escapes::strip(&colored_prompt) {
|
|
||||||
String::from_utf8_lossy(&bytes).to_string()
|
|
||||||
} else {
|
|
||||||
"> ".to_string()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
rl.helper_mut().expect("No helper").colored_prompt = colored_prompt;
|
|
||||||
let mut initial_command = Some(String::new());
|
|
||||||
let mut readline = Err(ReadlineError::Eof);
|
|
||||||
while let Some(ref cmd) = initial_command {
|
|
||||||
readline = rl.readline_with_initial(&prompt, (&cmd, ""));
|
|
||||||
initial_command = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let line = process_line(readline, &mut context, false, true).await;
|
|
||||||
|
|
||||||
// Check the config to see if we need to update the path
|
|
||||||
// TODO: make sure config is cached so we don't path this load every call
|
|
||||||
// FIXME: we probably want to be a bit more graceful if we can't set the environment
|
|
||||||
syncer.reload();
|
|
||||||
syncer.sync_env_vars(&mut context);
|
|
||||||
syncer.sync_path_vars(&mut context);
|
|
||||||
|
|
||||||
match line {
|
|
||||||
LineResult::Success(line) => {
|
|
||||||
rl.add_history_entry(&line);
|
|
||||||
let _ = rl.save_history(&history_path);
|
|
||||||
context.maybe_print_errors(Text::from(line));
|
|
||||||
}
|
|
||||||
|
|
||||||
LineResult::Error(line, err) => {
|
|
||||||
rl.add_history_entry(&line);
|
|
||||||
let _ = rl.save_history(&history_path);
|
|
||||||
|
|
||||||
context.with_host(|_host| {
|
|
||||||
print_err(err, &Text::from(line.clone()));
|
|
||||||
});
|
|
||||||
|
|
||||||
context.maybe_print_errors(Text::from(line.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
LineResult::CtrlC => {
|
|
||||||
let config_ctrlc_exit = config::config(Tag::unknown())?
|
|
||||||
.get("ctrlc_exit")
|
|
||||||
.map(|s| s.value.expect_string() == "true")
|
|
||||||
.unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells
|
|
||||||
|
|
||||||
if !config_ctrlc_exit {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctrlcbreak {
|
|
||||||
let _ = rl.save_history(&history_path);
|
|
||||||
std::process::exit(0);
|
|
||||||
} else {
|
|
||||||
context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)"));
|
|
||||||
ctrlcbreak = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LineResult::Break => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctrlcbreak = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we are ok if we can not save history
|
|
||||||
let _ = rl.save_history(&history_path);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_hinter(config: &IndexMap<String, Value>) -> Option<rustyline::hint::HistoryHinter> {
|
#[cfg(feature = "rustyline-support")]
|
||||||
// Show hints unless explicitly disabled in config
|
fn nu_line_editor_helper(
|
||||||
if let Some(line_editor_vars) = config.get("line_editor") {
|
context: &mut EvaluationContext,
|
||||||
|
config: &dyn nu_data::config::Conf,
|
||||||
|
) -> crate::shell::Helper {
|
||||||
|
let hinter = rustyline_hinter(config);
|
||||||
|
crate::shell::Helper::new(context.clone(), hinter)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rustyline-support")]
|
||||||
|
fn rustyline_hinter(config: &dyn nu_data::config::Conf) -> Option<rustyline::hint::HistoryHinter> {
|
||||||
|
if let Some(line_editor_vars) = config.var("line_editor") {
|
||||||
for (idx, value) in line_editor_vars.row_entries() {
|
for (idx, value) in line_editor_vars.row_entries() {
|
||||||
if idx == "show_hints" && value.expect_string() == "false" {
|
if idx == "show_hints" && value.expect_string() == "false" {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(rustyline::hint::HistoryHinter {})
|
Some(rustyline::hint::HistoryHinter {})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -806,11 +842,12 @@ fn chomp_newline(s: &str) -> &str {
|
|||||||
pub enum LineResult {
|
pub enum LineResult {
|
||||||
Success(String),
|
Success(String),
|
||||||
Error(String, ShellError),
|
Error(String, ShellError),
|
||||||
CtrlC,
|
|
||||||
Break,
|
Break,
|
||||||
|
CtrlC,
|
||||||
|
CtrlD,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn parse_and_eval(line: &str, ctx: &mut Context) -> Result<String, ShellError> {
|
pub async fn parse_and_eval(line: &str, ctx: &mut EvaluationContext) -> Result<String, ShellError> {
|
||||||
let line = if line.ends_with('\n') {
|
let line = if line.ends_with('\n') {
|
||||||
&line[..line.len() - 1]
|
&line[..line.len() - 1]
|
||||||
} else {
|
} else {
|
||||||
@ -830,9 +867,7 @@ pub async fn parse_and_eval(line: &str, ctx: &mut Context) -> Result<String, She
|
|||||||
&classified_block.block,
|
&classified_block.block,
|
||||||
ctx,
|
ctx,
|
||||||
input_stream,
|
input_stream,
|
||||||
&Value::nothing(),
|
Scope::from_env(env),
|
||||||
&IndexMap::new(),
|
|
||||||
&env,
|
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
.collect_string(Tag::unknown())
|
.collect_string(Tag::unknown())
|
||||||
@ -842,15 +877,14 @@ pub async fn parse_and_eval(line: &str, ctx: &mut Context) -> Result<String, She
|
|||||||
|
|
||||||
/// Process the line by parsing the text to turn it into commands, classify those commands so that we understand what is being called in the pipeline, and then run this pipeline
|
/// Process the line by parsing the text to turn it into commands, classify those commands so that we understand what is being called in the pipeline, and then run this pipeline
|
||||||
pub async fn process_line(
|
pub async fn process_line(
|
||||||
readline: Result<String, ReadlineError>,
|
line: &str,
|
||||||
ctx: &mut Context,
|
ctx: &mut EvaluationContext,
|
||||||
redirect_stdin: bool,
|
redirect_stdin: bool,
|
||||||
cli_mode: bool,
|
cli_mode: bool,
|
||||||
) -> LineResult {
|
) -> LineResult {
|
||||||
match &readline {
|
if line.trim() == "" {
|
||||||
Ok(line) if line.trim() == "" => LineResult::Success(line.clone()),
|
LineResult::Success(line.to_string())
|
||||||
|
} else {
|
||||||
Ok(line) => {
|
|
||||||
let line = chomp_newline(line);
|
let line = chomp_newline(line);
|
||||||
ctx.raw_input = line.to_string();
|
ctx.raw_input = line.to_string();
|
||||||
|
|
||||||
@ -990,9 +1024,7 @@ pub async fn process_line(
|
|||||||
&classified_block.block,
|
&classified_block.block,
|
||||||
ctx,
|
ctx,
|
||||||
input_stream,
|
input_stream,
|
||||||
&Value::nothing(),
|
Scope::from_env(env),
|
||||||
&IndexMap::new(),
|
|
||||||
&env,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@ -1038,13 +1070,6 @@ pub async fn process_line(
|
|||||||
Err(err) => LineResult::Error(line.to_string(), err),
|
Err(err) => LineResult::Error(line.to_string(), err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Interrupted) => LineResult::CtrlC,
|
|
||||||
Err(ReadlineError::Eof) => LineResult::Break,
|
|
||||||
Err(err) => {
|
|
||||||
outln!("Error: {:?}", err);
|
|
||||||
LineResult::Break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_err(err: ShellError, source: &Text) {
|
pub fn print_err(err: ShellError, source: &Text) {
|
||||||
@ -1069,7 +1094,7 @@ mod tests {
|
|||||||
#[quickcheck]
|
#[quickcheck]
|
||||||
fn quickcheck_parse(data: String) -> bool {
|
fn quickcheck_parse(data: String) -> bool {
|
||||||
if let Ok(lite_block) = nu_parser::lite_parse(&data, 0) {
|
if let Ok(lite_block) = nu_parser::lite_parse(&data, 0) {
|
||||||
let context = crate::context::Context::basic().unwrap();
|
let context = crate::evaluation_context::EvaluationContext::basic().unwrap();
|
||||||
let _ = nu_parser::classify_block(&lite_block, context.registry());
|
let _ = nu_parser::classify_block(&lite_block, context.registry());
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
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 cal;
|
||||||
pub(crate) mod cd;
|
pub(crate) mod cd;
|
||||||
pub(crate) mod char_;
|
pub(crate) mod char_;
|
||||||
|
pub(crate) mod chart;
|
||||||
pub(crate) mod classified;
|
pub(crate) mod classified;
|
||||||
#[cfg(feature = "clipboard")]
|
#[cfg(feature = "clipboard-cli")]
|
||||||
pub(crate) mod clip;
|
pub(crate) mod clip;
|
||||||
pub(crate) mod command;
|
pub(crate) mod command;
|
||||||
pub(crate) mod compact;
|
pub(crate) mod compact;
|
||||||
@ -29,13 +30,16 @@ pub(crate) mod cp;
|
|||||||
pub(crate) mod date;
|
pub(crate) mod date;
|
||||||
pub(crate) mod debug;
|
pub(crate) mod debug;
|
||||||
pub(crate) mod default;
|
pub(crate) mod default;
|
||||||
|
pub(crate) mod describe;
|
||||||
pub(crate) mod do_;
|
pub(crate) mod do_;
|
||||||
pub(crate) mod drop;
|
pub(crate) mod drop;
|
||||||
pub(crate) mod du;
|
pub(crate) mod du;
|
||||||
pub(crate) mod each;
|
pub(crate) mod each;
|
||||||
pub(crate) mod echo;
|
pub(crate) mod echo;
|
||||||
|
pub(crate) mod empty;
|
||||||
pub(crate) mod enter;
|
pub(crate) mod enter;
|
||||||
pub(crate) mod every;
|
pub(crate) mod every;
|
||||||
|
pub(crate) mod exec;
|
||||||
pub(crate) mod exit;
|
pub(crate) mod exit;
|
||||||
pub(crate) mod first;
|
pub(crate) mod first;
|
||||||
pub(crate) mod format;
|
pub(crate) mod format;
|
||||||
@ -64,7 +68,6 @@ pub(crate) mod history;
|
|||||||
pub(crate) mod if_;
|
pub(crate) mod if_;
|
||||||
pub(crate) mod insert;
|
pub(crate) mod insert;
|
||||||
pub(crate) mod into_int;
|
pub(crate) mod into_int;
|
||||||
pub(crate) mod is_empty;
|
|
||||||
pub(crate) mod keep;
|
pub(crate) mod keep;
|
||||||
pub(crate) mod last;
|
pub(crate) mod last;
|
||||||
pub(crate) mod lines;
|
pub(crate) mod lines;
|
||||||
@ -75,6 +78,7 @@ pub(crate) mod mkdir;
|
|||||||
pub(crate) mod move_;
|
pub(crate) mod move_;
|
||||||
pub(crate) mod next;
|
pub(crate) mod next;
|
||||||
pub(crate) mod nth;
|
pub(crate) mod nth;
|
||||||
|
pub(crate) mod nu;
|
||||||
pub(crate) mod open;
|
pub(crate) mod open;
|
||||||
pub(crate) mod parse;
|
pub(crate) mod parse;
|
||||||
pub(crate) mod path;
|
pub(crate) mod path;
|
||||||
@ -114,12 +118,10 @@ pub(crate) mod to_tsv;
|
|||||||
pub(crate) mod to_url;
|
pub(crate) mod to_url;
|
||||||
pub(crate) mod to_xml;
|
pub(crate) mod to_xml;
|
||||||
pub(crate) mod to_yaml;
|
pub(crate) mod to_yaml;
|
||||||
pub(crate) mod trim;
|
|
||||||
pub(crate) mod uniq;
|
pub(crate) mod uniq;
|
||||||
pub(crate) mod update;
|
pub(crate) mod update;
|
||||||
pub(crate) mod url_;
|
pub(crate) mod url_;
|
||||||
pub(crate) mod version;
|
pub(crate) mod version;
|
||||||
pub(crate) mod what;
|
|
||||||
pub(crate) mod where_;
|
pub(crate) mod where_;
|
||||||
pub(crate) mod which_;
|
pub(crate) mod which_;
|
||||||
pub(crate) mod with_env;
|
pub(crate) mod with_env;
|
||||||
@ -133,7 +135,7 @@ pub(crate) use command::{
|
|||||||
|
|
||||||
pub(crate) use alias::Alias;
|
pub(crate) use alias::Alias;
|
||||||
pub(crate) use ansi::Ansi;
|
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::Autoenv;
|
||||||
pub(crate) use autoenv_trust::AutoenvTrust;
|
pub(crate) use autoenv_trust::AutoenvTrust;
|
||||||
pub(crate) use autoenv_untrust::AutoenvUnTrust;
|
pub(crate) use autoenv_untrust::AutoenvUnTrust;
|
||||||
@ -141,6 +143,7 @@ pub(crate) use benchmark::Benchmark;
|
|||||||
pub(crate) use build_string::BuildString;
|
pub(crate) use build_string::BuildString;
|
||||||
pub(crate) use cal::Cal;
|
pub(crate) use cal::Cal;
|
||||||
pub(crate) use char_::Char;
|
pub(crate) use char_::Char;
|
||||||
|
pub(crate) use chart::Chart;
|
||||||
pub(crate) use compact::Compact;
|
pub(crate) use compact::Compact;
|
||||||
pub(crate) use config::{
|
pub(crate) use config::{
|
||||||
Config, ConfigClear, ConfigGet, ConfigLoad, ConfigPath, ConfigRemove, ConfigSet, ConfigSetInto,
|
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 date::{Date, DateFormat, DateNow, DateUTC};
|
||||||
pub(crate) use debug::Debug;
|
pub(crate) use debug::Debug;
|
||||||
pub(crate) use default::Default;
|
pub(crate) use default::Default;
|
||||||
|
pub(crate) use describe::Describe;
|
||||||
pub(crate) use do_::Do;
|
pub(crate) use do_::Do;
|
||||||
pub(crate) use drop::Drop;
|
pub(crate) use drop::Drop;
|
||||||
pub(crate) use du::Du;
|
pub(crate) use du::Du;
|
||||||
pub(crate) use each::Each;
|
pub(crate) use each::Each;
|
||||||
|
pub(crate) use each::EachGroup;
|
||||||
|
pub(crate) use each::EachWindow;
|
||||||
pub(crate) use echo::Echo;
|
pub(crate) use echo::Echo;
|
||||||
|
pub(crate) use empty::Command as Empty;
|
||||||
pub(crate) use if_::If;
|
pub(crate) use if_::If;
|
||||||
pub(crate) use is_empty::IsEmpty;
|
pub(crate) use nu::NuPlugin;
|
||||||
pub(crate) use update::Update;
|
pub(crate) use update::Command as Update;
|
||||||
pub(crate) mod kill;
|
pub(crate) mod kill;
|
||||||
pub(crate) use kill::Kill;
|
pub(crate) use kill::Kill;
|
||||||
pub(crate) mod clear;
|
pub(crate) mod clear;
|
||||||
@ -165,6 +172,7 @@ pub(crate) use clear::Clear;
|
|||||||
pub(crate) mod touch;
|
pub(crate) mod touch;
|
||||||
pub(crate) use enter::Enter;
|
pub(crate) use enter::Enter;
|
||||||
pub(crate) use every::Every;
|
pub(crate) use every::Every;
|
||||||
|
pub(crate) use exec::Exec;
|
||||||
pub(crate) use exit::Exit;
|
pub(crate) use exit::Exit;
|
||||||
pub(crate) use first::First;
|
pub(crate) use first::First;
|
||||||
pub(crate) use format::Format;
|
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::FromYAML;
|
||||||
pub(crate) use from_yaml::FromYML;
|
pub(crate) use from_yaml::FromYML;
|
||||||
pub(crate) use get::Get;
|
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 group_by_date::GroupByDate;
|
||||||
pub(crate) use headers::Headers;
|
pub(crate) use headers::Headers;
|
||||||
pub(crate) use help::Help;
|
pub(crate) use help::Help;
|
||||||
pub(crate) use histogram::Histogram;
|
pub(crate) use histogram::Histogram;
|
||||||
pub(crate) use history::History;
|
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 into_int::IntoInt;
|
||||||
pub(crate) use keep::{Keep, KeepUntil, KeepWhile};
|
pub(crate) use keep::{Keep, KeepUntil, KeepWhile};
|
||||||
pub(crate) use last::Last;
|
pub(crate) use last::Last;
|
||||||
@ -218,7 +226,7 @@ pub(crate) use prev::Previous;
|
|||||||
pub(crate) use pwd::Pwd;
|
pub(crate) use pwd::Pwd;
|
||||||
#[cfg(feature = "uuid_crate")]
|
#[cfg(feature = "uuid_crate")]
|
||||||
pub(crate) use random::RandomUUID;
|
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 range::Range;
|
||||||
pub(crate) use reduce::Reduce;
|
pub(crate) use reduce::Reduce;
|
||||||
pub(crate) use reject::Reject;
|
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 split_by::SplitBy;
|
||||||
pub(crate) use str_::{
|
pub(crate) use str_::{
|
||||||
Str, StrCamelCase, StrCapitalize, StrCollect, StrContains, StrDowncase, StrEndsWith,
|
Str, StrCamelCase, StrCapitalize, StrCollect, StrContains, StrDowncase, StrEndsWith,
|
||||||
StrFindReplace, StrFrom, StrIndexOf, StrKebabCase, StrLength, StrPascalCase, StrReverse,
|
StrFindReplace, StrFrom, StrIndexOf, StrKebabCase, StrLPad, StrLength, StrPascalCase, StrRPad,
|
||||||
StrScreamingSnakeCase, StrSet, StrSnakeCase, StrStartsWith, StrSubstring, StrToDatetime,
|
StrReverse, StrScreamingSnakeCase, StrSet, StrSnakeCase, StrStartsWith, StrSubstring,
|
||||||
StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
|
StrToDatetime, StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
|
||||||
};
|
};
|
||||||
pub(crate) use table::Table;
|
pub(crate) use table::Table;
|
||||||
pub(crate) use tags::Tags;
|
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_xml::ToXML;
|
||||||
pub(crate) use to_yaml::ToYAML;
|
pub(crate) use to_yaml::ToYAML;
|
||||||
pub(crate) use touch::Touch;
|
pub(crate) use touch::Touch;
|
||||||
pub(crate) use trim::Trim;
|
|
||||||
pub(crate) use uniq::Uniq;
|
pub(crate) use uniq::Uniq;
|
||||||
pub(crate) use url_::{UrlCommand, UrlHost, UrlPath, UrlQuery, UrlScheme};
|
pub(crate) use url_::{UrlCommand, UrlHost, UrlPath, UrlQuery, UrlScheme};
|
||||||
pub(crate) use version::Version;
|
pub(crate) use version::Version;
|
||||||
pub(crate) use what::What;
|
|
||||||
pub(crate) use where_::Where;
|
pub(crate) use where_::Where;
|
||||||
pub(crate) use which_::Which;
|
pub(crate) use which_::Which;
|
||||||
pub(crate) use with_env::WithEnv;
|
pub(crate) use with_env::WithEnv;
|
||||||
pub(crate) use wrap::Wrap;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_data::config;
|
use nu_data::config;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
@ -340,11 +340,12 @@ fn find_block_shapes(block: &Block, registry: &CommandRegistry) -> Result<ShapeM
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Alias;
|
use super::Alias;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Ansi;
|
use super::Ansi;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct AppendArgs {
|
struct Arguments {
|
||||||
row: Value,
|
row: Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Append;
|
pub struct Command;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl WholeStreamCommand for Append {
|
impl WholeStreamCommand for Command {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"append"
|
"append"
|
||||||
}
|
}
|
||||||
@ -34,11 +34,18 @@ impl WholeStreamCommand for Append {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> 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> {
|
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,
|
external_redirection: ExternalRedirection::Stdout,
|
||||||
},
|
},
|
||||||
name_tag: context.name.clone(),
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Command;
|
use super::Command;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(Command {})
|
Ok(test_examples(Command {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,25 +10,16 @@ pub enum AutoPivotMode {
|
|||||||
|
|
||||||
impl AutoPivotMode {
|
impl AutoPivotMode {
|
||||||
pub fn is_auto(&self) -> bool {
|
pub fn is_auto(&self) -> bool {
|
||||||
match &self {
|
matches!(self, AutoPivotMode::Auto)
|
||||||
AutoPivotMode::Auto => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_always(&self) -> bool {
|
pub fn is_always(&self) -> bool {
|
||||||
match &self {
|
matches!(self, AutoPivotMode::Always)
|
||||||
AutoPivotMode::Always => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn is_never(&self) -> bool {
|
pub fn is_never(&self) -> bool {
|
||||||
match &self {
|
matches!(self, AutoPivotMode::Never)
|
||||||
AutoPivotMode::Never => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +28,7 @@ pub trait ConfigExtensions: Debug + Send {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn pivot_mode(config: &NuConfig) -> AutoPivotMode {
|
pub fn pivot_mode(config: &NuConfig) -> AutoPivotMode {
|
||||||
let vars = config.vars.lock();
|
let vars = &config.vars;
|
||||||
|
|
||||||
if let Some(mode) = vars.get("pivot_mode") {
|
if let Some(mode) = vars.get("pivot_mode") {
|
||||||
let mode = match mode.as_string() {
|
let mode = match mode.as_string() {
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
use crate::commands::classified::block::run_block;
|
use crate::commands::classified::block::run_block;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
#[cfg(feature = "rich-benchmark")]
|
||||||
|
use heim::cpu::time;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
hir::Block, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
hir::{Block, ClassifiedCommand, Commands, InternalCommand},
|
||||||
|
Dictionary, Scope, Signature, SyntaxShape, UntaggedValue, Value,
|
||||||
};
|
};
|
||||||
|
use rand::{
|
||||||
use chrono::prelude::*;
|
distributions::Alphanumeric,
|
||||||
|
prelude::{thread_rng, Rng},
|
||||||
|
};
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
pub struct Benchmark;
|
pub struct Benchmark;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct BenchmarkArgs {
|
struct BenchmarkArgs {
|
||||||
block: Block,
|
block: Block,
|
||||||
|
passthrough: Option<Block>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@ -22,15 +30,22 @@ impl WholeStreamCommand for Benchmark {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("benchmark").required(
|
Signature::build("benchmark")
|
||||||
|
.required(
|
||||||
"block",
|
"block",
|
||||||
SyntaxShape::Block,
|
SyntaxShape::Block,
|
||||||
"the block to run and benchmark",
|
"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 {
|
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(
|
async fn run(
|
||||||
@ -40,6 +55,21 @@ impl WholeStreamCommand for Benchmark {
|
|||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
benchmark(args, registry).await
|
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(
|
async fn benchmark(
|
||||||
@ -48,31 +78,135 @@ async fn benchmark(
|
|||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
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 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(
|
let start_time = Instant::now();
|
||||||
&block,
|
|
||||||
&mut context,
|
|
||||||
input,
|
|
||||||
&scope.it,
|
|
||||||
&scope.vars,
|
|
||||||
&scope.env,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let _ = result?.drain_vec().await;
|
#[cfg(feature = "rich-benchmark")]
|
||||||
let run_duration: chrono::Duration = Utc::now().signed_duration_since(start_time);
|
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();
|
context.clear_errors();
|
||||||
|
|
||||||
let output = Ok(ReturnSuccess::Value(Value {
|
// return basic runtime
|
||||||
value: UntaggedValue::Primitive(Primitive::from(run_duration)),
|
#[cfg(not(feature = "rich-benchmark"))]
|
||||||
tag: Tag::from(block.span),
|
{
|
||||||
}));
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Cal;
|
use super::Cal;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(Cal {})
|
Ok(test_examples(Cal {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,11 +72,12 @@ impl WholeStreamCommand for Cd {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Cd;
|
use super::Cd;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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()), // ⚡
|
"high_voltage_sign" | "elevated" => Some('\u{26a1}'.to_string()), // ⚡
|
||||||
"tilde" | "twiddle" | "squiggly" | "home" => Some("~".into()), // ~
|
"tilde" | "twiddle" | "squiggly" | "home" => Some("~".into()), // ~
|
||||||
"hash" | "hashtag" | "pound_sign" | "sharp" | "root" => 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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,11 +130,12 @@ fn str_to_character(s: &str) -> Option<String> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Char;
|
use super::Char;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::expr::run_expression_block;
|
||||||
use crate::commands::classified::internal::run_internal_command;
|
use crate::commands::classified::internal::run_internal_command;
|
||||||
use crate::context::Context;
|
use crate::evaluation_context::EvaluationContext;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::stream::InputStream;
|
use crate::stream::InputStream;
|
||||||
use futures::stream::TryStreamExt;
|
use futures::stream::TryStreamExt;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::hir::{Block, ClassifiedCommand, Commands};
|
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;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
pub(crate) async fn run_block(
|
pub(crate) async fn run_block(
|
||||||
block: &Block,
|
block: &Block,
|
||||||
ctx: &mut Context,
|
ctx: &mut EvaluationContext,
|
||||||
mut input: InputStream,
|
mut input: InputStream,
|
||||||
it: &Value,
|
scope: Arc<Scope>,
|
||||||
vars: &IndexMap<String, Value>,
|
|
||||||
env: &IndexMap<String, String>,
|
|
||||||
) -> Result<InputStream, ShellError> {
|
) -> Result<InputStream, ShellError> {
|
||||||
let mut output: Result<InputStream, ShellError> = Ok(InputStream::empty());
|
let mut output: Result<InputStream, ShellError> = Ok(InputStream::empty());
|
||||||
for pipeline in &block.block {
|
for pipeline in &block.block {
|
||||||
@ -54,7 +52,7 @@ pub(crate) async fn run_block(
|
|||||||
return Err(e);
|
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();
|
input = InputStream::empty();
|
||||||
}
|
}
|
||||||
@ -64,11 +62,9 @@ pub(crate) async fn run_block(
|
|||||||
|
|
||||||
async fn run_pipeline(
|
async fn run_pipeline(
|
||||||
commands: &Commands,
|
commands: &Commands,
|
||||||
ctx: &mut Context,
|
ctx: &mut EvaluationContext,
|
||||||
mut input: InputStream,
|
mut input: InputStream,
|
||||||
it: &Value,
|
scope: Arc<Scope>,
|
||||||
vars: &IndexMap<String, Value>,
|
|
||||||
env: &IndexMap<String, String>,
|
|
||||||
) -> Result<InputStream, ShellError> {
|
) -> Result<InputStream, ShellError> {
|
||||||
for item in commands.list.clone() {
|
for item in commands.list.clone() {
|
||||||
input = match item {
|
input = match item {
|
||||||
@ -77,13 +73,13 @@ async fn run_pipeline(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ClassifiedCommand::Expr(expr) => {
|
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::Error(err) => return Err(err.into()),
|
||||||
|
|
||||||
ClassifiedCommand::Internal(left) => {
|
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 futures::stream::once;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::hir::SpannedExpression;
|
use nu_protocol::hir::SpannedExpression;
|
||||||
use nu_protocol::Value;
|
use nu_protocol::Scope;
|
||||||
|
|
||||||
pub(crate) async fn run_expression_block(
|
pub(crate) async fn run_expression_block(
|
||||||
expr: SpannedExpression,
|
expr: SpannedExpression,
|
||||||
context: &mut Context,
|
context: &mut EvaluationContext,
|
||||||
it: &Value,
|
scope: Arc<Scope>,
|
||||||
vars: &IndexMap<String, Value>,
|
|
||||||
env: &IndexMap<String, String>,
|
|
||||||
) -> Result<InputStream, ShellError> {
|
) -> Result<InputStream, ShellError> {
|
||||||
if log_enabled!(log::Level::Trace) {
|
if log_enabled!(log::Level::Trace) {
|
||||||
trace!(target: "nu::run::expr", "->");
|
trace!(target: "nu::run::expr", "->");
|
||||||
@ -21,7 +19,7 @@ pub(crate) async fn run_expression_block(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let registry = context.registry().clone();
|
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())
|
Ok(once(async { Ok(output) }).to_input_stream())
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,9 @@ use nu_source::Tag;
|
|||||||
|
|
||||||
pub(crate) async fn run_external_command(
|
pub(crate) async fn run_external_command(
|
||||||
command: ExternalCommand,
|
command: ExternalCommand,
|
||||||
context: &mut Context,
|
context: &mut EvaluationContext,
|
||||||
input: InputStream,
|
input: InputStream,
|
||||||
scope: &Scope,
|
scope: Arc<Scope>,
|
||||||
external_redirection: ExternalRedirection,
|
external_redirection: ExternalRedirection,
|
||||||
) -> Result<InputStream, ShellError> {
|
) -> Result<InputStream, ShellError> {
|
||||||
trace!(target: "nu::run::external", "-> {}", command.name);
|
trace!(target: "nu::run::external", "-> {}", command.name);
|
||||||
@ -39,9 +39,9 @@ pub(crate) async fn run_external_command(
|
|||||||
|
|
||||||
async fn run_with_stdin(
|
async fn run_with_stdin(
|
||||||
command: ExternalCommand,
|
command: ExternalCommand,
|
||||||
context: &mut Context,
|
context: &mut EvaluationContext,
|
||||||
input: InputStream,
|
input: InputStream,
|
||||||
scope: &Scope,
|
scope: Arc<Scope>,
|
||||||
external_redirection: ExternalRedirection,
|
external_redirection: ExternalRedirection,
|
||||||
) -> Result<InputStream, ShellError> {
|
) -> Result<InputStream, ShellError> {
|
||||||
let path = context.shell_manager.path();
|
let path = context.shell_manager.path();
|
||||||
@ -50,9 +50,7 @@ async fn run_with_stdin(
|
|||||||
|
|
||||||
let mut command_args = vec![];
|
let mut command_args = vec![];
|
||||||
for arg in command.args.iter() {
|
for arg in command.args.iter() {
|
||||||
let value =
|
let value = evaluate_baseline_expr(arg, &context.registry, scope.clone()).await?;
|
||||||
evaluate_baseline_expr(arg, &context.registry, &scope.it, &scope.vars, &scope.env)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Skip any arguments that don't really exist, treating them as optional
|
// 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
|
// 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],
|
args: &[String],
|
||||||
input: InputStream,
|
input: InputStream,
|
||||||
external_redirection: ExternalRedirection,
|
external_redirection: ExternalRedirection,
|
||||||
scope: &Scope,
|
scope: Arc<Scope>,
|
||||||
) -> Result<InputStream, ShellError> {
|
) -> Result<InputStream, ShellError> {
|
||||||
let command = command.clone();
|
let command = command.clone();
|
||||||
|
|
||||||
@ -169,7 +167,7 @@ fn spawn(
|
|||||||
trace!(target: "nu::run::external", "cwd = {:?}", &path);
|
trace!(target: "nu::run::external", "cwd = {:?}", &path);
|
||||||
|
|
||||||
process.env_clear();
|
process.env_clear();
|
||||||
process.envs(scope.env.iter());
|
process.envs(scope.env());
|
||||||
|
|
||||||
// We want stdout regardless of what
|
// We want stdout regardless of what
|
||||||
// we are doing ($it case or pipe stdin)
|
// 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,
|
add_quotes, argument_contains_whitespace, argument_is_quoted, expand_tilde, remove_quotes,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "which")]
|
#[cfg(feature = "which")]
|
||||||
use super::{run_external_command, Context, InputStream};
|
use super::{run_external_command, EvaluationContext, InputStream};
|
||||||
|
|
||||||
#[cfg(feature = "which")]
|
#[cfg(feature = "which")]
|
||||||
use futures::executor::block_on;
|
use futures::executor::block_on;
|
||||||
@ -573,13 +571,14 @@ mod tests {
|
|||||||
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
|
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
|
||||||
|
|
||||||
let input = InputStream::empty();
|
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(
|
assert!(run_external_command(
|
||||||
cmd,
|
cmd,
|
||||||
&mut ctx,
|
&mut ctx,
|
||||||
input,
|
input,
|
||||||
&Scope::new(),
|
Scope::create(),
|
||||||
ExternalRedirection::Stdout
|
ExternalRedirection::Stdout
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -591,7 +590,7 @@ mod tests {
|
|||||||
// async fn failure_run() -> Result<(), ShellError> {
|
// async fn failure_run() -> Result<(), ShellError> {
|
||||||
// let cmd = ExternalBuilder::for_name("fail").build();
|
// 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)
|
// let stream = run_external_command(cmd, &mut ctx, None, false)
|
||||||
// .await?
|
// .await?
|
||||||
// .expect("There was a problem running the external command.");
|
// .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(
|
pub(crate) async fn run_internal_command(
|
||||||
command: InternalCommand,
|
command: InternalCommand,
|
||||||
context: &mut Context,
|
context: &mut EvaluationContext,
|
||||||
input: InputStream,
|
input: InputStream,
|
||||||
it: &Value,
|
scope: Arc<Scope>,
|
||||||
vars: &IndexMap<String, Value>,
|
|
||||||
env: &IndexMap<String, String>,
|
|
||||||
) -> Result<InputStream, ShellError> {
|
) -> Result<InputStream, ShellError> {
|
||||||
if log_enabled!(log::Level::Trace) {
|
if log_enabled!(log::Level::Trace) {
|
||||||
trace!(target: "nu::run::internal", "->");
|
trace!(target: "nu::run::internal", "->");
|
||||||
trace!(target: "nu::run::internal", "{}", command.name);
|
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 objects: InputStream = trace_stream!(target: "nu::trace_stream::internal", "input" = input);
|
||||||
let internal_command = context.expect_command(&command.name);
|
let internal_command = context.expect_command(&command.name);
|
||||||
|
|
||||||
@ -38,7 +31,7 @@ pub(crate) async fn run_internal_command(
|
|||||||
internal_command?,
|
internal_command?,
|
||||||
Tag::unknown_anchor(command.name_span),
|
Tag::unknown_anchor(command.name_span),
|
||||||
command.args.clone(),
|
command.args.clone(),
|
||||||
&scope,
|
scope.clone(),
|
||||||
objects,
|
objects,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
@ -48,8 +41,6 @@ pub(crate) async fn run_internal_command(
|
|||||||
//let context = Arc::new(context.clone());
|
//let context = Arc::new(context.clone());
|
||||||
let context = context.clone();
|
let context = context.clone();
|
||||||
let command = Arc::new(command);
|
let command = Arc::new(command);
|
||||||
let scope = Arc::new(scope);
|
|
||||||
// let scope = scope.clone();
|
|
||||||
|
|
||||||
Ok(InputStream::from_stream(
|
Ok(InputStream::from_stream(
|
||||||
result
|
result
|
||||||
@ -90,7 +81,7 @@ pub(crate) async fn run_internal_command(
|
|||||||
external_redirection: ExternalRedirection::Stdout,
|
external_redirection: ExternalRedirection::Stdout,
|
||||||
},
|
},
|
||||||
name_tag: Tag::unknown_anchor(command.name_span),
|
name_tag: Tag::unknown_anchor(command.name_span),
|
||||||
scope: (&*scope).clone(),
|
scope,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let result = converter
|
let result = converter
|
||||||
@ -200,6 +191,28 @@ pub(crate) async fn run_internal_command(
|
|||||||
)]);
|
)]);
|
||||||
InputStream::from_stream(futures::stream::iter(vec![]))
|
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 => {
|
CommandAction::PreviousShell => {
|
||||||
context.shell_manager.prev();
|
context.shell_manager.prev();
|
||||||
InputStream::from_stream(futures::stream::iter(vec![]))
|
InputStream::from_stream(futures::stream::iter(vec![]))
|
||||||
|
@ -130,7 +130,7 @@ async fn run_filter(
|
|||||||
UntaggedValue::Primitive(Primitive::EndOfStream).into_untagged_value()
|
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 real_path = Path::new(&path);
|
||||||
let ext = real_path.extension();
|
let ext = real_path.extension();
|
||||||
|
@ -17,7 +17,7 @@ impl WholeStreamCommand for Clear {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"clears the terminal"
|
"Clears the terminal"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(&self, _: CommandArgs, _: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
@ -32,11 +32,18 @@ impl WholeStreamCommand for Clip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
|
Example {
|
||||||
description: "Save text to the clipboard",
|
description: "Save text to the clipboard",
|
||||||
example: "echo 'secret value' | clip",
|
example: "echo 'secret value' | clip",
|
||||||
result: None,
|
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;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let string: String = match i.as_string() {
|
let string: String = i.convert_to_string();
|
||||||
Ok(string) => string.to_string(),
|
if string.is_empty() {
|
||||||
Err(_) => {
|
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Given non-string data",
|
"Unable to convert to string",
|
||||||
"expected strings from pipeline",
|
"Unable to convert to string",
|
||||||
name,
|
name,
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
new_copy_data.push_str(&string);
|
new_copy_data.push_str(&string);
|
||||||
}
|
}
|
||||||
@ -99,11 +104,12 @@ pub async fn clip(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Clip;
|
use super::Clip;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::help::get_help;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::deserializer::ConfigDeserializer;
|
use crate::deserializer::ConfigDeserializer;
|
||||||
use crate::evaluate::evaluate_args::evaluate_args;
|
use crate::evaluate::evaluate_args::evaluate_args;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -17,27 +17,12 @@ use std::sync::atomic::AtomicBool;
|
|||||||
pub struct UnevaluatedCallInfo {
|
pub struct UnevaluatedCallInfo {
|
||||||
pub args: hir::Call,
|
pub args: hir::Call,
|
||||||
pub name_tag: Tag,
|
pub name_tag: Tag,
|
||||||
pub scope: Scope,
|
pub scope: Arc<Scope>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnevaluatedCallInfo {
|
impl UnevaluatedCallInfo {
|
||||||
pub async fn evaluate(self, registry: &CommandRegistry) -> Result<CallInfo, ShellError> {
|
pub async fn evaluate(self, registry: &CommandRegistry) -> Result<CallInfo, ShellError> {
|
||||||
let args = evaluate_args(&self.args, registry, &self.scope).await?;
|
let args = evaluate_args(&self.args, registry, self.scope.clone()).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?;
|
|
||||||
|
|
||||||
Ok(CallInfo {
|
Ok(CallInfo {
|
||||||
args,
|
args,
|
||||||
@ -115,7 +100,7 @@ impl CommandArgs {
|
|||||||
pub async fn evaluate_once_with_scope(
|
pub async fn evaluate_once_with_scope(
|
||||||
self,
|
self,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
scope: &Scope,
|
scope: Arc<Scope>,
|
||||||
) -> Result<EvaluatedWholeStreamCommandArgs, ShellError> {
|
) -> Result<EvaluatedWholeStreamCommandArgs, ShellError> {
|
||||||
let host = self.host.clone();
|
let host = self.host.clone();
|
||||||
let ctrl_c = self.ctrl_c.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)]
|
#[derive(Getters, new)]
|
||||||
#[get = "pub(crate)"]
|
#[get = "pub(crate)"]
|
||||||
pub struct EvaluatedCommandArgs {
|
pub struct EvaluatedCommandArgs {
|
||||||
@ -303,6 +257,11 @@ pub trait WholeStreamCommand: Send + Sync {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Commands that are not meant to be run by users
|
||||||
|
fn is_internal(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
@ -367,74 +326,15 @@ impl Command {
|
|||||||
self.0.is_binary()
|
self.0.is_binary()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_internal(&self) -> bool {
|
||||||
|
self.0.is_internal()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stream_command(&self) -> &dyn WholeStreamCommand {
|
pub fn stream_command(&self) -> &dyn WholeStreamCommand {
|
||||||
&*self.0
|
&*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 {
|
pub fn whole_stream_command(command: impl WholeStreamCommand + 'static) -> Command {
|
||||||
Command(Arc::new(command))
|
Command(Arc::new(command))
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
use crate::command_registry::CommandRegistry;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use futures::future;
|
use futures::future;
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
@ -37,22 +37,11 @@ impl WholeStreamCommand for Compact {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![Example {
|
||||||
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'",
|
description: "Filter out all directory entries having no 'target'",
|
||||||
example: "ls -la | compact target",
|
example: "ls -la | compact target",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
}]
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,11 +84,12 @@ pub async fn compact(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Compact;
|
use super::Compact;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
|
use crate::command_registry::CommandRegistry;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
use nu_source::Tagged;
|
|
||||||
|
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct GetArgs {
|
pub struct GetArgs {
|
||||||
get: Tagged<String>,
|
path: ColumnPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@ -21,7 +20,7 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("config get").required(
|
Signature::build("config get").required(
|
||||||
"get",
|
"get",
|
||||||
SyntaxShape::Any,
|
SyntaxShape::ColumnPath,
|
||||||
"value to get from the config",
|
"value to get from the config",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -51,17 +50,14 @@ pub async fn get(
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let name_span = args.call_info.name_tag.clone();
|
let name_tag = args.call_info.name_tag.clone();
|
||||||
let (GetArgs { get }, _) = args.process(®istry).await?;
|
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
|
// NOTE: None because we are not loading a new config file, we just want to read from the
|
||||||
// existing config
|
// 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 = crate::commands::get::get_column_path(&path, &result)?;
|
||||||
let value = result
|
|
||||||
.get(&key)
|
|
||||||
.ok_or_else(|| ShellError::labeled_error("Missing key in config", "key", get.tag()))?;
|
|
||||||
|
|
||||||
Ok(match value {
|
Ok(match value {
|
||||||
Value {
|
Value {
|
||||||
@ -75,9 +71,6 @@ pub async fn get(
|
|||||||
|
|
||||||
futures::stream::iter(list).to_output_stream()
|
futures::stream::iter(list).to_output_stream()
|
||||||
}
|
}
|
||||||
x => {
|
x => OutputStream::one(ReturnSuccess::value(x)),
|
||||||
let x = x.clone();
|
|
||||||
OutputStream::one(ReturnSuccess::value(x))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
use crate::command_registry::CommandRegistry;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
use crate::command_registry::CommandRegistry;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
|
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
use crate::command_registry::CommandRegistry;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
|
use crate::command_registry::CommandRegistry;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
use nu_source::Tagged;
|
|
||||||
|
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct SetArgs {
|
pub struct SetArgs {
|
||||||
key: Tagged<String>,
|
path: ColumnPath,
|
||||||
value: Value,
|
value: Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("config set")
|
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")
|
.required("value", SyntaxShape::Any, "value to use")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,11 +37,28 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
description: "Set nonzero_exit_errors to true",
|
Example {
|
||||||
example: "config set nonzero_exit_errors $true",
|
description: "Set auto pivoting",
|
||||||
|
example: "config set pivot_mode always",
|
||||||
result: None,
|
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,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let name_span = args.call_info.name_tag.clone();
|
let name_tag = args.call_info.name_tag.clone();
|
||||||
let (SetArgs { key, value }, _) = args.process(®istry).await?;
|
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
|
// NOTE: None because we are not loading a new config file, we just want to read from the
|
||||||
// existing config
|
// 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(
|
Ok(OutputStream::one(ReturnSuccess::value(
|
||||||
UntaggedValue::Row(result.into()).into_value(&value.tag),
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
use crate::command_registry::CommandRegistry;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
@ -80,11 +80,12 @@ impl WholeStreamCommand for Count {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Count;
|
use super::Count;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Signature, SyntaxShape};
|
use nu_protocol::{Signature, SyntaxShape};
|
||||||
@ -66,11 +66,12 @@ impl WholeStreamCommand for Cpy {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Cpy;
|
use super::Cpy;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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 {
|
fn usage(&self) -> &str {
|
||||||
"Work with dates."
|
"Apply date function"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(
|
||||||
@ -36,11 +36,12 @@ impl WholeStreamCommand for Command {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Command;
|
use super::Command;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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))
|
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))
|
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))
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Debug;
|
use super::Debug;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
@ -83,11 +83,12 @@ async fn default(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Default;
|
use super::Default;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||||
|
|
||||||
pub struct What;
|
pub struct Describe;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct WhatArgs {}
|
pub struct DescribeArgs {}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl WholeStreamCommand for What {
|
impl WholeStreamCommand for Describe {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"describe"
|
"describe"
|
||||||
}
|
}
|
||||||
@ -28,11 +28,11 @@ impl WholeStreamCommand for What {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
what(args, registry).await
|
describe(args, registry).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn what(
|
pub async fn describe(
|
||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
_registry: &CommandRegistry,
|
_registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
@ -49,12 +49,13 @@ pub async fn what(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::What;
|
use super::Describe;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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 registry = registry.clone();
|
||||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
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 scope = raw_args.call_info.scope.clone();
|
||||||
let (
|
let (
|
||||||
DoArgs {
|
DoArgs {
|
||||||
@ -95,15 +95,7 @@ async fn do_(
|
|||||||
|
|
||||||
block.set_redirect(block_redirection);
|
block.set_redirect(block_redirection);
|
||||||
|
|
||||||
let result = run_block(
|
let result = run_block(&block, &mut context, input, scope).await;
|
||||||
&block,
|
|
||||||
&mut context,
|
|
||||||
input,
|
|
||||||
&scope.it,
|
|
||||||
&scope.vars,
|
|
||||||
&scope.env,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if ignore_errors {
|
if ignore_errors {
|
||||||
// To properly ignore errors we need to redirect stderr, consume it, and remove
|
// To properly ignore errors we need to redirect stderr, consume it, and remove
|
||||||
@ -125,11 +117,12 @@ async fn do_(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Do;
|
use super::Do;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||||
@ -85,11 +85,12 @@ async fn drop(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Drop;
|
use super::Drop;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Du;
|
use super::Du;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::classified::block::run_block;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use futures::stream::once;
|
use futures::stream::once;
|
||||||
@ -84,7 +84,7 @@ pub async fn process_row(
|
|||||||
block: Arc<Block>,
|
block: Arc<Block>,
|
||||||
scope: Arc<Scope>,
|
scope: Arc<Scope>,
|
||||||
head: Arc<Box<SpannedExpression>>,
|
head: Arc<Box<SpannedExpression>>,
|
||||||
mut context: Arc<Context>,
|
mut context: Arc<EvaluationContext>,
|
||||||
input: Value,
|
input: Value,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let input_clone = input.clone();
|
let input_clone = input.clone();
|
||||||
@ -97,9 +97,7 @@ pub async fn process_row(
|
|||||||
&block,
|
&block,
|
||||||
Arc::make_mut(&mut context),
|
Arc::make_mut(&mut context),
|
||||||
input_stream,
|
input_stream,
|
||||||
&input,
|
Scope::append_it(scope, input),
|
||||||
&scope.vars,
|
|
||||||
&scope.env,
|
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
.to_output_stream())
|
.to_output_stream())
|
||||||
@ -119,8 +117,8 @@ async fn each(
|
|||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let head = Arc::new(raw_args.call_info.args.head.clone());
|
let head = Arc::new(raw_args.call_info.args.head.clone());
|
||||||
let scope = Arc::new(raw_args.call_info.scope.clone());
|
let scope = raw_args.call_info.scope.clone();
|
||||||
let context = Arc::new(Context::from_raw(&raw_args, ®istry));
|
let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry));
|
||||||
let (each_args, input): (EachArgs, _) = raw_args.process(®istry).await?;
|
let (each_args, input): (EachArgs, _) = raw_args.process(®istry).await?;
|
||||||
let block = Arc::new(each_args.block);
|
let block = Arc::new(each_args.block);
|
||||||
|
|
||||||
@ -166,11 +164,12 @@ async fn each(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Each;
|
use super::Each;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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,
|
end: Primitive,
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
is_end_inclusive: bool,
|
is_end_inclusive: bool,
|
||||||
is_done: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RangeIterator {
|
impl RangeIterator {
|
||||||
pub fn new(range: Range, tag: Tag) -> 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 {
|
RangeIterator {
|
||||||
curr: range.from.0.item,
|
curr: start,
|
||||||
end: range.to.0.item,
|
end: range.to.0.item,
|
||||||
tag,
|
tag,
|
||||||
is_end_inclusive: matches!(range.to.1, RangeInclusion::Inclusive),
|
is_end_inclusive: matches!(range.to.1, RangeInclusion::Inclusive),
|
||||||
is_done: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,14 +102,40 @@ impl RangeIterator {
|
|||||||
impl Iterator for RangeIterator {
|
impl Iterator for RangeIterator {
|
||||||
type Item = Result<ReturnSuccess, ShellError>;
|
type Item = Result<ReturnSuccess, ShellError>;
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
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());
|
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,
|
Operator::Plus,
|
||||||
&UntaggedValue::Primitive(self.curr.clone()),
|
&UntaggedValue::Primitive(self.curr.clone()),
|
||||||
&UntaggedValue::int(1),
|
&UntaggedValue::int(1),
|
||||||
) {
|
);
|
||||||
|
|
||||||
|
self.curr = match next_value {
|
||||||
Ok(result) => match result {
|
Ok(result) => match result {
|
||||||
UntaggedValue::Primitive(p) => p,
|
UntaggedValue::Primitive(p) => p,
|
||||||
_ => {
|
_ => {
|
||||||
@ -123,11 +152,6 @@ impl Iterator for RangeIterator {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
Some(ReturnSuccess::value(output))
|
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 {
|
} else {
|
||||||
// TODO: add inclusive/exclusive ranges
|
// TODO: add inclusive/exclusive ranges
|
||||||
None
|
None
|
||||||
@ -138,11 +162,12 @@ impl Iterator for RangeIterator {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Echo;
|
use super::Echo;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::UnevaluatedCallInfo;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::hir::ExternalRedirection;
|
use nu_protocol::hir::ExternalRedirection;
|
||||||
@ -191,11 +191,12 @@ async fn enter(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Enter;
|
use super::Enter;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||||
@ -93,11 +93,12 @@ async fn every(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Every;
|
use super::Every;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::command::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{CommandAction, ReturnSuccess, Signature};
|
use nu_protocol::{CommandAction, ReturnSuccess, Signature};
|
||||||
@ -63,11 +63,12 @@ pub async fn exit(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Exit;
|
use super::Exit;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||||
@ -72,11 +72,12 @@ async fn first(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::First;
|
use super::First;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::evaluate::evaluate_baseline_expr;
|
use crate::evaluate::evaluate_baseline_expr;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue};
|
||||||
use nu_source::Tagged;
|
use nu_source::Tagged;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ async fn format_command(
|
|||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = Arc::new(registry.clone());
|
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 (FormatArgs { pattern }, input) = args.process(®istry).await?;
|
||||||
|
|
||||||
let format_pattern = format(&pattern);
|
let format_pattern = format(&pattern);
|
||||||
@ -83,9 +83,7 @@ async fn format_command(
|
|||||||
let result = evaluate_baseline_expr(
|
let result = evaluate_baseline_expr(
|
||||||
&full_column_path.0,
|
&full_column_path.0,
|
||||||
®istry,
|
®istry,
|
||||||
&value,
|
Scope::append_it(scope.clone(), value.clone()),
|
||||||
&scope.vars,
|
|
||||||
&scope.env,
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -153,11 +151,12 @@ fn format(input: &str) -> Vec<FormatCommand> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Format;
|
use super::Format;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(Format {})
|
Ok(test_examples(Format {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,11 +35,12 @@ impl WholeStreamCommand for From {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::From;
|
use super::From;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(From {})
|
Ok(test_examples(From {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,11 +109,12 @@ async fn from_csv(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::FromCSV;
|
use super::FromCSV;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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)
|
.delimiter(separator as u8)
|
||||||
.from_reader(s.as_bytes());
|
.from_reader(s.as_bytes());
|
||||||
let tag = tag.into();
|
let tag = tag.into();
|
||||||
|
let span = tag.span;
|
||||||
|
|
||||||
let headers = if headerless {
|
let headers = if headerless {
|
||||||
(1..=reader.headers()?.len())
|
(1..=reader.headers()?.len())
|
||||||
@ -30,7 +31,10 @@ fn from_delimited_string_to_value(
|
|||||||
if let Ok(i) = value.parse::<i64>() {
|
if let Ok(i) = value.parse::<i64>() {
|
||||||
tagged_row.insert_value(header, UntaggedValue::int(i).into_value(&tag))
|
tagged_row.insert_value(header, UntaggedValue::int(i).into_value(&tag))
|
||||||
} else if let Ok(f) = value.parse::<f64>() {
|
} 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 {
|
} else {
|
||||||
tagged_row.insert_value(header, UntaggedValue::string(value).into_value(&tag))
|
tagged_row.insert_value(header, UntaggedValue::string(value).into_value(&tag))
|
||||||
}
|
}
|
||||||
|
@ -130,11 +130,12 @@ async fn from_eml(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::FromEML;
|
use super::FromEML;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::FromIcs;
|
use super::FromIcs;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(FromIcs {})
|
Ok(test_examples(FromIcs {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,11 +95,12 @@ async fn from_ini(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::FromINI;
|
use super::FromINI;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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 {
|
fn convert_json_value_to_nu_value(v: &serde_hjson::Value, tag: impl Into<Tag>) -> Value {
|
||||||
let tag = tag.into();
|
let tag = tag.into();
|
||||||
|
let span = tag.span;
|
||||||
|
|
||||||
match v {
|
match v {
|
||||||
serde_hjson::Value::Null => UntaggedValue::Primitive(Primitive::Nothing).into_value(&tag),
|
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::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::U64(n) => UntaggedValue::int(*n).into_value(&tag),
|
||||||
serde_hjson::Value::I64(n) => UntaggedValue::int(*n).into_value(&tag),
|
serde_hjson::Value::I64(n) => UntaggedValue::int(*n).into_value(&tag),
|
||||||
serde_hjson::Value::String(s) => {
|
serde_hjson::Value::String(s) => {
|
||||||
@ -143,11 +144,12 @@ async fn from_json(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::FromJSON;
|
use super::FromJSON;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(FromJSON {})
|
Ok(test_examples(FromJSON {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ async fn from_ods(
|
|||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let tag = args.call_info.name_tag.clone();
|
let tag = args.call_info.name_tag.clone();
|
||||||
|
let span = tag.span;
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
|
|
||||||
let (
|
let (
|
||||||
@ -73,7 +74,7 @@ async fn from_ods(
|
|||||||
let value = match cell {
|
let value = match cell {
|
||||||
DataType::Empty => UntaggedValue::nothing(),
|
DataType::Empty => UntaggedValue::nothing(),
|
||||||
DataType::String(s) => UntaggedValue::string(s),
|
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::Int(i) => UntaggedValue::int(*i),
|
||||||
DataType::Bool(b) => UntaggedValue::boolean(*b),
|
DataType::Bool(b) => UntaggedValue::boolean(*b),
|
||||||
_ => UntaggedValue::nothing(),
|
_ => UntaggedValue::nothing(),
|
||||||
@ -101,11 +102,12 @@ async fn from_ods(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::FromODS;
|
use super::FromODS;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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))
|
.flat_map(|s| find_indices(*s))
|
||||||
.collect::<Vec<usize>>();
|
.collect::<Vec<usize>>();
|
||||||
|
|
||||||
indices.sort();
|
indices.sort_unstable();
|
||||||
indices.dedup();
|
indices.dedup();
|
||||||
|
|
||||||
let headers: Vec<(String, usize)> = indices
|
let headers: Vec<(String, usize)> = indices
|
||||||
@ -302,7 +302,9 @@ async fn from_ssv(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::ShellError;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn owned(x: &str, y: &str) -> (String, String) {
|
fn owned(x: &str, y: &str) -> (String, String) {
|
||||||
(String::from(x), String::from(y))
|
(String::from(x), String::from(y))
|
||||||
}
|
}
|
||||||
@ -504,10 +506,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use super::FromSSV;
|
use super::FromSSV;
|
||||||
use crate::examples::test as test_examples;
|
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 {
|
pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into<Tag>) -> Value {
|
||||||
let tag = tag.into();
|
let tag = tag.into();
|
||||||
|
let span = tag.span;
|
||||||
|
|
||||||
match v {
|
match v {
|
||||||
toml::Value::Boolean(b) => UntaggedValue::boolean(*b).into_value(tag),
|
toml::Value::Boolean(b) => UntaggedValue::boolean(*b).into_value(tag),
|
||||||
toml::Value::Integer(n) => UntaggedValue::int(*n).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) => {
|
toml::Value::String(s) => {
|
||||||
UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(tag)
|
UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(tag)
|
||||||
}
|
}
|
||||||
@ -100,11 +101,12 @@ pub async fn from_toml(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::FromTOML;
|
use super::FromTOML;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(FromTOML {})
|
Ok(test_examples(FromTOML {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,11 +52,12 @@ async fn from_tsv(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::FromTSV;
|
use super::FromTSV;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(FromTSV {})
|
Ok(test_examples(FromTSV {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,11 +64,12 @@ async fn from_url(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::FromURL;
|
use super::FromURL;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::FromVcf;
|
use super::FromVcf;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(FromVcf {})
|
Ok(test_examples(FromVcf {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ async fn from_xlsx(
|
|||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let tag = args.call_info.name_tag.clone();
|
let tag = args.call_info.name_tag.clone();
|
||||||
|
let span = tag.span;
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let (
|
let (
|
||||||
FromXLSXArgs {
|
FromXLSXArgs {
|
||||||
@ -73,7 +74,7 @@ async fn from_xlsx(
|
|||||||
let value = match cell {
|
let value = match cell {
|
||||||
DataType::Empty => UntaggedValue::nothing(),
|
DataType::Empty => UntaggedValue::nothing(),
|
||||||
DataType::String(s) => UntaggedValue::string(s),
|
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::Int(i) => UntaggedValue::int(*i),
|
||||||
DataType::Bool(b) => UntaggedValue::boolean(*b),
|
DataType::Bool(b) => UntaggedValue::boolean(*b),
|
||||||
_ => UntaggedValue::nothing(),
|
_ => UntaggedValue::nothing(),
|
||||||
@ -101,11 +102,12 @@ async fn from_xlsx(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::FromXLSX;
|
use super::FromXLSX;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(FromXLSX {})
|
Ok(test_examples(FromXLSX {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,6 +135,7 @@ async fn from_xml(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::ShellError;
|
||||||
use crate::commands::from_xml;
|
use crate::commands::from_xml;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use nu_protocol::{UntaggedValue, Value};
|
use nu_protocol::{UntaggedValue, Value};
|
||||||
@ -304,10 +305,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use super::FromXML;
|
use super::FromXML;
|
||||||
use crate::examples::test as test_examples;
|
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>,
|
tag: impl Into<Tag>,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
let tag = tag.into();
|
let tag = tag.into();
|
||||||
|
let span = tag.span;
|
||||||
|
|
||||||
let err_not_compatible_number = ShellError::labeled_error(
|
let err_not_compatible_number = ShellError::labeled_error(
|
||||||
"Expected a compatible number",
|
"Expected a compatible number",
|
||||||
@ -69,10 +70,11 @@ fn convert_yaml_value_to_nu_value(
|
|||||||
serde_yaml::Value::Number(n) if n.is_i64() => {
|
serde_yaml::Value::Number(n) if n.is_i64() => {
|
||||||
UntaggedValue::int(n.as_i64().ok_or_else(|| err_not_compatible_number)?).into_value(tag)
|
UntaggedValue::int(n.as_i64().ok_or_else(|| err_not_compatible_number)?).into_value(tag)
|
||||||
}
|
}
|
||||||
serde_yaml::Value::Number(n) if n.is_f64() => {
|
serde_yaml::Value::Number(n) if n.is_f64() => UntaggedValue::decimal_from_float(
|
||||||
UntaggedValue::decimal(n.as_f64().ok_or_else(|| err_not_compatible_number)?)
|
n.as_f64().ok_or_else(|| err_not_compatible_number)?,
|
||||||
.into_value(tag)
|
span,
|
||||||
}
|
)
|
||||||
|
.into_value(tag),
|
||||||
serde_yaml::Value::String(s) => UntaggedValue::string(s).into_value(tag),
|
serde_yaml::Value::String(s) => UntaggedValue::string(s).into_value(tag),
|
||||||
serde_yaml::Value::Sequence(a) => {
|
serde_yaml::Value::Sequence(a) => {
|
||||||
let result: Result<Vec<Value>, ShellError> = a
|
let result: Result<Vec<Value>, ShellError> = a
|
||||||
@ -171,15 +173,16 @@ async fn from_yaml(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::ShellError;
|
||||||
use super::*;
|
use super::*;
|
||||||
use nu_plugin::row;
|
use nu_plugin::row;
|
||||||
use nu_plugin::test_helpers::value::string;
|
use nu_plugin::test_helpers::value::string;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(FromYAML {})
|
Ok(test_examples(FromYAML {})?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -4,8 +4,8 @@ use indexmap::set::IndexSet;
|
|||||||
use log::trace;
|
use log::trace;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
did_you_mean, ColumnPath, PathMember, Primitive, ReturnSuccess, Signature, SyntaxShape,
|
did_you_mean, ColumnPath, Dictionary, PathMember, Primitive, ReturnSuccess, Signature,
|
||||||
UnspannedPathMember, UntaggedValue, Value,
|
SyntaxShape, UnspannedPathMember, UntaggedValue, Value,
|
||||||
};
|
};
|
||||||
use nu_source::HasFallibleSpan;
|
use nu_source::HasFallibleSpan;
|
||||||
use nu_value_ext::get_data_by_column_path;
|
use nu_value_ext::get_data_by_column_path;
|
||||||
@ -58,17 +58,95 @@ impl WholeStreamCommand for Get {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_column_path(path: &ColumnPath, obj: &Value) -> Result<Value, ShellError> {
|
pub async fn get(
|
||||||
let fields = path.clone();
|
args: CommandArgs,
|
||||||
|
registry: &CommandRegistry,
|
||||||
|
) -> Result<OutputStream, ShellError> {
|
||||||
|
let registry = registry.clone();
|
||||||
|
let (GetArgs { rest: column_paths }, mut input) = args.process(®istry).await?;
|
||||||
|
if column_paths.is_empty() {
|
||||||
|
let vec = input.drain_vec().await;
|
||||||
|
|
||||||
get_data_by_column_path(
|
let descs = nu_protocol::merge_descriptors(&vec);
|
||||||
obj,
|
|
||||||
path,
|
Ok(futures::stream::iter(descs.into_iter().map(ReturnSuccess::value)).to_output_stream())
|
||||||
Box::new(move |(obj_source, column_path_tried, error)| {
|
} else {
|
||||||
let path_members_span = fields.maybe_span().unwrap_or_else(Span::unknown);
|
trace!("get {:?}", column_paths);
|
||||||
|
let output_stream = input
|
||||||
|
.map(move |item| {
|
||||||
|
let output = column_paths
|
||||||
|
.iter()
|
||||||
|
.map(move |path| get_output(&item, path))
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
futures::stream::iter(output)
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.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 {
|
match &obj_source.value {
|
||||||
UntaggedValue::Table(rows) => match column_path_tried {
|
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 {
|
PathMember {
|
||||||
unspanned: UnspannedPathMember::String(column),
|
unspanned: UnspannedPathMember::String(column),
|
||||||
..
|
..
|
||||||
@ -77,8 +155,8 @@ pub fn get_column_path(path: &ColumnPath, obj: &Value) -> Result<Value, ShellErr
|
|||||||
|
|
||||||
let suggestions: IndexSet<_> = rows
|
let suggestions: IndexSet<_> = rows
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|r| did_you_mean(&r, &column_path_tried))
|
.filter_map(|r| did_you_mean(&r, column_path_tried.as_string()))
|
||||||
.map(|s| s[0].1.to_owned())
|
.map(|s| s[0].to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
let mut existing_columns: IndexSet<_> = IndexSet::default();
|
let mut existing_columns: IndexSet<_> = IndexSet::default();
|
||||||
let mut names: Vec<String> = vec![];
|
let mut names: Vec<String> = vec![];
|
||||||
@ -93,15 +171,15 @@ pub fn get_column_path(path: &ColumnPath, obj: &Value) -> Result<Value, ShellErr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if names.is_empty() {
|
if names.is_empty() {
|
||||||
return ShellError::labeled_error_with_secondary(
|
ShellError::labeled_error_with_secondary(
|
||||||
"Unknown column",
|
"Unknown column",
|
||||||
primary_label,
|
primary_label,
|
||||||
column_path_tried.span,
|
column_path_tried.span,
|
||||||
"Appears to contain rows. Try indexing instead.",
|
"Appears to contain rows. Try indexing instead.",
|
||||||
column_path_tried.span.since(path_members_span),
|
column_path_tried.span.since(path_members_span),
|
||||||
);
|
)
|
||||||
} else {
|
} else {
|
||||||
return ShellError::labeled_error_with_secondary(
|
ShellError::labeled_error_with_secondary(
|
||||||
"Unknown column",
|
"Unknown column",
|
||||||
primary_label,
|
primary_label,
|
||||||
column_path_tried.span,
|
column_path_tried.span,
|
||||||
@ -115,8 +193,8 @@ pub fn get_column_path(path: &ColumnPath, obj: &Value) -> Result<Value, ShellErr
|
|||||||
names.join(", ")
|
names.join(", ")
|
||||||
),
|
),
|
||||||
column_path_tried.span.since(path_members_span),
|
column_path_tried.span.since(path_members_span),
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
PathMember {
|
PathMember {
|
||||||
unspanned: UnspannedPathMember::Int(idx),
|
unspanned: UnspannedPathMember::Int(idx),
|
||||||
@ -130,41 +208,50 @@ pub fn get_column_path(path: &ColumnPath, obj: &Value) -> Result<Value, ShellErr
|
|||||||
format!("The table only has {} rows (0 to {})", total, total - 1)
|
format!("The table only has {} rows (0 to {})", total, total - 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
return ShellError::labeled_error_with_secondary(
|
ShellError::labeled_error_with_secondary(
|
||||||
"Row not found",
|
"Row not found",
|
||||||
format!("There isn't a row indexed at {}", idx),
|
format!("There isn't a row indexed at {}", idx),
|
||||||
column_path_tried.span,
|
column_path_tried.span,
|
||||||
secondary_label,
|
secondary_label,
|
||||||
column_path_tried.span.since(path_members_span),
|
column_path_tried.span.since(path_members_span),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
UntaggedValue::Row(columns) => match column_path_tried {
|
}
|
||||||
|
|
||||||
|
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 {
|
PathMember {
|
||||||
unspanned: UnspannedPathMember::String(column),
|
unspanned: UnspannedPathMember::String(column),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let primary_label = format!("There isn't a column named '{}'", &column);
|
let primary_label = format!("There isn't a column named '{}'", &column);
|
||||||
|
|
||||||
if let Some(suggestions) = did_you_mean(&obj_source, column_path_tried) {
|
if let Some(suggestions) = did_you_mean(&obj_source, column_path_tried.as_string()) {
|
||||||
return ShellError::labeled_error_with_secondary(
|
Some(ShellError::labeled_error_with_secondary(
|
||||||
"Unknown column",
|
"Unknown column",
|
||||||
primary_label,
|
primary_label,
|
||||||
column_path_tried.span,
|
column_path_tried.span,
|
||||||
format!(
|
format!(
|
||||||
"Perhaps you meant '{}'? Columns available: {}",
|
"Perhaps you meant '{}'? Columns available: {}",
|
||||||
suggestions[0].1,
|
suggestions[0],
|
||||||
&obj_source.data_descriptors().join(", ")
|
&obj_source.data_descriptors().join(", ")
|
||||||
),
|
),
|
||||||
column_path_tried.span.since(path_members_span),
|
column_path_tried.span.since(path_members_span),
|
||||||
);
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PathMember {
|
PathMember {
|
||||||
unspanned: UnspannedPathMember::Int(idx),
|
unspanned: UnspannedPathMember::Int(idx),
|
||||||
..
|
..
|
||||||
} => {
|
} => Some(ShellError::labeled_error_with_secondary(
|
||||||
return ShellError::labeled_error_with_secondary(
|
|
||||||
"No rows available",
|
"No rows available",
|
||||||
format!("A row at '{}' can't be indexed.", &idx),
|
format!("A row at '{}' can't be indexed.", &idx),
|
||||||
column_path_tried.span,
|
column_path_tried.span,
|
||||||
@ -173,91 +260,19 @@ pub fn get_column_path(path: &ColumnPath, obj: &Value) -> Result<Value, ShellErr
|
|||||||
columns.keys().join(", ")
|
columns.keys().join(", ")
|
||||||
),
|
),
|
||||||
column_path_tried.span.since(path_members_span),
|
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 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
|
|
||||||
.map(move |item| {
|
|
||||||
let member = vec![member.clone()];
|
|
||||||
|
|
||||||
let column_paths = vec![&member, &fields]
|
|
||||||
.into_iter()
|
|
||||||
.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(),
|
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
futures::stream::iter(output)
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
.to_output_stream())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Get;
|
use super::Get;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(Get {})
|
Ok(test_examples(Get {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::utils::suggestions::suggestions;
|
||||||
use indexmap::indexmap;
|
use indexmap::indexmap;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
use nu_source::Tagged;
|
use nu_source::Tagged;
|
||||||
use nu_value_ext::as_string;
|
use nu_value_ext::as_string;
|
||||||
|
|
||||||
pub struct GroupBy;
|
pub struct Command;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct GroupByArgs {
|
pub struct Arguments {
|
||||||
grouper: Option<Value>,
|
grouper: Option<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl WholeStreamCommand for GroupBy {
|
impl WholeStreamCommand for Command {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"group-by"
|
"group-by"
|
||||||
}
|
}
|
||||||
@ -28,7 +29,7 @@ impl WholeStreamCommand for GroupBy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"create a new table grouped."
|
"Create a new table grouped."
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(
|
||||||
@ -39,17 +40,45 @@ impl WholeStreamCommand for GroupBy {
|
|||||||
group_by(args, registry).await
|
group_by(args, registry).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
use nu_data::value::date_naive_from_str as date;
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "group items by column named \"type\"",
|
description: "group items by column named \"type\"",
|
||||||
example: r#"ls | group-by type"#,
|
example: r#"ls | group-by type"#,
|
||||||
result: None,
|
result: Some(vec![UntaggedValue::row(indexmap! {
|
||||||
},
|
"File".to_string() => UntaggedValue::Table(vec![
|
||||||
Example {
|
UntaggedValue::row(indexmap! {
|
||||||
description: "blocks can be used for generating a grouping key (same as above)",
|
"modified".to_string() => date("2019-07-23".tagged_unknown()).unwrap().into(),
|
||||||
example: r#"ls | group-by { get type }"#,
|
"name".to_string() => UntaggedValue::string("Andrés.txt").into(),
|
||||||
result: None,
|
"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 {
|
Example {
|
||||||
description: "you can also group by raw values by leaving out the argument",
|
description: "you can also group by raw values by leaving out the argument",
|
||||||
@ -74,10 +103,26 @@ impl WholeStreamCommand for GroupBy {
|
|||||||
.into()]),
|
.into()]),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "write pipelines for a more involved grouping key",
|
description:
|
||||||
example:
|
"use the block form to generate a grouping key when each row gets processed",
|
||||||
"echo [1 3 1 3 2 1 1] | group-by { echo `({{$it}} - 1) % 3` | calc | str from }",
|
example: "echo [1 3 1 3 2 1 1] | group-by { = ($it - 1) mod 3 }",
|
||||||
result: None,
|
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 name = args.call_info.name_tag.clone();
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let head = Arc::new(args.call_info.args.head.clone());
|
let head = Arc::new(args.call_info.args.head.clone());
|
||||||
let scope = Arc::new(args.call_info.scope.clone());
|
let scope = args.call_info.scope.clone();
|
||||||
let context = Arc::new(Context::from_raw(&args, ®istry));
|
let context = Arc::new(EvaluationContext::from_raw(&args, ®istry));
|
||||||
let (GroupByArgs { grouper }, input) = args.process(®istry).await?;
|
let (Arguments { grouper }, input) = args.process(®istry).await?;
|
||||||
|
|
||||||
let values: Vec<Value> = input.collect().await;
|
let values: Vec<Value> = input.collect().await;
|
||||||
let mut keys: Vec<Result<String, ShellError>> = vec![];
|
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 values = UntaggedValue::table(&values).into_value(&name);
|
||||||
|
|
||||||
let group_value = match group_strategy {
|
let group_value = match group_strategy {
|
||||||
@ -179,39 +232,14 @@ pub async fn group_by(
|
|||||||
None => as_string(row),
|
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?)))
|
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(
|
pub fn group(
|
||||||
column_name: &Option<Tagged<String>>,
|
column_name: &Option<Tagged<String>>,
|
||||||
values: &Value,
|
values: &Value,
|
||||||
@ -311,12 +339,4 @@ mod tests {
|
|||||||
|
|
||||||
Ok(())
|
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::commands::WholeStreamCommand;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::utils::suggestions::suggestions;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
use nu_source::Tagged;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::GroupByDate;
|
use super::GroupByDate;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
@ -116,11 +116,12 @@ pub async fn headers(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Headers;
|
use super::Headers;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
|
||||||
use nu_source::{SpannedItem, Tagged};
|
use nu_source::{SpannedItem, Tagged};
|
||||||
use nu_value_ext::get_data_by_key;
|
use nu_value_ext::ValueExt;
|
||||||
|
|
||||||
pub struct Help;
|
pub struct Help;
|
||||||
|
|
||||||
@ -63,54 +63,115 @@ async fn help(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
|||||||
let mut sorted_names = registry.names();
|
let mut sorted_names = registry.names();
|
||||||
sorted_names.sort();
|
sorted_names.sort();
|
||||||
|
|
||||||
Ok(
|
let (mut subcommand_names, command_names) = sorted_names
|
||||||
futures::stream::iter(sorted_names.into_iter().filter_map(move |cmd| {
|
.into_iter()
|
||||||
// If it's a subcommand, don't list it during the commands list
|
// Internal only commands shouldn't be displayed
|
||||||
if cmd.contains(' ') {
|
.filter(|cmd_name| {
|
||||||
return None;
|
registry
|
||||||
}
|
.get_command(&cmd_name)
|
||||||
let mut short_desc = TaggedDictBuilder::new(name.clone());
|
.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 document_tag = rest[0].tag.clone();
|
||||||
let value = command_dict(
|
let value = command_dict(
|
||||||
match registry.get_command(&cmd).ok_or_else(|| {
|
registry.get_command(&cmd_name).ok_or_else(|| {
|
||||||
ShellError::labeled_error(
|
ShellError::labeled_error(
|
||||||
format!("Could not load {}", cmd),
|
format!("Could not load {}", cmd_name),
|
||||||
"could not load command",
|
"could not load command",
|
||||||
document_tag,
|
document_tag,
|
||||||
)
|
)
|
||||||
}) {
|
})?,
|
||||||
Ok(ok) => ok,
|
name,
|
||||||
Err(err) => return Some(Err(err)),
|
|
||||||
},
|
|
||||||
name.clone(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
short_desc.insert_untagged("name", cmd);
|
dict.insert_untagged("name", cmd_name);
|
||||||
short_desc.insert_untagged(
|
dict.insert_untagged(
|
||||||
"description",
|
"description",
|
||||||
match match get_data_by_key(&value, "usage".spanned_unknown()).ok_or_else(
|
value
|
||||||
|| {
|
.get_data_by_key("usage".spanned_unknown())
|
||||||
|
.ok_or_else(|| {
|
||||||
ShellError::labeled_error(
|
ShellError::labeled_error(
|
||||||
"Expected a usage key",
|
"Expected a usage key",
|
||||||
"expected a 'usage' key",
|
"expected a 'usage' key",
|
||||||
&value.tag,
|
&value.tag,
|
||||||
)
|
)
|
||||||
},
|
})?
|
||||||
) {
|
.as_string()?,
|
||||||
Ok(ok) => ok,
|
|
||||||
Err(err) => return Some(Err(err)),
|
|
||||||
}
|
|
||||||
.as_string()
|
|
||||||
{
|
|
||||||
Ok(ok) => ok,
|
|
||||||
Err(err) => return Some(Err(err)),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Some(ReturnSuccess::value(short_desc.into_value()))
|
Ok(())
|
||||||
}))
|
}
|
||||||
.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" {
|
} else if rest[0].item == "generate_docs" {
|
||||||
Ok(OutputStream::one(ReturnSuccess::value(generate_docs(
|
Ok(OutputStream::one(ReturnSuccess::value(generate_docs(
|
||||||
®istry,
|
®istry,
|
||||||
@ -174,11 +235,12 @@ pub fn get_help(cmd: &dyn WholeStreamCommand, registry: &CommandRegistry) -> Str
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Help;
|
use super::Help;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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 {
|
nu_data::utils::Operation {
|
||||||
grouper: Some(Box::new(move |_, _| Ok(String::from("frequencies")))),
|
grouper: Some(Box::new(move |_, _| Ok(String::from("frequencies")))),
|
||||||
splitter: Some(splitter(column_grouper)),
|
splitter: Some(splitter(column_grouper)),
|
||||||
format: None,
|
format: &None,
|
||||||
eval: &evaluate_with,
|
eval: &evaluate_with,
|
||||||
|
reduction: &nu_data::utils::Reduction::Count,
|
||||||
},
|
},
|
||||||
&name,
|
&name,
|
||||||
)?;
|
)?;
|
||||||
@ -123,17 +124,33 @@ pub async fn histogram(
|
|||||||
|
|
||||||
Ok(futures::stream::iter(
|
Ok(futures::stream::iter(
|
||||||
results
|
results
|
||||||
.percentages
|
.data
|
||||||
.table_entries()
|
.table_entries()
|
||||||
.map(move |value| {
|
.cloned()
|
||||||
let values = value.table_entries().cloned().collect::<Vec<_>>();
|
|
||||||
let occurrences = values.len();
|
|
||||||
|
|
||||||
(occurrences, values[occurrences - 1].clone())
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into_iter()
|
.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 mut fact = TaggedDictBuilder::new(&name);
|
||||||
let column_value = labels
|
let column_value = labels
|
||||||
.get(idx)
|
.get(idx)
|
||||||
@ -147,19 +164,19 @@ pub async fn histogram(
|
|||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
fact.insert_value(&column.item, column_value);
|
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
|
// Some(2) < the number of digits
|
||||||
// true < group the 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()?
|
.as_string()?
|
||||||
);
|
);
|
||||||
fact.insert_untagged("percentage", UntaggedValue::string(percentage));
|
fact.insert_untagged("percentage", UntaggedValue::string(fmt_percentage));
|
||||||
|
|
||||||
let string = std::iter::repeat("*")
|
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)
|
ShellError::labeled_error("expected a number", "expected a number", &name)
|
||||||
})? as usize)
|
})? as usize)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
@ -178,11 +195,7 @@ fn evaluator(by: ColumnPath) -> Box<dyn Fn(usize, &Value) -> Result<Value, Shell
|
|||||||
Box::new(move |_: usize, value: &Value| {
|
Box::new(move |_: usize, value: &Value| {
|
||||||
let path = by.clone();
|
let path = by.clone();
|
||||||
|
|
||||||
let eval = nu_value_ext::get_data_by_column_path(
|
let eval = nu_value_ext::get_data_by_column_path(value, &path, move |_, _, error| error);
|
||||||
value,
|
|
||||||
&path,
|
|
||||||
Box::new(move |(_, _, error)| error),
|
|
||||||
);
|
|
||||||
|
|
||||||
match eval {
|
match eval {
|
||||||
Ok(with_value) => Ok(with_value),
|
Ok(with_value) => Ok(with_value),
|
||||||
@ -214,11 +227,12 @@ fn splitter(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Histogram;
|
use super::Histogram;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(Histogram {})
|
Ok(test_examples(Histogram {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_data::config::NuConfig;
|
use nu_data::config::{Conf, NuConfig};
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -9,9 +9,7 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
const DEFAULT_LOCATION: &str = "history.txt";
|
const DEFAULT_LOCATION: &str = "history.txt";
|
||||||
|
|
||||||
pub fn history_path(config: &NuConfig) -> PathBuf {
|
pub fn history_path(config: &dyn Conf) -> PathBuf {
|
||||||
let vars = config.vars.lock();
|
|
||||||
|
|
||||||
let default_path = nu_data::config::user_data()
|
let default_path = nu_data::config::user_data()
|
||||||
.map(|mut p| {
|
.map(|mut p| {
|
||||||
p.push(DEFAULT_LOCATION);
|
p.push(DEFAULT_LOCATION);
|
||||||
@ -19,7 +17,8 @@ pub fn history_path(config: &NuConfig) -> PathBuf {
|
|||||||
})
|
})
|
||||||
.unwrap_or_else(|_| PathBuf::from(DEFAULT_LOCATION));
|
.unwrap_or_else(|_| PathBuf::from(DEFAULT_LOCATION));
|
||||||
|
|
||||||
vars.get("history-path")
|
config
|
||||||
|
.var("history-path")
|
||||||
.map_or(default_path.clone(), |custom_path| {
|
.map_or(default_path.clone(), |custom_path| {
|
||||||
match custom_path.as_string() {
|
match custom_path.as_string() {
|
||||||
Ok(path) => PathBuf::from(path),
|
Ok(path) => PathBuf::from(path),
|
||||||
@ -54,7 +53,7 @@ impl WholeStreamCommand for History {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn history(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
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 tag = args.call_info.name_tag;
|
||||||
let path = history_path(&config);
|
let path = history_path(&config);
|
||||||
let file = File::open(path);
|
let file = File::open(path);
|
||||||
@ -80,11 +79,12 @@ fn history(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStrea
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::History;
|
use super::History;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::classified::block::run_block;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::evaluate::evaluate_baseline_expr;
|
use crate::evaluate::evaluate_baseline_expr;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
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;
|
pub struct If;
|
||||||
|
|
||||||
@ -72,9 +74,9 @@ async fn if_command(
|
|||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = Arc::new(registry.clone());
|
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 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 (
|
let (
|
||||||
IfArgs {
|
IfArgs {
|
||||||
@ -119,14 +121,12 @@ async fn if_command(
|
|||||||
let then_case = then_case.clone();
|
let then_case = then_case.clone();
|
||||||
let else_case = else_case.clone();
|
let else_case = else_case.clone();
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let scope = scope.clone();
|
let scope = Scope::append_it(scope.clone(), input);
|
||||||
let mut context = context.clone();
|
let mut context = context.clone();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
//FIXME: should we use the scope that's brought in as well?
|
//FIXME: should we use the scope that's brought in as well?
|
||||||
let condition =
|
let condition = evaluate_baseline_expr(&condition, &*registry, scope.clone()).await;
|
||||||
evaluate_baseline_expr(&condition, &*registry, &input, &scope.vars, &scope.env)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match condition {
|
match condition {
|
||||||
Ok(condition) => match condition.as_bool() {
|
Ok(condition) => match condition.as_bool() {
|
||||||
@ -136,9 +136,7 @@ async fn if_command(
|
|||||||
&then_case,
|
&then_case,
|
||||||
Arc::make_mut(&mut context),
|
Arc::make_mut(&mut context),
|
||||||
InputStream::empty(),
|
InputStream::empty(),
|
||||||
&input,
|
scope,
|
||||||
&scope.vars,
|
|
||||||
&scope.env,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@ -151,9 +149,7 @@ async fn if_command(
|
|||||||
&else_case,
|
&else_case,
|
||||||
Arc::make_mut(&mut context),
|
Arc::make_mut(&mut context),
|
||||||
InputStream::empty(),
|
InputStream::empty(),
|
||||||
&input,
|
scope,
|
||||||
&scope.vars,
|
|
||||||
&scope.env,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@ -178,11 +174,12 @@ async fn if_command(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::If;
|
use super::If;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::classified::block::run_block;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
@ -9,16 +9,18 @@ use nu_protocol::{
|
|||||||
use nu_value_ext::ValueExt;
|
use nu_value_ext::ValueExt;
|
||||||
|
|
||||||
use futures::stream::once;
|
use futures::stream::once;
|
||||||
pub struct Insert;
|
use indexmap::indexmap;
|
||||||
|
|
||||||
|
pub struct Command;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct InsertArgs {
|
pub struct Arguments {
|
||||||
column: ColumnPath,
|
column: ColumnPath,
|
||||||
value: Value,
|
value: Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl WholeStreamCommand for Insert {
|
impl WholeStreamCommand for Command {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"insert"
|
"insert"
|
||||||
}
|
}
|
||||||
@ -44,11 +46,33 @@ impl WholeStreamCommand for Insert {
|
|||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
insert(args, registry).await
|
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(
|
async fn process_row(
|
||||||
scope: Arc<Scope>,
|
scope: Arc<Scope>,
|
||||||
mut context: Arc<Context>,
|
mut context: Arc<EvaluationContext>,
|
||||||
input: Value,
|
input: Value,
|
||||||
mut value: Arc<Value>,
|
mut value: Arc<Value>,
|
||||||
field: Arc<ColumnPath>,
|
field: Arc<ColumnPath>,
|
||||||
@ -63,15 +87,9 @@ async fn process_row(
|
|||||||
let for_block = input.clone();
|
let for_block = input.clone();
|
||||||
let input_stream = once(async { Ok(for_block) }).to_input_stream();
|
let input_stream = once(async { Ok(for_block) }).to_input_stream();
|
||||||
|
|
||||||
let result = run_block(
|
let scope = Scope::append_it(scope, input.clone());
|
||||||
&block,
|
|
||||||
Arc::make_mut(&mut context),
|
let result = run_block(&block, Arc::make_mut(&mut context), input_stream, scope).await;
|
||||||
input_stream,
|
|
||||||
&input,
|
|
||||||
&scope.vars,
|
|
||||||
&scope.env,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(mut stream) => {
|
Ok(mut stream) => {
|
||||||
@ -85,13 +103,16 @@ async fn process_row(
|
|||||||
let result = if values.len() == 1 {
|
let result = if values.len() == 1 {
|
||||||
let value = values
|
let value = values
|
||||||
.get(0)
|
.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() {
|
} else if values.is_empty() {
|
||||||
UntaggedValue::nothing().into_untagged_value()
|
UntaggedValue::nothing().into_value(&input.tag)
|
||||||
} else {
|
} else {
|
||||||
UntaggedValue::table(&values).into_untagged_value()
|
UntaggedValue::table(&values).into_value(&input.tag)
|
||||||
};
|
};
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
@ -118,7 +139,11 @@ async fn process_row(
|
|||||||
Value {
|
Value {
|
||||||
value: UntaggedValue::Primitive(Primitive::Nothing),
|
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)),
|
Ok(v) => OutputStream::one(ReturnSuccess::value(v)),
|
||||||
Err(e) => OutputStream::one(Err(e)),
|
Err(e) => OutputStream::one(Err(e)),
|
||||||
},
|
},
|
||||||
@ -135,9 +160,9 @@ async fn insert(
|
|||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let scope = Arc::new(raw_args.call_info.scope.clone());
|
let scope = raw_args.call_info.scope.clone();
|
||||||
let context = Arc::new(Context::from_raw(&raw_args, ®istry));
|
let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry));
|
||||||
let (InsertArgs { column, value }, input) = raw_args.process(®istry).await?;
|
let (Arguments { column, value }, input) = raw_args.process(®istry).await?;
|
||||||
let value = Arc::new(value);
|
let value = Arc::new(value);
|
||||||
let column = Arc::new(column);
|
let column = Arc::new(column);
|
||||||
|
|
||||||
@ -158,15 +183,3 @@ async fn insert(
|
|||||||
.flatten()
|
.flatten()
|
||||||
.to_output_stream())
|
.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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::IntoInt;
|
use super::IntoInt;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||||
@ -74,11 +74,12 @@ async fn keep(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Command;
|
use super::Command;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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 crate::prelude::*;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use nu_errors::ShellError;
|
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;
|
pub struct SubCommand;
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = Arc::new(registry.clone());
|
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 call_info = args.evaluate_once(®istry).await?;
|
||||||
|
|
||||||
@ -85,19 +85,11 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
.take_while(move |item| {
|
.take_while(move |item| {
|
||||||
let condition = condition.clone();
|
let condition = condition.clone();
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let scope = scope.clone();
|
let scope = Scope::append_it(scope.clone(), item.clone());
|
||||||
let item = item.clone();
|
|
||||||
trace!("ITEM = {:?}", item);
|
trace!("ITEM = {:?}", item);
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let result = evaluate_baseline_expr(
|
let result = evaluate_baseline_expr(&*condition, ®istry, scope).await;
|
||||||
&*condition,
|
|
||||||
®istry,
|
|
||||||
&item,
|
|
||||||
&scope.vars,
|
|
||||||
&scope.env,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
trace!("RESULT = {:?}", result);
|
trace!("RESULT = {:?}", result);
|
||||||
|
|
||||||
!matches!(result, Ok(ref v) if v.is_true())
|
!matches!(result, Ok(ref v) if v.is_true())
|
||||||
@ -109,12 +101,13 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::ShellError;
|
||||||
use super::SubCommand;
|
use super::SubCommand;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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 crate::prelude::*;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use nu_errors::ShellError;
|
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;
|
pub struct SubCommand;
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let registry = Arc::new(registry.clone());
|
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 call_info = args.evaluate_once(®istry).await?;
|
||||||
|
|
||||||
let block = call_info.args.expect_nth(0)?.clone();
|
let block = call_info.args.expect_nth(0)?.clone();
|
||||||
@ -84,20 +84,11 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
.take_while(move |item| {
|
.take_while(move |item| {
|
||||||
let condition = condition.clone();
|
let condition = condition.clone();
|
||||||
let registry = registry.clone();
|
let registry = registry.clone();
|
||||||
let scope = scope.clone();
|
let scope = Scope::append_it(scope.clone(), item.clone());
|
||||||
let item = item.clone();
|
|
||||||
|
|
||||||
trace!("ITEM = {:?}", item);
|
trace!("ITEM = {:?}", item);
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let result = evaluate_baseline_expr(
|
let result = evaluate_baseline_expr(&*condition, ®istry, scope).await;
|
||||||
&*condition,
|
|
||||||
®istry,
|
|
||||||
&item,
|
|
||||||
&scope.vars,
|
|
||||||
&scope.env,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
trace!("RESULT = {:?}", result);
|
trace!("RESULT = {:?}", result);
|
||||||
|
|
||||||
matches!(result, Ok(ref v) if v.is_true())
|
matches!(result, Ok(ref v) if v.is_true())
|
||||||
@ -109,12 +100,13 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::ShellError;
|
||||||
use super::SubCommand;
|
use super::SubCommand;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Signature, SyntaxShape};
|
use nu_protocol::{Signature, SyntaxShape};
|
||||||
@ -121,11 +121,12 @@ async fn kill(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Kill;
|
use super::Kill;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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::commands::WholeStreamCommand;
|
||||||
use crate::context::CommandRegistry;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
@ -83,11 +83,12 @@ async fn last(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Last;
|
use super::Last;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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 mut leftover_string = leftover_string.lock();
|
||||||
|
|
||||||
let st = (&*leftover_string).clone() + &st;
|
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> = st.lines().map(|x| x.to_string()).collect();
|
let mut lines: Vec<String> = lo_lines.chain(st_lines).collect();
|
||||||
|
|
||||||
leftover_string.clear();
|
leftover_string.clear();
|
||||||
|
|
||||||
@ -125,11 +125,12 @@ async fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Lines;
|
use super::Lines;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(Lines {})
|
Ok(test_examples(Lines {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,11 +89,12 @@ impl WholeStreamCommand for Ls {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Ls;
|
use super::Ls;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(Ls {})
|
Ok(test_examples(Ls {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,11 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Get the average of a list of numbers",
|
description: "Get the average of a list of numbers",
|
||||||
example: "echo [-50 100.0 25] | math avg",
|
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 {
|
||||||
value: UntaggedValue::Primitive(other),
|
value: UntaggedValue::Primitive(other),
|
||||||
..
|
..
|
||||||
@ -162,12 +188,13 @@ pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::ShellError;
|
||||||
use super::SubCommand;
|
use super::SubCommand;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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,
|
sum::summation, utils::calculate, utils::MathFunction, variance::variance,
|
||||||
};
|
};
|
||||||
use nu_plugin::row;
|
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 nu_protocol::Value;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(Command {})
|
Ok(test_examples(Command {})?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -98,17 +98,17 @@ mod tests {
|
|||||||
},
|
},
|
||||||
TestCase {
|
TestCase {
|
||||||
description: "Mixed Values",
|
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_err: None,
|
||||||
expected_res: vec![
|
expected_res: vec![
|
||||||
Ok(decimal(21)),
|
Ok(decimal(21)),
|
||||||
Ok(int(10)),
|
Ok(int(10)),
|
||||||
Ok(decimal(26.5)),
|
Ok(decimal_from_float(26.5)),
|
||||||
Ok(decimal(26.5)),
|
Ok(decimal_from_float(26.5)),
|
||||||
Ok(table(&[decimal(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(BigDecimal::from_str("7.77817459305202276840928798315333943213319531457321440247173855894902863154158871367713143880202865").expect("Could not convert to decimal from string"))),
|
||||||
Ok(decimal(63)),
|
Ok(decimal(63)),
|
||||||
Ok(decimal(60.5)),
|
Ok(decimal_from_float(60.5)),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
TestCase {
|
TestCase {
|
||||||
@ -128,14 +128,14 @@ mod tests {
|
|||||||
},
|
},
|
||||||
TestCase {
|
TestCase {
|
||||||
description: "Mixed Negative Values",
|
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_err: None,
|
||||||
expected_res: vec![
|
expected_res: vec![
|
||||||
Ok(decimal(-5)),
|
Ok(decimal(-5)),
|
||||||
Ok(decimal(-13.5)),
|
Ok(decimal_from_float(-13.5)),
|
||||||
Ok(int(10)),
|
Ok(int(10)),
|
||||||
Ok(decimal(-11.5)),
|
Ok(decimal_from_float(-11.5)),
|
||||||
Ok(table(&[decimal(-13.5), decimal(-11.5), int(10)])),
|
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(BigDecimal::from_str("10.63798226482196513098036125801342585449179971588207816421068645273754903468375890632981926875247027").expect("Could not convert to decimal from string"))),
|
||||||
Ok(decimal(-15)),
|
Ok(decimal(-15)),
|
||||||
Ok(decimal(BigDecimal::from_str("113.1666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667").expect("Could not convert to decimal from string"))),
|
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_err: None,
|
||||||
expected_res: vec![
|
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(1), "col2".to_owned() => int(5)]),
|
||||||
Ok(row!["col1".to_owned() => int(4), "col2".to_owned() => int(8)]),
|
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![
|
Ok(row![
|
||||||
"col1".to_owned() => table(&[int(1), int(2), int(3), int(4)]),
|
"col1".to_owned() => table(&[int(1), int(2), int(3), int(4)]),
|
||||||
"col2".to_owned() => table(&[int(5), int(6), int(7), int(8)])
|
"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"))
|
"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() => 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
|
// TODO-Uncomment once Issue: https://github.com/nushell/nushell/issues/1883 is resolved
|
||||||
|
@ -41,7 +41,11 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Evalulate math in the pipeline",
|
description: "Evalulate math in the pipeline",
|
||||||
example: "echo '10 / 4' | math eval",
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::ShellError;
|
||||||
use super::SubCommand;
|
use super::SubCommand;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::ShellError;
|
||||||
use super::SubCommand;
|
use super::SubCommand;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
test_examples(SubCommand {})
|
Ok(test_examples(SubCommand {})?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,11 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Get the median of a list of numbers",
|
description: "Get the median of a list of numbers",
|
||||||
example: "echo [3 8 9 12 12 15] | math median",
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::ShellError;
|
||||||
use super::SubCommand;
|
use super::SubCommand;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::ShellError;
|
||||||
use super::SubCommand;
|
use super::SubCommand;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
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