mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 07:00:37 +02:00
Compare commits
78 Commits
Author | SHA1 | Date | |
---|---|---|---|
b2eecfb110 | |||
b0aa142542 | |||
247d8b00f8 | |||
0b520eeaf0 | |||
c3535b5c67 | |||
8b9a8daa1d | |||
c5ea4a31bd | |||
2275575575 | |||
c3a066eeb4 | |||
42eb658c37 | |||
a2e9bbd358 | |||
8951a01e58 | |||
f702aae72f | |||
f5e03aaf1c | |||
0f0847e45b | |||
ccd5d69fd1 | |||
55374ee54f | |||
f93ff9ec33 | |||
9a94b3c656 | |||
e04b89f747 | |||
180c1204f3 | |||
96e5fc05a3 | |||
c3efdf2689 | |||
27fdef5479 | |||
7ce8026916 | |||
8a9fc6a721 | |||
c06a692709 | |||
b37e420c7c | |||
22e70478a4 | |||
8ab2b92405 | |||
3201c90647 | |||
454f560eaa | |||
d2ac506de3 | |||
a9968046ed | |||
453087248a | |||
81ff598d6c | |||
d7d487de73 | |||
8d69c77989 | |||
0779a46179 | |||
ada92f41b4 | |||
ef3049f5a1 | |||
1dab82ffa1 | |||
e9e3fac59d | |||
7d403a6cc7 | |||
cf53264438 | |||
d834708be8 | |||
f8b4784891 | |||
789b28ac8a | |||
db8219e798 | |||
73d5310c9c | |||
8d197e1b2f | |||
c704157bc8 | |||
6abb9181d5 | |||
006171d669 | |||
8bd3cedce1 | |||
6f2ef05195 | |||
80025ea684 | |||
a62745eefb | |||
2ac501f88e | |||
df90d9e4b6 | |||
ad7a3fd908 | |||
ad8ab5b04d | |||
e7767ab7b3 | |||
846a779516 | |||
e7a4f31b38 | |||
10768b6ecf | |||
716c4def03 | |||
0e510ad42b | |||
3c222916c6 | |||
6887554888 | |||
d7bd77829f | |||
9e8434326d | |||
27bff35c79 | |||
e2fae63a42 | |||
701711eada | |||
9ec2aca86f | |||
818171cc2c | |||
b3c623396f |
@ -37,6 +37,8 @@ steps:
|
||||
fi
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain "stable"
|
||||
echo "Installing clippy"
|
||||
rustup component add clippy --toolchain stable-x86_64-apple-darwin
|
||||
export PATH=$HOME/.cargo/bin:$PATH
|
||||
fi
|
||||
rustup update
|
||||
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -11,3 +11,12 @@ debian/debhelper-build-stamp
|
||||
debian/files
|
||||
debian/nu.substvars
|
||||
debian/nu/
|
||||
|
||||
# macOS junk
|
||||
.DS_Store
|
||||
|
||||
# JetBrains' IDE items
|
||||
.idea/*
|
||||
|
||||
# VSCode's IDE items
|
||||
.vscode/*
|
||||
|
7
.gitpod.Dockerfile
vendored
7
.gitpod.Dockerfile
vendored
@ -6,4 +6,9 @@ RUN sudo apt-get update && \
|
||||
sudo apt-get install -y \
|
||||
libssl-dev \
|
||||
libxcb-composite0-dev \
|
||||
pkg-config
|
||||
pkg-config \
|
||||
libpython3.6 \
|
||||
rust-lldb \
|
||||
&& sudo rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV RUST_LLDB=/usr/bin/lldb-8
|
||||
|
22
.gitpod.yml
22
.gitpod.yml
@ -1,23 +1,19 @@
|
||||
image:
|
||||
file: .gitpod.Dockerfile
|
||||
tasks:
|
||||
- init: cargo install --path . --force --features=stable
|
||||
- name: Clippy
|
||||
init: cargo clippy --all --features=stable -- -D clippy::result_unwrap_used -D clippy::option_unwrap_used
|
||||
- name: Testing
|
||||
init: cargo test --all --features=stable,test-bins
|
||||
- name: Build
|
||||
init: cargo build --features=stable
|
||||
- name: Nu
|
||||
init: cargo install --path . --features=stable
|
||||
command: nu
|
||||
github:
|
||||
prebuilds:
|
||||
# enable for the master/default branch (defaults to true)
|
||||
master: true
|
||||
# enable for all branches in this repo (defaults to false)
|
||||
branches: true
|
||||
# enable for pull requests coming from this repo (defaults to true)
|
||||
pullRequests: true
|
||||
# enable for pull requests coming from forks (defaults to false)
|
||||
pullRequestsFromForks: true
|
||||
# add a "Review in Gitpod" button as a comment to pull requests (defaults to true)
|
||||
addComment: true
|
||||
# add a "Review in Gitpod" button to pull requests (defaults to false)
|
||||
addBadge: false
|
||||
# add a label once the prebuild is ready to pull requests (defaults to false)
|
||||
addLabel: prebuilt-in-gitpod
|
||||
vscode:
|
||||
extensions:
|
||||
@ -25,5 +21,5 @@ vscode:
|
||||
- Swellaby.vscode-rust-test-adapter@0.11.0:Xg+YeZZQiVpVUsIkH+uiiw==
|
||||
- serayuzgur.crates@0.4.7:HMkoguLcXp9M3ud7ac3eIw==
|
||||
- belfz.search-crates-io@1.2.1:kSLnyrOhXtYPjQpKnMr4eQ==
|
||||
- vadimcn.vscode-lldb@1.4.5:lwHCNwtm0kmOBXeQUIPGMQ==
|
||||
- bungcip.better-toml@0.3.2:3QfgGxxYtGHfJKQU7H0nEw==
|
||||
- webfreak.debug@0.24.0:1zVcRsAhewYEX3/A9xjMNw==
|
||||
|
14
.theia/launch.json
Normal file
14
.theia/launch.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "gdb",
|
||||
"request": "launch",
|
||||
"name": "Debug Rust Code",
|
||||
"preLaunchTask": "cargo",
|
||||
"target": "${workspaceFolder}/target/debug/nu",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"valuesFormatting": "parseText"
|
||||
}
|
||||
]
|
||||
}
|
12
.theia/tasks.json
Normal file
12
.theia/tasks.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"command": "cargo",
|
||||
"args": [
|
||||
"build"
|
||||
],
|
||||
"type": "process",
|
||||
"label": "cargo",
|
||||
}
|
||||
],
|
||||
}
|
@ -9,3 +9,20 @@ For speedy contributions open it in Gitpod, nu will be pre-installed with the la
|
||||
To get live support from the community see our [Discord](https://discordapp.com/invite/NtAbbGn), [Twitter](https://twitter.com/nu_shell) or file an issue or feature request here on [GitHub](https://github.com/nushell/nushell/issues/new/choose)!
|
||||
<!--WIP-->
|
||||
|
||||
# Developing
|
||||
## Set up
|
||||
This is no different than other Rust projects.
|
||||
|
||||
```shell
|
||||
git clone https://github.com/nushell/nushell
|
||||
cd nushell
|
||||
cargo build
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
Run tests with:
|
||||
|
||||
```shell
|
||||
cargo test --all --features=stable,test-bins
|
||||
```
|
||||
|
84
Cargo.lock
generated
84
Cargo.lock
generated
@ -809,6 +809,15 @@ version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
|
||||
[[package]]
|
||||
name = "eml-parser"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9e30d14e24cd200f2351837a02feacf8f043410f2a56441868c93ef33f90239"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
@ -2060,7 +2069,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"crossterm",
|
||||
@ -2084,6 +2093,7 @@ dependencies = [
|
||||
"nu_plugin_match",
|
||||
"nu_plugin_post",
|
||||
"nu_plugin_ps",
|
||||
"nu_plugin_start",
|
||||
"nu_plugin_str",
|
||||
"nu_plugin_sys",
|
||||
"nu_plugin_textview",
|
||||
@ -2098,7 +2108,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-build"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0",
|
||||
"serde 1.0.106",
|
||||
@ -2108,7 +2118,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-cli"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"app_dirs",
|
||||
@ -2128,6 +2138,7 @@ dependencies = [
|
||||
"derive-new",
|
||||
"dirs 2.0.2",
|
||||
"dunce",
|
||||
"eml-parser",
|
||||
"filesize",
|
||||
"futures 0.3.4",
|
||||
"futures-util",
|
||||
@ -2165,6 +2176,7 @@ dependencies = [
|
||||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
"rand",
|
||||
"rayon",
|
||||
"regex",
|
||||
"roxmltree",
|
||||
"rusqlite",
|
||||
@ -2189,16 +2201,18 @@ dependencies = [
|
||||
"umask",
|
||||
"unicode-xid",
|
||||
"users",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-errors"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"bigdecimal",
|
||||
"derive-new",
|
||||
"getset",
|
||||
"glob",
|
||||
"language-reporting",
|
||||
"nu-build",
|
||||
"nu-source",
|
||||
@ -2212,7 +2226,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-parser"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"derive-new",
|
||||
@ -2231,7 +2245,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-plugin"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"nu-build",
|
||||
@ -2246,7 +2260,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-protocol"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"bigdecimal",
|
||||
@ -2274,7 +2288,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-source"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"derive-new",
|
||||
"getset",
|
||||
@ -2287,7 +2301,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-test-support"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"app_dirs",
|
||||
"dunce",
|
||||
@ -2303,7 +2317,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-value-ext"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itertools 0.9.0",
|
||||
@ -2317,7 +2331,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_average"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"nu-build",
|
||||
"nu-errors",
|
||||
@ -2328,7 +2342,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_binaryview"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"crossterm",
|
||||
@ -2345,7 +2359,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_fetch"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"futures 0.3.4",
|
||||
"nu-build",
|
||||
@ -2359,7 +2373,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_inc"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"nu-build",
|
||||
"nu-errors",
|
||||
@ -2372,7 +2386,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_match"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"futures 0.3.4",
|
||||
"nu-build",
|
||||
@ -2385,7 +2399,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_post"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"base64 0.12.0",
|
||||
"futures 0.3.4",
|
||||
@ -2402,7 +2416,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_ps"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"futures 0.3.4",
|
||||
"futures-timer 3.0.2",
|
||||
@ -2414,9 +2428,22 @@ dependencies = [
|
||||
"nu-source",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_start"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nu-build",
|
||||
"nu-errors",
|
||||
"nu-plugin",
|
||||
"nu-protocol",
|
||||
"nu-source",
|
||||
"open",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_str"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"nu-build",
|
||||
@ -2431,7 +2458,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_sys"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"battery",
|
||||
"futures 0.3.4",
|
||||
@ -2446,7 +2473,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_textview"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"crossterm",
|
||||
@ -2461,7 +2488,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_tree"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"derive-new",
|
||||
"nu-build",
|
||||
@ -3175,9 +3202,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustyline"
|
||||
version = "6.1.1"
|
||||
version = "6.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61a7384a84da856bd163ef2c22982c816b0bcedaf17978ec13d8e0e277ddc332"
|
||||
checksum = "1cd20b28d972040c627e209eb29f19c24a71a19d661cc5a220089176e20ee202"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dirs 2.0.2",
|
||||
@ -3185,6 +3212,7 @@ dependencies = [
|
||||
"log",
|
||||
"memchr",
|
||||
"nix 0.17.0",
|
||||
"scopeguard",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"utf8parse 0.2.0",
|
||||
@ -4073,6 +4101,16 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
|
||||
dependencies = [
|
||||
"failure",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.4.0"
|
||||
|
51
Cargo.toml
51
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nu"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
authors = ["The Nu Project Contributors"]
|
||||
description = "A new type of shell"
|
||||
license = "MIT"
|
||||
@ -18,24 +18,25 @@ members = ["crates/*/"]
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nu-cli = { version = "0.13.0", path = "./crates/nu-cli" }
|
||||
nu-source = { version = "0.13.0", path = "./crates/nu-source" }
|
||||
nu-plugin = { version = "0.13.0", path = "./crates/nu-plugin" }
|
||||
nu-protocol = { version = "0.13.0", path = "./crates/nu-protocol" }
|
||||
nu-errors = { version = "0.13.0", path = "./crates/nu-errors" }
|
||||
nu-parser = { version = "0.13.0", path = "./crates/nu-parser" }
|
||||
nu-value-ext = { version = "0.13.0", path = "./crates/nu-value-ext" }
|
||||
nu_plugin_average = { version = "0.13.0", path = "./crates/nu_plugin_average", optional=true }
|
||||
nu_plugin_binaryview = { version = "0.13.0", path = "./crates/nu_plugin_binaryview", optional=true }
|
||||
nu_plugin_fetch = { version = "0.13.0", path = "./crates/nu_plugin_fetch", optional=true }
|
||||
nu_plugin_inc = { version = "0.13.0", path = "./crates/nu_plugin_inc", optional=true }
|
||||
nu_plugin_match = { version = "0.13.0", path = "./crates/nu_plugin_match", optional=true }
|
||||
nu_plugin_post = { version = "0.13.0", path = "./crates/nu_plugin_post", optional=true }
|
||||
nu_plugin_ps = { version = "0.13.0", path = "./crates/nu_plugin_ps", optional=true }
|
||||
nu_plugin_str = { version = "0.13.0", path = "./crates/nu_plugin_str", optional=true }
|
||||
nu_plugin_sys = { version = "0.13.0", path = "./crates/nu_plugin_sys", optional=true }
|
||||
nu_plugin_textview = { version = "0.13.0", path = "./crates/nu_plugin_textview", optional=true }
|
||||
nu_plugin_tree = { version = "0.13.0", path = "./crates/nu_plugin_tree", optional=true }
|
||||
nu-cli = { version = "0.14.0", path = "./crates/nu-cli" }
|
||||
nu-source = { version = "0.14.0", path = "./crates/nu-source" }
|
||||
nu-plugin = { version = "0.14.0", path = "./crates/nu-plugin" }
|
||||
nu-protocol = { version = "0.14.0", path = "./crates/nu-protocol" }
|
||||
nu-errors = { version = "0.14.0", path = "./crates/nu-errors" }
|
||||
nu-parser = { version = "0.14.0", path = "./crates/nu-parser" }
|
||||
nu-value-ext = { version = "0.14.0", path = "./crates/nu-value-ext" }
|
||||
nu_plugin_average = { version = "0.14.0", path = "./crates/nu_plugin_average", optional=true }
|
||||
nu_plugin_binaryview = { version = "0.14.0", path = "./crates/nu_plugin_binaryview", optional=true }
|
||||
nu_plugin_fetch = { version = "0.14.0", path = "./crates/nu_plugin_fetch", optional=true }
|
||||
nu_plugin_inc = { version = "0.14.0", path = "./crates/nu_plugin_inc", optional=true }
|
||||
nu_plugin_match = { version = "0.14.0", path = "./crates/nu_plugin_match", optional=true }
|
||||
nu_plugin_post = { version = "0.14.0", path = "./crates/nu_plugin_post", optional=true }
|
||||
nu_plugin_ps = { version = "0.14.0", path = "./crates/nu_plugin_ps", optional=true }
|
||||
nu_plugin_start = { version = "0.1.0", path = "./crates/nu_plugin_start", optional=true }
|
||||
nu_plugin_str = { version = "0.14.0", path = "./crates/nu_plugin_str", optional=true }
|
||||
nu_plugin_sys = { version = "0.14.0", path = "./crates/nu_plugin_sys", optional=true }
|
||||
nu_plugin_textview = { version = "0.14.0", path = "./crates/nu_plugin_textview", optional=true }
|
||||
nu_plugin_tree = { version = "0.14.0", path = "./crates/nu_plugin_tree", optional=true }
|
||||
|
||||
crossterm = { version = "0.17.2", optional = true }
|
||||
semver = { version = "0.9.0", optional = true }
|
||||
@ -50,19 +51,19 @@ log = "0.4.8"
|
||||
pretty_env_logger = "0.4.0"
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { version = "0.13.0", path = "./crates/nu-test-support" }
|
||||
nu-test-support = { version = "0.14.0", path = "./crates/nu-test-support" }
|
||||
|
||||
[build-dependencies]
|
||||
toml = "0.5.6"
|
||||
serde = { version = "1.0.106", features = ["derive"] }
|
||||
nu-build = { version = "0.13.0", path = "./crates/nu-build" }
|
||||
nu-build = { version = "0.14.0", path = "./crates/nu-build" }
|
||||
|
||||
[features]
|
||||
# Test executables
|
||||
test-bins = []
|
||||
|
||||
default = ["sys", "ps", "textview", "inc", "str"]
|
||||
stable = ["default", "starship-prompt", "binaryview", "match", "tree", "average", "post", "fetch", "clipboard-cli", "trash-support"]
|
||||
stable = ["default", "starship-prompt", "binaryview", "match", "tree", "average", "post", "fetch", "clipboard-cli", "trash-support", "start"]
|
||||
|
||||
# Default
|
||||
textview = ["crossterm", "syntect", "url", "nu_plugin_textview"]
|
||||
@ -79,6 +80,7 @@ match = ["nu_plugin_match"]
|
||||
post = ["nu_plugin_post"]
|
||||
trace = ["nu-parser/trace"]
|
||||
tree = ["nu_plugin_tree"]
|
||||
start = ["nu_plugin_start"]
|
||||
|
||||
clipboard-cli = ["nu-cli/clipboard-cli"]
|
||||
starship-prompt = ["nu-cli/starship-prompt"]
|
||||
@ -168,6 +170,11 @@ name = "nu_plugin_stable_tree"
|
||||
path = "src/plugins/nu_plugin_stable_tree.rs"
|
||||
required-features = ["tree"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_stable_start"
|
||||
path = "src/plugins/nu_plugin_stable_start.rs"
|
||||
required-features = ["start"]
|
||||
|
||||
# Main nu binary
|
||||
[[bin]]
|
||||
name = "nu"
|
||||
|
@ -276,6 +276,10 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat
|
||||
|
||||
You can find a list of Nu commands, complete with documentation, in [quick command references](https://www.nushell.sh/documentation.html#quick-command-references).
|
||||
|
||||
# Contributing
|
||||
|
||||
See [Contributing](CONTRIBUTING.md) for details.
|
||||
|
||||
# License
|
||||
|
||||
The project is made available under the MIT license. See the `LICENSE` file for more information.
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nu-build"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
authors = ["The Nu Project Contributors"]
|
||||
edition = "2018"
|
||||
description = "Core build system for nushell"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nu-cli"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
authors = ["The Nu Project Contributors"]
|
||||
description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
@ -10,13 +10,14 @@ license = "MIT"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-source = { version = "0.13.0", path = "../nu-source" }
|
||||
nu-plugin = { version = "0.13.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.13.0", path = "../nu-protocol" }
|
||||
nu-errors = { version = "0.13.0", path = "../nu-errors" }
|
||||
nu-parser = { version = "0.13.0", path = "../nu-parser" }
|
||||
nu-value-ext = { version = "0.13.0", path = "../nu-value-ext" }
|
||||
nu-test-support = { version = "0.13.0", path = "../nu-test-support" }
|
||||
nu-source = { version = "0.14.0", path = "../nu-source" }
|
||||
nu-plugin = { version = "0.14.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.14.0", path = "../nu-protocol" }
|
||||
nu-errors = { version = "0.14.0", path = "../nu-errors" }
|
||||
nu-parser = { version = "0.14.0", path = "../nu-parser" }
|
||||
nu-value-ext = { version = "0.14.0", path = "../nu-value-ext" }
|
||||
nu-test-support = { version = "0.14.0", path = "../nu-test-support" }
|
||||
|
||||
|
||||
ansi_term = "0.12.1"
|
||||
app_dirs = "1.2.1"
|
||||
@ -35,6 +36,7 @@ ctrlc = "3.1.4"
|
||||
derive-new = "0.5.8"
|
||||
dirs = "2.0.2"
|
||||
dunce = "1.0.0"
|
||||
eml-parser = "0.1.0"
|
||||
filesize = "0.2.0"
|
||||
futures = { version = "0.3", features = ["compat", "io-compat"] }
|
||||
futures-util = "0.3.4"
|
||||
@ -64,7 +66,7 @@ query_interface = "0.3.5"
|
||||
rand = "0.7"
|
||||
regex = "1"
|
||||
roxmltree = "0.10.1"
|
||||
rustyline = "6.1.1"
|
||||
rustyline = "6.1.2"
|
||||
serde = { version = "1.0.106", features = ["derive"] }
|
||||
serde-hjson = "0.9.1"
|
||||
serde_bytes = "0.11.3"
|
||||
@ -82,10 +84,12 @@ toml = "0.5.6"
|
||||
typetag = "0.1.4"
|
||||
umask = "0.1"
|
||||
unicode-xid = "0.2.0"
|
||||
which = "3"
|
||||
|
||||
trash = { version = "1.0.0", optional = true }
|
||||
clipboard = { version = "0.5", optional = true }
|
||||
starship = { version = "0.39.0", optional = true }
|
||||
rayon = "1.3.0"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
users = "0.10.0"
|
||||
@ -95,7 +99,7 @@ version = "0.22.0"
|
||||
features = ["bundled", "blob"]
|
||||
|
||||
[build-dependencies]
|
||||
nu-build = { version = "0.13.0", path = "../nu-build" }
|
||||
nu-build = { version = "0.14.0", path = "../nu-build" }
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = "0.9"
|
||||
|
@ -6,6 +6,7 @@ use crate::commands::whole_stream_command;
|
||||
use crate::context::Context;
|
||||
#[cfg(not(feature = "starship-prompt"))]
|
||||
use crate::git::current_branch;
|
||||
use crate::path::canonicalize;
|
||||
use crate::prelude::*;
|
||||
use futures_codec::FramedRead;
|
||||
|
||||
@ -22,9 +23,11 @@ use rustyline::{
|
||||
use std::error::Error;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::iter::Iterator;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use rayon::prelude::*;
|
||||
|
||||
fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), ShellError> {
|
||||
let mut child = std::process::Command::new(path)
|
||||
.stdin(std::process::Stdio::piped())
|
||||
@ -131,62 +134,60 @@ pub fn load_plugins(context: &mut Context) -> Result<(), ShellError> {
|
||||
|
||||
pattern.push(std::path::Path::new("nu_plugin_[a-z0-9][a-z0-9]*"));
|
||||
|
||||
match glob::glob_with(&pattern.to_string_lossy(), opts) {
|
||||
Err(_) => {}
|
||||
Ok(binaries) => {
|
||||
for bin in binaries.filter_map(Result::ok) {
|
||||
if !bin.is_file() {
|
||||
continue;
|
||||
let plugs: Vec<_> = glob::glob_with(&pattern.to_string_lossy(), opts)?
|
||||
.filter_map(|x| x.ok())
|
||||
.collect();
|
||||
|
||||
let _failures: Vec<_> = plugs
|
||||
.par_iter()
|
||||
.map(|path| {
|
||||
let bin_name = {
|
||||
if let Some(name) = path.file_name() {
|
||||
match name.to_str() {
|
||||
Some(raw) => raw,
|
||||
None => "",
|
||||
}
|
||||
} else {
|
||||
""
|
||||
}
|
||||
};
|
||||
|
||||
let is_valid_name = {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
bin_name
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.')
|
||||
}
|
||||
|
||||
let bin_name = {
|
||||
if let Some(name) = bin.file_name() {
|
||||
match name.to_str() {
|
||||
Some(raw) => raw,
|
||||
None => continue,
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let is_valid_name = {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
bin_name
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.')
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
bin_name
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || c == '_')
|
||||
}
|
||||
};
|
||||
|
||||
let is_executable = {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
bin_name.ends_with(".exe") || bin_name.ends_with(".bat")
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
if is_valid_name && is_executable {
|
||||
trace!("Trying {:?}", bin.display());
|
||||
|
||||
// we are ok if this plugin load fails
|
||||
let _ = load_plugin(&bin, context);
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
bin_name
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || c == '_')
|
||||
}
|
||||
};
|
||||
|
||||
let is_executable = {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
bin_name.ends_with(".exe") || bin_name.ends_with(".bat")
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
if is_valid_name && is_executable {
|
||||
trace!("Trying {:?}", path.display());
|
||||
|
||||
// we are ok if this plugin load fails
|
||||
let _ = load_plugin(&path, &mut context.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -223,7 +224,8 @@ fn create_default_starship_config() -> Option<toml::Value> {
|
||||
}
|
||||
|
||||
pub fn create_default_context(
|
||||
syncer: &mut crate::env::environment_syncer::EnvironmentSyncer,
|
||||
syncer: &mut crate::EnvironmentSyncer,
|
||||
interactive: bool,
|
||||
) -> Result<Context, Box<dyn Error>> {
|
||||
syncer.load_environment();
|
||||
|
||||
@ -237,28 +239,30 @@ pub fn create_default_context(
|
||||
context.add_commands(vec![
|
||||
// System/file operations
|
||||
whole_stream_command(Pwd),
|
||||
per_item_command(Ls),
|
||||
per_item_command(Du),
|
||||
whole_stream_command(Ls),
|
||||
whole_stream_command(Du),
|
||||
whole_stream_command(Cd),
|
||||
per_item_command(Remove),
|
||||
per_item_command(Open),
|
||||
whole_stream_command(Remove),
|
||||
whole_stream_command(Open),
|
||||
whole_stream_command(Config),
|
||||
per_item_command(Help),
|
||||
per_item_command(History),
|
||||
whole_stream_command(Help),
|
||||
whole_stream_command(History),
|
||||
whole_stream_command(Save),
|
||||
per_item_command(Touch),
|
||||
per_item_command(Cpy),
|
||||
whole_stream_command(Touch),
|
||||
whole_stream_command(Cpy),
|
||||
whole_stream_command(Date),
|
||||
per_item_command(Calc),
|
||||
per_item_command(Mkdir),
|
||||
per_item_command(Move),
|
||||
per_item_command(Kill),
|
||||
whole_stream_command(Cal),
|
||||
whole_stream_command(Calc),
|
||||
whole_stream_command(Mkdir),
|
||||
whole_stream_command(Move),
|
||||
whole_stream_command(Kill),
|
||||
whole_stream_command(Version),
|
||||
whole_stream_command(Clear),
|
||||
whole_stream_command(What),
|
||||
whole_stream_command(Which),
|
||||
whole_stream_command(Debug),
|
||||
per_item_command(Alias),
|
||||
whole_stream_command(Alias),
|
||||
whole_stream_command(WithEnv),
|
||||
// Statistics
|
||||
whole_stream_command(Size),
|
||||
whole_stream_command(Count),
|
||||
@ -268,7 +272,7 @@ pub fn create_default_context(
|
||||
whole_stream_command(Next),
|
||||
whole_stream_command(Previous),
|
||||
whole_stream_command(Shells),
|
||||
per_item_command(Enter),
|
||||
whole_stream_command(Enter),
|
||||
whole_stream_command(Exit),
|
||||
// Viewers
|
||||
whole_stream_command(Autoview),
|
||||
@ -278,14 +282,14 @@ pub fn create_default_context(
|
||||
whole_stream_command(SplitRow),
|
||||
whole_stream_command(Lines),
|
||||
whole_stream_command(Trim),
|
||||
per_item_command(Echo),
|
||||
per_item_command(Parse),
|
||||
whole_stream_command(Echo),
|
||||
whole_stream_command(Parse),
|
||||
// Column manipulation
|
||||
whole_stream_command(Reject),
|
||||
whole_stream_command(Pick),
|
||||
whole_stream_command(Select),
|
||||
whole_stream_command(Get),
|
||||
per_item_command(Edit),
|
||||
per_item_command(Insert),
|
||||
whole_stream_command(Update),
|
||||
whole_stream_command(Insert),
|
||||
whole_stream_command(SplitBy),
|
||||
// Row manipulation
|
||||
whole_stream_command(Reverse),
|
||||
@ -295,18 +299,25 @@ pub fn create_default_context(
|
||||
whole_stream_command(GroupBy),
|
||||
whole_stream_command(First),
|
||||
whole_stream_command(Last),
|
||||
whole_stream_command(Skip),
|
||||
whole_stream_command(Nth),
|
||||
per_item_command(Format),
|
||||
per_item_command(Where),
|
||||
whole_stream_command(Drop),
|
||||
whole_stream_command(Format),
|
||||
whole_stream_command(Where),
|
||||
whole_stream_command(Compact),
|
||||
whole_stream_command(Default),
|
||||
whole_stream_command(Skip),
|
||||
whole_stream_command(SkipUntil),
|
||||
whole_stream_command(SkipWhile),
|
||||
whole_stream_command(Keep),
|
||||
whole_stream_command(KeepUntil),
|
||||
whole_stream_command(KeepWhile),
|
||||
whole_stream_command(Range),
|
||||
whole_stream_command(Rename),
|
||||
whole_stream_command(Uniq),
|
||||
per_item_command(Each),
|
||||
whole_stream_command(Each),
|
||||
whole_stream_command(IsEmpty),
|
||||
// Table manipulation
|
||||
whole_stream_command(Merge),
|
||||
whole_stream_command(Shuffle),
|
||||
whole_stream_command(Wrap),
|
||||
whole_stream_command(Pivot),
|
||||
@ -315,6 +326,7 @@ pub fn create_default_context(
|
||||
whole_stream_command(Histogram),
|
||||
whole_stream_command(Sum),
|
||||
// File format output
|
||||
whole_stream_command(To),
|
||||
whole_stream_command(ToBSON),
|
||||
whole_stream_command(ToCSV),
|
||||
whole_stream_command(ToHTML),
|
||||
@ -327,7 +339,9 @@ pub fn create_default_context(
|
||||
whole_stream_command(ToURL),
|
||||
whole_stream_command(ToYAML),
|
||||
// File format input
|
||||
whole_stream_command(From),
|
||||
whole_stream_command(FromCSV),
|
||||
whole_stream_command(FromEML),
|
||||
whole_stream_command(FromTSV),
|
||||
whole_stream_command(FromSSV),
|
||||
whole_stream_command(FromINI),
|
||||
@ -345,7 +359,7 @@ pub fn create_default_context(
|
||||
whole_stream_command(FromIcs),
|
||||
whole_stream_command(FromVcf),
|
||||
// "Private" commands (not intended to be accessed directly)
|
||||
whole_stream_command(RunExternalCommand),
|
||||
whole_stream_command(RunExternalCommand { interactive }),
|
||||
]);
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
@ -375,7 +389,7 @@ pub async fn run_vec_of_pipelines(
|
||||
redirect_stdin: bool,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut syncer = crate::EnvironmentSyncer::new();
|
||||
let mut context = crate::create_default_context(&mut syncer)?;
|
||||
let mut context = create_default_context(&mut syncer, false)?;
|
||||
|
||||
let _ = crate::load_plugins(&mut context);
|
||||
|
||||
@ -445,7 +459,7 @@ pub async fn run_pipeline_standalone(
|
||||
};
|
||||
|
||||
context.maybe_print_errors(Text::from(line));
|
||||
if error_code != 0 {
|
||||
if error_code != 0 && exit_on_error {
|
||||
std::process::exit(error_code);
|
||||
}
|
||||
}
|
||||
@ -469,8 +483,13 @@ pub async fn run_pipeline_standalone(
|
||||
|
||||
/// 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() -> Result<(), Box<dyn Error>> {
|
||||
let mut syncer = crate::env::environment_syncer::EnvironmentSyncer::new();
|
||||
let mut context = create_default_context(&mut syncer)?;
|
||||
#[cfg(windows)]
|
||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::Circular;
|
||||
#[cfg(not(windows))]
|
||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
|
||||
|
||||
let mut syncer = crate::EnvironmentSyncer::new();
|
||||
let mut context = create_default_context(&mut syncer, true)?;
|
||||
|
||||
let _ = load_plugins(&mut context);
|
||||
|
||||
@ -550,14 +569,21 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
rl.set_edit_mode(edit_mode);
|
||||
|
||||
let key_timeout = config::config(Tag::unknown())?
|
||||
.get("key_timeout")
|
||||
.map(|s| s.value.expect_int())
|
||||
.unwrap_or(1);
|
||||
|
||||
rl.set_keyseq_timeout(key_timeout as i32);
|
||||
|
||||
let completion_mode = config::config(Tag::unknown())?
|
||||
.get("completion_mode")
|
||||
.map(|s| match s.value.expect_string() {
|
||||
"list" => CompletionType::List,
|
||||
"circular" => CompletionType::Circular,
|
||||
_ => CompletionType::Circular,
|
||||
_ => DEFAULT_COMPLETION_MODE,
|
||||
})
|
||||
.unwrap_or(CompletionType::Circular);
|
||||
.unwrap_or(DEFAULT_COMPLETION_MODE);
|
||||
|
||||
rl.set_completion_type(completion_mode);
|
||||
|
||||
@ -710,7 +736,7 @@ async fn process_line(
|
||||
debug!("=== Parsed ===");
|
||||
debug!("{:#?}", result);
|
||||
|
||||
let classified_block = nu_parser::classify_block(&result, ctx.registry());
|
||||
let mut classified_block = nu_parser::classify_block(&result, ctx.registry());
|
||||
|
||||
debug!("{:#?}", classified_block);
|
||||
//println!("{:#?}", pipeline);
|
||||
@ -759,9 +785,9 @@ async fn process_line(
|
||||
.as_ref()
|
||||
.map(NamedArguments::is_empty)
|
||||
.unwrap_or(true)
|
||||
&& dunce::canonicalize(&name).is_ok()
|
||||
&& PathBuf::from(&name).is_dir()
|
||||
&& ichwh::which(&name).await.unwrap_or(None).is_none()
|
||||
&& canonicalize(ctx.shell_manager.path(), name).is_ok()
|
||||
&& Path::new(&name).is_dir()
|
||||
&& which::which(&name).is_err()
|
||||
{
|
||||
// Here we work differently if we're in Windows because of the expected Windows behavior
|
||||
#[cfg(windows)]
|
||||
@ -787,7 +813,8 @@ async fn process_line(
|
||||
ctx.shell_manager.set_path(val.to_string());
|
||||
return LineResult::Success(line.to_string());
|
||||
} else {
|
||||
ctx.shell_manager.set_path(name.to_string());
|
||||
ctx.shell_manager
|
||||
.set_path(format!("{}\\", name.to_string()));
|
||||
return LineResult::Success(line.to_string());
|
||||
}
|
||||
} else {
|
||||
@ -829,7 +856,11 @@ async fn process_line(
|
||||
InputStream::empty()
|
||||
};
|
||||
|
||||
match run_block(&classified_block.block, ctx, input_stream, &Scope::empty()).await {
|
||||
classified_block.block.expand_it_usage();
|
||||
|
||||
trace!("{:#?}", classified_block);
|
||||
let env = ctx.get_env();
|
||||
match run_block(&classified_block.block, ctx, input_stream, &Scope::env(env)).await {
|
||||
Ok(input) => {
|
||||
// Running a pipeline gives us back a stream that we can then
|
||||
// work through. At the top level, we just want to pull on the
|
||||
@ -896,7 +927,6 @@ pub fn print_err(err: ShellError, host: &dyn Host, source: &Text) {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[quickcheck]
|
||||
fn quickcheck_parse(data: String) -> bool {
|
||||
if let Ok(lite_block) = nu_parser::lite_parse(&data, 0) {
|
||||
|
@ -8,6 +8,7 @@ pub(crate) mod alias;
|
||||
pub(crate) mod append;
|
||||
pub(crate) mod args;
|
||||
pub(crate) mod autoview;
|
||||
pub(crate) mod cal;
|
||||
pub(crate) mod calc;
|
||||
pub(crate) mod cd;
|
||||
pub(crate) mod classified;
|
||||
@ -20,18 +21,20 @@ pub(crate) mod cp;
|
||||
pub(crate) mod date;
|
||||
pub(crate) mod debug;
|
||||
pub(crate) mod default;
|
||||
pub(crate) mod drop;
|
||||
pub(crate) mod du;
|
||||
pub(crate) mod each;
|
||||
pub(crate) mod echo;
|
||||
pub(crate) mod edit;
|
||||
pub(crate) mod enter;
|
||||
#[allow(unused)]
|
||||
pub(crate) mod evaluate_by;
|
||||
pub(crate) mod exit;
|
||||
pub(crate) mod first;
|
||||
pub(crate) mod format;
|
||||
pub(crate) mod from;
|
||||
pub(crate) mod from_bson;
|
||||
pub(crate) mod from_csv;
|
||||
pub(crate) mod from_eml;
|
||||
pub(crate) mod from_ics;
|
||||
pub(crate) mod from_ini;
|
||||
pub(crate) mod from_json;
|
||||
@ -52,18 +55,22 @@ pub(crate) mod help;
|
||||
pub(crate) mod histogram;
|
||||
pub(crate) mod history;
|
||||
pub(crate) mod insert;
|
||||
pub(crate) mod is_empty;
|
||||
pub(crate) mod keep;
|
||||
pub(crate) mod keep_until;
|
||||
pub(crate) mod keep_while;
|
||||
pub(crate) mod last;
|
||||
pub(crate) mod lines;
|
||||
pub(crate) mod ls;
|
||||
#[allow(unused)]
|
||||
pub(crate) mod map_max_by;
|
||||
pub(crate) mod merge;
|
||||
pub(crate) mod mkdir;
|
||||
pub(crate) mod mv;
|
||||
pub(crate) mod next;
|
||||
pub(crate) mod nth;
|
||||
pub(crate) mod open;
|
||||
pub(crate) mod parse;
|
||||
pub(crate) mod pick;
|
||||
pub(crate) mod pivot;
|
||||
pub(crate) mod plugin;
|
||||
pub(crate) mod prepend;
|
||||
@ -79,10 +86,12 @@ pub(crate) mod rm;
|
||||
pub(crate) mod run_alias;
|
||||
pub(crate) mod run_external;
|
||||
pub(crate) mod save;
|
||||
pub(crate) mod select;
|
||||
pub(crate) mod shells;
|
||||
pub(crate) mod shuffle;
|
||||
pub(crate) mod size;
|
||||
pub(crate) mod skip;
|
||||
pub(crate) mod skip_until;
|
||||
pub(crate) mod skip_while;
|
||||
pub(crate) mod sort_by;
|
||||
pub(crate) mod split_by;
|
||||
@ -93,6 +102,7 @@ pub(crate) mod sum;
|
||||
pub(crate) mod t_sort_by;
|
||||
pub(crate) mod table;
|
||||
pub(crate) mod tags;
|
||||
pub(crate) mod to;
|
||||
pub(crate) mod to_bson;
|
||||
pub(crate) mod to_csv;
|
||||
pub(crate) mod to_html;
|
||||
@ -105,21 +115,23 @@ pub(crate) mod to_url;
|
||||
pub(crate) mod to_yaml;
|
||||
pub(crate) mod trim;
|
||||
pub(crate) mod uniq;
|
||||
pub(crate) mod update;
|
||||
pub(crate) mod version;
|
||||
pub(crate) mod what;
|
||||
pub(crate) mod where_;
|
||||
pub(crate) mod which_;
|
||||
pub(crate) mod with_env;
|
||||
pub(crate) mod wrap;
|
||||
|
||||
pub(crate) use autoview::Autoview;
|
||||
pub(crate) use cd::Cd;
|
||||
pub(crate) use command::{
|
||||
per_item_command, whole_stream_command, Command, PerItemCommand, UnevaluatedCallInfo,
|
||||
WholeStreamCommand,
|
||||
whole_stream_command, Command, Example, UnevaluatedCallInfo, WholeStreamCommand,
|
||||
};
|
||||
|
||||
pub(crate) use alias::Alias;
|
||||
pub(crate) use append::Append;
|
||||
pub(crate) use cal::Cal;
|
||||
pub(crate) use calc::Calc;
|
||||
pub(crate) use compact::Compact;
|
||||
pub(crate) use config::Config;
|
||||
@ -128,10 +140,12 @@ pub(crate) use cp::Cpy;
|
||||
pub(crate) use date::Date;
|
||||
pub(crate) use debug::Debug;
|
||||
pub(crate) use default::Default;
|
||||
pub(crate) use drop::Drop;
|
||||
pub(crate) use du::Du;
|
||||
pub(crate) use each::Each;
|
||||
pub(crate) use echo::Echo;
|
||||
pub(crate) use edit::Edit;
|
||||
pub(crate) use is_empty::IsEmpty;
|
||||
pub(crate) use update::Update;
|
||||
pub(crate) mod kill;
|
||||
pub(crate) use kill::Kill;
|
||||
pub(crate) mod clear;
|
||||
@ -143,8 +157,10 @@ pub(crate) use evaluate_by::EvaluateBy;
|
||||
pub(crate) use exit::Exit;
|
||||
pub(crate) use first::First;
|
||||
pub(crate) use format::Format;
|
||||
pub(crate) use from::From;
|
||||
pub(crate) use from_bson::FromBSON;
|
||||
pub(crate) use from_csv::FromCSV;
|
||||
pub(crate) use from_eml::FromEML;
|
||||
pub(crate) use from_ics::FromIcs;
|
||||
pub(crate) use from_ini::FromINI;
|
||||
pub(crate) use from_json::FromJSON;
|
||||
@ -167,18 +183,21 @@ pub(crate) use help::Help;
|
||||
pub(crate) use histogram::Histogram;
|
||||
pub(crate) use history::History;
|
||||
pub(crate) use insert::Insert;
|
||||
pub(crate) use keep::Keep;
|
||||
pub(crate) use keep_until::KeepUntil;
|
||||
pub(crate) use keep_while::KeepWhile;
|
||||
pub(crate) use last::Last;
|
||||
pub(crate) use lines::Lines;
|
||||
pub(crate) use ls::Ls;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use map_max_by::MapMaxBy;
|
||||
pub(crate) use merge::Merge;
|
||||
pub(crate) use mkdir::Mkdir;
|
||||
pub(crate) use mv::Move;
|
||||
pub(crate) use next::Next;
|
||||
pub(crate) use nth::Nth;
|
||||
pub(crate) use open::Open;
|
||||
pub(crate) use parse::Parse;
|
||||
pub(crate) use pick::Pick;
|
||||
pub(crate) use pivot::Pivot;
|
||||
pub(crate) use prepend::Prepend;
|
||||
pub(crate) use prev::Previous;
|
||||
@ -192,10 +211,12 @@ pub(crate) use reverse::Reverse;
|
||||
pub(crate) use rm::Remove;
|
||||
pub(crate) use run_external::RunExternalCommand;
|
||||
pub(crate) use save::Save;
|
||||
pub(crate) use select::Select;
|
||||
pub(crate) use shells::Shells;
|
||||
pub(crate) use shuffle::Shuffle;
|
||||
pub(crate) use size::Size;
|
||||
pub(crate) use skip::Skip;
|
||||
pub(crate) use skip_until::SkipUntil;
|
||||
pub(crate) use skip_while::SkipWhile;
|
||||
pub(crate) use sort_by::SortBy;
|
||||
pub(crate) use split_by::SplitBy;
|
||||
@ -206,6 +227,7 @@ pub(crate) use sum::Sum;
|
||||
pub(crate) use t_sort_by::TSortBy;
|
||||
pub(crate) use table::Table;
|
||||
pub(crate) use tags::Tags;
|
||||
pub(crate) use to::To;
|
||||
pub(crate) use to_bson::ToBSON;
|
||||
pub(crate) use to_csv::ToCSV;
|
||||
pub(crate) use to_html::ToHTML;
|
||||
@ -224,4 +246,5 @@ pub(crate) use version::Version;
|
||||
pub(crate) use what::What;
|
||||
pub(crate) use where_::Where;
|
||||
pub(crate) use which_::Which;
|
||||
pub(crate) use with_env::WithEnv;
|
||||
pub(crate) use wrap::Wrap;
|
||||
|
@ -1,14 +1,20 @@
|
||||
use crate::commands::PerItemCommand;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
CallInfo, CommandAction, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{hir::Block, CommandAction, ReturnSuccess, Signature, SyntaxShape, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Alias;
|
||||
|
||||
impl PerItemCommand for Alias {
|
||||
#[derive(Deserialize)]
|
||||
pub struct AliasArgs {
|
||||
pub name: Tagged<String>,
|
||||
pub args: Vec<Value>,
|
||||
pub block: Block,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Alias {
|
||||
fn name(&self) -> &str {
|
||||
"alias"
|
||||
}
|
||||
@ -17,49 +23,58 @@ impl PerItemCommand for Alias {
|
||||
Signature::build("alias")
|
||||
.required("name", SyntaxShape::String, "the name of the alias")
|
||||
.required("args", SyntaxShape::Table, "the arguments to the alias")
|
||||
.required("block", SyntaxShape::Block, "the block to run on each row")
|
||||
.required(
|
||||
"block",
|
||||
SyntaxShape::Block,
|
||||
"the block to run as the body of the alias",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Run a block on each row of the table."
|
||||
"Define a shortcut for another command."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
_raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let call_info = call_info.clone();
|
||||
let stream = async_stream! {
|
||||
match (call_info.args.expect_nth(0)?, call_info.args.expect_nth(1)?, call_info.args.expect_nth(2)?) {
|
||||
(Value {value: UntaggedValue::Primitive(Primitive::String(name)), .. },
|
||||
Value { value: UntaggedValue::Table(list), .. },
|
||||
Value {
|
||||
value: UntaggedValue::Block(block),
|
||||
tag
|
||||
}) => {
|
||||
let mut args: Vec<String> = vec![];
|
||||
for item in list.iter() {
|
||||
if let Ok(string) = item.as_string() {
|
||||
args.push(format!("${}", string));
|
||||
} else {
|
||||
yield Err(ShellError::labeled_error("Expected a string", "expected a string", item.tag()));
|
||||
}
|
||||
}
|
||||
yield ReturnSuccess::action(CommandAction::AddAlias(name.to_string(), args, block.clone()))
|
||||
}
|
||||
_ => {
|
||||
yield Err(ShellError::labeled_error(
|
||||
"Expected `name [args] {block}",
|
||||
"needs a name, args, and a block",
|
||||
call_info.name_tag,
|
||||
))
|
||||
}
|
||||
};
|
||||
};
|
||||
args.process(registry, alias)?.run()
|
||||
}
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "An alias without parameters",
|
||||
example: "alias say-hi [] { echo 'Hello!' }",
|
||||
},
|
||||
Example {
|
||||
description: "An alias with a single parameter",
|
||||
example: "alias l [x] { ls $x }",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alias(
|
||||
AliasArgs {
|
||||
name,
|
||||
args: list,
|
||||
block,
|
||||
}: AliasArgs,
|
||||
_: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let stream = async_stream! {
|
||||
let mut args: Vec<String> = vec![];
|
||||
for item in list.iter() {
|
||||
if let Ok(string) = item.as_string() {
|
||||
args.push(format!("${}", string));
|
||||
} else {
|
||||
yield Err(ShellError::labeled_error("Expected a string", "expected a string", item.tag()));
|
||||
}
|
||||
}
|
||||
yield ReturnSuccess::action(CommandAction::AddAlias(name.to_string(), args, block.clone()))
|
||||
};
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
|
@ -35,6 +35,13 @@ impl WholeStreamCommand for Append {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, append)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Add something to the end of a list or table",
|
||||
example: "echo [1 2 3] | append 4",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn append(
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::commands::UnevaluatedCallInfo;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::data::value::format_leaf;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir, hir::Expression, hir::Literal, hir::SpannedExpression};
|
||||
@ -36,6 +37,19 @@ impl WholeStreamCommand for Autoview {
|
||||
name: args.call_info.name_tag,
|
||||
})
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Automatically view the results",
|
||||
example: "ls | autoview",
|
||||
},
|
||||
Example {
|
||||
description: "Autoview is also implied. The above can be written as",
|
||||
example: "ls",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RunnableContextWithoutInput {
|
||||
@ -162,6 +176,26 @@ pub fn autoview(context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
} => {
|
||||
out!("{}", b);
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Duration(d)),
|
||||
..
|
||||
} => {
|
||||
let output = format_leaf(&x).plain_string(100_000);
|
||||
out!("{}", output);
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Date(d)),
|
||||
..
|
||||
} => {
|
||||
out!("{}", d);
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Range(_)),
|
||||
..
|
||||
} => {
|
||||
let output = format_leaf(&x).plain_string(100_000);
|
||||
out!("{}", output);
|
||||
}
|
||||
|
||||
Value { value: UntaggedValue::Primitive(Primitive::Binary(ref b)), .. } => {
|
||||
if let Some(binary) = binary {
|
||||
|
300
crates/nu-cli/src/commands/cal.rs
Normal file
300
crates/nu-cli/src/commands/cal.rs
Normal file
@ -0,0 +1,300 @@
|
||||
use crate::prelude::*;
|
||||
use chrono::{DateTime, Datelike, Local, NaiveDate};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::Dictionary;
|
||||
|
||||
use crate::commands::{command::EvaluatedWholeStreamCommandArgs, WholeStreamCommand};
|
||||
use indexmap::IndexMap;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct Cal;
|
||||
|
||||
impl WholeStreamCommand for Cal {
|
||||
fn name(&self) -> &str {
|
||||
"cal"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("cal")
|
||||
.switch("year", "Display the year column", Some('y'))
|
||||
.switch("quarter", "Display the quarter column", Some('q'))
|
||||
.switch("month", "Display the month column", Some('m'))
|
||||
.named(
|
||||
"full-year",
|
||||
SyntaxShape::Int,
|
||||
"Display a year-long calendar for the specified year",
|
||||
None,
|
||||
)
|
||||
.switch(
|
||||
"month-names",
|
||||
"Display the month names instead of integers",
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Display a calendar."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
cal(args, registry)
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "This month's calendar",
|
||||
example: "cal",
|
||||
},
|
||||
Example {
|
||||
description: "The calendar for all of 2012",
|
||||
example: "cal --full-year 2012",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cal(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let mut calendar_vec_deque = VecDeque::new();
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (current_year, current_month, current_day) = get_current_date();
|
||||
|
||||
if args.has("full-year") {
|
||||
let mut day_value: Option<u32> = Some(current_day);
|
||||
let mut year_value = current_year as u64;
|
||||
|
||||
if let Some(year) = args.get("full-year") {
|
||||
if let Ok(year_u64) = year.as_u64() {
|
||||
year_value = year_u64;
|
||||
}
|
||||
|
||||
if year_value != current_year as u64 {
|
||||
day_value = None
|
||||
}
|
||||
}
|
||||
|
||||
add_year_to_table(
|
||||
&mut calendar_vec_deque,
|
||||
&tag,
|
||||
year_value as i32,
|
||||
current_year,
|
||||
current_month,
|
||||
day_value,
|
||||
&args,
|
||||
);
|
||||
} else {
|
||||
let (day_start_offset, number_of_days_in_month, _) =
|
||||
get_month_information(current_year, current_month, current_year);
|
||||
|
||||
add_month_to_table(
|
||||
&mut calendar_vec_deque,
|
||||
&tag,
|
||||
current_year,
|
||||
current_month,
|
||||
Some(current_day),
|
||||
day_start_offset,
|
||||
number_of_days_in_month as usize,
|
||||
&args,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(futures::stream::iter(calendar_vec_deque).to_output_stream())
|
||||
}
|
||||
|
||||
fn get_current_date() -> (i32, u32, u32) {
|
||||
let local_now: DateTime<Local> = Local::now();
|
||||
|
||||
let current_year: i32 = local_now.date().year();
|
||||
let current_month: u32 = local_now.date().month();
|
||||
let current_day: u32 = local_now.date().day();
|
||||
|
||||
(current_year, current_month, current_day)
|
||||
}
|
||||
|
||||
fn add_year_to_table(
|
||||
mut calendar_vec_deque: &mut VecDeque<Value>,
|
||||
tag: &Tag,
|
||||
mut selected_year: i32,
|
||||
current_year: i32,
|
||||
current_month: u32,
|
||||
current_day_option: Option<u32>,
|
||||
args: &EvaluatedWholeStreamCommandArgs,
|
||||
) {
|
||||
for month_number in 1..=12 {
|
||||
let (day_start_offset, number_of_days_in_month, chosen_date_is_valid) =
|
||||
get_month_information(selected_year, month_number, current_year);
|
||||
|
||||
if !chosen_date_is_valid {
|
||||
selected_year = current_year;
|
||||
}
|
||||
|
||||
let mut new_current_day_option: Option<u32> = None;
|
||||
|
||||
if let Some(current_day) = current_day_option {
|
||||
if month_number == current_month {
|
||||
new_current_day_option = Some(current_day)
|
||||
}
|
||||
}
|
||||
|
||||
add_month_to_table(
|
||||
&mut calendar_vec_deque,
|
||||
&tag,
|
||||
selected_year,
|
||||
month_number,
|
||||
new_current_day_option,
|
||||
day_start_offset,
|
||||
number_of_days_in_month,
|
||||
&args,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn add_month_to_table(
|
||||
calendar_vec_deque: &mut VecDeque<Value>,
|
||||
tag: &Tag,
|
||||
year: i32,
|
||||
month: u32,
|
||||
_current_day_option: Option<u32>, // Can be used in the future to display current day
|
||||
day_start_offset: usize,
|
||||
number_of_days_in_month: usize,
|
||||
args: &EvaluatedWholeStreamCommandArgs,
|
||||
) {
|
||||
let day_limit = number_of_days_in_month + day_start_offset;
|
||||
let mut day_count: usize = 1;
|
||||
|
||||
let days_of_the_week = [
|
||||
"sunday",
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday",
|
||||
"thurday",
|
||||
"friday",
|
||||
"saturday",
|
||||
];
|
||||
|
||||
let should_show_year_column = args.has("year");
|
||||
let should_show_month_column = args.has("month");
|
||||
let should_show_quarter_column = args.has("quarter");
|
||||
let should_show_month_names = args.has("month-names");
|
||||
|
||||
while day_count <= day_limit {
|
||||
let mut indexmap = IndexMap::new();
|
||||
|
||||
if should_show_year_column {
|
||||
indexmap.insert("year".to_string(), UntaggedValue::int(year).into_value(tag));
|
||||
}
|
||||
|
||||
if should_show_quarter_column {
|
||||
indexmap.insert(
|
||||
"quarter".to_string(),
|
||||
UntaggedValue::int(get_quarter_number(month)).into_value(tag),
|
||||
);
|
||||
}
|
||||
|
||||
if should_show_month_column {
|
||||
let month_value = if should_show_month_names {
|
||||
UntaggedValue::string(get_month_name(month)).into_value(tag)
|
||||
} else {
|
||||
UntaggedValue::int(month).into_value(tag)
|
||||
};
|
||||
|
||||
indexmap.insert("month".to_string(), month_value);
|
||||
}
|
||||
|
||||
for day in &days_of_the_week {
|
||||
let value = if (day_count <= day_limit) && (day_count > day_start_offset) {
|
||||
UntaggedValue::int(day_count - day_start_offset).into_value(tag)
|
||||
} else {
|
||||
UntaggedValue::nothing().into_value(tag)
|
||||
};
|
||||
|
||||
indexmap.insert((*day).to_string(), value);
|
||||
|
||||
day_count += 1;
|
||||
}
|
||||
|
||||
calendar_vec_deque
|
||||
.push_back(UntaggedValue::Row(Dictionary::from(indexmap)).into_value(tag));
|
||||
}
|
||||
}
|
||||
|
||||
fn get_quarter_number(month_number: u32) -> u8 {
|
||||
match month_number {
|
||||
1..=3 => 1,
|
||||
4..=6 => 2,
|
||||
7..=9 => 3,
|
||||
_ => 4,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_month_name(month_number: u32) -> String {
|
||||
let month_name = match month_number {
|
||||
1 => "january",
|
||||
2 => "february",
|
||||
3 => "march",
|
||||
4 => "april",
|
||||
5 => "may",
|
||||
6 => "june",
|
||||
7 => "july",
|
||||
8 => "august",
|
||||
9 => "september",
|
||||
10 => "october",
|
||||
11 => "november",
|
||||
_ => "december",
|
||||
};
|
||||
|
||||
month_name.to_string()
|
||||
}
|
||||
|
||||
fn get_month_information(
|
||||
selected_year: i32,
|
||||
month: u32,
|
||||
current_year: i32,
|
||||
) -> (usize, usize, bool) {
|
||||
let (naive_date, chosen_date_is_valid_one) =
|
||||
get_safe_naive_date(selected_year, month, current_year);
|
||||
let weekday = naive_date.weekday();
|
||||
let (days_in_month, chosen_date_is_valid_two) =
|
||||
get_days_in_month(selected_year, month, current_year);
|
||||
|
||||
(
|
||||
weekday.num_days_from_sunday() as usize,
|
||||
days_in_month,
|
||||
chosen_date_is_valid_one && chosen_date_is_valid_two,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_days_in_month(selected_year: i32, month: u32, current_year: i32) -> (usize, bool) {
|
||||
// Chrono does not provide a method to output the amount of days in a month
|
||||
// This is a workaround taken from the example code from the Chrono docs here:
|
||||
// https://docs.rs/chrono/0.3.0/chrono/naive/date/struct.NaiveDate.html#example-30
|
||||
let (adjusted_year, adjusted_month) = if month == 12 {
|
||||
(selected_year + 1, 1)
|
||||
} else {
|
||||
(selected_year, month + 1)
|
||||
};
|
||||
|
||||
let (naive_date, chosen_date_is_valid) =
|
||||
get_safe_naive_date(adjusted_year, adjusted_month, current_year);
|
||||
|
||||
(naive_date.pred().day() as usize, chosen_date_is_valid)
|
||||
}
|
||||
|
||||
fn get_safe_naive_date(
|
||||
selected_year: i32,
|
||||
selected_month: u32,
|
||||
current_year: i32,
|
||||
) -> (NaiveDate, bool) {
|
||||
if let Some(naive_date) = NaiveDate::from_ymd_opt(selected_year, selected_month, 1) {
|
||||
return (naive_date, true);
|
||||
}
|
||||
|
||||
(NaiveDate::from_ymd(current_year, selected_month, 1), false)
|
||||
}
|
@ -1,11 +1,14 @@
|
||||
use crate::commands::PerItemCommand;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, Primitive, ReturnSuccess, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value};
|
||||
|
||||
pub struct Calc;
|
||||
|
||||
impl PerItemCommand for Calc {
|
||||
#[derive(Deserialize)]
|
||||
pub struct CalcArgs {}
|
||||
|
||||
impl WholeStreamCommand for Calc {
|
||||
fn name(&self) -> &str {
|
||||
"calc"
|
||||
}
|
||||
@ -16,36 +19,44 @@ impl PerItemCommand for Calc {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
input: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
calc(input, raw_args)
|
||||
args.process(registry, calc)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Calculate math in the pipeline",
|
||||
example: "echo '10 / 4' | calc",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn calc(input: Value, args: &RawCommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_span = &args.call_info.name_tag.span;
|
||||
|
||||
let output = if let Ok(string) = input.as_string() {
|
||||
match parse(&string, &input.tag) {
|
||||
Ok(value) => ReturnSuccess::value(value),
|
||||
Err(err) => Err(ShellError::labeled_error(
|
||||
"Calculation error",
|
||||
err,
|
||||
&input.tag.span,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
name_span,
|
||||
))
|
||||
};
|
||||
|
||||
Ok(vec![output].into())
|
||||
pub fn calc(
|
||||
_: CalcArgs,
|
||||
RunnableContext { input, name, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
Ok(input
|
||||
.map(move |input| {
|
||||
if let Ok(string) = input.as_string() {
|
||||
match parse(&string, &input.tag) {
|
||||
Ok(value) => ReturnSuccess::value(value),
|
||||
Err(err) => Err(ShellError::labeled_error(
|
||||
"Calculation error",
|
||||
err,
|
||||
&input.tag.span,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
name.clone(),
|
||||
))
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
pub fn parse(math_expression: &str, tag: impl Into<Tag>) -> Result<Value, String> {
|
||||
|
@ -1,7 +1,16 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CdArgs {
|
||||
pub(crate) path: Option<Tagged<PathBuf>>,
|
||||
}
|
||||
|
||||
pub struct Cd;
|
||||
|
||||
@ -27,12 +36,31 @@ impl WholeStreamCommand for Cd {
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
cd(args, registry)
|
||||
args.process(registry, cd)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Change to a new directory called 'dirname'",
|
||||
example: "cd dirname",
|
||||
},
|
||||
Example {
|
||||
description: "Change to your home directory",
|
||||
example: "cd",
|
||||
},
|
||||
Example {
|
||||
description: "Change to your home directory (alternate version)",
|
||||
example: "cd ~",
|
||||
},
|
||||
Example {
|
||||
description: "Change to the previous directory",
|
||||
example: "cd -",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn cd(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
let args = args.evaluate_once(registry)?;
|
||||
shell_manager.cd(args)
|
||||
fn cd(args: CdArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
context.shell_manager.cd(args, &context)
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::commands::classified::expr::run_expression_block;
|
||||
//use crate::commands::classified::external::run_external_command;
|
||||
use crate::commands::classified::internal::run_internal_command;
|
||||
use crate::context::Context;
|
||||
use crate::prelude::*;
|
||||
|
@ -3,6 +3,7 @@ use crate::prelude::*;
|
||||
|
||||
use log::{log_enabled, trace};
|
||||
|
||||
use futures::stream::once;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::SpannedExpression;
|
||||
use nu_protocol::Scope;
|
||||
@ -10,7 +11,7 @@ use nu_protocol::Scope;
|
||||
pub(crate) fn run_expression_block(
|
||||
expr: SpannedExpression,
|
||||
context: &mut Context,
|
||||
input: InputStream,
|
||||
_input: InputStream,
|
||||
scope: &Scope,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
if log_enabled!(log::Level::Trace) {
|
||||
@ -20,10 +21,7 @@ pub(crate) fn run_expression_block(
|
||||
|
||||
let scope = scope.clone();
|
||||
let registry = context.registry().clone();
|
||||
let stream = input.map(move |row| {
|
||||
let scope = scope.clone().set_it(row);
|
||||
evaluate_baseline_expr(&expr, ®istry, &scope)
|
||||
});
|
||||
let output = evaluate_baseline_expr(&expr, ®istry, &scope)?;
|
||||
|
||||
Ok(stream.to_input_stream())
|
||||
Ok(once(async { Ok(output) }).to_input_stream())
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use std::sync::mpsc;
|
||||
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use futures::executor::block_on_stream;
|
||||
use futures::stream::StreamExt;
|
||||
// use futures::stream::StreamExt;
|
||||
use futures_codec::FramedRead;
|
||||
use log::trace;
|
||||
|
||||
@ -82,20 +82,6 @@ impl futures_codec::Decoder for MaybeTextCodec {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nu_value_to_string(name_tag: &Tag, from: &Value) -> Result<String, ShellError> {
|
||||
match &from.value {
|
||||
UntaggedValue::Primitive(Primitive::Int(i)) => Ok(i.to_string()),
|
||||
UntaggedValue::Primitive(Primitive::String(s))
|
||||
| UntaggedValue::Primitive(Primitive::Line(s)) => Ok(s.clone()),
|
||||
UntaggedValue::Primitive(Primitive::Path(p)) => Ok(p.to_string_lossy().to_string()),
|
||||
unsupported => Err(ShellError::labeled_error(
|
||||
format!("needs string data (given: {})", unsupported.type_name()),
|
||||
"expected a string",
|
||||
name_tag,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn run_external_command(
|
||||
command: ExternalCommand,
|
||||
context: &mut Context,
|
||||
@ -113,85 +99,7 @@ pub(crate) async fn run_external_command(
|
||||
));
|
||||
}
|
||||
|
||||
if command.has_it_argument() {
|
||||
run_with_iterator_arg(command, context, input, scope, is_last)
|
||||
} else {
|
||||
run_with_stdin(command, context, input, scope, is_last)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_with_iterator_arg(
|
||||
command: ExternalCommand,
|
||||
context: &mut Context,
|
||||
input: InputStream,
|
||||
scope: &Scope,
|
||||
is_last: bool,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
let path = context.shell_manager.path();
|
||||
|
||||
let mut inputs: InputStream =
|
||||
trace_stream!(target: "nu::trace_stream::external::it", "input" = input);
|
||||
|
||||
let name_tag = command.name_tag.clone();
|
||||
let scope = scope.clone();
|
||||
let context = context.clone();
|
||||
|
||||
let stream = async_stream! {
|
||||
while let Some(value) = inputs.next().await {
|
||||
// Evaluate the expressions into values, and from values into strings for each iteration
|
||||
let mut command_args = vec![];
|
||||
let scope = scope.clone().set_it(value);
|
||||
for arg in command.args.iter() {
|
||||
let value = evaluate_baseline_expr(arg, &context.registry, &scope)?;
|
||||
command_args.push(nu_value_to_string(&name_tag, &value)?);
|
||||
}
|
||||
|
||||
let process_args = command_args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
let arg = expand_tilde(arg.deref(), dirs::home_dir);
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
if argument_contains_whitespace(&arg) && argument_is_quoted(&arg) {
|
||||
if let Some(unquoted) = remove_quotes(&arg) {
|
||||
format!(r#""{}""#, unquoted)
|
||||
} else {
|
||||
arg.as_ref().to_string()
|
||||
}
|
||||
} else {
|
||||
arg.as_ref().to_string()
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if let Some(unquoted) = remove_quotes(&arg) {
|
||||
unquoted.to_string()
|
||||
} else {
|
||||
arg.as_ref().to_string()
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
match spawn(&command, &path, &process_args[..], InputStream::empty(), is_last) {
|
||||
Ok(mut res) => {
|
||||
while let Some(item) = res.next().await {
|
||||
yield Ok(item)
|
||||
}
|
||||
}
|
||||
Err(reason) => {
|
||||
yield Ok(Value {
|
||||
value: UntaggedValue::Error(reason),
|
||||
tag: name_tag
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(stream.to_input_stream())
|
||||
run_with_stdin(command, context, input, scope, is_last)
|
||||
}
|
||||
|
||||
fn run_with_stdin(
|
||||
@ -208,7 +116,29 @@ fn run_with_stdin(
|
||||
let mut command_args = vec![];
|
||||
for arg in command.args.iter() {
|
||||
let value = evaluate_baseline_expr(arg, &context.registry, scope)?;
|
||||
command_args.push(value.as_string()?);
|
||||
// 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
|
||||
// what value we would put in its place.
|
||||
if value.value.is_none() {
|
||||
continue;
|
||||
}
|
||||
// Do the cleanup that we need to do on any argument going out:
|
||||
let trimmed_value_string = value.as_string()?.trim_end_matches('\n').to_string();
|
||||
|
||||
let value_string;
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
value_string = trimmed_value_string
|
||||
.replace('$', "\\$")
|
||||
.replace('"', "\\\"")
|
||||
.to_string()
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
value_string = trimmed_value_string
|
||||
}
|
||||
|
||||
command_args.push(value_string);
|
||||
}
|
||||
|
||||
let process_args = command_args
|
||||
@ -235,7 +165,7 @@ fn run_with_stdin(
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
spawn(&command, &path, &process_args[..], input, is_last)
|
||||
spawn(&command, &path, &process_args[..], input, is_last, scope)
|
||||
}
|
||||
|
||||
fn spawn(
|
||||
@ -244,6 +174,7 @@ fn spawn(
|
||||
args: &[String],
|
||||
input: InputStream,
|
||||
is_last: bool,
|
||||
scope: &Scope,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
let command = command.clone();
|
||||
|
||||
@ -273,6 +204,9 @@ fn spawn(
|
||||
process.current_dir(path);
|
||||
trace!(target: "nu::run::external", "cwd = {:?}", &path);
|
||||
|
||||
process.env_clear();
|
||||
process.envs(scope.env.iter());
|
||||
|
||||
// We want stdout regardless of what
|
||||
// we are doing ($it case or pipe stdin)
|
||||
if !is_last {
|
||||
@ -405,16 +339,27 @@ fn spawn(
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"Unable to read from stdout.",
|
||||
"unable to read from stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
break;
|
||||
Err(e) => {
|
||||
// If there's an exit status, it makes sense that we may error when
|
||||
// trying to read from its stdout pipe (likely been closed). In that
|
||||
// case, don't emit an error.
|
||||
let should_error = match child.wait() {
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
Err(_) => true,
|
||||
};
|
||||
|
||||
if should_error {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!("Unable to read from stdout ({})", e),
|
||||
"unable to read from stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -424,10 +369,7 @@ fn spawn(
|
||||
// than what other shells will do.
|
||||
let external_failed = match child.wait() {
|
||||
Err(_) => true,
|
||||
Ok(exit_status) => match exit_status.code() {
|
||||
Some(e) if e != 0 => true,
|
||||
_ => false,
|
||||
},
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
};
|
||||
|
||||
if external_failed {
|
||||
@ -467,12 +409,12 @@ fn spawn(
|
||||
async fn did_find_command(name: &str) -> bool {
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
ichwh::which(name).await.unwrap_or(None).is_some()
|
||||
which::which(name).is_ok()
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if ichwh::which(name).await.unwrap_or(None).is_some() {
|
||||
if which::which(name).is_ok() {
|
||||
true
|
||||
} else {
|
||||
let cmd_builtins = [
|
||||
@ -514,6 +456,7 @@ fn add_quotes(argument: &str) -> String {
|
||||
format!("\"{}\"", argument)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn remove_quotes(argument: &str) -> Option<&str> {
|
||||
if !argument_is_quoted(argument) {
|
||||
return None;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::commands::command::per_item_command;
|
||||
use crate::commands::command::whole_stream_command;
|
||||
use crate::commands::run_alias::AliasCommand;
|
||||
use crate::commands::UnevaluatedCallInfo;
|
||||
use crate::prelude::*;
|
||||
@ -33,6 +33,7 @@ pub(crate) fn run_internal_command(
|
||||
|
||||
let mut result = trace_out_stream!(target: "nu::trace_stream::internal", "output" = result);
|
||||
let mut context = context.clone();
|
||||
let scope = scope.clone();
|
||||
|
||||
let stream = async_stream! {
|
||||
let mut soft_errs: Vec<ShellError> = vec![];
|
||||
@ -51,7 +52,7 @@ pub(crate) fn run_internal_command(
|
||||
}
|
||||
CommandAction::AutoConvert(tagged_contents, extension) => {
|
||||
let contents_tag = tagged_contents.tag.clone();
|
||||
let command_name = format!("from-{}", extension);
|
||||
let command_name = format!("from {}", extension);
|
||||
let command = command.clone();
|
||||
if let Some(converter) = context.registry.get_command(&command_name) {
|
||||
let new_args = RawCommandArgs {
|
||||
@ -67,7 +68,7 @@ pub(crate) fn run_internal_command(
|
||||
is_last: false,
|
||||
},
|
||||
name_tag: Tag::unknown_anchor(command.name_span),
|
||||
scope: Scope::empty(),
|
||||
scope: scope.clone(),
|
||||
}
|
||||
};
|
||||
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), &context.registry);
|
||||
@ -117,12 +118,12 @@ pub(crate) fn run_internal_command(
|
||||
}
|
||||
CommandAction::EnterShell(location) => {
|
||||
context.shell_manager.insert_at_current(Box::new(
|
||||
FilesystemShell::with_location(location, context.registry().clone()),
|
||||
FilesystemShell::with_location(location, context.registry().clone())?,
|
||||
));
|
||||
}
|
||||
CommandAction::AddAlias(name, args, block) => {
|
||||
context.add_commands(vec![
|
||||
per_item_command(AliasCommand::new(
|
||||
whole_stream_command(AliasCommand::new(
|
||||
name,
|
||||
args,
|
||||
block,
|
||||
|
@ -23,6 +23,12 @@ impl WholeStreamCommand for Clear {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
clear(args, registry)
|
||||
}
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Clear the screen",
|
||||
example: "clear",
|
||||
}]
|
||||
}
|
||||
}
|
||||
fn clear(_args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
if cfg!(windows) {
|
||||
|
@ -34,6 +34,13 @@ pub mod clipboard {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, clip)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Save text to the clipboard",
|
||||
example: "echo 'secret value' | clip",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clip(
|
||||
|
@ -49,36 +49,6 @@ impl UnevaluatedCallInfo {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CallInfoExt {
|
||||
fn process<'de, T: Deserialize<'de>>(
|
||||
&self,
|
||||
shell_manager: &ShellManager,
|
||||
ctrl_c: Arc<AtomicBool>,
|
||||
callback: fn(T, &RunnablePerItemContext) -> Result<OutputStream, ShellError>,
|
||||
) -> Result<RunnablePerItemArgs<T>, ShellError>;
|
||||
}
|
||||
|
||||
impl CallInfoExt for CallInfo {
|
||||
fn process<'de, T: Deserialize<'de>>(
|
||||
&self,
|
||||
shell_manager: &ShellManager,
|
||||
ctrl_c: Arc<AtomicBool>,
|
||||
callback: fn(T, &RunnablePerItemContext) -> Result<OutputStream, ShellError>,
|
||||
) -> Result<RunnablePerItemArgs<T>, ShellError> {
|
||||
let mut deserializer = ConfigDeserializer::from_call_info(self.clone());
|
||||
|
||||
Ok(RunnablePerItemArgs {
|
||||
args: T::deserialize(&mut deserializer)?,
|
||||
context: RunnablePerItemContext {
|
||||
shell_manager: shell_manager.clone(),
|
||||
name: self.name_tag.clone(),
|
||||
ctrl_c,
|
||||
},
|
||||
callback,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Getters)]
|
||||
#[get = "pub(crate)"]
|
||||
pub struct CommandArgs {
|
||||
@ -227,12 +197,6 @@ impl CommandArgs {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RunnablePerItemContext {
|
||||
pub shell_manager: ShellManager,
|
||||
pub name: Tag,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
pub struct RunnableContext {
|
||||
pub input: InputStream,
|
||||
pub shell_manager: ShellManager,
|
||||
@ -243,23 +207,11 @@ pub struct RunnableContext {
|
||||
}
|
||||
|
||||
impl RunnableContext {
|
||||
pub fn get_command(&self, name: &str) -> Option<Arc<Command>> {
|
||||
pub fn get_command(&self, name: &str) -> Option<Command> {
|
||||
self.registry.get_command(name)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RunnablePerItemArgs<T> {
|
||||
args: T,
|
||||
context: RunnablePerItemContext,
|
||||
callback: fn(T, &RunnablePerItemContext) -> Result<OutputStream, ShellError>,
|
||||
}
|
||||
|
||||
impl<T> RunnablePerItemArgs<T> {
|
||||
pub fn run(self) -> Result<OutputStream, ShellError> {
|
||||
(self.callback)(self.args, &self.context)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RunnableArgs<T, O: ToOutputStream> {
|
||||
args: T,
|
||||
context: RunnableContext,
|
||||
@ -398,6 +350,11 @@ impl EvaluatedCommandArgs {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Example {
|
||||
pub example: &'static str,
|
||||
pub description: &'static str,
|
||||
}
|
||||
|
||||
pub trait WholeStreamCommand: Send + Sync {
|
||||
fn name(&self) -> &str;
|
||||
|
||||
@ -416,152 +373,64 @@ pub trait WholeStreamCommand: Send + Sync {
|
||||
fn is_binary(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PerItemCommand: Send + Sync {
|
||||
fn name(&self) -> &str;
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::new(self.name()).desc(self.usage()).filter()
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str;
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
input: Value,
|
||||
) -> Result<OutputStream, ShellError>;
|
||||
|
||||
fn is_binary(&self) -> bool {
|
||||
false
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Command {
|
||||
WholeStream(Arc<dyn WholeStreamCommand>),
|
||||
PerItem(Arc<dyn PerItemCommand>),
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Command(Arc<dyn WholeStreamCommand>);
|
||||
|
||||
impl PrettyDebugWithSource for Command {
|
||||
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
||||
match self {
|
||||
Command::WholeStream(command) => b::typed(
|
||||
"whole stream command",
|
||||
b::description(command.name())
|
||||
+ b::space()
|
||||
+ b::equals()
|
||||
+ b::space()
|
||||
+ command.signature().pretty_debug(source),
|
||||
),
|
||||
Command::PerItem(command) => b::typed(
|
||||
"per item command",
|
||||
b::description(command.name())
|
||||
+ b::space()
|
||||
+ b::equals()
|
||||
+ b::space()
|
||||
+ command.signature().pretty_debug(source),
|
||||
),
|
||||
}
|
||||
b::typed(
|
||||
"whole stream command",
|
||||
b::description(self.name())
|
||||
+ b::space()
|
||||
+ b::equals()
|
||||
+ b::space()
|
||||
+ self.signature().pretty_debug(source),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Command {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Command::WholeStream(command) => write!(f, "WholeStream({})", command.name()),
|
||||
Command::PerItem(command) => write!(f, "PerItem({})", command.name()),
|
||||
}
|
||||
write!(f, "Command({})", self.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Command::WholeStream(command) => command.name(),
|
||||
Command::PerItem(command) => command.name(),
|
||||
}
|
||||
self.0.name()
|
||||
}
|
||||
|
||||
pub fn signature(&self) -> Signature {
|
||||
match self {
|
||||
Command::WholeStream(command) => command.signature(),
|
||||
Command::PerItem(command) => command.signature(),
|
||||
}
|
||||
self.0.signature()
|
||||
}
|
||||
|
||||
pub fn usage(&self) -> &str {
|
||||
match self {
|
||||
Command::WholeStream(command) => command.usage(),
|
||||
Command::PerItem(command) => command.usage(),
|
||||
}
|
||||
self.0.usage()
|
||||
}
|
||||
|
||||
pub fn run(&self, args: CommandArgs, registry: &CommandRegistry) -> OutputStream {
|
||||
if args.call_info.switch_present("help") {
|
||||
get_help(self.name(), self.usage(), self.signature()).into()
|
||||
get_help(&*self.0, registry).into()
|
||||
} else {
|
||||
match self {
|
||||
Command::WholeStream(command) => match command.run(args, registry) {
|
||||
Ok(stream) => stream,
|
||||
Err(err) => OutputStream::one(Err(err)),
|
||||
},
|
||||
Command::PerItem(command) => {
|
||||
self.run_helper(command.clone(), args, registry.clone())
|
||||
}
|
||||
match self.0.run(args, registry) {
|
||||
Ok(stream) => stream,
|
||||
Err(err) => OutputStream::one(Err(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_helper(
|
||||
&self,
|
||||
command: Arc<dyn PerItemCommand>,
|
||||
args: CommandArgs,
|
||||
registry: CommandRegistry,
|
||||
) -> OutputStream {
|
||||
let raw_args = RawCommandArgs {
|
||||
host: args.host,
|
||||
ctrl_c: args.ctrl_c,
|
||||
shell_manager: args.shell_manager,
|
||||
call_info: args.call_info,
|
||||
};
|
||||
|
||||
let out = args
|
||||
.input
|
||||
.map(move |x| {
|
||||
let call_info = UnevaluatedCallInfo {
|
||||
args: raw_args.call_info.args.clone(),
|
||||
name_tag: raw_args.call_info.name_tag.clone(),
|
||||
scope: raw_args.call_info.scope.clone().set_it(x.clone()),
|
||||
}
|
||||
.evaluate(®istry);
|
||||
// let call_info = raw_args
|
||||
// .clone()
|
||||
// .call_info
|
||||
// .evaluate(®istry, &Scope::it_value(x.clone()));
|
||||
|
||||
match call_info {
|
||||
Ok(call_info) => match command.run(&call_info, ®istry, &raw_args, x) {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
futures::stream::iter(vec![ReturnValue::Err(e)]).to_output_stream()
|
||||
}
|
||||
},
|
||||
Err(e) => futures::stream::iter(vec![ReturnValue::Err(e)]).to_output_stream(),
|
||||
}
|
||||
})
|
||||
.flatten();
|
||||
|
||||
out.to_output_stream()
|
||||
pub fn is_binary(&self) -> bool {
|
||||
self.0.is_binary()
|
||||
}
|
||||
|
||||
pub fn is_binary(&self) -> bool {
|
||||
match self {
|
||||
Command::WholeStream(command) => command.is_binary(),
|
||||
Command::PerItem(command) => command.is_binary(),
|
||||
}
|
||||
pub fn stream_command(&self) -> &dyn WholeStreamCommand {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -623,10 +492,6 @@ impl WholeStreamCommand for FnFilterCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn whole_stream_command(command: impl WholeStreamCommand + 'static) -> Arc<Command> {
|
||||
Arc::new(Command::WholeStream(Arc::new(command)))
|
||||
}
|
||||
|
||||
pub fn per_item_command(command: impl PerItemCommand + 'static) -> Arc<Command> {
|
||||
Arc::new(Command::PerItem(Arc::new(command)))
|
||||
pub fn whole_stream_command(command: impl WholeStreamCommand + 'static) -> Command {
|
||||
Command(Arc::new(command))
|
||||
}
|
||||
|
@ -33,6 +33,13 @@ impl WholeStreamCommand for Compact {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, compact)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Remove all directory entries, except those with a 'target'",
|
||||
example: "ls -af | compact target",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compact(
|
||||
|
@ -72,6 +72,39 @@ impl WholeStreamCommand for Config {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, config)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "See all config values",
|
||||
example: "config",
|
||||
},
|
||||
Example {
|
||||
description: "Set completion_mode to circular",
|
||||
example: "config --set [completion_mode circular]",
|
||||
},
|
||||
Example {
|
||||
description: "Store the contents of the pipeline as a path",
|
||||
example: "echo ['/usr/bin' '/bin'] | config --set_into path",
|
||||
},
|
||||
Example {
|
||||
description: "Get the current startup commands",
|
||||
example: "config --get startup",
|
||||
},
|
||||
Example {
|
||||
description: "Remove the startup commands",
|
||||
example: "config --remove startup",
|
||||
},
|
||||
Example {
|
||||
description: "Clear the config (be careful!)",
|
||||
example: "config --clear",
|
||||
},
|
||||
Example {
|
||||
description: "Get the path to the current config file",
|
||||
example: "config --path",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config(
|
||||
|
@ -20,7 +20,7 @@ impl WholeStreamCommand for Count {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Show the total number of rows."
|
||||
"Show the total number of rows or items."
|
||||
}
|
||||
|
||||
fn run(
|
||||
@ -30,6 +30,13 @@ impl WholeStreamCommand for Count {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, count)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Count the number of files/directories in the current directory",
|
||||
example: "ls | count",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn count(
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::commands::command::RunnablePerItemContext;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@ -15,7 +15,7 @@ pub struct CopyArgs {
|
||||
pub recursive: Tagged<bool>,
|
||||
}
|
||||
|
||||
impl PerItemCommand for Cpy {
|
||||
impl WholeStreamCommand for Cpy {
|
||||
fn name(&self) -> &str {
|
||||
"cp"
|
||||
}
|
||||
@ -37,18 +37,27 @@ impl PerItemCommand for Cpy {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
call_info
|
||||
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), cp)?
|
||||
.run()
|
||||
args.process(registry, cp)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Copy myfile to dir_b",
|
||||
example: "cp myfile dir_b",
|
||||
},
|
||||
Example {
|
||||
description: "Recursively copy dir_a to dir_b",
|
||||
example: "cp -r dir_a dir_b",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn cp(args: CopyArgs, context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> {
|
||||
pub fn cp(args: CopyArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
let shell_manager = context.shell_manager.clone();
|
||||
shell_manager.cp(args, context)
|
||||
shell_manager.cp(args, &context)
|
||||
}
|
||||
|
@ -33,6 +33,19 @@ impl WholeStreamCommand for Date {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
date(args, registry)
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Get the current local time and date",
|
||||
example: "date",
|
||||
},
|
||||
Example {
|
||||
description: "Get the current UTC time and date",
|
||||
example: "date --utc",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn date_to_value<T: TimeZone>(dt: DateTime<T>, tag: Tag) -> Value
|
||||
|
@ -40,6 +40,13 @@ impl WholeStreamCommand for Default {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, default)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Give a default 'target' to all file entries",
|
||||
example: "ls -af | default target 'nothing'",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn default(
|
||||
|
73
crates/nu-cli/src/commands/drop.rs
Normal file
73
crates/nu-cli/src/commands/drop.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Drop;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DropArgs {
|
||||
rows: Option<Tagged<u64>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Drop {
|
||||
fn name(&self) -> &str {
|
||||
"drop"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("drop").optional(
|
||||
"rows",
|
||||
SyntaxShape::Number,
|
||||
"starting from the back, the number of rows to drop",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Drop the last number of rows."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, drop)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Remove the last item of a list/table",
|
||||
example: "echo [1 2 3] | drop",
|
||||
},
|
||||
Example {
|
||||
description: "Remove the last 2 items of a list/table",
|
||||
example: "echo [1 2 3] | drop 2",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn drop(DropArgs { rows }: DropArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
let stream = async_stream! {
|
||||
let v: Vec<_> = context.input.into_vec().await;
|
||||
|
||||
let rows_to_drop = if let Some(quantity) = rows {
|
||||
*quantity as usize
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
if rows_to_drop < v.len() {
|
||||
let k = v.len() - rows_to_drop;
|
||||
for x in v[0..k].iter() {
|
||||
let y: Value = x.clone();
|
||||
yield ReturnSuccess::value(y)
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
@ -1,12 +1,10 @@
|
||||
extern crate filesize;
|
||||
|
||||
use crate::commands::command::RunnablePerItemContext;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use filesize::file_real_size_fast;
|
||||
use glob::*;
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@ -31,7 +29,7 @@ pub struct DuArgs {
|
||||
min_size: Option<Tagged<u64>>,
|
||||
}
|
||||
|
||||
impl PerItemCommand for Du {
|
||||
impl WholeStreamCommand for Du {
|
||||
fn name(&self) -> &str {
|
||||
NAME
|
||||
}
|
||||
@ -75,18 +73,21 @@ impl PerItemCommand for Du {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
call_info
|
||||
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), du)?
|
||||
.run()
|
||||
args.process(registry, du)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Disk usage of the current directory",
|
||||
example: "du *",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn du(args: DuArgs, ctx: &RunnablePerItemContext) -> Result<OutputStream, ShellError> {
|
||||
fn du(args: DuArgs, ctx: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
let tag = ctx.name.clone();
|
||||
|
||||
let exclude = args.exclude.map_or(Ok(None), move |x| {
|
||||
@ -117,7 +118,7 @@ fn du(args: DuArgs, ctx: &RunnablePerItemContext) -> Result<OutputStream, ShellE
|
||||
})
|
||||
.map(|v| v.map_err(glob_err_into));
|
||||
|
||||
let ctrl_c = ctx.ctrl_c.clone();
|
||||
let ctrl_c = ctx.ctrl_c;
|
||||
let all = args.all;
|
||||
let deref = args.deref;
|
||||
let max_depth = args.max_depth.map(|f| f.item);
|
||||
@ -149,7 +150,7 @@ fn du(args: DuArgs, ctx: &RunnablePerItemContext) -> Result<OutputStream, ShellE
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
|
||||
struct DirBuilder {
|
||||
pub struct DirBuilder {
|
||||
tag: Tag,
|
||||
min: Option<u64>,
|
||||
deref: bool,
|
||||
@ -157,7 +158,25 @@ struct DirBuilder {
|
||||
all: bool,
|
||||
}
|
||||
|
||||
struct DirInfo {
|
||||
impl DirBuilder {
|
||||
pub fn new(
|
||||
tag: Tag,
|
||||
min: Option<u64>,
|
||||
deref: bool,
|
||||
exclude: Option<Pattern>,
|
||||
all: bool,
|
||||
) -> DirBuilder {
|
||||
DirBuilder {
|
||||
tag,
|
||||
min,
|
||||
deref,
|
||||
exclude,
|
||||
all,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DirInfo {
|
||||
dirs: Vec<DirInfo>,
|
||||
files: Vec<FileInfo>,
|
||||
errors: Vec<ShellError>,
|
||||
@ -200,7 +219,7 @@ impl FileInfo {
|
||||
}
|
||||
|
||||
impl DirInfo {
|
||||
fn new(path: impl Into<PathBuf>, params: &DirBuilder, depth: Option<u64>) -> Self {
|
||||
pub fn new(path: impl Into<PathBuf>, params: &DirBuilder, depth: Option<u64>) -> Self {
|
||||
let path = path.into();
|
||||
|
||||
let mut s = Self {
|
||||
@ -280,6 +299,10 @@ impl DirInfo {
|
||||
self.errors.push(e);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
fn glob_err_into(e: GlobError) -> ShellError {
|
||||
|
@ -1,16 +1,23 @@
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::PerItemCommand;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
|
||||
use futures::stream::once;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{
|
||||
hir::Block, hir::Expression, hir::SpannedExpression, hir::Synthetic, ReturnSuccess, Signature,
|
||||
SyntaxShape,
|
||||
};
|
||||
|
||||
pub struct Each;
|
||||
|
||||
impl PerItemCommand for Each {
|
||||
#[derive(Deserialize)]
|
||||
pub struct EachArgs {
|
||||
block: Block,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Each {
|
||||
fn name(&self) -> &str {
|
||||
"each"
|
||||
}
|
||||
@ -29,58 +36,74 @@ impl PerItemCommand for Each {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
input: Value,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let call_info = call_info.clone();
|
||||
let registry = registry.clone();
|
||||
let raw_args = raw_args.clone();
|
||||
let stream = async_stream! {
|
||||
match call_info.args.expect_nth(0)? {
|
||||
Value {
|
||||
value: UntaggedValue::Block(block),
|
||||
tag
|
||||
} => {
|
||||
let mut context = Context::from_raw(&raw_args, ®istry);
|
||||
let input_clone = input.clone();
|
||||
let input_stream = once(async { Ok(input) }).to_input_stream();
|
||||
Ok(args.process_raw(registry, each)?.run())
|
||||
}
|
||||
|
||||
let result = run_block(
|
||||
block,
|
||||
&mut context,
|
||||
input_stream,
|
||||
&Scope::new(input_clone),
|
||||
).await;
|
||||
|
||||
match result {
|
||||
Ok(mut stream) => {
|
||||
let errors = context.get_errors();
|
||||
if let Some(error) = errors.first() {
|
||||
yield Err(error.clone());
|
||||
return;
|
||||
}
|
||||
|
||||
while let Some(result) = stream.next().await {
|
||||
yield Ok(ReturnSuccess::Value(result));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
yield Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Value { tag, .. } => {
|
||||
yield Err(ShellError::labeled_error(
|
||||
"Expected a block",
|
||||
"each needs a block",
|
||||
tag,
|
||||
))
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Print the name of each file",
|
||||
example: "ls | each { echo $it.name }",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn is_expanded_it_usage(head: &SpannedExpression) -> bool {
|
||||
match &*head {
|
||||
SpannedExpression {
|
||||
expr: Expression::Synthetic(Synthetic::String(s)),
|
||||
..
|
||||
} if s == "expanded-each" => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn each(
|
||||
each_args: EachArgs,
|
||||
context: RunnableContext,
|
||||
raw_args: RawCommandArgs,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let block = each_args.block;
|
||||
let scope = raw_args.call_info.scope.clone();
|
||||
let registry = context.registry.clone();
|
||||
let mut input_stream = context.input;
|
||||
let stream = async_stream! {
|
||||
while let Some(input) = input_stream.next().await {
|
||||
let mut context = Context::from_raw(&raw_args, ®istry);
|
||||
|
||||
let input_clone = input.clone();
|
||||
let input_stream = if is_expanded_it_usage(&raw_args.call_info.args.head) {
|
||||
InputStream::empty()
|
||||
} else {
|
||||
once(async { Ok(input) }).to_input_stream()
|
||||
};
|
||||
|
||||
let result = run_block(
|
||||
&block,
|
||||
&mut context,
|
||||
input_stream,
|
||||
&scope.clone().set_it(input_clone),
|
||||
).await;
|
||||
|
||||
match result {
|
||||
Ok(mut stream) => {
|
||||
while let Some(result) = stream.next().await {
|
||||
yield Ok(ReturnSuccess::Value(result));
|
||||
}
|
||||
|
||||
let errors = context.get_errors();
|
||||
if let Some(error) = errors.first() {
|
||||
yield Err(error.clone());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
yield Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
|
@ -1,10 +1,16 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct Echo;
|
||||
|
||||
impl PerItemCommand for Echo {
|
||||
#[derive(Deserialize)]
|
||||
pub struct EchoArgs {
|
||||
pub rest: Vec<Value>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Echo {
|
||||
fn name(&self) -> &str {
|
||||
"echo"
|
||||
}
|
||||
@ -19,44 +25,49 @@ impl PerItemCommand for Echo {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
run(call_info, registry, raw_args)
|
||||
args.process(registry, echo)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Put a hello message in the pipeline",
|
||||
example: "echo 'hello'",
|
||||
},
|
||||
Example {
|
||||
description: "Print the value of the special '$nu' variable",
|
||||
example: "echo $nu",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
_raw_args: &RawCommandArgs,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
fn echo(args: EchoArgs, _: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
let mut output = vec![];
|
||||
|
||||
if let Some(ref positional) = call_info.args.positional {
|
||||
for i in positional {
|
||||
match i.as_string() {
|
||||
Ok(s) => {
|
||||
output.push(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(s).into_value(i.tag.clone()),
|
||||
)));
|
||||
}
|
||||
_ => match i {
|
||||
Value {
|
||||
value: UntaggedValue::Table(table),
|
||||
..
|
||||
} => {
|
||||
for value in table {
|
||||
output.push(Ok(ReturnSuccess::Value(value.clone())));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
output.push(Ok(ReturnSuccess::Value(i.clone())));
|
||||
}
|
||||
},
|
||||
for i in args.rest {
|
||||
match i.as_string() {
|
||||
Ok(s) => {
|
||||
output.push(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(s).into_value(i.tag.clone()),
|
||||
)));
|
||||
}
|
||||
_ => match i {
|
||||
Value {
|
||||
value: UntaggedValue::Table(table),
|
||||
..
|
||||
} => {
|
||||
for value in table {
|
||||
output.push(Ok(ReturnSuccess::Value(value.clone())));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
output.push(Ok(ReturnSuccess::Value(i.clone())));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,72 +0,0 @@
|
||||
use crate::commands::PerItemCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_value_ext::ValueExt;
|
||||
|
||||
pub struct Edit;
|
||||
|
||||
impl PerItemCommand for Edit {
|
||||
fn name(&self) -> &str {
|
||||
"edit"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("edit")
|
||||
.required(
|
||||
"Field",
|
||||
SyntaxShape::ColumnPath,
|
||||
"the name of the column to edit",
|
||||
)
|
||||
.required(
|
||||
"Value",
|
||||
SyntaxShape::String,
|
||||
"the new value to give the cell(s)",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Edit an existing column to have a new value."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
_raw_args: &RawCommandArgs,
|
||||
value: Value,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let value_tag = value.tag();
|
||||
let field = call_info.args.expect_nth(0)?.as_column_path()?;
|
||||
let replacement = call_info.args.expect_nth(1)?.tagged_unknown();
|
||||
|
||||
let stream = match value {
|
||||
obj
|
||||
@
|
||||
Value {
|
||||
value: UntaggedValue::Row(_),
|
||||
..
|
||||
} => match obj.replace_data_at_column_path(&field, replacement.item.clone()) {
|
||||
Some(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]),
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"edit could not find place to insert column",
|
||||
"column name",
|
||||
&field.tag,
|
||||
))
|
||||
}
|
||||
},
|
||||
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unrecognized type in stream",
|
||||
"original value",
|
||||
value_tag,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
}
|
@ -1,15 +1,22 @@
|
||||
use crate::commands::PerItemCommand;
|
||||
use crate::commands::UnevaluatedCallInfo;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
CallInfo, CommandAction, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
CommandAction, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct Enter;
|
||||
|
||||
impl PerItemCommand for Enter {
|
||||
#[derive(Deserialize)]
|
||||
pub struct EnterArgs {
|
||||
location: Tagged<PathBuf>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Enter {
|
||||
fn name(&self) -> &str {
|
||||
"enter"
|
||||
}
|
||||
@ -28,130 +35,134 @@ impl PerItemCommand for Enter {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let raw_args = raw_args.clone();
|
||||
match call_info.args.expect_nth(0)? {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Path(location)),
|
||||
tag,
|
||||
..
|
||||
} => {
|
||||
let location_string = location.display().to_string();
|
||||
let location_clone = location_string.clone();
|
||||
let tag_clone = tag.clone();
|
||||
Ok(args.process_raw(registry, enter)?.run())
|
||||
}
|
||||
|
||||
if location_string.starts_with("help") {
|
||||
let spec = location_string.split(':').collect::<Vec<&str>>();
|
||||
|
||||
if spec.len() == 2 {
|
||||
let (_, command) = (spec[0], spec[1]);
|
||||
|
||||
if registry.has(command) {
|
||||
return Ok(vec![Ok(ReturnSuccess::Action(
|
||||
CommandAction::EnterHelpShell(
|
||||
UntaggedValue::string(command).into_value(Tag::unknown()),
|
||||
),
|
||||
))]
|
||||
.into());
|
||||
}
|
||||
}
|
||||
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell(
|
||||
UntaggedValue::nothing().into_value(Tag::unknown()),
|
||||
)))]
|
||||
.into())
|
||||
} else if location.is_dir() {
|
||||
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell(
|
||||
location_clone,
|
||||
)))]
|
||||
.into())
|
||||
} else {
|
||||
let stream = async_stream! {
|
||||
// If it's a file, attempt to open the file as a value and enter it
|
||||
let cwd = raw_args.shell_manager.path();
|
||||
|
||||
let full_path = std::path::PathBuf::from(cwd);
|
||||
|
||||
let (file_extension, contents, contents_tag) =
|
||||
crate::commands::open::fetch(
|
||||
&full_path,
|
||||
&location_clone,
|
||||
tag_clone.span,
|
||||
).await?;
|
||||
|
||||
match contents {
|
||||
UntaggedValue::Primitive(Primitive::String(_)) => {
|
||||
let tagged_contents = contents.into_value(&contents_tag);
|
||||
|
||||
if let Some(extension) = file_extension {
|
||||
let command_name = format!("from-{}", extension);
|
||||
if let Some(converter) =
|
||||
registry.get_command(&command_name)
|
||||
{
|
||||
let new_args = RawCommandArgs {
|
||||
host: raw_args.host,
|
||||
ctrl_c: raw_args.ctrl_c,
|
||||
shell_manager: raw_args.shell_manager,
|
||||
call_info: UnevaluatedCallInfo {
|
||||
args: nu_protocol::hir::Call {
|
||||
head: raw_args.call_info.args.head,
|
||||
positional: None,
|
||||
named: None,
|
||||
span: Span::unknown(),
|
||||
is_last: false,
|
||||
},
|
||||
name_tag: raw_args.call_info.name_tag,
|
||||
scope: raw_args.call_info.scope.clone()
|
||||
},
|
||||
};
|
||||
let mut result = converter.run(
|
||||
new_args.with_input(vec![tagged_contents]),
|
||||
®istry,
|
||||
);
|
||||
let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
|
||||
result.drain_vec().await;
|
||||
for res in result_vec {
|
||||
match res {
|
||||
Ok(ReturnSuccess::Value(Value {
|
||||
value,
|
||||
..
|
||||
})) => {
|
||||
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
|
||||
Value {
|
||||
value,
|
||||
tag: contents_tag.clone(),
|
||||
})));
|
||||
}
|
||||
x => yield x,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
|
||||
}
|
||||
} else {
|
||||
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let tagged_contents = contents.into_value(contents_tag);
|
||||
|
||||
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
}
|
||||
x => Ok(
|
||||
vec![Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
|
||||
x.clone(),
|
||||
)))]
|
||||
.into(),
|
||||
),
|
||||
}
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Enter a path as a new shell",
|
||||
example: "enter ../projectB",
|
||||
},
|
||||
Example {
|
||||
description: "Enter a file as a new shell",
|
||||
example: "enter package.json",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn enter(
|
||||
EnterArgs { location }: EnterArgs,
|
||||
RunnableContext {
|
||||
registry,
|
||||
name: tag,
|
||||
..
|
||||
}: RunnableContext,
|
||||
raw_args: RawCommandArgs,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let location_string = location.display().to_string();
|
||||
let location_clone = location_string.clone();
|
||||
|
||||
if location_string.starts_with("help") {
|
||||
let spec = location_string.split(':').collect::<Vec<&str>>();
|
||||
|
||||
if spec.len() == 2 {
|
||||
let (_, command) = (spec[0], spec[1]);
|
||||
|
||||
if registry.has(command) {
|
||||
return Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell(
|
||||
UntaggedValue::string(command).into_value(Tag::unknown()),
|
||||
)))]
|
||||
.into());
|
||||
}
|
||||
}
|
||||
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell(
|
||||
UntaggedValue::nothing().into_value(Tag::unknown()),
|
||||
)))]
|
||||
.into())
|
||||
} else if location.is_dir() {
|
||||
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell(
|
||||
location_clone,
|
||||
)))]
|
||||
.into())
|
||||
} else {
|
||||
let stream = async_stream! {
|
||||
// If it's a file, attempt to open the file as a value and enter it
|
||||
let cwd = raw_args.shell_manager.path();
|
||||
|
||||
let full_path = std::path::PathBuf::from(cwd);
|
||||
|
||||
let (file_extension, contents, contents_tag) =
|
||||
crate::commands::open::fetch(
|
||||
&full_path,
|
||||
&PathBuf::from(location_clone),
|
||||
tag.span,
|
||||
).await?;
|
||||
|
||||
match contents {
|
||||
UntaggedValue::Primitive(Primitive::String(_)) => {
|
||||
let tagged_contents = contents.into_value(&contents_tag);
|
||||
|
||||
if let Some(extension) = file_extension {
|
||||
let command_name = format!("from {}", extension);
|
||||
if let Some(converter) =
|
||||
registry.get_command(&command_name)
|
||||
{
|
||||
let new_args = RawCommandArgs {
|
||||
host: raw_args.host,
|
||||
ctrl_c: raw_args.ctrl_c,
|
||||
shell_manager: raw_args.shell_manager,
|
||||
call_info: UnevaluatedCallInfo {
|
||||
args: nu_protocol::hir::Call {
|
||||
head: raw_args.call_info.args.head,
|
||||
positional: None,
|
||||
named: None,
|
||||
span: Span::unknown(),
|
||||
is_last: false,
|
||||
},
|
||||
name_tag: raw_args.call_info.name_tag,
|
||||
scope: raw_args.call_info.scope.clone()
|
||||
},
|
||||
};
|
||||
let mut result = converter.run(
|
||||
new_args.with_input(vec![tagged_contents]),
|
||||
®istry,
|
||||
);
|
||||
let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
|
||||
result.drain_vec().await;
|
||||
for res in result_vec {
|
||||
match res {
|
||||
Ok(ReturnSuccess::Value(Value {
|
||||
value,
|
||||
..
|
||||
})) => {
|
||||
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
|
||||
Value {
|
||||
value,
|
||||
tag: contents_tag.clone(),
|
||||
})));
|
||||
}
|
||||
x => yield x,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
|
||||
}
|
||||
} else {
|
||||
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let tagged_contents = contents.into_value(contents_tag);
|
||||
|
||||
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,19 @@ impl WholeStreamCommand for Exit {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
exit(args, registry)
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Exit the current shell",
|
||||
example: "exit",
|
||||
},
|
||||
Example {
|
||||
description: "Exit all shells (exiting Nu)",
|
||||
example: "exit --now",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exit(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
|
@ -36,6 +36,19 @@ impl WholeStreamCommand for First {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, first)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Return the first item of a list/table",
|
||||
example: "echo [1 2 3] | first",
|
||||
},
|
||||
Example {
|
||||
description: "Return the first 2 items of a list/table",
|
||||
example: "echo [1 2 3] | first 2",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn first(
|
||||
|
@ -1,17 +1,20 @@
|
||||
use crate::commands::PerItemCommand;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
CallInfo, ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use nu_value_ext::{as_column_path, get_data_by_column_path};
|
||||
use std::borrow::Borrow;
|
||||
|
||||
pub struct Format;
|
||||
|
||||
impl PerItemCommand for Format {
|
||||
#[derive(Deserialize)]
|
||||
pub struct FormatArgs {
|
||||
pattern: Tagged<String>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Format {
|
||||
fn name(&self) -> &str {
|
||||
"format"
|
||||
}
|
||||
@ -19,7 +22,7 @@ impl PerItemCommand for Format {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("format").required(
|
||||
"pattern",
|
||||
SyntaxShape::Any,
|
||||
SyntaxShape::String,
|
||||
"the pattern to output. Eg) \"{foo}: {bar}\"",
|
||||
)
|
||||
}
|
||||
@ -30,61 +33,74 @@ impl PerItemCommand for Format {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
_raw_args: &RawCommandArgs,
|
||||
value: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
//let value_tag = value.tag();
|
||||
let pattern = call_info.args.expect_nth(0)?;
|
||||
let pattern_tag = pattern.tag.clone();
|
||||
let pattern = pattern.as_string()?;
|
||||
args.process(registry, format_command)?.run()
|
||||
}
|
||||
|
||||
let format_pattern = format(&pattern);
|
||||
let commands = format_pattern;
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Print filenames with their sizes",
|
||||
example: "ls | format '{name}: {size}'",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
let output = match value {
|
||||
value
|
||||
@
|
||||
Value {
|
||||
value: UntaggedValue::Row(_),
|
||||
..
|
||||
} => {
|
||||
let mut output = String::new();
|
||||
fn format_command(
|
||||
FormatArgs { pattern }: FormatArgs,
|
||||
RunnableContext { input, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let pattern_tag = pattern.tag.clone();
|
||||
|
||||
for command in &commands {
|
||||
match command {
|
||||
FormatCommand::Text(s) => {
|
||||
output.push_str(&s);
|
||||
}
|
||||
FormatCommand::Column(c) => {
|
||||
let key = to_column_path(&c, &pattern_tag)?;
|
||||
let format_pattern = format(&pattern);
|
||||
let commands = format_pattern;
|
||||
let mut input = input;
|
||||
|
||||
let fetcher = get_data_by_column_path(
|
||||
&value,
|
||||
&key,
|
||||
Box::new(move |(_, _, error)| error),
|
||||
);
|
||||
let stream = async_stream! {
|
||||
while let Some(value) = input.next().await {
|
||||
match value {
|
||||
value
|
||||
@
|
||||
Value {
|
||||
value: UntaggedValue::Row(_),
|
||||
..
|
||||
} => {
|
||||
let mut output = String::new();
|
||||
|
||||
if let Ok(c) = fetcher {
|
||||
output
|
||||
.push_str(&value::format_leaf(c.borrow()).plain_string(100_000))
|
||||
for command in &commands {
|
||||
match command {
|
||||
FormatCommand::Text(s) => {
|
||||
output.push_str(&s);
|
||||
}
|
||||
FormatCommand::Column(c) => {
|
||||
let key = to_column_path(&c, &pattern_tag)?;
|
||||
|
||||
let fetcher = get_data_by_column_path(
|
||||
&value,
|
||||
&key,
|
||||
Box::new(move |(_, _, error)| error),
|
||||
);
|
||||
|
||||
if let Ok(c) = fetcher {
|
||||
output
|
||||
.push_str(&value::format_leaf(c.borrow()).plain_string(100_000))
|
||||
}
|
||||
// That column doesn't match, so don't emit anything
|
||||
}
|
||||
// That column doesn't match, so don't emit anything
|
||||
}
|
||||
}
|
||||
|
||||
yield ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_untagged_value())
|
||||
}
|
||||
_ => yield ReturnSuccess::value(
|
||||
UntaggedValue::string(String::new()).into_untagged_value()),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
output
|
||||
}
|
||||
_ => String::new(),
|
||||
};
|
||||
|
||||
Ok(futures::stream::iter(vec![ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_untagged_value(),
|
||||
)])
|
||||
.to_output_stream())
|
||||
}
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
28
crates/nu-cli/src/commands/from.rs
Normal file
28
crates/nu-cli/src/commands/from.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::Signature;
|
||||
|
||||
pub struct From;
|
||||
|
||||
impl WholeStreamCommand for From {
|
||||
fn name(&self) -> &str {
|
||||
"from"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Parse content (string or binary) as a table (input format based on subcommand, like csv, ini, json, toml)"
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
Ok(crate::commands::help::get_help(&*self, registry).into())
|
||||
}
|
||||
}
|
@ -10,15 +10,15 @@ pub struct FromBSON;
|
||||
|
||||
impl WholeStreamCommand for FromBSON {
|
||||
fn name(&self) -> &str {
|
||||
"from-bson"
|
||||
"from bson"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-bson")
|
||||
Signature::build("from bson")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Parse text as .bson and create table."
|
||||
"Parse binary as .bson and create table."
|
||||
}
|
||||
|
||||
fn run(
|
||||
@ -28,6 +28,13 @@ impl WholeStreamCommand for FromBSON {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
from_bson(args, registry)
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Convert bson data to a table",
|
||||
example: "open file.bin | from bson",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn bson_array(input: &[Bson], tag: Tag) -> Result<Vec<Value>, ShellError> {
|
||||
|
@ -14,11 +14,11 @@ pub struct FromCSVArgs {
|
||||
|
||||
impl WholeStreamCommand for FromCSV {
|
||||
fn name(&self) -> &str {
|
||||
"from-csv"
|
||||
"from csv"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-csv")
|
||||
Signature::build("from csv")
|
||||
.named(
|
||||
"separator",
|
||||
SyntaxShape::String,
|
||||
@ -43,6 +43,23 @@ impl WholeStreamCommand for FromCSV {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, from_csv)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Convert comma-separated data to a table",
|
||||
example: "open data.txt | from csv",
|
||||
},
|
||||
Example {
|
||||
description: "Convert comma-separated data to a table, ignoring headers",
|
||||
example: "open data.txt | from csv --headerless",
|
||||
},
|
||||
Example {
|
||||
description: "Convert semicolon-separated data to a table",
|
||||
example: "open data.txt | from csv --separator ';'",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn from_csv(
|
||||
|
122
crates/nu-cli/src/commands/from_eml.rs
Normal file
122
crates/nu-cli/src/commands/from_eml.rs
Normal file
@ -0,0 +1,122 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use ::eml_parser::eml::*;
|
||||
use ::eml_parser::EmlParser;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct FromEML;
|
||||
|
||||
const DEFAULT_BODY_PREVIEW: usize = 50;
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct FromEMLArgs {
|
||||
#[serde(rename(deserialize = "preview-body"))]
|
||||
preview_body: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for FromEML {
|
||||
fn name(&self) -> &str {
|
||||
"from eml"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from eml").named(
|
||||
"preview-body",
|
||||
SyntaxShape::Int,
|
||||
"How many bytes of the body to preview",
|
||||
Some('b'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Parse text as .eml and create table."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, from_eml)?.run()
|
||||
}
|
||||
}
|
||||
|
||||
fn emailaddress_to_value(tag: &Tag, email_address: &EmailAddress) -> TaggedDictBuilder {
|
||||
let mut dict = TaggedDictBuilder::with_capacity(tag, 2);
|
||||
let (n, a) = match email_address {
|
||||
EmailAddress::AddressOnly { address } => {
|
||||
(UntaggedValue::nothing(), UntaggedValue::string(address))
|
||||
}
|
||||
EmailAddress::NameAndEmailAddress { name, address } => {
|
||||
(UntaggedValue::string(name), UntaggedValue::string(address))
|
||||
}
|
||||
};
|
||||
|
||||
dict.insert_untagged("Name", n);
|
||||
dict.insert_untagged("Address", a);
|
||||
|
||||
dict
|
||||
}
|
||||
|
||||
fn headerfieldvalue_to_value(tag: &Tag, value: &HeaderFieldValue) -> UntaggedValue {
|
||||
use HeaderFieldValue::*;
|
||||
|
||||
match value {
|
||||
SingleEmailAddress(address) => emailaddress_to_value(tag, address).into_untagged_value(),
|
||||
MultipleEmailAddresses(addresses) => UntaggedValue::Table(
|
||||
addresses
|
||||
.iter()
|
||||
.map(|a| emailaddress_to_value(tag, a).into_value())
|
||||
.collect(),
|
||||
),
|
||||
Unstructured(s) => UntaggedValue::string(s),
|
||||
Empty => UntaggedValue::nothing(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_eml(
|
||||
eml_args: FromEMLArgs,
|
||||
runnable_context: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let input = runnable_context.input;
|
||||
let tag = runnable_context.name;
|
||||
|
||||
let stream = async_stream! {
|
||||
let value = input.collect_string(tag.clone()).await?;
|
||||
|
||||
let body_preview = eml_args.preview_body.map(|b| b.item).unwrap_or(DEFAULT_BODY_PREVIEW);
|
||||
|
||||
let eml = EmlParser::from_string(value.item)
|
||||
.with_body_preview(body_preview)
|
||||
.parse()
|
||||
.map_err(|_| ShellError::labeled_error("Could not parse .eml file", "could not parse .eml file", &tag))?;
|
||||
|
||||
let mut dict = TaggedDictBuilder::new(&tag);
|
||||
|
||||
if let Some(subj) = eml.subject {
|
||||
dict.insert_untagged("Subject", UntaggedValue::string(subj));
|
||||
}
|
||||
|
||||
if let Some(from) = eml.from {
|
||||
dict.insert_untagged("From", headerfieldvalue_to_value(&tag, &from));
|
||||
}
|
||||
|
||||
if let Some(to) = eml.to {
|
||||
dict.insert_untagged("To", headerfieldvalue_to_value(&tag, &to));
|
||||
}
|
||||
|
||||
for HeaderField{ name, value } in eml.headers.iter() {
|
||||
dict.insert_untagged(name, headerfieldvalue_to_value(&tag, &value));
|
||||
}
|
||||
|
||||
if let Some(body) = eml.body {
|
||||
dict.insert_untagged("Body", UntaggedValue::string(body));
|
||||
}
|
||||
|
||||
yield ReturnSuccess::value(dict.into_value());
|
||||
};
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
@ -11,11 +11,11 @@ pub struct FromIcs;
|
||||
|
||||
impl WholeStreamCommand for FromIcs {
|
||||
fn name(&self) -> &str {
|
||||
"from-ics"
|
||||
"from ics"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-ics")
|
||||
Signature::build("from ics")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -8,11 +8,11 @@ pub struct FromINI;
|
||||
|
||||
impl WholeStreamCommand for FromINI {
|
||||
fn name(&self) -> &str {
|
||||
"from-ini"
|
||||
"from ini"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-ini")
|
||||
Signature::build("from ini")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -12,11 +12,11 @@ pub struct FromJSONArgs {
|
||||
|
||||
impl WholeStreamCommand for FromJSON {
|
||||
fn name(&self) -> &str {
|
||||
"from-json"
|
||||
"from json"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-json").switch(
|
||||
Signature::build("from json").switch(
|
||||
"objects",
|
||||
"treat each line as a separate value",
|
||||
Some('o'),
|
||||
|
@ -15,11 +15,11 @@ pub struct FromODSArgs {
|
||||
|
||||
impl WholeStreamCommand for FromODS {
|
||||
fn name(&self) -> &str {
|
||||
"from-ods"
|
||||
"from ods"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-ods").switch(
|
||||
Signature::build("from ods").switch(
|
||||
"headerless",
|
||||
"don't treat the first row as column names",
|
||||
None,
|
||||
|
@ -10,11 +10,11 @@ pub struct FromSQLite;
|
||||
|
||||
impl WholeStreamCommand for FromSQLite {
|
||||
fn name(&self) -> &str {
|
||||
"from-sqlite"
|
||||
"from sqlite"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-sqlite")
|
||||
Signature::build("from sqlite")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -34,11 +34,11 @@ pub struct FromDB;
|
||||
|
||||
impl WholeStreamCommand for FromDB {
|
||||
fn name(&self) -> &str {
|
||||
"from-db"
|
||||
"from db"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-db")
|
||||
Signature::build("from db")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -17,7 +17,7 @@ pub struct FromSSVArgs {
|
||||
minimum_spaces: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
const STRING_REPRESENTATION: &str = "from-ssv";
|
||||
const STRING_REPRESENTATION: &str = "from ssv";
|
||||
const DEFAULT_MINIMUM_SPACES: usize = 2;
|
||||
|
||||
impl WholeStreamCommand for FromSSV {
|
||||
|
@ -7,11 +7,11 @@ pub struct FromTOML;
|
||||
|
||||
impl WholeStreamCommand for FromTOML {
|
||||
fn name(&self) -> &str {
|
||||
"from-toml"
|
||||
"from toml"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-toml")
|
||||
Signature::build("from toml")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -13,11 +13,11 @@ pub struct FromTSVArgs {
|
||||
|
||||
impl WholeStreamCommand for FromTSV {
|
||||
fn name(&self) -> &str {
|
||||
"from-tsv"
|
||||
"from tsv"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-tsv").switch(
|
||||
Signature::build("from tsv").switch(
|
||||
"headerless",
|
||||
"don't treat the first row as column names",
|
||||
None,
|
||||
|
@ -7,11 +7,11 @@ pub struct FromURL;
|
||||
|
||||
impl WholeStreamCommand for FromURL {
|
||||
fn name(&self) -> &str {
|
||||
"from-url"
|
||||
"from url"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-url")
|
||||
Signature::build("from url")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -11,11 +11,11 @@ pub struct FromVcf;
|
||||
|
||||
impl WholeStreamCommand for FromVcf {
|
||||
fn name(&self) -> &str {
|
||||
"from-vcf"
|
||||
"from vcf"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-vcf")
|
||||
Signature::build("from vcf")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -15,11 +15,11 @@ pub struct FromXLSXArgs {
|
||||
|
||||
impl WholeStreamCommand for FromXLSX {
|
||||
fn name(&self) -> &str {
|
||||
"from-xlsx"
|
||||
"from xlsx"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-xlsx").switch(
|
||||
Signature::build("from xlsx").switch(
|
||||
"headerless",
|
||||
"don't treat the first row as column names",
|
||||
None,
|
||||
|
@ -7,11 +7,11 @@ pub struct FromXML;
|
||||
|
||||
impl WholeStreamCommand for FromXML {
|
||||
fn name(&self) -> &str {
|
||||
"from-xml"
|
||||
"from xml"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-xml")
|
||||
Signature::build("from xml")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -7,11 +7,11 @@ pub struct FromYAML;
|
||||
|
||||
impl WholeStreamCommand for FromYAML {
|
||||
fn name(&self) -> &str {
|
||||
"from-yaml"
|
||||
"from yaml"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-yaml")
|
||||
Signature::build("from yaml")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -31,11 +31,11 @@ pub struct FromYML;
|
||||
|
||||
impl WholeStreamCommand for FromYML {
|
||||
fn name(&self) -> &str {
|
||||
"from-yml"
|
||||
"from yml"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from-yml")
|
||||
Signature::build("from yml")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -40,6 +40,19 @@ impl WholeStreamCommand for Get {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, get)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Extract the name of files as a list",
|
||||
example: "ls | get name",
|
||||
},
|
||||
Example {
|
||||
description: "Extract the cpu list from the sys information",
|
||||
example: "sys | get cpu",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_column_path(path: &ColumnPath, obj: &Value) -> Result<Value, ShellError> {
|
||||
|
@ -1,15 +1,16 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, Value};
|
||||
use nu_source::Tagged;
|
||||
use nu_value_ext::get_data_by_key;
|
||||
|
||||
pub struct GroupBy;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GroupByArgs {
|
||||
column_name: Tagged<String>,
|
||||
date: Tagged<bool>,
|
||||
format: Option<Tagged<String>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for GroupBy {
|
||||
@ -18,11 +19,19 @@ impl WholeStreamCommand for GroupBy {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("group-by").required(
|
||||
"column_name",
|
||||
SyntaxShape::String,
|
||||
"the name of the column to group by",
|
||||
)
|
||||
Signature::build("group-by")
|
||||
.required(
|
||||
"column_name",
|
||||
SyntaxShape::String,
|
||||
"the name of the column to group by",
|
||||
)
|
||||
.named(
|
||||
"format",
|
||||
SyntaxShape::String,
|
||||
"Specify date and time formatting",
|
||||
Some('f'),
|
||||
)
|
||||
.switch("date", "by date", Some('d'))
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -36,10 +45,26 @@ impl WholeStreamCommand for GroupBy {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, group_by)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Group files by type",
|
||||
example: "ls | group-by type",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
enum Grouper {
|
||||
Default,
|
||||
ByDate(Option<String>),
|
||||
}
|
||||
|
||||
pub fn group_by(
|
||||
GroupByArgs { column_name }: GroupByArgs,
|
||||
GroupByArgs {
|
||||
column_name,
|
||||
date,
|
||||
format,
|
||||
}: GroupByArgs,
|
||||
RunnableContext { input, name, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let stream = async_stream! {
|
||||
@ -52,9 +77,38 @@ pub fn group_by(
|
||||
column_name.span()
|
||||
))
|
||||
} else {
|
||||
match group(&column_name, values, name) {
|
||||
Ok(grouped) => yield ReturnSuccess::value(grouped),
|
||||
Err(err) => yield Err(err)
|
||||
|
||||
let grouper = if let Tagged { item: true, tag } = date {
|
||||
if let Some(Tagged { item: fmt, tag }) = format {
|
||||
Grouper::ByDate(Some(fmt))
|
||||
} else {
|
||||
Grouper::ByDate(None)
|
||||
}
|
||||
} else {
|
||||
Grouper::Default
|
||||
};
|
||||
|
||||
match grouper {
|
||||
Grouper::Default => {
|
||||
match crate::utils::data::group(column_name, &values, None, &name) {
|
||||
Ok(grouped) => yield ReturnSuccess::value(grouped),
|
||||
Err(err) => yield Err(err),
|
||||
}
|
||||
}
|
||||
Grouper::ByDate(None) => {
|
||||
match crate::utils::data::group(column_name, &values, Some(Box::new(|row: &Value| row.format("%Y-%b-%d"))), &name) {
|
||||
Ok(grouped) => yield ReturnSuccess::value(grouped),
|
||||
Err(err) => yield Err(err),
|
||||
}
|
||||
}
|
||||
Grouper::ByDate(Some(fmt)) => {
|
||||
match crate::utils::data::group(column_name, &values, Some(Box::new(move |row: &Value| {
|
||||
row.format(&fmt)
|
||||
})), &name) {
|
||||
Ok(grouped) => yield ReturnSuccess::value(grouped),
|
||||
Err(err) => yield Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -67,50 +121,7 @@ pub fn group(
|
||||
values: Vec<Value>,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
|
||||
let mut groups: indexmap::IndexMap<String, Vec<Value>> = indexmap::IndexMap::new();
|
||||
|
||||
for value in values {
|
||||
let group_key = get_data_by_key(&value, column_name.borrow_spanned());
|
||||
|
||||
if let Some(group_key) = group_key {
|
||||
let group_key = group_key.as_string()?.to_string();
|
||||
let group = groups.entry(group_key).or_insert(vec![]);
|
||||
group.push(value);
|
||||
} else {
|
||||
let possibilities = value.data_descriptors();
|
||||
|
||||
let mut possible_matches: Vec<_> = possibilities
|
||||
.iter()
|
||||
.map(|x| (natural::distance::levenshtein_distance(x, column_name), x))
|
||||
.collect();
|
||||
|
||||
possible_matches.sort();
|
||||
|
||||
if !possible_matches.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
format!("did you mean '{}'?", possible_matches[0].1),
|
||||
column_name.tag(),
|
||||
));
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
"row does not contain this column",
|
||||
column_name.tag(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut out = TaggedDictBuilder::new(&tag);
|
||||
|
||||
for (k, v) in groups.iter() {
|
||||
out.insert_untagged(k, UntaggedValue::table(v));
|
||||
}
|
||||
|
||||
Ok(out.into_value())
|
||||
crate::utils::data::group(column_name.clone(), &values, None, tag)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -15,12 +15,15 @@ impl WholeStreamCommand for Headers {
|
||||
fn name(&self) -> &str {
|
||||
"headers"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("headers")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Use the first row of the table as column names"
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
@ -28,6 +31,13 @@ impl WholeStreamCommand for Headers {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, headers)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Create headers for a raw string",
|
||||
example: "echo \"a b c|1 2 3\" | split-row \"|\" | split-column \" \" | headers",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn headers(
|
||||
|
@ -1,24 +1,29 @@
|
||||
use crate::commands::PerItemCommand;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::data::command_dict;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
CallInfo, NamedType, PositionalType, Primitive, ReturnSuccess, Signature, SyntaxShape,
|
||||
TaggedDictBuilder, UntaggedValue, Value,
|
||||
NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder,
|
||||
UntaggedValue,
|
||||
};
|
||||
use nu_source::SpannedItem;
|
||||
use nu_source::{SpannedItem, Tagged};
|
||||
use nu_value_ext::get_data_by_key;
|
||||
|
||||
pub struct Help;
|
||||
|
||||
impl PerItemCommand for Help {
|
||||
#[derive(Deserialize)]
|
||||
pub struct HelpArgs {
|
||||
rest: Vec<Tagged<String>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Help {
|
||||
fn name(&self) -> &str {
|
||||
"help"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("help").rest(SyntaxShape::Any, "the name of command(s) to get help on")
|
||||
Signature::build("help").rest(SyntaxShape::String, "the name of command to get help on")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -27,67 +32,75 @@ impl PerItemCommand for Help {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
_raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let tag = &call_info.name_tag;
|
||||
args.process(registry, help)?.run()
|
||||
}
|
||||
}
|
||||
|
||||
match call_info.args.nth(0) {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(document)),
|
||||
tag,
|
||||
}) => {
|
||||
let mut help = VecDeque::new();
|
||||
if document == "commands" {
|
||||
let mut sorted_names = registry.names();
|
||||
sorted_names.sort();
|
||||
for cmd in sorted_names {
|
||||
let mut short_desc = TaggedDictBuilder::new(tag.clone());
|
||||
let value = command_dict(
|
||||
registry.get_command(&cmd).ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
format!("Could not load {}", cmd),
|
||||
"could not load command",
|
||||
tag,
|
||||
)
|
||||
})?,
|
||||
tag.clone(),
|
||||
);
|
||||
|
||||
short_desc.insert_untagged("name", cmd);
|
||||
short_desc.insert_untagged(
|
||||
"description",
|
||||
get_data_by_key(&value, "usage".spanned_unknown())
|
||||
.ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
"Expected a usage key",
|
||||
"expected a 'usage' key",
|
||||
&value.tag,
|
||||
)
|
||||
})?
|
||||
.as_string()?,
|
||||
);
|
||||
|
||||
help.push_back(ReturnSuccess::value(short_desc.into_value()));
|
||||
}
|
||||
} else if let Some(command) = registry.get_command(document) {
|
||||
return Ok(
|
||||
get_help(&command.name(), &command.usage(), command.signature()).into(),
|
||||
);
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Can't find command (use 'help commands' for full list)",
|
||||
"can't find command",
|
||||
tag,
|
||||
));
|
||||
fn help(
|
||||
HelpArgs { rest }: HelpArgs,
|
||||
RunnableContext { registry, name, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
if let Some(document) = rest.get(0) {
|
||||
let mut help = VecDeque::new();
|
||||
if document.item == "commands" {
|
||||
let mut sorted_names = registry.names();
|
||||
sorted_names.sort();
|
||||
for cmd in sorted_names {
|
||||
// If it's a subcommand, don't list it during the commands list
|
||||
if cmd.contains(' ') {
|
||||
continue;
|
||||
}
|
||||
let help = futures::stream::iter(help);
|
||||
Ok(help.to_output_stream())
|
||||
let mut short_desc = TaggedDictBuilder::new(name.clone());
|
||||
let document_tag = document.tag.clone();
|
||||
let value = command_dict(
|
||||
registry.get_command(&cmd).ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
format!("Could not load {}", cmd),
|
||||
"could not load command",
|
||||
document_tag,
|
||||
)
|
||||
})?,
|
||||
name.clone(),
|
||||
);
|
||||
|
||||
short_desc.insert_untagged("name", cmd);
|
||||
short_desc.insert_untagged(
|
||||
"description",
|
||||
get_data_by_key(&value, "usage".spanned_unknown())
|
||||
.ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
"Expected a usage key",
|
||||
"expected a 'usage' key",
|
||||
&value.tag,
|
||||
)
|
||||
})?
|
||||
.as_string()?,
|
||||
);
|
||||
|
||||
help.push_back(ReturnSuccess::value(short_desc.into_value()));
|
||||
}
|
||||
_ => {
|
||||
let msg = r#"Welcome to Nushell.
|
||||
} else if rest.len() == 2 {
|
||||
// Check for a subcommand
|
||||
let command_name = format!("{} {}", rest[0].item, rest[1].item);
|
||||
if let Some(command) = registry.get_command(&command_name) {
|
||||
return Ok(get_help(command.stream_command(), ®istry).into());
|
||||
}
|
||||
} else if let Some(command) = registry.get_command(&document.item) {
|
||||
return Ok(get_help(command.stream_command(), ®istry).into());
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Can't find command (use 'help commands' for full list)",
|
||||
"can't find command",
|
||||
document.tag.span,
|
||||
));
|
||||
}
|
||||
let help = futures::stream::iter(help);
|
||||
Ok(help.to_output_stream())
|
||||
} else {
|
||||
let msg = r#"Welcome to Nushell.
|
||||
|
||||
Here are some tips to help you get started.
|
||||
* help commands - list all available commands
|
||||
@ -109,28 +122,35 @@ Get the processes on your system actively using CPU:
|
||||
|
||||
You can also learn more at https://www.nushell.sh/book/"#;
|
||||
|
||||
let output_stream = futures::stream::iter(vec![ReturnSuccess::value(
|
||||
UntaggedValue::string(msg).into_value(tag),
|
||||
)]);
|
||||
let output_stream = futures::stream::iter(vec![ReturnSuccess::value(
|
||||
UntaggedValue::string(msg).into_value(name),
|
||||
)]);
|
||||
|
||||
Ok(output_stream.to_output_stream())
|
||||
}
|
||||
}
|
||||
Ok(output_stream.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub(crate) fn get_help(
|
||||
cmd_name: &str,
|
||||
cmd_usage: &str,
|
||||
cmd_sig: Signature,
|
||||
cmd: &dyn WholeStreamCommand,
|
||||
registry: &CommandRegistry,
|
||||
) -> impl Into<OutputStream> {
|
||||
let cmd_name = cmd.name();
|
||||
let signature = cmd.signature();
|
||||
let mut help = VecDeque::new();
|
||||
let mut long_desc = String::new();
|
||||
|
||||
long_desc.push_str(&cmd_usage);
|
||||
long_desc.push_str(&cmd.usage());
|
||||
long_desc.push_str("\n");
|
||||
|
||||
let signature = cmd_sig;
|
||||
let mut subcommands = String::new();
|
||||
for name in registry.names() {
|
||||
if name.starts_with(&format!("{} ", cmd_name)) {
|
||||
let subcommand = registry.get_command(&name).expect("This shouldn't happen");
|
||||
|
||||
subcommands.push_str(&format!(" {} - {}\n", name, subcommand.usage()));
|
||||
}
|
||||
}
|
||||
|
||||
let mut one_liner = String::new();
|
||||
one_liner.push_str(&signature.name);
|
||||
@ -151,14 +171,23 @@ pub(crate) fn get_help(
|
||||
one_liner.push_str(" ...args");
|
||||
}
|
||||
|
||||
if !subcommands.is_empty() {
|
||||
one_liner.push_str("<subcommand> ");
|
||||
}
|
||||
|
||||
if !signature.named.is_empty() {
|
||||
one_liner.push_str("{flags} ");
|
||||
}
|
||||
|
||||
long_desc.push_str(&format!("\nUsage:\n > {}\n", one_liner));
|
||||
|
||||
if !subcommands.is_empty() {
|
||||
long_desc.push_str("\nSubcommands:\n");
|
||||
long_desc.push_str(&subcommands);
|
||||
}
|
||||
|
||||
if !signature.positional.is_empty() || signature.rest_positional.is_some() {
|
||||
long_desc.push_str("\nparameters:\n");
|
||||
long_desc.push_str("\nParameters:\n");
|
||||
for positional in signature.positional {
|
||||
match positional.0 {
|
||||
PositionalType::Mandatory(name, _m) => {
|
||||
@ -175,7 +204,7 @@ pub(crate) fn get_help(
|
||||
}
|
||||
}
|
||||
if !signature.named.is_empty() {
|
||||
long_desc.push_str("\nflags:\n");
|
||||
long_desc.push_str("\nFlags:\n");
|
||||
for (flag, ty) in signature.named {
|
||||
let msg = match ty.0 {
|
||||
NamedType::Switch(s) => {
|
||||
@ -241,6 +270,21 @@ pub(crate) fn get_help(
|
||||
}
|
||||
}
|
||||
|
||||
let examples = cmd.examples();
|
||||
if !examples.is_empty() {
|
||||
long_desc.push_str("\nExamples:");
|
||||
}
|
||||
for example in examples {
|
||||
long_desc.push_str("\n");
|
||||
long_desc.push_str(" ");
|
||||
long_desc.push_str(example.description);
|
||||
let colored_example =
|
||||
crate::shell::helper::Painter::paint_string(example.example, registry);
|
||||
long_desc.push_str(&format!("\n > {}\n", colored_example));
|
||||
}
|
||||
|
||||
long_desc.push_str("\n");
|
||||
|
||||
help.push_back(ReturnSuccess::value(
|
||||
UntaggedValue::string(long_desc).into_value(Tag::from((0, cmd_name.len(), None))),
|
||||
));
|
||||
|
@ -46,6 +46,24 @@ impl WholeStreamCommand for Histogram {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, histogram)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Get a histogram for the types of files",
|
||||
example: "ls | histogram type",
|
||||
},
|
||||
Example {
|
||||
description:
|
||||
"Get a histogram for the types of files, with frequency column named count",
|
||||
example: "ls | histogram type count",
|
||||
},
|
||||
Example {
|
||||
description: "Get a histogram for a list of numbers",
|
||||
example: "echo [1 2 3 1 2 3 1 1 1 1 3 2 1 1 3] | wrap | histogram Column",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn histogram(
|
||||
|
@ -1,14 +1,17 @@
|
||||
use crate::cli::History as HistoryFile;
|
||||
use crate::commands::PerItemCommand;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, ReturnSuccess, Signature, UntaggedValue, Value};
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
pub struct History;
|
||||
|
||||
impl PerItemCommand for History {
|
||||
#[derive(Deserialize)]
|
||||
pub struct HistoryArgs {}
|
||||
|
||||
impl WholeStreamCommand for History {
|
||||
fn name(&self) -> &str {
|
||||
"history"
|
||||
}
|
||||
@ -23,27 +26,30 @@ impl PerItemCommand for History {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
_raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let tag = call_info.name_tag.clone();
|
||||
|
||||
let stream = async_stream! {
|
||||
let history_path = HistoryFile::path();
|
||||
let file = File::open(history_path);
|
||||
if let Ok(file) = file {
|
||||
let reader = BufReader::new(file);
|
||||
for line in reader.lines() {
|
||||
if let Ok(line) = line {
|
||||
yield ReturnSuccess::value(UntaggedValue::string(line).into_value(tag.clone()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
yield Err(ShellError::labeled_error("Could not open history", "history file could not be opened", tag.clone()));
|
||||
}
|
||||
};
|
||||
Ok(stream.to_output_stream())
|
||||
args.process(registry, history)?.run()
|
||||
}
|
||||
}
|
||||
|
||||
fn history(
|
||||
_: HistoryArgs,
|
||||
RunnableContext { name: tag, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let stream = async_stream! {
|
||||
let history_path = HistoryFile::path();
|
||||
let file = File::open(history_path);
|
||||
if let Ok(file) = file {
|
||||
let reader = BufReader::new(file);
|
||||
for line in reader.lines() {
|
||||
if let Ok(line) = line {
|
||||
yield ReturnSuccess::value(UntaggedValue::string(line).into_value(tag.clone()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
yield Err(ShellError::labeled_error("Could not open history", "history file could not be opened", tag.clone()));
|
||||
}
|
||||
};
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
|
@ -1,13 +1,19 @@
|
||||
use crate::commands::PerItemCommand;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_value_ext::ValueExt;
|
||||
|
||||
pub struct Insert;
|
||||
|
||||
impl PerItemCommand for Insert {
|
||||
#[derive(Deserialize)]
|
||||
pub struct InsertArgs {
|
||||
column: ColumnPath,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Insert {
|
||||
fn name(&self) -> &str {
|
||||
"insert"
|
||||
}
|
||||
@ -27,40 +33,45 @@ impl PerItemCommand for Insert {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Edit an existing column to have a new value."
|
||||
"Insert a new column with a given value."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
_raw_args: &RawCommandArgs,
|
||||
value: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let value_tag = value.tag();
|
||||
let field = call_info.args.expect_nth(0)?.as_column_path()?;
|
||||
let replacement = call_info.args.expect_nth(1)?.tagged_unknown();
|
||||
|
||||
let stream = match value {
|
||||
obj
|
||||
@
|
||||
Value {
|
||||
value: UntaggedValue::Row(_),
|
||||
..
|
||||
} => match obj.insert_data_at_column_path(&field, replacement.item.clone()) {
|
||||
Ok(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]),
|
||||
Err(err) => return Err(err),
|
||||
},
|
||||
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unrecognized type in stream",
|
||||
"original value",
|
||||
value_tag,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
args.process(registry, insert)?.run()
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(
|
||||
InsertArgs { column, value }: InsertArgs,
|
||||
RunnableContext { input, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let mut input = input;
|
||||
|
||||
let stream = async_stream! {
|
||||
match input.next().await {
|
||||
Some(obj @ Value {
|
||||
value: UntaggedValue::Row(_),
|
||||
..
|
||||
}) => match obj.insert_data_at_column_path(&column, value.clone()) {
|
||||
Ok(v) => yield Ok(ReturnSuccess::Value(v)),
|
||||
Err(err) => yield Err(err),
|
||||
},
|
||||
|
||||
Some(Value { tag, ..}) => {
|
||||
yield Err(ShellError::labeled_error(
|
||||
"Unrecognized type in stream",
|
||||
"original value",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
|
||||
None => {}
|
||||
};
|
||||
|
||||
};
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
|
198
crates/nu-cli/src/commands/is_empty.rs
Normal file
198
crates/nu-cli/src/commands/is_empty.rs
Normal file
@ -0,0 +1,198 @@
|
||||
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>,
|
||||
}
|
||||
|
||||
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."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, is_empty)?.run()
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(
|
||||
IsEmptyArgs { rest }: IsEmptyArgs,
|
||||
RunnableContext { input, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
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 =
|
||||
out.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?;
|
||||
|
||||
let emptiness_value = match out {
|
||||
obj
|
||||
@
|
||||
Value {
|
||||
value: UntaggedValue::Row(_),
|
||||
..
|
||||
} => {
|
||||
if val.is_empty() {
|
||||
match obj.replace_data_at_column_path(&field, default.clone()) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(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 =
|
||||
value.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?;
|
||||
|
||||
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 =
|
||||
value.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?;
|
||||
|
||||
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())
|
||||
}
|
62
crates/nu-cli/src/commands/keep.rs
Normal file
62
crates/nu-cli/src/commands/keep.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Keep;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct KeepArgs {
|
||||
rows: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Keep {
|
||||
fn name(&self) -> &str {
|
||||
"keep"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("keep").optional(
|
||||
"rows",
|
||||
SyntaxShape::Int,
|
||||
"starting from the front, the number of rows to keep",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Keep the number of rows only"
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, keep)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Keep the first row",
|
||||
example: "ls | keep",
|
||||
},
|
||||
Example {
|
||||
description: "Keep the first four rows",
|
||||
example: "ls | keep 4",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn keep(KeepArgs { rows }: KeepArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
let rows_desired = if let Some(quantity) = rows {
|
||||
*quantity
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
Ok(OutputStream::from_input(context.input.take(rows_desired)))
|
||||
}
|
98
crates/nu-cli/src/commands/keep_until.rs
Normal file
98
crates/nu-cli/src/commands/keep_until.rs
Normal file
@ -0,0 +1,98 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::evaluate::evaluate_baseline_expr;
|
||||
use crate::prelude::*;
|
||||
use log::trace;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct KeepUntil;
|
||||
|
||||
impl WholeStreamCommand for KeepUntil {
|
||||
fn name(&self) -> &str {
|
||||
"keep-until"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("keep-until")
|
||||
.required(
|
||||
"condition",
|
||||
SyntaxShape::Math,
|
||||
"the condition that must be met to stop keeping rows",
|
||||
)
|
||||
.filter()
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Keeps rows until the condition matches."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let scope = args.call_info.scope.clone();
|
||||
let call_info = args.evaluate_once(®istry)?;
|
||||
|
||||
let block = call_info.args.expect_nth(0)?.clone();
|
||||
|
||||
let condition = match block {
|
||||
Value {
|
||||
value: UntaggedValue::Block(block),
|
||||
tag,
|
||||
} => {
|
||||
if block.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
match block.block[0].list.get(0) {
|
||||
Some(item) => match item {
|
||||
ClassifiedCommand::Expr(expr) => expr.clone(),
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
))
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Value { tag, .. } => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let objects = call_info.input.take_while(move |item| {
|
||||
let condition = condition.clone();
|
||||
trace!("ITEM = {:?}", item);
|
||||
let result =
|
||||
evaluate_baseline_expr(&*condition, ®istry, &scope.clone().set_it(item.clone()));
|
||||
trace!("RESULT = {:?}", result);
|
||||
|
||||
let return_value = match result {
|
||||
Ok(ref v) if v.is_true() => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
futures::future::ready(return_value)
|
||||
});
|
||||
|
||||
Ok(objects.from_input_stream())
|
||||
}
|
||||
}
|
98
crates/nu-cli/src/commands/keep_while.rs
Normal file
98
crates/nu-cli/src/commands/keep_while.rs
Normal file
@ -0,0 +1,98 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::evaluate::evaluate_baseline_expr;
|
||||
use crate::prelude::*;
|
||||
use log::trace;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct KeepWhile;
|
||||
|
||||
impl WholeStreamCommand for KeepWhile {
|
||||
fn name(&self) -> &str {
|
||||
"keep-while"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("keep-while")
|
||||
.required(
|
||||
"condition",
|
||||
SyntaxShape::Math,
|
||||
"the condition that must be met to keep rows",
|
||||
)
|
||||
.filter()
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Keeps rows while the condition matches."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let scope = args.call_info.scope.clone();
|
||||
let call_info = args.evaluate_once(®istry)?;
|
||||
|
||||
let block = call_info.args.expect_nth(0)?.clone();
|
||||
|
||||
let condition = match block {
|
||||
Value {
|
||||
value: UntaggedValue::Block(block),
|
||||
tag,
|
||||
} => {
|
||||
if block.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
match block.block[0].list.get(0) {
|
||||
Some(item) => match item {
|
||||
ClassifiedCommand::Expr(expr) => expr.clone(),
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
))
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Value { tag, .. } => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let objects = call_info.input.take_while(move |item| {
|
||||
let condition = condition.clone();
|
||||
trace!("ITEM = {:?}", item);
|
||||
let result =
|
||||
evaluate_baseline_expr(&*condition, ®istry, &scope.clone().set_it(item.clone()));
|
||||
trace!("RESULT = {:?}", result);
|
||||
|
||||
let return_value = match result {
|
||||
Ok(ref v) if v.is_true() => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
futures::future::ready(return_value)
|
||||
});
|
||||
|
||||
Ok(objects.from_input_stream())
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
use crate::commands::command::RunnablePerItemContext;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
@ -16,7 +16,7 @@ pub struct KillArgs {
|
||||
pub quiet: Tagged<bool>,
|
||||
}
|
||||
|
||||
impl PerItemCommand for Kill {
|
||||
impl WholeStreamCommand for Kill {
|
||||
fn name(&self) -> &str {
|
||||
"kill"
|
||||
}
|
||||
@ -39,14 +39,23 @@ impl PerItemCommand for Kill {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
call_info
|
||||
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), kill)?
|
||||
.run()
|
||||
args.process(registry, kill)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Kill the pid using the most memory",
|
||||
example: "ps | sort-by mem | last | kill $it.pid",
|
||||
},
|
||||
Example {
|
||||
description: "Force kill a given pid",
|
||||
example: "kill --force 12345",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +66,7 @@ fn kill(
|
||||
force,
|
||||
quiet,
|
||||
}: KillArgs,
|
||||
_context: &RunnablePerItemContext,
|
||||
_context: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let mut cmd = if cfg!(windows) {
|
||||
let mut cmd = Command::new("taskkill");
|
||||
|
@ -36,6 +36,19 @@ impl WholeStreamCommand for Last {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, last)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Get the last row",
|
||||
example: "ls | last",
|
||||
},
|
||||
Example {
|
||||
description: "Get the last three rows",
|
||||
example: "ls | last 3",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn last(LastArgs { rows }: LastArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
|
@ -25,6 +25,13 @@ impl WholeStreamCommand for Lines {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
lines(args, registry)
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Split output from an external command into lines",
|
||||
example: "^ls -l | lines",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn ends_with_line_ending(st: &str) -> bool {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::commands::command::RunnablePerItemContext;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@ -16,9 +16,11 @@ pub struct LsArgs {
|
||||
pub short_names: bool,
|
||||
#[serde(rename = "with-symlink-targets")]
|
||||
pub with_symlink_targets: bool,
|
||||
#[serde(rename = "du")]
|
||||
pub du: bool,
|
||||
}
|
||||
|
||||
impl PerItemCommand for Ls {
|
||||
impl WholeStreamCommand for Ls {
|
||||
fn name(&self) -> &str {
|
||||
"ls"
|
||||
}
|
||||
@ -46,6 +48,11 @@ impl PerItemCommand for Ls {
|
||||
"display the paths to the target files that symlinks point to",
|
||||
Some('w'),
|
||||
)
|
||||
.switch(
|
||||
"du",
|
||||
"display the apparent directory size in place of the directory metadata size",
|
||||
Some('d'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -54,17 +61,30 @@ impl PerItemCommand for Ls {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
call_info
|
||||
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), ls)?
|
||||
.run()
|
||||
args.process(registry, ls)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "List all files in the current directory",
|
||||
example: "ls",
|
||||
},
|
||||
Example {
|
||||
description: "List all files in a subdirectory",
|
||||
example: "ls subdir",
|
||||
},
|
||||
Example {
|
||||
description: "List all rust files",
|
||||
example: "ls *.rs",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn ls(args: LsArgs, context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> {
|
||||
context.shell_manager.ls(args, context)
|
||||
fn ls(args: LsArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
context.shell_manager.ls(args, &context)
|
||||
}
|
||||
|
103
crates/nu-cli/src/commands/merge.rs
Normal file
103
crates/nu-cli/src/commands/merge.rs
Normal file
@ -0,0 +1,103 @@
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::data::value::merge_values;
|
||||
use crate::prelude::*;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::Block, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
pub struct Merge;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct MergeArgs {
|
||||
block: Block,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Merge {
|
||||
fn name(&self) -> &str {
|
||||
"merge"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("merge").required(
|
||||
"block",
|
||||
SyntaxShape::Block,
|
||||
"the block to run and merge into the table",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Merge a table."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
Ok(args.process_raw(registry, merge)?.run())
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Merge a 1-based index column with some ls output",
|
||||
example: "ls | select name | keep 3 | merge { echo [1 2 3] | wrap index }",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(
|
||||
merge_args: MergeArgs,
|
||||
context: RunnableContext,
|
||||
raw_args: RawCommandArgs,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let block = merge_args.block;
|
||||
let registry = context.registry.clone();
|
||||
let mut input = context.input;
|
||||
let scope = raw_args.call_info.scope.clone();
|
||||
|
||||
let mut context = Context::from_raw(&raw_args, ®istry);
|
||||
|
||||
let stream = async_stream! {
|
||||
let table: Option<Vec<Value>> = match run_block(&block,
|
||||
&mut context,
|
||||
InputStream::empty(),
|
||||
&scope).await {
|
||||
Ok(mut stream) => Some(stream.drain_vec().await),
|
||||
Err(err) => {
|
||||
yield Err(err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let table = table.unwrap_or_else(|| vec![Value {
|
||||
value: UntaggedValue::row(IndexMap::default()),
|
||||
tag: raw_args.call_info.name_tag,
|
||||
}]);
|
||||
|
||||
let mut idx = 0;
|
||||
|
||||
while let Some(value) = input.next().await {
|
||||
let other = table.get(idx);
|
||||
|
||||
match other {
|
||||
Some(replacement) => {
|
||||
match merge_values(&value.value, &replacement.value) {
|
||||
Ok(merged_value) => yield ReturnSuccess::value(merged_value.into_value(&value.tag)),
|
||||
Err(err) => {
|
||||
let message = format!("The row at {:?} types mismatch", idx);
|
||||
yield Err(ShellError::labeled_error("Could not merge", &message, &value.tag));
|
||||
}
|
||||
}
|
||||
}
|
||||
None => yield ReturnSuccess::value(value),
|
||||
}
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
use crate::commands::command::RunnablePerItemContext;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@ -13,7 +13,7 @@ pub struct MkdirArgs {
|
||||
pub rest: Vec<Tagged<PathBuf>>,
|
||||
}
|
||||
|
||||
impl PerItemCommand for Mkdir {
|
||||
impl WholeStreamCommand for Mkdir {
|
||||
fn name(&self) -> &str {
|
||||
"mkdir"
|
||||
}
|
||||
@ -28,18 +28,21 @@ impl PerItemCommand for Mkdir {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
call_info
|
||||
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), mkdir)?
|
||||
.run()
|
||||
args.process(registry, mkdir)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Make a directory named foo",
|
||||
example: "mkdir foo",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn mkdir(args: MkdirArgs, context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> {
|
||||
fn mkdir(args: MkdirArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
let shell_manager = context.shell_manager.clone();
|
||||
shell_manager.mkdir(args, context)
|
||||
shell_manager.mkdir(args, &context)
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::commands::command::RunnablePerItemContext;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@ -14,7 +14,7 @@ pub struct MoveArgs {
|
||||
pub dst: Tagged<PathBuf>,
|
||||
}
|
||||
|
||||
impl PerItemCommand for Move {
|
||||
impl WholeStreamCommand for Move {
|
||||
fn name(&self) -> &str {
|
||||
"mv"
|
||||
}
|
||||
@ -39,18 +39,31 @@ impl PerItemCommand for Move {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
call_info
|
||||
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), mv)?
|
||||
.run()
|
||||
args.process(registry, mv)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Rename a file",
|
||||
example: "mv before.txt after.txt",
|
||||
},
|
||||
Example {
|
||||
description: "Move a file into a directory",
|
||||
example: "mv test.txt my/subdirectory",
|
||||
},
|
||||
Example {
|
||||
description: "Move many files into a directory",
|
||||
example: "mv *.txt my/subdirectory",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn mv(args: MoveArgs, context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> {
|
||||
fn mv(args: MoveArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
let shell_manager = context.shell_manager.clone();
|
||||
shell_manager.mv(args, context)
|
||||
shell_manager.mv(args, &context)
|
||||
}
|
||||
|
@ -39,6 +39,19 @@ impl WholeStreamCommand for Nth {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, nth)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Get the second row",
|
||||
example: "echo [first second third] | get 1",
|
||||
},
|
||||
Example {
|
||||
description: "Get the first and third rows",
|
||||
example: "echo [first second third] | get 0 2",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn nth(
|
||||
|
@ -1,14 +1,19 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
CallInfo, CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::{AnchorLocation, Span};
|
||||
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::{AnchorLocation, Span, Tagged};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub struct Open;
|
||||
|
||||
impl PerItemCommand for Open {
|
||||
#[derive(Deserialize)]
|
||||
pub struct OpenArgs {
|
||||
path: Tagged<PathBuf>,
|
||||
raw: Tagged<bool>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Open {
|
||||
fn name(&self) -> &str {
|
||||
"open"
|
||||
}
|
||||
@ -33,36 +38,23 @@ impl PerItemCommand for Open {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
run(call_info, raw_args)
|
||||
args.process(registry, open)?.run()
|
||||
}
|
||||
}
|
||||
|
||||
fn run(call_info: &CallInfo, raw_args: &RawCommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let shell_manager = &raw_args.shell_manager;
|
||||
fn open(
|
||||
OpenArgs { path, raw }: OpenArgs,
|
||||
RunnableContext { shell_manager, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let cwd = PathBuf::from(shell_manager.path());
|
||||
let full_path = cwd;
|
||||
|
||||
let path = call_info.args.nth(0).ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
"No file or directory specified",
|
||||
"for command",
|
||||
&call_info.name_tag,
|
||||
)
|
||||
})?;
|
||||
|
||||
let path_buf = path.as_path()?;
|
||||
let path_str = path_buf.display().to_string();
|
||||
let path_span = path.tag.span;
|
||||
let has_raw = call_info.args.has("raw");
|
||||
|
||||
let stream = async_stream! {
|
||||
|
||||
let result = fetch(&full_path, &path_str, path_span).await;
|
||||
let result = fetch(&full_path, &path.item, path.tag.span).await;
|
||||
|
||||
if let Err(e) = result {
|
||||
yield Err(e);
|
||||
@ -70,12 +62,12 @@ fn run(call_info: &CallInfo, raw_args: &RawCommandArgs) -> Result<OutputStream,
|
||||
}
|
||||
let (file_extension, contents, contents_tag) = result?;
|
||||
|
||||
let file_extension = if has_raw {
|
||||
let file_extension = if raw.item {
|
||||
None
|
||||
} else {
|
||||
// If the extension could not be determined via mimetype, try to use the path
|
||||
// extension. Some file types do not declare their mimetypes (such as bson files).
|
||||
file_extension.or(path_str.split('.').last().map(String::from))
|
||||
file_extension.or(path.extension().map(|x| x.to_string_lossy().to_string()))
|
||||
};
|
||||
|
||||
let tagged_contents = contents.into_value(&contents_tag);
|
||||
@ -92,7 +84,7 @@ fn run(call_info: &CallInfo, raw_args: &RawCommandArgs) -> Result<OutputStream,
|
||||
|
||||
pub async fn fetch(
|
||||
cwd: &PathBuf,
|
||||
location: &str,
|
||||
location: &PathBuf,
|
||||
span: Span,
|
||||
) -> Result<(Option<String>, UntaggedValue, Tag), ShellError> {
|
||||
let mut cwd = cwd.clone();
|
||||
|
@ -1,11 +1,9 @@
|
||||
use crate::commands::PerItemCommand;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
CallInfo, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -84,7 +82,12 @@ fn build_regex(commands: &[ParseCommand]) -> String {
|
||||
}
|
||||
pub struct Parse;
|
||||
|
||||
impl PerItemCommand for Parse {
|
||||
#[derive(Deserialize)]
|
||||
pub struct ParseArgs {
|
||||
pattern: Tagged<String>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Parse {
|
||||
fn name(&self) -> &str {
|
||||
"parse"
|
||||
}
|
||||
@ -92,7 +95,7 @@ impl PerItemCommand for Parse {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("parse").required(
|
||||
"pattern",
|
||||
SyntaxShape::Any,
|
||||
SyntaxShape::String,
|
||||
"the pattern to match. Eg) \"{foo}: {bar}\"",
|
||||
)
|
||||
}
|
||||
@ -103,41 +106,62 @@ impl PerItemCommand for Parse {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
_raw_args: &RawCommandArgs,
|
||||
value: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
//let value_tag = value.tag();
|
||||
let pattern = call_info.args.expect_nth(0)?.as_string()?;
|
||||
args.process(registry, parse_command)?.run()
|
||||
}
|
||||
|
||||
let parse_pattern = parse(&pattern);
|
||||
let parse_regex = build_regex(&parse_pattern);
|
||||
|
||||
let column_names = column_names(&parse_pattern);
|
||||
let regex = Regex::new(&parse_regex).map_err(|_| {
|
||||
ShellError::labeled_error("Could not parse regex", "could not parse regex", &value.tag)
|
||||
})?;
|
||||
|
||||
let output = if let Ok(s) = value.as_string() {
|
||||
let mut results = vec![];
|
||||
for cap in regex.captures_iter(&s) {
|
||||
let mut dict = TaggedDictBuilder::new(value.tag());
|
||||
|
||||
for (idx, column_name) in column_names.iter().enumerate() {
|
||||
dict.insert_untagged(
|
||||
column_name,
|
||||
UntaggedValue::string(&cap[idx + 1].to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
results.push(ReturnSuccess::value(dict.into_value()));
|
||||
}
|
||||
|
||||
VecDeque::from(results)
|
||||
} else {
|
||||
VecDeque::new()
|
||||
};
|
||||
Ok(output.into())
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Parse values from a string into a table",
|
||||
example: r#"echo "data: 123" | parse "{key}: {value}""#,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_command(
|
||||
ParseArgs { pattern }: ParseArgs,
|
||||
RunnableContext { name, input, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let parse_pattern = parse(&pattern.item);
|
||||
let parse_regex = build_regex(&parse_pattern);
|
||||
let column_names = column_names(&parse_pattern);
|
||||
let name = name.span;
|
||||
let regex = Regex::new(&parse_regex).map_err(|_| {
|
||||
ShellError::labeled_error(
|
||||
"Could not parse regex",
|
||||
"could not parse regex",
|
||||
&pattern.tag,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(input
|
||||
.map(move |value| {
|
||||
if let Ok(s) = value.as_string() {
|
||||
let mut output = vec![];
|
||||
for cap in regex.captures_iter(&s) {
|
||||
let mut dict = TaggedDictBuilder::new(value.tag());
|
||||
for (idx, column_name) in column_names.iter().enumerate() {
|
||||
dict.insert_untagged(
|
||||
column_name,
|
||||
UntaggedValue::string(cap[idx + 1].to_string()),
|
||||
);
|
||||
}
|
||||
output.push(Ok(ReturnSuccess::Value(dict.into_value())));
|
||||
}
|
||||
output
|
||||
} else {
|
||||
vec![Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected string input",
|
||||
"expected string input",
|
||||
name,
|
||||
"value originated here",
|
||||
value.tag,
|
||||
))]
|
||||
}
|
||||
})
|
||||
.map(futures::stream::iter)
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
}
|
||||
|
@ -35,6 +35,13 @@ impl WholeStreamCommand for Prepend {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, prepend)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Add something to the beginning of a list or table",
|
||||
example: "echo [2 3 4] | prepend 1",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn prepend(
|
||||
|
@ -25,6 +25,13 @@ impl WholeStreamCommand for Pwd {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
pwd(args, registry)
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Print the current working directory",
|
||||
example: "pwd",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pwd(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
|
@ -23,12 +23,9 @@ impl WholeStreamCommand for Rename {
|
||||
.required(
|
||||
"column_name",
|
||||
SyntaxShape::String,
|
||||
"the name of the column to rename for",
|
||||
)
|
||||
.rest(
|
||||
SyntaxShape::String,
|
||||
"Additional column name(s) to rename for",
|
||||
"the new name for the first column",
|
||||
)
|
||||
.rest(SyntaxShape::String, "the new name for additional columns")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -42,6 +39,19 @@ impl WholeStreamCommand for Rename {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, rename)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Rename a column",
|
||||
example: "ls | rename my_name",
|
||||
},
|
||||
Example {
|
||||
description: "Rename many columns",
|
||||
example: "echo \"{a: 1, b: 2, c: 3}\" | from json | rename spam eggs cars",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename(
|
||||
|
@ -26,6 +26,13 @@ impl WholeStreamCommand for Reverse {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
reverse(args, registry)
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Sort files in descending file size",
|
||||
example: "ls | sort-by size | reverse",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn reverse(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::commands::command::RunnablePerItemContext;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@ -16,7 +16,7 @@ pub struct RemoveArgs {
|
||||
pub trash: Tagged<bool>,
|
||||
}
|
||||
|
||||
impl PerItemCommand for Remove {
|
||||
impl WholeStreamCommand for Remove {
|
||||
fn name(&self) -> &str {
|
||||
"rm"
|
||||
}
|
||||
@ -38,18 +38,27 @@ impl PerItemCommand for Remove {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
_input: Value,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
call_info
|
||||
.process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), rm)?
|
||||
.run()
|
||||
args.process(registry, rm)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Delete a file",
|
||||
example: "rm file.txt",
|
||||
},
|
||||
Example {
|
||||
description: "Move a file to the system trash",
|
||||
example: "rm --trash file.txt",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn rm(args: RemoveArgs, context: &RunnablePerItemContext) -> Result<OutputStream, ShellError> {
|
||||
fn rm(args: RemoveArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
let shell_manager = context.shell_manager.clone();
|
||||
shell_manager.rm(args, context)
|
||||
shell_manager.rm(args, &context)
|
||||
}
|
||||
|
@ -1,18 +1,19 @@
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
|
||||
use derive_new::new;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::Block, CallInfo, ReturnSuccess, Scope, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{hir::Block, ReturnSuccess, Signature, SyntaxShape};
|
||||
|
||||
#[derive(new)]
|
||||
#[derive(new, Clone)]
|
||||
pub struct AliasCommand {
|
||||
name: String,
|
||||
args: Vec<String>,
|
||||
block: Block,
|
||||
}
|
||||
|
||||
impl PerItemCommand for AliasCommand {
|
||||
impl WholeStreamCommand for AliasCommand {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
@ -33,35 +34,31 @@ impl PerItemCommand for AliasCommand {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
input: Value,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let tag = call_info.name_tag.clone();
|
||||
let call_info = call_info.clone();
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let call_info = args.call_info.clone();
|
||||
let registry = registry.clone();
|
||||
let raw_args = raw_args.clone();
|
||||
let block = self.block.clone();
|
||||
|
||||
let mut scope = Scope::it_value(input.clone());
|
||||
if let Some(positional) = &call_info.args.positional {
|
||||
for (pos, arg) in positional.iter().enumerate() {
|
||||
scope = scope.set_var(self.args[pos].to_string(), arg.clone());
|
||||
}
|
||||
}
|
||||
let alias_command = self.clone();
|
||||
let mut context = Context::from_args(&args, ®istry);
|
||||
let input = args.input;
|
||||
|
||||
let stream = async_stream! {
|
||||
let mut context = Context::from_raw(&raw_args, ®istry);
|
||||
|
||||
let input_clone = Ok(input.clone());
|
||||
let input_stream = futures::stream::once(async { input_clone }).boxed().to_input_stream();
|
||||
let mut scope = call_info.scope.clone();
|
||||
let evaluated = call_info.evaluate(®istry)?;
|
||||
if let Some(positional) = &evaluated.args.positional {
|
||||
for (pos, arg) in positional.iter().enumerate() {
|
||||
scope = scope.set_var(alias_command.args[pos].to_string(), arg.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let result = run_block(
|
||||
&block,
|
||||
&mut context,
|
||||
input_stream,
|
||||
&scope
|
||||
input,
|
||||
&scope,
|
||||
).await;
|
||||
|
||||
match result {
|
||||
@ -80,7 +77,6 @@ impl PerItemCommand for AliasCommand {
|
||||
|
||||
let errors = context.get_errors();
|
||||
if let Some(x) = errors.first() {
|
||||
//yield Err(error.clone());
|
||||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Alias failed to run",
|
||||
"alias failed to run",
|
||||
@ -91,7 +87,6 @@ impl PerItemCommand for AliasCommand {
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
//yield Err(e);
|
||||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Alias failed to run",
|
||||
"alias failed to run",
|
||||
|
@ -1,29 +1,39 @@
|
||||
use crate::commands::cd::CdArgs;
|
||||
use crate::commands::classified::external;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
|
||||
use derive_new::new;
|
||||
use parking_lot::Mutex;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::{Expression, ExternalArgs, ExternalCommand, Literal, SpannedExpression};
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RunExternalArgs {}
|
||||
|
||||
#[derive(new)]
|
||||
pub struct RunExternalCommand;
|
||||
pub struct RunExternalCommand {
|
||||
/// Whether or not nushell is being used in an interactive context
|
||||
pub(crate) interactive: bool,
|
||||
}
|
||||
|
||||
fn spanned_expression_to_string(expr: SpannedExpression) -> String {
|
||||
fn spanned_expression_to_string(expr: SpannedExpression) -> Result<String, ShellError> {
|
||||
if let SpannedExpression {
|
||||
expr: Expression::Literal(Literal::String(s)),
|
||||
..
|
||||
} = expr
|
||||
{
|
||||
s
|
||||
Ok(s)
|
||||
} else {
|
||||
"notacommand!!!".to_string()
|
||||
Err(ShellError::labeled_error(
|
||||
"Expected string for command name",
|
||||
"expected string",
|
||||
expr.span,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +55,7 @@ impl WholeStreamCommand for RunExternalCommand {
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let positionals = args.call_info.args.positional.ok_or_else(|| {
|
||||
let positionals = args.call_info.args.positional.clone().ok_or_else(|| {
|
||||
ShellError::untagged_runtime_error("positional arguments unexpectedly empty")
|
||||
})?;
|
||||
|
||||
@ -53,49 +63,89 @@ impl WholeStreamCommand for RunExternalCommand {
|
||||
|
||||
let name = positionals
|
||||
.next()
|
||||
.map(spanned_expression_to_string)
|
||||
.ok_or_else(|| {
|
||||
ShellError::untagged_runtime_error(
|
||||
"run_external unexpectedly missing external name positional arg",
|
||||
)
|
||||
})?;
|
||||
ShellError::untagged_runtime_error("run_external called with no arguments")
|
||||
})
|
||||
.and_then(spanned_expression_to_string)?;
|
||||
|
||||
let command = ExternalCommand {
|
||||
name,
|
||||
name_tag: args.call_info.name_tag.clone(),
|
||||
args: ExternalArgs {
|
||||
list: positionals.collect(),
|
||||
span: args.call_info.args.span,
|
||||
},
|
||||
let mut external_context = {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
Context {
|
||||
registry: registry.clone(),
|
||||
host: args.host.clone(),
|
||||
shell_manager: args.shell_manager.clone(),
|
||||
ctrl_c: args.ctrl_c.clone(),
|
||||
current_errors: Arc::new(Mutex::new(vec![])),
|
||||
windows_drives_previous_cwd: Arc::new(Mutex::new(
|
||||
std::collections::HashMap::new(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
Context {
|
||||
registry: registry.clone(),
|
||||
host: args.host.clone(),
|
||||
shell_manager: args.shell_manager.clone(),
|
||||
ctrl_c: args.ctrl_c.clone(),
|
||||
current_errors: Arc::new(Mutex::new(vec![])),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut external_context;
|
||||
#[cfg(windows)]
|
||||
{
|
||||
external_context = Context {
|
||||
registry: registry.clone(),
|
||||
host: args.host.clone(),
|
||||
shell_manager: args.shell_manager.clone(),
|
||||
ctrl_c: args.ctrl_c.clone(),
|
||||
current_errors: Arc::new(Mutex::new(vec![])),
|
||||
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
||||
};
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
external_context = Context {
|
||||
registry: registry.clone(),
|
||||
host: args.host.clone(),
|
||||
shell_manager: args.shell_manager.clone(),
|
||||
ctrl_c: args.ctrl_c.clone(),
|
||||
current_errors: Arc::new(Mutex::new(vec![])),
|
||||
};
|
||||
}
|
||||
let scope = args.call_info.scope.clone();
|
||||
let is_interactive = self.interactive;
|
||||
|
||||
let is_last = args.call_info.args.is_last;
|
||||
let input = args.input;
|
||||
let stream = async_stream! {
|
||||
let command = ExternalCommand {
|
||||
name,
|
||||
name_tag: args.call_info.name_tag.clone(),
|
||||
args: ExternalArgs {
|
||||
list: positionals.collect(),
|
||||
span: args.call_info.args.span,
|
||||
},
|
||||
};
|
||||
|
||||
// If we're in interactive mode, we will "auto cd". That is, instead of interpreting
|
||||
// this as an external command, we will see it as a path and `cd` into it.
|
||||
if is_interactive {
|
||||
if let Some(path) = maybe_autocd_dir(&command, &mut external_context).await {
|
||||
let cd_args = CdArgs {
|
||||
path: Some(Tagged {
|
||||
item: PathBuf::from(path),
|
||||
tag: args.call_info.name_tag.clone(),
|
||||
})
|
||||
};
|
||||
|
||||
let context = RunnableContext {
|
||||
input: InputStream::empty(),
|
||||
shell_manager: external_context.shell_manager.clone(),
|
||||
host: external_context.host.clone(),
|
||||
ctrl_c: external_context.ctrl_c.clone(),
|
||||
registry: external_context.registry.clone(),
|
||||
name: args.call_info.name_tag.clone(),
|
||||
};
|
||||
|
||||
let result = external_context.shell_manager.cd(cd_args, &context);
|
||||
match result {
|
||||
Ok(mut stream) => {
|
||||
while let Some(value) = stream.next().await {
|
||||
yield value;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
yield Err(e);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let scope = args.call_info.scope.clone();
|
||||
let is_last = args.call_info.args.is_last;
|
||||
let input = args.input;
|
||||
let result = external::run_external_command(
|
||||
command,
|
||||
&mut external_context,
|
||||
@ -120,3 +170,54 @@ impl WholeStreamCommand for RunExternalCommand {
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
async fn maybe_autocd_dir<'a>(cmd: &ExternalCommand, ctx: &mut Context) -> Option<String> {
|
||||
// We will "auto cd" if
|
||||
// - the command name ends in a path separator, or
|
||||
// - it's not a command on the path and no arguments were given.
|
||||
let name = &cmd.name;
|
||||
let path_name = if name.ends_with(std::path::MAIN_SEPARATOR)
|
||||
|| (cmd.args.is_empty()
|
||||
&& PathBuf::from(name).is_dir()
|
||||
&& dunce::canonicalize(name).is_ok()
|
||||
&& which::which(&name).is_err())
|
||||
{
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
path_name.map(|name| {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if name.ends_with(':') {
|
||||
// This looks like a drive shortcut. We need to a) switch drives and b) go back to the previous directory we were viewing on that drive
|
||||
// But first, we need to save where we are now
|
||||
let current_path = ctx.shell_manager.path();
|
||||
|
||||
let split_path: Vec<_> = current_path.split(':').collect();
|
||||
if split_path.len() > 1 {
|
||||
ctx.windows_drives_previous_cwd
|
||||
.lock()
|
||||
.insert(split_path[0].to_string(), current_path);
|
||||
}
|
||||
|
||||
let name = name.to_uppercase();
|
||||
let new_drive: Vec<_> = name.split(':').collect();
|
||||
|
||||
if let Some(val) = ctx.windows_drives_previous_cwd.lock().get(new_drive[0]) {
|
||||
val.to_string()
|
||||
} else {
|
||||
name
|
||||
}
|
||||
} else {
|
||||
name.to_string()
|
||||
}
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
name.to_string()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand};
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@ -175,6 +175,7 @@ fn save(
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let mut full_path = PathBuf::from(shell_manager.path());
|
||||
let name_tag = name.clone();
|
||||
let scope = raw_args.call_info.scope.clone();
|
||||
|
||||
let stream = async_stream! {
|
||||
let input: Vec<Value> = input.collect().await;
|
||||
@ -221,7 +222,7 @@ fn save(
|
||||
let content : Result<Vec<u8>, ShellError> = 'scope: loop {
|
||||
break if !save_raw {
|
||||
if let Some(extension) = full_path.extension() {
|
||||
let command_name = format!("to-{}", extension.to_string_lossy());
|
||||
let command_name = format!("to {}", extension.to_string_lossy());
|
||||
if let Some(converter) = registry.get_command(&command_name) {
|
||||
let new_args = RawCommandArgs {
|
||||
host,
|
||||
@ -236,7 +237,7 @@ fn save(
|
||||
is_last: false,
|
||||
},
|
||||
name_tag: raw_args.call_info.name_tag,
|
||||
scope: Scope::empty(), // FIXME?
|
||||
scope,
|
||||
}
|
||||
};
|
||||
let mut result = converter.run(new_args.with_input(input), ®istry);
|
||||
|
@ -10,19 +10,19 @@ use nu_source::span_for_spanned_list;
|
||||
use nu_value_ext::{as_string, get_data_by_column_path};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct PickArgs {
|
||||
struct SelectArgs {
|
||||
rest: Vec<ColumnPath>,
|
||||
}
|
||||
|
||||
pub struct Pick;
|
||||
pub struct Select;
|
||||
|
||||
impl WholeStreamCommand for Pick {
|
||||
impl WholeStreamCommand for Select {
|
||||
fn name(&self) -> &str {
|
||||
"pick"
|
||||
"select"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pick").rest(
|
||||
Signature::build("select").rest(
|
||||
SyntaxShape::ColumnPath,
|
||||
"the columns to select from the table",
|
||||
)
|
||||
@ -37,19 +37,32 @@ impl WholeStreamCommand for Pick {
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, pick)?.run()
|
||||
args.process(registry, select)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Select just the name column",
|
||||
example: "ls | select name",
|
||||
},
|
||||
Example {
|
||||
description: "Select the name and size columns",
|
||||
example: "ls | select name size",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn pick(
|
||||
PickArgs { rest: mut fields }: PickArgs,
|
||||
fn select(
|
||||
SelectArgs { rest: mut fields }: SelectArgs,
|
||||
RunnableContext {
|
||||
mut input, name, ..
|
||||
}: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
if fields.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Pick requires columns to pick",
|
||||
"Select requires columns to select",
|
||||
"needs parameter",
|
||||
name,
|
||||
));
|
||||
@ -76,7 +89,7 @@ fn pick(
|
||||
if let PathMember { unspanned: UnspannedPathMember::String(column), .. } = path_member_tried {
|
||||
return ShellError::labeled_error_with_secondary(
|
||||
"No data to fetch.",
|
||||
format!("Couldn't pick column \"{}\"", column),
|
||||
format!("Couldn't select column \"{}\"", column),
|
||||
path_member_tried.span,
|
||||
format!("How about exploring it with \"get\"? Check the input is appropriate originating from here"),
|
||||
obj_source.tag.span)
|
@ -25,6 +25,13 @@ impl WholeStreamCommand for Size {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
size(args, registry)
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Count the number of words in a string",
|
||||
example: r#"echo "There are seven words in this sentence" | size"#,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
|
@ -32,6 +32,13 @@ impl WholeStreamCommand for Skip {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, skip)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[Example {
|
||||
description: "Skip the first 5 rows",
|
||||
example: "ls | skip 5",
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn skip(SkipArgs { rows }: SkipArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
|
98
crates/nu-cli/src/commands/skip_until.rs
Normal file
98
crates/nu-cli/src/commands/skip_until.rs
Normal file
@ -0,0 +1,98 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::evaluate::evaluate_baseline_expr;
|
||||
use crate::prelude::*;
|
||||
use log::trace;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SkipUntil;
|
||||
|
||||
impl WholeStreamCommand for SkipUntil {
|
||||
fn name(&self) -> &str {
|
||||
"skip-until"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("skip-until")
|
||||
.required(
|
||||
"condition",
|
||||
SyntaxShape::Math,
|
||||
"the condition that must be met to stop skipping",
|
||||
)
|
||||
.filter()
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Skips rows until the condition matches."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let scope = args.call_info.scope.clone();
|
||||
let call_info = args.evaluate_once(®istry)?;
|
||||
|
||||
let block = call_info.args.expect_nth(0)?.clone();
|
||||
|
||||
let condition = match block {
|
||||
Value {
|
||||
value: UntaggedValue::Block(block),
|
||||
tag,
|
||||
} => {
|
||||
if block.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
match block.block[0].list.get(0) {
|
||||
Some(item) => match item {
|
||||
ClassifiedCommand::Expr(expr) => expr.clone(),
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
))
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Value { tag, .. } => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let objects = call_info.input.skip_while(move |item| {
|
||||
let condition = condition.clone();
|
||||
trace!("ITEM = {:?}", item);
|
||||
let result =
|
||||
evaluate_baseline_expr(&*condition, ®istry, &scope.clone().set_it(item.clone()));
|
||||
trace!("RESULT = {:?}", result);
|
||||
|
||||
let return_value = match result {
|
||||
Ok(ref v) if v.is_true() => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
futures::future::ready(return_value)
|
||||
});
|
||||
|
||||
Ok(objects.from_input_stream())
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ use crate::evaluate::evaluate_baseline_expr;
|
||||
use crate::prelude::*;
|
||||
use log::trace;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::ClassifiedCommand, Scope, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SkipWhile;
|
||||
|
||||
@ -32,6 +32,7 @@ impl WholeStreamCommand for SkipWhile {
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let registry = registry.clone();
|
||||
let scope = args.call_info.scope.clone();
|
||||
let call_info = args.evaluate_once(®istry)?;
|
||||
|
||||
let block = call_info.args.expect_nth(0)?.clone();
|
||||
@ -80,7 +81,8 @@ impl WholeStreamCommand for SkipWhile {
|
||||
let objects = call_info.input.skip_while(move |item| {
|
||||
let condition = condition.clone();
|
||||
trace!("ITEM = {:?}", item);
|
||||
let result = evaluate_baseline_expr(&*condition, ®istry, &Scope::new(item.clone()));
|
||||
let result =
|
||||
evaluate_baseline_expr(&*condition, ®istry, &scope.clone().set_it(item.clone()));
|
||||
trace!("RESULT = {:?}", result);
|
||||
|
||||
let return_value = match result {
|
||||
|
@ -22,7 +22,7 @@ impl WholeStreamCommand for SortBy {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Sort by the given columns."
|
||||
"Sort by the given columns, in increasing order."
|
||||
}
|
||||
|
||||
fn run(
|
||||
@ -32,6 +32,19 @@ impl WholeStreamCommand for SortBy {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, sort_by)?.run()
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Sort output by increasing file size",
|
||||
example: "ls | sort-by size",
|
||||
},
|
||||
Example {
|
||||
description: "Sort output by type, and then by file size for each type",
|
||||
example: "ls | sort-by type size",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_by(
|
||||
|
@ -34,6 +34,19 @@ impl WholeStreamCommand for Sum {
|
||||
name: args.call_info.name_tag,
|
||||
})
|
||||
}
|
||||
|
||||
fn examples(&self) -> &[Example] {
|
||||
&[
|
||||
Example {
|
||||
description: "Sum a list of numbers",
|
||||
example: "echo [1 2 3] | sum",
|
||||
},
|
||||
Example {
|
||||
description: "Get the disk usage for the current directory",
|
||||
example: "ls --all --du | get size | sum",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn sum(RunnableContext { mut input, .. }: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
|
28
crates/nu-cli/src/commands/to.rs
Normal file
28
crates/nu-cli/src/commands/to.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::Signature;
|
||||
|
||||
pub struct To;
|
||||
|
||||
impl WholeStreamCommand for To {
|
||||
fn name(&self) -> &str {
|
||||
"to"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("to")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert table into an output format (based on subcommand, like csv, html, json, yaml)."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
Ok(crate::commands::help::get_help(self, registry).into())
|
||||
}
|
||||
}
|
@ -12,11 +12,11 @@ pub struct ToBSON;
|
||||
|
||||
impl WholeStreamCommand for ToBSON {
|
||||
fn name(&self) -> &str {
|
||||
"to-bson"
|
||||
"to bson"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("to-bson")
|
||||
Signature::build("to bson")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -2,7 +2,7 @@ use crate::commands::to_delimited_data::to_delimited_data;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct ToCSV;
|
||||
|
||||
@ -14,15 +14,22 @@ pub struct ToCSVArgs {
|
||||
|
||||
impl WholeStreamCommand for ToCSV {
|
||||
fn name(&self) -> &str {
|
||||
"to-csv"
|
||||
"to csv"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("to-csv").switch(
|
||||
"headerless",
|
||||
"do not output the columns names as the first row",
|
||||
None,
|
||||
)
|
||||
Signature::build("to csv")
|
||||
.named(
|
||||
"separator",
|
||||
SyntaxShape::String,
|
||||
"a character to separate columns, defaults to ','",
|
||||
Some('s'),
|
||||
)
|
||||
.switch(
|
||||
"headerless",
|
||||
"do not output the columns names as the first row",
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -175,40 +175,41 @@ pub fn to_delimited_data(
|
||||
let name_span = name_tag.span;
|
||||
|
||||
let stream = async_stream! {
|
||||
let input: Vec<Value> = input.collect().await;
|
||||
let input: Vec<Value> = input.collect().await;
|
||||
|
||||
let to_process_input = if input.len() > 1 {
|
||||
let tag = input[0].tag.clone();
|
||||
vec![Value { value: UntaggedValue::Table(input), tag } ]
|
||||
} else if input.len() == 1 {
|
||||
input
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let to_process_input = if input.len() > 1 {
|
||||
let tag = input[0].tag.clone();
|
||||
vec![Value { value: UntaggedValue::Table(input), tag } ]
|
||||
} else if input.len() == 1 {
|
||||
input
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
for value in to_process_input {
|
||||
match from_value_to_delimited_string(&clone_tagged_value(&value), sep) {
|
||||
Ok(x) => {
|
||||
let converted = if headerless {
|
||||
x.lines().skip(1).collect()
|
||||
} else {
|
||||
x
|
||||
};
|
||||
yield ReturnSuccess::value(UntaggedValue::Primitive(Primitive::String(converted)).into_value(&name_tag))
|
||||
}
|
||||
Err(x) => {
|
||||
let expected = format!("Expected a table with {}-compatible structure from pipeline", format_name);
|
||||
let requires = format!("requires {}-compatible input", format_name);
|
||||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
expected,
|
||||
requires,
|
||||
name_span,
|
||||
"originates from here".to_string(),
|
||||
value.tag.span,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
for value in to_process_input {
|
||||
match from_value_to_delimited_string(&clone_tagged_value(&value), sep) {
|
||||
Ok(mut x) => {
|
||||
if headerless {
|
||||
x.find('\n').map(|second_line|{
|
||||
let start = second_line + 1;
|
||||
x.replace_range(0..start, "");
|
||||
});
|
||||
}
|
||||
yield ReturnSuccess::value(UntaggedValue::Primitive(Primitive::String(x)).into_value(&name_tag))
|
||||
}
|
||||
Err(x) => {
|
||||
let expected = format!("Expected a table with {}-compatible structure from pipeline", format_name);
|
||||
let requires = format!("requires {}-compatible input", format_name);
|
||||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
expected,
|
||||
requires,
|
||||
name_span,
|
||||
"originates from here".to_string(),
|
||||
value.tag.span,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
|
@ -10,11 +10,11 @@ pub struct ToHTML;
|
||||
|
||||
impl WholeStreamCommand for ToHTML {
|
||||
fn name(&self) -> &str {
|
||||
"to-html"
|
||||
"to html"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("to-html")
|
||||
Signature::build("to html")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -39,7 +39,7 @@ fn to_html(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
|
||||
let headers = nu_protocol::merge_descriptors(&input);
|
||||
let mut output_string = "<html><body>".to_string();
|
||||
|
||||
if !headers.is_empty() && (headers.len() > 1 || headers[0] != "<value>") {
|
||||
if !headers.is_empty() && (headers.len() > 1 || headers[0] != "") {
|
||||
output_string.push_str("<table>");
|
||||
|
||||
output_string.push_str("<tr>");
|
||||
@ -109,7 +109,7 @@ fn to_html(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
|
||||
}
|
||||
}
|
||||
|
||||
if !headers.is_empty() && (headers.len() > 1 || headers[0] != "<value>") {
|
||||
if !headers.is_empty() && (headers.len() > 1 || headers[0] != "") {
|
||||
output_string.push_str("</table>");
|
||||
}
|
||||
output_string.push_str("</body></html>");
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user