mirror of
https://github.com/nushell/nushell.git
synced 2025-07-05 09:00:25 +02:00
Compare commits
143 Commits
Author | SHA1 | Date | |
---|---|---|---|
390d06d4e7 | |||
89acbda877 | |||
0d40d0438f | |||
1e8212a938 | |||
2da8310b11 | |||
c16d8f0d5f | |||
2ac5b0480a | |||
4e90b478b7 | |||
3a38fb94f0 | |||
c6f6dcb57c | |||
b80299eba7 | |||
a48616697a | |||
b82dccf0bd | |||
84caf8859f | |||
be7f35246e | |||
3917fda7ed | |||
3b357e5402 | |||
79da470239 | |||
37949e70e0 | |||
5d00ecef56 | |||
6dde231dde | |||
58fa2e51a2 | |||
cf0877bf72 | |||
a0db4ce747 | |||
6ee13126f7 | |||
1c15a4ed3a | |||
7aabc381a3 | |||
8c9dced71b | |||
06d5a31301 | |||
ffbc0b0180 | |||
c0901ef707 | |||
d3e84daa49 | |||
228ede18cf | |||
c5a69271a2 | |||
dc9d939c83 | |||
32f0f94b46 | |||
a142d1a192 | |||
173d60d59d | |||
f2989bf704 | |||
575ddbd4ef | |||
ef9b72d360 | |||
25349a1eac | |||
99e4c44862 | |||
1345f97202 | |||
f02076daa8 | |||
533e04a60a | |||
13c152b00f | |||
f231a6df4a | |||
3c0bccb900 | |||
f43a65d7a7 | |||
0827ed143d | |||
4b84825dbf | |||
82ae06865c | |||
128ce6f9b7 | |||
44cbd88b55 | |||
7164929c61 | |||
848ff8453b | |||
f94ca6cfde | |||
fab3f8fd40 | |||
dbcfcdae89 | |||
08aa248c42 | |||
9f07bcc66f | |||
2caa44cea8 | |||
28c21121cf | |||
a17d46f200 | |||
6cc8402127 | |||
5f0ad1d6ad | |||
8d7bb9147e | |||
bc48b4553c | |||
28c07a5072 | |||
30c8dabeb4 | |||
8b368b6a4e | |||
8c0d60d0fb | |||
8b0a4ccf4c | |||
cfe4eff566 | |||
38f3957edf | |||
cb66d2bcad | |||
ff73623873 | |||
d1c719a8cc | |||
4d854f36af | |||
8d5848c955 | |||
fe88d58b1e | |||
42dbfd1fa0 | |||
534e1fc3ce | |||
ff946a2f21 | |||
3c0cbec993 | |||
48e29e9ed6 | |||
ff53352afe | |||
4fd4136d50 | |||
dc1248a454 | |||
de554f8e5f | |||
44979f3051 | |||
7ae7394c85 | |||
9dbf7556b8 | |||
caafd26deb | |||
43a218240c | |||
11d7d8ea1e | |||
2dea9e6f1f | |||
c5cb369d8d | |||
b6959197bf | |||
d5b99ae316 | |||
9d10007085 | |||
2e0b964d5b | |||
5bae7e56ef | |||
b42ef45c7c | |||
3423cd54a1 | |||
837f0463eb | |||
56f6f683fc | |||
c57f41e5f2 | |||
8c74b1e437 | |||
8318d59ef1 | |||
64efa30f3e | |||
820a6bfb08 | |||
b8d253cbd7 | |||
3c421c5726 | |||
75b2d26187 | |||
17a5aa3052 | |||
e4a22799d5 | |||
fda456e469 | |||
e5d38dcff6 | |||
a82fa75c31 | |||
0c16464320 | |||
888758b813 | |||
cb909f810e | |||
a75318d7e8 | |||
7a9bf06005 | |||
a06299c77a | |||
e4bcd1934d | |||
4673adecc5 | |||
1b8051ece5 | |||
d44059c36b | |||
b79abdb2a5 | |||
ee8a0c9477 | |||
41853b9f18 | |||
997d56a288 | |||
0769e9b750 | |||
f5519e2a09 | |||
8259d463aa | |||
e2c015f725 | |||
eb12fffbc6 | |||
c42096c34e | |||
46eb34b35d | |||
23a73cd31f |
@ -1,6 +1,7 @@
|
|||||||
# increase the default windows stack size
|
|
||||||
[target.x86_64-pc-windows-msvc]
|
[target.x86_64-pc-windows-msvc]
|
||||||
rustflags = ["-C", "link-args=-stack:10000000"]
|
# increase the default windows stack size
|
||||||
|
# statically link the CRT so users don't have to install it
|
||||||
|
rustflags = ["-C", "link-args=-stack:10000000", "-C", "target-feature=+crt-static"]
|
||||||
|
|
||||||
# keeping this but commentting out in case we need them in the future
|
# keeping this but commentting out in case we need them in the future
|
||||||
|
|
||||||
|
6
.github/pull_request_template.md
vendored
6
.github/pull_request_template.md
vendored
@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
|
|
||||||
|
Make sure you've done the following:
|
||||||
|
|
||||||
|
- [ ] Add tests that cover your changes, either in the command examples, the crate/tests folder, or in the /tests folder.
|
||||||
|
- [ ] Try to think about corner cases and various ways how your changes could break. Cover them with tests.
|
||||||
|
- [ ] If adding tests is not possible, please document in the PR body a minimal example with steps on how to reproduce so one can verify your change works.
|
||||||
|
|
||||||
Make sure you've run and fixed any issues with these commands:
|
Make sure you've run and fixed any issues with these commands:
|
||||||
|
|
||||||
- [ ] `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
- [ ] `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
||||||
|
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -80,9 +80,11 @@ jobs:
|
|||||||
toolchain: ${{ matrix.rust }}
|
toolchain: ${{ matrix.rust }}
|
||||||
override: true
|
override: true
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v1
|
# Temporarily disabled; the cache was getting huge (2.6GB compressed) on Windows and causing issues.
|
||||||
with:
|
# TODO: investigate why the cache was so big
|
||||||
key: ${{ matrix.style }}v3 # increment this to bust the cache if needed
|
# - uses: Swatinem/rust-cache@v1
|
||||||
|
# with:
|
||||||
|
# key: ${{ matrix.style }}v3 # increment this to bust the cache if needed
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
|
4
.github/workflows/release-pkg.nu
vendored
4
.github/workflows/release-pkg.nu
vendored
@ -102,8 +102,8 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
|
|||||||
let releaseStem = $'($bin)-($version)-($target)'
|
let releaseStem = $'($bin)-($version)-($target)'
|
||||||
|
|
||||||
$'(char nl)Download less related stuffs...'; hr-line
|
$'(char nl)Download less related stuffs...'; hr-line
|
||||||
curl https://github.com/jftuga/less-Windows/releases/download/less-v590/less.exe -o $'($dist)\less.exe'
|
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v590/less.exe -o less.exe
|
||||||
curl https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o $'($dist)\LICENSE-for-less.txt'
|
aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
|
||||||
|
|
||||||
# Create Windows msi release package
|
# Create Windows msi release package
|
||||||
if (get-env _EXTRA_) == 'msi' {
|
if (get-env _EXTRA_) == 'msi' {
|
||||||
|
2
.github/workflows/winget-submission.yml
vendored
2
.github/workflows/winget-submission.yml
vendored
@ -15,5 +15,5 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||||
$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json
|
$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json
|
||||||
$installerUrl = $github.release.assets | Where-Object -Property name -match 'windows.msi' | Select -ExpandProperty browser_download_url -First 1
|
$installerUrl = $github.release.assets | Where-Object -Property name -match 'windows-msvc.msi' | Select -ExpandProperty browser_download_url -First 1
|
||||||
.\wingetcreate.exe update Nushell.Nushell -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.NUSHELL_PAT }}
|
.\wingetcreate.exe update Nushell.Nushell -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.NUSHELL_PAT }}
|
||||||
|
@ -1,21 +1,14 @@
|
|||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Welcome to nushell!
|
Welcome to Nushell!
|
||||||
|
|
||||||
*Note: for a more complete guide see [The nu contributor book](https://www.nushell.sh/contributor-book/)*
|
|
||||||
|
|
||||||
For speedy contributions open it in Gitpod, nu will be pre-installed with the latest build in a VSCode like editor all from your browser.
|
|
||||||
|
|
||||||
[](https://gitpod.io/#https://github.com/nushell/nushell)
|
|
||||||
|
|
||||||
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)!
|
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
|
## Developing
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
This is no different than other Rust projects.
|
Nushell requires a recent Rust toolchain and some dependencies; [refer to the Nu Book for up-to-date requirements](https://www.nushell.sh/book/installation.html#build-from-source). After installing dependencies, you should be able to clone+build Nu like any other Rust project:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/nushell/nushell
|
git clone https://github.com/nushell/nushell
|
||||||
@ -28,24 +21,24 @@ cargo build
|
|||||||
- Build and run Nushell:
|
- Build and run Nushell:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo build --release && cargo run --release
|
cargo run
|
||||||
```
|
```
|
||||||
|
|
||||||
- Build and run with extra features:
|
- Build and run with extra features. Currently extra features include dataframes and sqlite database support.
|
||||||
```shell
|
```shell
|
||||||
cargo build --release --features=extra && cargo run --release --features=extra
|
cargo run --features=extra
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run Clippy on Nushell:
|
- Run Clippy on Nushell:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo clippy --all --features=stable
|
cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run all tests:
|
- Run all tests:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo test --all --features=stable
|
cargo test --workspace --features=extra
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run all tests for a specific command
|
- Run all tests for a specific command
|
||||||
@ -71,5 +64,5 @@ cargo build
|
|||||||
- To view verbose logs when developing, enable the `trace` log level.
|
- To view verbose logs when developing, enable the `trace` log level.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo build --release --features=extra && cargo run --release --features=extra -- --log-level trace
|
cargo run --release --features=extra -- --log-level trace
|
||||||
```
|
```
|
||||||
|
291
Cargo.lock
generated
291
Cargo.lock
generated
@ -80,9 +80,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi-str"
|
name = "ansi-str"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90cb0ceb8c444d026166795e474e9dfe54b443bdc07cc21bd6c5073f5e637482"
|
checksum = "e04d04a41a1463228d6eed971b9cdadd86b5577c69c09fd2379c57fe5e159071"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi-parser",
|
"ansi-parser",
|
||||||
]
|
]
|
||||||
@ -131,9 +131,9 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrow-format"
|
name = "arrow-format"
|
||||||
version = "0.4.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2333f8ccf0d597ba779863c57a0b61f635721187fb2fdeabae92691d7d582fe5"
|
checksum = "216249afef413d7e9e9b4b543e73b3e371ace3a812380af98f1c871521572cdd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"planus",
|
"planus",
|
||||||
"serde",
|
"serde",
|
||||||
@ -141,26 +141,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrow2"
|
name = "arrow2"
|
||||||
version = "0.11.2"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b040061368d1314b0fd8b8f1fde0671eba1afc63a1c61a4dafaf2d4fc10c96f9"
|
checksum = "5feafd6df4e3f577529e6aa2b9b7cdb3c9fe8e8f66ebc8dc29abbe71a7e968f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow-format",
|
"arrow-format",
|
||||||
"base64",
|
"base64",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"chrono",
|
"chrono",
|
||||||
"csv-core",
|
|
||||||
"either",
|
"either",
|
||||||
"fallible-streaming-iterator",
|
"fallible-streaming-iterator",
|
||||||
"futures",
|
"futures",
|
||||||
"hash_hasher",
|
"hash_hasher",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
"json-deserializer",
|
||||||
"lexical-core",
|
"lexical-core",
|
||||||
"multiversion",
|
"multiversion",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"parquet2",
|
"parquet2",
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"simdutf8",
|
"simdutf8",
|
||||||
"streaming-iterator",
|
"streaming-iterator",
|
||||||
"strength_reduce",
|
"strength_reduce",
|
||||||
@ -215,9 +213,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.53"
|
version = "0.1.56"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600"
|
checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -375,9 +373,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.9.1"
|
version = "3.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
|
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byte-unit"
|
name = "byte-unit"
|
||||||
@ -561,9 +559,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_format"
|
name = "const_format"
|
||||||
version = "0.2.23"
|
version = "0.2.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0936ffe6d0c8d6a51b3b0a73b2acbe925d786f346cf45bfddc8341d79fb7dc8a"
|
checksum = "e6a1316fa6a23fea1ee41cb80321590385e5f3e575e99f77c4d918ce5d44379d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const_format_proc_macros",
|
"const_format_proc_macros",
|
||||||
]
|
]
|
||||||
@ -603,9 +601,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m"
|
name = "cortex-m"
|
||||||
version = "0.7.4"
|
version = "0.7.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37ff967e867ca14eba0c34ac25cd71ea98c678e741e3915d923999bb2fe7c826"
|
checksum = "cd20d4ac4aa86f4f75f239d59e542ef67de87cce2c282818dc6e84155d3ea126"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 0.2.5",
|
"bare-metal 0.2.5",
|
||||||
"bitfield",
|
"bitfield",
|
||||||
@ -698,7 +696,7 @@ dependencies = [
|
|||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
"libc",
|
"libc",
|
||||||
"mio 0.8.3",
|
"mio 0.8.3",
|
||||||
"parking_lot 0.12.0",
|
"parking_lot 0.12.1",
|
||||||
"serde",
|
"serde",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"signal-hook-mio",
|
"signal-hook-mio",
|
||||||
@ -1128,13 +1126,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.23"
|
version = "1.0.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af"
|
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"libc",
|
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1361,6 +1357,16 @@ dependencies = [
|
|||||||
"version_check 0.9.4",
|
"version_check 0.9.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gethostname"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getopts"
|
name = "getopts"
|
||||||
version = "0.2.21"
|
version = "0.2.21"
|
||||||
@ -1649,9 +1655,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.18"
|
version = "0.14.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2"
|
checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
@ -1712,9 +1718,9 @@ checksum = "0cfe9645a18782869361d9c8732246be7b410ad4e919d3609ebabdac00ba12c3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.8.1"
|
version = "1.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown 0.11.2",
|
"hashbrown 0.11.2",
|
||||||
@ -1791,6 +1797,16 @@ version = "2.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
|
checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-root"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04a4202a60e86f1c9702706bb42270dadd333f2db7810157563c86f17af3c873"
|
||||||
|
dependencies = [
|
||||||
|
"users 0.10.0",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_ci"
|
name = "is_ci"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
@ -1857,6 +1873,12 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "json-deserializer"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47631885425c482fcf2dc4b182fc973c3c5b81a8f43a028055559bd24cccfa6e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kernel32-sys"
|
name = "kernel32-sys"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@ -1914,9 +1936,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lexical-parse-integer"
|
name = "lexical-parse-integer"
|
||||||
version = "0.8.5"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "125e1f93e5003d4bd89758c2ca2771bfae13632df633cde581efe07c87d354e5"
|
checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lexical-util",
|
"lexical-util",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
@ -2015,9 +2037,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libz-sys"
|
name = "libz-sys"
|
||||||
version = "1.1.6"
|
version = "1.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e"
|
checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
@ -2142,9 +2164,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memmap2"
|
name = "memmap2"
|
||||||
version = "0.5.3"
|
version = "0.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f"
|
checksum = "d5172b50c23043ff43dd53e51392f36519d9b35a8f3a410d30ece5d1aedd58ae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@ -2181,13 +2203,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miette"
|
name = "miette"
|
||||||
version = "4.7.1"
|
version = "5.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c90329e44f9208b55f45711f9558cec15d7ef8295cc65ecd6d4188ae8edc58c"
|
checksum = "6ec753a43fd71bb5f28751c9ec17fbe89d6d26ca8282d1e1f82f5ac3dbd5581e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"miette-derive 4.7.1",
|
"miette-derive 5.1.0",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
"supports-color",
|
"supports-color",
|
||||||
@ -2212,9 +2234,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miette-derive"
|
name = "miette-derive"
|
||||||
version = "4.7.1"
|
version = "5.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b5bc45b761bcf1b5e6e6c4128cd93b84c218721a8d9b894aa0aff4ed180174c"
|
checksum = "fdfc33ea15c5446600f91d319299dd40301614afff7143cdfa9bf4c09da3ca64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2235,9 +2257,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.5.1"
|
version = "0.5.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
|
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
]
|
]
|
||||||
@ -2452,7 +2474,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu"
|
name = "nu"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -2462,7 +2484,7 @@ dependencies = [
|
|||||||
"is_executable",
|
"is_executable",
|
||||||
"itertools",
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
"miette 4.7.1",
|
"miette 5.1.0",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"nu-cli",
|
"nu-cli",
|
||||||
"nu-color-config",
|
"nu-color-config",
|
||||||
@ -2486,15 +2508,16 @@ dependencies = [
|
|||||||
"reedline",
|
"reedline",
|
||||||
"rstest",
|
"rstest",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
|
"signal-hook",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"winres",
|
"winres",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.45.1"
|
version = "0.46.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7bca0d33a384280d1563b97f49cb95303df9fa22588739a04b7d8015c1ccd50"
|
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"overload",
|
"overload",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
@ -2502,13 +2525,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"fuzzy-matcher",
|
"fuzzy-matcher",
|
||||||
"is_executable",
|
"is_executable",
|
||||||
"log",
|
"log",
|
||||||
"miette 4.7.1",
|
"miette 5.1.0",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"nu-color-config",
|
"nu-color-config",
|
||||||
"nu-command",
|
"nu-command",
|
||||||
@ -2519,12 +2543,13 @@ dependencies = [
|
|||||||
"nu-test-support",
|
"nu-test-support",
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"reedline",
|
"reedline",
|
||||||
|
"sysinfo 0.24.1",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-color-config"
|
name = "nu-color-config"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"nu-json",
|
"nu-json",
|
||||||
@ -2535,11 +2560,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-command"
|
name = "nu-command"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Inflector",
|
"Inflector",
|
||||||
"alphanumeric-sort",
|
"alphanumeric-sort",
|
||||||
"base64",
|
"base64",
|
||||||
|
"byteorder",
|
||||||
"bytesize",
|
"bytesize",
|
||||||
"calamine",
|
"calamine",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -2560,6 +2586,7 @@ dependencies = [
|
|||||||
"htmlescape",
|
"htmlescape",
|
||||||
"ical",
|
"ical",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
"is-root",
|
||||||
"itertools",
|
"itertools",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
@ -2595,6 +2622,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
|
"rstest",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"serde",
|
"serde",
|
||||||
@ -2603,9 +2631,10 @@ dependencies = [
|
|||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"sha2 0.10.2",
|
"sha2 0.10.2",
|
||||||
"shadow-rs",
|
"shadow-rs",
|
||||||
|
"signal-hook",
|
||||||
"sqlparser",
|
"sqlparser",
|
||||||
"strip-ansi-escapes",
|
"strip-ansi-escapes",
|
||||||
"sysinfo",
|
"sysinfo 0.23.13",
|
||||||
"terminal_size",
|
"terminal_size",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"titlecase",
|
"titlecase",
|
||||||
@ -2614,27 +2643,28 @@ dependencies = [
|
|||||||
"umask",
|
"umask",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"url",
|
"url",
|
||||||
"users",
|
"users 0.11.0",
|
||||||
"uuid",
|
"uuid",
|
||||||
"wax",
|
"wax",
|
||||||
"which",
|
"which",
|
||||||
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-engine"
|
name = "nu-engine"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"nu-glob",
|
"nu-glob",
|
||||||
"nu-path",
|
"nu-path",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"sysinfo",
|
"sysinfo 0.23.13",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-glob"
|
name = "nu-glob"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"doc-comment",
|
"doc-comment",
|
||||||
"tempdir",
|
"tempdir",
|
||||||
@ -2642,7 +2672,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-json"
|
name = "nu-json"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
@ -2655,11 +2685,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-parser"
|
name = "nu-parser"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
"miette 4.7.1",
|
"miette 5.1.0",
|
||||||
"nu-path",
|
"nu-path",
|
||||||
"nu-plugin",
|
"nu-plugin",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
@ -2669,7 +2700,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-path"
|
name = "nu-path"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs-next",
|
"dirs-next",
|
||||||
"dunce",
|
"dunce",
|
||||||
@ -2678,7 +2709,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-plugin"
|
name = "nu-plugin"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"capnp",
|
"capnp",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
@ -2689,7 +2720,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-pretty-hex"
|
name = "nu-pretty-hex"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heapless 0.7.13",
|
"heapless 0.7.13",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
@ -2698,13 +2729,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-protocol"
|
name = "nu-protocol"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byte-unit",
|
"byte-unit",
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-humanize",
|
"chrono-humanize",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"miette 4.7.1",
|
"miette 5.1.0",
|
||||||
"nu-json",
|
"nu-json",
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"num-format",
|
"num-format",
|
||||||
@ -2718,7 +2749,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-system"
|
name = "nu-system"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"errno",
|
"errno",
|
||||||
@ -2732,7 +2763,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-table"
|
name = "nu-table"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi-str",
|
"ansi-str",
|
||||||
"atty",
|
"atty",
|
||||||
@ -2745,7 +2776,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-term-grid"
|
name = "nu-term-grid"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"strip-ansi-escapes",
|
"strip-ansi-escapes",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
@ -2753,7 +2784,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-test-support"
|
name = "nu-test-support"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getset",
|
"getset",
|
||||||
"hamcrest2",
|
"hamcrest2",
|
||||||
@ -2765,14 +2796,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-utils"
|
name = "nu-utils"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_example"
|
name = "nu_plugin_example"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-plugin",
|
"nu-plugin",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
@ -2780,7 +2811,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_gstat"
|
name = "nu_plugin_gstat"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"git2",
|
"git2",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
@ -2790,7 +2821,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_inc"
|
name = "nu_plugin_inc"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-plugin",
|
"nu-plugin",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
@ -2799,7 +2830,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_query"
|
name = "nu_plugin_query"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gjson",
|
"gjson",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
@ -3028,9 +3059,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.73"
|
version = "0.9.74"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d5fd19fb3e0a8191c1e34935718976a3e70c112ab9a24af6d7cadccd9d90bc0"
|
checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cc",
|
"cc",
|
||||||
@ -3074,9 +3105,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.0"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
|
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"parking_lot_core 0.9.3",
|
"parking_lot_core 0.9.3",
|
||||||
@ -3122,9 +3153,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parquet2"
|
name = "parquet2"
|
||||||
version = "0.12.1"
|
version = "0.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cbacca5619bdee7f942938890451dea1a61f082c682aac913d7b4e326e66d7b4"
|
checksum = "73fd2690ad041f9296876daef1f2706f6347073bdbcc719090887f1691e4a09d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-stream",
|
"async-stream",
|
||||||
"bitpacking",
|
"bitpacking",
|
||||||
@ -3296,9 +3327,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polars"
|
name = "polars"
|
||||||
version = "0.21.1"
|
version = "0.22.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b140da767e129c60c41c8e1968ffab5f114bcf823182edb7fa900464a31bf421"
|
checksum = "3d175c67e80ceaef7219258cfc3a8686531d9510875b0cefa25404e5b80a7933"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"polars-core",
|
"polars-core",
|
||||||
"polars-io",
|
"polars-io",
|
||||||
@ -3309,9 +3340,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polars-arrow"
|
name = "polars-arrow"
|
||||||
version = "0.21.1"
|
version = "0.22.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d27df11ee28956bd6f5aed54e7e05ce87b886871995e1da501134627ec89077"
|
checksum = "f66c7d3da2c10a09131294dbe7802fac792f570be639dc6ebf207bfc3e144287"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow2",
|
"arrow2",
|
||||||
"hashbrown 0.12.1",
|
"hashbrown 0.12.1",
|
||||||
@ -3322,9 +3353,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polars-core"
|
name = "polars-core"
|
||||||
version = "0.21.1"
|
version = "0.22.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fdf8d12cb7ec278516228fc86469f98c62ab81ca31e4e76d2c0ccf5a09c70491"
|
checksum = "f7f15f443a90d5367c4fbbb151e203f03b5b96055c8b928c6bc30655a3644f13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -3333,8 +3364,8 @@ dependencies = [
|
|||||||
"comfy-table",
|
"comfy-table",
|
||||||
"hashbrown 0.12.1",
|
"hashbrown 0.12.1",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"lazy_static",
|
|
||||||
"num 0.4.0",
|
"num 0.4.0",
|
||||||
|
"once_cell",
|
||||||
"polars-arrow",
|
"polars-arrow",
|
||||||
"polars-utils",
|
"polars-utils",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
@ -3348,20 +3379,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polars-io"
|
name = "polars-io"
|
||||||
version = "0.21.1"
|
version = "0.22.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fdd4b762e5694f359ded21ca0627b5bc95b6eb49f6b330569afc1d20f0564b01"
|
checksum = "058d0a847ce5009b974c69ec878ed416e306436f21b626543019f738cee12315"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arrow2",
|
"arrow2",
|
||||||
"csv-core",
|
"csv-core",
|
||||||
"dirs",
|
"dirs",
|
||||||
"lazy_static",
|
|
||||||
"lexical",
|
"lexical",
|
||||||
|
"lexical-core",
|
||||||
"memchr",
|
"memchr",
|
||||||
"memmap2",
|
"memmap2",
|
||||||
"num 0.4.0",
|
"num 0.4.0",
|
||||||
|
"once_cell",
|
||||||
"polars-arrow",
|
"polars-arrow",
|
||||||
"polars-core",
|
"polars-core",
|
||||||
"polars-time",
|
"polars-time",
|
||||||
@ -3375,16 +3407,17 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polars-lazy"
|
name = "polars-lazy"
|
||||||
version = "0.21.1"
|
version = "0.22.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eedc21001f05611e41bb7439b38d0f4ef9406aa49c17f3b289b5f57d8fa40c59"
|
checksum = "dad86a4ce7e32540ff12089bce6f77270fd133a5b263328a92be61defdd6b151"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"glob",
|
"glob",
|
||||||
"parking_lot 0.12.0",
|
"parking_lot 0.12.1",
|
||||||
"polars-arrow",
|
"polars-arrow",
|
||||||
"polars-core",
|
"polars-core",
|
||||||
"polars-io",
|
"polars-io",
|
||||||
|
"polars-ops",
|
||||||
"polars-time",
|
"polars-time",
|
||||||
"polars-utils",
|
"polars-utils",
|
||||||
"rayon",
|
"rayon",
|
||||||
@ -3393,33 +3426,35 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polars-ops"
|
name = "polars-ops"
|
||||||
version = "0.21.1"
|
version = "0.22.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86fae68f0992955f224f09d1f15648a6fb76d8e3b962efac2f97ccc2aa58977a"
|
checksum = "030ecd473be113cd0264f1bc19de39a844fa12fa565db9dc52c859cbc292cf04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"polars-arrow",
|
||||||
"polars-core",
|
"polars-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polars-time"
|
name = "polars-time"
|
||||||
version = "0.21.1"
|
version = "0.22.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "be499f73749e820f96689c5f9ec59669b7cdd551d864358e2bdaebb5944e4bfb"
|
checksum = "94047b20d2da3bcc55c421be187a0c6f316cf1eea7fe7ed7347c1160a32d017c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"lexical",
|
"lexical",
|
||||||
"polars-arrow",
|
"polars-arrow",
|
||||||
"polars-core",
|
"polars-core",
|
||||||
|
"polars-utils",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polars-utils"
|
name = "polars-utils"
|
||||||
version = "0.21.1"
|
version = "0.22.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7f4cd569d383f5f000abbd6d5146550e6cb4e43fac30d1af98699499a440d56"
|
checksum = "fcd3d0238462d5d9f7fbeaaea46e73ed4d58f6fae8b70d53cbe51d7538cc43f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parking_lot 0.12.0",
|
"parking_lot 0.12.1",
|
||||||
"rayon",
|
"rayon",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3793,17 +3828,23 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reedline"
|
name = "reedline"
|
||||||
version = "0.6.0"
|
version = "0.8.0"
|
||||||
source = "git+https://github.com/nushell/reedline?branch=main#fe795caabc5401d811006b93d5a6d4f220a049ff"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d064ec92a21deb048c440b6461ccf0d0babdb5f2160ea9dfac451bc7b4b556e2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"fd-lock",
|
"fd-lock",
|
||||||
|
"gethostname",
|
||||||
|
"itertools",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
|
"rusqlite",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"strip-ansi-escapes",
|
"strip-ansi-escapes",
|
||||||
"strum 0.24.0",
|
"strum 0.24.0",
|
||||||
"strum_macros 0.24.0",
|
"strum_macros 0.24.0",
|
||||||
|
"thiserror",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
@ -4012,9 +4053,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.34.7"
|
version = "0.34.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f117495127afb702af6706f879fb2b5c008c38ccf3656afc514e26f35bdb8180"
|
checksum = "2079c267b8394eb529872c3cf92e181c378b41fea36e68130357b52493701d2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
@ -4196,7 +4237,6 @@ version = "1.0.81"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
|
||||||
"itoa 1.0.2",
|
"itoa 1.0.2",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
@ -4438,7 +4478,7 @@ checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"new_debug_unreachable",
|
"new_debug_unreachable",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot 0.12.0",
|
"parking_lot 0.12.1",
|
||||||
"phf_shared 0.10.0",
|
"phf_shared 0.10.0",
|
||||||
"precomputed-hash",
|
"precomputed-hash",
|
||||||
"serde",
|
"serde",
|
||||||
@ -4554,9 +4594,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.95"
|
version = "1.0.96"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
|
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -4603,6 +4643,21 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sysinfo"
|
||||||
|
version = "0.24.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6a8e71535da31837213ac114531d31def75d7aebd133264e420a3451fa7f703"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
"ntapi",
|
||||||
|
"once_cell",
|
||||||
|
"rayon",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempdir"
|
name = "tempdir"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
@ -4747,9 +4802,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.18.2"
|
version = "1.19.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395"
|
checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
@ -4774,9 +4829,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.2"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c"
|
checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@ -4809,21 +4864,9 @@ checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tracing-attributes",
|
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-attributes"
|
|
||||||
version = "0.1.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.26"
|
version = "0.1.26"
|
||||||
@ -4972,6 +5015,16 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "users"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "users"
|
name = "users"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
41
Cargo.toml
41
Cargo.toml
@ -11,7 +11,7 @@ name = "nu"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/nushell/nushell"
|
repository = "https://github.com/nushell/nushell"
|
||||||
rust-version = "1.60"
|
rust-version = "1.60"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -32,37 +32,38 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.19"
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
crossterm = "0.23.0"
|
crossterm = "0.23.0"
|
||||||
ctrlc = "3.2.1"
|
ctrlc = "3.2.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = "4.5.0"
|
miette = "5.1.0"
|
||||||
nu-ansi-term = "0.45.1"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-cli = { path="./crates/nu-cli", version = "0.63.1" }
|
nu-cli = { path="./crates/nu-cli", version = "0.65.0" }
|
||||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.63.1" }
|
nu-color-config = { path = "./crates/nu-color-config", version = "0.65.0" }
|
||||||
nu-command = { path="./crates/nu-command", version = "0.63.1" }
|
nu-command = { path="./crates/nu-command", version = "0.65.0" }
|
||||||
nu-engine = { path="./crates/nu-engine", version = "0.63.1" }
|
nu-engine = { path="./crates/nu-engine", version = "0.65.0" }
|
||||||
nu-json = { path="./crates/nu-json", version = "0.63.1" }
|
nu-json = { path="./crates/nu-json", version = "0.65.0" }
|
||||||
nu-parser = { path="./crates/nu-parser", version = "0.63.1" }
|
nu-parser = { path="./crates/nu-parser", version = "0.65.0" }
|
||||||
nu-path = { path="./crates/nu-path", version = "0.63.1" }
|
nu-path = { path="./crates/nu-path", version = "0.65.0" }
|
||||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.63.1" }
|
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.65.0" }
|
||||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.63.1" }
|
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.65.0" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.63.1" }
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.65.0" }
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.63.1" }
|
nu-system = { path = "./crates/nu-system", version = "0.65.0" }
|
||||||
nu-table = { path = "./crates/nu-table", version = "0.63.1" }
|
nu-table = { path = "./crates/nu-table", version = "0.65.0" }
|
||||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.63.1" }
|
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.65.0" }
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.63.1" }
|
nu-utils = { path = "./crates/nu-utils", version = "0.65.0" }
|
||||||
|
reedline = { version = "0.8.0", features = ["bashisms", "sqlite"]}
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
rayon = "1.5.1"
|
rayon = "1.5.1"
|
||||||
reedline = { git = "https://github.com/nushell/reedline", branch = "main", features = ["bashisms"]}
|
|
||||||
is_executable = "1.0.1"
|
is_executable = "1.0.1"
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
||||||
# Our dependencies don't use OpenSSL on Windows
|
# Our dependencies don't use OpenSSL on Windows
|
||||||
openssl = { version = "0.10.38", features = ["vendored"], optional = true }
|
openssl = { version = "0.10.38", features = ["vendored"], optional = true }
|
||||||
|
signal-hook = { version = "0.3.14", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path="./crates/nu-test-support", version = "0.63.1" }
|
nu-test-support = { path="./crates/nu-test-support", version = "0.65.0" }
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
assert_cmd = "2.0.2"
|
assert_cmd = "2.0.2"
|
||||||
pretty_assertions = "1.0.0"
|
pretty_assertions = "1.0.0"
|
||||||
|
249
README.md
249
README.md
@ -1,5 +1,4 @@
|
|||||||
# README
|
# Nushell <!-- omit in toc -->
|
||||||
|
|
||||||
[](https://crates.io/crates/nu)
|
[](https://crates.io/crates/nu)
|
||||||

|

|
||||||
[](https://discord.gg/NtAbbGn)
|
[](https://discord.gg/NtAbbGn)
|
||||||
@ -8,128 +7,100 @@
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
## Nushell
|
|
||||||
|
|
||||||
A new type of shell.
|
A new type of shell.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Table of Contents <!-- omit in toc -->
|
||||||
|
|
||||||
|
- [Status](#status)
|
||||||
|
- [Learning About Nu](#learning-about-nu)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Philosophy](#philosophy)
|
||||||
|
- [Pipelines](#pipelines)
|
||||||
|
- [Opening files](#opening-files)
|
||||||
|
- [Plugins](#plugins)
|
||||||
|
- [Goals](#goals)
|
||||||
|
- [Progress](#progress)
|
||||||
|
- [Officially Supported By](#officially-supported-by)
|
||||||
|
- [Contributing](#contributing)
|
||||||
|
- [License](#license)
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
This project has reached a minimum-viable product level of quality.
|
This project has reached a minimum-viable-product level of quality. Many people use it as their daily driver, but it may be unstable for some commands. Nu's design is subject to change as it matures.
|
||||||
While contributors dogfood it as their daily driver, it may be unstable for some commands.
|
|
||||||
Future releases will work to fill out missing features and improve stability.
|
|
||||||
Its design is also subject to change as it matures.
|
|
||||||
|
|
||||||
Nu comes with a set of built-in commands (listed below).
|
## Learning About Nu
|
||||||
If a command is unknown, the command will shell-out and execute it (using cmd on Windows or bash on Linux and macOS), correctly passing through stdin, stdout, and stderr, so things like your daily git workflows and even `vim` will work just fine.
|
|
||||||
|
|
||||||
## Learning more
|
The [Nushell book](https://www.nushell.sh/book/) is the primary source of Nushell documentation. You can find [a full list of Nu commands in the book](https://www.nushell.sh/book/command_reference.html), and we have many examples of using Nu in our [cookbook](https://www.nushell.sh/cookbook/).
|
||||||
|
|
||||||
There are a few good resources to learn about Nu.
|
We're also active on [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell); come and chat with us!
|
||||||
There is a [book](https://www.nushell.sh/book/) about Nu that is currently in progress.
|
|
||||||
The book focuses on using Nu and its core concepts.
|
|
||||||
|
|
||||||
If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://www.nushell.sh/contributor-book/) to help you get started.
|
|
||||||
There are also [good first issues](https://github.com/nushell/nushell/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) to help you dive in.
|
|
||||||
|
|
||||||
We also have an active [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell) if you'd like to come and chat with us.
|
|
||||||
|
|
||||||
You can also find information on more specific topics in our [cookbook](https://www.nushell.sh/cookbook/).
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Local
|
To quickly install Nu:
|
||||||
|
|
||||||
Up-to-date installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). **Windows users**: please note that Nu works on Windows 10 and does not currently have Windows 7/8.1 support.
|
```bash
|
||||||
|
# Linux and macOS
|
||||||
To build Nu, you will need to use the **latest stable (1.60 or later)** version of the compiler.
|
brew install nushell
|
||||||
|
# Windows
|
||||||
Required dependencies:
|
|
||||||
|
|
||||||
- pkg-config and libssl (only needed on Linux)
|
|
||||||
- On Debian/Ubuntu: `apt install pkg-config libssl-dev`
|
|
||||||
|
|
||||||
Optional dependencies:
|
|
||||||
|
|
||||||
- To use Nu with all possible optional features enabled, you'll also need the following:
|
|
||||||
- On Linux (on Debian/Ubuntu): `apt install libxcb-composite0-dev libx11-dev`
|
|
||||||
|
|
||||||
To install Nu via cargo (make sure you have installed [rustup](https://rustup.rs/) and the latest stable compiler via `rustup install stable`):
|
|
||||||
|
|
||||||
For Windows users, you may also need to install the [Microsoft Visual C++ 2015 Redistributables](https://docs.microsoft.com/cpp/windows/latest-supported-vc-redist).
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cargo install nu
|
|
||||||
```
|
|
||||||
|
|
||||||
To install Nu via the [Windows Package Manager](https://aka.ms/winget-cli):
|
|
||||||
|
|
||||||
```shell
|
|
||||||
winget install nushell
|
winget install nushell
|
||||||
```
|
```
|
||||||
|
|
||||||
To install Nu via the [Chocolatey](https://chocolatey.org) package manager:
|
To use `Nu` in Github Action, check [setup-nu](https://github.com/marketplace/actions/setup-nu) for more detail.
|
||||||
|
|
||||||
```shell
|
Detailed installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). Nu is available via many package managers:
|
||||||
choco install nushell
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also build Nu yourself with all the bells and whistles (be sure to have installed the [dependencies](https://www.nushell.sh/book/installation.html#dependencies) for your platform), once you have checked out this repo with git:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cargo build --workspace --features=extra
|
|
||||||
```
|
|
||||||
### Packaging status
|
|
||||||
|
|
||||||
[](https://repology.org/project/nushell/versions)
|
[](https://repology.org/project/nushell/versions)
|
||||||
|
|
||||||
#### Fedora
|
|
||||||
|
|
||||||
[COPR repo](https://copr.fedorainfracloud.org/coprs/atim/nushell/): `sudo dnf copr enable atim/nushell -y && sudo dnf install nushell -y`
|
|
||||||
|
|
||||||
## Philosophy
|
## Philosophy
|
||||||
|
|
||||||
Nu draws inspiration from projects like PowerShell, functional programming languages, and modern CLI tools.
|
Nu draws inspiration from projects like PowerShell, functional programming languages, and modern CLI tools.
|
||||||
Rather than thinking of files and services as raw streams of text, Nu looks at each input as something with structure.
|
Rather than thinking of files and data as raw streams of text, Nu looks at each input as something with structure.
|
||||||
For example, when you list the contents of a directory, what you get back is a table of rows, where each row represents an item in that directory.
|
For example, when you list the contents of a directory what you get back is a table of rows, where each row represents an item in that directory.
|
||||||
These values can be piped through a series of steps, in a series of commands called a 'pipeline'.
|
These values can be piped through a series of steps, in a series of commands called a 'pipeline'.
|
||||||
|
|
||||||
### Pipelines
|
### Pipelines
|
||||||
|
|
||||||
In Unix, it's common to pipe between commands to split up a sophisticated command over multiple steps.
|
In Unix, it's common to pipe between commands to split up a sophisticated command over multiple steps.
|
||||||
Nu takes this a step further and builds heavily on the idea of _pipelines_.
|
Nu takes this a step further and builds heavily on the idea of _pipelines_.
|
||||||
Just as the Unix philosophy, Nu allows commands to output to stdout and read from stdin.
|
As in the Unix philosophy, Nu allows commands to output to stdout and read from stdin.
|
||||||
Additionally, commands can output structured data (you can think of this as a third kind of stream).
|
Additionally, commands can output structured data (you can think of this as a third kind of stream).
|
||||||
Commands that work in the pipeline fit into one of three categories:
|
Commands that work in the pipeline fit into one of three categories:
|
||||||
|
|
||||||
- Commands that produce a stream (e.g., `ls`)
|
- Commands that produce a stream (e.g., `ls`)
|
||||||
- Commands that filter a stream (eg, `where type == "Dir"`)
|
- Commands that filter a stream (eg, `where type == "dir"`)
|
||||||
- Commands that consume the output of the pipeline (e.g., `autoview`)
|
- Commands that consume the output of the pipeline (e.g., `table`)
|
||||||
|
|
||||||
Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing left to right.
|
Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing left to right.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
> ls | where type == "Dir" | autoview
|
> ls | where type == "dir" | table
|
||||||
───┬────────┬──────┬───────┬──────────────
|
╭────┬──────────┬──────┬─────────┬───────────────╮
|
||||||
# │ name │ type │ size │ modified
|
│ # │ name │ type │ size │ modified │
|
||||||
───┼────────┼──────┼───────┼──────────────
|
├────┼──────────┼──────┼─────────┼───────────────┤
|
||||||
0 │ assets │ Dir │ 128 B │ 5 months ago
|
│ 0 │ .cargo │ dir │ 0 B │ 9 minutes ago │
|
||||||
1 │ crates │ Dir │ 704 B │ 50 mins ago
|
│ 1 │ assets │ dir │ 0 B │ 2 weeks ago │
|
||||||
2 │ debian │ Dir │ 352 B │ 5 months ago
|
│ 2 │ crates │ dir │ 4.0 KiB │ 2 weeks ago │
|
||||||
3 │ docs │ Dir │ 192 B │ 50 mins ago
|
│ 3 │ docker │ dir │ 0 B │ 2 weeks ago │
|
||||||
4 │ images │ Dir │ 160 B │ 5 months ago
|
│ 4 │ docs │ dir │ 0 B │ 2 weeks ago │
|
||||||
5 │ src │ Dir │ 128 B │ 1 day ago
|
│ 5 │ images │ dir │ 0 B │ 2 weeks ago │
|
||||||
6 │ target │ Dir │ 160 B │ 5 days ago
|
│ 6 │ pkg_mgrs │ dir │ 0 B │ 2 weeks ago │
|
||||||
7 │ tests │ Dir │ 192 B │ 3 months ago
|
│ 7 │ samples │ dir │ 0 B │ 2 weeks ago │
|
||||||
───┴────────┴──────┴───────┴──────────────
|
│ 8 │ src │ dir │ 4.0 KiB │ 2 weeks ago │
|
||||||
|
│ 9 │ target │ dir │ 0 B │ a day ago │
|
||||||
|
│ 10 │ tests │ dir │ 4.0 KiB │ 2 weeks ago │
|
||||||
|
│ 11 │ wix │ dir │ 0 B │ 2 weeks ago │
|
||||||
|
╰────┴──────────┴──────┴─────────┴───────────────╯
|
||||||
```
|
```
|
||||||
|
|
||||||
Because most of the time you'll want to see the output of a pipeline, `autoview` is assumed.
|
Because most of the time you'll want to see the output of a pipeline, `table` is assumed.
|
||||||
We could have also written the above:
|
We could have also written the above:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
> ls | where type == Dir
|
> ls | where type == "dir"
|
||||||
```
|
```
|
||||||
|
|
||||||
Being able to use the same commands and compose them differently is an important philosophy in Nu.
|
Being able to use the same commands and compose them differently is an important philosophy in Nu.
|
||||||
@ -137,15 +108,13 @@ For example, we could use the built-in `ps` command to get a list of the running
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
> ps | where cpu > 0
|
> ps | where cpu > 0
|
||||||
───┬────────┬───────────────────┬──────────┬─────────┬──────────┬──────────
|
╭───┬───────┬───────────┬───────┬───────────┬───────────╮
|
||||||
# │ pid │ name │ status │ cpu │ mem │ virtual
|
│ # │ pid │ name │ cpu │ mem │ virtual │
|
||||||
───┼────────┼───────────────────┼──────────┼─────────┼──────────┼──────────
|
├───┼───────┼───────────┼───────┼───────────┼───────────┤
|
||||||
0 │ 435 │ irq/142-SYNA327 │ Sleeping │ 7.5699 │ 0 B │ 0 B
|
│ 0 │ 2240 │ Slack.exe │ 16.40 │ 178.3 MiB │ 232.6 MiB │
|
||||||
1 │ 1609 │ pulseaudio │ Sleeping │ 6.5605 │ 10.6 MB │ 2.3 GB
|
│ 1 │ 16948 │ Slack.exe │ 16.32 │ 205.0 MiB │ 197.9 MiB │
|
||||||
2 │ 1625 │ gnome-shell │ Sleeping │ 6.5684 │ 639.6 MB │ 7.3 GB
|
│ 2 │ 17700 │ nu.exe │ 3.77 │ 26.1 MiB │ 8.8 MiB │
|
||||||
3 │ 2202 │ Web Content │ Sleeping │ 6.8157 │ 320.8 MB │ 3.0 GB
|
╰───┴───────┴───────────┴───────┴───────────┴───────────╯
|
||||||
4 │ 328788 │ nu_plugin_core_ps │ Sleeping │ 92.5750 │ 5.9 MB │ 633.2 MB
|
|
||||||
───┴────────┴───────────────────┴──────────┴─────────┴──────────┴──────────
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Opening files
|
### Opening files
|
||||||
@ -155,72 +124,49 @@ For example, you can load a .toml file as structured data and explore it:
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
> open Cargo.toml
|
> open Cargo.toml
|
||||||
────────────────────┬───────────────────────────
|
╭──────────────────┬────────────────────╮
|
||||||
bin │ [table 18 rows]
|
│ bin │ [table 1 row] │
|
||||||
build-dependencies │ [row serde toml]
|
│ dependencies │ {record 24 fields} │
|
||||||
dependencies │ [row 29 columns]
|
│ dev-dependencies │ {record 8 fields} │
|
||||||
dev-dependencies │ [row nu-test-support]
|
│ features │ {record 10 fields} │
|
||||||
features │ [row 19 columns]
|
│ package │ {record 13 fields} │
|
||||||
package │ [row 12 columns]
|
│ profile │ {record 3 fields} │
|
||||||
workspace │ [row members]
|
│ target │ {record 2 fields} │
|
||||||
────────────────────┴───────────────────────────
|
│ workspace │ {record 1 field} │
|
||||||
|
╰──────────────────┴────────────────────╯
|
||||||
```
|
```
|
||||||
|
|
||||||
We can pipeline this into a command that gets the contents of one of the columns:
|
We can pipe this into a command that gets the contents of one of the columns:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
> open Cargo.toml | get package
|
> open Cargo.toml | get package
|
||||||
───────────────┬────────────────────────────────────
|
╭───────────────┬────────────────────────────────────╮
|
||||||
authors │ [table 1 rows]
|
│ authors │ [list 1 item] │
|
||||||
default-run │ nu
|
│ default-run │ nu │
|
||||||
description │ A new type of shell
|
│ description │ A new type of shell │
|
||||||
documentation │ https://www.nushell.sh/book/
|
│ documentation │ https://www.nushell.sh/book/ │
|
||||||
edition │ 2018
|
│ edition │ 2018 │
|
||||||
exclude │ [table 1 rows]
|
│ exclude │ [list 1 item] │
|
||||||
homepage │ https://www.nushell.sh
|
│ homepage │ https://www.nushell.sh │
|
||||||
license │ MIT
|
│ license │ MIT │
|
||||||
name │ nu
|
│ name │ nu │
|
||||||
readme │ README.md
|
│ readme │ README.md │
|
||||||
repository │ https://github.com/nushell/nushell
|
│ repository │ https://github.com/nushell/nushell │
|
||||||
version │ 0.32.0
|
│ rust-version │ 1.60 │
|
||||||
───────────────┴────────────────────────────────────
|
│ version │ 0.63.1 │
|
||||||
|
╰───────────────┴────────────────────────────────────╯
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, we can use commands outside of Nu once we have the data we want:
|
And if needed we can drill down further:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
> open Cargo.toml | get package.version
|
> open Cargo.toml | get package.version
|
||||||
0.32.0
|
0.63.1
|
||||||
```
|
```
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
Nu has early support for configuring the shell. You can refer to the book for a list of [all supported variables](https://www.nushell.sh/book/configuration.html).
|
|
||||||
|
|
||||||
To set one of these variables, you can use `config set`. For example:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
> config set line_editor.edit_mode "vi"
|
|
||||||
> config set path $nu.path
|
|
||||||
```
|
|
||||||
|
|
||||||
### Shells
|
|
||||||
|
|
||||||
Nu will work inside of a single directory and allow you to navigate around your filesystem by default.
|
|
||||||
Nu also offers a way of adding additional working directories that you can jump between, allowing you to work in multiple directories simultaneously.
|
|
||||||
|
|
||||||
To do so, use the `enter` command, which will allow you to create a new "shell" and enter it at the specified path.
|
|
||||||
You can toggle between this new shell and the original shell with the `p` (for previous) and `n` (for next), allowing you to navigate around a ring buffer of shells.
|
|
||||||
Once you're done with a shell, you can `exit` it and remove it from the ring buffer.
|
|
||||||
|
|
||||||
Finally, to get a list of all the current shells, you can use the `shells` command.
|
|
||||||
|
|
||||||
### Plugins
|
### Plugins
|
||||||
|
|
||||||
Nu supports plugins that offer additional functionality to the shell and follow the same structured data model that built-in commands use.
|
Nu supports plugins that offer additional functionality to the shell and follow the same structured data model that built-in commands use. There are a few examples in the `crates/nu_plugins_*` directories.
|
||||||
This allows you to extend nu for your needs.
|
|
||||||
|
|
||||||
There are a few examples in the `plugins` directory.
|
|
||||||
|
|
||||||
Plugins are binaries that are available in your path and follow a `nu_plugin_*` naming convention.
|
Plugins are binaries that are available in your path and follow a `nu_plugin_*` naming convention.
|
||||||
These binaries interact with nu via a simple JSON-RPC protocol where the command identifies itself and passes along its configuration, making it available for use.
|
These binaries interact with nu via a simple JSON-RPC protocol where the command identifies itself and passes along its configuration, making it available for use.
|
||||||
@ -231,23 +177,19 @@ If the plugin is a sink, it is given the full vector of final data and is given
|
|||||||
|
|
||||||
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.
|
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.
|
||||||
|
|
||||||
- First and foremost, Nu is cross-platform. Commands and techniques should carry between platforms and offer consistent first-class support for Windows, macOS, and Linux.
|
- First and foremost, Nu is cross-platform. Commands and techniques should work across platforms and Nu has first-class support for Windows, macOS, and Linux.
|
||||||
|
|
||||||
- Nu ensures direct compatibility with existing platform-specific executables that make up people's workflows.
|
- Nu ensures compatibility with existing platform-specific executables.
|
||||||
|
|
||||||
- Nu's workflow and tools should have the usability in day-to-day experience of using a shell in 2019 (and beyond).
|
- Nu's workflow and tools should have the usability expected of modern software in 2022 (and beyond).
|
||||||
|
|
||||||
- Nu views data as both structured and unstructured. It is a structured shell like PowerShell.
|
- Nu views data as either structured or unstructured. It is a structured shell like PowerShell.
|
||||||
|
|
||||||
- Finally, Nu views data functionally. Rather than using mutation, pipelines act as a means to load, change, and save data without mutable state.
|
- Finally, Nu views data functionally. Rather than using mutation, pipelines act as a means to load, change, and save data without mutable state.
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
You can find a list of Nu commands, complete with documentation, in [quick command references](https://www.nushell.sh/book/command_reference.html).
|
|
||||||
|
|
||||||
## Progress
|
## Progress
|
||||||
|
|
||||||
Nu is in heavy development and will naturally change as it matures and people use it. The chart below isn't meant to be exhaustive, but rather helps give an idea for some of the areas of development and their relative completion:
|
Nu is under heavy development and will naturally change as it matures. The chart below isn't meant to be exhaustive, but it helps give an idea for some of the areas of development and their relative maturity:
|
||||||
|
|
||||||
| Features | Not started | Prototype | MVP | Preview | Mature | Notes |
|
| Features | Not started | Prototype | MVP | Preview | Mature | Notes |
|
||||||
| ------------- | :---------: | :-------: | :-: | :-----: | :----: | -------------------------------------------------------------------- |
|
| ------------- | :---------: | :-------: | :-: | :-----: | :----: | -------------------------------------------------------------------- |
|
||||||
@ -270,20 +212,15 @@ Nu is in heavy development and will naturally change as it matures and people us
|
|||||||
|
|
||||||
Please submit an issue or PR to be added to this list.
|
Please submit an issue or PR to be added to this list.
|
||||||
|
|
||||||
### Integrations
|
|
||||||
- [zoxide](https://github.com/ajeetdsouza/zoxide)
|
- [zoxide](https://github.com/ajeetdsouza/zoxide)
|
||||||
- [starship](https://github.com/starship/starship)
|
- [starship](https://github.com/starship/starship)
|
||||||
- [oh-my-posh](https://ohmyposh.dev)
|
- [oh-my-posh](https://ohmyposh.dev)
|
||||||
- [Couchbase Shell](https://couchbase.sh)
|
- [Couchbase Shell](https://couchbase.sh)
|
||||||
- [virtualenv](https://github.com/pypa/virtualenv)
|
- [virtualenv](https://github.com/pypa/virtualenv)
|
||||||
### Mentions
|
|
||||||
- [The Python Launcher for Unix](https://github.com/brettcannon/python-launcher#how-do-i-get-a-table-of-python-executables-in-nushell)
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
See [Contributing](CONTRIBUTING.md) for details.
|
See [Contributing](CONTRIBUTING.md) for details. Thanks to all the people who already contributed!
|
||||||
|
|
||||||
Thanks to all the people who already contributed!
|
|
||||||
|
|
||||||
<a href="https://github.com/nushell/nushell/graphs/contributors">
|
<a href="https://github.com/nushell/nushell/graphs/contributors">
|
||||||
<img src="https://contributors-img.web.app/image?repo=nushell/nushell&max=500" />
|
<img src="https://contributors-img.web.app/image?repo=nushell/nushell&max=500" />
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 7.3 KiB |
@ -4,28 +4,30 @@ description = "CLI-related functionality for Nushell"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path="../nu-test-support", version = "0.63.1" }
|
nu-test-support = { path="../nu-test-support", version = "0.65.0" }
|
||||||
nu-command = { path = "../nu-command", version = "0.63.1" }
|
nu-command = { path = "../nu-command", version = "0.65.0" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.63.1" }
|
nu-engine = { path = "../nu-engine", version = "0.65.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.63.1" }
|
nu-path = { path = "../nu-path", version = "0.65.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.63.1" }
|
nu-parser = { path = "../nu-parser", version = "0.65.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.63.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.65.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.63.1" }
|
nu-utils = { path = "../nu-utils", version = "0.65.0" }
|
||||||
nu-ansi-term = "0.45.1"
|
nu-ansi-term = "0.46.0"
|
||||||
reedline = { git = "https://github.com/nushell/reedline", branch = "main", features = ["bashisms"]}
|
nu-color-config = { path = "../nu-color-config", version = "0.65.0" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.63.1" }
|
reedline = { version = "0.8.0", features = ["bashisms", "sqlite"]}
|
||||||
crossterm = "0.23.0"
|
crossterm = "0.23.0"
|
||||||
miette = { version = "4.5.0", features = ["fancy"] }
|
miette = { version = "5.1.0", features = ["fancy"] }
|
||||||
thiserror = "1.0.29"
|
thiserror = "1.0.31"
|
||||||
fuzzy-matcher = "0.3.7"
|
fuzzy-matcher = "0.3.7"
|
||||||
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
is_executable = "1.0.1"
|
is_executable = "1.0.1"
|
||||||
|
chrono = "0.4.19"
|
||||||
|
sysinfo = "0.24.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = []
|
plugin = []
|
||||||
|
@ -18,7 +18,7 @@ pub fn evaluate_commands(
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
is_perf_true: bool,
|
is_perf_true: bool,
|
||||||
table_mode: Option<Value>,
|
table_mode: Option<Value>,
|
||||||
) -> Result<()> {
|
) -> Result<Option<i64>> {
|
||||||
// Run a command (or commands) given to us by the user
|
// Run a command (or commands) given to us by the user
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
if let Some(ref t_mode) = table_mode {
|
if let Some(ref t_mode) = table_mode {
|
||||||
@ -74,7 +74,7 @@ pub fn evaluate_commands(
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
match eval_block(engine_state, stack, &block, input, false, false) {
|
let exit_code = match eval_block(engine_state, stack, &block, input, false, false) {
|
||||||
Ok(pipeline_data) => {
|
Ok(pipeline_data) => {
|
||||||
crate::eval_file::print_table_or_error(engine_state, stack, pipeline_data, &mut config)
|
crate::eval_file::print_table_or_error(engine_state, stack, pipeline_data, &mut config)
|
||||||
}
|
}
|
||||||
@ -84,11 +84,11 @@ pub fn evaluate_commands(
|
|||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if is_perf_true {
|
if is_perf_true {
|
||||||
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(exit_code)
|
||||||
}
|
}
|
||||||
|
@ -199,12 +199,19 @@ impl Completer for CommandCompletion {
|
|||||||
return subcommands;
|
return subcommands;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let config = working_set.get_config();
|
||||||
let commands = if matches!(self.flat_shape, nu_parser::FlatShape::External)
|
let commands = if matches!(self.flat_shape, nu_parser::FlatShape::External)
|
||||||
|| matches!(self.flat_shape, nu_parser::FlatShape::InternalCall)
|
|| matches!(self.flat_shape, nu_parser::FlatShape::InternalCall)
|
||||||
|| ((span.end - span.start) == 0)
|
|| ((span.end - span.start) == 0)
|
||||||
{
|
{
|
||||||
// we're in a gap or at a command
|
// we're in a gap or at a command
|
||||||
self.complete_commands(working_set, span, offset, true, options.match_algorithm)
|
self.complete_commands(
|
||||||
|
working_set,
|
||||||
|
span,
|
||||||
|
offset,
|
||||||
|
config.enable_external_completion,
|
||||||
|
options.match_algorithm,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
@ -59,61 +59,39 @@ impl NuCompleter {
|
|||||||
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
|
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let offset = working_set.next_span_start();
|
let offset = working_set.next_span_start();
|
||||||
|
let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set);
|
||||||
let initial_line = line.to_string();
|
let initial_line = line.to_string();
|
||||||
let mut line = line.to_string();
|
new_line.push(b'a');
|
||||||
line.insert(pos, 'a');
|
|
||||||
let pos = offset + pos;
|
let pos = offset + pos;
|
||||||
let (output, _err) = parse(
|
let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]);
|
||||||
&mut working_set,
|
|
||||||
Some("completer"),
|
|
||||||
line.as_bytes(),
|
|
||||||
false,
|
|
||||||
&[],
|
|
||||||
);
|
|
||||||
|
|
||||||
for pipeline in output.pipelines.into_iter() {
|
for pipeline in output.pipelines.into_iter() {
|
||||||
for expr in pipeline.expressions {
|
for expr in pipeline.expressions {
|
||||||
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
||||||
|
let span_offset: usize = alias_offset.iter().sum();
|
||||||
|
|
||||||
for (flat_idx, flat) in flattened.iter().enumerate() {
|
for (flat_idx, flat) in flattened.iter().enumerate() {
|
||||||
if pos >= flat.0.start && pos < flat.0.end {
|
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
|
||||||
// Context variables
|
// Context variables
|
||||||
let most_left_var =
|
let most_left_var =
|
||||||
most_left_variable(flat_idx, &working_set, flattened.clone());
|
most_left_variable(flat_idx, &working_set, flattened.clone());
|
||||||
|
|
||||||
// Create a new span
|
// Create a new span
|
||||||
let new_span = Span {
|
let new_span = if flat_idx == 0 {
|
||||||
|
Span {
|
||||||
start: flat.0.start,
|
start: flat.0.start,
|
||||||
end: flat.0.end - 1,
|
end: flat.0.end - 1 - span_offset,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Span {
|
||||||
|
start: flat.0.start - span_offset,
|
||||||
|
end: flat.0.end - 1 - span_offset,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parses the prefix
|
// Parses the prefix
|
||||||
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
||||||
prefix.remove(pos - flat.0.start);
|
prefix.remove(pos - (flat.0.start - span_offset));
|
||||||
|
|
||||||
// Completions that depends on the previous expression (e.g: use, source)
|
|
||||||
if flat_idx > 0 {
|
|
||||||
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
|
|
||||||
// Read the content for the previous expression
|
|
||||||
let prev_expr_str =
|
|
||||||
working_set.get_span_contents(previous_expr.0).to_vec();
|
|
||||||
|
|
||||||
// Completion for .nu files
|
|
||||||
if prev_expr_str == b"use" || prev_expr_str == b"source" {
|
|
||||||
let mut completer =
|
|
||||||
DotNuCompletion::new(self.engine_state.clone());
|
|
||||||
|
|
||||||
return self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variables completion
|
// Variables completion
|
||||||
if prefix.starts_with(b"$") || most_left_var.is_some() {
|
if prefix.starts_with(b"$") || most_left_var.is_some() {
|
||||||
@ -147,6 +125,42 @@ impl NuCompleter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Completions that depends on the previous expression (e.g: use, source)
|
||||||
|
if flat_idx > 0 {
|
||||||
|
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
|
||||||
|
// Read the content for the previous expression
|
||||||
|
let prev_expr_str =
|
||||||
|
working_set.get_span_contents(previous_expr.0).to_vec();
|
||||||
|
|
||||||
|
// Completion for .nu files
|
||||||
|
if prev_expr_str == b"use" || prev_expr_str == b"source" {
|
||||||
|
let mut completer =
|
||||||
|
DotNuCompletion::new(self.engine_state.clone());
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
} else if prev_expr_str == b"ls" {
|
||||||
|
let mut completer =
|
||||||
|
FileCompletion::new(self.engine_state.clone());
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Match other types
|
// Match other types
|
||||||
match &flat.1 {
|
match &flat.1 {
|
||||||
FlatShape::Custom(decl_id) => {
|
FlatShape::Custom(decl_id) => {
|
||||||
@ -179,6 +193,18 @@ impl NuCompleter {
|
|||||||
pos,
|
pos,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
FlatShape::Filepath | FlatShape::GlobPattern => {
|
||||||
|
let mut completer = FileCompletion::new(self.engine_state.clone());
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
flat_shape => {
|
flat_shape => {
|
||||||
let mut completer = CommandCompletion::new(
|
let mut completer = CommandCompletion::new(
|
||||||
self.engine_state.clone(),
|
self.engine_state.clone(),
|
||||||
@ -229,6 +255,85 @@ impl ReedlineCompleter for NuCompleter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MatchedAlias = Vec<(Vec<u8>, Vec<u8>)>;
|
||||||
|
|
||||||
|
// Handler the completion when giving lines contains at least one alias. (e.g: `g checkout`)
|
||||||
|
// that `g` is an alias of `git`
|
||||||
|
fn try_find_alias(line: &[u8], working_set: &StateWorkingSet) -> (Vec<u8>, Vec<usize>) {
|
||||||
|
// An vector represents the offsets of alias
|
||||||
|
// e.g: the offset is 2 for the alias `g` of `git`
|
||||||
|
let mut alias_offset = vec![];
|
||||||
|
let mut output = vec![];
|
||||||
|
if let Some(matched_alias) = search_alias(line, working_set) {
|
||||||
|
let mut lens = matched_alias.len();
|
||||||
|
for (input_vec, line_vec) in matched_alias {
|
||||||
|
alias_offset.push(line_vec.len() - input_vec.len());
|
||||||
|
output.extend(line_vec);
|
||||||
|
if lens > 1 {
|
||||||
|
output.push(b' ');
|
||||||
|
lens -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !line.is_empty() {
|
||||||
|
let last = line.last().expect("input is empty");
|
||||||
|
if last == &b' ' {
|
||||||
|
output.push(b' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output = line.to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
|
(output, alias_offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_alias(input: &[u8], working_set: &StateWorkingSet) -> Option<MatchedAlias> {
|
||||||
|
let mut vec_names = vec![];
|
||||||
|
let mut vec_alias = vec![];
|
||||||
|
let mut pos = 0;
|
||||||
|
let mut is_alias = false;
|
||||||
|
for (index, character) in input.iter().enumerate() {
|
||||||
|
if *character == b' ' {
|
||||||
|
let range = &input[pos..index];
|
||||||
|
vec_names.push(range.to_owned());
|
||||||
|
pos = index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Push the rest to names vector.
|
||||||
|
if pos < input.len() {
|
||||||
|
vec_names.push((&input[pos..]).to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
for name in &vec_names {
|
||||||
|
if let Some(alias_id) = working_set.find_alias(&name[..]) {
|
||||||
|
let alias_span = working_set.get_alias(alias_id);
|
||||||
|
let mut span_vec = vec![];
|
||||||
|
is_alias = true;
|
||||||
|
for alias in alias_span {
|
||||||
|
let name = working_set.get_span_contents(*alias);
|
||||||
|
if !name.is_empty() {
|
||||||
|
span_vec.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Join span of vector together for complex alias, e.g: `f` is an alias for `git remote -v`
|
||||||
|
let full_aliases = span_vec.join(&[b' '][..]);
|
||||||
|
vec_alias.push(full_aliases);
|
||||||
|
} else {
|
||||||
|
vec_alias.push(name.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_alias {
|
||||||
|
// Zip names and alias vectors, the original inputs and its aliases mapping.
|
||||||
|
// e.g:(['g'], ['g','i','t'])
|
||||||
|
let output = vec_names.into_iter().zip(vec_alias).collect();
|
||||||
|
Some(output)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// reads the most left variable returning it's name (e.g: $myvar)
|
// reads the most left variable returning it's name (e.g: $myvar)
|
||||||
// and the depth (a.b.c)
|
// and the depth (a.b.c)
|
||||||
fn most_left_variable(
|
fn most_left_variable(
|
||||||
|
@ -41,7 +41,7 @@ impl Completer for VariableCompletion {
|
|||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
let builtins = ["$nu", "$in", "$config", "$env", "$nothing"];
|
let builtins = ["$nu", "$in", "$env", "$nothing"];
|
||||||
let var_str = std::str::from_utf8(&self.var_context.0)
|
let var_str = std::str::from_utf8(&self.var_context.0)
|
||||||
.unwrap_or("")
|
.unwrap_or("")
|
||||||
.to_lowercase();
|
.to_lowercase();
|
||||||
|
@ -2,12 +2,15 @@ use crate::util::{eval_source, report_error};
|
|||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use log::info;
|
use log::info;
|
||||||
use nu_protocol::engine::{EngineState, Stack, StateDelta, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, Stack, StateDelta, StateWorkingSet};
|
||||||
use nu_protocol::{PipelineData, Span};
|
use nu_protocol::{HistoryFileFormat, PipelineData, Span};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
const PLUGIN_FILE: &str = "plugin.nu";
|
const PLUGIN_FILE: &str = "plugin.nu";
|
||||||
|
|
||||||
|
const HISTORY_FILE_TXT: &str = "history.txt";
|
||||||
|
const HISTORY_FILE_SQLITE: &str = "history.sqlite3";
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
pub fn read_plugin_file(
|
pub fn read_plugin_file(
|
||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
@ -84,3 +87,14 @@ pub fn eval_config_contents(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_history_path(storage_path: &str, mode: HistoryFileFormat) -> Option<PathBuf> {
|
||||||
|
nu_path::config_dir().map(|mut history_path| {
|
||||||
|
history_path.push(storage_path);
|
||||||
|
history_path.push(match mode {
|
||||||
|
HistoryFileFormat::PlainText => HISTORY_FILE_TXT,
|
||||||
|
HistoryFileFormat::Sqlite => HISTORY_FILE_SQLITE,
|
||||||
|
});
|
||||||
|
history_path
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ use log::trace;
|
|||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use nu_engine::convert_env_values;
|
use nu_engine::convert_env_values;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
|
use nu_protocol::Type;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
@ -34,7 +35,7 @@ pub fn evaluate_file(
|
|||||||
|
|
||||||
let _ = parse(&mut working_set, Some(&path), &file, false, &[]);
|
let _ = parse(&mut working_set, Some(&path), &file, false, &[]);
|
||||||
|
|
||||||
if working_set.find_decl(b"main").is_some() {
|
if working_set.find_decl(b"main", &Type::Any).is_some() {
|
||||||
let args = format!("main {}", args.join(" "));
|
let args = format!("main {}", args.join(" "));
|
||||||
|
|
||||||
if !eval_source(
|
if !eval_source(
|
||||||
@ -65,7 +66,7 @@ pub fn print_table_or_error(
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
mut pipeline_data: PipelineData,
|
mut pipeline_data: PipelineData,
|
||||||
config: &mut Config,
|
config: &mut Config,
|
||||||
) {
|
) -> Option<i64> {
|
||||||
let exit_code = match &mut pipeline_data {
|
let exit_code = match &mut pipeline_data {
|
||||||
PipelineData::ExternalStream { exit_code, .. } => exit_code.take(),
|
PipelineData::ExternalStream { exit_code, .. } => exit_code.take(),
|
||||||
_ => None,
|
_ => None,
|
||||||
@ -129,6 +130,14 @@ pub fn print_table_or_error(
|
|||||||
|
|
||||||
// Make sure everything has finished
|
// Make sure everything has finished
|
||||||
if let Some(exit_code) = exit_code {
|
if let Some(exit_code) = exit_code {
|
||||||
let _: Vec<_> = exit_code.into_iter().collect();
|
let mut exit_code: Vec<_> = exit_code.into_iter().collect();
|
||||||
|
exit_code
|
||||||
|
.pop()
|
||||||
|
.and_then(|last_exit_code| match last_exit_code {
|
||||||
|
Value::Int { val: code, .. } => Some(code),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use nu_engine::documentation::get_flags_section;
|
use nu_engine::documentation::get_flags_section;
|
||||||
use nu_protocol::{engine::EngineState, levenshtein_distance};
|
use nu_protocol::{engine::EngineState, levenshtein_distance};
|
||||||
use reedline::{Completer, Suggestion};
|
use reedline::{Completer, Suggestion};
|
||||||
|
use std::fmt::Write;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct NuHelpCompleter(Arc<EngineState>);
|
pub struct NuHelpCompleter(Arc<EngineState>);
|
||||||
@ -19,6 +20,10 @@ impl NuHelpCompleter {
|
|||||||
.filter(|(sig, _, _, _)| {
|
.filter(|(sig, _, _, _)| {
|
||||||
sig.name.to_lowercase().contains(&line.to_lowercase())
|
sig.name.to_lowercase().contains(&line.to_lowercase())
|
||||||
|| sig.usage.to_lowercase().contains(&line.to_lowercase())
|
|| sig.usage.to_lowercase().contains(&line.to_lowercase())
|
||||||
|
|| sig
|
||||||
|
.search_terms
|
||||||
|
.iter()
|
||||||
|
.any(|term| term.to_lowercase().contains(&line.to_lowercase()))
|
||||||
|| sig
|
|| sig
|
||||||
.extra_usage
|
.extra_usage
|
||||||
.to_lowercase()
|
.to_lowercase()
|
||||||
@ -49,7 +54,7 @@ impl NuHelpCompleter {
|
|||||||
long_desc.push_str("\r\n\r\n");
|
long_desc.push_str("\r\n\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
long_desc.push_str(&format!("Usage:\r\n > {}\r\n", sig.call_signature()));
|
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
|
||||||
|
|
||||||
if !sig.named.is_empty() {
|
if !sig.named.is_empty() {
|
||||||
long_desc.push_str(&get_flags_section(sig))
|
long_desc.push_str(&get_flags_section(sig))
|
||||||
@ -61,27 +66,28 @@ impl NuHelpCompleter {
|
|||||||
{
|
{
|
||||||
long_desc.push_str("\r\nParameters:\r\n");
|
long_desc.push_str("\r\nParameters:\r\n");
|
||||||
for positional in &sig.required_positional {
|
for positional in &sig.required_positional {
|
||||||
long_desc
|
let _ = write!(long_desc, " {}: {}\r\n", positional.name, positional.desc);
|
||||||
.push_str(&format!(" {}: {}\r\n", positional.name, positional.desc));
|
|
||||||
}
|
}
|
||||||
for positional in &sig.optional_positional {
|
for positional in &sig.optional_positional {
|
||||||
long_desc.push_str(&format!(
|
let _ = write!(
|
||||||
|
long_desc,
|
||||||
" (optional) {}: {}\r\n",
|
" (optional) {}: {}\r\n",
|
||||||
positional.name, positional.desc
|
positional.name, positional.desc
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(rest_positional) = &sig.rest_positional {
|
if let Some(rest_positional) = &sig.rest_positional {
|
||||||
long_desc.push_str(&format!(
|
let _ = write!(
|
||||||
|
long_desc,
|
||||||
" ...{}: {}\r\n",
|
" ...{}: {}\r\n",
|
||||||
rest_positional.name, rest_positional.desc
|
rest_positional.name, rest_positional.desc
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let extra: Vec<String> = examples
|
let extra: Vec<String> = examples
|
||||||
.iter()
|
.iter()
|
||||||
.map(|example| example.example.to_string())
|
.map(|example| example.example.replace('\n', "\r\n"))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Suggestion {
|
Suggestion {
|
||||||
|
@ -19,6 +19,10 @@ impl Command for NuHighlight {
|
|||||||
"Syntax highlight the input string."
|
"Syntax highlight the input string."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["syntax", "color", "convert"]
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -17,15 +17,27 @@ impl Command for Print {
|
|||||||
Signature::build("print")
|
Signature::build("print")
|
||||||
.rest("rest", SyntaxShape::Any, "the values to print")
|
.rest("rest", SyntaxShape::Any, "the values to print")
|
||||||
.switch(
|
.switch(
|
||||||
"no_newline",
|
"no-newline",
|
||||||
"print without inserting a newline for the line ending",
|
"print without inserting a newline for the line ending",
|
||||||
Some('n'),
|
Some('n'),
|
||||||
)
|
)
|
||||||
|
.switch("stderr", "print to stderr instead of stdout", Some('e'))
|
||||||
.category(Category::Strings)
|
.category(Category::Strings)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Prints the values given"
|
"Print the given values to stdout"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"Unlike `echo`, this command does not return any value (`print | describe` will return "nothing").
|
||||||
|
Since this command has no output, there is no point in piping it with other commands.
|
||||||
|
|
||||||
|
`print` may be used inside blocks of code (e.g.: hooks) to display text during execution without interfering with the pipeline."#
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["display"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -36,12 +48,13 @@ impl Command for Print {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
||||||
let no_newline = call.has_flag("no_newline");
|
let no_newline = call.has_flag("no-newline");
|
||||||
|
let to_stderr = call.has_flag("stderr");
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
arg.into_pipeline_data()
|
arg.into_pipeline_data()
|
||||||
.print(engine_state, stack, no_newline)?;
|
.print(engine_state, stack, no_newline, to_stderr)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(PipelineData::new(head))
|
Ok(PipelineData::new(head))
|
||||||
|
@ -875,7 +875,16 @@ fn edit_from_record(
|
|||||||
"moveleft" => EditCommand::MoveLeft,
|
"moveleft" => EditCommand::MoveLeft,
|
||||||
"moveright" => EditCommand::MoveRight,
|
"moveright" => EditCommand::MoveRight,
|
||||||
"movewordleft" => EditCommand::MoveWordLeft,
|
"movewordleft" => EditCommand::MoveWordLeft,
|
||||||
|
"movebigwordleft" => EditCommand::MoveBigWordLeft,
|
||||||
"movewordright" => EditCommand::MoveWordRight,
|
"movewordright" => EditCommand::MoveWordRight,
|
||||||
|
"movewordrightend" => EditCommand::MoveWordRightEnd,
|
||||||
|
"movebigwordrightend" => EditCommand::MoveBigWordRightEnd,
|
||||||
|
"movewordrightstart" => EditCommand::MoveWordRightStart,
|
||||||
|
"movebigwordrightstart" => EditCommand::MoveBigWordRightStart,
|
||||||
|
"movetoposition" => {
|
||||||
|
let value = extract_value("value", cols, vals, span)?;
|
||||||
|
EditCommand::MoveToPosition(value.as_integer()? as usize)
|
||||||
|
}
|
||||||
"insertchar" => {
|
"insertchar" => {
|
||||||
let value = extract_value("value", cols, vals, span)?;
|
let value = extract_value("value", cols, vals, span)?;
|
||||||
let char = extract_char(value, config)?;
|
let char = extract_char(value, config)?;
|
||||||
@ -888,6 +897,7 @@ fn edit_from_record(
|
|||||||
"insertnewline" => EditCommand::InsertNewline,
|
"insertnewline" => EditCommand::InsertNewline,
|
||||||
"backspace" => EditCommand::Backspace,
|
"backspace" => EditCommand::Backspace,
|
||||||
"delete" => EditCommand::Delete,
|
"delete" => EditCommand::Delete,
|
||||||
|
"cutchar" => EditCommand::CutChar,
|
||||||
"backspaceword" => EditCommand::BackspaceWord,
|
"backspaceword" => EditCommand::BackspaceWord,
|
||||||
"deleteword" => EditCommand::DeleteWord,
|
"deleteword" => EditCommand::DeleteWord,
|
||||||
"clear" => EditCommand::Clear,
|
"clear" => EditCommand::Clear,
|
||||||
@ -898,7 +908,11 @@ fn edit_from_record(
|
|||||||
"cuttoend" => EditCommand::CutToEnd,
|
"cuttoend" => EditCommand::CutToEnd,
|
||||||
"cuttolineend" => EditCommand::CutToLineEnd,
|
"cuttolineend" => EditCommand::CutToLineEnd,
|
||||||
"cutwordleft" => EditCommand::CutWordLeft,
|
"cutwordleft" => EditCommand::CutWordLeft,
|
||||||
|
"cutbigwordleft" => EditCommand::CutBigWordLeft,
|
||||||
"cutwordright" => EditCommand::CutWordRight,
|
"cutwordright" => EditCommand::CutWordRight,
|
||||||
|
"cutbigwordright" => EditCommand::CutBigWordRight,
|
||||||
|
"cutwordrighttonext" => EditCommand::CutWordRightToNext,
|
||||||
|
"cutbigwordrighttonext" => EditCommand::CutBigWordRightToNext,
|
||||||
"pastecutbufferbefore" => EditCommand::PasteCutBufferBefore,
|
"pastecutbufferbefore" => EditCommand::PasteCutBufferBefore,
|
||||||
"pastecutbufferafter" => EditCommand::PasteCutBufferAfter,
|
"pastecutbufferafter" => EditCommand::PasteCutBufferAfter,
|
||||||
"uppercaseword" => EditCommand::UppercaseWord,
|
"uppercaseword" => EditCommand::UppercaseWord,
|
||||||
|
@ -12,21 +12,22 @@ use nu_engine::{convert_env_values, eval_block};
|
|||||||
use nu_parser::lex;
|
use nu_parser::lex;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
BlockId, PipelineData, PositionalArg, ShellError, Span, Value,
|
BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
use reedline::{DefaultHinter, Emacs, Vi};
|
use reedline::{DefaultHinter, Emacs, SqliteBackedHistory, Vi};
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::{sync::atomic::Ordering, time::Instant};
|
use std::{sync::atomic::Ordering, time::Instant};
|
||||||
|
use sysinfo::SystemExt;
|
||||||
|
|
||||||
const PRE_EXECUTE_MARKER: &str = "\x1b]133;A\x1b\\";
|
const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
|
||||||
const PRE_PROMPT_MARKER: &str = "\x1b]133;C\x1b\\";
|
const PRE_EXECUTE_MARKER: &str = "\x1b]133;C\x1b\\";
|
||||||
|
const CMD_FINISHED_MARKER: &str = "\x1b]133;D\x1b\\";
|
||||||
const RESET_APPLICATION_MODE: &str = "\x1b[?1l";
|
const RESET_APPLICATION_MODE: &str = "\x1b[?1l";
|
||||||
|
|
||||||
pub fn evaluate_repl(
|
pub fn evaluate_repl(
|
||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
history_path: Option<PathBuf>,
|
nushell_path: &str,
|
||||||
is_perf_true: bool,
|
is_perf_true: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
use reedline::{FileBackedHistory, Reedline, Signal};
|
use reedline::{FileBackedHistory, Reedline, Signal};
|
||||||
@ -84,20 +85,32 @@ pub fn evaluate_repl(
|
|||||||
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
|
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
|
||||||
}
|
}
|
||||||
let mut line_editor = Reedline::create();
|
let mut line_editor = Reedline::create();
|
||||||
|
let history_path = crate::config_files::get_history_path(
|
||||||
|
nushell_path,
|
||||||
|
engine_state.config.history_file_format,
|
||||||
|
);
|
||||||
if let Some(history_path) = history_path.as_deref() {
|
if let Some(history_path) = history_path.as_deref() {
|
||||||
if is_perf_true {
|
if is_perf_true {
|
||||||
info!("setup history {}:{}:{}", file!(), line!(), column!());
|
info!("setup history {}:{}:{}", file!(), line!(), column!());
|
||||||
}
|
}
|
||||||
let history = Box::new(
|
|
||||||
|
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
|
||||||
|
HistoryFileFormat::PlainText => Box::new(
|
||||||
FileBackedHistory::with_file(
|
FileBackedHistory::with_file(
|
||||||
config.max_history_size as usize,
|
config.max_history_size as usize,
|
||||||
history_path.to_path_buf(),
|
history_path.to_path_buf(),
|
||||||
)
|
)
|
||||||
.into_diagnostic()?,
|
.into_diagnostic()?,
|
||||||
);
|
),
|
||||||
|
HistoryFileFormat::Sqlite => Box::new(
|
||||||
|
SqliteBackedHistory::with_file(history_path.to_path_buf()).into_diagnostic()?,
|
||||||
|
),
|
||||||
|
};
|
||||||
line_editor = line_editor.with_history(history);
|
line_editor = line_editor.with_history(history);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let sys = sysinfo::System::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if is_perf_true {
|
if is_perf_true {
|
||||||
info!(
|
info!(
|
||||||
@ -112,6 +125,10 @@ pub fn evaluate_repl(
|
|||||||
if let Some(ctrlc) = &mut engine_state.ctrlc {
|
if let Some(ctrlc) = &mut engine_state.ctrlc {
|
||||||
ctrlc.store(false, Ordering::SeqCst);
|
ctrlc.store(false, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
// Reset the SIGQUIT handler
|
||||||
|
if let Some(sig_quit) = engine_state.get_sig_quit() {
|
||||||
|
sig_quit.store(false, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
config = engine_state.get_config();
|
config = engine_state.get_config();
|
||||||
|
|
||||||
@ -272,8 +289,9 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
config = engine_state.get_config();
|
config = engine_state.get_config();
|
||||||
|
|
||||||
if config.shell_integration {
|
let shell_integration = config.shell_integration;
|
||||||
run_ansi_sequence(PRE_EXECUTE_MARKER)?;
|
if shell_integration {
|
||||||
|
run_ansi_sequence(PRE_PROMPT_MARKER)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let prompt =
|
let prompt =
|
||||||
@ -294,6 +312,20 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
match input {
|
match input {
|
||||||
Ok(Signal::Success(s)) => {
|
Ok(Signal::Success(s)) => {
|
||||||
|
let history_supports_meta =
|
||||||
|
matches!(config.history_file_format, HistoryFileFormat::Sqlite);
|
||||||
|
if history_supports_meta && !s.is_empty() {
|
||||||
|
line_editor
|
||||||
|
.update_last_command_context(&|mut c| {
|
||||||
|
c.start_timestamp = Some(chrono::Utc::now());
|
||||||
|
c.hostname = sys.host_name();
|
||||||
|
|
||||||
|
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd());
|
||||||
|
c
|
||||||
|
})
|
||||||
|
.into_diagnostic()?; // todo: don't stop repl if error here?
|
||||||
|
}
|
||||||
|
|
||||||
// Right before we start running the code the user gave us,
|
// Right before we start running the code the user gave us,
|
||||||
// fire the "pre_execution" hook
|
// fire the "pre_execution" hook
|
||||||
if let Some(hook) = &config.hooks.pre_execution {
|
if let Some(hook) = &config.hooks.pre_execution {
|
||||||
@ -303,9 +335,9 @@ pub fn evaluate_repl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.shell_integration {
|
if shell_integration {
|
||||||
run_ansi_sequence(RESET_APPLICATION_MODE)?;
|
run_ansi_sequence(RESET_APPLICATION_MODE)?;
|
||||||
run_ansi_sequence(PRE_PROMPT_MARKER)?;
|
run_ansi_sequence(PRE_EXECUTE_MARKER)?;
|
||||||
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
|
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
|
||||||
let path = cwd.as_string()?;
|
let path = cwd.as_string()?;
|
||||||
// Try to abbreviate string for windows title
|
// Try to abbreviate string for windows title
|
||||||
@ -395,11 +427,12 @@ pub fn evaluate_repl(
|
|||||||
PipelineData::new(Span::new(0, 0)),
|
PipelineData::new(Span::new(0, 0)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
let cmd_duration = start_time.elapsed();
|
||||||
|
|
||||||
stack.add_env_var(
|
stack.add_env_var(
|
||||||
"CMD_DURATION_MS".into(),
|
"CMD_DURATION_MS".into(),
|
||||||
Value::String {
|
Value::String {
|
||||||
val: format!("{}", start_time.elapsed().as_millis()),
|
val: format!("{}", cmd_duration.as_millis()),
|
||||||
span: Span { start: 0, end: 0 },
|
span: Span { start: 0, end: 0 },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -411,12 +444,35 @@ pub fn evaluate_repl(
|
|||||||
let _ = std::env::set_current_dir(path);
|
let _ = std::env::set_current_dir(path);
|
||||||
engine_state.add_env_var("PWD".into(), cwd);
|
engine_state.add_env_var("PWD".into(), cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if history_supports_meta && !s.is_empty() {
|
||||||
|
line_editor
|
||||||
|
.update_last_command_context(&|mut c| {
|
||||||
|
c.duration = Some(cmd_duration);
|
||||||
|
c.exit_status = stack
|
||||||
|
.get_env_var(engine_state, "LAST_EXIT_CODE")
|
||||||
|
.and_then(|e| e.as_i64().ok());
|
||||||
|
c
|
||||||
|
})
|
||||||
|
.into_diagnostic()?; // todo: don't stop repl if error here?
|
||||||
|
}
|
||||||
|
|
||||||
|
if shell_integration {
|
||||||
|
// FIXME: use variant with exit code, if apropriate
|
||||||
|
run_ansi_sequence(CMD_FINISHED_MARKER)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(Signal::CtrlC) => {
|
Ok(Signal::CtrlC) => {
|
||||||
// `Reedline` clears the line content. New prompt is shown
|
// `Reedline` clears the line content. New prompt is shown
|
||||||
|
if shell_integration {
|
||||||
|
run_ansi_sequence(CMD_FINISHED_MARKER)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(Signal::CtrlD) => {
|
Ok(Signal::CtrlD) => {
|
||||||
// When exiting clear to a new line
|
// When exiting clear to a new line
|
||||||
|
if shell_integration {
|
||||||
|
run_ansi_sequence(CMD_FINISHED_MARKER)?;
|
||||||
|
}
|
||||||
println!();
|
println!();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -425,6 +481,9 @@ pub fn evaluate_repl(
|
|||||||
if !message.contains("duration") {
|
if !message.contains("duration") {
|
||||||
println!("Error: {:?}", err);
|
println!("Error: {:?}", err);
|
||||||
}
|
}
|
||||||
|
if shell_integration {
|
||||||
|
run_ansi_sequence(CMD_FINISHED_MARKER)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,18 +9,35 @@ use nu_protocol::{
|
|||||||
};
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use nu_utils::enable_vt_processing;
|
use nu_utils::enable_vt_processing;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
// This will collect environment variables from std::env and adds them to a stack.
|
// This will collect environment variables from std::env and adds them to a stack.
|
||||||
//
|
//
|
||||||
// In order to ensure the values have spans, it first creates a dummy file, writes the collected
|
// In order to ensure the values have spans, it first creates a dummy file, writes the collected
|
||||||
// env vars into it (in a "NAME"="value" format, quite similar to the output of the Unix 'env'
|
// env vars into it (in a "NAME"="value" format, quite similar to the output of the Unix 'env'
|
||||||
// tool), then uses the file to get the spans. The file stays in memory, no filesystem IO is done.
|
// tool), then uses the file to get the spans. The file stays in memory, no filesystem IO is done.
|
||||||
pub fn gather_parent_env_vars(engine_state: &mut EngineState) {
|
//
|
||||||
gather_env_vars(std::env::vars(), engine_state);
|
// The "PWD" env value will be forced to `init_cwd`.
|
||||||
|
// The reason to use `init_cwd`:
|
||||||
|
//
|
||||||
|
// While gathering parent env vars, the parent `PWD` may not be the same as `current working directory`.
|
||||||
|
// Consider to the following command as the case (assume we execute command inside `/tmp`):
|
||||||
|
//
|
||||||
|
// tmux split-window -v -c "#{pane_current_path}"
|
||||||
|
//
|
||||||
|
// Here nu execute external command `tmux`, and tmux starts a new `nushell`, with `init_cwd` value "#{pane_current_path}".
|
||||||
|
// But at the same time `PWD` still remains to be `/tmp`.
|
||||||
|
//
|
||||||
|
// In this scenario, the new `nushell`'s PWD should be "#{pane_current_path}" rather init_cwd.
|
||||||
|
pub fn gather_parent_env_vars(engine_state: &mut EngineState, init_cwd: &Path) {
|
||||||
|
gather_env_vars(std::env::vars(), engine_state, init_cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gather_env_vars(vars: impl Iterator<Item = (String, String)>, engine_state: &mut EngineState) {
|
fn gather_env_vars(
|
||||||
|
vars: impl Iterator<Item = (String, String)>,
|
||||||
|
engine_state: &mut EngineState,
|
||||||
|
init_cwd: &Path,
|
||||||
|
) {
|
||||||
fn report_capture_error(engine_state: &EngineState, env_str: &str, msg: &str) {
|
fn report_capture_error(engine_state: &EngineState, env_str: &str, msg: &str) {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(
|
report_error(
|
||||||
@ -43,37 +60,33 @@ fn gather_env_vars(vars: impl Iterator<Item = (String, String)>, engine_state: &
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut fake_env_file = String::new();
|
let mut fake_env_file = String::new();
|
||||||
let mut has_pwd = false;
|
|
||||||
|
|
||||||
// Write all the env vars into a fake file
|
// Write all the env vars into a fake file
|
||||||
for (name, val) in vars {
|
for (name, val) in vars {
|
||||||
if name == "PWD" {
|
|
||||||
has_pwd = true;
|
|
||||||
}
|
|
||||||
put_env_to_fake_file(&name, &val, &mut fake_env_file);
|
put_env_to_fake_file(&name, &val, &mut fake_env_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !has_pwd {
|
match init_cwd.to_str() {
|
||||||
match std::env::current_dir() {
|
Some(cwd) => {
|
||||||
Ok(cwd) => {
|
put_env_to_fake_file("PWD", cwd, &mut fake_env_file);
|
||||||
put_env_to_fake_file("PWD", &cwd.to_string_lossy(), &mut fake_env_file);
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
None => {
|
||||||
// Could not capture current working directory
|
// Could not capture current working directory
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(
|
report_error(
|
||||||
&working_set,
|
&working_set,
|
||||||
&ShellError::GenericError(
|
&ShellError::GenericError(
|
||||||
"Current directory not found".to_string(),
|
"Current directory is not a valid utf-8 path".to_string(),
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
None,
|
None,
|
||||||
Some(format!("Retrieving current directory failed: {:?}", e)),
|
Some(format!(
|
||||||
|
"Retrieving current directory failed: {:?} not a valid utf-8 path",
|
||||||
|
init_cwd
|
||||||
|
)),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Lex the fake file, assign spans to all environment variables and add them
|
// Lex the fake file, assign spans to all environment variables and add them
|
||||||
// to stack
|
// to stack
|
||||||
@ -234,7 +247,7 @@ pub fn eval_source(
|
|||||||
set_last_exit_code(stack, 0);
|
set_last_exit_code(stack, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err) = pipeline_data.print(engine_state, stack, false) {
|
if let Err(err) = pipeline_data.print(engine_state, stack, false, false) {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
@ -314,6 +327,7 @@ mod test {
|
|||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
&mut engine_state,
|
&mut engine_state,
|
||||||
|
Path::new("t"),
|
||||||
);
|
);
|
||||||
|
|
||||||
let env = engine_state.render_env_vars();
|
let env = engine_state.render_env_vars();
|
||||||
|
65
crates/nu-cli/tests/alias.rs
Normal file
65
crates/nu-cli/tests/alias.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
pub mod support;
|
||||||
|
|
||||||
|
use nu_cli::NuCompleter;
|
||||||
|
use reedline::Completer;
|
||||||
|
use support::{match_suggestions, new_engine};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_of_command_and_flags() {
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
// Create an alias
|
||||||
|
let alias = r#"alias ll = ls -l"#;
|
||||||
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let suggestions = completer.complete("ll t", 4);
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_of_basic_command() {
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
// Create an alias
|
||||||
|
let alias = r#"alias ll = ls "#;
|
||||||
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let suggestions = completer.complete("ll t", 4);
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_of_another_alias() {
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
// Create an alias
|
||||||
|
let alias = r#"alias ll = ls -la"#;
|
||||||
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir.clone()).is_ok());
|
||||||
|
// Create the second alias
|
||||||
|
let alias = r#"alias lf = ll -f"#;
|
||||||
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let suggestions = completer.complete("lf t", 4);
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
@ -14,11 +14,11 @@ fn variables_completions() {
|
|||||||
def my-command [animal: string@animals] { print $animal }"#;
|
def my-command [animal: string@animals] { print $animal }"#;
|
||||||
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
// Instatiate a new completer
|
// Instantiate a new completer
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
// Test completions for $nu
|
// Test completions for $nu
|
||||||
let suggestions = completer.complete("my-command ".into(), 11);
|
let suggestions = completer.complete("my-command ", 11);
|
||||||
|
|
||||||
assert_eq!(3, suggestions.len());
|
assert_eq!(3, suggestions.len());
|
||||||
|
|
||||||
|
@ -40,3 +40,233 @@ fn file_completions() {
|
|||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(expected_paths, suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_ls_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "ls ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn command_open_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "open ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_rm_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "rm ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_cp_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "cp ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_save_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "save ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_touch_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "touch ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_watch_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "watch ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
@ -12,7 +12,7 @@ fn flag_completions() {
|
|||||||
// Instatiate a new completer
|
// Instatiate a new completer
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
// Test completions for the 'ls' flags
|
// Test completions for the 'ls' flags
|
||||||
let suggestions = completer.complete("ls -".into(), 4);
|
let suggestions = completer.complete("ls -", 4);
|
||||||
|
|
||||||
assert_eq!(12, suggestions.len());
|
assert_eq!(12, suggestions.len());
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ pub fn merge_input(
|
|||||||
dir: PathBuf,
|
dir: PathBuf,
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<(), ShellError> {
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
let (block, err) = parse(&mut working_set, None, input, false, &[]);
|
let (block, err) = parse(&mut working_set, None, input, false, &[]);
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ pub fn merge_input(
|
|||||||
(block, working_set.render())
|
(block, working_set.render())
|
||||||
};
|
};
|
||||||
assert!(eval_block(
|
assert!(eval_block(
|
||||||
&engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
&block,
|
&block,
|
||||||
PipelineData::Value(
|
PipelineData::Value(
|
||||||
|
@ -17,15 +17,16 @@ fn variables_completions() {
|
|||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
// Test completions for $nu
|
// Test completions for $nu
|
||||||
let suggestions = completer.complete("$nu.".into(), 4);
|
let suggestions = completer.complete("$nu.", 4);
|
||||||
|
|
||||||
assert_eq!(8, suggestions.len());
|
assert_eq!(9, suggestions.len());
|
||||||
|
|
||||||
let expected: Vec<String> = vec![
|
let expected: Vec<String> = vec![
|
||||||
"config-path".into(),
|
"config-path".into(),
|
||||||
"env-path".into(),
|
"env-path".into(),
|
||||||
"history-path".into(),
|
"history-path".into(),
|
||||||
"home-path".into(),
|
"home-path".into(),
|
||||||
|
"loginshell-path".into(),
|
||||||
"os-info".into(),
|
"os-info".into(),
|
||||||
"pid".into(),
|
"pid".into(),
|
||||||
"scope".into(),
|
"scope".into(),
|
||||||
@ -36,7 +37,7 @@ fn variables_completions() {
|
|||||||
match_suggestions(expected, suggestions);
|
match_suggestions(expected, suggestions);
|
||||||
|
|
||||||
// Test completions for $nu.h (filter)
|
// Test completions for $nu.h (filter)
|
||||||
let suggestions = completer.complete("$nu.h".into(), 5);
|
let suggestions = completer.complete("$nu.h", 5);
|
||||||
|
|
||||||
assert_eq!(2, suggestions.len());
|
assert_eq!(2, suggestions.len());
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ fn variables_completions() {
|
|||||||
match_suggestions(expected, suggestions);
|
match_suggestions(expected, suggestions);
|
||||||
|
|
||||||
// Test completions for custom var
|
// Test completions for custom var
|
||||||
let suggestions = completer.complete("$actor.".into(), 7);
|
let suggestions = completer.complete("$actor.", 7);
|
||||||
|
|
||||||
assert_eq!(2, suggestions.len());
|
assert_eq!(2, suggestions.len());
|
||||||
|
|
||||||
@ -56,7 +57,7 @@ fn variables_completions() {
|
|||||||
match_suggestions(expected, suggestions);
|
match_suggestions(expected, suggestions);
|
||||||
|
|
||||||
// Test completions for custom var (filtering)
|
// Test completions for custom var (filtering)
|
||||||
let suggestions = completer.complete("$actor.n".into(), 7);
|
let suggestions = completer.complete("$actor.n", 8);
|
||||||
|
|
||||||
assert_eq!(1, suggestions.len());
|
assert_eq!(1, suggestions.len());
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ fn variables_completions() {
|
|||||||
match_suggestions(expected, suggestions);
|
match_suggestions(expected, suggestions);
|
||||||
|
|
||||||
// Test completions for $env
|
// Test completions for $env
|
||||||
let suggestions = completer.complete("$env.".into(), 5);
|
let suggestions = completer.complete("$env.", 5);
|
||||||
|
|
||||||
assert_eq!(2, suggestions.len());
|
assert_eq!(2, suggestions.len());
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ fn variables_completions() {
|
|||||||
match_suggestions(expected, suggestions);
|
match_suggestions(expected, suggestions);
|
||||||
|
|
||||||
// Test completions for $env
|
// Test completions for $env
|
||||||
let suggestions = completer.complete("$env.T".into(), 5);
|
let suggestions = completer.complete("$env.T", 6);
|
||||||
|
|
||||||
assert_eq!(1, suggestions.len());
|
assert_eq!(1, suggestions.len());
|
||||||
|
|
||||||
|
@ -4,11 +4,11 @@ description = "Color configuration code used by Nushell"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-color-config"
|
name = "nu-color-config"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.63.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.65.0" }
|
||||||
nu-ansi-term = "0.45.1"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-json = { path = "../nu-json", version = "0.63.1" }
|
nu-json = { path = "../nu-json", version = "0.65.0" }
|
||||||
nu-table = { path = "../nu-table", version = "0.63.1" }
|
nu-table = { path = "../nu-table", version = "0.65.0" }
|
||||||
serde = { version="1.0.123", features=["derive"] }
|
serde = { version="1.0.123", features=["derive"] }
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use nu_ansi_term::{Color, Style};
|
use nu_ansi_term::{Color, Style};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize, PartialEq, Debug)]
|
#[derive(Deserialize, PartialEq, Eq, Debug)]
|
||||||
pub struct NuStyle {
|
pub struct NuStyle {
|
||||||
pub fg: Option<String>,
|
pub fg: Option<String>,
|
||||||
pub bg: Option<String>,
|
pub bg: Option<String>,
|
||||||
|
@ -4,30 +4,31 @@ description = "Nushell's built-in commands"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-command"
|
name = "nu-command"
|
||||||
version = "0.63.1"
|
version = "0.65.0"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.63.1" }
|
nu-color-config = { path = "../nu-color-config", version = "0.65.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.63.1" }
|
nu-engine = { path = "../nu-engine", version = "0.65.0" }
|
||||||
nu-glob = { path = "../nu-glob", version = "0.63.1" }
|
nu-glob = { path = "../nu-glob", version = "0.65.0" }
|
||||||
nu-json = { path = "../nu-json", version = "0.63.1" }
|
nu-json = { path = "../nu-json", version = "0.65.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.63.1" }
|
nu-parser = { path = "../nu-parser", version = "0.65.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.63.1" }
|
nu-path = { path = "../nu-path", version = "0.65.0" }
|
||||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.63.1" }
|
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.65.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.63.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.65.0" }
|
||||||
nu-system = { path = "../nu-system", version = "0.63.1" }
|
nu-system = { path = "../nu-system", version = "0.65.0" }
|
||||||
nu-table = { path = "../nu-table", version = "0.63.1" }
|
nu-table = { path = "../nu-table", version = "0.65.0" }
|
||||||
nu-term-grid = { path = "../nu-term-grid", version = "0.63.1" }
|
nu-term-grid = { path = "../nu-term-grid", version = "0.65.0" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.63.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.65.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.63.1" }
|
nu-utils = { path = "../nu-utils", version = "0.65.0" }
|
||||||
nu-ansi-term = "0.45.1"
|
nu-ansi-term = "0.46.0"
|
||||||
|
|
||||||
# Potential dependencies for extras
|
# Potential dependencies for extras
|
||||||
alphanumeric-sort = "1.4.4"
|
alphanumeric-sort = "1.4.4"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
|
byteorder = "1.4.3"
|
||||||
bytesize = "1.1.0"
|
bytesize = "1.1.0"
|
||||||
calamine = "0.18.0"
|
calamine = "0.18.0"
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
@ -47,6 +48,7 @@ htmlescape = "0.3.1"
|
|||||||
ical = "0.7.0"
|
ical = "0.7.0"
|
||||||
indexmap = { version="1.7", features=["serde-1"] }
|
indexmap = { version="1.7", features=["serde-1"] }
|
||||||
Inflector = "0.11"
|
Inflector = "0.11"
|
||||||
|
is-root = "0.1.2"
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
@ -75,14 +77,14 @@ shadow-rs = { version = "0.11.0", default-features = false }
|
|||||||
strip-ansi-escapes = "0.1.1"
|
strip-ansi-escapes = "0.1.1"
|
||||||
sysinfo = "0.23.5"
|
sysinfo = "0.23.5"
|
||||||
terminal_size = "0.1.17"
|
terminal_size = "0.1.17"
|
||||||
thiserror = "1.0.29"
|
thiserror = "1.0.31"
|
||||||
titlecase = "1.1.0"
|
titlecase = "1.1.0"
|
||||||
toml = "0.5.8"
|
toml = "0.5.8"
|
||||||
unicode-segmentation = "1.8.0"
|
unicode-segmentation = "1.8.0"
|
||||||
url = "2.2.1"
|
url = "2.2.1"
|
||||||
uuid = { version = "0.8.2", features = ["v4"] }
|
uuid = { version = "0.8.2", features = ["v4"] }
|
||||||
which = { version = "4.2.2", optional = true }
|
which = { version = "4.2.2", optional = true }
|
||||||
reedline = { git = "https://github.com/nushell/reedline", branch = "main", features = ["bashisms"]}
|
reedline = { version = "0.8.0", features = ["bashisms", "sqlite"]}
|
||||||
wax = { version = "0.4.0", features = ["diagnostics"] }
|
wax = { version = "0.4.0", features = ["diagnostics"] }
|
||||||
rusqlite = { version = "0.27.0", features = ["bundled"], optional = true }
|
rusqlite = { version = "0.27.0", features = ["bundled"], optional = true }
|
||||||
sqlparser = { version = "0.16.0", features = ["serde"], optional = true }
|
sqlparser = { version = "0.16.0", features = ["serde"], optional = true }
|
||||||
@ -90,13 +92,14 @@ sqlparser = { version = "0.16.0", features = ["serde"], optional = true }
|
|||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
umask = "2.0.0"
|
umask = "2.0.0"
|
||||||
users = "0.11.0"
|
users = "0.11.0"
|
||||||
|
signal-hook = { version = "0.3.14", default-features = false }
|
||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
||||||
version = "2.1.3"
|
version = "2.1.3"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.polars]
|
[dependencies.polars]
|
||||||
version = "0.21.1"
|
version = "0.22.8"
|
||||||
# path = "../../../../polars/polars"
|
# path = "../../../../polars/polars"
|
||||||
optional = true
|
optional = true
|
||||||
features = [
|
features = [
|
||||||
@ -104,7 +107,16 @@ features = [
|
|||||||
"object", "checked_arithmetic", "strings", "cum_agg", "is_in",
|
"object", "checked_arithmetic", "strings", "cum_agg", "is_in",
|
||||||
"rolling_window", "strings", "rows", "random",
|
"rolling_window", "strings", "rows", "random",
|
||||||
"dtype-datetime", "dtype-struct", "lazy", "cross_join",
|
"dtype-datetime", "dtype-struct", "lazy", "cross_join",
|
||||||
"dynamic_groupby"
|
"dynamic_groupby", "dtype-categorical"
|
||||||
|
]
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
|
version = "0.37.0"
|
||||||
|
features = [
|
||||||
|
"alloc",
|
||||||
|
"Win32_Foundation",
|
||||||
|
"Win32_Storage_FileSystem",
|
||||||
|
"Win32_System_SystemServices",
|
||||||
]
|
]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
@ -122,3 +134,4 @@ hamcrest2 = "0.3.0"
|
|||||||
dirs-next = "2.0.0"
|
dirs-next = "2.0.0"
|
||||||
quickcheck = "1.0.3"
|
quickcheck = "1.0.3"
|
||||||
quickcheck_macros = "1.0.0"
|
quickcheck_macros = "1.0.0"
|
||||||
|
rstest = "0.12.0"
|
||||||
|
98
crates/nu-command/src/bytes/length.rs
Normal file
98
crates/nu-command/src/bytes/length.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
use super::{operate, BytesArgument};
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::ast::CellPath;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::Category;
|
||||||
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BytesLen;
|
||||||
|
|
||||||
|
struct Arguments {
|
||||||
|
column_paths: Option<Vec<CellPath>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BytesArgument for Arguments {
|
||||||
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
|
self.column_paths.take()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command for BytesLen {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"bytes length"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("bytes length")
|
||||||
|
.rest(
|
||||||
|
"rest",
|
||||||
|
SyntaxShape::CellPath,
|
||||||
|
"optionally find length of binary by column paths",
|
||||||
|
)
|
||||||
|
.category(Category::Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Output the length of any bytes in the pipeline"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["len", "size", "count"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
|
let column_paths = if column_paths.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(column_paths)
|
||||||
|
};
|
||||||
|
let arg = Arguments { column_paths };
|
||||||
|
operate(length, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Return the lengths of multiple strings",
|
||||||
|
example: "0x[1F FF AA AB] | bytes length",
|
||||||
|
result: Some(Value::test_int(4)),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Return the lengths of multiple strings",
|
||||||
|
example: "[0x[1F FF AA AB] 0x[1F]] | bytes length",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![Value::test_int(4), Value::test_int(1)],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn length(input: &[u8], _arg: &Arguments, span: Span) -> Value {
|
||||||
|
Value::Int {
|
||||||
|
val: input.len() as i64,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(BytesLen {})
|
||||||
|
}
|
||||||
|
}
|
77
crates/nu-command/src/bytes/mod.rs
Normal file
77
crates/nu-command/src/bytes/mod.rs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
mod length;
|
||||||
|
mod starts_with;
|
||||||
|
use nu_protocol::ast::CellPath;
|
||||||
|
use nu_protocol::{PipelineData, ShellError, Span, Value};
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub use length::BytesLen;
|
||||||
|
pub use starts_with::BytesStartsWith;
|
||||||
|
|
||||||
|
trait BytesArgument {
|
||||||
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// map input pipeline data, for each elements, if it's Binary, invoke relative `cmd` with `arg`.
|
||||||
|
fn operate<C, A>(
|
||||||
|
cmd: C,
|
||||||
|
mut arg: A,
|
||||||
|
input: PipelineData,
|
||||||
|
span: Span,
|
||||||
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
|
) -> Result<PipelineData, ShellError>
|
||||||
|
where
|
||||||
|
A: BytesArgument + Send + Sync + 'static,
|
||||||
|
C: Fn(&[u8], &A, Span) -> Value + Send + Sync + 'static + Clone + Copy,
|
||||||
|
{
|
||||||
|
match arg.take_column_paths() {
|
||||||
|
None => input.map(
|
||||||
|
move |v| match v {
|
||||||
|
Value::Binary {
|
||||||
|
val,
|
||||||
|
span: val_span,
|
||||||
|
} => cmd(&val, &arg, val_span),
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ctrlc,
|
||||||
|
),
|
||||||
|
Some(column_paths) => {
|
||||||
|
let arg = Arc::new(arg);
|
||||||
|
input.map(
|
||||||
|
move |mut v| {
|
||||||
|
for path in &column_paths {
|
||||||
|
let opt = arg.clone();
|
||||||
|
let r = v.update_cell_path(
|
||||||
|
&path.members,
|
||||||
|
Box::new(move |old| {
|
||||||
|
match old {
|
||||||
|
Value::Binary {val, span: val_span} => cmd(val, &opt, *val_span),
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
}}}),
|
||||||
|
);
|
||||||
|
if let Err(error) = r {
|
||||||
|
return Value::Error { error };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v
|
||||||
|
},
|
||||||
|
ctrlc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
122
crates/nu-command/src/bytes/starts_with.rs
Normal file
122
crates/nu-command/src/bytes/starts_with.rs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
use super::{operate, BytesArgument};
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::ast::CellPath;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::Category;
|
||||||
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
||||||
|
|
||||||
|
struct Arguments {
|
||||||
|
pattern: Vec<u8>,
|
||||||
|
column_paths: Option<Vec<CellPath>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BytesArgument for Arguments {
|
||||||
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
|
self.column_paths.take()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
|
||||||
|
pub struct BytesStartsWith;
|
||||||
|
|
||||||
|
impl Command for BytesStartsWith {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"bytes starts-with"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("bytes starts-with")
|
||||||
|
.required("pattern", SyntaxShape::Binary, "the pattern to match")
|
||||||
|
.rest(
|
||||||
|
"rest",
|
||||||
|
SyntaxShape::CellPath,
|
||||||
|
"optionally matches prefix of text by column paths",
|
||||||
|
)
|
||||||
|
.category(Category::Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Check if bytes starts with a pattern"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["pattern", "match", "find", "search"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||||
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
|
let column_paths = if column_paths.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(column_paths)
|
||||||
|
};
|
||||||
|
let arg = Arguments {
|
||||||
|
pattern,
|
||||||
|
column_paths,
|
||||||
|
};
|
||||||
|
operate(
|
||||||
|
starts_with,
|
||||||
|
arg,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Checks if binary starts with `0x[1F FF AA]`",
|
||||||
|
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F FF AA]",
|
||||||
|
result: Some(Value::Bool {
|
||||||
|
val: true,
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Checks if binary starts with `0x[1F]`",
|
||||||
|
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F]",
|
||||||
|
result: Some(Value::Bool {
|
||||||
|
val: true,
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Checks if binary starts with `0x[1F]`",
|
||||||
|
example: "0x[1F FF AA AA] | bytes starts-with 0x[11]",
|
||||||
|
result: Some(Value::Bool {
|
||||||
|
val: false,
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn starts_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value {
|
||||||
|
Value::Bool {
|
||||||
|
val: input.starts_with(pattern),
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(BytesStartsWith {})
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::{generate_strftime_list, parse_date_from_string};
|
||||||
use chrono::{DateTime, FixedOffset, Local, TimeZone, Utc};
|
use chrono::{DateTime, FixedOffset, Local, TimeZone, Utc};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
@ -7,9 +8,6 @@ use nu_protocol::{
|
|||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::generate_strftime_list;
|
|
||||||
use crate::parse_date_from_string;
|
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
timezone: Option<Spanned<String>>,
|
timezone: Option<Spanned<String>>,
|
||||||
offset: Option<Spanned<i64>>,
|
offset: Option<Spanned<i64>>,
|
||||||
@ -148,6 +146,15 @@ impl Command for SubCommand {
|
|||||||
example: "1614434140 | into datetime -o +9",
|
example: "1614434140 | into datetime -o +9",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description:
|
||||||
|
"Convert timestamps like the sqlite history t",
|
||||||
|
example: "1656165681720 | into datetime",
|
||||||
|
result: Some(Value::Date {
|
||||||
|
val: Utc.timestamp_millis(1656165681720).into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,10 +258,19 @@ fn action(
|
|||||||
|
|
||||||
return match timezone {
|
return match timezone {
|
||||||
// default to UTC
|
// default to UTC
|
||||||
None => Value::Date {
|
None => {
|
||||||
val: Utc.timestamp(ts, 0).into(),
|
// be able to convert chrono::Utc::now()
|
||||||
|
let dt = match ts.to_string().len() {
|
||||||
|
x if x > 13 => Utc.timestamp_nanos(ts).into(),
|
||||||
|
x if x > 10 => Utc.timestamp_millis(ts).into(),
|
||||||
|
_ => Utc.timestamp(ts, 0).into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Value::Date {
|
||||||
|
val: dt,
|
||||||
span: head,
|
span: head,
|
||||||
},
|
}
|
||||||
|
}
|
||||||
Some(Spanned { item, span }) => match item {
|
Some(Spanned { item, span }) => match item {
|
||||||
Zone::Utc => Value::Date {
|
Zone::Utc => Value::Date {
|
||||||
val: Utc.timestamp(ts, 0).into(),
|
val: Utc.timestamp(ts, 0).into(),
|
||||||
|
@ -8,6 +8,7 @@ use nu_protocol::{
|
|||||||
struct Arguments {
|
struct Arguments {
|
||||||
radix: Option<Value>,
|
radix: Option<Value>,
|
||||||
column_paths: Vec<CellPath>,
|
column_paths: Vec<CellPath>,
|
||||||
|
little_endian: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -21,6 +22,7 @@ impl Command for SubCommand {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("into int")
|
Signature::build("into int")
|
||||||
.named("radix", SyntaxShape::Number, "radix of integer", Some('r'))
|
.named("radix", SyntaxShape::Number, "radix of integer", Some('r'))
|
||||||
|
.switch("little-endian", "use little-endian byte decoding", None)
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
@ -114,6 +116,7 @@ fn into_int(
|
|||||||
|
|
||||||
let options = Arguments {
|
let options = Arguments {
|
||||||
radix: call.get_flag(engine_state, stack, "radix")?,
|
radix: call.get_flag(engine_state, stack, "radix")?,
|
||||||
|
little_endian: call.has_flag("little-endian"),
|
||||||
column_paths: call.rest(engine_state, stack, 0)?,
|
column_paths: call.rest(engine_state, stack, 0)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -135,13 +138,13 @@ fn into_int(
|
|||||||
input.map(
|
input.map(
|
||||||
move |v| {
|
move |v| {
|
||||||
if options.column_paths.is_empty() {
|
if options.column_paths.is_empty() {
|
||||||
action(&v, head, radix)
|
action(&v, head, radix, options.little_endian)
|
||||||
} else {
|
} else {
|
||||||
let mut ret = v;
|
let mut ret = v;
|
||||||
for path in &options.column_paths {
|
for path in &options.column_paths {
|
||||||
let r = ret.update_cell_path(
|
let r = ret.update_cell_path(
|
||||||
&path.members,
|
&path.members,
|
||||||
Box::new(move |old| action(old, head, radix)),
|
Box::new(move |old| action(old, head, radix, options.little_endian)),
|
||||||
);
|
);
|
||||||
if let Err(error) = r {
|
if let Err(error) = r {
|
||||||
return Value::Error { error };
|
return Value::Error { error };
|
||||||
@ -155,7 +158,7 @@ fn into_int(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn action(input: &Value, span: Span, radix: u32) -> Value {
|
pub fn action(input: &Value, span: Span, radix: u32, little_endian: bool) -> Value {
|
||||||
match input {
|
match input {
|
||||||
Value::Int { val: _, .. } => {
|
Value::Int { val: _, .. } => {
|
||||||
if radix == 10 {
|
if radix == 10 {
|
||||||
@ -190,8 +193,38 @@ pub fn action(input: &Value, span: Span, radix: u32) -> Value {
|
|||||||
val: val.timestamp(),
|
val: val.timestamp(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
|
Value::Binary { val, span } => {
|
||||||
|
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
||||||
|
|
||||||
|
let mut val = val.to_vec();
|
||||||
|
|
||||||
|
if little_endian {
|
||||||
|
while val.len() < 8 {
|
||||||
|
val.push(0);
|
||||||
|
}
|
||||||
|
val.resize(8, 0);
|
||||||
|
|
||||||
|
Value::Int {
|
||||||
|
val: LittleEndian::read_i64(&val),
|
||||||
|
span: *span,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while val.len() < 8 {
|
||||||
|
val.insert(0, 0);
|
||||||
|
}
|
||||||
|
val.resize(8, 0);
|
||||||
|
|
||||||
|
Value::Int {
|
||||||
|
val: BigEndian::read_i64(&val),
|
||||||
|
span: *span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => Value::Error {
|
_ => Value::Error {
|
||||||
error: ShellError::UnsupportedInput("'into int' for unsupported type".into(), span),
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!("'into int' for unsupported type '{}'", input.get_type()),
|
||||||
|
span,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -291,21 +324,21 @@ mod test {
|
|||||||
let word = Value::test_string("10");
|
let word = Value::test_string("10");
|
||||||
let expected = Value::test_int(10);
|
let expected = Value::test_int(10);
|
||||||
|
|
||||||
let actual = action(&word, Span::test_data(), 10);
|
let actual = action(&word, Span::test_data(), 10, false);
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn turns_binary_to_integer() {
|
fn turns_binary_to_integer() {
|
||||||
let s = Value::test_string("0b101");
|
let s = Value::test_string("0b101");
|
||||||
let actual = action(&s, Span::test_data(), 10);
|
let actual = action(&s, Span::test_data(), 10, false);
|
||||||
assert_eq!(actual, Value::test_int(5));
|
assert_eq!(actual, Value::test_int(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn turns_hex_to_integer() {
|
fn turns_hex_to_integer() {
|
||||||
let s = Value::test_string("0xFF");
|
let s = Value::test_string("0xFF");
|
||||||
let actual = action(&s, Span::test_data(), 16);
|
let actual = action(&s, Span::test_data(), 16, false);
|
||||||
assert_eq!(actual, Value::test_int(255));
|
assert_eq!(actual, Value::test_int(255));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +346,7 @@ mod test {
|
|||||||
fn communicates_parsing_error_given_an_invalid_integerlike_string() {
|
fn communicates_parsing_error_given_an_invalid_integerlike_string() {
|
||||||
let integer_str = Value::test_string("36anra");
|
let integer_str = Value::test_string("36anra");
|
||||||
|
|
||||||
let actual = action(&integer_str, Span::test_data(), 10);
|
let actual = action(&integer_str, Span::test_data(), 10, false);
|
||||||
|
|
||||||
assert_eq!(actual.get_type(), Error)
|
assert_eq!(actual.get_type(), Error)
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
|
into_code, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature,
|
||||||
SyntaxShape, Value,
|
Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml)
|
// TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml)
|
||||||
@ -247,6 +247,15 @@ pub fn action(
|
|||||||
val: input.into_string(", ", config),
|
val: input.into_string(", ", config),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
|
Value::Error { error } => Value::String {
|
||||||
|
val: {
|
||||||
|
match into_code(error) {
|
||||||
|
Some(code) => code,
|
||||||
|
None => "".to_string(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
},
|
||||||
Value::Nothing { .. } => Value::String {
|
Value::Nothing { .. } => Value::String {
|
||||||
val: "".to_string(),
|
val: "".to_string(),
|
||||||
span,
|
span,
|
||||||
|
@ -34,6 +34,10 @@ impl Command for Alias {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["abbr", "aka", "fn", "func", "function"]
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
|
@ -28,7 +28,33 @@ impl Command for DefEnv {
|
|||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
fn extra_usage(&self) -> &str {
|
||||||
r#"This command is a parser keyword. For details, check:
|
r#"This command is a parser keyword. For details, check:
|
||||||
https://www.nushell.sh/book/thinking_in_nushell.html"#
|
https://www.nushell.sh/book/thinking_in_nushell.html
|
||||||
|
|
||||||
|
=== EXTRA NOTE ===
|
||||||
|
All blocks are scoped, including variable definition and environment variable changes.
|
||||||
|
|
||||||
|
Because of this, the following doesn't work:
|
||||||
|
|
||||||
|
def-env cd_with_fallback [arg = ""] {
|
||||||
|
let fall_back_path = "/tmp"
|
||||||
|
if $arg != "" {
|
||||||
|
cd $arg
|
||||||
|
} else {
|
||||||
|
cd $fall_back_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Instead, you have to use cd in the top level scope:
|
||||||
|
|
||||||
|
def-env cd_with_fallback [arg = ""] {
|
||||||
|
let fall_back_path = "/tmp"
|
||||||
|
let path = if $arg != "" {
|
||||||
|
$arg
|
||||||
|
} else {
|
||||||
|
$fall_back_path
|
||||||
|
}
|
||||||
|
cd $path
|
||||||
|
}"#
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_parser_keyword(&self) -> bool {
|
fn is_parser_keyword(&self) -> bool {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use nu_engine::{eval_block, CallExt};
|
use nu_engine::{eval_block, CallExt};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
|
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
|
||||||
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Value};
|
use nu_protocol::{
|
||||||
|
Category, Example, IntoPipelineData, PipelineData, Signature, SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Do;
|
pub struct Do;
|
||||||
@ -23,6 +25,11 @@ impl Command for Do {
|
|||||||
"ignore errors as the block runs",
|
"ignore errors as the block runs",
|
||||||
Some('i'),
|
Some('i'),
|
||||||
)
|
)
|
||||||
|
.switch(
|
||||||
|
"capture-errors",
|
||||||
|
"capture errors as the block runs and return it",
|
||||||
|
Some('c'),
|
||||||
|
)
|
||||||
.rest("rest", SyntaxShape::Any, "the parameter(s) for the block")
|
.rest("rest", SyntaxShape::Any, "the parameter(s) for the block")
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
@ -37,6 +44,7 @@ impl Command for Do {
|
|||||||
let block: CaptureBlock = call.req(engine_state, stack, 0)?;
|
let block: CaptureBlock = call.req(engine_state, stack, 0)?;
|
||||||
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
|
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
|
||||||
let ignore_errors = call.has_flag("ignore-errors");
|
let ignore_errors = call.has_flag("ignore-errors");
|
||||||
|
let capture_errors = call.has_flag("capture-errors");
|
||||||
|
|
||||||
let mut stack = stack.captures_to_stack(&block.captures);
|
let mut stack = stack.captures_to_stack(&block.captures);
|
||||||
let block = engine_state.get_block(block.block_id);
|
let block = engine_state.get_block(block.block_id);
|
||||||
@ -85,7 +93,7 @@ impl Command for Do {
|
|||||||
block,
|
block,
|
||||||
input,
|
input,
|
||||||
call.redirect_stdout,
|
call.redirect_stdout,
|
||||||
ignore_errors,
|
ignore_errors || capture_errors,
|
||||||
);
|
);
|
||||||
|
|
||||||
if ignore_errors {
|
if ignore_errors {
|
||||||
@ -93,6 +101,11 @@ impl Command for Do {
|
|||||||
Ok(x) => Ok(x),
|
Ok(x) => Ok(x),
|
||||||
Err(_) => Ok(PipelineData::new(call.head)),
|
Err(_) => Ok(PipelineData::new(call.head)),
|
||||||
}
|
}
|
||||||
|
} else if capture_errors {
|
||||||
|
match result {
|
||||||
|
Ok(x) => Ok(x),
|
||||||
|
Err(err) => Ok((Value::Error { error: err }).into_pipeline_data()),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,10 @@ impl Command for Echo {
|
|||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
"Unlike `print`, this command returns an actual value that will be passed to the next command of the pipeline."
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -2,8 +2,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||||
Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -16,7 +15,7 @@ impl Command for ErrorMake {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("error make")
|
Signature::build("error make")
|
||||||
.optional("error_struct", SyntaxShape::Record, "the error to create")
|
.required("error_struct", SyntaxShape::Record, "the error to create")
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,43 +32,20 @@ impl Command for ErrorMake {
|
|||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let arg: Value = call.req(engine_state, stack, 0)?;
|
||||||
let arg: Option<Value> = call.opt(engine_state, stack, 0)?;
|
|
||||||
|
|
||||||
if let Some(arg) = arg {
|
Err(make_error(&arg, span).unwrap_or_else(|| {
|
||||||
Ok(make_error(&arg, span)
|
ShellError::GenericError(
|
||||||
.map(|err| Value::Error { error: err })
|
|
||||||
.unwrap_or_else(|| Value::Error {
|
|
||||||
error: ShellError::GenericError(
|
|
||||||
"Creating error value not supported.".into(),
|
"Creating error value not supported.".into(),
|
||||||
"unsupported error format".into(),
|
"unsupported error format".into(),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
),
|
|
||||||
})
|
|
||||||
.into_pipeline_data())
|
|
||||||
} else {
|
|
||||||
input.map(
|
|
||||||
move |value| {
|
|
||||||
make_error(&value, span)
|
|
||||||
.map(|err| Value::Error { error: err })
|
|
||||||
.unwrap_or_else(|| Value::Error {
|
|
||||||
error: ShellError::GenericError(
|
|
||||||
"Creating error value not supported.".into(),
|
|
||||||
"unsupported error format".into(),
|
|
||||||
Some(span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
ctrlc,
|
|
||||||
)
|
)
|
||||||
}
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -28,7 +28,33 @@ impl Command for ExportDefEnv {
|
|||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
fn extra_usage(&self) -> &str {
|
||||||
r#"This command is a parser keyword. For details, check:
|
r#"This command is a parser keyword. For details, check:
|
||||||
https://www.nushell.sh/book/thinking_in_nushell.html"#
|
https://www.nushell.sh/book/thinking_in_nushell.html
|
||||||
|
|
||||||
|
=== EXTRA NOTE ===
|
||||||
|
All blocks are scoped, including variable definition and environment variable changes.
|
||||||
|
|
||||||
|
Because of this, the following doesn't work:
|
||||||
|
|
||||||
|
export def-env cd_with_fallback [arg = ""] {
|
||||||
|
let fall_back_path = "/tmp"
|
||||||
|
if $arg != "" {
|
||||||
|
cd $arg
|
||||||
|
} else {
|
||||||
|
cd $fall_back_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Instead, you have to use cd in the top level scope:
|
||||||
|
|
||||||
|
export def-env cd_with_fallback [arg = ""] {
|
||||||
|
let fall_back_path = "/tmp"
|
||||||
|
let path = if $arg != "" {
|
||||||
|
$arg
|
||||||
|
} else {
|
||||||
|
$fall_back_path
|
||||||
|
}
|
||||||
|
cd $path
|
||||||
|
}"#
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_parser_keyword(&self) -> bool {
|
fn is_parser_keyword(&self) -> bool {
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
const NEWLINE_ESCAPE_CODE: &str = "<\\n>";
|
|
||||||
|
|
||||||
fn decode_newlines(escaped: &str) -> String {
|
|
||||||
escaped.replace(NEWLINE_ESCAPE_CODE, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct History;
|
|
||||||
|
|
||||||
impl Command for History {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"history"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Get the command history"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
|
||||||
Signature::build("history")
|
|
||||||
.switch("clear", "Clears out the history entries", Some('c'))
|
|
||||||
.category(Category::Core)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
_stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
if let Some(config_path) = nu_path::config_dir() {
|
|
||||||
let clear = call.has_flag("clear");
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
|
||||||
|
|
||||||
let mut history_path = config_path;
|
|
||||||
history_path.push("nushell");
|
|
||||||
history_path.push("history.txt");
|
|
||||||
|
|
||||||
if clear {
|
|
||||||
let _ = std::fs::remove_file(history_path);
|
|
||||||
Ok(PipelineData::new(head))
|
|
||||||
} else {
|
|
||||||
let contents = std::fs::read_to_string(history_path);
|
|
||||||
|
|
||||||
if let Ok(contents) = contents {
|
|
||||||
Ok(contents
|
|
||||||
.lines()
|
|
||||||
.enumerate()
|
|
||||||
.map(move |(index, command)| Value::Record {
|
|
||||||
cols: vec!["command".to_string(), "index".to_string()],
|
|
||||||
vals: vec![
|
|
||||||
Value::String {
|
|
||||||
val: decode_newlines(command),
|
|
||||||
span: head,
|
|
||||||
},
|
|
||||||
Value::Int {
|
|
||||||
val: index as i64,
|
|
||||||
span: head,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
span: head,
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_iter()
|
|
||||||
.into_pipeline_data(ctrlc))
|
|
||||||
} else {
|
|
||||||
Err(ShellError::FileNotFound(head))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(ShellError::FileNotFound(head))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
example: "history | length",
|
|
||||||
description: "Get current history length",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
example: "history | last 5",
|
|
||||||
description: "Show last 5 commands you have ran",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
example: "history | wrap cmd | where cmd =~ cargo",
|
|
||||||
description: "Search all the commands from history that contains 'cargo'",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,7 +16,6 @@ mod extern_;
|
|||||||
mod for_;
|
mod for_;
|
||||||
mod help;
|
mod help;
|
||||||
mod hide;
|
mod hide;
|
||||||
mod history;
|
|
||||||
mod if_;
|
mod if_;
|
||||||
mod ignore;
|
mod ignore;
|
||||||
mod let_;
|
mod let_;
|
||||||
@ -24,7 +23,6 @@ mod metadata;
|
|||||||
mod module;
|
mod module;
|
||||||
pub(crate) mod overlay;
|
pub(crate) mod overlay;
|
||||||
mod source;
|
mod source;
|
||||||
mod tutor;
|
|
||||||
mod use_;
|
mod use_;
|
||||||
mod version;
|
mod version;
|
||||||
|
|
||||||
@ -46,7 +44,6 @@ pub use extern_::Extern;
|
|||||||
pub use for_::For;
|
pub use for_::For;
|
||||||
pub use help::Help;
|
pub use help::Help;
|
||||||
pub use hide::Hide;
|
pub use hide::Hide;
|
||||||
pub use history::History;
|
|
||||||
pub use if_::If;
|
pub use if_::If;
|
||||||
pub use ignore::Ignore;
|
pub use ignore::Ignore;
|
||||||
pub use let_::Let;
|
pub use let_::Let;
|
||||||
@ -54,7 +51,6 @@ pub use metadata::Metadata;
|
|||||||
pub use module::Module;
|
pub use module::Module;
|
||||||
pub use overlay::*;
|
pub use overlay::*;
|
||||||
pub use source::Source;
|
pub use source::Source;
|
||||||
pub use tutor::Tutor;
|
|
||||||
pub use use_::Use;
|
pub use use_::Use;
|
||||||
pub use version::Version;
|
pub use version::Version;
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
use crate::{
|
use crate::SQLiteDatabase;
|
||||||
database::values::dsl::{ExprDb, SelectDb},
|
|
||||||
SQLiteDatabase,
|
|
||||||
};
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
};
|
};
|
||||||
use sqlparser::ast::{Ident, SelectItem, SetExpr, Statement, TableAlias, TableFactor};
|
use sqlparser::ast::{Ident, SetExpr, Statement, TableAlias, TableFactor};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AliasExpr;
|
pub struct AliasDb;
|
||||||
|
|
||||||
impl Command for AliasExpr {
|
impl Command for AliasDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db as"
|
"as"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.required("alias", SyntaxShape::String, "alias name")
|
.required("alias", SyntaxShape::String, "alias name")
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
.category(Category::Custom("database".into()))
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,11 +29,59 @@ impl Command for AliasExpr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
description: "Creates an alias for a column selection",
|
Example {
|
||||||
example: "db col name_a | db as new_a",
|
description: "Creates an alias for a selected table",
|
||||||
result: None,
|
example: r#"open db.mysql
|
||||||
}]
|
| into db
|
||||||
|
| select a
|
||||||
|
| from table_1
|
||||||
|
| as t1
|
||||||
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT a FROM table_1 AS t1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Creates an alias for a derived table",
|
||||||
|
example: r#"open db.mysql
|
||||||
|
| into db
|
||||||
|
| select a
|
||||||
|
| from (
|
||||||
|
open db.mysql
|
||||||
|
| into db
|
||||||
|
| select a b
|
||||||
|
| from table_a
|
||||||
|
)
|
||||||
|
| as t1
|
||||||
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT a FROM (SELECT a, b FROM table_a) AS t1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -48,51 +96,11 @@ impl Command for AliasExpr {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let alias: String = call.req(engine_state, stack, 0)?;
|
let alias: String = call.req(engine_state, stack, 0)?;
|
||||||
let value = input.into_value(call.head);
|
|
||||||
|
|
||||||
if let Ok(expr) = ExprDb::try_from_value(&value) {
|
let db = SQLiteDatabase::try_from_pipeline(input, call.head)?;
|
||||||
alias_selection(expr.into_native().into(), alias, call)
|
|
||||||
} else if let Ok(select) = SelectDb::try_from_value(&value) {
|
|
||||||
alias_selection(select, alias, call)
|
|
||||||
} else if let Ok(db) = SQLiteDatabase::try_from_value(value.clone()) {
|
|
||||||
alias_db(db, alias, call)
|
alias_db(db, alias, call)
|
||||||
} else {
|
|
||||||
Err(ShellError::CantConvert(
|
|
||||||
"expression or query".into(),
|
|
||||||
value.get_type().to_string(),
|
|
||||||
value.span()?,
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn alias_selection(
|
|
||||||
select: SelectDb,
|
|
||||||
alias: String,
|
|
||||||
call: &Call,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let select = match select.into_native() {
|
|
||||||
SelectItem::UnnamedExpr(expr) => SelectItem::ExprWithAlias {
|
|
||||||
expr,
|
|
||||||
alias: Ident {
|
|
||||||
value: alias,
|
|
||||||
quote_style: None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SelectItem::ExprWithAlias { expr, .. } => SelectItem::ExprWithAlias {
|
|
||||||
expr,
|
|
||||||
alias: Ident {
|
|
||||||
value: alias,
|
|
||||||
quote_style: None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
select => select,
|
|
||||||
};
|
|
||||||
|
|
||||||
let select: SelectDb = select.into();
|
|
||||||
Ok(select.into_value(call.head).into_pipeline_data())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn alias_db(
|
fn alias_db(
|
||||||
mut db: SQLiteDatabase,
|
mut db: SQLiteDatabase,
|
||||||
@ -142,7 +150,7 @@ fn alias_db(
|
|||||||
},
|
},
|
||||||
s => {
|
s => {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"Connection doesnt define a query".into(),
|
"Connection doesn't define a query".into(),
|
||||||
format!("Expected a connection with query. Got {}", s),
|
format!("Expected a connection with query. Got {}", s),
|
||||||
Some(call.head),
|
Some(call.head),
|
||||||
None,
|
None,
|
||||||
@ -152,3 +160,19 @@ fn alias_db(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::{FromDb, ProjectionDb};
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![
|
||||||
|
Box::new(AliasDb {}),
|
||||||
|
Box::new(ProjectionDb {}),
|
||||||
|
Box::new(FromDb {}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,7 +6,7 @@ use nu_protocol::{
|
|||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
Value,
|
Type, Value,
|
||||||
};
|
};
|
||||||
use sqlparser::ast::{BinaryOperator, Expr, Query, Select, SetExpr, Statement};
|
use sqlparser::ast::{BinaryOperator, Expr, Query, Select, SetExpr, Statement};
|
||||||
|
|
||||||
@ -15,16 +15,18 @@ pub struct AndDb;
|
|||||||
|
|
||||||
impl Command for AndDb {
|
impl Command for AndDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db and"
|
"and"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Includes an AND clause for a query or expression"
|
"Includes an AND clause for a query"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.required("where", SyntaxShape::Any, "Where expression on the table")
|
.required("where", SyntaxShape::Any, "Where expression on the table")
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
.category(Category::Custom("database".into()))
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,23 +37,52 @@ impl Command for AndDb {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "selects a column from a database with a where clause",
|
description: "Selects a column from a database with an AND clause",
|
||||||
example: r#"db open db.mysql
|
example: r#"open db.mysql
|
||||||
| db select a
|
| into db
|
||||||
| db from table_1
|
| select a
|
||||||
| db where ((db col a) > 1)
|
| from table_1
|
||||||
| db and ((db col b) == 1)
|
| where ((field a) > 1)
|
||||||
| db describe"#,
|
| and ((field b) == 1)
|
||||||
result: None,
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT a FROM table_1 WHERE a > 1 AND b = 1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Creates a nested where clause",
|
description: "Creates a AND clause combined with an expression AND",
|
||||||
example: r#"db open db.mysql
|
example: r#"open db.mysql
|
||||||
| db select a
|
| into db
|
||||||
| db from table_1
|
| select a
|
||||||
| db where ((db col a) > 1 | db and ((db col a) < 10))
|
| from table_1
|
||||||
| db describe"#,
|
| where ((field a) > 1 | and ((field a) < 10))
|
||||||
result: None,
|
| and ((field b) == 1)
|
||||||
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT a FROM table_1 WHERE (a > 1 AND a < 10) AND b = 1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -66,24 +97,13 @@ impl Command for AndDb {
|
|||||||
let value: Value = call.req(engine_state, stack, 0)?;
|
let value: Value = call.req(engine_state, stack, 0)?;
|
||||||
let expr = ExprDb::try_from_value(&value)?.into_native();
|
let expr = ExprDb::try_from_value(&value)?.into_native();
|
||||||
|
|
||||||
let value = input.into_value(call.head);
|
let mut db = SQLiteDatabase::try_from_pipeline(input, call.head)?;
|
||||||
if let Ok(expression) = ExprDb::try_from_value(&value) {
|
|
||||||
let expression = Expr::BinaryOp {
|
|
||||||
left: Box::new(expression.into_native()),
|
|
||||||
op: BinaryOperator::And,
|
|
||||||
right: Box::new(expr),
|
|
||||||
};
|
|
||||||
|
|
||||||
let expression: ExprDb = Expr::Nested(Box::new(expression)).into();
|
|
||||||
|
|
||||||
Ok(expression.into_value(call.head).into_pipeline_data())
|
|
||||||
} else if let Ok(mut db) = SQLiteDatabase::try_from_value(value.clone()) {
|
|
||||||
match db.statement.as_mut() {
|
match db.statement.as_mut() {
|
||||||
Some(statement) => match statement {
|
Some(statement) => match statement {
|
||||||
Statement::Query(query) => modify_query(query, expr, call.head)?,
|
Statement::Query(query) => modify_query(query, expr, call.head)?,
|
||||||
s => {
|
s => {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"Connection doesnt define a query".into(),
|
"Connection doesn't define a query".into(),
|
||||||
format!("Expected a connection with query. Got {}", s),
|
format!("Expected a connection with query. Got {}", s),
|
||||||
Some(call.head),
|
Some(call.head),
|
||||||
None,
|
None,
|
||||||
@ -103,14 +123,6 @@ impl Command for AndDb {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok(db.into_value(call.head).into_pipeline_data())
|
Ok(db.into_value(call.head).into_pipeline_data())
|
||||||
} else {
|
|
||||||
Err(ShellError::CantConvert(
|
|
||||||
"expression or query".into(),
|
|
||||||
value.get_type().to_string(),
|
|
||||||
value.span()?,
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,3 +162,23 @@ fn modify_select(select: &mut Box<Select>, expression: Expr, span: Span) -> Resu
|
|||||||
select.as_mut().selection = Some(new_expression);
|
select.as_mut().selection = Some(new_expression);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::super::expressions::{AndExpr, FieldExpr};
|
||||||
|
use super::super::{FromDb, ProjectionDb, WhereDb};
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![
|
||||||
|
Box::new(AndDb {}),
|
||||||
|
Box::new(ProjectionDb {}),
|
||||||
|
Box::new(FromDb {}),
|
||||||
|
Box::new(WhereDb {}),
|
||||||
|
Box::new(FieldExpr {}),
|
||||||
|
Box::new(AndExpr {}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
use crate::database::values::dsl::ExprDb;
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::Call,
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ColExpr;
|
|
||||||
|
|
||||||
impl Command for ColExpr {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"db col"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build(self.name())
|
|
||||||
.required("name", SyntaxShape::String, "column name")
|
|
||||||
.category(Category::Custom("database".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Creates column expression for database"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Creates a named column expression",
|
|
||||||
example: "db col name_1",
|
|
||||||
result: None,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["database", "column", "expression"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let value: Value = call.req(engine_state, stack, 0)?;
|
|
||||||
let expression = ExprDb::try_from_value(&value)?;
|
|
||||||
|
|
||||||
Ok(expression.into_value(call.head).into_pipeline_data())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::SQLiteDatabase;
|
use super::super::SQLiteDatabase;
|
||||||
@ -11,21 +11,24 @@ pub struct CollectDb;
|
|||||||
|
|
||||||
impl Command for CollectDb {
|
impl Command for CollectDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db collect"
|
"collect"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name()).category(Category::Custom("database".into()))
|
Signature::build(self.name())
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Any)
|
||||||
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Query a database using SQL."
|
"Collects a query from a database database connection"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Collect from a select query",
|
description: "Collect from a select query",
|
||||||
example: "open foo.db | db select a | db from table_1 | db collect",
|
example: "open foo.db | into db | select a | from table_1 | collect",
|
||||||
result: None,
|
result: None,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
use nu_engine::get_full_help;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::Call,
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, IntoPipelineData, PipelineData, ShellError, Signature, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Database;
|
|
||||||
|
|
||||||
impl Command for Database {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"db"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Database commands"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build(self.name()).category(Category::Custom("database".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
Ok(Value::String {
|
|
||||||
val: get_full_help(
|
|
||||||
&Database.signature(),
|
|
||||||
&Database.examples(),
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
),
|
|
||||||
span: call.head,
|
|
||||||
}
|
|
||||||
.into_pipeline_data())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::SQLiteDatabase;
|
use super::super::SQLiteDatabase;
|
||||||
@ -11,11 +11,14 @@ pub struct DescribeDb;
|
|||||||
|
|
||||||
impl Command for DescribeDb {
|
impl Command for DescribeDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db describe"
|
"describe"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name()).category(Category::Custom("database".into()))
|
Signature::build(self.name())
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Any)
|
||||||
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -25,13 +28,26 @@ impl Command for DescribeDb {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Describe SQLite database constructed query",
|
description: "Describe SQLite database constructed query",
|
||||||
example: "db open foo.db | db select table_1 | db describe",
|
example: "open foo.db | into db | select col_1 | from table_1 | describe",
|
||||||
result: None,
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "foo.db".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT col_1 FROM table_1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
vec!["database", "SQLite"]
|
vec!["database", "SQLite", "describe"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -45,3 +61,19 @@ impl Command for DescribeDb {
|
|||||||
Ok(db.describe(call.head).into_pipeline_data())
|
Ok(db.describe(call.head).into_pipeline_data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::{FromDb, ProjectionDb};
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![
|
||||||
|
Box::new(DescribeDb {}),
|
||||||
|
Box::new(ProjectionDb {}),
|
||||||
|
Box::new(FromDb {}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,7 +5,8 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
};
|
};
|
||||||
use sqlparser::ast::{Ident, Query, Select, SetExpr, Statement, TableAlias, TableWithJoins};
|
use sqlparser::ast::{Ident, Query, Select, SetExpr, Statement, TableAlias, TableWithJoins};
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ pub struct FromDb;
|
|||||||
|
|
||||||
impl Command for FromDb {
|
impl Command for FromDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db from"
|
"from"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -34,6 +35,8 @@ impl Command for FromDb {
|
|||||||
"Alias for the selected table",
|
"Alias for the selected table",
|
||||||
Some('a'),
|
Some('a'),
|
||||||
)
|
)
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
.category(Category::Custom("database".into()))
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,9 +46,22 @@ impl Command for FromDb {
|
|||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Selects table from database",
|
description: "Selects a table from database",
|
||||||
example: "db open db.mysql | db from table_a",
|
example: "open db.mysql | into db | from table_a | describe",
|
||||||
result: None,
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT FROM table_a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,3 +196,14 @@ fn create_table(
|
|||||||
|
|
||||||
Ok(table)
|
Ok(table)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![Box::new(FromDb {})])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
use crate::database::values::dsl::ExprDb;
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::Call,
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
|
||||||
};
|
|
||||||
use sqlparser::ast::{Expr, Function, FunctionArg, FunctionArgExpr, Ident, ObjectName};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct FunctionExpr;
|
|
||||||
|
|
||||||
impl Command for FunctionExpr {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"db fn"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build(self.name())
|
|
||||||
.required("name", SyntaxShape::String, "function name")
|
|
||||||
.switch("distinct", "distict values", Some('d'))
|
|
||||||
.rest("arguments", SyntaxShape::Any, "function arguments")
|
|
||||||
.category(Category::Custom("database".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Creates function expression for a select operation"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Creates a function expression",
|
|
||||||
example: "db fn count name_1",
|
|
||||||
result: None,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["database", "function", "expression"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let name: String = call.req(engine_state, stack, 0)?;
|
|
||||||
let vals: Vec<Value> = call.rest(engine_state, stack, 1)?;
|
|
||||||
let value = Value::List {
|
|
||||||
vals,
|
|
||||||
span: call.head,
|
|
||||||
};
|
|
||||||
let expressions = ExprDb::extract_exprs(value)?;
|
|
||||||
|
|
||||||
let name: Vec<Ident> = name
|
|
||||||
.split('.')
|
|
||||||
.map(|part| Ident {
|
|
||||||
value: part.to_string(),
|
|
||||||
quote_style: None,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let name = ObjectName(name);
|
|
||||||
|
|
||||||
let args: Vec<FunctionArg> = expressions
|
|
||||||
.into_iter()
|
|
||||||
.map(|expr| {
|
|
||||||
let arg = FunctionArgExpr::Expr(expr);
|
|
||||||
|
|
||||||
FunctionArg::Unnamed(arg)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let expression: ExprDb = Expr::Function(Function {
|
|
||||||
name,
|
|
||||||
args,
|
|
||||||
over: None,
|
|
||||||
distinct: call.has_flag("distinct"),
|
|
||||||
})
|
|
||||||
.into();
|
|
||||||
|
|
||||||
Ok(expression.into_value(call.head).into_pipeline_data())
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,8 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
};
|
};
|
||||||
use sqlparser::ast::{SetExpr, Statement};
|
use sqlparser::ast::{SetExpr, Statement};
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ pub struct GroupByDb;
|
|||||||
|
|
||||||
impl Command for GroupByDb {
|
impl Command for GroupByDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db group-by"
|
"group-by"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -28,6 +29,8 @@ impl Command for GroupByDb {
|
|||||||
SyntaxShape::Any,
|
SyntaxShape::Any,
|
||||||
"Select expression(s) on the table",
|
"Select expression(s) on the table",
|
||||||
)
|
)
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
.category(Category::Custom("database".into()))
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,15 +39,54 @@ impl Command for GroupByDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
description: "orders query by a column",
|
Example {
|
||||||
example: r#"db open db.mysql
|
description: "groups by column a and calculates the max",
|
||||||
| db from table_a
|
example: r#"open db.mysql
|
||||||
| db select a
|
| into db
|
||||||
| db group-by a
|
| from table_a
|
||||||
| db describe"#,
|
| select (fn max a)
|
||||||
result: None,
|
| group-by a
|
||||||
}]
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT max(a) FROM table_a GROUP BY a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "groups by column column a and counts records",
|
||||||
|
example: r#"open db.mysql
|
||||||
|
| into db
|
||||||
|
| from table_a
|
||||||
|
| select (fn count *)
|
||||||
|
| group-by a
|
||||||
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT count(*) FROM table_a GROUP BY a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -100,3 +142,24 @@ impl Command for GroupByDb {
|
|||||||
Ok(db.into_value(call.head).into_pipeline_data())
|
Ok(db.into_value(call.head).into_pipeline_data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::super::expressions::{FieldExpr, FunctionExpr, OrExpr};
|
||||||
|
use super::super::{FromDb, ProjectionDb, WhereDb};
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![
|
||||||
|
Box::new(GroupByDb {}),
|
||||||
|
Box::new(ProjectionDb {}),
|
||||||
|
Box::new(FunctionExpr {}),
|
||||||
|
Box::new(FromDb {}),
|
||||||
|
Box::new(WhereDb {}),
|
||||||
|
Box::new(FieldExpr {}),
|
||||||
|
Box::new(OrExpr {}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,7 +4,8 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
};
|
};
|
||||||
use sqlparser::ast::{
|
use sqlparser::ast::{
|
||||||
Ident, Join, JoinConstraint, JoinOperator, Select, SetExpr, Statement, TableAlias,
|
Ident, Join, JoinConstraint, JoinOperator, Select, SetExpr, Statement, TableAlias,
|
||||||
@ -15,7 +16,7 @@ pub struct JoinDb;
|
|||||||
|
|
||||||
impl Command for JoinDb {
|
impl Command for JoinDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db join"
|
"join"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -40,6 +41,8 @@ impl Command for JoinDb {
|
|||||||
.switch("right", "right outer join", Some('r'))
|
.switch("right", "right outer join", Some('r'))
|
||||||
.switch("outer", "full outer join", Some('o'))
|
.switch("outer", "full outer join", Some('o'))
|
||||||
.switch("cross", "cross join", Some('c'))
|
.switch("cross", "cross join", Some('c'))
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
.category(Category::Custom("database".into()))
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,11 +51,61 @@ impl Command for JoinDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
description: "",
|
Example {
|
||||||
example: "",
|
description: "joins two tables on col_b",
|
||||||
result: None,
|
example: r#"open db.mysql
|
||||||
}]
|
| into db
|
||||||
|
| select col_a
|
||||||
|
| from table_1 --as t1
|
||||||
|
| join table_2 col_b --as t2
|
||||||
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT col_a FROM table_1 AS t1 JOIN table_2 AS t2 ON col_b"
|
||||||
|
.into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "joins a table with a derived table using aliases",
|
||||||
|
example: r#"open db.mysql
|
||||||
|
| into db
|
||||||
|
| select col_a
|
||||||
|
| from table_1 --as t1
|
||||||
|
| join (
|
||||||
|
open db.mysql
|
||||||
|
| into db
|
||||||
|
| select col_c
|
||||||
|
| from table_2
|
||||||
|
) ((field t1.col_a) == (field t2.col_c)) --as t2 --right
|
||||||
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT col_a FROM table_1 AS t1 RIGHT JOIN (SELECT col_c FROM table_2) AS t2 ON t1.col_a = t2.col_c"
|
||||||
|
.into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -176,3 +229,23 @@ fn modify_from(
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::super::expressions::{FieldExpr, OrExpr};
|
||||||
|
use super::super::{FromDb, ProjectionDb, WhereDb};
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![
|
||||||
|
Box::new(JoinDb {}),
|
||||||
|
Box::new(ProjectionDb {}),
|
||||||
|
Box::new(FromDb {}),
|
||||||
|
Box::new(WhereDb {}),
|
||||||
|
Box::new(FieldExpr {}),
|
||||||
|
Box::new(OrExpr {}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,7 +4,8 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
};
|
};
|
||||||
use sqlparser::ast::Statement;
|
use sqlparser::ast::Statement;
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ pub struct LimitDb;
|
|||||||
|
|
||||||
impl Command for LimitDb {
|
impl Command for LimitDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db limit"
|
"limit"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -27,6 +28,8 @@ impl Command for LimitDb {
|
|||||||
SyntaxShape::Int,
|
SyntaxShape::Int,
|
||||||
"Number of rows to extract for query",
|
"Number of rows to extract for query",
|
||||||
)
|
)
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
.category(Category::Custom("database".into()))
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,12 +40,26 @@ impl Command for LimitDb {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Limits selection from table",
|
description: "Limits selection from table",
|
||||||
example: r#"db open db.mysql
|
example: r#"open db.mysql
|
||||||
| db from table_a
|
| into db
|
||||||
| db select a
|
| from table_a
|
||||||
| db limit 10
|
| select a
|
||||||
| db describe"#,
|
| limit 10
|
||||||
result: None,
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT a FROM table_a LIMIT 10".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,3 +101,23 @@ impl Command for LimitDb {
|
|||||||
Ok(db.into_value(call.head).into_pipeline_data())
|
Ok(db.into_value(call.head).into_pipeline_data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::super::expressions::{FieldExpr, OrExpr};
|
||||||
|
use super::super::{FromDb, ProjectionDb, WhereDb};
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![
|
||||||
|
Box::new(LimitDb {}),
|
||||||
|
Box::new(ProjectionDb {}),
|
||||||
|
Box::new(FromDb {}),
|
||||||
|
Box::new(WhereDb {}),
|
||||||
|
Box::new(FieldExpr {}),
|
||||||
|
Box::new(OrExpr {}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,22 +3,19 @@ pub mod conversions;
|
|||||||
|
|
||||||
mod alias;
|
mod alias;
|
||||||
mod and;
|
mod and;
|
||||||
mod col;
|
|
||||||
mod collect;
|
mod collect;
|
||||||
mod command;
|
|
||||||
mod describe;
|
mod describe;
|
||||||
mod from;
|
mod from;
|
||||||
mod function;
|
|
||||||
mod group_by;
|
mod group_by;
|
||||||
mod join;
|
mod join;
|
||||||
mod limit;
|
mod limit;
|
||||||
mod open;
|
mod open;
|
||||||
mod or;
|
mod or;
|
||||||
mod order_by;
|
mod order_by;
|
||||||
mod over;
|
|
||||||
mod query;
|
mod query;
|
||||||
mod schema;
|
mod schema;
|
||||||
mod select;
|
mod select;
|
||||||
|
mod to_db;
|
||||||
mod where_;
|
mod where_;
|
||||||
|
|
||||||
// Temporal module to create Query objects
|
// Temporal module to create Query objects
|
||||||
@ -27,27 +24,24 @@ use testing::TestingDb;
|
|||||||
|
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
|
|
||||||
use alias::AliasExpr;
|
use alias::AliasDb;
|
||||||
use and::AndDb;
|
use and::AndDb;
|
||||||
use col::ColExpr;
|
|
||||||
use collect::CollectDb;
|
use collect::CollectDb;
|
||||||
use command::Database;
|
pub(crate) use describe::DescribeDb;
|
||||||
use describe::DescribeDb;
|
pub(crate) use from::FromDb;
|
||||||
use from::FromDb;
|
|
||||||
use function::FunctionExpr;
|
|
||||||
use group_by::GroupByDb;
|
use group_by::GroupByDb;
|
||||||
use join::JoinDb;
|
use join::JoinDb;
|
||||||
use limit::LimitDb;
|
use limit::LimitDb;
|
||||||
use open::OpenDb;
|
use open::OpenDb;
|
||||||
use or::OrDb;
|
use or::OrDb;
|
||||||
use order_by::OrderByDb;
|
use order_by::OrderByDb;
|
||||||
use over::OverExpr;
|
|
||||||
use query::QueryDb;
|
use query::QueryDb;
|
||||||
use schema::SchemaDb;
|
use schema::SchemaDb;
|
||||||
use select::ProjectionDb;
|
pub(crate) use select::ProjectionDb;
|
||||||
|
pub(crate) use to_db::ToDataBase;
|
||||||
use where_::WhereDb;
|
use where_::WhereDb;
|
||||||
|
|
||||||
pub fn add_database_decls(working_set: &mut StateWorkingSet) {
|
pub fn add_commands_decls(working_set: &mut StateWorkingSet) {
|
||||||
macro_rules! bind_command {
|
macro_rules! bind_command {
|
||||||
( $command:expr ) => {
|
( $command:expr ) => {
|
||||||
working_set.add_decl(Box::new($command));
|
working_set.add_decl(Box::new($command));
|
||||||
@ -59,21 +53,18 @@ pub fn add_database_decls(working_set: &mut StateWorkingSet) {
|
|||||||
|
|
||||||
// Series commands
|
// Series commands
|
||||||
bind_command!(
|
bind_command!(
|
||||||
AliasExpr,
|
ToDataBase,
|
||||||
|
AliasDb,
|
||||||
AndDb,
|
AndDb,
|
||||||
ColExpr,
|
|
||||||
CollectDb,
|
CollectDb,
|
||||||
Database,
|
|
||||||
DescribeDb,
|
DescribeDb,
|
||||||
FromDb,
|
FromDb,
|
||||||
FunctionExpr,
|
|
||||||
GroupByDb,
|
GroupByDb,
|
||||||
JoinDb,
|
JoinDb,
|
||||||
LimitDb,
|
LimitDb,
|
||||||
OpenDb,
|
OpenDb,
|
||||||
OrderByDb,
|
OrderByDb,
|
||||||
OrDb,
|
OrDb,
|
||||||
OverExpr,
|
|
||||||
QueryDb,
|
QueryDb,
|
||||||
ProjectionDb,
|
ProjectionDb,
|
||||||
SchemaDb,
|
SchemaDb,
|
||||||
|
@ -4,6 +4,7 @@ use nu_protocol::{
|
|||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
|
||||||
|
Type,
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@ -12,12 +13,14 @@ pub struct OpenDb;
|
|||||||
|
|
||||||
impl Command for OpenDb {
|
impl Command for OpenDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db open"
|
"open-db"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.required("query", SyntaxShape::Filepath, "SQLite file to be opened")
|
.required("query", SyntaxShape::Filepath, "SQLite file to be opened")
|
||||||
|
.input_type(Type::Any)
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
.category(Category::Custom("database".into()))
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,8 +34,8 @@ impl Command for OpenDb {
|
|||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Open a sqlite file",
|
description: "Creates a connection to a sqlite database based on the file name",
|
||||||
example: r#"db open file.sqlite"#,
|
example: r#"open-db file.sqlite"#,
|
||||||
result: None,
|
result: None,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use nu_protocol::{
|
|||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
Value,
|
Type, Value,
|
||||||
};
|
};
|
||||||
use sqlparser::ast::{BinaryOperator, Expr, Query, Select, SetExpr, Statement};
|
use sqlparser::ast::{BinaryOperator, Expr, Query, Select, SetExpr, Statement};
|
||||||
|
|
||||||
@ -15,16 +15,18 @@ pub struct OrDb;
|
|||||||
|
|
||||||
impl Command for OrDb {
|
impl Command for OrDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db or"
|
"or"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Includes an OR clause for a query or expression"
|
"Includes an OR clause for a query"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.required("where", SyntaxShape::Any, "Where expression on the table")
|
.required("where", SyntaxShape::Any, "Where expression on the table")
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
.category(Category::Custom("database".into()))
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,23 +37,52 @@ impl Command for OrDb {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "selects a column from a database with a where clause",
|
description: "selects a column from a database with an OR clause",
|
||||||
example: r#"db open db.mysql
|
example: r#"open db.mysql
|
||||||
| db select a
|
| into db
|
||||||
| db from table_1
|
| select a
|
||||||
| db where ((db col a) > 1)
|
| from table_1
|
||||||
| db or ((db col b) == 1)
|
| where ((field a) > 1)
|
||||||
| db describe"#,
|
| or ((field b) == 1)
|
||||||
result: None,
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT a FROM table_1 WHERE a > 1 OR b = 1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Creates a nested where clause",
|
description: "Creates an OR clause in the column names and a column",
|
||||||
example: r#"db open db.mysql
|
example: r#"open db.mysql
|
||||||
| db select a
|
| into db
|
||||||
| db from table_1
|
| select a
|
||||||
| db where ((db col a) > 1 | db or ((db col a) < 10))
|
| from table_1
|
||||||
| db describe"#,
|
| where ((field a) > 1 | or ((field a) < 10))
|
||||||
result: None,
|
| or ((field b) == 1)
|
||||||
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT a FROM table_1 WHERE (a > 1 OR a < 10) OR b = 1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -66,18 +97,7 @@ impl Command for OrDb {
|
|||||||
let value: Value = call.req(engine_state, stack, 0)?;
|
let value: Value = call.req(engine_state, stack, 0)?;
|
||||||
let expr = ExprDb::try_from_value(&value)?.into_native();
|
let expr = ExprDb::try_from_value(&value)?.into_native();
|
||||||
|
|
||||||
let value = input.into_value(call.head);
|
let mut db = SQLiteDatabase::try_from_pipeline(input, call.head)?;
|
||||||
if let Ok(expression) = ExprDb::try_from_value(&value) {
|
|
||||||
let expression = Expr::BinaryOp {
|
|
||||||
left: Box::new(expression.into_native()),
|
|
||||||
op: BinaryOperator::Or,
|
|
||||||
right: Box::new(expr),
|
|
||||||
};
|
|
||||||
|
|
||||||
let expression: ExprDb = Expr::Nested(Box::new(expression)).into();
|
|
||||||
|
|
||||||
Ok(expression.into_value(call.head).into_pipeline_data())
|
|
||||||
} else if let Ok(mut db) = SQLiteDatabase::try_from_value(value.clone()) {
|
|
||||||
match db.statement {
|
match db.statement {
|
||||||
Some(ref mut statement) => match statement {
|
Some(ref mut statement) => match statement {
|
||||||
Statement::Query(query) => modify_query(query, expr, call.head)?,
|
Statement::Query(query) => modify_query(query, expr, call.head)?,
|
||||||
@ -103,14 +123,6 @@ impl Command for OrDb {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok(db.into_value(call.head).into_pipeline_data())
|
Ok(db.into_value(call.head).into_pipeline_data())
|
||||||
} else {
|
|
||||||
Err(ShellError::CantConvert(
|
|
||||||
"expression or query".into(),
|
|
||||||
value.get_type().to_string(),
|
|
||||||
value.span()?,
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,3 +162,23 @@ fn modify_select(select: &mut Box<Select>, expression: Expr, span: Span) -> Resu
|
|||||||
select.as_mut().selection = Some(new_expression);
|
select.as_mut().selection = Some(new_expression);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::super::expressions::{FieldExpr, OrExpr};
|
||||||
|
use super::super::{FromDb, ProjectionDb, WhereDb};
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![
|
||||||
|
Box::new(OrDb {}),
|
||||||
|
Box::new(ProjectionDb {}),
|
||||||
|
Box::new(FromDb {}),
|
||||||
|
Box::new(WhereDb {}),
|
||||||
|
Box::new(FieldExpr {}),
|
||||||
|
Box::new(OrExpr {}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,7 +5,8 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
};
|
};
|
||||||
use sqlparser::ast::{Expr, OrderByExpr, Statement};
|
use sqlparser::ast::{Expr, OrderByExpr, Statement};
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ pub struct OrderByDb;
|
|||||||
|
|
||||||
impl Command for OrderByDb {
|
impl Command for OrderByDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db order-by"
|
"order-by"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -24,29 +25,71 @@ impl Command for OrderByDb {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.switch("ascending", "Order by ascending values", Some('a'))
|
.switch("ascending", "Order by ascending values", Some('a'))
|
||||||
.switch("nulls_first", "Show nulls first in order", Some('n'))
|
.switch("nulls-first", "Show nulls first in order", Some('n'))
|
||||||
.rest(
|
.rest(
|
||||||
"select",
|
"select",
|
||||||
SyntaxShape::Any,
|
SyntaxShape::Any,
|
||||||
"Select expression(s) on the table",
|
"Select expression(s) on the table",
|
||||||
)
|
)
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
.category(Category::Custom("database".into()))
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
vec!["database", "select"]
|
vec!["database", "order-by"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
|
Example {
|
||||||
description: "orders query by a column",
|
description: "orders query by a column",
|
||||||
example: r#"db open db.mysql
|
example: r#"open db.mysql
|
||||||
| db from table_a
|
| into db
|
||||||
| db select a
|
| from table_a
|
||||||
| db order-by a
|
| select a
|
||||||
| db describe"#,
|
| order-by a
|
||||||
result: None,
|
| describe"#,
|
||||||
}]
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT a FROM table_a ORDER BY a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "orders query by column a ascending and by column b",
|
||||||
|
example: r#"open db.mysql
|
||||||
|
| into db
|
||||||
|
| from table_a
|
||||||
|
| select a
|
||||||
|
| order-by a --ascending
|
||||||
|
| order-by b
|
||||||
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT a FROM table_a ORDER BY a ASC, b".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -57,7 +100,7 @@ impl Command for OrderByDb {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let asc = call.has_flag("ascending");
|
let asc = call.has_flag("ascending");
|
||||||
let nulls_first = call.has_flag("nulls_first");
|
let nulls_first = call.has_flag("nulls-first");
|
||||||
let expressions: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
let expressions: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
||||||
let expressions = Value::List {
|
let expressions = Value::List {
|
||||||
vals: expressions,
|
vals: expressions,
|
||||||
@ -155,3 +198,23 @@ fn update_connection(
|
|||||||
|
|
||||||
Ok(db.into_value(call.head).into_pipeline_data())
|
Ok(db.into_value(call.head).into_pipeline_data())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::super::expressions::{FieldExpr, OrExpr};
|
||||||
|
use super::super::{FromDb, ProjectionDb, WhereDb};
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![
|
||||||
|
Box::new(OrderByDb {}),
|
||||||
|
Box::new(ProjectionDb {}),
|
||||||
|
Box::new(FromDb {}),
|
||||||
|
Box::new(WhereDb {}),
|
||||||
|
Box::new(FieldExpr {}),
|
||||||
|
Box::new(OrExpr {}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
use crate::database::values::dsl::ExprDb;
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::Call,
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
|
||||||
};
|
|
||||||
use sqlparser::ast::{Expr, WindowSpec};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct OverExpr;
|
|
||||||
|
|
||||||
impl Command for OverExpr {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"db over"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build(self.name())
|
|
||||||
.rest(
|
|
||||||
"partition-by",
|
|
||||||
SyntaxShape::Any,
|
|
||||||
"columns to partition the window function",
|
|
||||||
)
|
|
||||||
.category(Category::Custom("database".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Adds a partition to an expression function"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Adds a partition to a function expresssion",
|
|
||||||
example: "db function avg col_a | db over col_b",
|
|
||||||
result: None,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["database", "column", "expression"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let vals: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
|
||||||
let value = Value::List {
|
|
||||||
vals,
|
|
||||||
span: call.head,
|
|
||||||
};
|
|
||||||
let partitions = ExprDb::extract_exprs(value)?;
|
|
||||||
|
|
||||||
let mut expression = ExprDb::try_from_pipeline(input, call.head)?;
|
|
||||||
match expression.as_mut() {
|
|
||||||
Expr::Function(function) => {
|
|
||||||
function.over = Some(WindowSpec {
|
|
||||||
partition_by: partitions,
|
|
||||||
order_by: Vec::new(),
|
|
||||||
window_frame: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
s => {
|
|
||||||
return Err(ShellError::GenericError(
|
|
||||||
"Expression doesnt define a function".into(),
|
|
||||||
format!("Expected an expression with a function. Got {}", s),
|
|
||||||
Some(call.head),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(expression.into_value(call.head).into_pipeline_data())
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,7 @@ use nu_protocol::{
|
|||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
|
||||||
|
Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::SQLiteDatabase;
|
use super::super::SQLiteDatabase;
|
||||||
@ -12,7 +13,7 @@ pub struct QueryDb;
|
|||||||
|
|
||||||
impl Command for QueryDb {
|
impl Command for QueryDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db query"
|
"query"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
@ -22,6 +23,8 @@ impl Command for QueryDb {
|
|||||||
SyntaxShape::String,
|
SyntaxShape::String,
|
||||||
"SQL to execute against the database",
|
"SQL to execute against the database",
|
||||||
)
|
)
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Any)
|
||||||
.category(Category::Custom("database".into()))
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,8 +34,8 @@ impl Command for QueryDb {
|
|||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Get 1 table out of a SQLite database",
|
description: "Execute a query statement using the database connection",
|
||||||
example: r#"db open foo.db | db query "SELECT * FROM Bar""#,
|
example: r#"open foo.db | into db | query "SELECT * FROM Bar""#,
|
||||||
result: None,
|
result: None,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use crate::database::values::definitions::{db::Db, db_row::DbRow, db_table::DbTa
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||||
};
|
};
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -11,21 +11,24 @@ pub struct SchemaDb;
|
|||||||
|
|
||||||
impl Command for SchemaDb {
|
impl Command for SchemaDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db schema"
|
"schema"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name()).category(Category::Custom("database".into()))
|
Signature::build(self.name())
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Any)
|
||||||
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Show database information, including its schema."
|
"Show sqlite database information, including its schema."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Show the schema of a SQLite database",
|
description: "Show the schema of a SQLite database",
|
||||||
example: r#"open foo.db | db schema"#,
|
example: r#"open foo.db | into db | schema"#,
|
||||||
result: None,
|
result: None,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use nu_protocol::{
|
|||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
Value,
|
Type, Value,
|
||||||
};
|
};
|
||||||
use sqlparser::ast::{Query, Select, SelectItem, SetExpr, Statement};
|
use sqlparser::ast::{Query, Select, SelectItem, SetExpr, Statement};
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ pub struct ProjectionDb;
|
|||||||
|
|
||||||
impl Command for ProjectionDb {
|
impl Command for ProjectionDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db select"
|
"select"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -27,6 +27,8 @@ impl Command for ProjectionDb {
|
|||||||
SyntaxShape::Any,
|
SyntaxShape::Any,
|
||||||
"Select expression(s) on the table",
|
"Select expression(s) on the table",
|
||||||
)
|
)
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
.category(Category::Custom("database".into()))
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,13 +40,43 @@ impl Command for ProjectionDb {
|
|||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "selects a column from a database",
|
description: "selects a column from a database",
|
||||||
example: "db open db.mysql | db select a | db describe",
|
example: "open db.mysql | into db | select a | describe",
|
||||||
result: None,
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "selects columns from a database",
|
description: "selects columns from a database using alias",
|
||||||
example: "db open db.mysql | db select a b c | db describe",
|
example: r#"open db.mysql
|
||||||
result: None,
|
| into db
|
||||||
|
| select (field a | as new_a) b c
|
||||||
|
| from table_1
|
||||||
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT a AS new_a, b, c FROM table_1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -104,7 +136,7 @@ fn modify_statement(
|
|||||||
Ok(statement)
|
Ok(statement)
|
||||||
}
|
}
|
||||||
s => Err(ShellError::GenericError(
|
s => Err(ShellError::GenericError(
|
||||||
"Connection doesnt define a statement".into(),
|
"Connection doesn't define a statement".into(),
|
||||||
format!("Expected a connection with query. Got {}", s),
|
format!("Expected a connection with query. Got {}", s),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
@ -129,3 +161,21 @@ fn create_select(projection: Vec<SelectItem>) -> Select {
|
|||||||
having: None,
|
having: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::super::expressions::{AliasExpr, FieldExpr};
|
||||||
|
use super::super::FromDb;
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![
|
||||||
|
Box::new(ProjectionDb {}),
|
||||||
|
Box::new(FromDb {}),
|
||||||
|
Box::new(FieldExpr {}),
|
||||||
|
Box::new(AliasExpr {}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,7 +13,7 @@ pub struct TestingDb;
|
|||||||
|
|
||||||
impl Command for TestingDb {
|
impl Command for TestingDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db testing"
|
"testing-db"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
@ -27,7 +27,7 @@ impl Command for TestingDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Create query object"
|
"Temporal Command: Create query object"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
50
crates/nu-command/src/database/commands/to_db.rs
Normal file
50
crates/nu-command/src/database/commands/to_db.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use super::super::SQLiteDatabase;
|
||||||
|
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ToDataBase;
|
||||||
|
|
||||||
|
impl Command for ToDataBase {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"into db"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Converts into an open db connection"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.input_type(Type::Any)
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
|
.category(Category::Custom("database".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["database", "into", "db"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Converts an open file into a db object",
|
||||||
|
example: "open db.mysql | into db",
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let db = SQLiteDatabase::try_from_pipeline(input, call.head)?;
|
||||||
|
Ok(db.into_value(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,8 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
};
|
};
|
||||||
use sqlparser::ast::{Expr, Query, Select, SetExpr, Statement};
|
use sqlparser::ast::{Expr, Query, Select, SetExpr, Statement};
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ pub struct WhereDb;
|
|||||||
|
|
||||||
impl Command for WhereDb {
|
impl Command for WhereDb {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"db where"
|
"where"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -24,6 +25,8 @@ impl Command for WhereDb {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.required("where", SyntaxShape::Any, "Where expression on the table")
|
.required("where", SyntaxShape::Any, "Where expression on the table")
|
||||||
|
.input_type(Type::Custom("database".into()))
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
.category(Category::Custom("database".into()))
|
.category(Category::Custom("database".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,12 +37,26 @@ impl Command for WhereDb {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "selects a column from a database with a where clause",
|
description: "selects a column from a database with a where clause",
|
||||||
example: r#"db open db.mysql
|
example: r#"open db.mysql
|
||||||
| db select a
|
| into db
|
||||||
| db from table_1
|
| select a
|
||||||
| db where ((db col a) > 1)
|
| from table_1
|
||||||
| db describe"#,
|
| where ((field a) > 1)
|
||||||
result: None,
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT a FROM table_1 WHERE a > 1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,3 +128,23 @@ fn create_select(expression: Expr) -> Select {
|
|||||||
having: None,
|
having: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::super::expressions::{FieldExpr, OrExpr};
|
||||||
|
use super::super::{FromDb, ProjectionDb};
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![
|
||||||
|
Box::new(WhereDb {}),
|
||||||
|
Box::new(ProjectionDb {}),
|
||||||
|
Box::new(FromDb {}),
|
||||||
|
Box::new(WhereDb {}),
|
||||||
|
Box::new(FieldExpr {}),
|
||||||
|
Box::new(OrExpr {}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
132
crates/nu-command/src/database/expressions/alias.rs
Normal file
132
crates/nu-command/src/database/expressions/alias.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
use crate::database::values::dsl::{ExprDb, SelectDb};
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
|
};
|
||||||
|
use sqlparser::ast::{Ident, SelectItem};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AliasExpr;
|
||||||
|
|
||||||
|
impl Command for AliasExpr {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"as"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.required("alias", SyntaxShape::String, "alias name")
|
||||||
|
.input_type(Type::Custom("db-expression".into()))
|
||||||
|
.output_type(Type::Custom("db-expression".into()))
|
||||||
|
.category(Category::Custom("db-expression".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Creates an alias for a column selection"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Creates an alias for a column selection",
|
||||||
|
example: "field name_a | as new_a | into nu",
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["expression".into(), "alias".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["value".into(), "quoted_style".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "name_a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "None".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["value".into(), "quoted_style".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "new_a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "None".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["database", "alias", "column"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let alias: String = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
|
let value = input.into_value(call.head);
|
||||||
|
if let Ok(expr) = ExprDb::try_from_value(&value) {
|
||||||
|
alias_selection(expr.into_native().into(), alias, call)
|
||||||
|
} else {
|
||||||
|
let select = SelectDb::try_from_value(&value)?;
|
||||||
|
alias_selection(select, alias, call)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alias_selection(
|
||||||
|
select: SelectDb,
|
||||||
|
alias: String,
|
||||||
|
call: &Call,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let select = match select.into_native() {
|
||||||
|
SelectItem::UnnamedExpr(expr) => SelectItem::ExprWithAlias {
|
||||||
|
expr,
|
||||||
|
alias: Ident {
|
||||||
|
value: alias,
|
||||||
|
quote_style: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SelectItem::ExprWithAlias { expr, .. } => SelectItem::ExprWithAlias {
|
||||||
|
expr,
|
||||||
|
alias: Ident {
|
||||||
|
value: alias,
|
||||||
|
quote_style: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select => select,
|
||||||
|
};
|
||||||
|
|
||||||
|
let select: SelectDb = select.into();
|
||||||
|
Ok(select.into_value(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::FieldExpr;
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![Box::new(AliasExpr {}), Box::new(FieldExpr {})])
|
||||||
|
}
|
||||||
|
}
|
141
crates/nu-command/src/database/expressions/and.rs
Normal file
141
crates/nu-command/src/database/expressions/and.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
use crate::database::values::dsl::ExprDb;
|
||||||
|
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
|
};
|
||||||
|
use sqlparser::ast::{BinaryOperator, Expr};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AndExpr;
|
||||||
|
|
||||||
|
impl Command for AndExpr {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"and"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Includes an AND clause for an expression"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.required("and", SyntaxShape::Any, "AND expression")
|
||||||
|
.input_type(Type::Custom("db-expression".into()))
|
||||||
|
.output_type(Type::Custom("db-expression".into()))
|
||||||
|
.category(Category::Custom("db-expression".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["database", "and", "expression"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Creates an AND expression",
|
||||||
|
example: r#"(field a) > 1 | and ((field a) < 10) | into nu"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["left".into(), "op".into(), "right".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["left".into(), "op".into(), "right".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["value".into(), "quoted_style".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "None".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: ">".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "AND".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["left".into(), "op".into(), "right".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["value".into(), "quoted_style".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "None".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "<".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "10".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let value: Value = call.req(engine_state, stack, 0)?;
|
||||||
|
let expr = ExprDb::try_from_value(&value)?.into_native();
|
||||||
|
|
||||||
|
let expression = ExprDb::try_from_pipeline(input, call.head)?;
|
||||||
|
let expression = Expr::BinaryOp {
|
||||||
|
left: Box::new(expression.into_native()),
|
||||||
|
op: BinaryOperator::And,
|
||||||
|
right: Box::new(expr),
|
||||||
|
};
|
||||||
|
|
||||||
|
let expression: ExprDb = Expr::Nested(Box::new(expression)).into();
|
||||||
|
Ok(expression.into_value(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::FieldExpr;
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![Box::new(AndExpr {}), Box::new(FieldExpr {})])
|
||||||
|
}
|
||||||
|
}
|
76
crates/nu-command/src/database/expressions/as_nu.rs
Normal file
76
crates/nu-command/src/database/expressions/as_nu.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use crate::database::values::dsl::{ExprDb, SelectDb};
|
||||||
|
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ExprAsNu;
|
||||||
|
|
||||||
|
impl Command for ExprAsNu {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"into nu"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Convert a db expression into a nu value for access and exploration"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.input_type(Type::Custom("db-expression".into()))
|
||||||
|
.output_type(Type::Any)
|
||||||
|
.category(Category::Custom("db-expression".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Convert a col expression into a nushell value",
|
||||||
|
example: "field name_1 | into nu",
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["value".into(), "quoted_style".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "name_1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "None".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let value = input.into_value(call.head);
|
||||||
|
if let Ok(expr) = ExprDb::try_from_value(&value) {
|
||||||
|
Ok(expr.to_value(call.head).into_pipeline_data())
|
||||||
|
} else {
|
||||||
|
let select = SelectDb::try_from_value(&value)?;
|
||||||
|
Ok(select.to_value(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::FieldExpr;
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![Box::new(ExprAsNu {}), Box::new(FieldExpr {})])
|
||||||
|
}
|
||||||
|
}
|
78
crates/nu-command/src/database/expressions/field.rs
Normal file
78
crates/nu-command/src/database/expressions/field.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use crate::database::values::dsl::ExprDb;
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct FieldExpr;
|
||||||
|
|
||||||
|
impl Command for FieldExpr {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"field"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.required("name", SyntaxShape::String, "column name")
|
||||||
|
.input_type(Type::Any)
|
||||||
|
.output_type(Type::Custom("db-expression".into()))
|
||||||
|
.category(Category::Custom("db-expression".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Creates column expression for database"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["database", "column", "expression"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Creates a named field expression",
|
||||||
|
example: "field name_1 | into nu",
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["value".into(), "quoted_style".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "name_1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "None".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let value: Value = call.req(engine_state, stack, 0)?;
|
||||||
|
let expression = ExprDb::try_from_value(&value)?;
|
||||||
|
|
||||||
|
Ok(expression.into_value(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![Box::new(FieldExpr {})])
|
||||||
|
}
|
||||||
|
}
|
157
crates/nu-command/src/database/expressions/function.rs
Normal file
157
crates/nu-command/src/database/expressions/function.rs
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
use crate::database::values::dsl::ExprDb;
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
|
};
|
||||||
|
use sqlparser::ast::{Expr, Function, FunctionArg, FunctionArgExpr, Ident, ObjectName};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct FunctionExpr;
|
||||||
|
|
||||||
|
impl Command for FunctionExpr {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"fn"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.required("name", SyntaxShape::String, "function name")
|
||||||
|
.switch("distinct", "distict values", Some('d'))
|
||||||
|
.rest("arguments", SyntaxShape::Any, "function arguments")
|
||||||
|
.input_type(Type::Any)
|
||||||
|
.output_type(Type::Custom("db-expression".into()))
|
||||||
|
.category(Category::Custom("db-expression".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Creates function expression for a select operation"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Creates a function expression",
|
||||||
|
example: "fn count name_1 | into nu",
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec![
|
||||||
|
"name".into(),
|
||||||
|
"args".into(),
|
||||||
|
"over".into(),
|
||||||
|
"distinct".into(),
|
||||||
|
],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "count".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::List {
|
||||||
|
vals: vec![Value::String {
|
||||||
|
val: "name_1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "None".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::Bool {
|
||||||
|
val: false,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "orders query by a column",
|
||||||
|
example: r#"open db.mysql
|
||||||
|
| into db
|
||||||
|
| select (fn lead col_a)
|
||||||
|
| from table_a
|
||||||
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT lead(col_a) FROM table_a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["database", "function", "expression"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let name: String = call.req(engine_state, stack, 0)?;
|
||||||
|
let vals: Vec<Value> = call.rest(engine_state, stack, 1)?;
|
||||||
|
let value = Value::List {
|
||||||
|
vals,
|
||||||
|
span: call.head,
|
||||||
|
};
|
||||||
|
let expressions = ExprDb::extract_exprs(value)?;
|
||||||
|
|
||||||
|
let name: Vec<Ident> = name
|
||||||
|
.split('.')
|
||||||
|
.map(|part| Ident {
|
||||||
|
value: part.to_string(),
|
||||||
|
quote_style: None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let name = ObjectName(name);
|
||||||
|
|
||||||
|
let args: Vec<FunctionArg> = expressions
|
||||||
|
.into_iter()
|
||||||
|
.map(|expr| {
|
||||||
|
let arg = FunctionArgExpr::Expr(expr);
|
||||||
|
|
||||||
|
FunctionArg::Unnamed(arg)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let expression: ExprDb = Expr::Function(Function {
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
over: None,
|
||||||
|
distinct: call.has_flag("distinct"),
|
||||||
|
})
|
||||||
|
.into();
|
||||||
|
|
||||||
|
Ok(expression.into_value(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::super::commands::{FromDb, ProjectionDb};
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![
|
||||||
|
Box::new(FunctionExpr {}),
|
||||||
|
Box::new(ProjectionDb {}),
|
||||||
|
Box::new(FromDb {}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
40
crates/nu-command/src/database/expressions/mod.rs
Normal file
40
crates/nu-command/src/database/expressions/mod.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Conversions between value and sqlparser objects
|
||||||
|
mod alias;
|
||||||
|
mod and;
|
||||||
|
mod as_nu;
|
||||||
|
mod field;
|
||||||
|
mod function;
|
||||||
|
mod or;
|
||||||
|
mod over;
|
||||||
|
|
||||||
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
|
|
||||||
|
pub(crate) use alias::AliasExpr;
|
||||||
|
pub(crate) use and::AndExpr;
|
||||||
|
pub(crate) use as_nu::ExprAsNu;
|
||||||
|
pub(crate) use field::FieldExpr;
|
||||||
|
pub(crate) use function::FunctionExpr;
|
||||||
|
pub(crate) use or::OrExpr;
|
||||||
|
pub(crate) use over::OverExpr;
|
||||||
|
|
||||||
|
pub fn add_expressions_decls(working_set: &mut StateWorkingSet) {
|
||||||
|
macro_rules! bind_command {
|
||||||
|
( $command:expr ) => {
|
||||||
|
working_set.add_decl(Box::new($command));
|
||||||
|
};
|
||||||
|
( $( $command:expr ),* ) => {
|
||||||
|
$( working_set.add_decl(Box::new($command)); )*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Series commands
|
||||||
|
bind_command!(
|
||||||
|
ExprAsNu,
|
||||||
|
AliasExpr,
|
||||||
|
AndExpr,
|
||||||
|
FieldExpr,
|
||||||
|
FunctionExpr,
|
||||||
|
OrExpr,
|
||||||
|
OverExpr
|
||||||
|
);
|
||||||
|
}
|
141
crates/nu-command/src/database/expressions/or.rs
Normal file
141
crates/nu-command/src/database/expressions/or.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
use crate::database::values::dsl::ExprDb;
|
||||||
|
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
|
};
|
||||||
|
use sqlparser::ast::{BinaryOperator, Expr};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct OrExpr;
|
||||||
|
|
||||||
|
impl Command for OrExpr {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"or"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Includes an OR clause for an expression"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.required("or", SyntaxShape::Any, "OR expression")
|
||||||
|
.input_type(Type::Custom("db-expression".into()))
|
||||||
|
.output_type(Type::Custom("db-expression".into()))
|
||||||
|
.category(Category::Custom("db-expression".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["database", "or", "expression"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Creates an AND expression",
|
||||||
|
example: r#"(field a) > 1 | or ((field a) < 10) | into nu"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["left".into(), "op".into(), "right".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["left".into(), "op".into(), "right".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["value".into(), "quoted_style".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "None".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: ">".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "1".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "OR".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["left".into(), "op".into(), "right".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["value".into(), "quoted_style".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "None".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "<".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "10".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let value: Value = call.req(engine_state, stack, 0)?;
|
||||||
|
let expr = ExprDb::try_from_value(&value)?.into_native();
|
||||||
|
|
||||||
|
let expression = ExprDb::try_from_pipeline(input, call.head)?;
|
||||||
|
let expression = Expr::BinaryOp {
|
||||||
|
left: Box::new(expression.into_native()),
|
||||||
|
op: BinaryOperator::Or,
|
||||||
|
right: Box::new(expr),
|
||||||
|
};
|
||||||
|
|
||||||
|
let expression: ExprDb = Expr::Nested(Box::new(expression)).into();
|
||||||
|
Ok(expression.into_value(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::FieldExpr;
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![Box::new(OrExpr {}), Box::new(FieldExpr {})])
|
||||||
|
}
|
||||||
|
}
|
153
crates/nu-command/src/database/expressions/over.rs
Normal file
153
crates/nu-command/src/database/expressions/over.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
use crate::database::values::dsl::ExprDb;
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
|
};
|
||||||
|
use sqlparser::ast::{Expr, WindowSpec};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct OverExpr;
|
||||||
|
|
||||||
|
impl Command for OverExpr {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"over"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.rest(
|
||||||
|
"partition-by",
|
||||||
|
SyntaxShape::Any,
|
||||||
|
"columns to partition the window function",
|
||||||
|
)
|
||||||
|
.input_type(Type::Custom("db-expression".into()))
|
||||||
|
.output_type(Type::Custom("db-expression".into()))
|
||||||
|
.category(Category::Custom("db-expression".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Adds a partition to an expression function"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Adds a partition to a function expression",
|
||||||
|
example: "fn avg col_a | over col_b | into nu",
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec![
|
||||||
|
"name".into(),
|
||||||
|
"args".into(),
|
||||||
|
"over".into(),
|
||||||
|
"distinct".into(),
|
||||||
|
],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "avg".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::List {
|
||||||
|
vals: vec![Value::String {
|
||||||
|
val: "col_a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "Some(WindowSpec { partition_by: [Identifier(Ident { value: \"col_b\", quote_style: None })], order_by: [], window_frame: None })".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::Bool {
|
||||||
|
val: false,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "orders query by a column",
|
||||||
|
example: r#"open db.mysql
|
||||||
|
| into db
|
||||||
|
| select (fn lead col_a | over col_b)
|
||||||
|
| from table_a
|
||||||
|
| describe"#,
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["connection".into(), "query".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: "db.mysql".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "SELECT lead(col_a) OVER (PARTITION BY col_b) FROM table_a".into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["database", "over", "expression"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let vals: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
||||||
|
let value = Value::List {
|
||||||
|
vals,
|
||||||
|
span: call.head,
|
||||||
|
};
|
||||||
|
let partitions = ExprDb::extract_exprs(value)?;
|
||||||
|
|
||||||
|
let mut expression = ExprDb::try_from_pipeline(input, call.head)?;
|
||||||
|
match expression.as_mut() {
|
||||||
|
Expr::Function(function) => {
|
||||||
|
function.over = Some(WindowSpec {
|
||||||
|
partition_by: partitions,
|
||||||
|
order_by: Vec::new(),
|
||||||
|
window_frame: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
s => {
|
||||||
|
return Err(ShellError::GenericError(
|
||||||
|
"Expression doesnt define a function".into(),
|
||||||
|
format!("Expected an expression with a function. Got {}", s),
|
||||||
|
Some(call.head),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(expression.into_value(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::super::super::commands::{FromDb, ProjectionDb};
|
||||||
|
use super::super::FunctionExpr;
|
||||||
|
use super::*;
|
||||||
|
use crate::database::test_database::test_database;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
test_database(vec![
|
||||||
|
Box::new(OverExpr {}),
|
||||||
|
Box::new(FunctionExpr {}),
|
||||||
|
Box::new(ProjectionDb {}),
|
||||||
|
Box::new(FromDb {}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,21 @@
|
|||||||
mod commands;
|
mod commands;
|
||||||
|
mod expressions;
|
||||||
mod values;
|
mod values;
|
||||||
|
|
||||||
pub use commands::add_database_decls;
|
use commands::add_commands_decls;
|
||||||
|
use expressions::add_expressions_decls;
|
||||||
|
|
||||||
pub use values::{
|
pub use values::{
|
||||||
convert_sqlite_row_to_nu_value, convert_sqlite_value_to_nu_value, open_connection_in_memory,
|
convert_sqlite_row_to_nu_value, convert_sqlite_value_to_nu_value, open_connection_in_memory,
|
||||||
SQLiteDatabase,
|
SQLiteDatabase,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
|
|
||||||
|
pub fn add_database_decls(working_set: &mut StateWorkingSet) {
|
||||||
|
add_commands_decls(working_set);
|
||||||
|
add_expressions_decls(working_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_database;
|
||||||
|
138
crates/nu-command/src/database/test_database.rs
Normal file
138
crates/nu-command/src/database/test_database.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use super::commands::{DescribeDb, ToDataBase};
|
||||||
|
use super::expressions::ExprAsNu;
|
||||||
|
use crate::SQLiteDatabase;
|
||||||
|
use nu_engine::{eval_block, CallExt};
|
||||||
|
use nu_parser::parse;
|
||||||
|
use nu_protocol::{
|
||||||
|
engine::{Command, EngineState, Stack, StateWorkingSet},
|
||||||
|
Category, IntoPipelineData, PipelineData, Signature, Span, Type,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CustomOpen;
|
||||||
|
|
||||||
|
impl Command for CustomOpen {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"open"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Mock open file command"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.required(
|
||||||
|
"filename",
|
||||||
|
nu_protocol::SyntaxShape::String,
|
||||||
|
"the filename to use",
|
||||||
|
)
|
||||||
|
.input_type(Type::Any)
|
||||||
|
.output_type(Type::Custom("database".into()))
|
||||||
|
.category(Category::Custom("database".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &nu_protocol::ast::Call,
|
||||||
|
_input: nu_protocol::PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
let path: String = call.req(engine_state, stack, 0)?;
|
||||||
|
let path = Path::new(&path);
|
||||||
|
|
||||||
|
let db = SQLiteDatabase::new(path);
|
||||||
|
Ok(db.into_value(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_database(cmds: Vec<Box<dyn Command + 'static>>) {
|
||||||
|
if cmds.is_empty() {
|
||||||
|
panic!("Empty commands vector")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first element in the cmds vector must be the one tested
|
||||||
|
let examples = cmds[0].examples();
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&*engine_state);
|
||||||
|
working_set.add_decl(Box::new(DescribeDb {}));
|
||||||
|
working_set.add_decl(Box::new(ToDataBase {}));
|
||||||
|
working_set.add_decl(Box::new(CustomOpen {}));
|
||||||
|
working_set.add_decl(Box::new(ExprAsNu {}));
|
||||||
|
|
||||||
|
// Adding the command that is being tested to the working set
|
||||||
|
for cmd in cmds {
|
||||||
|
working_set.add_decl(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
let cwd = std::env::current_dir().expect("Could not get current working directory.");
|
||||||
|
let _ = engine_state.merge_delta(delta, None, &cwd);
|
||||||
|
|
||||||
|
for example in examples {
|
||||||
|
// Skip tests that don't have results to compare to
|
||||||
|
if example.result.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
|
||||||
|
let (block, delta) = {
|
||||||
|
let mut working_set = StateWorkingSet::new(&*engine_state);
|
||||||
|
let (output, err) = parse(
|
||||||
|
&mut working_set,
|
||||||
|
None,
|
||||||
|
example.example.as_bytes(),
|
||||||
|
false,
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(err) = err {
|
||||||
|
panic!("test parse error in `{}`: {:?}", example.example, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
(output, working_set.render())
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = engine_state.merge_delta(delta, None, &cwd);
|
||||||
|
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
match eval_block(
|
||||||
|
&engine_state,
|
||||||
|
&mut stack,
|
||||||
|
&block,
|
||||||
|
PipelineData::new(Span::test_data()),
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
) {
|
||||||
|
Err(err) => panic!("test eval error in `{}`: {:?}", example.example, err),
|
||||||
|
Ok(result) => {
|
||||||
|
let result = result.into_value(Span::test_data());
|
||||||
|
println!("input: {}", example.example);
|
||||||
|
println!("result: {:?}", result);
|
||||||
|
println!("done: {:?}", start.elapsed());
|
||||||
|
|
||||||
|
// Note. Value implements PartialEq for Bool, Int, Float, String and Block
|
||||||
|
// If the command you are testing requires to compare another case, then
|
||||||
|
// you need to define its equality in the Value struct
|
||||||
|
if let Some(expected) = example.result {
|
||||||
|
if result != expected {
|
||||||
|
panic!(
|
||||||
|
"the example result is different to expected value: {:?} != {:?}",
|
||||||
|
result, expected
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -52,7 +52,7 @@ impl CustomValue for ExprDb {
|
|||||||
fn follow_path_int(&self, count: usize, span: Span) -> Result<Value, ShellError> {
|
fn follow_path_int(&self, count: usize, span: Span) -> Result<Value, ShellError> {
|
||||||
let path = PathMember::Int { val: count, span };
|
let path = PathMember::Int { val: count, span };
|
||||||
|
|
||||||
ExprDb::expr_to_value(self.as_ref(), span).follow_cell_path(&[path])
|
ExprDb::expr_to_value(self.as_ref(), span).follow_cell_path(&[path], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn follow_path_string(&self, column_name: String, span: Span) -> Result<Value, ShellError> {
|
fn follow_path_string(&self, column_name: String, span: Span) -> Result<Value, ShellError> {
|
||||||
@ -61,7 +61,7 @@ impl CustomValue for ExprDb {
|
|||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
|
|
||||||
ExprDb::expr_to_value(self.as_ref(), span).follow_cell_path(&[path])
|
ExprDb::expr_to_value(self.as_ref(), span).follow_cell_path(&[path], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn typetag_name(&self) -> &'static str {
|
fn typetag_name(&self) -> &'static str {
|
||||||
@ -91,7 +91,7 @@ impl CustomValue for ExprDb {
|
|||||||
Value::Bool { val, .. } => Ok(Expr::Value(sqlparser::ast::Value::Boolean(*val))),
|
Value::Bool { val, .. } => Ok(Expr::Value(sqlparser::ast::Value::Boolean(*val))),
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
op_span: op,
|
op_span: op,
|
||||||
lhs_ty: Type::Custom,
|
lhs_ty: Type::Custom(self.typetag_name().into()),
|
||||||
lhs_span,
|
lhs_span,
|
||||||
rhs_ty: right.get_type(),
|
rhs_ty: right.get_type(),
|
||||||
rhs_span: right.span()?,
|
rhs_span: right.span()?,
|
||||||
@ -112,11 +112,17 @@ impl CustomValue for ExprDb {
|
|||||||
Operator::Multiply => Ok(BinaryOperator::Multiply),
|
Operator::Multiply => Ok(BinaryOperator::Multiply),
|
||||||
Operator::Divide => Ok(BinaryOperator::Divide),
|
Operator::Divide => Ok(BinaryOperator::Divide),
|
||||||
Operator::Modulo => Ok(BinaryOperator::Modulo),
|
Operator::Modulo => Ok(BinaryOperator::Modulo),
|
||||||
|
Operator::FloorDivision => Ok(BinaryOperator::Divide),
|
||||||
Operator::And => Ok(BinaryOperator::And),
|
Operator::And => Ok(BinaryOperator::And),
|
||||||
Operator::Or => Ok(BinaryOperator::Or),
|
Operator::Or => Ok(BinaryOperator::Or),
|
||||||
Operator::In
|
Operator::In
|
||||||
| Operator::NotIn
|
| Operator::NotIn
|
||||||
| Operator::Pow
|
| Operator::Pow
|
||||||
|
| Operator::BitOr
|
||||||
|
| Operator::BitXor
|
||||||
|
| Operator::BitAnd
|
||||||
|
| Operator::ShiftLeft
|
||||||
|
| Operator::ShiftRight
|
||||||
| Operator::StartsWith
|
| Operator::StartsWith
|
||||||
| Operator::EndsWith => Err(ShellError::UnsupportedOperator(operator, op)),
|
| Operator::EndsWith => Err(ShellError::UnsupportedOperator(operator, op)),
|
||||||
}?;
|
}?;
|
||||||
|
@ -56,7 +56,7 @@ impl CustomValue for SelectDb {
|
|||||||
fn follow_path_int(&self, count: usize, span: Span) -> Result<Value, ShellError> {
|
fn follow_path_int(&self, count: usize, span: Span) -> Result<Value, ShellError> {
|
||||||
let path = PathMember::Int { val: count, span };
|
let path = PathMember::Int { val: count, span };
|
||||||
|
|
||||||
SelectDb::select_to_value(self.as_ref(), span).follow_cell_path(&[path])
|
SelectDb::select_to_value(self.as_ref(), span).follow_cell_path(&[path], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn follow_path_string(&self, column_name: String, span: Span) -> Result<Value, ShellError> {
|
fn follow_path_string(&self, column_name: String, span: Span) -> Result<Value, ShellError> {
|
||||||
@ -64,7 +64,7 @@ impl CustomValue for SelectDb {
|
|||||||
val: column_name,
|
val: column_name,
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
SelectDb::select_to_value(self.as_ref(), span).follow_cell_path(&[path])
|
SelectDb::select_to_value(self.as_ref(), span).follow_cell_path(&[path], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn typetag_name(&self) -> &'static str {
|
fn typetag_name(&self) -> &'static str {
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::values::{Axis, Column, NuDataFrame};
|
use super::super::values::{Axis, Column, NuDataFrame};
|
||||||
@ -12,7 +12,7 @@ pub struct AppendDF;
|
|||||||
|
|
||||||
impl Command for AppendDF {
|
impl Command for AppendDF {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr append"
|
"append"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -23,6 +23,8 @@ impl Command for AppendDF {
|
|||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.required("other", SyntaxShape::Any, "dataframe to be appended")
|
.required("other", SyntaxShape::Any, "dataframe to be appended")
|
||||||
.switch("col", "appends in col orientation", Some('c'))
|
.switch("col", "appends in col orientation", Some('c'))
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
.category(Category::Custom("dataframe".into()))
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,8 +32,8 @@ impl Command for AppendDF {
|
|||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Appends a dataframe as new columns",
|
description: "Appends a dataframe as new columns",
|
||||||
example: r#"let a = ([[a b]; [1 2] [3 4]] | dfr to-df);
|
example: r#"let a = ([[a b]; [1 2] [3 4]] | into df);
|
||||||
$a | dfr append $a"#,
|
$a | append $a"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new(
|
Column::new(
|
||||||
@ -57,8 +59,8 @@ impl Command for AppendDF {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Appends a dataframe merging at the end of columns",
|
description: "Appends a dataframe merging at the end of columns",
|
||||||
example: r#"let a = ([[a b]; [1 2] [3 4]] | dfr to-df);
|
example: r#"let a = ([[a b]; [1 2] [3 4]] | into df);
|
||||||
$a | dfr append $a --col"#,
|
$a | append $a --col"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new(
|
Column::new(
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::Call,
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::super::values::{Column, NuDataFrame};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ColumnDF;
|
|
||||||
|
|
||||||
impl Command for ColumnDF {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"dfr column"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Returns the selected column"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build(self.name())
|
|
||||||
.required("column", SyntaxShape::String, "column name")
|
|
||||||
.category(Category::Custom("dataframe".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Returns the selected column as series",
|
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr to-df | dfr column a",
|
|
||||||
result: Some(
|
|
||||||
NuDataFrame::try_from_columns(vec![Column::new(
|
|
||||||
"a".to_string(),
|
|
||||||
vec![Value::test_int(1), Value::test_int(3)],
|
|
||||||
)])
|
|
||||||
.expect("simple df for test should not fail")
|
|
||||||
.into_value(Span::test_data()),
|
|
||||||
),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
command(engine_state, stack, call, input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command(
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let column: Spanned<String> = call.req(engine_state, stack, 0)?;
|
|
||||||
|
|
||||||
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
|
|
||||||
|
|
||||||
let res = df.as_ref().column(&column.item).map_err(|e| {
|
|
||||||
ShellError::GenericError(
|
|
||||||
"Error selecting column".into(),
|
|
||||||
e.to_string(),
|
|
||||||
Some(column.span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
NuDataFrame::try_from_series(vec![res.clone()], call.head)
|
|
||||||
.map(|df| PipelineData::Value(NuDataFrame::into_value(df, call.head), None))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::super::super::test_dataframe::test_dataframe;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
test_dataframe(vec![Box::new(ColumnDF {})])
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
use nu_engine::get_full_help;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::Call,
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, IntoPipelineData, PipelineData, ShellError, Signature, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Dataframe;
|
|
||||||
|
|
||||||
impl Command for Dataframe {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"dfr"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Dataframe commands"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build(self.name()).category(Category::Custom("dataframe".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
Ok(Value::String {
|
|
||||||
val: get_full_help(
|
|
||||||
&Dataframe.signature(),
|
|
||||||
&Dataframe.examples(),
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
),
|
|
||||||
span: call.head,
|
|
||||||
}
|
|
||||||
.into_pipeline_data())
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use polars::{
|
use polars::{
|
||||||
chunked_array::ChunkedArray,
|
chunked_array::ChunkedArray,
|
||||||
@ -19,7 +19,7 @@ pub struct DescribeDF;
|
|||||||
|
|
||||||
impl Command for DescribeDF {
|
impl Command for DescribeDF {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr describe"
|
"describe"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -29,6 +29,8 @@ impl Command for DescribeDF {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.category(Category::Custom("dataframe".into()))
|
.category(Category::Custom("dataframe".into()))
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
.named(
|
.named(
|
||||||
"quantiles",
|
"quantiles",
|
||||||
SyntaxShape::Table,
|
SyntaxShape::Table,
|
||||||
@ -40,7 +42,7 @@ impl Command for DescribeDF {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "dataframe description",
|
description: "dataframe description",
|
||||||
example: "[[a b]; [1 1] [1 1]] | dfr to-df | dfr describe",
|
example: "[[a b]; [1 1] [1 1]] | into df | describe",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new(
|
Column::new(
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::values::utils::convert_columns;
|
use super::super::values::utils::convert_columns;
|
||||||
@ -13,7 +13,7 @@ pub struct DropDF;
|
|||||||
|
|
||||||
impl Command for DropDF {
|
impl Command for DropDF {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr drop"
|
"drop"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -23,13 +23,15 @@ impl Command for DropDF {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.rest("rest", SyntaxShape::Any, "column names to be dropped")
|
.rest("rest", SyntaxShape::Any, "column names to be dropped")
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
.category(Category::Custom("dataframe".into()))
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "drop column a",
|
description: "drop column a",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr to-df | dfr drop a",
|
example: "[[a b]; [1 2] [3 4]] | into df | drop a",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![Column::new(
|
NuDataFrame::try_from_columns(vec![Column::new(
|
||||||
"b".to_string(),
|
"b".to_string(),
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use polars::prelude::UniqueKeepStrategy;
|
use polars::prelude::UniqueKeepStrategy;
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ pub struct DropDuplicates;
|
|||||||
|
|
||||||
impl Command for DropDuplicates {
|
impl Command for DropDuplicates {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr drop-duplicates"
|
"drop-duplicates"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -34,13 +34,15 @@ impl Command for DropDuplicates {
|
|||||||
"keeps last duplicate value (by default keeps first)",
|
"keeps last duplicate value (by default keeps first)",
|
||||||
Some('l'),
|
Some('l'),
|
||||||
)
|
)
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
.category(Category::Custom("dataframe".into()))
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "drop duplicates",
|
description: "drop duplicates",
|
||||||
example: "[[a b]; [1 2] [3 4] [1 2]] | dfr to-df | dfr drop-duplicates",
|
example: "[[a b]; [1 2] [3 4] [1 2]] | into df | drop-duplicates",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new(
|
Column::new(
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::values::utils::convert_columns_string;
|
use super::super::values::utils::convert_columns_string;
|
||||||
@ -13,7 +13,7 @@ pub struct DropNulls;
|
|||||||
|
|
||||||
impl Command for DropNulls {
|
impl Command for DropNulls {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr drop-nulls"
|
"drop-nulls"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -27,6 +27,8 @@ impl Command for DropNulls {
|
|||||||
SyntaxShape::Table,
|
SyntaxShape::Table,
|
||||||
"subset of columns to drop nulls",
|
"subset of columns to drop nulls",
|
||||||
)
|
)
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
.category(Category::Custom("dataframe".into()))
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,10 +36,10 @@ impl Command for DropNulls {
|
|||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "drop null values in dataframe",
|
description: "drop null values in dataframe",
|
||||||
example: r#"let df = ([[a b]; [1 2] [3 0] [1 2]] | dfr to-df);
|
example: r#"let df = ([[a b]; [1 2] [3 0] [1 2]] | into df);
|
||||||
let res = ($df.b / $df.b);
|
let res = ($df.b / $df.b);
|
||||||
let a = ($df | dfr with-column $res --name res);
|
let a = ($df | with-column $res --name res);
|
||||||
$a | dfr drop-nulls"#,
|
$a | drop-nulls"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new(
|
Column::new(
|
||||||
@ -59,8 +61,8 @@ impl Command for DropNulls {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "drop null values in dataframe",
|
description: "drop null values in dataframe",
|
||||||
example: r#"let s = ([1 2 0 0 3 4] | dfr to-df);
|
example: r#"let s = ([1 2 0 0 3 4] | into df);
|
||||||
($s / $s) | dfr drop-nulls"#,
|
($s / $s) | drop-nulls"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![Column::new(
|
NuDataFrame::try_from_columns(vec![Column::new(
|
||||||
"div_0_0".to_string(),
|
"div_0_0".to_string(),
|
||||||
|
@ -2,7 +2,7 @@ use super::super::values::{Column, NuDataFrame};
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -10,7 +10,7 @@ pub struct DataTypes;
|
|||||||
|
|
||||||
impl Command for DataTypes {
|
impl Command for DataTypes {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr dtypes"
|
"dtypes"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -18,13 +18,16 @@ impl Command for DataTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name()).category(Category::Custom("dataframe".into()))
|
Signature::build(self.name())
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Dataframe dtypes",
|
description: "Dataframe dtypes",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr to-df | dfr dtypes",
|
example: "[[a b]; [1 2] [3 4]] | into df | dtypes",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new(
|
Column::new(
|
||||||
|
@ -2,7 +2,7 @@ use super::super::values::{Column, NuDataFrame};
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||||
};
|
};
|
||||||
use polars::prelude::DataFrameOps;
|
use polars::prelude::DataFrameOps;
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ pub struct Dummies;
|
|||||||
|
|
||||||
impl Command for Dummies {
|
impl Command for Dummies {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr to-dummies"
|
"dummies"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -19,14 +19,17 @@ impl Command for Dummies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name()).category(Category::Custom("dataframe".into()))
|
Signature::build(self.name())
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Create new dataframe with dummy variables from a dataframe",
|
description: "Create new dataframe with dummy variables from a dataframe",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr to-df | dfr to-dummies",
|
example: "[[a b]; [1 2] [3 4]] | into df | dummies",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new(
|
Column::new(
|
||||||
@ -52,7 +55,7 @@ impl Command for Dummies {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Create new dataframe with dummy variables from a series",
|
description: "Create new dataframe with dummy variables from a series",
|
||||||
example: "[1 2 2 3 3] | dfr to-df | dfr to-dummies",
|
example: "[1 2 2 3 3] | into df | dummies",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new(
|
Column::new(
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use polars::prelude::LazyFrame;
|
use polars::prelude::LazyFrame;
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ pub struct FilterWith;
|
|||||||
|
|
||||||
impl Command for FilterWith {
|
impl Command for FilterWith {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr filter-with"
|
"filter-with"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -29,14 +29,17 @@ impl Command for FilterWith {
|
|||||||
SyntaxShape::Any,
|
SyntaxShape::Any,
|
||||||
"boolean mask used to filter data",
|
"boolean mask used to filter data",
|
||||||
)
|
)
|
||||||
.category(Category::Custom("dataframe".into()))
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
|
.category(Category::Custom("dataframe or lazyframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
|
Example {
|
||||||
description: "Filter dataframe using a bool mask",
|
description: "Filter dataframe using a bool mask",
|
||||||
example: r#"let mask = ([true false] | dfr to-df);
|
example: r#"let mask = ([true false] | into df);
|
||||||
[[a b]; [1 2] [3 4]] | dfr to-df | dfr filter-with $mask"#,
|
[[a b]; [1 2] [3 4]] | into df | filter-with $mask"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new("a".to_string(), vec![Value::test_int(1)]),
|
Column::new("a".to_string(), vec![Value::test_int(1)]),
|
||||||
@ -45,7 +48,20 @@ impl Command for FilterWith {
|
|||||||
.expect("simple df for test should not fail")
|
.expect("simple df for test should not fail")
|
||||||
.into_value(Span::test_data()),
|
.into_value(Span::test_data()),
|
||||||
),
|
),
|
||||||
}]
|
},
|
||||||
|
Example {
|
||||||
|
description: "Filter dataframe using an expression",
|
||||||
|
example: "[[a b]; [1 2] [3 4]] | into df | filter-with ((col a) > 1)",
|
||||||
|
result: Some(
|
||||||
|
NuDataFrame::try_from_columns(vec![
|
||||||
|
Column::new("a".to_string(), vec![Value::test_int(3)]),
|
||||||
|
Column::new("b".to_string(), vec![Value::test_int(4)]),
|
||||||
|
])
|
||||||
|
.expect("simple df for test should not fail")
|
||||||
|
.into_value(Span::test_data()),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -60,16 +76,9 @@ impl Command for FilterWith {
|
|||||||
if NuLazyFrame::can_downcast(&value) {
|
if NuLazyFrame::can_downcast(&value) {
|
||||||
let df = NuLazyFrame::try_from_value(value)?;
|
let df = NuLazyFrame::try_from_value(value)?;
|
||||||
command_lazy(engine_state, stack, call, df)
|
command_lazy(engine_state, stack, call, df)
|
||||||
} else if NuDataFrame::can_downcast(&value) {
|
} else {
|
||||||
let df = NuDataFrame::try_from_value(value)?;
|
let df = NuDataFrame::try_from_value(value)?;
|
||||||
command_eager(engine_state, stack, call, df)
|
command_eager(engine_state, stack, call, df)
|
||||||
} else {
|
|
||||||
Err(ShellError::CantConvert(
|
|
||||||
"expression or query".into(),
|
|
||||||
value.get_type().to_string(),
|
|
||||||
value.span()?,
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,8 +90,18 @@ fn command_eager(
|
|||||||
df: NuDataFrame,
|
df: NuDataFrame,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let mask_value: Value = call.req(engine_state, stack, 0)?;
|
let mask_value: Value = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
let mask_span = mask_value.span()?;
|
let mask_span = mask_value.span()?;
|
||||||
|
|
||||||
|
if NuExpression::can_downcast(&mask_value) {
|
||||||
|
let expression = NuExpression::try_from_value(mask_value)?;
|
||||||
|
let lazy = NuLazyFrame::new(true, df.lazy());
|
||||||
|
let lazy = lazy.apply_with_expr(expression, LazyFrame::filter);
|
||||||
|
|
||||||
|
Ok(PipelineData::Value(
|
||||||
|
NuLazyFrame::into_value(lazy, call.head)?,
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
let mask = NuDataFrame::try_from_value(mask_value)?.as_series(mask_span)?;
|
let mask = NuDataFrame::try_from_value(mask_value)?.as_series(mask_span)?;
|
||||||
let mask = mask.bool().map_err(|e| {
|
let mask = mask.bool().map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
@ -98,7 +117,7 @@ fn command_eager(
|
|||||||
.filter(mask)
|
.filter(mask)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Error calculating dummies".into(),
|
"Error filtering dataframe".into(),
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
Some(call.head),
|
Some(call.head),
|
||||||
Some("The only allowed column types for dummies are String or Int".into()),
|
Some("The only allowed column types for dummies are String or Int".into()),
|
||||||
@ -107,6 +126,7 @@ fn command_eager(
|
|||||||
})
|
})
|
||||||
.map(|df| PipelineData::Value(NuDataFrame::dataframe_into_value(df, call.head), None))
|
.map(|df| PipelineData::Value(NuDataFrame::dataframe_into_value(df, call.head), None))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn command_lazy(
|
fn command_lazy(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -120,7 +140,7 @@ fn command_lazy(
|
|||||||
let lazy = lazy.apply_with_expr(expr, LazyFrame::filter);
|
let lazy = lazy.apply_with_expr(expr, LazyFrame::filter);
|
||||||
|
|
||||||
Ok(PipelineData::Value(
|
Ok(PipelineData::Value(
|
||||||
NuLazyFrame::into_value(lazy, call.head),
|
NuLazyFrame::into_value(lazy, call.head)?,
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -129,9 +149,10 @@ fn command_lazy(
|
|||||||
mod test {
|
mod test {
|
||||||
use super::super::super::test_dataframe::test_dataframe;
|
use super::super::super::test_dataframe::test_dataframe;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::dataframe::expressions::ExprCol;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_examples() {
|
fn test_examples() {
|
||||||
test_dataframe(vec![Box::new(FilterWith {})])
|
test_dataframe(vec![Box::new(FilterWith {}), Box::new(ExprCol {})])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
use super::super::values::{utils::DEFAULT_ROWS, Column, NuDataFrame};
|
use super::super::values::{utils::DEFAULT_ROWS, Column, NuDataFrame};
|
||||||
use crate::dataframe::values::NuExpression;
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -12,24 +11,25 @@ pub struct FirstDF;
|
|||||||
|
|
||||||
impl Command for FirstDF {
|
impl Command for FirstDF {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr first"
|
"first"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Creates new dataframe with first rows or creates a first expression"
|
"Creates new dataframe with first rows"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.optional("rows", SyntaxShape::Int, "Number of rows for head")
|
.optional("rows", SyntaxShape::Int, "Number of rows for head")
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
.category(Category::Custom("dataframe".into()))
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![Example {
|
||||||
Example {
|
|
||||||
description: "Create new dataframe with head rows",
|
description: "Create new dataframe with head rows",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr to-df | dfr first 1",
|
example: "[[a b]; [1 2] [3 4]] | into df | first 1",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new("a".to_string(), vec![Value::test_int(1)]),
|
Column::new("a".to_string(), vec![Value::test_int(1)]),
|
||||||
@ -38,13 +38,7 @@ impl Command for FirstDF {
|
|||||||
.expect("simple df for test should not fail")
|
.expect("simple df for test should not fail")
|
||||||
.into_value(Span::test_data()),
|
.into_value(Span::test_data()),
|
||||||
),
|
),
|
||||||
},
|
}]
|
||||||
Example {
|
|
||||||
description: "Creates a first expression from a column",
|
|
||||||
example: "dfr col a | dfr first",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -54,27 +48,8 @@ impl Command for FirstDF {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let value = input.into_value(call.head);
|
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
|
||||||
|
|
||||||
if NuExpression::can_downcast(&value) {
|
|
||||||
let expr = NuExpression::try_from_value(value)?;
|
|
||||||
let expr: NuExpression = expr.into_polars().is_null().into();
|
|
||||||
|
|
||||||
Ok(PipelineData::Value(
|
|
||||||
NuExpression::into_value(expr, call.head),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
} else if NuDataFrame::can_downcast(&value) {
|
|
||||||
let df = NuDataFrame::try_from_value(value)?;
|
|
||||||
command(engine_state, stack, call, df)
|
command(engine_state, stack, call, df)
|
||||||
} else {
|
|
||||||
Err(ShellError::CantConvert(
|
|
||||||
"expression or query".into(),
|
|
||||||
value.get_type().to_string(),
|
|
||||||
value.span()?,
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::dataframe::values::utils::convert_columns_string;
|
use crate::dataframe::values::utils::convert_columns_string;
|
||||||
@ -14,7 +14,7 @@ pub struct GetDF;
|
|||||||
|
|
||||||
impl Command for GetDF {
|
impl Command for GetDF {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr get"
|
"get"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -24,13 +24,15 @@ impl Command for GetDF {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.rest("rest", SyntaxShape::Any, "column names to sort dataframe")
|
.rest("rest", SyntaxShape::Any, "column names to sort dataframe")
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
.category(Category::Custom("dataframe".into()))
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Creates dataframe with selected columns",
|
description: "Returns the selected column",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr to-df | dfr get a",
|
example: "[[a b]; [1 2] [3 4]] | into df | get a",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![Column::new(
|
NuDataFrame::try_from_columns(vec![Column::new(
|
||||||
"a".to_string(),
|
"a".to_string(),
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
use super::super::values::{utils::DEFAULT_ROWS, Column, NuDataFrame};
|
use super::super::values::{utils::DEFAULT_ROWS, Column, NuDataFrame};
|
||||||
use crate::dataframe::values::NuExpression;
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -12,7 +11,7 @@ pub struct LastDF;
|
|||||||
|
|
||||||
impl Command for LastDF {
|
impl Command for LastDF {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr last"
|
"last"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -22,14 +21,15 @@ impl Command for LastDF {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.optional("rows", SyntaxShape::Int, "Number of rows for tail")
|
.optional("rows", SyntaxShape::Int, "Number of rows for tail")
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
.category(Category::Custom("dataframe".into()))
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![Example {
|
||||||
Example {
|
|
||||||
description: "Create new dataframe with last rows",
|
description: "Create new dataframe with last rows",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr to-df | dfr last 1",
|
example: "[[a b]; [1 2] [3 4]] | into df | last 1",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new("a".to_string(), vec![Value::test_int(3)]),
|
Column::new("a".to_string(), vec![Value::test_int(3)]),
|
||||||
@ -38,13 +38,7 @@ impl Command for LastDF {
|
|||||||
.expect("simple df for test should not fail")
|
.expect("simple df for test should not fail")
|
||||||
.into_value(Span::test_data()),
|
.into_value(Span::test_data()),
|
||||||
),
|
),
|
||||||
},
|
}]
|
||||||
Example {
|
|
||||||
description: "Creates a last expression from a column",
|
|
||||||
example: "dfr col a | dfr last",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -54,27 +48,8 @@ impl Command for LastDF {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let value = input.into_value(call.head);
|
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
|
||||||
|
|
||||||
if NuExpression::can_downcast(&value) {
|
|
||||||
let expr = NuExpression::try_from_value(value)?;
|
|
||||||
let expr: NuExpression = expr.into_polars().is_null().into();
|
|
||||||
|
|
||||||
Ok(PipelineData::Value(
|
|
||||||
NuExpression::into_value(expr, call.head),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
} else if NuDataFrame::can_downcast(&value) {
|
|
||||||
let df = NuDataFrame::try_from_value(value)?;
|
|
||||||
command(engine_state, stack, call, df)
|
command(engine_state, stack, call, df)
|
||||||
} else {
|
|
||||||
Err(ShellError::CantConvert(
|
|
||||||
"expression or query".into(),
|
|
||||||
value.get_type().to_string(),
|
|
||||||
value.span()?,
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ pub struct ListDF;
|
|||||||
|
|
||||||
impl Command for ListDF {
|
impl Command for ListDF {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr ls"
|
"ls-df"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -25,8 +25,8 @@ impl Command for ListDF {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Creates a new dataframe and shows it in the dataframe list",
|
description: "Creates a new dataframe and shows it in the dataframe list",
|
||||||
example: r#"let test = ([[a b];[1 2] [3 4]] | dfr to-df);
|
example: r#"let test = ([[a b];[1 2] [3 4]] | into df);
|
||||||
dfr ls"#,
|
ls-df"#,
|
||||||
result: None,
|
result: None,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
||||||
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::dataframe::values::utils::convert_columns_string;
|
use crate::dataframe::values::utils::convert_columns_string;
|
||||||
@ -14,7 +15,7 @@ pub struct MeltDF;
|
|||||||
|
|
||||||
impl Command for MeltDF {
|
impl Command for MeltDF {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr melt"
|
"melt"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -47,6 +48,8 @@ impl Command for MeltDF {
|
|||||||
"optional name for value column",
|
"optional name for value column",
|
||||||
Some('l'),
|
Some('l'),
|
||||||
)
|
)
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
.category(Category::Custom("dataframe".into()))
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +57,7 @@ impl Command for MeltDF {
|
|||||||
vec![Example {
|
vec![Example {
|
||||||
description: "melt dataframe",
|
description: "melt dataframe",
|
||||||
example:
|
example:
|
||||||
"[[a b c d]; [x 1 4 a] [y 2 5 b] [z 3 6 c]] | dfr to-df | dfr melt -c [b c] -v [a d]",
|
"[[a b c d]; [x 1 4 a] [y 2 5 b] [z 3 6 c]] | into df | melt -c [b c] -v [a d]",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new(
|
Column::new(
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
mod append;
|
mod append;
|
||||||
mod column;
|
|
||||||
mod command;
|
|
||||||
mod describe;
|
mod describe;
|
||||||
mod drop;
|
mod drop;
|
||||||
mod drop_duplicates;
|
mod drop_duplicates;
|
||||||
@ -18,7 +16,6 @@ mod rename;
|
|||||||
mod sample;
|
mod sample;
|
||||||
mod shape;
|
mod shape;
|
||||||
mod slice;
|
mod slice;
|
||||||
mod sort;
|
|
||||||
mod take;
|
mod take;
|
||||||
mod to_csv;
|
mod to_csv;
|
||||||
mod to_df;
|
mod to_df;
|
||||||
@ -29,8 +26,6 @@ mod with_column;
|
|||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
|
|
||||||
pub use append::AppendDF;
|
pub use append::AppendDF;
|
||||||
pub use column::ColumnDF;
|
|
||||||
pub use command::Dataframe;
|
|
||||||
pub use describe::DescribeDF;
|
pub use describe::DescribeDF;
|
||||||
pub use drop::DropDF;
|
pub use drop::DropDF;
|
||||||
pub use drop_duplicates::DropDuplicates;
|
pub use drop_duplicates::DropDuplicates;
|
||||||
@ -48,7 +43,6 @@ pub use rename::RenameDF;
|
|||||||
pub use sample::SampleDF;
|
pub use sample::SampleDF;
|
||||||
pub use shape::ShapeDF;
|
pub use shape::ShapeDF;
|
||||||
pub use slice::SliceDF;
|
pub use slice::SliceDF;
|
||||||
pub use sort::SortDF;
|
|
||||||
pub use take::TakeDF;
|
pub use take::TakeDF;
|
||||||
pub use to_csv::ToCSV;
|
pub use to_csv::ToCSV;
|
||||||
pub use to_df::ToDataFrame;
|
pub use to_df::ToDataFrame;
|
||||||
@ -69,8 +63,6 @@ pub fn add_eager_decls(working_set: &mut StateWorkingSet) {
|
|||||||
// Dataframe commands
|
// Dataframe commands
|
||||||
bind_command!(
|
bind_command!(
|
||||||
AppendDF,
|
AppendDF,
|
||||||
ColumnDF,
|
|
||||||
Dataframe,
|
|
||||||
DataTypes,
|
DataTypes,
|
||||||
DescribeDF,
|
DescribeDF,
|
||||||
DropDF,
|
DropDF,
|
||||||
@ -88,7 +80,6 @@ pub fn add_eager_decls(working_set: &mut StateWorkingSet) {
|
|||||||
SampleDF,
|
SampleDF,
|
||||||
ShapeDF,
|
ShapeDF,
|
||||||
SliceDF,
|
SliceDF,
|
||||||
SortDF,
|
|
||||||
TakeDF,
|
TakeDF,
|
||||||
ToCSV,
|
ToCSV,
|
||||||
ToDataFrame,
|
ToDataFrame,
|
||||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
|
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{fs::File, io::BufReader, path::PathBuf};
|
use std::{fs::File, io::BufReader, path::PathBuf};
|
||||||
@ -15,7 +15,7 @@ pub struct OpenDataFrame;
|
|||||||
|
|
||||||
impl Command for OpenDataFrame {
|
impl Command for OpenDataFrame {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr open"
|
"open-df"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -58,13 +58,15 @@ impl Command for OpenDataFrame {
|
|||||||
"Columns to be selected from csv file. CSV and Parquet file",
|
"Columns to be selected from csv file. CSV and Parquet file",
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
.input_type(Type::Any)
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
.category(Category::Custom("dataframe".into()))
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Takes a file name and creates a dataframe",
|
description: "Takes a file name and creates a dataframe",
|
||||||
example: "dfr open test.csv",
|
example: "open test.csv",
|
||||||
result: None,
|
result: None,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
@ -147,7 +149,7 @@ fn from_json(
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
) -> Result<polars::prelude::DataFrame, ShellError> {
|
) -> Result<polars::prelude::DataFrame, ShellError> {
|
||||||
let file: Spanned<PathBuf> = call.req(engine_state, stack, 0)?;
|
let file: Spanned<PathBuf> = call.req(engine_state, stack, 0)?;
|
||||||
let mut file = File::open(&file.item).map_err(|e| {
|
let file = File::open(&file.item).map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Error opening file".into(),
|
"Error opening file".into(),
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
@ -157,7 +159,7 @@ fn from_json(
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let buf_reader = BufReader::new(&mut file);
|
let buf_reader = BufReader::new(file);
|
||||||
let reader = JsonReader::new(buf_reader);
|
let reader = JsonReader::new(buf_reader);
|
||||||
|
|
||||||
reader.finish().map_err(|e| {
|
reader.finish().map_err(|e| {
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::dataframe::{utils::extract_strings, values::NuLazyFrame};
|
use crate::dataframe::{utils::extract_strings, values::NuLazyFrame};
|
||||||
@ -14,7 +14,7 @@ pub struct RenameDF;
|
|||||||
|
|
||||||
impl Command for RenameDF {
|
impl Command for RenameDF {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr rename-col"
|
"rename"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -33,13 +33,33 @@ impl Command for RenameDF {
|
|||||||
SyntaxShape::Any,
|
SyntaxShape::Any,
|
||||||
"New names for the selected column(s). A string or list of strings",
|
"New names for the selected column(s). A string or list of strings",
|
||||||
)
|
)
|
||||||
.category(Category::Custom("dataframe".into()))
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
|
.category(Category::Custom("dataframe or lazyframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Renames a series",
|
||||||
|
example: "[5 6 7 8] | into df | rename '0' new_name",
|
||||||
|
result: Some(
|
||||||
|
NuDataFrame::try_from_columns(vec![Column::new(
|
||||||
|
"new_name".to_string(),
|
||||||
|
vec![
|
||||||
|
Value::test_int(5),
|
||||||
|
Value::test_int(6),
|
||||||
|
Value::test_int(7),
|
||||||
|
Value::test_int(8),
|
||||||
|
],
|
||||||
|
)])
|
||||||
|
.expect("simple df for test should not fail")
|
||||||
|
.into_value(Span::test_data()),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
description: "Renames a dataframe column",
|
description: "Renames a dataframe column",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr to-df | dfr rename-col a a_new",
|
example: "[[a b]; [1 2] [3 4]] | into df | rename a a_new",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new(
|
Column::new(
|
||||||
@ -54,7 +74,26 @@ impl Command for RenameDF {
|
|||||||
.expect("simple df for test should not fail")
|
.expect("simple df for test should not fail")
|
||||||
.into_value(Span::test_data()),
|
.into_value(Span::test_data()),
|
||||||
),
|
),
|
||||||
}]
|
},
|
||||||
|
Example {
|
||||||
|
description: "Renames two dataframe columns",
|
||||||
|
example: "[[a b]; [1 2] [3 4]] | into df | rename [a b] [a_new b_new]",
|
||||||
|
result: Some(
|
||||||
|
NuDataFrame::try_from_columns(vec![
|
||||||
|
Column::new(
|
||||||
|
"a_new".to_string(),
|
||||||
|
vec![Value::test_int(1), Value::test_int(3)],
|
||||||
|
),
|
||||||
|
Column::new(
|
||||||
|
"b_new".to_string(),
|
||||||
|
vec![Value::test_int(2), Value::test_int(4)],
|
||||||
|
),
|
||||||
|
])
|
||||||
|
.expect("simple df for test should not fail")
|
||||||
|
.into_value(Span::test_data()),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -69,16 +108,9 @@ impl Command for RenameDF {
|
|||||||
if NuLazyFrame::can_downcast(&value) {
|
if NuLazyFrame::can_downcast(&value) {
|
||||||
let df = NuLazyFrame::try_from_value(value)?;
|
let df = NuLazyFrame::try_from_value(value)?;
|
||||||
command_lazy(engine_state, stack, call, df)
|
command_lazy(engine_state, stack, call, df)
|
||||||
} else if NuDataFrame::can_downcast(&value) {
|
} else {
|
||||||
let df = NuDataFrame::try_from_value(value)?;
|
let df = NuDataFrame::try_from_value(value)?;
|
||||||
command_eager(engine_state, stack, call, df)
|
command_eager(engine_state, stack, call, df)
|
||||||
} else {
|
|
||||||
Err(ShellError::CantConvert(
|
|
||||||
"expression or query".into(),
|
|
||||||
value.get_type().to_string(),
|
|
||||||
value.span()?,
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,7 +165,7 @@ fn command_lazy(
|
|||||||
let lazy = lazy.into_polars();
|
let lazy = lazy.into_polars();
|
||||||
let lazy: NuLazyFrame = lazy.rename(&columns, &new_names).into();
|
let lazy: NuLazyFrame = lazy.rename(&columns, &new_names).into();
|
||||||
|
|
||||||
Ok(PipelineData::Value(lazy.into_value(call.head), None))
|
Ok(PipelineData::Value(lazy.into_value(call.head)?, None))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
|
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::values::NuDataFrame;
|
use super::super::values::NuDataFrame;
|
||||||
@ -12,7 +12,7 @@ pub struct SampleDF;
|
|||||||
|
|
||||||
impl Command for SampleDF {
|
impl Command for SampleDF {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr sample"
|
"sample"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -40,6 +40,9 @@ impl Command for SampleDF {
|
|||||||
Some('s'),
|
Some('s'),
|
||||||
)
|
)
|
||||||
.switch("replace", "sample with replace", Some('e'))
|
.switch("replace", "sample with replace", Some('e'))
|
||||||
|
.switch("shuffle", "shuffle sample", Some('u'))
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
.category(Category::Custom("dataframe".into()))
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,12 +50,12 @@ impl Command for SampleDF {
|
|||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Sample rows from dataframe",
|
description: "Sample rows from dataframe",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr to-df | dfr sample -n 1",
|
example: "[[a b]; [1 2] [3 4]] | into df | sample -n 1",
|
||||||
result: None, // No expected value because sampling is random
|
result: None, // No expected value because sampling is random
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Shows sample row using fraction and replace",
|
description: "Shows sample row using fraction and replace",
|
||||||
example: "[[a b]; [1 2] [3 4] [5 6]] | dfr to-df | dfr sample -f 0.5 -e",
|
example: "[[a b]; [1 2] [3 4] [5 6]] | into df | sample -f 0.5 -e",
|
||||||
result: None, // No expected value because sampling is random
|
result: None, // No expected value because sampling is random
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -81,11 +84,15 @@ fn command(
|
|||||||
.get_flag::<i64>(engine_state, stack, "seed")?
|
.get_flag::<i64>(engine_state, stack, "seed")?
|
||||||
.map(|val| val as u64);
|
.map(|val| val as u64);
|
||||||
let replace: bool = call.has_flag("replace");
|
let replace: bool = call.has_flag("replace");
|
||||||
|
let shuffle: bool = call.has_flag("shuffle");
|
||||||
|
|
||||||
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
|
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
|
||||||
|
|
||||||
match (rows, fraction) {
|
match (rows, fraction) {
|
||||||
(Some(rows), None) => df.as_ref().sample_n(rows.item, replace, seed).map_err(|e| {
|
(Some(rows), None) => df
|
||||||
|
.as_ref()
|
||||||
|
.sample_n(rows.item, replace, shuffle, seed)
|
||||||
|
.map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Error creating sample".into(),
|
"Error creating sample".into(),
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
@ -96,7 +103,7 @@ fn command(
|
|||||||
}),
|
}),
|
||||||
(None, Some(frac)) => df
|
(None, Some(frac)) => df
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.sample_frac(frac.item, replace, seed)
|
.sample_frac(frac.item, replace, shuffle, seed)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Error creating sample".into(),
|
"Error creating sample".into(),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::dataframe::values::Column;
|
use crate::dataframe::values::Column;
|
||||||
@ -13,7 +13,7 @@ pub struct ShapeDF;
|
|||||||
|
|
||||||
impl Command for ShapeDF {
|
impl Command for ShapeDF {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"dfr shape"
|
"shape"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -21,13 +21,16 @@ impl Command for ShapeDF {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name()).category(Category::Custom("dataframe".into()))
|
Signature::build(self.name())
|
||||||
|
.input_type(Type::Custom("dataframe".into()))
|
||||||
|
.output_type(Type::Custom("dataframe".into()))
|
||||||
|
.category(Category::Custom("dataframe".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Shows row and column shape",
|
description: "Shows row and column shape",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr to-df | dfr shape",
|
example: "[[a b]; [1 2] [3 4]] | into df | shape",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![
|
NuDataFrame::try_from_columns(vec![
|
||||||
Column::new("rows".to_string(), vec![Value::test_int(2)]),
|
Column::new("rows".to_string(), vec![Value::test_int(2)]),
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user