forked from extern/nushell
Compare commits
138 Commits
0.86.0
...
patch-rele
Author | SHA1 | Date | |
---|---|---|---|
3d631490bc | |||
68211dea3e | |||
b8e9293c45 | |||
77a1c3c7b2 | |||
82b3ae826f | |||
1b3092ae7c | |||
942ff7df4d | |||
51abe4c262 | |||
e8e0526f57 | |||
0b25385109 | |||
415b1273b4 | |||
6bee80dcd7 | |||
588a078872 | |||
93096a07aa | |||
523d0bca16 | |||
fe92051bb3 | |||
ee648ecb7d | |||
91920373b5 | |||
33a7bc405f | |||
cd75640a90 | |||
0f600bc3f5 | |||
aed4b626b8 | |||
92503e6571 | |||
44c0db46e1 | |||
1fd3bc1ba6 | |||
59ea28cf06 | |||
435abadd8a | |||
86cd387439 | |||
edbf3aaccb | |||
b03ef56bcb | |||
55316a9f27 | |||
d3ec3dc66b | |||
7ebae0b5f7 | |||
f45aed257f | |||
60da7abbc7 | |||
7a3cbf43e8 | |||
45b02ce2ab | |||
c039e4b3d0 | |||
9b202d560d | |||
1874082a2c | |||
1359b26da2 | |||
b9bc527d27 | |||
51cdd9fbb2 | |||
d838871063 | |||
4d16d92847 | |||
fc01701a41 | |||
51d5d0eac8 | |||
81d00f71a9 | |||
77fbf3e2d2 | |||
f565661f42 | |||
1a864ea6f4 | |||
c1738620e3 | |||
56e35fc3f9 | |||
29591c97a7 | |||
697dee6750 | |||
0ca8fcf58c | |||
a46048f362 | |||
0569a9c92e | |||
c1ca10ffd1 | |||
15c22db8f4 | |||
1c52b112c8 | |||
275dba82d5 | |||
cf7040a215 | |||
72cb4b6032 | |||
d4cbab454e | |||
3645178ff1 | |||
005180f269 | |||
72f7b9b7cc | |||
3f61ca19f0 | |||
d8c59eddb3 | |||
ac43372618 | |||
dccb2b48f3 | |||
2e68e6ddbf | |||
3dfe1a4f0e | |||
c87bac04c0 | |||
4b301710d3 | |||
7d67ca3652 | |||
01d8961eb7 | |||
7ac5a01e2f | |||
e2fb0e5b82 | |||
a11e41332c | |||
f3656f7822 | |||
38f4ab0bc9 | |||
f35741d50e | |||
d93315d8f5 | |||
8429aec57f | |||
f043a8a8ff | |||
c6016d7659 | |||
78b4472b32 | |||
cb754befe9 | |||
0588a4fc19 | |||
ff3a0a0de3 | |||
c799f77577 | |||
d3182a6737 | |||
b5e09b8a30 | |||
05efd735b9 | |||
5e0499fcf9 | |||
74d3f3c1d6 | |||
de6edf18d9 | |||
a35ecb4837 | |||
a01ef85bda | |||
6445c4e7de | |||
066c9118ae | |||
52e8b0afb2 | |||
db3f3eaf5a | |||
878f0cf6e1 | |||
7caf27b665 | |||
22b375ccd4 | |||
6a2539534f | |||
f310a9be8c | |||
d0dc6986dd | |||
11480c77be | |||
4fd2b702ee | |||
030e55acbf | |||
c5e1b64b40 | |||
999f7b229f | |||
de1c7bb39f | |||
54bc662e0e | |||
5f2089a15b | |||
adb99938f7 | |||
b907939916 | |||
27e6271402 | |||
0a8f27f6f2 | |||
1662e61ecb | |||
b58819d51e | |||
9692240b4f | |||
d204defb68 | |||
9e7f84afb0 | |||
ed8dee04b6 | |||
4171c654a5 | |||
22ee041002 | |||
7162d4d9aa | |||
9c70c68914 | |||
93b4aa5fcf | |||
71b0e12b57 | |||
09b3dab35d | |||
88a87158c2 | |||
faf84e69b4 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -9,6 +9,7 @@ name: continuous-integration
|
||||
env:
|
||||
NUSHELL_CARGO_PROFILE: ci
|
||||
NU_LOG_LEVEL: DEBUG
|
||||
# If changing these settings also change toolkit.nu
|
||||
CLIPPY_OPTIONS: "-D warnings -D clippy::unwrap_used"
|
||||
|
||||
jobs:
|
||||
@ -47,6 +48,7 @@ jobs:
|
||||
- name: cargo fmt
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
# If changing these settings also change toolkit.nu
|
||||
- name: Clippy
|
||||
run: cargo clippy --workspace ${{ matrix.flags }} --exclude nu_plugin_* -- $CLIPPY_OPTIONS
|
||||
|
||||
|
22
.github/workflows/nightly-build.yml
vendored
22
.github/workflows/nightly-build.yml
vendored
@ -36,10 +36,10 @@ jobs:
|
||||
token: ${{ secrets.WORKFLOW_TOKEN }}
|
||||
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3.6
|
||||
uses: hustcer/setup-nu@v3.8
|
||||
if: github.repository == 'nushell/nightly'
|
||||
with:
|
||||
version: 0.85.0
|
||||
version: 0.86.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@ -136,11 +136,14 @@ jobs:
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
||||
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
||||
with:
|
||||
rustflags: ''
|
||||
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3.6
|
||||
uses: hustcer/setup-nu@v3.8
|
||||
with:
|
||||
version: 0.85.0
|
||||
version: 0.86.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@ -247,11 +250,14 @@ jobs:
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
||||
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
||||
with:
|
||||
rustflags: ''
|
||||
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3.6
|
||||
uses: hustcer/setup-nu@v3.8
|
||||
with:
|
||||
version: 0.85.0
|
||||
version: 0.86.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@ -315,9 +321,9 @@ jobs:
|
||||
ref: main
|
||||
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3.6
|
||||
uses: hustcer/setup-nu@v3.8
|
||||
with:
|
||||
version: 0.85.0
|
||||
version: 0.86.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
30
.github/workflows/release-pkg.nu
vendored
30
.github/workflows/release-pkg.nu
vendored
@ -9,34 +9,36 @@
|
||||
# Instructions for manually creating an MSI for Winget Releases when they fail
|
||||
# Added 2022-11-29 when Windows packaging wouldn't work
|
||||
# Updated again on 2023-02-23 because msis are still failing validation
|
||||
# Update on 2023-10-18 to use RELEASE_TYPE env var to determine if full or not
|
||||
# To run this manual for windows here are the steps I take
|
||||
# checkout the release you want to publish
|
||||
# 1. git checkout 0.76.0
|
||||
# 1. git checkout 0.86.0
|
||||
# unset CARGO_TARGET_DIR if set (I have to do this in the parent shell to get it to work)
|
||||
# 2. $env:CARGO_TARGET_DIR = ""
|
||||
# 2. hide-env CARGO_TARGET_DIR
|
||||
# 3. $env.TARGET = 'x86_64-pc-windows-msvc'
|
||||
# 4. $env.TARGET_RUSTFLAGS = ''
|
||||
# 5. $env.GITHUB_WORKSPACE = 'C:\Users\dschroeder\source\repos\forks\nushell'
|
||||
# 6. $env.GITHUB_OUTPUT = 'C:\Users\dschroeder\source\repos\forks\nushell\output\out.txt'
|
||||
# 5. $env.GITHUB_WORKSPACE = 'D:\nushell'
|
||||
# 6. $env.GITHUB_OUTPUT = 'D:\nushell\output\out.txt'
|
||||
# 7. $env.OS = 'windows-latest'
|
||||
# 8. $env.RELEASE_TYPE = '' # There is full and '' for normal releases
|
||||
# make sure 7z.exe is in your path https://www.7-zip.org/download.html
|
||||
# 8. $env.Path = ($env.Path | append 'c:\apps\7-zip')
|
||||
# 9. $env.Path = ($env.Path | append 'c:\apps\7-zip')
|
||||
# make sure aria2c.exe is in your path https://github.com/aria2/aria2
|
||||
# 9. $env.Path = ($env.Path | append 'c:\path\to\aria2c')
|
||||
# 10. $env.Path = ($env.Path | append 'c:\path\to\aria2c')
|
||||
# make sure you have the wixtools installed https://wixtoolset.org/
|
||||
# 10. $env.Path = ($env.Path | append 'C:\Users\dschroeder\AppData\Local\tauri\WixTools')
|
||||
# 11. $env.Path = ($env.Path | append 'C:\Users\dschroeder\AppData\Local\tauri\WixTools')
|
||||
# You need to run the release-pkg twice. The first pass, with _EXTRA_ as 'bin', makes the output
|
||||
# folder and builds everything. The second pass, that generates the msi file, with _EXTRA_ as 'msi'
|
||||
# 11. $env._EXTRA_ = 'bin'
|
||||
# 12. source .github\workflows\release-pkg.nu
|
||||
# 13. cd ..
|
||||
# 14. $env._EXTRA_ = 'msi'
|
||||
# 15. source .github\workflows\release-pkg.nu
|
||||
# 12. $env._EXTRA_ = 'bin'
|
||||
# 13. source .github\workflows\release-pkg.nu
|
||||
# 14. cd ..
|
||||
# 15. $env._EXTRA_ = 'msi'
|
||||
# 16. source .github\workflows\release-pkg.nu
|
||||
# After msi is generated, you have to update winget-pkgs repo, you'll need to patch the release
|
||||
# by deleting the existing msi and uploading this new msi. Then you'll need to update the hash
|
||||
# on the winget-pkgs PR. To generate the hash, run this command
|
||||
# 16. open target\wix\nu-0.74.0-x86_64-pc-windows-msvc.msi | hash sha256
|
||||
# 17. open target\wix\nu-0.74.0-x86_64-pc-windows-msvc.msi | hash sha256
|
||||
# Then, just take the output and put it in the winget-pkgs PR for the hash on the msi
|
||||
|
||||
|
||||
@ -134,7 +136,7 @@ print (ls -f $executable); sleep 1sec
|
||||
print $'(char nl)Copying release files...'; hr-line
|
||||
"To use Nu plugins, use the register command to tell Nu where to find the plugin. For example:
|
||||
|
||||
> register ./nu_plugin_query" | save $'($dist)/README.txt'
|
||||
> register ./nu_plugin_query" | save $'($dist)/README.txt' -f
|
||||
[LICENSE $executable] | each {|it| cp -rv $it $dist } | flatten
|
||||
# Sleep a few seconds to make sure the cp process finished successfully
|
||||
sleep 3sec
|
||||
@ -225,7 +227,7 @@ def 'cargo-build-nu' [ options: string ] {
|
||||
|
||||
# Print a horizontal line marker
|
||||
def 'hr-line' [
|
||||
--blank-line(-b): bool
|
||||
--blank-line(-b)
|
||||
] {
|
||||
print $'(ansi g)---------------------------------------------------------------------------->(ansi reset)'
|
||||
if $blank_line { char nl }
|
||||
|
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@ -80,11 +80,14 @@ jobs:
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
||||
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
||||
with:
|
||||
rustflags: ''
|
||||
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3.6
|
||||
uses: hustcer/setup-nu@v3.8
|
||||
with:
|
||||
version: 0.85.0
|
||||
version: 0.86.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@ -168,11 +171,14 @@ jobs:
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
||||
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
||||
with:
|
||||
rustflags: ''
|
||||
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3.6
|
||||
uses: hustcer/setup-nu@v3.8
|
||||
with:
|
||||
version: 0.85.0
|
||||
version: 0.86.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@ -10,6 +10,6 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check spelling
|
||||
uses: crate-ci/typos@v1.16.19
|
||||
uses: crate-ci/typos@v1.16.23
|
||||
with:
|
||||
config: ./.github/.typos.toml
|
||||
|
269
Cargo.lock
generated
269
Cargo.lock
generated
@ -997,9 +997,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.2.2"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086"
|
||||
checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
|
||||
dependencies = [
|
||||
"csv-core",
|
||||
"itoa",
|
||||
@ -1009,9 +1009,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.10"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||
checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -1306,7 +1306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"rustix 0.38.15",
|
||||
"rustix 0.38.21",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@ -1856,9 +1856,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.0.2"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.1",
|
||||
@ -1946,7 +1946,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"rustix 0.38.15",
|
||||
"rustix 0.38.21",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@ -2293,9 +2293,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.11.1"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21"
|
||||
checksum = "1efa59af2ddfad1854ae27d75009d538d0998b4b2fd47083e743ac1a10e46c60"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.1",
|
||||
]
|
||||
@ -2309,6 +2309,31 @@ dependencies = [
|
||||
"nu-ansi-term",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lsp-server"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b52dccdf3302eefab8c8a1273047f0a3c3dca4b527c8458d00c09484c8371928"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lsp-types"
|
||||
version = "0.94.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lz4"
|
||||
version = "1.24.0"
|
||||
@ -2620,7 +2645,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"criterion",
|
||||
@ -2641,6 +2666,7 @@ dependencies = [
|
||||
"nu-engine",
|
||||
"nu-explore",
|
||||
"nu-json",
|
||||
"nu-lsp",
|
||||
"nu-parser",
|
||||
"nu-path",
|
||||
"nu-plugin",
|
||||
@ -2676,7 +2702,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-cli"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossterm 0.27.0",
|
||||
@ -2708,23 +2734,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-cmd-base"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 2.1.0",
|
||||
"miette",
|
||||
"nu-engine",
|
||||
"nu-glob",
|
||||
"nu-parser",
|
||||
"nu-path",
|
||||
"nu-protocol",
|
||||
"nu-test-support",
|
||||
"nu-utils",
|
||||
"rstest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-cmd-dataframe"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"fancy-regex",
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 2.1.0",
|
||||
"nu-cmd-lang",
|
||||
"nu-engine",
|
||||
"nu-parser",
|
||||
@ -2739,7 +2770,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-cmd-extra"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"fancy-regex",
|
||||
@ -2764,7 +2795,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-cmd-lang"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"fancy-regex",
|
||||
"itertools 0.11.0",
|
||||
@ -2778,7 +2809,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-color-config"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
"nu-engine",
|
||||
@ -2791,7 +2822,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-command"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"alphanumeric-sort",
|
||||
"base64",
|
||||
@ -2815,7 +2846,7 @@ dependencies = [
|
||||
"filetime",
|
||||
"fs_extra",
|
||||
"htmlescape",
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 2.1.0",
|
||||
"indicatif",
|
||||
"itertools 0.11.0",
|
||||
"libc",
|
||||
@ -2880,16 +2911,18 @@ dependencies = [
|
||||
"ureq",
|
||||
"url",
|
||||
"uu_cp",
|
||||
"uu_mkdir",
|
||||
"uu_whoami",
|
||||
"uuid",
|
||||
"wax",
|
||||
"which",
|
||||
"which 5.0.0",
|
||||
"windows 0.48.0",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-engine"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"nu-glob",
|
||||
"nu-path",
|
||||
@ -2899,7 +2932,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-explore"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"ansi-str",
|
||||
"crossterm 0.27.0",
|
||||
@ -2920,23 +2953,44 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-glob"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"doc-comment",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-json"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
"num-traits",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-lsp"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"assert-json-diff",
|
||||
"lsp-server",
|
||||
"lsp-types",
|
||||
"miette",
|
||||
"nu-cli",
|
||||
"nu-cmd-lang",
|
||||
"nu-command",
|
||||
"nu-parser",
|
||||
"nu-protocol",
|
||||
"nu-test-support",
|
||||
"reedline",
|
||||
"ropey",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-parser"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"bytesize",
|
||||
"chrono",
|
||||
@ -2952,7 +3006,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-path"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"dirs-next",
|
||||
"omnipath",
|
||||
@ -2961,7 +3015,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-plugin"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"nu-engine",
|
||||
@ -2973,7 +3027,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-pretty-hex"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"heapless",
|
||||
"nu-ansi-term",
|
||||
@ -2982,13 +3036,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-protocol"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"byte-unit",
|
||||
"chrono",
|
||||
"chrono-humanize",
|
||||
"fancy-regex",
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 2.1.0",
|
||||
"lru",
|
||||
"miette",
|
||||
"nu-path",
|
||||
@ -3007,7 +3061,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-std"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"miette",
|
||||
"nu-engine",
|
||||
@ -3017,7 +3071,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-system"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"libc",
|
||||
@ -3034,7 +3088,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-table"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"fancy-regex",
|
||||
"nu-ansi-term",
|
||||
@ -3048,7 +3102,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-term-grid"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"nu-utils",
|
||||
"unicode-width",
|
||||
@ -3056,7 +3110,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-test-support"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"hamcrest2",
|
||||
"nu-glob",
|
||||
@ -3064,12 +3118,12 @@ dependencies = [
|
||||
"nu-utils",
|
||||
"num-format",
|
||||
"tempfile",
|
||||
"which",
|
||||
"which 4.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-utils"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"crossterm_winapi",
|
||||
"log",
|
||||
@ -3077,6 +3131,7 @@ dependencies = [
|
||||
"num-format",
|
||||
"strip-ansi-escapes",
|
||||
"sys-locale",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3091,7 +3146,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_example"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"nu-plugin",
|
||||
"nu-protocol",
|
||||
@ -3099,11 +3154,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_formats"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"eml-parser",
|
||||
"ical",
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 2.1.0",
|
||||
"nu-plugin",
|
||||
"nu-protocol",
|
||||
"rust-ini",
|
||||
@ -3111,7 +3166,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_gstat"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"git2",
|
||||
"nu-plugin",
|
||||
@ -3120,7 +3175,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_inc"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"nu-plugin",
|
||||
"nu-protocol",
|
||||
@ -3129,7 +3184,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_query"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
dependencies = [
|
||||
"gjson",
|
||||
"nu-engine",
|
||||
@ -3402,12 +3457,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ordered-multimap"
|
||||
version = "0.6.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
|
||||
checksum = "a4d6a8c22fc714f0c2373e6091bf6f5e9b37b1bc0b1184874b7e0a4e303d318f"
|
||||
dependencies = [
|
||||
"dlv-list",
|
||||
"hashbrown 0.13.2",
|
||||
"hashbrown 0.14.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3728,7 +3783,7 @@ dependencies = [
|
||||
"comfy-table",
|
||||
"either",
|
||||
"hashbrown 0.14.1",
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 2.1.0",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"polars-arrow",
|
||||
@ -3801,7 +3856,7 @@ dependencies = [
|
||||
"arrow2",
|
||||
"fallible-streaming-iterator",
|
||||
"hashbrown 0.14.1",
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 2.1.0",
|
||||
"num-traits",
|
||||
"polars-arrow",
|
||||
"polars-error",
|
||||
@ -3842,7 +3897,7 @@ dependencies = [
|
||||
"argminmax",
|
||||
"arrow2",
|
||||
"either",
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 2.1.0",
|
||||
"memchr",
|
||||
"polars-arrow",
|
||||
"polars-core",
|
||||
@ -4064,7 +4119,7 @@ dependencies = [
|
||||
"flate2",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"rustix 0.36.15",
|
||||
"rustix 0.36.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4244,9 +4299,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reedline"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7dc1d1d369c194cf79acc204397aca1fecc4248df3e1c1eabb15e5ef2d16991"
|
||||
checksum = "d0a093a20a6c473247c2e9971aaf4cedf9041bcd3f444dc7fad667d3b6b7a5fd"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossterm 0.27.0",
|
||||
@ -4327,6 +4382,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ropey"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
"str_indices",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roxmltree"
|
||||
version = "0.18.1"
|
||||
@ -4415,9 +4480,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-ini"
|
||||
version = "0.19.0"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
|
||||
checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"ordered-multimap",
|
||||
@ -4456,9 +4521,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.15"
|
||||
version = "0.36.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941"
|
||||
checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"errno",
|
||||
@ -4470,9 +4535,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.37.24"
|
||||
version = "0.37.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4279d76516df406a8bd37e7dff53fd37d1a093f997a3c34a5c21658c126db06d"
|
||||
checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"errno",
|
||||
@ -4484,9 +4549,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.15"
|
||||
version = "0.38.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2f9da0cbd88f9f09e7814e388301c8414c51c62aa6ce1e4b5c551d49d96e531"
|
||||
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"errno",
|
||||
@ -4533,9 +4598,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "scraper"
|
||||
version = "0.17.1"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c95a930e03325234c18c7071fd2b60118307e025d6fff3e12745ffbf63a3d29c"
|
||||
checksum = "585480e3719b311b78a573db1c9d9c4c1f8010c2dee4cc59c2efe58ea4dbc3e1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"cssparser",
|
||||
@ -4543,7 +4608,6 @@ dependencies = [
|
||||
"html5ever",
|
||||
"once_cell",
|
||||
"selectors",
|
||||
"smallvec",
|
||||
"tendril",
|
||||
]
|
||||
|
||||
@ -4632,6 +4696,17 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.3"
|
||||
@ -4659,7 +4734,7 @@ version = "0.9.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574"
|
||||
dependencies = [
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 2.1.0",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
@ -4899,6 +4974,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "str_indices"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8eeaedde8e50d8a331578c9fa9a288df146ce5e16173ad26ce82f6e263e2be4"
|
||||
|
||||
[[package]]
|
||||
name = "streaming-decompression"
|
||||
version = "0.1.2"
|
||||
@ -5124,7 +5205,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall 0.3.5",
|
||||
"rustix 0.38.15",
|
||||
"rustix 0.38.21",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@ -5164,7 +5245,7 @@ version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
|
||||
dependencies = [
|
||||
"rustix 0.37.24",
|
||||
"rustix 0.37.27",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@ -5174,7 +5255,7 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
|
||||
dependencies = [
|
||||
"rustix 0.38.15",
|
||||
"rustix 0.38.21",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@ -5383,7 +5464,7 @@ version = "0.19.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||
dependencies = [
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 2.1.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
@ -5396,7 +5477,7 @@ version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca676d9ba1a322c1b64eb8045a5ec5c0cfb0c9d08e15e9ff622589ad5221c8fe"
|
||||
dependencies = [
|
||||
"indexmap 2.0.2",
|
||||
"indexmap 2.1.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
@ -5431,9 +5512,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "trash"
|
||||
version = "3.1.0"
|
||||
version = "3.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7b23f2b0cf93f537bbe90cbb59ea9176cc8ce9b010a36dcd5b726facd82825e"
|
||||
checksum = "8c646008e5144d988005bec12b1e56f5e0a951e957176686815eba8b025e0418"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"libc",
|
||||
@ -5582,6 +5663,7 @@ dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5618,6 +5700,28 @@ dependencies = [
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_mkdir"
|
||||
version = "0.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4776960a036a4ec375f0701004a41013d66d2e3e46a19e9216fd18d4d92f88f3"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_whoami"
|
||||
version = "0.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "585d70c283e4c741889ec8575c23f91b350e4d12bfe39b938d18083816830c5d"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"libc",
|
||||
"uucore",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uucore"
|
||||
version = "0.0.22"
|
||||
@ -5657,9 +5761,9 @@ checksum = "6de61731e36d52d3babb63e2dce8fe648e2644e9ddfe2621e0eea699022051a6"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.4.1"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
|
||||
checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
@ -5841,7 +5945,20 @@ dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix 0.38.15",
|
||||
"rustix 0.38.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix 0.38.21",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
48
Cargo.toml
48
Cargo.toml
@ -11,7 +11,7 @@ license = "MIT"
|
||||
name = "nu"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
rust-version = "1.60"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -33,6 +33,7 @@ members = [
|
||||
"crates/nu-cmd-lang",
|
||||
"crates/nu-cmd-dataframe",
|
||||
"crates/nu-command",
|
||||
"crates/nu-lsp",
|
||||
"crates/nu-protocol",
|
||||
"crates/nu-plugin",
|
||||
"crates/nu_plugin_inc",
|
||||
@ -46,28 +47,29 @@ members = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
nu-cli = { path = "./crates/nu-cli", version = "0.86.0" }
|
||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.86.0" }
|
||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.86.0" }
|
||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.86.0" }
|
||||
nu-cmd-dataframe = { path = "./crates/nu-cmd-dataframe", version = "0.86.0", features = ["dataframe"], optional = true }
|
||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.86.0", optional = true }
|
||||
nu-command = { path = "./crates/nu-command", version = "0.86.0" }
|
||||
nu-engine = { path = "./crates/nu-engine", version = "0.86.0" }
|
||||
nu-explore = { path = "./crates/nu-explore", version = "0.86.0" }
|
||||
nu-json = { path = "./crates/nu-json", version = "0.86.0" }
|
||||
nu-parser = { path = "./crates/nu-parser", version = "0.86.0" }
|
||||
nu-path = { path = "./crates/nu-path", version = "0.86.0" }
|
||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.86.0" }
|
||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.86.0" }
|
||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.86.0" }
|
||||
nu-system = { path = "./crates/nu-system", version = "0.86.0" }
|
||||
nu-table = { path = "./crates/nu-table", version = "0.86.0" }
|
||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.86.0" }
|
||||
nu-std = { path = "./crates/nu-std", version = "0.86.0" }
|
||||
nu-utils = { path = "./crates/nu-utils", version = "0.86.0" }
|
||||
nu-cli = { path = "./crates/nu-cli", version = "0.87.1" }
|
||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.87.1" }
|
||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.87.1" }
|
||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.87.1" }
|
||||
nu-cmd-dataframe = { path = "./crates/nu-cmd-dataframe", version = "0.87.1", features = ["dataframe"], optional = true }
|
||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.87.1", optional = true }
|
||||
nu-command = { path = "./crates/nu-command", version = "0.87.1" }
|
||||
nu-engine = { path = "./crates/nu-engine", version = "0.87.1" }
|
||||
nu-explore = { path = "./crates/nu-explore", version = "0.87.1" }
|
||||
nu-json = { path = "./crates/nu-json", version = "0.87.1" }
|
||||
nu-lsp = { path = "./crates/nu-lsp/", version = "0.87.1" }
|
||||
nu-parser = { path = "./crates/nu-parser", version = "0.87.1" }
|
||||
nu-path = { path = "./crates/nu-path", version = "0.87.1" }
|
||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.87.1" }
|
||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.87.1" }
|
||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.87.1" }
|
||||
nu-system = { path = "./crates/nu-system", version = "0.87.1" }
|
||||
nu-table = { path = "./crates/nu-table", version = "0.87.1" }
|
||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.87.1" }
|
||||
nu-std = { path = "./crates/nu-std", version = "0.87.1" }
|
||||
nu-utils = { path = "./crates/nu-utils", version = "0.87.1" }
|
||||
nu-ansi-term = "0.49.0"
|
||||
reedline = { version = "0.25.0", features = ["bashisms", "sqlite"] }
|
||||
reedline = { version = "0.26.0", features = ["bashisms", "sqlite"] }
|
||||
|
||||
crossterm = "0.27"
|
||||
ctrlc = "3.4"
|
||||
@ -95,7 +97,7 @@ nix = { version = "0.27", default-features = false, features = [
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.86.0" }
|
||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.87.1" }
|
||||
assert_cmd = "2.0"
|
||||
criterion = "0.5"
|
||||
pretty_assertions = "1.4"
|
||||
|
70
PLATFORM_SUPPORT.md
Normal file
70
PLATFORM_SUPPORT.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Nushell platform support policy
|
||||
|
||||
Nushell envisions to be a cross-platform shell, despite taking some strong design inspiration from UNIX and POSIX command names and style conventions we explicitly support Windows.
|
||||
|
||||
## cross-platform design
|
||||
This commitment to a cross-platform Nushell forces us to make provisions so users on Windows can have the generally same pleasant experience: e.g. supporting paths with backslash as the directory separator, forces us to support string literals that accept those.
|
||||
|
||||
In general our design strives to have a consistent behavior across all platforms if defining the semantics is possible for Nushell.
|
||||
In some cases where the platform requirements dominate we may choose to follow the platform specific defaults. (some nuances around the file system)
|
||||
Only rarely do we want to accept commands/language features that only support a single platform, only to access common system behavior of this particular platform (e.g. `registry query` command for the windows registry, `exec` for Linux and MacOS)
|
||||
|
||||
## cross-platform builds and testing
|
||||
|
||||
The Nushell team runs **testing of Nushell for the following platforms** through our CI:
|
||||
|
||||
- macOS (latest version available through GitHub CI)
|
||||
- Windows (10 and 11)
|
||||
- Linux (our test runners use `ubuntu-20.04` to represent distributions with not the latest glibc versions.)
|
||||
|
||||
All PR level tests are performed on x86/AMD64 (at least at the time of writing the default macOS runner was not yet using arm64).
|
||||
|
||||
As an additional layer of validation we perform [nightly builds](https://github.com/nushell/nightly/releases).
|
||||
|
||||
Those target **additional build targets**:
|
||||
- **aarch64 for all platforms**
|
||||
- musl as an alternative to Glibc on linux
|
||||
- riscv only for linux
|
||||
- armv7 only for linux
|
||||
|
||||
We will try to provide builds for all of them but a standard configuration for x86-64 or aarch64 will take priority for us should we face technical challenges in a release cycle.
|
||||
|
||||
### Supported feature flags
|
||||
|
||||
We have features of Nushell behind flags that can be passed at compilation time.
|
||||
|
||||
The design focus of Nushell is primarily expressed by everything accessible without passing additional feature flag. This provides a standard command set and receives the most attention.
|
||||
Two other feature flags are actively tested but are not guaranteed to express the stable design direction of Nushell:
|
||||
- `extra`
|
||||
- This includes commands where we are not convinced that they are ready to be stabilized for 1.0 or popular enough
|
||||
- `dataframe`
|
||||
- This includes dataframe support via `polars` and `arrow2`. Introduces a significant additional compilation and binary size.
|
||||
- Due to the use of SIMD extensions may not be compatible with every minimal architecture.
|
||||
|
||||
## Passively supported platforms
|
||||
|
||||
These platforms are not actively managed through our CI so may encounter unintended regressions.
|
||||
Furthermore certain features may not yet be available, even though we are willing to accept PRs trying to close that gap.
|
||||
|
||||
|
||||
- OpenBSD
|
||||
- e.g. missing the `ps` command
|
||||
- FreeBSD
|
||||
- e.g. missing the `ps` command
|
||||
- Android via Termux
|
||||
|
||||
Help from the community to make sure they get tested and improved so they can become first class targets would be greatly appreciated!
|
||||
|
||||
|
||||
## Providing builds and packaging
|
||||
|
||||
The Nushell team only provides a select few distribution sources and so far encourages community members to maintain the individual packages for particular package managers:
|
||||
|
||||
We provide:
|
||||
- source code distribution via `crates.io` -> `cargo install nu`
|
||||
- GitHub builds with each release: (following the build matrix of the nightly builds)
|
||||
- the setup for `winget` packaging
|
||||
|
||||
### For package maintainers:
|
||||
|
||||
We aim to support the rust version that is two releases behind the most recent version of stable Rust so the build infrastructure of your packaging environment can already be proven out.
|
@ -54,6 +54,7 @@ Detailed installation instructions can be found in the [installation chapter of
|
||||
|
||||
[](https://repology.org/project/nushell/versions)
|
||||
|
||||
For details about which platforms the Nushell team actively supports, see [our platform support policy](PLATFORM_SUPPORT.md).
|
||||
|
||||
## Configuration
|
||||
|
||||
@ -198,7 +199,7 @@ topics that have been presented.
|
||||
|
||||
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 work across platforms and Nu has 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](PLATFORM_SUPPORT.md).
|
||||
|
||||
- Nu ensures compatibility with existing platform-specific executables.
|
||||
|
||||
|
@ -5,27 +5,27 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cli"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
[dev-dependencies]
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.86.0" }
|
||||
nu-command = { path = "../nu-command", version = "0.86.0" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.87.1" }
|
||||
nu-command = { path = "../nu-command", version = "0.87.1" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.87.1" }
|
||||
rstest = { version = "0.18.1", default-features = false }
|
||||
|
||||
[dependencies]
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.86.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.86.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.86.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.86.0" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.86.0" }
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.87.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.87.1" }
|
||||
nu-path = { path = "../nu-path", version = "0.87.1" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.87.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.87.1" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.87.1" }
|
||||
nu-ansi-term = "0.49.0"
|
||||
reedline = { version = "0.25.0", features = ["bashisms", "sqlite"] }
|
||||
reedline = { version = "0.26.0", features = ["bashisms", "sqlite"] }
|
||||
|
||||
chrono = { default-features = false, features = ["std"], version = "0.4" }
|
||||
crossterm = "0.27"
|
||||
@ -39,7 +39,7 @@ percent-encoding = "2"
|
||||
pathdiff = "0.2"
|
||||
sysinfo = "0.29"
|
||||
unicode-segmentation = "1.10"
|
||||
uuid = { version = "1.4.1", features = ["v4"] }
|
||||
uuid = { version = "1.5.0", features = ["v4"] }
|
||||
|
||||
[features]
|
||||
plugin = []
|
||||
|
@ -36,7 +36,7 @@ impl Command for KeybindingsList {
|
||||
vec![
|
||||
Example {
|
||||
description: "Get list of key modifiers",
|
||||
example: "keybindings list -m",
|
||||
example: "keybindings list --modifiers",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::completions::{
|
||||
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
|
||||
DotNuCompletion, FileCompletion, FlagCompletion, MatchAlgorithm, VariableCompletion,
|
||||
DotNuCompletion, FileCompletion, FlagCompletion, VariableCompletion,
|
||||
};
|
||||
use nu_engine::eval_block;
|
||||
use nu_parser::{flatten_expression, parse, FlatShape};
|
||||
@ -39,15 +39,12 @@ impl NuCompleter {
|
||||
) -> Vec<Suggestion> {
|
||||
let config = self.engine_state.get_config();
|
||||
|
||||
let mut options = CompletionOptions {
|
||||
let options = CompletionOptions {
|
||||
case_sensitive: config.case_sensitive_completions,
|
||||
match_algorithm: config.completion_algorithm.into(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if config.completion_algorithm == "fuzzy" {
|
||||
options.match_algorithm = MatchAlgorithm::Fuzzy;
|
||||
}
|
||||
|
||||
// Fetch
|
||||
let mut suggestions =
|
||||
completer.fetch(working_set, prefix.clone(), new_span, offset, pos, &options);
|
||||
@ -347,7 +344,9 @@ impl NuCompleter {
|
||||
if let Some(external_result) = self.external_completion(
|
||||
block_id, &spans, offset, new_span,
|
||||
) {
|
||||
return external_result;
|
||||
if !external_result.is_empty() {
|
||||
return external_result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::completions::{matches, CompletionOptions};
|
||||
use nu_path::home_dir;
|
||||
use nu_protocol::{engine::StateWorkingSet, Span};
|
||||
use std::path::{is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP};
|
||||
|
||||
fn complete_rec(
|
||||
@ -152,9 +153,44 @@ pub fn complete_item(
|
||||
pub fn escape_path(path: String, dir: bool) -> String {
|
||||
let filename_contaminated = !dir && path.contains(['\'', '"', ' ', '#', '(', ')']);
|
||||
let dirname_contaminated = dir && path.contains(['\'', '"', ' ', '#']);
|
||||
if filename_contaminated || dirname_contaminated || path.parse::<f64>().is_ok() {
|
||||
let maybe_flag = path.starts_with('-');
|
||||
let maybe_number = path.parse::<f64>().is_ok();
|
||||
if filename_contaminated || dirname_contaminated || maybe_flag || maybe_number {
|
||||
format!("`{path}`")
|
||||
} else {
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AdjustView {
|
||||
pub prefix: String,
|
||||
pub span: Span,
|
||||
pub readjusted: bool,
|
||||
}
|
||||
|
||||
pub fn adjust_if_intermediate(
|
||||
prefix: &[u8],
|
||||
working_set: &StateWorkingSet,
|
||||
mut span: nu_protocol::Span,
|
||||
) -> AdjustView {
|
||||
let span_contents = String::from_utf8_lossy(working_set.get_span_contents(span)).to_string();
|
||||
let mut prefix = String::from_utf8_lossy(prefix).to_string();
|
||||
|
||||
// A difference of 1 because of the cursor's unicode code point in between.
|
||||
// Using .chars().count() because unicode and Windows.
|
||||
let readjusted = span_contents.chars().count() - prefix.chars().count() > 1;
|
||||
if readjusted {
|
||||
let remnant: String = span_contents
|
||||
.chars()
|
||||
.skip(prefix.chars().count() + 1)
|
||||
.take_while(|&c| !is_separator(c))
|
||||
.collect();
|
||||
prefix.push_str(&remnant);
|
||||
span = Span::new(span.start, span.start + prefix.chars().count() + 1);
|
||||
}
|
||||
AdjustView {
|
||||
prefix,
|
||||
span,
|
||||
readjusted,
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use std::fmt::Display;
|
||||
|
||||
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
||||
use nu_parser::trim_quotes_str;
|
||||
use nu_protocol::CompletionAlgorithm;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum SortBy {
|
||||
@ -55,6 +56,15 @@ impl MatchAlgorithm {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CompletionAlgorithm> for MatchAlgorithm {
|
||||
fn from(value: CompletionAlgorithm) -> Self {
|
||||
match value {
|
||||
CompletionAlgorithm::Prefix => MatchAlgorithm::Prefix,
|
||||
CompletionAlgorithm::Fuzzy => MatchAlgorithm::Fuzzy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for MatchAlgorithm {
|
||||
type Error = InvalidMatchAlgorithm;
|
||||
|
||||
|
@ -5,6 +5,7 @@ use nu_protocol::{
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
PipelineData, Span, Type, Value,
|
||||
};
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
use reedline::Suggestion;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
@ -79,21 +80,20 @@ impl Completer for CustomCompletion {
|
||||
.map(|pd| {
|
||||
let value = pd.into_value(span);
|
||||
match &value {
|
||||
Value::Record { .. } => {
|
||||
let completions = value
|
||||
.get_data_by_key("completions")
|
||||
Value::Record { val, .. } => {
|
||||
let completions = val
|
||||
.get("completions")
|
||||
.and_then(|val| {
|
||||
val.as_list()
|
||||
.ok()
|
||||
.map(|it| map_value_completions(it.iter(), span, offset))
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let options = value.get_data_by_key("options");
|
||||
let options = val.get("options");
|
||||
|
||||
if let Some(Value::Record { .. }) = &options {
|
||||
let options = options.unwrap_or_default();
|
||||
if let Some(Value::Record { val: options, .. }) = &options {
|
||||
let should_sort = options
|
||||
.get_data_by_key("sort")
|
||||
.get("sort")
|
||||
.and_then(|val| val.as_bool().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
@ -103,11 +103,11 @@ impl Completer for CustomCompletion {
|
||||
|
||||
custom_completion_options = Some(CompletionOptions {
|
||||
case_sensitive: options
|
||||
.get_data_by_key("case_sensitive")
|
||||
.get("case_sensitive")
|
||||
.and_then(|val| val.as_bool().ok())
|
||||
.unwrap_or(true),
|
||||
positional: options
|
||||
.get_data_by_key("positional")
|
||||
.get("positional")
|
||||
.and_then(|val| val.as_bool().ok())
|
||||
.unwrap_or(true),
|
||||
sort_by: if should_sort {
|
||||
@ -115,9 +115,7 @@ impl Completer for CustomCompletion {
|
||||
} else {
|
||||
SortBy::None
|
||||
},
|
||||
match_algorithm: match options
|
||||
.get_data_by_key("completion_algorithm")
|
||||
{
|
||||
match_algorithm: match options.get("completion_algorithm") {
|
||||
Some(option) => option
|
||||
.as_string()
|
||||
.ok()
|
||||
@ -156,8 +154,8 @@ fn filter(prefix: &[u8], items: Vec<Suggestion>, options: &CompletionOptions) ->
|
||||
(true, true) => it.value.as_bytes().starts_with(prefix),
|
||||
(true, false) => it.value.contains(std::str::from_utf8(prefix).unwrap_or("")),
|
||||
(false, positional) => {
|
||||
let value = it.value.to_lowercase();
|
||||
let prefix = std::str::from_utf8(prefix).unwrap_or("").to_lowercase();
|
||||
let value = it.value.to_folded_case();
|
||||
let prefix = std::str::from_utf8(prefix).unwrap_or("").to_folded_case();
|
||||
if positional {
|
||||
value.starts_with(&prefix)
|
||||
} else {
|
||||
|
@ -1,4 +1,7 @@
|
||||
use crate::completions::{completion_common::complete_item, Completer, CompletionOptions, SortBy};
|
||||
use crate::completions::{
|
||||
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
|
||||
Completer, CompletionOptions, SortBy,
|
||||
};
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, StateWorkingSet},
|
||||
levenshtein_distance, Span,
|
||||
@ -21,19 +24,19 @@ impl DirectoryCompletion {
|
||||
impl Completer for DirectoryCompletion {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
_: &StateWorkingSet,
|
||||
working_set: &StateWorkingSet,
|
||||
prefix: Vec<u8>,
|
||||
span: Span,
|
||||
offset: usize,
|
||||
_: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<Suggestion> {
|
||||
let partial = String::from_utf8_lossy(&prefix).to_string();
|
||||
let AdjustView { prefix, span, .. } = adjust_if_intermediate(&prefix, working_set, span);
|
||||
|
||||
// Filter only the folders
|
||||
let output: Vec<_> = directory_completion(
|
||||
span,
|
||||
&partial,
|
||||
&prefix,
|
||||
&self.engine_state.current_work_dir(),
|
||||
options,
|
||||
)
|
||||
|
@ -1,8 +1,12 @@
|
||||
use crate::completions::{completion_common::complete_item, Completer, CompletionOptions, SortBy};
|
||||
use crate::completions::{
|
||||
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
|
||||
Completer, CompletionOptions, SortBy,
|
||||
};
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, StateWorkingSet},
|
||||
levenshtein_distance, Span,
|
||||
};
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
use reedline::Suggestion;
|
||||
use std::path::{Path, MAIN_SEPARATOR as SEP};
|
||||
use std::sync::Arc;
|
||||
@ -21,15 +25,21 @@ impl FileCompletion {
|
||||
impl Completer for FileCompletion {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
_: &StateWorkingSet,
|
||||
working_set: &StateWorkingSet,
|
||||
prefix: Vec<u8>,
|
||||
span: Span,
|
||||
offset: usize,
|
||||
_: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<Suggestion> {
|
||||
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
||||
let output: Vec<_> = file_path_completion(
|
||||
let AdjustView {
|
||||
prefix,
|
||||
span,
|
||||
readjusted,
|
||||
} = adjust_if_intermediate(&prefix, working_set, span);
|
||||
|
||||
let output: Vec<_> = complete_item(
|
||||
readjusted,
|
||||
span,
|
||||
&prefix,
|
||||
&self.engine_state.current_work_dir(),
|
||||
@ -116,7 +126,7 @@ pub fn matches(partial: &str, from: &str, options: &CompletionOptions) -> bool {
|
||||
if !options.case_sensitive {
|
||||
return options
|
||||
.match_algorithm
|
||||
.matches_str(&from.to_ascii_lowercase(), &partial.to_ascii_lowercase());
|
||||
.matches_str(&from.to_folded_case(), &partial.to_folded_case());
|
||||
}
|
||||
|
||||
options.match_algorithm.matches_str(from, partial)
|
||||
|
@ -43,10 +43,8 @@ impl Completer for VariableCompletion {
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<Suggestion> {
|
||||
let mut output = vec![];
|
||||
let builtins = ["$nu", "$in", "$env", "$nothing"];
|
||||
let var_str = std::str::from_utf8(&self.var_context.0)
|
||||
.unwrap_or("")
|
||||
.to_lowercase();
|
||||
let builtins = ["$nu", "$in", "$env"];
|
||||
let var_str = std::str::from_utf8(&self.var_context.0).unwrap_or("");
|
||||
let var_id = working_set.find_variable(&self.var_context.0);
|
||||
let current_span = reedline::Span {
|
||||
start: span.start - offset,
|
||||
@ -57,7 +55,7 @@ impl Completer for VariableCompletion {
|
||||
// Completions for the given variable
|
||||
if !var_str.is_empty() {
|
||||
// Completion for $env.<tab>
|
||||
if var_str.as_str() == "$env" {
|
||||
if var_str == "$env" {
|
||||
let env_vars = self.stack.get_env_vars(&self.engine_state);
|
||||
|
||||
// Return nested values
|
||||
@ -109,7 +107,7 @@ impl Completer for VariableCompletion {
|
||||
}
|
||||
|
||||
// Completions for $nu.<tab>
|
||||
if var_str.as_str() == "$nu" {
|
||||
if var_str == "$nu" {
|
||||
// Eval nu var
|
||||
if let Ok(nuval) = eval_variable(
|
||||
&self.engine_state,
|
||||
@ -237,9 +235,9 @@ fn nested_suggestions(
|
||||
match value {
|
||||
Value::Record { val, .. } => {
|
||||
// Add all the columns as completion
|
||||
for item in val.cols {
|
||||
for (col, _) in val.into_iter() {
|
||||
output.push(Suggestion {
|
||||
value: item,
|
||||
value: col,
|
||||
description: None,
|
||||
extra: None,
|
||||
span: current_span,
|
||||
|
@ -28,8 +28,8 @@ pub fn evaluate_commands(
|
||||
let (block, delta) = {
|
||||
if let Some(ref t_mode) = table_mode {
|
||||
let mut config = engine_state.get_config().clone();
|
||||
config.table_mode = t_mode.as_string()?;
|
||||
engine_state.set_config(&config);
|
||||
config.table_mode = t_mode.as_string()?.parse().unwrap_or_default();
|
||||
engine_state.set_config(config);
|
||||
}
|
||||
|
||||
let mut working_set = StateWorkingSet::new(engine_state);
|
||||
@ -55,7 +55,7 @@ pub fn evaluate_commands(
|
||||
Ok(pipeline_data) => {
|
||||
let mut config = engine_state.get_config().clone();
|
||||
if let Some(t_mode) = table_mode {
|
||||
config.table_mode = t_mode.as_string()?;
|
||||
config.table_mode = t_mode.as_string()?.parse().unwrap_or_default();
|
||||
}
|
||||
crate::eval_file::print_table_or_error(engine_state, stack, pipeline_data, &mut config)
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ pub(crate) fn print_table_or_error(
|
||||
};
|
||||
|
||||
// Change the engine_state config to use the passed in configuration
|
||||
engine_state.set_config(config);
|
||||
engine_state.set_config(config.clone());
|
||||
|
||||
if let PipelineData::Value(Value::Error { error, .. }, ..) = &pipeline_data {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
|
@ -1,5 +1,6 @@
|
||||
use nu_engine::documentation::get_flags_section;
|
||||
use nu_protocol::{engine::EngineState, levenshtein_distance};
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
use reedline::{Completer, Suggestion};
|
||||
use std::fmt::Write;
|
||||
use std::sync::Arc;
|
||||
@ -13,21 +14,19 @@ impl NuHelpCompleter {
|
||||
|
||||
fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> {
|
||||
let full_commands = self.0.get_signatures_with_examples(false);
|
||||
let folded_line = line.to_folded_case();
|
||||
|
||||
//Vec<(Signature, Vec<Example>, bool, bool)> {
|
||||
let mut commands = full_commands
|
||||
.iter()
|
||||
.filter(|(sig, _, _, _, _)| {
|
||||
sig.name.to_lowercase().contains(&line.to_lowercase())
|
||||
|| sig.usage.to_lowercase().contains(&line.to_lowercase())
|
||||
sig.name.to_folded_case().contains(&folded_line)
|
||||
|| sig.usage.to_folded_case().contains(&folded_line)
|
||||
|| sig
|
||||
.search_terms
|
||||
.iter()
|
||||
.any(|term| term.to_lowercase().contains(&line.to_lowercase()))
|
||||
|| sig
|
||||
.extra_usage
|
||||
.to_lowercase()
|
||||
.contains(&line.to_lowercase())
|
||||
.any(|term| term.to_folded_case().contains(&folded_line))
|
||||
|| sig.extra_usage.to_folded_case().contains(&folded_line)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
@ -80,24 +80,18 @@ fn convert_to_suggestions(
|
||||
only_buffer_difference: bool,
|
||||
) -> Vec<Suggestion> {
|
||||
match value {
|
||||
Value::Record { .. } => {
|
||||
let text = value
|
||||
.get_data_by_key("value")
|
||||
Value::Record { val, .. } => {
|
||||
let text = val
|
||||
.get("value")
|
||||
.and_then(|val| val.as_string().ok())
|
||||
.unwrap_or_else(|| "No value key".to_string());
|
||||
|
||||
let description = value
|
||||
.get_data_by_key("description")
|
||||
.and_then(|val| val.as_string().ok());
|
||||
let description = val.get("description").and_then(|val| val.as_string().ok());
|
||||
|
||||
let span = match value.get_data_by_key("span") {
|
||||
Some(span @ Value::Record { .. }) => {
|
||||
let start = span
|
||||
.get_data_by_key("start")
|
||||
.and_then(|val| val.as_int().ok());
|
||||
let end = span
|
||||
.get_data_by_key("end")
|
||||
.and_then(|val| val.as_int().ok());
|
||||
let span = match val.get("span") {
|
||||
Some(Value::Record { val: span, .. }) => {
|
||||
let start = span.get("start").and_then(|val| val.as_int().ok());
|
||||
let end = span.get("end").and_then(|val| val.as_int().ok());
|
||||
match (start, end) {
|
||||
(Some(start), Some(end)) => {
|
||||
let start = start.min(end);
|
||||
@ -126,12 +120,12 @@ fn convert_to_suggestions(
|
||||
},
|
||||
};
|
||||
|
||||
let extra = match value.get_data_by_key("extra") {
|
||||
let extra = match val.get("extra") {
|
||||
Some(Value::List { vals, .. }) => {
|
||||
let extra: Vec<String> = vals
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter_map(|extra| match extra {
|
||||
Value::String { val, .. } => Some(val),
|
||||
Value::String { val, .. } => Some(val.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
@ -40,13 +40,9 @@ fn get_prompt_string(
|
||||
stack
|
||||
.get_env_var(engine_state, prompt)
|
||||
.and_then(|v| match v {
|
||||
Value::Closure {
|
||||
val: block_id,
|
||||
captures,
|
||||
..
|
||||
} => {
|
||||
let block = engine_state.get_block(block_id);
|
||||
let mut stack = stack.captures_to_stack(&captures);
|
||||
Value::Closure { val, .. } => {
|
||||
let block = engine_state.get_block(val.block_id);
|
||||
let mut stack = stack.captures_to_stack(val.captures);
|
||||
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
|
||||
let ret_val =
|
||||
eval_subexpression(engine_state, &mut stack, block, PipelineData::empty());
|
||||
|
@ -7,8 +7,8 @@ use nu_parser::parse;
|
||||
use nu_protocol::{
|
||||
create_menus,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
extract_value, Config, ParsedKeybinding, ParsedMenu, PipelineData, Record, ShellError, Span,
|
||||
Value,
|
||||
extract_value, Config, EditBindings, ParsedKeybinding, ParsedMenu, PipelineData, Record,
|
||||
ShellError, Span, Value,
|
||||
};
|
||||
use reedline::{
|
||||
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
||||
@ -248,11 +248,11 @@ pub(crate) fn add_columnar_menu(
|
||||
Value::Nothing { .. } => {
|
||||
Ok(line_editor.with_menu(ReedlineMenu::EngineCompleter(Box::new(columnar_menu))))
|
||||
}
|
||||
Value::Closure { val, captures, .. } => {
|
||||
Value::Closure { val, .. } => {
|
||||
let menu_completer = NuMenuCompleter::new(
|
||||
*val,
|
||||
val.block_id,
|
||||
span,
|
||||
stack.captures_to_stack(captures),
|
||||
stack.captures_to_stack(val.captures.clone()),
|
||||
engine_state,
|
||||
only_buffer_difference,
|
||||
);
|
||||
@ -330,11 +330,11 @@ pub(crate) fn add_list_menu(
|
||||
Value::Nothing { .. } => {
|
||||
Ok(line_editor.with_menu(ReedlineMenu::HistoryMenu(Box::new(list_menu))))
|
||||
}
|
||||
Value::Closure { val, captures, .. } => {
|
||||
Value::Closure { val, .. } => {
|
||||
let menu_completer = NuMenuCompleter::new(
|
||||
*val,
|
||||
val.block_id,
|
||||
span,
|
||||
stack.captures_to_stack(captures),
|
||||
stack.captures_to_stack(val.captures.clone()),
|
||||
engine_state,
|
||||
only_buffer_difference,
|
||||
);
|
||||
@ -448,11 +448,11 @@ pub(crate) fn add_description_menu(
|
||||
completer,
|
||||
}))
|
||||
}
|
||||
Value::Closure { val, captures, .. } => {
|
||||
Value::Closure { val, .. } => {
|
||||
let menu_completer = NuMenuCompleter::new(
|
||||
*val,
|
||||
val.block_id,
|
||||
span,
|
||||
stack.captures_to_stack(captures),
|
||||
stack.captures_to_stack(val.captures.clone()),
|
||||
engine_state,
|
||||
only_buffer_difference,
|
||||
);
|
||||
@ -537,11 +537,11 @@ pub(crate) fn create_keybindings(config: &Config) -> Result<KeybindingsMode, She
|
||||
let mut insert_keybindings = default_vi_insert_keybindings();
|
||||
let mut normal_keybindings = default_vi_normal_keybindings();
|
||||
|
||||
match config.edit_mode.as_str() {
|
||||
"emacs" => {
|
||||
match config.edit_mode {
|
||||
EditBindings::Emacs => {
|
||||
add_menu_keybindings(&mut emacs_keybindings);
|
||||
}
|
||||
_ => {
|
||||
EditBindings::Vi => {
|
||||
add_menu_keybindings(&mut insert_keybindings);
|
||||
add_menu_keybindings(&mut normal_keybindings);
|
||||
}
|
||||
@ -557,9 +557,9 @@ pub(crate) fn create_keybindings(config: &Config) -> Result<KeybindingsMode, She
|
||||
)?
|
||||
}
|
||||
|
||||
match config.edit_mode.as_str() {
|
||||
"emacs" => Ok(KeybindingsMode::Emacs(emacs_keybindings)),
|
||||
_ => Ok(KeybindingsMode::Vi {
|
||||
match config.edit_mode {
|
||||
EditBindings::Emacs => Ok(KeybindingsMode::Emacs(emacs_keybindings)),
|
||||
EditBindings::Vi => Ok(KeybindingsMode::Vi {
|
||||
insert_keybindings,
|
||||
normal_keybindings,
|
||||
}),
|
||||
@ -616,7 +616,7 @@ fn add_parsed_keybinding(
|
||||
let modifier = match keybinding
|
||||
.modifier
|
||||
.into_string("", config)
|
||||
.to_lowercase()
|
||||
.to_ascii_lowercase()
|
||||
.as_str()
|
||||
{
|
||||
"control" => KeyModifiers::CONTROL,
|
||||
@ -641,7 +641,7 @@ fn add_parsed_keybinding(
|
||||
let keycode = match keybinding
|
||||
.keycode
|
||||
.into_string("", config)
|
||||
.to_lowercase()
|
||||
.to_ascii_lowercase()
|
||||
.as_str()
|
||||
{
|
||||
"backspace" => KeyCode::Backspace,
|
||||
@ -728,7 +728,7 @@ fn parse_event(value: &Value, config: &Config) -> Result<Option<ReedlineEvent>,
|
||||
match value {
|
||||
Value::Record { val: record, .. } => match EventType::try_from_record(record, span)? {
|
||||
EventType::Send(value) => event_from_record(
|
||||
value.into_string("", config).to_lowercase().as_str(),
|
||||
value.into_string("", config).to_ascii_lowercase().as_str(),
|
||||
record,
|
||||
config,
|
||||
span,
|
||||
@ -736,7 +736,7 @@ fn parse_event(value: &Value, config: &Config) -> Result<Option<ReedlineEvent>,
|
||||
.map(Some),
|
||||
EventType::Edit(value) => {
|
||||
let edit = edit_from_record(
|
||||
value.into_string("", config).to_lowercase().as_str(),
|
||||
value.into_string("", config).to_ascii_lowercase().as_str(),
|
||||
record,
|
||||
config,
|
||||
span,
|
||||
@ -976,13 +976,15 @@ fn extract_char(value: &Value, config: &Config) -> Result<char, ShellError> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use nu_protocol::record;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_send_event() {
|
||||
let cols = vec!["send".to_string()];
|
||||
let vals = vec![Value::test_string("Enter")];
|
||||
let event = Record { vals, cols };
|
||||
let event = record! {
|
||||
"send" => Value::test_string("Enter"),
|
||||
};
|
||||
|
||||
let span = Span::test_data();
|
||||
let b = EventType::try_from_record(&event, span).unwrap();
|
||||
@ -997,9 +999,9 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_edit_event() {
|
||||
let cols = vec!["edit".to_string()];
|
||||
let vals = vec![Value::test_string("Clear")];
|
||||
let event = Record { vals, cols };
|
||||
let event = record! {
|
||||
"edit" => Value::test_string("Clear"),
|
||||
};
|
||||
|
||||
let span = Span::test_data();
|
||||
let b = EventType::try_from_record(&event, span).unwrap();
|
||||
@ -1017,12 +1019,10 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_send_menu() {
|
||||
let cols = vec!["send".to_string(), "name".to_string()];
|
||||
let vals = vec![
|
||||
Value::test_string("Menu"),
|
||||
Value::test_string("history_menu"),
|
||||
];
|
||||
let event = Record { vals, cols };
|
||||
let event = record! {
|
||||
"send" => Value::test_string("Menu"),
|
||||
"name" => Value::test_string("history_menu"),
|
||||
};
|
||||
|
||||
let span = Span::test_data();
|
||||
let b = EventType::try_from_record(&event, span).unwrap();
|
||||
@ -1040,28 +1040,19 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_until_event() {
|
||||
// Menu event
|
||||
let cols = vec!["send".to_string(), "name".to_string()];
|
||||
let vals = vec![
|
||||
Value::test_string("Menu"),
|
||||
Value::test_string("history_menu"),
|
||||
];
|
||||
|
||||
let menu_event = Value::test_record(Record { cols, vals });
|
||||
|
||||
// Enter event
|
||||
let cols = vec!["send".to_string()];
|
||||
let vals = vec![Value::test_string("Enter")];
|
||||
|
||||
let enter_event = Value::test_record(Record { cols, vals });
|
||||
|
||||
// Until event
|
||||
let cols = vec!["until".to_string()];
|
||||
let vals = vec![Value::list(
|
||||
vec![menu_event, enter_event],
|
||||
Span::test_data(),
|
||||
)];
|
||||
let event = Record { cols, vals };
|
||||
let menu_event = Value::test_record(record! {
|
||||
"send" => Value::test_string("Menu"),
|
||||
"name" => Value::test_string("history_menu"),
|
||||
});
|
||||
let enter_event = Value::test_record(record! {
|
||||
"send" => Value::test_string("Enter"),
|
||||
});
|
||||
let event = record! {
|
||||
"until" => Value::list(
|
||||
vec![menu_event, enter_event],
|
||||
Span::test_data(),
|
||||
),
|
||||
};
|
||||
|
||||
let span = Span::test_data();
|
||||
let b = EventType::try_from_record(&event, span).unwrap();
|
||||
@ -1082,22 +1073,13 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_multiple_event() {
|
||||
// Menu event
|
||||
let cols = vec!["send".to_string(), "name".to_string()];
|
||||
let vals = vec![
|
||||
Value::test_string("Menu"),
|
||||
Value::test_string("history_menu"),
|
||||
];
|
||||
|
||||
let menu_event = Value::test_record(Record { cols, vals });
|
||||
|
||||
// Enter event
|
||||
let cols = vec!["send".to_string()];
|
||||
let vals = vec![Value::test_string("Enter")];
|
||||
|
||||
let enter_event = Value::test_record(Record { cols, vals });
|
||||
|
||||
// Multiple event
|
||||
let menu_event = Value::test_record(record! {
|
||||
"send" => Value::test_string("Menu"),
|
||||
"name" => Value::test_string("history_menu"),
|
||||
});
|
||||
let enter_event = Value::test_record(record! {
|
||||
"send" => Value::test_string("Enter"),
|
||||
});
|
||||
let event = Value::list(vec![menu_event, enter_event], Span::test_data());
|
||||
|
||||
let config = Config::default();
|
||||
@ -1113,9 +1095,9 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_error() {
|
||||
let cols = vec!["not_exist".to_string()];
|
||||
let vals = vec![Value::test_string("Enter")];
|
||||
let event = Record { cols, vals };
|
||||
let event = record! {
|
||||
"not_exist" => Value::test_string("Enter"),
|
||||
};
|
||||
|
||||
let span = Span::test_data();
|
||||
let b = EventType::try_from_record(&event, span);
|
||||
|
@ -22,8 +22,8 @@ use nu_protocol::{
|
||||
};
|
||||
use nu_utils::utils::perf;
|
||||
use reedline::{
|
||||
CursorConfig, DefaultHinter, EditCommand, Emacs, FileBackedHistory, HistorySessionId, Reedline,
|
||||
SqliteBackedHistory, Vi,
|
||||
CursorConfig, CwdAwareHinter, EditCommand, Emacs, FileBackedHistory, HistorySessionId,
|
||||
Reedline, SqliteBackedHistory, Vi,
|
||||
};
|
||||
use std::{
|
||||
env::temp_dir,
|
||||
@ -108,16 +108,8 @@ pub fn evaluate_repl(
|
||||
use_color,
|
||||
);
|
||||
|
||||
let config = engine_state.get_config();
|
||||
if config.bracketed_paste {
|
||||
// try to enable bracketed paste
|
||||
// It doesn't work on windows system: https://github.com/crossterm-rs/crossterm/issues/737
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let _ = line_editor.enable_bracketed_paste();
|
||||
}
|
||||
|
||||
// Setup history_isolation aka "history per session"
|
||||
let history_isolation = config.history_isolation;
|
||||
let history_isolation = engine_state.get_config().history_isolation;
|
||||
let history_session_id = if history_isolation {
|
||||
Reedline::create_history_session_id()
|
||||
} else {
|
||||
@ -182,12 +174,8 @@ pub fn evaluate_repl(
|
||||
);
|
||||
}
|
||||
|
||||
if engine_state.get_config().use_kitty_protocol {
|
||||
if line_editor.can_use_kitty_protocol() {
|
||||
line_editor.enable_kitty_protocol();
|
||||
} else {
|
||||
warn!("Terminal doesn't support use_kitty_protocol config");
|
||||
}
|
||||
if engine_state.get_config().use_kitty_protocol && !reedline::kitty_protocol_available() {
|
||||
warn!("Terminal doesn't support use_kitty_protocol config");
|
||||
}
|
||||
|
||||
loop {
|
||||
@ -245,15 +233,9 @@ pub fn evaluate_repl(
|
||||
|
||||
// Find the configured cursor shapes for each mode
|
||||
let cursor_config = CursorConfig {
|
||||
vi_insert: config
|
||||
.cursor_shape_vi_insert
|
||||
.map(map_nucursorshape_to_cursorshape),
|
||||
vi_normal: config
|
||||
.cursor_shape_vi_normal
|
||||
.map(map_nucursorshape_to_cursorshape),
|
||||
emacs: config
|
||||
.cursor_shape_emacs
|
||||
.map(map_nucursorshape_to_cursorshape),
|
||||
vi_insert: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_insert),
|
||||
vi_normal: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_normal),
|
||||
emacs: map_nucursorshape_to_cursorshape(config.cursor_shape_emacs),
|
||||
};
|
||||
perf(
|
||||
"get config/cursor config",
|
||||
@ -267,6 +249,10 @@ pub fn evaluate_repl(
|
||||
start_time = std::time::Instant::now();
|
||||
|
||||
line_editor = line_editor
|
||||
.use_kitty_keyboard_enhancement(config.use_kitty_protocol)
|
||||
// try to enable bracketed paste
|
||||
// It doesn't work on windows system: https://github.com/crossterm-rs/crossterm/issues/737
|
||||
.use_bracketed_paste(cfg!(not(target_os = "windows")) && config.bracketed_paste)
|
||||
.with_highlighter(Box::new(NuHighlighter {
|
||||
engine_state: engine_reference.clone(),
|
||||
config: config.clone(),
|
||||
@ -302,7 +288,7 @@ pub fn evaluate_repl(
|
||||
line_editor.with_hinter(Box::new({
|
||||
// As of Nov 2022, "hints" color_config closures only get `null` passed in.
|
||||
let style = style_computer.compute("hints", &Value::nothing(Span::unknown()));
|
||||
DefaultHinter::default().with_style(style)
|
||||
CwdAwareHinter::default().with_style(style)
|
||||
}))
|
||||
} else {
|
||||
line_editor.disable_hints()
|
||||
@ -596,10 +582,6 @@ pub fn evaluate_repl(
|
||||
PipelineData::empty(),
|
||||
false,
|
||||
);
|
||||
if engine_state.get_config().bracketed_paste {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let _ = line_editor.enable_bracketed_paste();
|
||||
}
|
||||
}
|
||||
let cmd_duration = start_time.elapsed();
|
||||
|
||||
@ -626,19 +608,29 @@ pub fn evaluate_repl(
|
||||
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
|
||||
let path = cwd.as_string()?;
|
||||
|
||||
// Communicate the path as OSC 7 (often used for spawning new tabs in the same dir)
|
||||
run_ansi_sequence(&format!(
|
||||
"\x1b]7;file://{}{}{}\x1b\\",
|
||||
percent_encoding::utf8_percent_encode(
|
||||
&hostname.unwrap_or_else(|| "localhost".to_string()),
|
||||
percent_encoding::CONTROLS
|
||||
),
|
||||
if path.starts_with('/') { "" } else { "/" },
|
||||
percent_encoding::utf8_percent_encode(
|
||||
&path,
|
||||
percent_encoding::CONTROLS
|
||||
)
|
||||
))?;
|
||||
// Supported escape sequences of Microsoft's Visual Studio Code (vscode)
|
||||
// https://code.visualstudio.com/docs/terminal/shell-integration#_supported-escape-sequences
|
||||
if stack.get_env_var(engine_state, "TERM_PROGRAM")
|
||||
== Some(Value::test_string("vscode"))
|
||||
{
|
||||
// If we're in vscode, run their specific ansi escape sequence.
|
||||
// This is helpful for ctrl+g to change directories in the terminal.
|
||||
run_ansi_sequence(&format!("\x1b]633;P;Cwd={}\x1b\\", path))?;
|
||||
} else {
|
||||
// Otherwise, communicate the path as OSC 7 (often used for spawning new tabs in the same dir)
|
||||
run_ansi_sequence(&format!(
|
||||
"\x1b]7;file://{}{}{}\x1b\\",
|
||||
percent_encoding::utf8_percent_encode(
|
||||
&hostname.unwrap_or_else(|| "localhost".to_string()),
|
||||
percent_encoding::CONTROLS
|
||||
),
|
||||
if path.starts_with('/') { "" } else { "/" },
|
||||
percent_encoding::utf8_percent_encode(
|
||||
&path,
|
||||
percent_encoding::CONTROLS
|
||||
)
|
||||
))?;
|
||||
}
|
||||
|
||||
// Try to abbreviate string for windows title
|
||||
let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
|
||||
@ -760,14 +752,15 @@ fn update_line_editor_history(
|
||||
Ok(line_editor)
|
||||
}
|
||||
|
||||
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> SetCursorStyle {
|
||||
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> Option<SetCursorStyle> {
|
||||
match shape {
|
||||
NuCursorShape::Block => SetCursorStyle::SteadyBlock,
|
||||
NuCursorShape::UnderScore => SetCursorStyle::SteadyUnderScore,
|
||||
NuCursorShape::Line => SetCursorStyle::SteadyBar,
|
||||
NuCursorShape::BlinkBlock => SetCursorStyle::BlinkingBlock,
|
||||
NuCursorShape::BlinkUnderScore => SetCursorStyle::BlinkingUnderScore,
|
||||
NuCursorShape::BlinkLine => SetCursorStyle::BlinkingBar,
|
||||
NuCursorShape::Block => Some(SetCursorStyle::SteadyBlock),
|
||||
NuCursorShape::UnderScore => Some(SetCursorStyle::SteadyUnderScore),
|
||||
NuCursorShape::Line => Some(SetCursorStyle::SteadyBar),
|
||||
NuCursorShape::BlinkBlock => Some(SetCursorStyle::BlinkingBlock),
|
||||
NuCursorShape::BlinkUnderScore => Some(SetCursorStyle::BlinkingUnderScore),
|
||||
NuCursorShape::BlinkLine => Some(SetCursorStyle::BlinkingBar),
|
||||
NuCursorShape::Inherit => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -527,6 +527,10 @@ fn file_completion_quoted() {
|
||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"`--help`".to_string(),
|
||||
"`-42`".to_string(),
|
||||
"`-inf`".to_string(),
|
||||
"`4.2`".to_string(),
|
||||
"`te st.txt`".to_string(),
|
||||
"`te#st.txt`".to_string(),
|
||||
"`te'st.txt`".to_string(),
|
||||
@ -791,7 +795,7 @@ fn run_external_completion(block: &str, input: &str) -> Vec<Suggestion> {
|
||||
// Change config adding the external completer
|
||||
let mut config = engine_state.get_config().clone();
|
||||
config.external_completer = Some(latest_block_id);
|
||||
engine_state.set_config(&config);
|
||||
engine_state.set_config(config);
|
||||
|
||||
// Instantiate a new completer
|
||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine_state), stack);
|
||||
|
@ -5,14 +5,21 @@ edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cmd-base"
|
||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.86.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.86.0" }
|
||||
nu-protocol = { version = "0.86.0", path = "../nu-protocol" }
|
||||
indexmap = { version = "2.0" }
|
||||
miette = { version = "5.10", features = ["fancy-no-backtrace"] }
|
||||
nu-engine = { path = "../nu-engine", version = "0.87.1" }
|
||||
nu-glob = { path = "../nu-glob", version = "0.87.1" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.87.1" }
|
||||
nu-path = { path = "../nu-path", version = "0.87.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.87.1" }
|
||||
|
||||
indexmap = "2.1"
|
||||
miette = "5.10.0"
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.87.1" }
|
||||
rstest = "0.18.2"
|
||||
|
21
crates/nu-cmd-base/LICENSE
Normal file
21
crates/nu-cmd-base/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
207
crates/nu-cmd-base/src/arg_glob.rs
Normal file
207
crates/nu-cmd-base/src/arg_glob.rs
Normal file
@ -0,0 +1,207 @@
|
||||
// utilities for expanding globs in command arguments
|
||||
|
||||
use nu_glob::{glob_with_parent, MatchOptions, Paths};
|
||||
use nu_protocol::{ShellError, Spanned};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
// standard glob options to use for filesystem command arguments
|
||||
|
||||
const GLOB_PARAMS: MatchOptions = MatchOptions {
|
||||
case_sensitive: true,
|
||||
require_literal_separator: false,
|
||||
require_literal_leading_dot: false,
|
||||
recursive_match_hidden_dir: true,
|
||||
};
|
||||
|
||||
// handle an argument that could be a literal path or a glob.
|
||||
// if literal path, return just that (whether user can access it or not).
|
||||
// if glob, expand into matching paths, using GLOB_PARAMS options.
|
||||
pub fn arg_glob(
|
||||
pattern: &Spanned<String>, // alleged path or glob
|
||||
cwd: &Path, // current working directory
|
||||
) -> Result<Paths, ShellError> {
|
||||
arg_glob_opt(pattern, cwd, GLOB_PARAMS)
|
||||
}
|
||||
|
||||
// variant of [arg_glob] that requires literal dot prefix in pattern to match dot-prefixed path.
|
||||
pub fn arg_glob_leading_dot(pattern: &Spanned<String>, cwd: &Path) -> Result<Paths, ShellError> {
|
||||
arg_glob_opt(
|
||||
pattern,
|
||||
cwd,
|
||||
MatchOptions {
|
||||
require_literal_leading_dot: true,
|
||||
..GLOB_PARAMS
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn arg_glob_opt(
|
||||
pattern: &Spanned<String>,
|
||||
cwd: &Path,
|
||||
options: MatchOptions,
|
||||
) -> Result<Paths, ShellError> {
|
||||
// remove ansi coloring (?)
|
||||
let pattern = {
|
||||
Spanned {
|
||||
item: nu_utils::strip_ansi_string_unlikely(pattern.item.clone()),
|
||||
span: pattern.span,
|
||||
}
|
||||
};
|
||||
|
||||
// if there's a file with same path as the pattern, just return that.
|
||||
let pp = cwd.join(&pattern.item);
|
||||
let md = fs::metadata(pp);
|
||||
#[allow(clippy::single_match)]
|
||||
match md {
|
||||
Ok(_metadata) => {
|
||||
return Ok(Paths::single(&PathBuf::from(pattern.item), cwd));
|
||||
}
|
||||
// file not found, but also "invalid chars in file" (e.g * on Windows). Fall through and glob
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
// user wasn't referring to a specific thing in filesystem, try to glob it.
|
||||
match glob_with_parent(&pattern.item, options, cwd) {
|
||||
Ok(p) => Ok(p),
|
||||
Err(pat_err) => {
|
||||
Err(ShellError::InvalidGlobPattern(
|
||||
pat_err.msg.into(),
|
||||
pattern.span, // improve specificity
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use nu_glob::GlobResult;
|
||||
use nu_protocol::{Span, Spanned};
|
||||
use nu_test_support::fs::Stub::EmptyFile;
|
||||
use nu_test_support::playground::Playground;
|
||||
use rstest::rstest;
|
||||
|
||||
fn spanned_string(str: &str) -> Spanned<String> {
|
||||
Spanned {
|
||||
item: str.to_string(),
|
||||
span: Span::test_data(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_something() {
|
||||
let act = arg_glob(&spanned_string("*"), &PathBuf::from("."));
|
||||
assert!(act.is_ok());
|
||||
for f in act.expect("checked ok") {
|
||||
match f {
|
||||
Ok(p) => {
|
||||
assert!(!p.to_str().unwrap().is_empty());
|
||||
}
|
||||
Err(e) => panic!("unexpected error {:?}", e),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn glob_format_error() {
|
||||
let act = arg_glob(&spanned_string(r#"ab]c[def"#), &PathBuf::from("."));
|
||||
assert!(act.is_err());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case("*", 4, "no dirs")]
|
||||
#[case("**/*", 7, "incl dirs")]
|
||||
fn glob_subdirs(#[case] pat: &str, #[case] exp_count: usize, #[case] case: &str) {
|
||||
Playground::setup("glob_subdirs", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![
|
||||
EmptyFile("yehuda.txt"),
|
||||
EmptyFile("jttxt"),
|
||||
EmptyFile("andres.txt"),
|
||||
]);
|
||||
sandbox.mkdir(".children");
|
||||
sandbox.within(".children").with_files(vec![
|
||||
EmptyFile("timothy.txt"),
|
||||
EmptyFile("tiffany.txt"),
|
||||
EmptyFile("trish.txt"),
|
||||
]);
|
||||
|
||||
let p: Vec<GlobResult> = arg_glob(&spanned_string(pat), &dirs.test)
|
||||
.expect("no error")
|
||||
.collect();
|
||||
assert_eq!(
|
||||
exp_count,
|
||||
p.iter().filter(|i| i.is_ok()).count(),
|
||||
" case: {case} ",
|
||||
);
|
||||
|
||||
// expected behavior -- that directories are included in results (if name matches pattern)
|
||||
let t = p
|
||||
.iter()
|
||||
.any(|i| i.as_ref().unwrap().to_string_lossy().contains(".children"));
|
||||
assert!(t, "check for dir, case {case}");
|
||||
})
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case("yehuda.txt", true, 1, "matches literal path")]
|
||||
#[case("*", false, 3, "matches glob")]
|
||||
#[case(r#"bad[glob.foo"#, true, 1, "matches literal, would be bad glob pat")]
|
||||
fn exact_vs_glob(
|
||||
#[case] pat: &str,
|
||||
#[case] exp_matches_input: bool,
|
||||
#[case] exp_count: usize,
|
||||
#[case] case: &str,
|
||||
) {
|
||||
Playground::setup("exact_vs_glob", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![
|
||||
EmptyFile("yehuda.txt"),
|
||||
EmptyFile("jttxt"),
|
||||
EmptyFile("bad[glob.foo"),
|
||||
]);
|
||||
|
||||
let res = arg_glob(&spanned_string(pat), &dirs.test)
|
||||
.expect("no error")
|
||||
.collect::<Vec<GlobResult>>();
|
||||
|
||||
eprintln!("res: {:?}", res);
|
||||
if exp_matches_input {
|
||||
assert_eq!(
|
||||
exp_count,
|
||||
res.len(),
|
||||
" case {case}: matches input, but count not 1? "
|
||||
);
|
||||
assert_eq!(
|
||||
&res[0].as_ref().unwrap().to_string_lossy(),
|
||||
pat, // todo: is it OK for glob to return relative paths (not to current cwd, but to arg cwd of arg_glob)?
|
||||
);
|
||||
} else {
|
||||
assert_eq!(exp_count, res.len(), " case: {}: matched glob", case);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(r#"realbad[glob.foo"#, true, 1, "error, bad glob")]
|
||||
fn exact_vs_bad_glob(
|
||||
// if path doesn't exist but pattern is not valid glob, should get error.
|
||||
#[case] pat: &str,
|
||||
#[case] _exp_matches_input: bool,
|
||||
#[case] _exp_count: usize,
|
||||
#[case] _tag: &str,
|
||||
) {
|
||||
Playground::setup("exact_vs_bad_glob", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![
|
||||
EmptyFile("yehuda.txt"),
|
||||
EmptyFile("jttxt"),
|
||||
EmptyFile("bad[glob.foo"),
|
||||
]);
|
||||
|
||||
let res = arg_glob(&spanned_string(pat), &dirs.test);
|
||||
assert!(res
|
||||
.expect_err("expected error")
|
||||
.to_string()
|
||||
.contains("Invalid glob pattern"));
|
||||
})
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ pub fn merge_descriptors(values: &[Value]) -> Vec<String> {
|
||||
let mut seen: IndexSet<String> = indexset! {};
|
||||
for value in values {
|
||||
let data_descriptors = match value {
|
||||
Value::Record { val, .. } => val.cols.clone(),
|
||||
Value::Record { val, .. } => val.columns().cloned().collect(),
|
||||
_ => vec!["".to_string()],
|
||||
};
|
||||
for desc in data_descriptors {
|
||||
|
@ -2,7 +2,6 @@ use crate::util::get_guaranteed_cwd;
|
||||
use miette::Result;
|
||||
use nu_engine::{eval_block, eval_block_with_early_return};
|
||||
use nu_parser::parse;
|
||||
use nu_protocol::ast::PathMember;
|
||||
use nu_protocol::cli_error::{report_error, report_error_new};
|
||||
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
||||
use nu_protocol::{BlockId, PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId};
|
||||
@ -62,28 +61,8 @@ pub fn eval_hook(
|
||||
value: &Value,
|
||||
hook_name: &str,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let value_span = value.span();
|
||||
|
||||
// Hooks can optionally be a record in this form:
|
||||
// {
|
||||
// condition: {|before, after| ... } # block that evaluates to true/false
|
||||
// code: # block or a string
|
||||
// }
|
||||
// The condition block will be run to check whether the main hook (in `code`) should be run.
|
||||
// If it returns true (the default if a condition block is not specified), the hook should be run.
|
||||
let condition_path = PathMember::String {
|
||||
val: "condition".to_string(),
|
||||
span: value_span,
|
||||
optional: false,
|
||||
};
|
||||
let mut output = PipelineData::empty();
|
||||
|
||||
let code_path = PathMember::String {
|
||||
val: "code".to_string(),
|
||||
span: value_span,
|
||||
optional: false,
|
||||
};
|
||||
|
||||
let span = value.span();
|
||||
match value {
|
||||
Value::String { val, .. } => {
|
||||
@ -161,46 +140,47 @@ pub fn eval_hook(
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Value::Record { .. } => {
|
||||
let do_run_hook = if let Ok(condition) =
|
||||
value.clone().follow_cell_path(&[condition_path], false)
|
||||
{
|
||||
Value::Record { val, .. } => {
|
||||
// Hooks can optionally be a record in this form:
|
||||
// {
|
||||
// condition: {|before, after| ... } # block that evaluates to true/false
|
||||
// code: # block or a string
|
||||
// }
|
||||
// The condition block will be run to check whether the main hook (in `code`) should be run.
|
||||
// If it returns true (the default if a condition block is not specified), the hook should be run.
|
||||
let do_run_hook = if let Some(condition) = val.get("condition") {
|
||||
let other_span = condition.span();
|
||||
match condition {
|
||||
Value::Block { val: block_id, .. } | Value::Closure { val: block_id, .. } => {
|
||||
match run_hook_block(
|
||||
engine_state,
|
||||
stack,
|
||||
block_id,
|
||||
None,
|
||||
arguments.clone(),
|
||||
other_span,
|
||||
) {
|
||||
Ok(pipeline_data) => {
|
||||
if let PipelineData::Value(Value::Bool { val, .. }, ..) =
|
||||
pipeline_data
|
||||
{
|
||||
val
|
||||
} else {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
"boolean output".to_string(),
|
||||
"other PipelineData variant".to_string(),
|
||||
other_span,
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err);
|
||||
if let Ok(block_id) = condition.as_block() {
|
||||
match run_hook_block(
|
||||
engine_state,
|
||||
stack,
|
||||
block_id,
|
||||
None,
|
||||
arguments.clone(),
|
||||
other_span,
|
||||
) {
|
||||
Ok(pipeline_data) => {
|
||||
if let PipelineData::Value(Value::Bool { val, .. }, ..) = pipeline_data
|
||||
{
|
||||
val
|
||||
} else {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
"boolean output".to_string(),
|
||||
"other PipelineData variant".to_string(),
|
||||
other_span,
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
other => {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
"block".to_string(),
|
||||
format!("{}", other.get_type()),
|
||||
other_span,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
"block".to_string(),
|
||||
format!("{}", condition.get_type()),
|
||||
other_span,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
// always run the hook
|
||||
@ -208,7 +188,13 @@ pub fn eval_hook(
|
||||
};
|
||||
|
||||
if do_run_hook {
|
||||
let follow = value.clone().follow_cell_path(&[code_path], false)?;
|
||||
let Some(follow) = val.get("code") else {
|
||||
return Err(ShellError::CantFindColumn {
|
||||
col_name: "code".into(),
|
||||
span,
|
||||
src_span: span,
|
||||
});
|
||||
};
|
||||
let source_span = follow.span();
|
||||
match follow {
|
||||
Value::String { val, .. } => {
|
||||
@ -274,17 +260,17 @@ pub fn eval_hook(
|
||||
run_hook_block(
|
||||
engine_state,
|
||||
stack,
|
||||
block_id,
|
||||
*block_id,
|
||||
input,
|
||||
arguments,
|
||||
source_span,
|
||||
)?;
|
||||
}
|
||||
Value::Closure { val: block_id, .. } => {
|
||||
Value::Closure { val, .. } => {
|
||||
run_hook_block(
|
||||
engine_state,
|
||||
stack,
|
||||
block_id,
|
||||
val.block_id,
|
||||
input,
|
||||
arguments,
|
||||
source_span,
|
||||
@ -303,8 +289,8 @@ pub fn eval_hook(
|
||||
Value::Block { val: block_id, .. } => {
|
||||
output = run_hook_block(engine_state, stack, *block_id, input, arguments, span)?;
|
||||
}
|
||||
Value::Closure { val: block_id, .. } => {
|
||||
output = run_hook_block(engine_state, stack, *block_id, input, arguments, span)?;
|
||||
Value::Closure { val, .. } => {
|
||||
output = run_hook_block(engine_state, stack, val.block_id, input, arguments, span)?;
|
||||
}
|
||||
other => {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
|
@ -1,4 +1,7 @@
|
||||
mod arg_glob;
|
||||
pub mod formats;
|
||||
pub mod hook;
|
||||
pub mod input_handler;
|
||||
pub mod util;
|
||||
pub use arg_glob::arg_glob;
|
||||
pub use arg_glob::arg_glob_leading_dot;
|
||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cmd-dataframe"
|
||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-dataframe"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -13,14 +13,15 @@ version = "0.86.0"
|
||||
bench = false
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.86.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.87.1" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.87.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
|
||||
|
||||
# Potential dependencies for extras
|
||||
chrono = { version = "0.4", features = ["std", "unstable-locales"], default-features = false }
|
||||
chrono-tz = "0.8"
|
||||
fancy-regex = "0.11"
|
||||
indexmap = { version = "2.0" }
|
||||
indexmap = { version = "2.1" }
|
||||
num = { version = "0.4", optional = true }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sqlparser = { version = "0.36.1", optional = true }
|
||||
@ -38,6 +39,10 @@ features = [
|
||||
"dtype-categorical",
|
||||
"dtype-datetime",
|
||||
"dtype-struct",
|
||||
"dtype-i8",
|
||||
"dtype-i16",
|
||||
"dtype-u8",
|
||||
"dtype-u16",
|
||||
"dynamic_group_by",
|
||||
"ipc",
|
||||
"is_in",
|
||||
@ -61,5 +66,5 @@ dataframe = ["num", "polars", "polars-io", "sqlparser"]
|
||||
default = []
|
||||
|
||||
[dev-dependencies]
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.86.0" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.87.1" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.87.1" }
|
||||
|
@ -1,10 +1,10 @@
|
||||
use super::super::values::{Column, NuDataFrame};
|
||||
use super::super::values::NuDataFrame;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, Type,
|
||||
};
|
||||
use polars::prelude::DataFrameOps;
|
||||
use polars::{prelude::*, series::Series};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Dummies;
|
||||
@ -34,24 +34,15 @@ impl Command for Dummies {
|
||||
description: "Create new dataframe with dummy variables from a dataframe",
|
||||
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr dummies",
|
||||
result: Some(
|
||||
NuDataFrame::try_from_columns(vec![
|
||||
Column::new(
|
||||
"a_1".to_string(),
|
||||
vec![Value::test_int(1), Value::test_int(0)],
|
||||
),
|
||||
Column::new(
|
||||
"a_3".to_string(),
|
||||
vec![Value::test_int(0), Value::test_int(1)],
|
||||
),
|
||||
Column::new(
|
||||
"b_2".to_string(),
|
||||
vec![Value::test_int(1), Value::test_int(0)],
|
||||
),
|
||||
Column::new(
|
||||
"b_4".to_string(),
|
||||
vec![Value::test_int(0), Value::test_int(1)],
|
||||
),
|
||||
])
|
||||
NuDataFrame::try_from_series(
|
||||
vec![
|
||||
Series::new("a_1", &[1_u8, 0]),
|
||||
Series::new("a_3", &[0_u8, 1]),
|
||||
Series::new("b_2", &[1_u8, 0]),
|
||||
Series::new("b_4", &[0_u8, 1]),
|
||||
],
|
||||
Span::test_data(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Span::test_data()),
|
||||
),
|
||||
@ -60,38 +51,14 @@ impl Command for Dummies {
|
||||
description: "Create new dataframe with dummy variables from a series",
|
||||
example: "[1 2 2 3 3] | dfr into-df | dfr dummies",
|
||||
result: Some(
|
||||
NuDataFrame::try_from_columns(vec![
|
||||
Column::new(
|
||||
"0_1".to_string(),
|
||||
vec![
|
||||
Value::test_int(1),
|
||||
Value::test_int(0),
|
||||
Value::test_int(0),
|
||||
Value::test_int(0),
|
||||
Value::test_int(0),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"0_2".to_string(),
|
||||
vec![
|
||||
Value::test_int(0),
|
||||
Value::test_int(1),
|
||||
Value::test_int(1),
|
||||
Value::test_int(0),
|
||||
Value::test_int(0),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"0_3".to_string(),
|
||||
vec![
|
||||
Value::test_int(0),
|
||||
Value::test_int(0),
|
||||
Value::test_int(0),
|
||||
Value::test_int(1),
|
||||
Value::test_int(1),
|
||||
],
|
||||
),
|
||||
])
|
||||
NuDataFrame::try_from_series(
|
||||
vec![
|
||||
Series::new("0_1", &[1_u8, 0, 0, 0, 0]),
|
||||
Series::new("0_2", &[0_u8, 1, 1, 0, 0]),
|
||||
Series::new("0_3", &[0_u8, 0, 0, 1, 1]),
|
||||
],
|
||||
Span::test_data(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Span::test_data()),
|
||||
),
|
||||
|
@ -152,7 +152,7 @@ fn apply_window_spec(expr: Expr, window_type: Option<&WindowType>) -> Result<Exp
|
||||
fn parse_sql_function(sql_function: &SQLFunction) -> Result<Expr> {
|
||||
use sqlparser::ast::{FunctionArg, FunctionArgExpr};
|
||||
// Function name mostly do not have name space, so it mostly take the first args
|
||||
let function_name = sql_function.name.0[0].value.to_lowercase();
|
||||
let function_name = sql_function.name.0[0].value.to_ascii_lowercase();
|
||||
let args = sql_function
|
||||
.args
|
||||
.iter()
|
||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use crate::dataframe::values::NuExpression;
|
||||
@ -39,18 +39,20 @@ impl Command for ToNu {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
let cols = vec!["index".into(), "a".into(), "b".into()];
|
||||
let rec_1 = Value::test_record(Record {
|
||||
cols: cols.clone(),
|
||||
vals: vec![Value::test_int(0), Value::test_int(1), Value::test_int(2)],
|
||||
let rec_1 = Value::test_record(record! {
|
||||
"index" => Value::test_int(0),
|
||||
"a" => Value::test_int(1),
|
||||
"b" => Value::test_int(2),
|
||||
});
|
||||
let rec_2 = Value::test_record(Record {
|
||||
cols: cols.clone(),
|
||||
vals: vec![Value::test_int(1), Value::test_int(3), Value::test_int(4)],
|
||||
let rec_2 = Value::test_record(record! {
|
||||
"index" => Value::test_int(1),
|
||||
"a" => Value::test_int(3),
|
||||
"b" => Value::test_int(4),
|
||||
});
|
||||
let rec_3 = Value::test_record(Record {
|
||||
cols,
|
||||
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(4)],
|
||||
let rec_3 = Value::test_record(record! {
|
||||
"index" => Value::test_int(2),
|
||||
"a" => Value::test_int(3),
|
||||
"b" => Value::test_int(4),
|
||||
});
|
||||
|
||||
vec![
|
||||
@ -67,9 +69,9 @@ impl Command for ToNu {
|
||||
Example {
|
||||
description: "Convert a col expression into a nushell value",
|
||||
example: "dfr col a | dfr into-nu",
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["expr".into(), "value".into()],
|
||||
vals: vec![Value::test_string("column"), Value::test_string("a")],
|
||||
result: Some(Value::test_record(record! {
|
||||
"expr" => Value::test_string("column"),
|
||||
"value" => Value::test_string("a"),
|
||||
})),
|
||||
},
|
||||
]
|
||||
|
@ -4,7 +4,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -38,20 +38,12 @@ impl Command for ExprAlias {
|
||||
description: "Creates and alias expression",
|
||||
example: "dfr col a | dfr as new_a | dfr into-nu",
|
||||
result: {
|
||||
let cols = vec!["expr".into(), "value".into()];
|
||||
let expr = Value::test_string("column");
|
||||
let value = Value::test_string("a");
|
||||
let expr = Value::test_record(Record {
|
||||
cols,
|
||||
vals: vec![expr, value],
|
||||
});
|
||||
|
||||
let cols = vec!["expr".into(), "alias".into()];
|
||||
let value = Value::test_string("new_a");
|
||||
|
||||
let record = Value::test_record(Record {
|
||||
cols,
|
||||
vals: vec![expr, value],
|
||||
let record = Value::test_record(record! {
|
||||
"expr" => Value::test_record(record! {
|
||||
"expr" => Value::test_string("column"),
|
||||
"value" => Value::test_string("a"),
|
||||
}),
|
||||
"alias" => Value::test_string("new_a"),
|
||||
});
|
||||
|
||||
Some(record)
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
use polars::prelude::col;
|
||||
|
||||
@ -34,9 +34,9 @@ impl Command for ExprCol {
|
||||
vec![Example {
|
||||
description: "Creates a named column expression and converts it to a nu object",
|
||||
example: "dfr col a | dfr into-nu",
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["expr".into(), "value".into()],
|
||||
vals: vec![Value::test_string("column"), Value::test_string("a")],
|
||||
result: Some(Value::test_record(record! {
|
||||
"expr" => Value::test_string("column"),
|
||||
"value" => Value::test_string("a"),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
|
@ -124,12 +124,12 @@ impl Command for ExprDatePart {
|
||||
"microsecond" => expr_dt.microsecond(),
|
||||
"nanosecond" => expr_dt.nanosecond(),
|
||||
_ => {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
format!("{} is not a valid datepart, expected one of year, month, day, hour, minute, second, millisecond, microsecond, nanosecond", part.item),
|
||||
"value originates from here".to_string(),
|
||||
call.head,
|
||||
part.span,
|
||||
));
|
||||
return Err(ShellError::UnsupportedInput {
|
||||
msg: format!("{} is not a valid datepart, expected one of year, month, day, hour, minute, second, millisecond, microsecond, nanosecond", part.item),
|
||||
input: "value originates from here".to_string(),
|
||||
msg_span: call.head,
|
||||
input_span: part.span,
|
||||
});
|
||||
}
|
||||
}.into();
|
||||
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -33,9 +33,9 @@ impl Command for ExprLit {
|
||||
vec![Example {
|
||||
description: "Created a literal expression and converts it to a nu object",
|
||||
example: "dfr lit 2 | dfr into-nu",
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["expr".into(), "value".into()],
|
||||
vals: vec![Value::test_string("literal"), Value::test_string("2")],
|
||||
result: Some(Value::test_record(record! {
|
||||
"expr" => Value::test_string("literal"),
|
||||
"value" => Value::test_string("2"),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
|
@ -98,12 +98,12 @@ fn command(
|
||||
let value = NuDataFrame::dataframe_into_value(res, call.head);
|
||||
Ok(PipelineData::Value(value, None))
|
||||
}
|
||||
_ => Err(ShellError::UnsupportedInput(
|
||||
"Expected the dataframe to have a column".to_string(),
|
||||
"".to_string(),
|
||||
call.head,
|
||||
call.head,
|
||||
)),
|
||||
_ => Err(ShellError::UnsupportedInput {
|
||||
msg: "Expected the dataframe to have a column".to_string(),
|
||||
input: "".to_string(),
|
||||
msg_span: call.head,
|
||||
input_span: call.head,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
use nu_protocol::{FromValue, ShellError, Value};
|
||||
|
||||
pub fn extract_strings(value: Value) -> Result<Vec<String>, ShellError> {
|
||||
let span = value.span();
|
||||
match (
|
||||
<String as FromValue>::from_value(&value),
|
||||
<Vec<String> as FromValue>::from_value(&value),
|
||||
<String as FromValue>::from_value(value.clone()),
|
||||
<Vec<String> as FromValue>::from_value(value),
|
||||
) {
|
||||
(Ok(col), Err(_)) => Ok(vec![col]),
|
||||
(Err(_), Ok(cols)) => Ok(cols),
|
||||
_ => Err(ShellError::IncompatibleParametersSingle {
|
||||
msg: "Expected a string or list of strings".into(),
|
||||
span: value.span(),
|
||||
span,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,31 @@
|
||||
use super::{DataFrameValue, NuDataFrame};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use chrono::{DateTime, FixedOffset, NaiveDateTime};
|
||||
use chrono::{DateTime, Duration, FixedOffset, NaiveTime, TimeZone, Utc};
|
||||
use chrono_tz::Tz;
|
||||
use indexmap::map::{Entry, IndexMap};
|
||||
use nu_protocol::{Record, ShellError, Span, Value};
|
||||
use polars::chunked_array::builder::AnonymousOwnedListBuilder;
|
||||
use polars::chunked_array::object::builder::ObjectChunkedBuilder;
|
||||
use polars::chunked_array::ChunkedArray;
|
||||
use polars::datatypes::AnyValue;
|
||||
use polars::export::arrow::array::{
|
||||
Array, BooleanArray, Float32Array, Float64Array, Int16Array, Int32Array, Int64Array, Int8Array,
|
||||
UInt16Array, UInt32Array, UInt64Array, UInt8Array,
|
||||
};
|
||||
use polars::export::arrow::Either;
|
||||
use polars::prelude::{
|
||||
DataFrame, DataType, DatetimeChunked, Float64Type, Int64Type, IntoSeries,
|
||||
ListBooleanChunkedBuilder, ListBuilderTrait, ListPrimitiveChunkedBuilder, ListType,
|
||||
ListUtf8ChunkedBuilder, NamedFrom, NewChunkedArray, ObjectType, Series, TemporalMethods,
|
||||
TimeUnit,
|
||||
ArrayRef, DataFrame, DataType, DatetimeChunked, Float64Type, Int64Type, IntoSeries,
|
||||
LargeBinaryArray, LargeListArray, LargeStringArray, ListBooleanChunkedBuilder,
|
||||
ListBuilderTrait, ListPrimitiveChunkedBuilder, ListType, ListUtf8ChunkedBuilder, NamedFrom,
|
||||
NewChunkedArray, ObjectType, Series, StructArray, TemporalMethods, TimeUnit,
|
||||
};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
const SECS_PER_DAY: i64 = 86_400;
|
||||
use nu_protocol::{Record, ShellError, Span, Value};
|
||||
|
||||
// The values capacity is for the size of an internal vec.
|
||||
use super::{DataFrameValue, NuDataFrame};
|
||||
|
||||
const NANOS_PER_DAY: i64 = 86_400_000_000_000;
|
||||
|
||||
// The values capacity is for the size of an vec.
|
||||
// Since this is impossible to determine without traversing every value
|
||||
// I just picked one. Since this is for converting back and forth
|
||||
// between nushell tables the values shouldn't be too extremely large for
|
||||
@ -199,7 +207,7 @@ fn value_to_input_type(value: &Value) -> InputType {
|
||||
Value::Filesize { .. } => InputType::Filesize,
|
||||
Value::List { vals, .. } => {
|
||||
// We need to determined the type inside of the list.
|
||||
// Since Value::List does not have any kind of internal
|
||||
// Since Value::List does not have any kind of
|
||||
// type information, we need to look inside the list.
|
||||
// This will cause errors if lists have inconsistent types.
|
||||
// Basically, if a list column needs to be converted to dataframe,
|
||||
@ -805,28 +813,21 @@ fn series_to_values(
|
||||
)),
|
||||
Some(ca) => {
|
||||
let it = ca.into_iter();
|
||||
let values: Vec<Value> =
|
||||
if let (Some(size), Some(from_row)) = (maybe_size, maybe_from_row) {
|
||||
Either::Left(it.skip(from_row).take(size))
|
||||
if let (Some(size), Some(from_row)) = (maybe_size, maybe_from_row) {
|
||||
Either::Left(it.skip(from_row).take(size))
|
||||
} else {
|
||||
Either::Right(it)
|
||||
}
|
||||
.map(|ca| {
|
||||
let sublist: Vec<Value> = if let Some(ref s) = ca {
|
||||
series_to_values(s, None, None, Span::unknown())?
|
||||
} else {
|
||||
Either::Right(it)
|
||||
}
|
||||
.map(|ca| {
|
||||
let sublist = ca
|
||||
.map(|ref s| {
|
||||
match series_to_values(s, None, None, Span::unknown()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
eprintln!("Error list values: {e}");
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap_or(vec![]);
|
||||
Value::list(sublist, span)
|
||||
})
|
||||
.collect::<Vec<Value>>();
|
||||
Ok(values)
|
||||
// empty item
|
||||
vec![]
|
||||
};
|
||||
Ok(Value::list(sublist, span))
|
||||
})
|
||||
.collect::<Result<Vec<Value>, ShellError>>()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -849,51 +850,16 @@ fn series_to_values(
|
||||
}
|
||||
.map(|v| match v {
|
||||
Some(a) => {
|
||||
// elapsed time in day since 1970-01-01
|
||||
let seconds = a as i64 * SECS_PER_DAY;
|
||||
let naive_datetime = match NaiveDateTime::from_timestamp_opt(seconds, 0) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
return Value::error(
|
||||
ShellError::UnsupportedInput(
|
||||
"The given local datetime representation is invalid."
|
||||
.to_string(),
|
||||
format!("timestamp is {a:?}"),
|
||||
span,
|
||||
Span::unknown(),
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
};
|
||||
// Zero length offset
|
||||
let offset = match FixedOffset::east_opt(0) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
return Value::error(
|
||||
ShellError::UnsupportedInput(
|
||||
"The given local datetime representation is invalid."
|
||||
.to_string(),
|
||||
format!("timestamp is {a:?}"),
|
||||
span,
|
||||
Span::unknown(),
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
};
|
||||
let datetime =
|
||||
DateTime::<FixedOffset>::from_naive_utc_and_offset(naive_datetime, offset);
|
||||
|
||||
Value::date(datetime, span)
|
||||
let nanos = nanos_per_day(a);
|
||||
let datetime = datetime_from_epoch_nanos(nanos, &None, span)?;
|
||||
Ok(Value::date(datetime, span))
|
||||
}
|
||||
None => Value::nothing(span),
|
||||
None => Ok(Value::nothing(span)),
|
||||
})
|
||||
.collect::<Vec<Value>>();
|
||||
|
||||
.collect::<Result<Vec<Value>, ShellError>>()?;
|
||||
Ok(values)
|
||||
}
|
||||
DataType::Datetime(time_unit, _) => {
|
||||
DataType::Datetime(time_unit, tz) => {
|
||||
let casted = series.datetime().map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"Error casting column to datetime".into(),
|
||||
@ -912,55 +878,48 @@ fn series_to_values(
|
||||
}
|
||||
.map(|v| match v {
|
||||
Some(a) => {
|
||||
let unit_divisor = match time_unit {
|
||||
TimeUnit::Nanoseconds => 1_000_000_000,
|
||||
TimeUnit::Microseconds => 1_000_000,
|
||||
TimeUnit::Milliseconds => 1_000,
|
||||
};
|
||||
// elapsed time in nano/micro/milliseconds since 1970-01-01
|
||||
let seconds = a / unit_divisor;
|
||||
let naive_datetime = match NaiveDateTime::from_timestamp_opt(seconds, 0) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
return Value::error(
|
||||
ShellError::UnsupportedInput(
|
||||
"The given local datetime representation is invalid."
|
||||
.to_string(),
|
||||
format!("timestamp is {a:?}"),
|
||||
span,
|
||||
Span::unknown(),
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
};
|
||||
// Zero length offset
|
||||
let offset = match FixedOffset::east_opt(0) {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
return Value::error(
|
||||
ShellError::UnsupportedInput(
|
||||
"The given local datetime representation is invalid."
|
||||
.to_string(),
|
||||
format!("timestamp is {a:?}"),
|
||||
span,
|
||||
Span::unknown(),
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
};
|
||||
let datetime =
|
||||
DateTime::<FixedOffset>::from_naive_utc_and_offset(naive_datetime, offset);
|
||||
|
||||
Value::date(datetime, span)
|
||||
let nanos = nanos_from_timeunit(a, *time_unit);
|
||||
let datetime = datetime_from_epoch_nanos(nanos, tz, span)?;
|
||||
Ok(Value::date(datetime, span))
|
||||
}
|
||||
None => Value::nothing(span),
|
||||
None => Ok(Value::nothing(span)),
|
||||
})
|
||||
.collect::<Vec<Value>>();
|
||||
|
||||
.collect::<Result<Vec<Value>, ShellError>>()?;
|
||||
Ok(values)
|
||||
}
|
||||
DataType::Struct(polar_fields) => {
|
||||
let casted = series.struct_().map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"Error casting column to struct".into(),
|
||||
"".to_string(),
|
||||
None,
|
||||
Some(e.to_string()),
|
||||
Vec::new(),
|
||||
)
|
||||
})?;
|
||||
let it = casted.into_iter();
|
||||
let values: Result<Vec<Value>, ShellError> =
|
||||
if let (Some(size), Some(from_row)) = (maybe_size, maybe_from_row) {
|
||||
Either::Left(it.skip(from_row).take(size))
|
||||
} else {
|
||||
Either::Right(it)
|
||||
}
|
||||
.map(|any_values| {
|
||||
let vals: Result<Vec<Value>, ShellError> = any_values
|
||||
.iter()
|
||||
.map(|v| any_value_to_value(v, span))
|
||||
.collect();
|
||||
let cols: Vec<String> = polar_fields
|
||||
.iter()
|
||||
.map(|field| field.name.to_string())
|
||||
.collect();
|
||||
let record = Record { cols, vals: vals? };
|
||||
Ok(Value::record(record, span))
|
||||
})
|
||||
.collect();
|
||||
values
|
||||
}
|
||||
DataType::Time => {
|
||||
let casted = series.timestamp(TimeUnit::Nanoseconds).map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
@ -996,10 +955,252 @@ fn series_to_values(
|
||||
}
|
||||
}
|
||||
|
||||
fn any_value_to_value(any_value: &AnyValue, span: Span) -> Result<Value, ShellError> {
|
||||
match any_value {
|
||||
AnyValue::Null => Ok(Value::nothing(span)),
|
||||
AnyValue::Boolean(b) => Ok(Value::bool(*b, span)),
|
||||
AnyValue::Utf8(s) => Ok(Value::string(s.to_string(), span)),
|
||||
AnyValue::UInt8(i) => Ok(Value::int(*i as i64, span)),
|
||||
AnyValue::UInt16(i) => Ok(Value::int(*i as i64, span)),
|
||||
AnyValue::UInt32(i) => Ok(Value::int(*i as i64, span)),
|
||||
AnyValue::UInt64(i) => Ok(Value::int(*i as i64, span)),
|
||||
AnyValue::Int8(i) => Ok(Value::int(*i as i64, span)),
|
||||
AnyValue::Int16(i) => Ok(Value::int(*i as i64, span)),
|
||||
AnyValue::Int32(i) => Ok(Value::int(*i as i64, span)),
|
||||
AnyValue::Int64(i) => Ok(Value::int(*i, span)),
|
||||
AnyValue::Float32(f) => Ok(Value::float(*f as f64, span)),
|
||||
AnyValue::Float64(f) => Ok(Value::float(*f, span)),
|
||||
AnyValue::Date(d) => {
|
||||
let nanos = nanos_per_day(*d);
|
||||
datetime_from_epoch_nanos(nanos, &None, span)
|
||||
.map(|datetime| Value::date(datetime, span))
|
||||
}
|
||||
AnyValue::Datetime(a, time_unit, tz) => {
|
||||
let nanos = nanos_from_timeunit(*a, *time_unit);
|
||||
datetime_from_epoch_nanos(nanos, tz, span).map(|datetime| Value::date(datetime, span))
|
||||
}
|
||||
AnyValue::Duration(a, time_unit) => {
|
||||
let nanos = match time_unit {
|
||||
TimeUnit::Nanoseconds => *a,
|
||||
TimeUnit::Microseconds => *a * 1_000,
|
||||
TimeUnit::Milliseconds => *a * 1_000_000,
|
||||
};
|
||||
Ok(Value::duration(nanos, span))
|
||||
}
|
||||
// AnyValue::Time represents the current time since midnight.
|
||||
// Unfortunately, there is no timezone related information.
|
||||
// Given this, calculate the current date from UTC and add the time.
|
||||
AnyValue::Time(nanos) => time_from_midnight(*nanos, span),
|
||||
AnyValue::List(series) => {
|
||||
series_to_values(series, None, None, span).map(|values| Value::list(values, span))
|
||||
}
|
||||
AnyValue::Struct(idx, struct_array, s_fields) => {
|
||||
let cols: Vec<String> = s_fields.iter().map(|f| f.name().to_string()).collect();
|
||||
let vals: Result<Vec<Value>, ShellError> = struct_array
|
||||
.values()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(pos, v)| {
|
||||
let f = &s_fields[pos];
|
||||
arr_to_value(&f.dtype, &**v, *idx, span)
|
||||
})
|
||||
.collect();
|
||||
let record = Record { cols, vals: vals? };
|
||||
Ok(Value::record(record, span))
|
||||
}
|
||||
AnyValue::StructOwned(struct_tuple) => {
|
||||
let values: Result<Vec<Value>, ShellError> = struct_tuple
|
||||
.0
|
||||
.iter()
|
||||
.map(|s| any_value_to_value(s, span))
|
||||
.collect();
|
||||
let fields = struct_tuple
|
||||
.1
|
||||
.iter()
|
||||
.map(|f| f.name().to_string())
|
||||
.collect();
|
||||
Ok(Value::Record {
|
||||
val: Record {
|
||||
cols: fields,
|
||||
vals: values?,
|
||||
},
|
||||
internal_span: span,
|
||||
})
|
||||
}
|
||||
AnyValue::Utf8Owned(s) => Ok(Value::string(s.to_string(), span)),
|
||||
AnyValue::Binary(bytes) => Ok(Value::binary(*bytes, span)),
|
||||
AnyValue::BinaryOwned(bytes) => Ok(Value::binary(bytes.to_owned(), span)),
|
||||
e => Err(ShellError::GenericError(
|
||||
"Error creating Value".into(),
|
||||
"".to_string(),
|
||||
None,
|
||||
Some(format!("Value not supported in nushell: {e}")),
|
||||
Vec::new(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn arr_to_value(
|
||||
dt: &DataType,
|
||||
arr: &dyn Array,
|
||||
idx: usize,
|
||||
span: Span,
|
||||
) -> Result<Value, ShellError> {
|
||||
macro_rules! downcast {
|
||||
($casttype:ident) => {{
|
||||
let arr = &*(arr as *const dyn Array as *const $casttype);
|
||||
arr.value_unchecked(idx)
|
||||
}};
|
||||
}
|
||||
|
||||
// Not loving the unsafe here, however this largely based off the one
|
||||
// example I found for converting Array values in:
|
||||
// polars_core::chunked_array::ops::any_value::arr_to_any_value
|
||||
unsafe {
|
||||
match dt {
|
||||
DataType::Boolean => Ok(Value::bool(downcast!(BooleanArray), span)),
|
||||
DataType::UInt8 => Ok(Value::int(downcast!(UInt8Array) as i64, span)),
|
||||
DataType::UInt16 => Ok(Value::int(downcast!(UInt16Array) as i64, span)),
|
||||
DataType::UInt32 => Ok(Value::int(downcast!(UInt32Array) as i64, span)),
|
||||
DataType::UInt64 => Ok(Value::int(downcast!(UInt64Array) as i64, span)),
|
||||
DataType::Int8 => Ok(Value::int(downcast!(Int8Array) as i64, span)),
|
||||
DataType::Int16 => Ok(Value::int(downcast!(Int16Array) as i64, span)),
|
||||
DataType::Int32 => Ok(Value::int(downcast!(Int32Array) as i64, span)),
|
||||
DataType::Int64 => Ok(Value::int(downcast!(Int64Array), span)),
|
||||
DataType::Float32 => Ok(Value::float(downcast!(Float32Array) as f64, span)),
|
||||
DataType::Float64 => Ok(Value::float(downcast!(Float64Array), span)),
|
||||
// DataType::Decimal(_, _) => {}
|
||||
DataType::Utf8 => Ok(Value::string(downcast!(LargeStringArray).to_string(), span)),
|
||||
DataType::Binary => Ok(Value::binary(downcast!(LargeBinaryArray).to_owned(), span)),
|
||||
DataType::Date => {
|
||||
let date = downcast!(Int32Array);
|
||||
let nanos = nanos_per_day(date);
|
||||
datetime_from_epoch_nanos(nanos, &None, span)
|
||||
.map(|datetime| Value::date(datetime, span))
|
||||
}
|
||||
DataType::Datetime(time_unit, tz) => {
|
||||
let nanos = nanos_from_timeunit(downcast!(Int64Array), *time_unit);
|
||||
datetime_from_epoch_nanos(nanos, tz, span)
|
||||
.map(|datetime| Value::date(datetime, span))
|
||||
}
|
||||
// DataType::Duration(_) => {}
|
||||
DataType::Time => {
|
||||
let t = downcast!(Int64Array);
|
||||
time_from_midnight(t, span)
|
||||
}
|
||||
DataType::List(dt) => {
|
||||
let v: ArrayRef = downcast!(LargeListArray);
|
||||
let values_result = if dt.is_primitive() {
|
||||
let s = Series::from_chunks_and_dtype_unchecked("", vec![v], dt);
|
||||
series_to_values(&s, None, None, span)
|
||||
} else {
|
||||
let s = Series::from_chunks_and_dtype_unchecked("", vec![v], &dt.to_physical())
|
||||
.cast_unchecked(dt)
|
||||
.map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"Error creating Value from polars LargeListArray".into(),
|
||||
e.to_string(),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
)
|
||||
})?;
|
||||
series_to_values(&s, None, None, span)
|
||||
};
|
||||
values_result.map(|values| Value::list(values, span))
|
||||
}
|
||||
DataType::Null => Ok(Value::nothing(span)),
|
||||
DataType::Struct(fields) => {
|
||||
let arr = &*(arr as *const dyn Array as *const StructArray);
|
||||
let vals: Result<Vec<Value>, ShellError> = arr
|
||||
.values()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(pos, v)| {
|
||||
let f = &fields[pos];
|
||||
arr_to_value(&f.dtype, &**v, 0, span)
|
||||
})
|
||||
.collect();
|
||||
let cols = fields.iter().map(|f| f.name().to_string()).collect();
|
||||
Ok(Value::record(Record { cols, vals: vals? }, span))
|
||||
}
|
||||
DataType::Unknown => Ok(Value::nothing(span)),
|
||||
_ => Err(ShellError::CantConvert {
|
||||
to_type: dt.to_string(),
|
||||
from_type: "polars array".to_string(),
|
||||
span,
|
||||
help: Some(format!(
|
||||
"Could not convert polars array of type {:?} to value",
|
||||
dt
|
||||
)),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn nanos_per_day(days: i32) -> i64 {
|
||||
days as i64 * NANOS_PER_DAY
|
||||
}
|
||||
|
||||
fn nanos_from_timeunit(a: i64, time_unit: TimeUnit) -> i64 {
|
||||
a * match time_unit {
|
||||
TimeUnit::Microseconds => 1_000, // Convert microseconds to nanoseconds
|
||||
TimeUnit::Milliseconds => 1_000_000, // Convert milliseconds to nanoseconds
|
||||
TimeUnit::Nanoseconds => 1, // Already in nanoseconds
|
||||
}
|
||||
}
|
||||
|
||||
fn datetime_from_epoch_nanos(
|
||||
nanos: i64,
|
||||
timezone: &Option<String>,
|
||||
span: Span,
|
||||
) -> Result<DateTime<FixedOffset>, ShellError> {
|
||||
let tz: Tz = if let Some(polars_tz) = timezone {
|
||||
polars_tz.parse::<Tz>().map_err(|_| {
|
||||
ShellError::GenericError(
|
||||
format!("Could not parse polars timezone: {polars_tz}"),
|
||||
"".to_string(),
|
||||
Some(span),
|
||||
None,
|
||||
vec![],
|
||||
)
|
||||
})?
|
||||
} else {
|
||||
Tz::UTC
|
||||
};
|
||||
|
||||
Ok(tz.timestamp_nanos(nanos).fixed_offset())
|
||||
}
|
||||
|
||||
fn time_from_midnight(nanos: i64, span: Span) -> Result<Value, ShellError> {
|
||||
let today = Utc::now().date_naive();
|
||||
NaiveTime::from_hms_opt(0, 0, 0) // midnight
|
||||
.map(|time| time + Duration::nanoseconds(nanos)) // current time
|
||||
.map(|time| today.and_time(time)) // current date and time
|
||||
.and_then(|datetime| {
|
||||
FixedOffset::east_opt(0) // utc
|
||||
.map(|offset| {
|
||||
DateTime::<FixedOffset>::from_naive_utc_and_offset(datetime, offset)
|
||||
})
|
||||
})
|
||||
.map(|datetime| Value::date(datetime, span)) // current date and time
|
||||
.ok_or(ShellError::CantConvert {
|
||||
to_type: "datetime".to_string(),
|
||||
from_type: "polars time".to_string(),
|
||||
span,
|
||||
help: Some("Could not convert polars time of {nanos} to datetime".to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use indexmap::indexmap;
|
||||
use polars::export::arrow::array::{ListArray, NullArray, PrimitiveArray};
|
||||
use polars::export::arrow::buffer::Buffer;
|
||||
use polars::prelude::Field;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parsed_column_string_list() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@ -1034,4 +1235,375 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_any_value_to_value() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let span = Span::test_data();
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::Null, span)?,
|
||||
Value::nothing(span)
|
||||
);
|
||||
|
||||
let test_bool = true;
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::Boolean(test_bool), span)?,
|
||||
Value::bool(test_bool, span)
|
||||
);
|
||||
|
||||
let test_str = "foo";
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::Utf8(test_str), span)?,
|
||||
Value::string(test_str.to_string(), span)
|
||||
);
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::Utf8Owned(test_str.into()), span)?,
|
||||
Value::string(test_str.to_owned(), span)
|
||||
);
|
||||
|
||||
let tests_uint8 = 4;
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::UInt8(tests_uint8), span)?,
|
||||
Value::int(tests_uint8 as i64, span)
|
||||
);
|
||||
|
||||
let tests_uint16 = 233;
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::UInt16(tests_uint16), span)?,
|
||||
Value::int(tests_uint16 as i64, span)
|
||||
);
|
||||
|
||||
let tests_uint32 = 897688233;
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::UInt32(tests_uint32), span)?,
|
||||
Value::int(tests_uint32 as i64, span)
|
||||
);
|
||||
|
||||
let tests_uint64 = 903225135897388233;
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::UInt64(tests_uint64), span)?,
|
||||
Value::int(tests_uint64 as i64, span)
|
||||
);
|
||||
|
||||
let tests_float32 = 903225135897388233.3223353;
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::Float32(tests_float32), span)?,
|
||||
Value::float(tests_float32 as f64, span)
|
||||
);
|
||||
|
||||
let tests_float64 = 9064251358973882322333.64233533232;
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::Float64(tests_float64), span)?,
|
||||
Value::float(tests_float64, span)
|
||||
);
|
||||
|
||||
let test_days = 10_957;
|
||||
let comparison_date = Utc
|
||||
.with_ymd_and_hms(2000, 1, 1, 0, 0, 0)
|
||||
.unwrap()
|
||||
.fixed_offset();
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::Date(test_days), span)?,
|
||||
Value::date(comparison_date, span)
|
||||
);
|
||||
|
||||
let test_millis = 946_684_800_000;
|
||||
assert_eq!(
|
||||
any_value_to_value(
|
||||
&AnyValue::Datetime(test_millis, TimeUnit::Milliseconds, &None),
|
||||
span
|
||||
)?,
|
||||
Value::date(comparison_date, span)
|
||||
);
|
||||
|
||||
let test_duration_millis = 99_999;
|
||||
let test_duration_micros = 99_999_000;
|
||||
let test_duration_nanos = 99_999_000_000;
|
||||
assert_eq!(
|
||||
any_value_to_value(
|
||||
&AnyValue::Duration(test_duration_nanos, TimeUnit::Nanoseconds),
|
||||
span
|
||||
)?,
|
||||
Value::duration(test_duration_nanos, span)
|
||||
);
|
||||
assert_eq!(
|
||||
any_value_to_value(
|
||||
&AnyValue::Duration(test_duration_micros, TimeUnit::Microseconds),
|
||||
span
|
||||
)?,
|
||||
Value::duration(test_duration_nanos, span)
|
||||
);
|
||||
assert_eq!(
|
||||
any_value_to_value(
|
||||
&AnyValue::Duration(test_duration_millis, TimeUnit::Milliseconds),
|
||||
span
|
||||
)?,
|
||||
Value::duration(test_duration_nanos, span)
|
||||
);
|
||||
|
||||
let test_binary = b"sdf2332f32q3f3afwaf3232f32";
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::Binary(test_binary), span)?,
|
||||
Value::binary(test_binary.to_vec(), span)
|
||||
);
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::BinaryOwned(test_binary.to_vec()), span)?,
|
||||
Value::binary(test_binary.to_vec(), span)
|
||||
);
|
||||
|
||||
let test_time_nanos = 54_000_000_000_000;
|
||||
let test_time = DateTime::<FixedOffset>::from_naive_utc_and_offset(
|
||||
Utc::now()
|
||||
.date_naive()
|
||||
.and_time(NaiveTime::from_hms_opt(15, 00, 00).unwrap()),
|
||||
FixedOffset::east_opt(0).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::Time(test_time_nanos), span)?,
|
||||
Value::date(test_time, span)
|
||||
);
|
||||
|
||||
let test_list_series = Series::new("int series", &[1, 2, 3]);
|
||||
let comparison_list_series = Value::list(
|
||||
vec![
|
||||
Value::int(1, span),
|
||||
Value::int(2, span),
|
||||
Value::int(3, span),
|
||||
],
|
||||
span,
|
||||
);
|
||||
assert_eq!(
|
||||
any_value_to_value(&AnyValue::List(test_list_series), span)?,
|
||||
comparison_list_series
|
||||
);
|
||||
|
||||
let field_value_0 = AnyValue::Int32(1);
|
||||
let field_value_1 = AnyValue::Boolean(true);
|
||||
let values = vec![field_value_0, field_value_1];
|
||||
let field_name_0 = "num_field";
|
||||
let field_name_1 = "bool_field";
|
||||
let fields = vec![
|
||||
Field::new(field_name_0, DataType::Int32),
|
||||
Field::new(field_name_1, DataType::Boolean),
|
||||
];
|
||||
let test_owned_struct = AnyValue::StructOwned(Box::new((values, fields.clone())));
|
||||
let comparison_owned_record = Value::record(
|
||||
Record {
|
||||
cols: vec![field_name_0.to_owned(), field_name_1.to_owned()],
|
||||
vals: vec![Value::int(1, span), Value::bool(true, span)],
|
||||
},
|
||||
span,
|
||||
);
|
||||
assert_eq!(
|
||||
any_value_to_value(&test_owned_struct, span)?,
|
||||
comparison_owned_record.clone()
|
||||
);
|
||||
|
||||
let test_int_arr = PrimitiveArray::from([Some(1_i32)]);
|
||||
let test_bool_arr = BooleanArray::from([Some(true)]);
|
||||
let test_struct_arr = StructArray::new(
|
||||
DataType::Struct(fields.clone()).to_arrow(),
|
||||
vec![Box::new(test_int_arr), Box::new(test_bool_arr)],
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
any_value_to_value(
|
||||
&AnyValue::Struct(0, &test_struct_arr, fields.as_slice()),
|
||||
span
|
||||
)?,
|
||||
comparison_owned_record
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arr_to_value() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let test_bool_arr = BooleanArray::from([Some(true)]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::Boolean, &test_bool_arr, 0, Span::test_data())?,
|
||||
Value::bool(true, Span::test_data())
|
||||
);
|
||||
|
||||
let test_uint8_arr = PrimitiveArray::from([Some(9_u8)]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::UInt8, &test_uint8_arr, 0, Span::test_data())?,
|
||||
Value::int(9, Span::test_data())
|
||||
);
|
||||
|
||||
let test_uint16_arr = PrimitiveArray::from([Some(3223_u16)]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::UInt16, &test_uint16_arr, 0, Span::test_data())?,
|
||||
Value::int(3223, Span::test_data())
|
||||
);
|
||||
|
||||
let test_uint32_arr = PrimitiveArray::from([Some(33_u32)]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::UInt32, &test_uint32_arr, 0, Span::test_data())?,
|
||||
Value::int(33, Span::test_data())
|
||||
);
|
||||
|
||||
let test_uint64_arr = PrimitiveArray::from([Some(33_3232_u64)]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::UInt64, &test_uint64_arr, 0, Span::test_data())?,
|
||||
Value::int(33_3232, Span::test_data())
|
||||
);
|
||||
|
||||
let test_int8_arr = PrimitiveArray::from([Some(9_i8)]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::Int8, &test_int8_arr, 0, Span::test_data())?,
|
||||
Value::int(9, Span::test_data())
|
||||
);
|
||||
|
||||
let test_int16_arr = PrimitiveArray::from([Some(3223_i16)]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::Int16, &test_int16_arr, 0, Span::test_data())?,
|
||||
Value::int(3223, Span::test_data())
|
||||
);
|
||||
|
||||
let test_int32_arr = PrimitiveArray::from([Some(33_i32)]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::Int32, &test_int32_arr, 0, Span::test_data())?,
|
||||
Value::int(33, Span::test_data())
|
||||
);
|
||||
|
||||
let test_int64_arr = PrimitiveArray::from([Some(33_3232_i64)]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::Int64, &test_int64_arr, 0, Span::test_data())?,
|
||||
Value::int(33_3232, Span::test_data())
|
||||
);
|
||||
|
||||
let test_float32_arr = PrimitiveArray::from([Some(33.32_f32)]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::Float32, &test_float32_arr, 0, Span::test_data())?,
|
||||
Value::float(33.32_f32 as f64, Span::test_data())
|
||||
);
|
||||
|
||||
let test_float64_arr = PrimitiveArray::from([Some(33_3232.999_f64)]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::Float64, &test_float64_arr, 0, Span::test_data())?,
|
||||
Value::float(33_3232.999, Span::test_data())
|
||||
);
|
||||
|
||||
let test_str = "hello world";
|
||||
let test_str_arr = LargeStringArray::from(vec![Some(test_str.to_string())]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::Utf8, &test_str_arr, 0, Span::test_data())?,
|
||||
Value::string(test_str.to_string(), Span::test_data())
|
||||
);
|
||||
|
||||
let test_bin = b"asdlfkjadsf";
|
||||
let test_bin_arr = LargeBinaryArray::from(vec![Some(test_bin.to_vec())]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::Binary, &test_bin_arr, 0, Span::test_data())?,
|
||||
Value::binary(test_bin.to_vec(), Span::test_data())
|
||||
);
|
||||
|
||||
let test_days = 10_957_i32;
|
||||
let comparison_date = Utc
|
||||
.with_ymd_and_hms(2000, 1, 1, 0, 0, 0)
|
||||
.unwrap()
|
||||
.fixed_offset();
|
||||
let test_date_arr = PrimitiveArray::from([Some(test_days)]);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::Date, &test_date_arr, 0, Span::test_data())?,
|
||||
Value::date(comparison_date, Span::test_data())
|
||||
);
|
||||
|
||||
let test_dt_nanos = 1_357_488_900_000_000_000_i64;
|
||||
let test_dt_arr = PrimitiveArray::from([Some(test_dt_nanos)]);
|
||||
let test_dt = Utc.timestamp_nanos(test_dt_nanos).fixed_offset();
|
||||
assert_eq!(
|
||||
arr_to_value(
|
||||
&DataType::Datetime(TimeUnit::Nanoseconds, Some("UTC".to_owned())),
|
||||
&test_dt_arr,
|
||||
0,
|
||||
Span::test_data()
|
||||
)?,
|
||||
Value::date(test_dt, Span::test_data())
|
||||
);
|
||||
|
||||
let test_time_nanos = 54_000_000_000_000_i64;
|
||||
let test_dt_arr = PrimitiveArray::from([Some(test_time_nanos)]);
|
||||
let test_time = DateTime::<FixedOffset>::from_naive_utc_and_offset(
|
||||
Utc::now()
|
||||
.date_naive()
|
||||
.and_time(NaiveTime::from_hms_opt(15, 00, 00).unwrap()),
|
||||
FixedOffset::east_opt(0).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
arr_to_value(&DataType::Time, &test_dt_arr, 0, Span::test_data())?,
|
||||
Value::date(test_time, Span::test_data())
|
||||
);
|
||||
|
||||
let values = Buffer::from(vec![1, 2, 3]);
|
||||
let values = PrimitiveArray::<i64>::new(DataType::Int64.to_arrow(), values, None);
|
||||
let data_type = ListArray::<i64>::default_datatype(DataType::Int64.to_arrow());
|
||||
let array = ListArray::<i64>::new(
|
||||
data_type,
|
||||
vec![0, 3].try_into().unwrap(),
|
||||
Box::new(values),
|
||||
None,
|
||||
);
|
||||
let comparison_list_series = Value::list(
|
||||
vec![
|
||||
Value::int(1, Span::test_data()),
|
||||
Value::int(2, Span::test_data()),
|
||||
Value::int(3, Span::test_data()),
|
||||
],
|
||||
Span::test_data(),
|
||||
);
|
||||
assert_eq!(
|
||||
arr_to_value(
|
||||
&DataType::List(Box::new(DataType::Int64)),
|
||||
&array,
|
||||
0,
|
||||
Span::test_data()
|
||||
)?,
|
||||
comparison_list_series
|
||||
);
|
||||
|
||||
let field_name_0 = "num_field";
|
||||
let field_name_1 = "bool_field";
|
||||
let fields = vec![
|
||||
Field::new(field_name_0, DataType::Int32),
|
||||
Field::new(field_name_1, DataType::Boolean),
|
||||
];
|
||||
let test_int_arr = PrimitiveArray::from([Some(1_i32)]);
|
||||
let test_struct_arr = StructArray::new(
|
||||
DataType::Struct(fields.clone()).to_arrow(),
|
||||
vec![Box::new(test_int_arr), Box::new(test_bool_arr)],
|
||||
None,
|
||||
);
|
||||
let comparison_owned_record = Value::record(
|
||||
Record {
|
||||
cols: vec![field_name_0.to_owned(), field_name_1.to_owned()],
|
||||
vals: vec![
|
||||
Value::int(1, Span::test_data()),
|
||||
Value::bool(true, Span::test_data()),
|
||||
],
|
||||
},
|
||||
Span::test_data(),
|
||||
);
|
||||
assert_eq!(
|
||||
arr_to_value(
|
||||
&DataType::Struct(fields),
|
||||
&test_struct_arr,
|
||||
0,
|
||||
Span::test_data(),
|
||||
)?,
|
||||
comparison_owned_record
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
arr_to_value(
|
||||
&DataType::Null,
|
||||
&NullArray::new(DataType::Null.to_arrow(), 0),
|
||||
0,
|
||||
Span::test_data()
|
||||
)?,
|
||||
Value::nothing(Span::test_data())
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -427,11 +427,11 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Result<Value, ShellError> {
|
||||
}
|
||||
// the parameter polars_plan::dsl::selector::Selector is not publicly exposed.
|
||||
// I am not sure what we can meaningfully do with this at this time.
|
||||
Expr::Selector(_) => Err(ShellError::UnsupportedInput(
|
||||
"Expressions of type Selector to Nu Values is not yet supported".to_string(),
|
||||
format!("Expression is {expr:?}"),
|
||||
span,
|
||||
Span::unknown(),
|
||||
)),
|
||||
Expr::Selector(_) => Err(ShellError::UnsupportedInput {
|
||||
msg: "Expressions of type Selector to Nu Values is not yet supported".to_string(),
|
||||
input: format!("Expression is {expr:?}"),
|
||||
msg_span: span,
|
||||
input_span: Span::unknown(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cmd-extra"
|
||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -13,11 +13,11 @@ version = "0.86.0"
|
||||
bench = false
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.86.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.86.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.86.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.87.1" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.87.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.87.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.87.1" }
|
||||
|
||||
# Potential dependencies for extras
|
||||
heck = "0.4.1"
|
||||
@ -27,8 +27,8 @@ nu-ansi-term = "0.49.0"
|
||||
fancy-regex = "0.11.0"
|
||||
rust-embed = "8.0.0"
|
||||
serde = "1.0.164"
|
||||
nu-pretty-hex = { version = "0.86.0", path = "../nu-pretty-hex" }
|
||||
nu-json = { version = "0.86.0", path = "../nu-json" }
|
||||
nu-pretty-hex = { version = "0.87.1", path = "../nu-pretty-hex" }
|
||||
nu-json = { version = "0.87.1", path = "../nu-json" }
|
||||
serde_urlencoded = "0.7.1"
|
||||
htmlescape = "0.3.1"
|
||||
|
||||
@ -37,6 +37,6 @@ extra = ["default"]
|
||||
default = []
|
||||
|
||||
[dev-dependencies]
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.86.0" }
|
||||
nu-command = { path = "../nu-command", version = "0.86.0" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.87.1" }
|
||||
nu-command = { path = "../nu-command", version = "0.87.1" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.87.1" }
|
||||
|
@ -60,12 +60,12 @@ impl Command for BitsNot {
|
||||
let bytes_len = get_number_bytes(number_bytes.as_ref());
|
||||
if let NumberBytes::Invalid = bytes_len {
|
||||
if let Some(val) = number_bytes {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
||||
"value originates from here".to_string(),
|
||||
head,
|
||||
val.span,
|
||||
));
|
||||
return Err(ShellError::UnsupportedInput {
|
||||
msg: "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
||||
input: "value originates from here".to_string(),
|
||||
msg_span: head,
|
||||
input_span: val.span,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,12 +63,12 @@ impl Command for BitsRol {
|
||||
let bytes_len = get_number_bytes(number_bytes.as_ref());
|
||||
if let NumberBytes::Invalid = bytes_len {
|
||||
if let Some(val) = number_bytes {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
||||
"value originates from here".to_string(),
|
||||
head,
|
||||
val.span,
|
||||
));
|
||||
return Err(ShellError::UnsupportedInput {
|
||||
msg: "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
||||
input: "value originates from here".to_string(),
|
||||
msg_span: head,
|
||||
input_span: val.span,
|
||||
});
|
||||
}
|
||||
}
|
||||
// This doesn't match explicit nulls
|
||||
|
@ -63,12 +63,12 @@ impl Command for BitsRor {
|
||||
let bytes_len = get_number_bytes(number_bytes.as_ref());
|
||||
if let NumberBytes::Invalid = bytes_len {
|
||||
if let Some(val) = number_bytes {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
||||
"value originates from here".to_string(),
|
||||
head,
|
||||
val.span,
|
||||
));
|
||||
return Err(ShellError::UnsupportedInput {
|
||||
msg: "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
||||
input: "value originates from here".to_string(),
|
||||
msg_span: head,
|
||||
input_span: val.span,
|
||||
});
|
||||
}
|
||||
}
|
||||
// This doesn't match explicit nulls
|
||||
|
@ -63,12 +63,12 @@ impl Command for BitsShl {
|
||||
let bytes_len = get_number_bytes(number_bytes.as_ref());
|
||||
if let NumberBytes::Invalid = bytes_len {
|
||||
if let Some(val) = number_bytes {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
||||
"value originates from here".to_string(),
|
||||
head,
|
||||
val.span,
|
||||
));
|
||||
return Err(ShellError::UnsupportedInput {
|
||||
msg: "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
||||
input: "value originates from here".to_string(),
|
||||
msg_span: head,
|
||||
input_span: val.span,
|
||||
});
|
||||
}
|
||||
}
|
||||
// This doesn't match explicit nulls
|
||||
|
@ -63,12 +63,12 @@ impl Command for BitsShr {
|
||||
let bytes_len = get_number_bytes(number_bytes.as_ref());
|
||||
if let NumberBytes::Invalid = bytes_len {
|
||||
if let Some(val) = number_bytes {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
||||
"value originates from here".to_string(),
|
||||
head,
|
||||
val.span,
|
||||
));
|
||||
return Err(ShellError::UnsupportedInput {
|
||||
msg: "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
||||
input: "value originates from here".to_string(),
|
||||
msg_span: head,
|
||||
input_span: val.span,
|
||||
});
|
||||
}
|
||||
}
|
||||
// This doesn't match explicit nulls
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
record, Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -32,27 +32,15 @@ impl Command for Fmt {
|
||||
vec![Example {
|
||||
description: "Get a record containing multiple formats for the number 42",
|
||||
example: "42 | fmt",
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"binary".into(),
|
||||
"debug".into(),
|
||||
"display".into(),
|
||||
"lowerexp".into(),
|
||||
"lowerhex".into(),
|
||||
"octal".into(),
|
||||
"upperexp".into(),
|
||||
"upperhex".into(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_string("0b101010"),
|
||||
Value::test_string("42"),
|
||||
Value::test_string("42"),
|
||||
Value::test_string("4.2e1"),
|
||||
Value::test_string("0x2a"),
|
||||
Value::test_string("0o52"),
|
||||
Value::test_string("4.2E1"),
|
||||
Value::test_string("0x2A"),
|
||||
],
|
||||
result: Some(Value::test_record(record! {
|
||||
"binary" => Value::test_string("0b101010"),
|
||||
"debug" => Value::test_string("42"),
|
||||
"display" => Value::test_string("42"),
|
||||
"lowerexp" => Value::test_string("4.2e1"),
|
||||
"lowerhex" => Value::test_string("0x2a"),
|
||||
"octal" => Value::test_string("0o52"),
|
||||
"upperexp" => Value::test_string("4.2E1"),
|
||||
"upperhex" => Value::test_string("0x2A"),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ impl Command for EachWhile {
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let engine_state = engine_state.clone();
|
||||
let block = engine_state.get_block(capture_block.block_id).clone();
|
||||
let mut stack = stack.captures_to_stack(&capture_block.captures);
|
||||
let mut stack = stack.captures_to_stack(capture_block.captures);
|
||||
let orig_env_vars = stack.env_vars.clone();
|
||||
let orig_env_hidden = stack.env_hidden.clone();
|
||||
let span = call.head;
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
use super::{vertical_rotate_value, VerticalDirection};
|
||||
@ -33,27 +33,23 @@ impl Command for RollDown {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
let columns = vec!["a".to_string(), "b".to_string()];
|
||||
vec![Example {
|
||||
description: "Rolls rows down of a table",
|
||||
example: "[[a b]; [1 2] [3 4] [5 6]] | roll down",
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![Value::test_int(5), Value::test_int(6)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![Value::test_int(1), Value::test_int(2)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns,
|
||||
vals: vec![Value::test_int(3), Value::test_int(4)],
|
||||
}),
|
||||
],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"a" => Value::test_int(5),
|
||||
"b" => Value::test_int(6),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"a" => Value::test_int(1),
|
||||
"b" => Value::test_int(2),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"a" => Value::test_int(3),
|
||||
"b" => Value::test_int(4),
|
||||
}),
|
||||
])),
|
||||
}]
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
use super::{horizontal_rotate_value, HorizontalDirection};
|
||||
@ -45,50 +45,47 @@ impl Command for RollLeft {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
let columns = vec!["a".to_string(), "b".to_string(), "c".to_string()];
|
||||
let rotated_columns = vec!["b".to_string(), "c".to_string(), "a".to_string()];
|
||||
vec![
|
||||
Example {
|
||||
description: "Rolls columns of a record to the left",
|
||||
example: "{a:1 b:2 c:3} | roll left",
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: rotated_columns.clone(),
|
||||
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(1)],
|
||||
result: Some(Value::test_record(record! {
|
||||
"b" => Value::test_int(2),
|
||||
"c" => Value::test_int(3),
|
||||
"a" => Value::test_int(1),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Rolls columns of a table to the left",
|
||||
example: "[[a b c]; [1 2 3] [4 5 6]] | roll left",
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: rotated_columns.clone(),
|
||||
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(1)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: rotated_columns,
|
||||
vals: vec![Value::test_int(5), Value::test_int(6), Value::test_int(4)],
|
||||
}),
|
||||
],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"b" => Value::test_int(2),
|
||||
"c" => Value::test_int(3),
|
||||
"a" => Value::test_int(1),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"b" => Value::test_int(5),
|
||||
"c" => Value::test_int(6),
|
||||
"a" => Value::test_int(4),
|
||||
}),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
description: "Rolls columns to the left without changing column names",
|
||||
example: "[[a b c]; [1 2 3] [4 5 6]] | roll left --cells-only",
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(1)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns,
|
||||
vals: vec![Value::test_int(5), Value::test_int(6), Value::test_int(4)],
|
||||
}),
|
||||
],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"a" => Value::test_int(2),
|
||||
"b" => Value::test_int(3),
|
||||
"c" => Value::test_int(1),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"a" => Value::test_int(5),
|
||||
"b" => Value::test_int(6),
|
||||
"c" => Value::test_int(4),
|
||||
}),
|
||||
])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
use super::{horizontal_rotate_value, HorizontalDirection};
|
||||
@ -45,50 +45,47 @@ impl Command for RollRight {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
let columns = vec!["a".to_string(), "b".to_string(), "c".to_string()];
|
||||
let rotated_columns = vec!["c".to_string(), "a".to_string(), "b".to_string()];
|
||||
vec![
|
||||
Example {
|
||||
description: "Rolls columns of a record to the right",
|
||||
example: "{a:1 b:2 c:3} | roll right",
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: rotated_columns.clone(),
|
||||
vals: vec![Value::test_int(3), Value::test_int(1), Value::test_int(2)],
|
||||
result: Some(Value::test_record(record! {
|
||||
"c" => Value::test_int(3),
|
||||
"a" => Value::test_int(1),
|
||||
"b" => Value::test_int(2),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Rolls columns to the right",
|
||||
example: "[[a b c]; [1 2 3] [4 5 6]] | roll right",
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: rotated_columns.clone(),
|
||||
vals: vec![Value::test_int(3), Value::test_int(1), Value::test_int(2)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: rotated_columns,
|
||||
vals: vec![Value::test_int(6), Value::test_int(4), Value::test_int(5)],
|
||||
}),
|
||||
],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"c" => Value::test_int(3),
|
||||
"a" => Value::test_int(1),
|
||||
"b" => Value::test_int(2),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"c" => Value::test_int(6),
|
||||
"a" => Value::test_int(4),
|
||||
"b" => Value::test_int(5),
|
||||
}),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
description: "Rolls columns to the right with fixed headers",
|
||||
example: "[[a b c]; [1 2 3] [4 5 6]] | roll right --cells-only",
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![Value::test_int(3), Value::test_int(1), Value::test_int(2)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns,
|
||||
vals: vec![Value::test_int(6), Value::test_int(4), Value::test_int(5)],
|
||||
}),
|
||||
],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"a" => Value::test_int(3),
|
||||
"b" => Value::test_int(1),
|
||||
"c" => Value::test_int(2),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"a" => Value::test_int(6),
|
||||
"b" => Value::test_int(4),
|
||||
"c" => Value::test_int(5),
|
||||
}),
|
||||
])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
use super::{vertical_rotate_value, VerticalDirection};
|
||||
@ -33,27 +33,23 @@ impl Command for RollUp {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
let columns = vec!["a".to_string(), "b".to_string()];
|
||||
vec![Example {
|
||||
description: "Rolls rows up",
|
||||
example: "[[a b]; [1 2] [3 4] [5 6]] | roll up",
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![Value::test_int(3), Value::test_int(4)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns.clone(),
|
||||
vals: vec![Value::test_int(5), Value::test_int(6)],
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: columns,
|
||||
vals: vec![Value::test_int(1), Value::test_int(2)],
|
||||
}),
|
||||
],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"a" => Value::test_int(3),
|
||||
"b" => Value::test_int(4),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"a" => Value::test_int(5),
|
||||
"b" => Value::test_int(6),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"a" => Value::test_int(1),
|
||||
"b" => Value::test_int(2),
|
||||
}),
|
||||
])),
|
||||
}]
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::IntoPipelineData;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, Record, ShellError, Signature, SyntaxShape, Type,
|
||||
Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -38,142 +39,104 @@ impl Command for Rotate {
|
||||
Example {
|
||||
description: "Rotate a record clockwise, producing a table (like `transpose` but with column order reversed)",
|
||||
example: "{a:1, b:2} | rotate",
|
||||
result: Some(Value::list(vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column0".to_string(), "column1".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_string("a")],
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_int(1),
|
||||
"column1" => Value::test_string("a"),
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column0".to_string(), "column1".to_string()],
|
||||
vals: vec![Value::test_int(2), Value::test_string("b")],
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_int(2),
|
||||
"column1" => Value::test_string("b"),
|
||||
}),
|
||||
],
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Rotate 2x3 table clockwise",
|
||||
example: "[[a b]; [1 2] [3 4] [5 6]] | rotate",
|
||||
result: Some(Value::list(
|
||||
result: Some(Value::test_list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"column0".to_string(),
|
||||
"column1".to_string(),
|
||||
"column2".to_string(),
|
||||
"column3".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_int(5),
|
||||
Value::test_int(3),
|
||||
Value::test_int(1),
|
||||
Value::test_string("a"),
|
||||
],
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_int(5),
|
||||
"column1" => Value::test_int(3),
|
||||
"column2" => Value::test_int(1),
|
||||
"column3" => Value::test_string("a"),
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"column0".to_string(),
|
||||
"column1".to_string(),
|
||||
"column2".to_string(),
|
||||
"column3".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_int(6),
|
||||
Value::test_int(4),
|
||||
Value::test_int(2),
|
||||
Value::test_string("b"),
|
||||
],
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_int(6),
|
||||
"column1" => Value::test_int(4),
|
||||
"column2" => Value::test_int(2),
|
||||
"column3" => Value::test_string("b"),
|
||||
}),
|
||||
],
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Rotate table clockwise and change columns names",
|
||||
example: "[[a b]; [1 2]] | rotate col_a col_b",
|
||||
result: Some(Value::list(
|
||||
result: Some(Value::test_list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec!["col_a".to_string(), "col_b".to_string()],
|
||||
vals: vec![Value::test_int(1), Value::test_string("a")],
|
||||
Value::test_record(record! {
|
||||
"col_a" => Value::test_int(1),
|
||||
"col_b" => Value::test_string("a"),
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["col_a".to_string(), "col_b".to_string()],
|
||||
vals: vec![Value::test_int(2), Value::test_string("b")],
|
||||
Value::test_record(record! {
|
||||
"col_a" => Value::test_int(2),
|
||||
"col_b" => Value::test_string("b"),
|
||||
}),
|
||||
],
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Rotate table counter clockwise",
|
||||
example: "[[a b]; [1 2]] | rotate --ccw",
|
||||
result: Some(Value::list(
|
||||
result: Some(Value::test_list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column0".to_string(), "column1".to_string()],
|
||||
vals: vec![Value::test_string("b"), Value::test_int(2)],
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_string("b"),
|
||||
"column1" => Value::test_int(2),
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["column0".to_string(), "column1".to_string()],
|
||||
vals: vec![Value::test_string("a"), Value::test_int(1)],
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_string("a"),
|
||||
"column1" => Value::test_int(1),
|
||||
}),
|
||||
],
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Rotate table counter-clockwise",
|
||||
example: "[[a b]; [1 2] [3 4] [5 6]] | rotate --ccw",
|
||||
result: Some(Value::list(
|
||||
result: Some(Value::test_list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"column0".to_string(),
|
||||
"column1".to_string(),
|
||||
"column2".to_string(),
|
||||
"column3".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_string("b"),
|
||||
Value::test_int(2),
|
||||
Value::test_int(4),
|
||||
Value::test_int(6),
|
||||
],
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_string("b"),
|
||||
"column1" => Value::test_int(2),
|
||||
"column2" => Value::test_int(4),
|
||||
"column3" => Value::test_int(6),
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec![
|
||||
"column0".to_string(),
|
||||
"column1".to_string(),
|
||||
"column2".to_string(),
|
||||
"column3".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_string("a"),
|
||||
Value::test_int(1),
|
||||
Value::test_int(3),
|
||||
Value::test_int(5),
|
||||
],
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_string("a"),
|
||||
"column1" => Value::test_int(1),
|
||||
"column2" => Value::test_int(3),
|
||||
"column3" => Value::test_int(5),
|
||||
}),
|
||||
],
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Rotate table counter-clockwise and change columns names",
|
||||
example: "[[a b]; [1 2]] | rotate --ccw col_a col_b",
|
||||
result: Some(Value::list(
|
||||
result: Some(Value::test_list(
|
||||
vec![
|
||||
Value::test_record(Record {
|
||||
cols: vec!["col_a".to_string(), "col_b".to_string()],
|
||||
vals: vec![Value::test_string("b"), Value::test_int(2)],
|
||||
Value::test_record(record! {
|
||||
"col_a" => Value::test_string("b"),
|
||||
"col_b" => Value::test_int(2),
|
||||
}),
|
||||
Value::test_record(Record {
|
||||
cols: vec!["col_a".to_string(), "col_b".to_string()],
|
||||
vals: vec![Value::test_string("a"), Value::test_int(1)],
|
||||
Value::test_record(record! {
|
||||
"col_a" => Value::test_string("a"),
|
||||
"col_b" => Value::test_int(1),
|
||||
}),
|
||||
],
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
]
|
||||
@ -237,13 +200,12 @@ pub fn rotate(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
"list input is empty".to_string(),
|
||||
"value originates from here".into(),
|
||||
call.head,
|
||||
// TODO: Maybe make all Pipelines have spans, so that this doesn't need to be unwrapped.
|
||||
span.unwrap_or(call.head),
|
||||
));
|
||||
return Err(ShellError::UnsupportedInput {
|
||||
msg: "list input is empty".to_string(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: call.head,
|
||||
input_span: span.unwrap_or(call.head),
|
||||
});
|
||||
}
|
||||
|
||||
let total_columns = &old_column_names.len();
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::ast::{Block, Call};
|
||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
PipelineIterator, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
PipelineIterator, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
use std::iter::FromIterator;
|
||||
@ -51,29 +51,15 @@ impl Command for UpdateCells {
|
||||
$value
|
||||
}
|
||||
}"#,
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
"2021-04-16".into(),
|
||||
"2021-06-10".into(),
|
||||
"2021-09-18".into(),
|
||||
"2021-10-15".into(),
|
||||
"2021-11-16".into(),
|
||||
"2021-11-17".into(),
|
||||
"2021-11-18".into(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_int(37),
|
||||
Value::test_string(""),
|
||||
Value::test_string(""),
|
||||
Value::test_string(""),
|
||||
Value::test_int(37),
|
||||
Value::test_string(""),
|
||||
Value::test_string(""),
|
||||
],
|
||||
})],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![Value::test_record(record! {
|
||||
"2021-04-16" => Value::test_int(37),
|
||||
"2021-06-10" => Value::test_string(""),
|
||||
"2021-09-18" => Value::test_string(""),
|
||||
"2021-10-15" => Value::test_string(""),
|
||||
"2021-11-16" => Value::test_int(37),
|
||||
"2021-11-17" => Value::test_string(""),
|
||||
"2021-11-18" => Value::test_string(""),
|
||||
})])),
|
||||
},
|
||||
Example {
|
||||
description: "Update the zero value cells to empty strings in 2 last columns.",
|
||||
@ -87,29 +73,15 @@ impl Command for UpdateCells {
|
||||
$value
|
||||
}
|
||||
}"#,
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec![
|
||||
"2021-04-16".into(),
|
||||
"2021-06-10".into(),
|
||||
"2021-09-18".into(),
|
||||
"2021-10-15".into(),
|
||||
"2021-11-16".into(),
|
||||
"2021-11-17".into(),
|
||||
"2021-11-18".into(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_int(37),
|
||||
Value::test_int(0),
|
||||
Value::test_int(0),
|
||||
Value::test_int(0),
|
||||
Value::test_int(37),
|
||||
Value::test_string(""),
|
||||
Value::test_string(""),
|
||||
],
|
||||
})],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![Value::test_record(record! {
|
||||
"2021-04-16" => Value::test_int(37),
|
||||
"2021-06-10" => Value::test_int(0),
|
||||
"2021-09-18" => Value::test_int(0),
|
||||
"2021-10-15" => Value::test_int(0),
|
||||
"2021-11-16" => Value::test_int(37),
|
||||
"2021-11-17" => Value::test_string(""),
|
||||
"2021-11-18" => Value::test_string(""),
|
||||
})])),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -124,7 +96,7 @@ impl Command for UpdateCells {
|
||||
// the block to run on each cell
|
||||
let engine_state = engine_state.clone();
|
||||
let block: Closure = call.req(&engine_state, stack, 0)?;
|
||||
let mut stack = stack.captures_to_stack(&block.captures);
|
||||
let mut stack = stack.captures_to_stack(block.captures);
|
||||
let orig_env_vars = stack.env_vars.clone();
|
||||
let orig_env_hidden = stack.env_hidden.clone();
|
||||
|
||||
@ -186,7 +158,7 @@ impl Iterator for UpdateCellIterator {
|
||||
match self.input.next() {
|
||||
Some(val) => {
|
||||
if let Some(ref cols) = self.columns {
|
||||
if !val.columns().iter().any(|c| cols.contains(c)) {
|
||||
if !val.columns().any(|c| cols.contains(c)) {
|
||||
return Some(val);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -37,19 +37,11 @@ impl Command for FromUrl {
|
||||
vec![Example {
|
||||
example: "'bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter' | from url",
|
||||
description: "Convert url encoded string into a record",
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec![
|
||||
"bread".to_string(),
|
||||
"cheese".to_string(),
|
||||
"meat".to_string(),
|
||||
"fat".to_string(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_string("baguette"),
|
||||
Value::test_string("comté"),
|
||||
Value::test_string("ham"),
|
||||
Value::test_string("butter"),
|
||||
],
|
||||
result: Some(Value::test_record(record! {
|
||||
"bread" => Value::test_string("baguette"),
|
||||
"cheese" => Value::test_string("comté"),
|
||||
"meat" => Value::test_string("ham"),
|
||||
"fat" => Value::test_string("butter"),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
@ -69,12 +61,12 @@ fn from_url(input: PipelineData, head: Span) -> Result<PipelineData, ShellError>
|
||||
|
||||
Ok(PipelineData::Value(Value::record(record, head), metadata))
|
||||
}
|
||||
_ => Err(ShellError::UnsupportedInput(
|
||||
"String not compatible with URL encoding".to_string(),
|
||||
"value originates from here".into(),
|
||||
head,
|
||||
span,
|
||||
)),
|
||||
_ => Err(ShellError::UnsupportedInput {
|
||||
msg: "String not compatible with URL encoding".to_string(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: head,
|
||||
input_span: span,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ use nu_protocol::{
|
||||
record, Category, Config, DataSource, Example, IntoPipelineData, PipelineData,
|
||||
PipelineMetadata, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
use rust_embed::RustEmbed;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
@ -180,7 +181,7 @@ fn get_theme_from_asset_file(
|
||||
let th = asset
|
||||
.themes
|
||||
.into_iter()
|
||||
.find(|n| n.name.to_lowercase() == theme_name.to_lowercase()) // case insensitive search
|
||||
.find(|n| n.name.eq_ignore_case(theme_name)) // case insensitive search
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(convert_html_theme_to_hash_map(is_dark, &th))
|
||||
@ -289,11 +290,9 @@ fn to_html(
|
||||
})
|
||||
.collect();
|
||||
return Ok(
|
||||
Value::list(result, head).into_pipeline_data_with_metadata(Box::new(
|
||||
PipelineMetadata {
|
||||
data_source: DataSource::HtmlThemes,
|
||||
},
|
||||
)),
|
||||
Value::list(result, head).into_pipeline_data_with_metadata(PipelineMetadata {
|
||||
data_source: DataSource::HtmlThemes,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
let theme_span = match &theme {
|
||||
@ -404,15 +403,15 @@ fn html_table(table: Vec<Value>, headers: Vec<String>, config: &Config) -> Strin
|
||||
|
||||
for row in table {
|
||||
let span = row.span();
|
||||
if let Value::Record { .. } = row {
|
||||
if let Value::Record { val: row, .. } = row {
|
||||
output_string.push_str("<tr>");
|
||||
for header in &headers {
|
||||
let data = row.get_data_by_key(header);
|
||||
let data = row
|
||||
.get(header)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Value::nothing(span));
|
||||
output_string.push_str("<td>");
|
||||
output_string.push_str(&html_value(
|
||||
data.unwrap_or_else(|| Value::nothing(span)),
|
||||
config,
|
||||
));
|
||||
output_string.push_str(&html_value(data, config));
|
||||
output_string.push_str("</td>");
|
||||
}
|
||||
output_string.push_str("</tr>");
|
||||
|
@ -60,7 +60,7 @@ impl Command for SubCommand {
|
||||
},
|
||||
Example {
|
||||
description: "Get the arccosine of -1 in degrees",
|
||||
example: "-1 | math arccos -d",
|
||||
example: "-1 | math arccos --degrees",
|
||||
result: Some(Value::test_float(180.0)),
|
||||
},
|
||||
]
|
||||
@ -84,12 +84,13 @@ fn operate(value: Value, head: Span, use_degrees: bool) -> Value {
|
||||
Value::float(val, span)
|
||||
} else {
|
||||
Value::error(
|
||||
ShellError::UnsupportedInput(
|
||||
"'arccos' undefined for values outside the closed interval [-1, 1].".into(),
|
||||
"value originates from here".into(),
|
||||
head,
|
||||
span,
|
||||
),
|
||||
ShellError::UnsupportedInput {
|
||||
msg: "'arccos' undefined for values outside the closed interval [-1, 1]."
|
||||
.into(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: head,
|
||||
input_span: span,
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
@ -74,12 +74,12 @@ fn operate(value: Value, head: Span) -> Value {
|
||||
Value::float(val, span)
|
||||
} else {
|
||||
Value::error(
|
||||
ShellError::UnsupportedInput(
|
||||
"'arccosh' undefined for values below 1.".into(),
|
||||
"value originates from here".into(),
|
||||
head,
|
||||
span,
|
||||
),
|
||||
ShellError::UnsupportedInput {
|
||||
msg: "'arccosh' undefined for values below 1.".into(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: head,
|
||||
input_span: span,
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ impl Command for SubCommand {
|
||||
},
|
||||
Example {
|
||||
description: "Get the arcsine of 1 in degrees",
|
||||
example: "1 | math arcsin -d",
|
||||
example: "1 | math arcsin --degrees",
|
||||
result: Some(Value::test_float(90.0)),
|
||||
},
|
||||
]
|
||||
@ -85,12 +85,13 @@ fn operate(value: Value, head: Span, use_degrees: bool) -> Value {
|
||||
Value::float(val, span)
|
||||
} else {
|
||||
Value::error(
|
||||
ShellError::UnsupportedInput(
|
||||
"'arcsin' undefined for values outside the closed interval [-1, 1].".into(),
|
||||
"value originates from here".into(),
|
||||
head,
|
||||
span,
|
||||
),
|
||||
ShellError::UnsupportedInput {
|
||||
msg: "'arcsin' undefined for values outside the closed interval [-1, 1]."
|
||||
.into(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: head,
|
||||
input_span: span,
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ impl Command for SubCommand {
|
||||
},
|
||||
Example {
|
||||
description: "Get the arctangent of -1 in degrees",
|
||||
example: "-1 | math arctan -d",
|
||||
example: "-1 | math arctan --degrees",
|
||||
result: Some(Value::test_float(-45.0)),
|
||||
},
|
||||
]
|
||||
|
@ -74,12 +74,13 @@ fn operate(value: Value, head: Span) -> Value {
|
||||
Value::float(val, span)
|
||||
} else {
|
||||
Value::error(
|
||||
ShellError::UnsupportedInput(
|
||||
"'arctanh' undefined for values outside the open interval (-1, 1).".into(),
|
||||
"value originates from here".into(),
|
||||
head,
|
||||
span,
|
||||
),
|
||||
ShellError::UnsupportedInput {
|
||||
msg: "'arctanh' undefined for values outside the open interval (-1, 1)."
|
||||
.into(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: head,
|
||||
input_span: span,
|
||||
},
|
||||
head,
|
||||
)
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ impl Command for SubCommand {
|
||||
},
|
||||
Example {
|
||||
description: "Apply the cosine to a list of angles in degrees",
|
||||
example: "[0 90 180 270 360] | math cos -d",
|
||||
example: "[0 90 180 270 360] | math cos --degrees",
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::test_float(1f64),
|
||||
|
@ -74,12 +74,12 @@ fn operate(value: Value, head: Span) -> Value {
|
||||
Value::float(val, span)
|
||||
} else {
|
||||
Value::error(
|
||||
ShellError::UnsupportedInput(
|
||||
"'ln' undefined for values outside the open interval (0, Inf).".into(),
|
||||
"value originates from here".into(),
|
||||
head,
|
||||
span,
|
||||
),
|
||||
ShellError::UnsupportedInput {
|
||||
msg: "'ln' undefined for values outside the open interval (0, Inf).".into(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: head,
|
||||
input_span: span,
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ impl Command for SubCommand {
|
||||
},
|
||||
Example {
|
||||
description: "Apply the tangent to a list of angles in degrees",
|
||||
example: "[-45 0 45] | math tan -d",
|
||||
example: "[-45 0 45] | math tan --degrees",
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::test_float(-1f64),
|
||||
|
@ -62,7 +62,7 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
|
||||
filters::Rotate
|
||||
);
|
||||
|
||||
bind_command!(platform::ansi::Gradient, platform::ansi::Link);
|
||||
bind_command!(platform::ansi::Gradient);
|
||||
|
||||
bind_command!(
|
||||
strings::format::Format,
|
||||
|
@ -1,5 +1,3 @@
|
||||
mod gradient;
|
||||
mod link;
|
||||
|
||||
pub(crate) use gradient::SubCommand as Gradient;
|
||||
pub(crate) use link::SubCommand as Link;
|
||||
|
@ -108,26 +108,14 @@ fn action(
|
||||
Value::Binary { val, .. } => match hex_config.action_type {
|
||||
ActionType::Encode => Value::string(hex_encode(val.as_ref()), command_span),
|
||||
ActionType::Decode => Value::error(
|
||||
ShellError::UnsupportedInput(
|
||||
"Binary data can only be encoded".to_string(),
|
||||
"value originates from here".into(),
|
||||
command_span,
|
||||
// This line requires the Value::Error {} match above.
|
||||
input.span(),
|
||||
),
|
||||
ShellError::UnsupportedInput { msg: "Binary data can only be encoded".to_string(), input: "value originates from here".into(), msg_span: command_span, input_span: input.span() },
|
||||
command_span,
|
||||
),
|
||||
},
|
||||
Value::String { val, .. } => {
|
||||
match hex_config.action_type {
|
||||
ActionType::Encode => Value::error(
|
||||
ShellError::UnsupportedInput(
|
||||
"String value can only be decoded".to_string(),
|
||||
"value originates from here".into(),
|
||||
command_span,
|
||||
// This line requires the Value::Error {} match above.
|
||||
input.span(),
|
||||
),
|
||||
ShellError::UnsupportedInput { msg: "String value can only be decoded".to_string(), input: "value originates from here".into(), msg_span: command_span, input_span: input.span() },
|
||||
command_span,
|
||||
),
|
||||
|
||||
|
@ -2,7 +2,7 @@ use heck::ToLowerCamelCase;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use super::operate;
|
||||
@ -79,13 +79,10 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "convert a column from a table to camelCase",
|
||||
example: r#"[[lang, gems]; [nu_test, 100]] | str camel-case lang"#,
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec!["lang".to_string(), "gems".to_string()],
|
||||
vals: vec![Value::test_string("nuTest"), Value::test_int(100)],
|
||||
})],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![Value::test_record(record! {
|
||||
"lang" => Value::test_string("nuTest"),
|
||||
"gems" => Value::test_int(100),
|
||||
})])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use heck::ToKebabCase;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use super::operate;
|
||||
@ -79,13 +79,10 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "convert a column from a table to kebab-case",
|
||||
example: r#"[[lang, gems]; [nuTest, 100]] | str kebab-case lang"#,
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec!["lang".to_string(), "gems".to_string()],
|
||||
vals: vec![Value::test_string("nu-test"), Value::test_int(100)],
|
||||
})],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![Value::test_record(record! {
|
||||
"lang" => Value::test_string("nu-test"),
|
||||
"gems" => Value::test_int(100),
|
||||
})])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use heck::ToUpperCamelCase;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use super::operate;
|
||||
@ -79,13 +79,10 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "convert a column from a table to PascalCase",
|
||||
example: r#"[[lang, gems]; [nu_test, 100]] | str pascal-case lang"#,
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec!["lang".to_string(), "gems".to_string()],
|
||||
vals: vec![Value::test_string("NuTest"), Value::test_int(100)],
|
||||
})],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![Value::test_record(record! {
|
||||
"lang" => Value::test_string("NuTest"),
|
||||
"gems" => Value::test_int(100),
|
||||
})])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use heck::ToShoutySnakeCase;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use super::operate;
|
||||
@ -79,13 +79,10 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "convert a column from a table to SCREAMING_SNAKE_CASE",
|
||||
example: r#"[[lang, gems]; [nu_test, 100]] | str screaming-snake-case lang"#,
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec!["lang".to_string(), "gems".to_string()],
|
||||
vals: vec![Value::test_string("NU_TEST"), Value::test_int(100)],
|
||||
})],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![Value::test_record(record! {
|
||||
"lang" => Value::test_string("NU_TEST"),
|
||||
"gems" => Value::test_int(100),
|
||||
})])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use heck::ToSnakeCase;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use super::operate;
|
||||
@ -78,13 +78,10 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "convert a column from a table to snake_case",
|
||||
example: r#"[[lang, gems]; [nuTest, 100]] | str snake-case lang"#,
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec!["lang".to_string(), "gems".to_string()],
|
||||
vals: vec![Value::test_string("nu_test"), Value::test_int(100)],
|
||||
})],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![Value::test_record(record! {
|
||||
"lang" => Value::test_string("nu_test"),
|
||||
"gems" => Value::test_int(100),
|
||||
})])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use heck::ToTitleCase;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
use super::operate;
|
||||
@ -74,13 +74,10 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "convert a column from a table to Title Case",
|
||||
example: r#"[[title, count]; ['nu test', 100]] | str title-case title"#,
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec!["title".to_string(), "count".to_string()],
|
||||
vals: vec![Value::test_string("Nu Test"), Value::test_int(100)],
|
||||
})],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![Value::test_record(record! {
|
||||
"title" => Value::test_string("Nu Test"),
|
||||
"count" => Value::test_int(100),
|
||||
})])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -6,16 +6,16 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cmd-lang"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.86.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.86.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.87.1" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.87.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.87.1" }
|
||||
nu-ansi-term = "0.49.0"
|
||||
|
||||
fancy-regex = "0.11"
|
||||
|
@ -44,7 +44,7 @@ impl Command for Collect {
|
||||
let capture_block: Closure = call.req(engine_state, stack, 0)?;
|
||||
|
||||
let block = engine_state.get_block(capture_block.block_id).clone();
|
||||
let mut stack_captures = stack.captures_to_stack(&capture_block.captures);
|
||||
let mut stack_captures = stack.captures_to_stack(capture_block.captures.clone());
|
||||
|
||||
let metadata = input.metadata();
|
||||
let input: Value = input.into_value(call.head);
|
||||
@ -71,8 +71,8 @@ impl Command for Collect {
|
||||
redirect_env(engine_state, stack, &stack_captures);
|
||||
// for when we support `data | let x = $in;`
|
||||
// remove the variables added earlier
|
||||
for var_id in capture_block.captures.keys() {
|
||||
stack_captures.remove_var(*var_id);
|
||||
for (var_id, _) in capture_block.captures {
|
||||
stack_captures.remove_var(var_id);
|
||||
}
|
||||
if let Some(u) = saved_positional {
|
||||
stack_captures.remove_var(u);
|
||||
|
@ -37,11 +37,21 @@ impl Command for DefEnv {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
nu_protocol::report_error_new(
|
||||
engine_state,
|
||||
&ShellError::GenericError(
|
||||
"Deprecated command".into(),
|
||||
"`def-env` is deprecated and will be removed in 0.88.".into(),
|
||||
Some(call.head),
|
||||
Some("Use `def --env` instead".into()),
|
||||
vec![],
|
||||
),
|
||||
);
|
||||
Ok(PipelineData::empty())
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
||||
ast::Call,
|
||||
engine::{Closure, Command, EngineState, Stack, StateWorkingSet},
|
||||
record, Category, Example, IntoPipelineData, PipelineData, PipelineMetadata, Record,
|
||||
ShellError, Signature, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -18,12 +19,21 @@ impl Command for Describe {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("describe")
|
||||
.input_output_types(vec![(Type::Any, Type::String)])
|
||||
.input_output_types(vec![
|
||||
(Type::Any, Type::String),
|
||||
(Type::Any, Type::Record(vec![])),
|
||||
])
|
||||
.switch(
|
||||
"no-collect",
|
||||
"do not collect streams of structured data",
|
||||
Some('n'),
|
||||
)
|
||||
.switch(
|
||||
"detailed",
|
||||
"show detailed information about the value",
|
||||
Some('d'),
|
||||
)
|
||||
.switch("collect-lazyrecords", "collect lazy records", Some('l'))
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
@ -33,12 +43,12 @@ impl Command for Describe {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
run(call, input)
|
||||
run(Some(engine_state), call, input)
|
||||
}
|
||||
|
||||
fn run_const(
|
||||
@ -47,7 +57,7 @@ impl Command for Describe {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
run(call, input)
|
||||
run(None, call, input)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -57,18 +67,79 @@ impl Command for Describe {
|
||||
example: "'hello' | describe",
|
||||
result: Some(Value::test_string("string")),
|
||||
},
|
||||
/*
|
||||
Example {
|
||||
description: "Describe the type of a record in a detailed way",
|
||||
example:
|
||||
"{shell:'true', uwu:true, features: {bugs:false, multiplatform:true, speed: 10}, fib: [1 1 2 3 5 8], on_save: {|x| print $'Saving ($x)'}, first_commit: 2019-05-10, my_duration: (4min + 20sec)} | describe -d",
|
||||
result: Some(Value::test_record(record!(
|
||||
"type" => Value::test_string("record"),
|
||||
"lazy" => Value::test_bool(false),
|
||||
"columns" => Value::test_record(record!(
|
||||
"shell" => Value::test_string("string"),
|
||||
"uwu" => Value::test_string("bool"),
|
||||
"features" => Value::test_record(record!(
|
||||
"type" => Value::test_string("record"),
|
||||
"lazy" => Value::test_bool(false),
|
||||
"columns" => Value::test_record(record!(
|
||||
"bugs" => Value::test_string("bool"),
|
||||
"multiplatform" => Value::test_string("bool"),
|
||||
"speed" => Value::test_string("int"),
|
||||
)),
|
||||
)),
|
||||
"fib" => Value::test_record(record!(
|
||||
"type" => Value::test_string("list"),
|
||||
"length" => Value::test_int(6),
|
||||
"values" => Value::test_list(vec![
|
||||
Value::test_string("int"),
|
||||
Value::test_string("int"),
|
||||
Value::test_string("int"),
|
||||
Value::test_string("int"),
|
||||
Value::test_string("int"),
|
||||
Value::test_string("int"),
|
||||
]),
|
||||
)),
|
||||
"on_save" => Value::test_record(record!(
|
||||
"type" => Value::test_string("closure"),
|
||||
"signature" => Value::test_record(record!(
|
||||
"name" => Value::test_string(""),
|
||||
"category" => Value::test_string("default"),
|
||||
)),
|
||||
)),
|
||||
"first_commit" => Value::test_string("date"),
|
||||
"my_duration" => Value::test_string("duration"),
|
||||
)),
|
||||
))),
|
||||
},
|
||||
Example {
|
||||
description: "Describe the type of a stream with detailed information",
|
||||
example: "[1 2 3] | each {|i| echo $i} | describe -d",
|
||||
result: None // Give "Running external commands not supported" error
|
||||
// result: Some(Value::test_record(record!(
|
||||
// "type" => Value::test_string("stream"),
|
||||
// "origin" => Value::test_string("nushell"),
|
||||
// "subtype" => Value::test_record(record!(
|
||||
// "type" => Value::test_string("list"),
|
||||
// "length" => Value::test_int(3),
|
||||
// "values" => Value::test_list(vec![
|
||||
// Value::test_string("int"),
|
||||
// Value::test_string("int"),
|
||||
// Value::test_string("int"),
|
||||
// ])
|
||||
// ))
|
||||
// ))),
|
||||
},
|
||||
Example {
|
||||
description: "Describe a stream of data, collecting it first",
|
||||
example: "[1 2 3] | each {|i| $i} | describe",
|
||||
result: Some(Value::test_string("list<int> (stream)")),
|
||||
example: "[1 2 3] | each {|i| echo $i} | describe",
|
||||
result: None // Give "Running external commands not supported" error
|
||||
// result: Some(Value::test_string("list<int> (stream)")),
|
||||
},
|
||||
Example {
|
||||
description: "Describe the input but do not collect streams",
|
||||
example: "[1 2 3] | each {|i| $i} | describe --no-collect",
|
||||
result: Some(Value::test_string("stream")),
|
||||
example: "[1 2 3] | each {|i| echo $i} | describe --no-collect",
|
||||
result: None // Give "Running external commands not supported" error
|
||||
// result: Some(Value::test_string("stream")),
|
||||
},
|
||||
*/
|
||||
]
|
||||
}
|
||||
|
||||
@ -77,16 +148,88 @@ impl Command for Describe {
|
||||
}
|
||||
}
|
||||
|
||||
fn run(call: &Call, input: PipelineData) -> Result<PipelineData, ShellError> {
|
||||
fn run(
|
||||
engine_state: Option<&EngineState>,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let metadata = input.metadata().clone().map(Box::new);
|
||||
let head = call.head;
|
||||
|
||||
let no_collect: bool = call.has_flag("no-collect");
|
||||
let detailed = call.has_flag("detailed");
|
||||
|
||||
let description = match input {
|
||||
PipelineData::ExternalStream { .. } => "raw input".into(),
|
||||
let description: Value = match input {
|
||||
PipelineData::ExternalStream {
|
||||
ref stdout,
|
||||
ref stderr,
|
||||
ref exit_code,
|
||||
..
|
||||
} => {
|
||||
if detailed {
|
||||
Value::record(
|
||||
record!(
|
||||
"type" => Value::string("stream", head),
|
||||
"origin" => Value::string("external", head),
|
||||
"stdout" => match stdout {
|
||||
Some(_) => Value::record(
|
||||
record!(
|
||||
"type" => Value::string("stream", head),
|
||||
"origin" => Value::string("external", head),
|
||||
"subtype" => Value::string("any", head),
|
||||
),
|
||||
head,
|
||||
),
|
||||
None => Value::nothing(head),
|
||||
},
|
||||
"stderr" => match stderr {
|
||||
Some(_) => Value::record(
|
||||
record!(
|
||||
"type" => Value::string("stream", head),
|
||||
"origin" => Value::string("external", head),
|
||||
"subtype" => Value::string("any", head),
|
||||
),
|
||||
head,
|
||||
),
|
||||
None => Value::nothing(head),
|
||||
},
|
||||
"exit_code" => match exit_code {
|
||||
Some(_) => Value::record(
|
||||
record!(
|
||||
"type" => Value::string("stream", head),
|
||||
"origin" => Value::string("external", head),
|
||||
"subtype" => Value::string("int", head),
|
||||
),
|
||||
head,
|
||||
),
|
||||
None => Value::nothing(head),
|
||||
},
|
||||
"metadata" => metadata_to_value(metadata, head),
|
||||
),
|
||||
head,
|
||||
)
|
||||
} else {
|
||||
Value::string("raw input", head)
|
||||
}
|
||||
}
|
||||
PipelineData::ListStream(_, _) => {
|
||||
if no_collect {
|
||||
"stream".into()
|
||||
if detailed {
|
||||
Value::record(
|
||||
record!(
|
||||
"type" => Value::string("stream", head),
|
||||
"origin" => Value::string("nushell", head),
|
||||
"subtype" => {
|
||||
if no_collect {
|
||||
Value::string("any", head)
|
||||
} else {
|
||||
describe_value(input.into_value(head), head, engine_state, call)?
|
||||
}
|
||||
},
|
||||
"metadata" => metadata_to_value(metadata, head),
|
||||
),
|
||||
head,
|
||||
)
|
||||
} else if no_collect {
|
||||
Value::string("stream", head)
|
||||
} else {
|
||||
let value = input.into_value(head);
|
||||
let base_description = match value {
|
||||
@ -94,19 +237,195 @@ fn run(call: &Call, input: PipelineData) -> Result<PipelineData, ShellError> {
|
||||
_ => value.get_type().to_string(),
|
||||
};
|
||||
|
||||
format!("{base_description} (stream)")
|
||||
Value::string(format!("{} (stream)", base_description), head)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let value = input.into_value(head);
|
||||
match value {
|
||||
Value::CustomValue { val, .. } => val.value_string(),
|
||||
_ => value.get_type().to_string(),
|
||||
if !detailed {
|
||||
match value {
|
||||
Value::CustomValue { val, .. } => Value::string(val.value_string(), head),
|
||||
_ => Value::string(value.get_type().to_string(), head),
|
||||
}
|
||||
} else {
|
||||
describe_value(value, head, engine_state, call)?
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Value::string(description, head).into_pipeline_data())
|
||||
Ok(description.into_pipeline_data())
|
||||
}
|
||||
|
||||
fn describe_value(
|
||||
value: Value,
|
||||
head: nu_protocol::Span,
|
||||
engine_state: Option<&EngineState>,
|
||||
call: &Call,
|
||||
) -> Result<Value, ShellError> {
|
||||
Ok(match value {
|
||||
Value::CustomValue { val, internal_span } => Value::record(
|
||||
record!(
|
||||
"type" => Value::string("custom", head),
|
||||
"subtype" => run(engine_state,call, val.to_base_value(internal_span)?.into_pipeline_data())?.into_value(head),
|
||||
),
|
||||
head,
|
||||
),
|
||||
Value::Bool { .. }
|
||||
| Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::Filesize { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Range { .. }
|
||||
| Value::String { .. }
|
||||
| Value::MatchPattern { .. }
|
||||
| Value::Nothing { .. } => Value::record(
|
||||
record!(
|
||||
"type" => Value::string(value.get_type().to_string(), head),
|
||||
),
|
||||
head,
|
||||
),
|
||||
Value::Record { val, .. } => {
|
||||
let mut record = Record::new();
|
||||
for i in 0..val.len() {
|
||||
let k = val.cols[i].clone();
|
||||
let v = val.vals[i].clone();
|
||||
|
||||
record.push(k, {
|
||||
if let Value::Record { val, .. } =
|
||||
describe_value(v.clone(), head, engine_state, call)?
|
||||
{
|
||||
if let [Value::String { val: k, .. }] = val.vals.as_slice() {
|
||||
Value::string(k, head)
|
||||
} else {
|
||||
Value::record(val, head)
|
||||
}
|
||||
} else {
|
||||
describe_value(v, head, engine_state, call)?
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Value::record(
|
||||
record!(
|
||||
"type" => Value::string("record", head),
|
||||
"lazy" => Value::bool(false, head),
|
||||
"columns" => Value::record(record, head),
|
||||
),
|
||||
head,
|
||||
)
|
||||
}
|
||||
Value::List { vals, .. } => Value::record(
|
||||
record!(
|
||||
"type" => Value::string("list", head),
|
||||
"length" => Value::int(vals.len() as i64, head),
|
||||
"values" => Value::list(vals.iter().map(|v|
|
||||
match describe_value(v.clone(), head, engine_state, call) {
|
||||
Ok(Value::Record {val, ..}) => if val.cols.as_slice() == ["type"] {Ok(val.vals[0].clone())} else {Ok(Value::record(val, head))},
|
||||
x => x
|
||||
}
|
||||
).collect::<Result<Vec<_>, _>>()?, head),
|
||||
),
|
||||
head,
|
||||
),
|
||||
Value::Block { val, .. }
|
||||
| Value::Closure {
|
||||
val: Closure { block_id: val, .. },
|
||||
..
|
||||
} => {
|
||||
let block = engine_state.map(|engine_state| engine_state.get_block(val));
|
||||
|
||||
if let Some(block) = block {
|
||||
let mut record = Record::new();
|
||||
record.push("type", Value::string(value.get_type().to_string(), head));
|
||||
record.push(
|
||||
"signature",
|
||||
Value::record(
|
||||
record!(
|
||||
"name" => Value::string(block.signature.name.clone(), head),
|
||||
"category" => Value::string(block.signature.category.to_string(), head),
|
||||
),
|
||||
head,
|
||||
),
|
||||
);
|
||||
Value::record(record, head)
|
||||
} else {
|
||||
Value::record(
|
||||
record!(
|
||||
"type" => Value::string("closure", head),
|
||||
),
|
||||
head,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Value::Error { error, .. } => Value::record(
|
||||
record!(
|
||||
"type" => Value::string("error", head),
|
||||
"subtype" => Value::string(error.to_string(), head),
|
||||
),
|
||||
head,
|
||||
),
|
||||
Value::Binary { val, .. } => Value::record(
|
||||
record!(
|
||||
"type" => Value::string("binary", head),
|
||||
"length" => Value::int(val.len() as i64, head),
|
||||
),
|
||||
head,
|
||||
),
|
||||
Value::CellPath { val, .. } => Value::record(
|
||||
record!(
|
||||
"type" => Value::string("cellpath", head),
|
||||
"length" => Value::int(val.members.len() as i64, head),
|
||||
),
|
||||
head,
|
||||
),
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let collect_lazyrecords: bool = call.has_flag("collect-lazyrecords");
|
||||
let mut record = Record::new();
|
||||
|
||||
record.push("type", Value::string("record", head));
|
||||
record.push("lazy", Value::bool(true, head));
|
||||
|
||||
if collect_lazyrecords {
|
||||
let collected = val.collect()?;
|
||||
if let Value::Record { val, .. } =
|
||||
describe_value(collected, head, engine_state, call)?
|
||||
{
|
||||
let mut record_cols = Record::new();
|
||||
record.push("length", Value::int(val.len() as i64, head));
|
||||
|
||||
for i in 0..val.len() {
|
||||
record_cols.push(
|
||||
val.cols[i].clone(),
|
||||
describe_value(val.vals[i].clone(), head, engine_state, call)?,
|
||||
);
|
||||
}
|
||||
record.push("columns", Value::record(record_cols, head));
|
||||
} else {
|
||||
let cols = val.column_names();
|
||||
record.push("length", Value::int(cols.len() as i64, head));
|
||||
}
|
||||
} else {
|
||||
let cols = val.column_names();
|
||||
record.push("length", Value::int(cols.len() as i64, head));
|
||||
}
|
||||
|
||||
Value::record(record, head)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn metadata_to_value(metadata: Option<Box<PipelineMetadata>>, head: nu_protocol::Span) -> Value {
|
||||
match metadata {
|
||||
Some(metadata) => Value::record(
|
||||
record!(
|
||||
"data_source" => Value::string(format!("{:?}", metadata.data_source), head),
|
||||
),
|
||||
head,
|
||||
),
|
||||
_ => Value::nothing(head),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -72,7 +72,7 @@ impl Command for Do {
|
||||
let capture_errors = call.has_flag("capture-errors");
|
||||
let has_env = call.has_flag("env");
|
||||
|
||||
let mut callee_stack = caller_stack.captures_to_stack(&block.captures);
|
||||
let mut callee_stack = caller_stack.captures_to_stack(block.captures);
|
||||
let block = engine_state.get_block(block.block_id);
|
||||
|
||||
let params: Vec<_> = block
|
||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -44,20 +44,15 @@ impl Command for ErrorMake {
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = call.head;
|
||||
let arg: Value = call.req(engine_state, stack, 0)?;
|
||||
let unspanned = call.has_flag("unspanned");
|
||||
|
||||
let throw_error = if unspanned { None } else { Some(span) };
|
||||
Err(make_error(&arg, throw_error).unwrap_or_else(|| {
|
||||
ShellError::GenericError(
|
||||
"Creating error value not supported.".into(),
|
||||
"unsupported error format".into(),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
)
|
||||
}))
|
||||
let throw_span = if call.has_flag("unspanned") {
|
||||
None
|
||||
} else {
|
||||
Some(call.head)
|
||||
};
|
||||
|
||||
Err(make_other_error(&arg, throw_span))
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -82,16 +77,21 @@ impl Command for ErrorMake {
|
||||
msg: "my custom error message"
|
||||
label: {
|
||||
text: "my custom label text" # not mandatory unless $.label exists
|
||||
start: 123 # not mandatory unless $.label.end is set
|
||||
end: 456 # not mandatory unless $.label.start is set
|
||||
# optional
|
||||
span: {
|
||||
# if $.label.span exists, both start and end must be present
|
||||
start: 123
|
||||
end: 456
|
||||
}
|
||||
}
|
||||
help: "A help string, suggesting a fix to the user" # optional
|
||||
}"#,
|
||||
result: Some(Value::error(
|
||||
ShellError::GenericError(
|
||||
"my custom error message".to_string(),
|
||||
"my custom label text".to_string(),
|
||||
Some(Span::new(123, 456)),
|
||||
None,
|
||||
Some("A help string, suggesting a fix to the user".to_string()),
|
||||
Vec::new(),
|
||||
),
|
||||
Span::unknown(),
|
||||
@ -101,13 +101,11 @@ impl Command for ErrorMake {
|
||||
description:
|
||||
"Create a custom error for a custom command that shows the span of the argument",
|
||||
example: r#"def foo [x] {
|
||||
let span = (metadata $x).span;
|
||||
error make {
|
||||
msg: "this is fishy"
|
||||
label: {
|
||||
text: "fish right here"
|
||||
start: $span.start
|
||||
end: $span.end
|
||||
span: (metadata $x).span
|
||||
}
|
||||
}
|
||||
}"#,
|
||||
@ -117,100 +115,166 @@ impl Command for ErrorMake {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
||||
const UNABLE_TO_PARSE: &str = "Unable to parse error format.";
|
||||
|
||||
fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
||||
let span = value.span();
|
||||
if let Value::Record { .. } = &value {
|
||||
let msg = value.get_data_by_key("msg");
|
||||
let label = value.get_data_by_key("label");
|
||||
|
||||
match (msg, &label) {
|
||||
(Some(Value::String { val: message, .. }), Some(label)) => {
|
||||
let label_start = label.get_data_by_key("start");
|
||||
let label_end = label.get_data_by_key("end");
|
||||
let label_text = label.get_data_by_key("text");
|
||||
|
||||
let label_span = Some(label.span());
|
||||
|
||||
match (label_start, label_end, label_text) {
|
||||
(
|
||||
Some(Value::Int { val: start, .. }),
|
||||
Some(Value::Int { val: end, .. }),
|
||||
Some(Value::String {
|
||||
val: label_text, ..
|
||||
}),
|
||||
) => {
|
||||
if start > end {
|
||||
Some(ShellError::GenericError(
|
||||
"invalid error format.".into(),
|
||||
"`$.label.start` should be smaller than `$.label.end`".into(),
|
||||
label_span,
|
||||
Some(format!("{} > {}", start, end)),
|
||||
Vec::new(),
|
||||
))
|
||||
} else {
|
||||
Some(ShellError::GenericError(
|
||||
message,
|
||||
label_text,
|
||||
Some(Span::new(start as usize, end as usize)),
|
||||
None,
|
||||
Vec::new(),
|
||||
))
|
||||
}
|
||||
}
|
||||
(
|
||||
None,
|
||||
None,
|
||||
Some(Value::String {
|
||||
val: label_text, ..
|
||||
}),
|
||||
) => Some(ShellError::GenericError(
|
||||
message,
|
||||
label_text,
|
||||
throw_span,
|
||||
None,
|
||||
Vec::new(),
|
||||
)),
|
||||
(_, _, None) => Some(ShellError::GenericError(
|
||||
"Unable to parse error format.".into(),
|
||||
"missing required member `$.label.text`".into(),
|
||||
label_span,
|
||||
None,
|
||||
Vec::new(),
|
||||
)),
|
||||
(Some(Value::Int { .. }), None, _) => Some(ShellError::GenericError(
|
||||
"Unable to parse error format.".into(),
|
||||
"missing required member `$.label.end`".into(),
|
||||
label_span,
|
||||
Some("required because `$.label.start` is set".to_string()),
|
||||
Vec::new(),
|
||||
)),
|
||||
(None, Some(Value::Int { .. }), _) => Some(ShellError::GenericError(
|
||||
"Unable to parse error format.".into(),
|
||||
"missing required member `$.label.start`".into(),
|
||||
label_span,
|
||||
Some("required because `$.label.end` is set".to_string()),
|
||||
Vec::new(),
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
(Some(Value::String { val: message, .. }), None) => Some(ShellError::GenericError(
|
||||
message,
|
||||
"originates from here".to_string(),
|
||||
let value = match value {
|
||||
Value::Record { val, .. } => val,
|
||||
_ => {
|
||||
return ShellError::GenericError(
|
||||
"Creating error value not supported.".into(),
|
||||
"unsupported error format, must be a record".into(),
|
||||
throw_span,
|
||||
None,
|
||||
Vec::new(),
|
||||
)),
|
||||
(None, _) => Some(ShellError::GenericError(
|
||||
"Unable to parse error format.".into(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let msg = match value.get("msg") {
|
||||
Some(Value::String { val, .. }) => val.clone(),
|
||||
Some(_) => {
|
||||
return ShellError::GenericError(
|
||||
UNABLE_TO_PARSE.into(),
|
||||
"`$.msg` has wrong type, must be string".into(),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
return ShellError::GenericError(
|
||||
UNABLE_TO_PARSE.into(),
|
||||
"missing required member `$.msg`".into(),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
)),
|
||||
_ => None,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let help = match value.get("help") {
|
||||
Some(Value::String { val, .. }) => Some(val.clone()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let (label, label_span) = match value.get("label") {
|
||||
Some(value @ Value::Record { val, .. }) => (val, value.span()),
|
||||
Some(_) => {
|
||||
return ShellError::GenericError(
|
||||
UNABLE_TO_PARSE.into(),
|
||||
"`$.label` has wrong type, must be a record".into(),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
)
|
||||
}
|
||||
// correct return: no label
|
||||
None => {
|
||||
return ShellError::GenericError(
|
||||
msg,
|
||||
"originates from here".to_string(),
|
||||
throw_span,
|
||||
help,
|
||||
Vec::new(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// remove after a few versions
|
||||
if label.get("start").is_some() || label.get("end").is_some() {
|
||||
return ShellError::GenericError(
|
||||
UNABLE_TO_PARSE.into(),
|
||||
"`start` and `end` are deprecated".into(),
|
||||
Some(span),
|
||||
Some("Use `$.label.span` instead".into()),
|
||||
Vec::new(),
|
||||
);
|
||||
}
|
||||
|
||||
let text = match label.get("text") {
|
||||
Some(Value::String { val, .. }) => val.clone(),
|
||||
Some(_) => {
|
||||
return ShellError::GenericError(
|
||||
UNABLE_TO_PARSE.into(),
|
||||
"`$.label.text` has wrong type, must be string".into(),
|
||||
Some(label_span),
|
||||
None,
|
||||
Vec::new(),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
return ShellError::GenericError(
|
||||
UNABLE_TO_PARSE.into(),
|
||||
"missing required member `$.label.text`".into(),
|
||||
Some(label_span),
|
||||
None,
|
||||
Vec::new(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let (span, span_span) = match label.get("span") {
|
||||
Some(value @ Value::Record { val, .. }) => (val, value.span()),
|
||||
Some(value) => {
|
||||
return ShellError::GenericError(
|
||||
UNABLE_TO_PARSE.into(),
|
||||
"`$.label.span` has wrong type, must be record".into(),
|
||||
Some(value.span()),
|
||||
None,
|
||||
Vec::new(),
|
||||
)
|
||||
}
|
||||
// correct return: label, no span
|
||||
None => return ShellError::GenericError(msg, text, throw_span, help, Vec::new()),
|
||||
};
|
||||
|
||||
let span_start = match get_span_sides(span, span_span, "start") {
|
||||
Ok(val) => val,
|
||||
Err(err) => return err,
|
||||
};
|
||||
let span_end = match get_span_sides(span, span_span, "end") {
|
||||
Ok(val) => val,
|
||||
Err(err) => return err,
|
||||
};
|
||||
|
||||
if span_start > span_end {
|
||||
return ShellError::GenericError(
|
||||
"invalid error format.".into(),
|
||||
"`$.label.start` should be smaller than `$.label.end`".into(),
|
||||
Some(label_span),
|
||||
Some(format!("{} > {}", span_start, span_end)),
|
||||
Vec::new(),
|
||||
);
|
||||
}
|
||||
|
||||
// correct return: everything present
|
||||
ShellError::GenericError(
|
||||
msg,
|
||||
text,
|
||||
Some(Span::new(span_start as usize, span_end as usize)),
|
||||
help,
|
||||
Vec::new(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_span_sides(span: &Record, span_span: Span, side: &str) -> Result<i64, ShellError> {
|
||||
match span.get(side) {
|
||||
Some(Value::Int { val, .. }) => Ok(*val),
|
||||
Some(_) => Err(ShellError::GenericError(
|
||||
UNABLE_TO_PARSE.into(),
|
||||
format!("`$.span.{side}` must be int"),
|
||||
Some(span_span),
|
||||
None,
|
||||
Vec::new(),
|
||||
)),
|
||||
None => Err(ShellError::GenericError(
|
||||
UNABLE_TO_PARSE.into(),
|
||||
format!("`$.span.{side}` must be present, if span is specified."),
|
||||
Some(span_span),
|
||||
None,
|
||||
Vec::new(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
@ -62,11 +62,21 @@ export def-env cd_with_fallback [arg = ""] {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
nu_protocol::report_error_new(
|
||||
engine_state,
|
||||
&ShellError::GenericError(
|
||||
"Deprecated command".into(),
|
||||
"`export def-env` is deprecated and will be removed in 0.88.".into(),
|
||||
Some(call.head),
|
||||
Some("Use `export def --env` instead".into()),
|
||||
vec![],
|
||||
),
|
||||
);
|
||||
Ok(PipelineData::empty())
|
||||
}
|
||||
|
||||
|
@ -34,11 +34,21 @@ impl Command for ExportExternWrapped {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
nu_protocol::report_error_new(
|
||||
engine_state,
|
||||
&ShellError::GenericError(
|
||||
"Deprecated command".into(),
|
||||
"`export extern-wrapped` is deprecated and will be removed in 0.88.".into(),
|
||||
Some(call.head),
|
||||
Some("Use `export def --wrapped` instead".into()),
|
||||
vec![],
|
||||
),
|
||||
);
|
||||
Ok(PipelineData::empty())
|
||||
}
|
||||
|
||||
|
@ -37,11 +37,21 @@ impl Command for ExternWrapped {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
nu_protocol::report_error_new(
|
||||
engine_state,
|
||||
&ShellError::GenericError(
|
||||
"Deprecated command".into(),
|
||||
"`extern-wrapped` is deprecated and will be removed in 0.88.".into(),
|
||||
Some(call.head),
|
||||
Some("Use `def --wrapped` instead".into()),
|
||||
vec![],
|
||||
),
|
||||
);
|
||||
Ok(PipelineData::empty())
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ impl Command for ScopeAliases {
|
||||
Signature::build("scope aliases")
|
||||
.input_output_types(vec![(Type::Nothing, Type::Any)])
|
||||
.allow_variants_without_examples(true)
|
||||
.category(Category::Filters)
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -17,7 +17,7 @@ impl Command for ScopeCommands {
|
||||
Signature::build("scope commands")
|
||||
.input_output_types(vec![(Type::Nothing, Type::Any)])
|
||||
.allow_variants_without_examples(true)
|
||||
.category(Category::Filters)
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -15,7 +15,7 @@ impl Command for ScopeEngineStats {
|
||||
Signature::build("scope engine-stats")
|
||||
.input_output_types(vec![(Type::Nothing, Type::Any)])
|
||||
.allow_variants_without_examples(true)
|
||||
.category(Category::Filters)
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -17,7 +17,7 @@ impl Command for ScopeExterns {
|
||||
Signature::build("scope externs")
|
||||
.input_output_types(vec![(Type::Nothing, Type::Any)])
|
||||
.allow_variants_without_examples(true)
|
||||
.category(Category::Filters)
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -17,7 +17,7 @@ impl Command for ScopeModules {
|
||||
Signature::build("scope modules")
|
||||
.input_output_types(vec![(Type::Nothing, Type::Any)])
|
||||
.allow_variants_without_examples(true)
|
||||
.category(Category::Filters)
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -17,7 +17,7 @@ impl Command for ScopeVariables {
|
||||
Signature::build("scope variables")
|
||||
.input_output_types(vec![(Type::Nothing, Type::Any)])
|
||||
.allow_variants_without_examples(true)
|
||||
.category(Category::Filters)
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use itertools::Itertools;
|
||||
use nu_protocol::{
|
||||
ast::Block,
|
||||
ast::{Block, RangeInclusion},
|
||||
engine::{EngineState, Stack, StateDelta, StateWorkingSet},
|
||||
Example, PipelineData, Signature, Span, Type, Value,
|
||||
};
|
||||
@ -143,7 +143,8 @@ pub fn check_example_evaluates_to_expected_output(
|
||||
// you need to define its equality in the Value struct
|
||||
if let Some(expected) = example.result.as_ref() {
|
||||
assert_eq!(
|
||||
&result, expected,
|
||||
DebuggableValue(&result),
|
||||
DebuggableValue(expected),
|
||||
"The example result differs from the expected value",
|
||||
)
|
||||
}
|
||||
@ -184,3 +185,103 @@ fn eval(
|
||||
let (block, delta) = parse(contents, engine_state);
|
||||
eval_block(block, input, cwd, engine_state, delta)
|
||||
}
|
||||
|
||||
pub struct DebuggableValue<'a>(pub &'a Value);
|
||||
|
||||
impl PartialEq for DebuggableValue<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Debug for DebuggableValue<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.0 {
|
||||
Value::Bool { val, .. } => {
|
||||
write!(f, "{:?}", val)
|
||||
}
|
||||
Value::Int { val, .. } => {
|
||||
write!(f, "{:?}", val)
|
||||
}
|
||||
Value::Float { val, .. } => {
|
||||
write!(f, "{:?}f", val)
|
||||
}
|
||||
Value::Filesize { val, .. } => {
|
||||
write!(f, "Filesize({:?})", val)
|
||||
}
|
||||
Value::Duration { val, .. } => {
|
||||
let duration = std::time::Duration::from_nanos(*val as u64);
|
||||
write!(f, "Duration({:?})", duration)
|
||||
}
|
||||
Value::Date { val, .. } => {
|
||||
write!(f, "Date({:?})", val)
|
||||
}
|
||||
Value::Range { val, .. } => match val.inclusion {
|
||||
RangeInclusion::Inclusive => write!(
|
||||
f,
|
||||
"Range({:?}..{:?}, step: {:?})",
|
||||
val.from, val.to, val.incr
|
||||
),
|
||||
RangeInclusion::RightExclusive => write!(
|
||||
f,
|
||||
"Range({:?}..<{:?}, step: {:?})",
|
||||
val.from, val.to, val.incr
|
||||
),
|
||||
},
|
||||
Value::String { val, .. } => {
|
||||
write!(f, "{:?}", val)
|
||||
}
|
||||
Value::Record { val, .. } => {
|
||||
write!(f, "{{")?;
|
||||
for i in 0..val.len() {
|
||||
let col = &val.cols[i];
|
||||
let value = &val.vals[i];
|
||||
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{:?}: {:?}", col, DebuggableValue(value))?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
Value::List { vals, .. } => {
|
||||
write!(f, "[")?;
|
||||
for (i, value) in vals.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{:?}", DebuggableValue(value))?;
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
Value::Block { val, .. } => {
|
||||
write!(f, "Block({:?})", val)
|
||||
}
|
||||
Value::Closure { val, .. } => {
|
||||
write!(f, "Closure({:?})", val)
|
||||
}
|
||||
Value::Nothing { .. } => {
|
||||
write!(f, "Nothing")
|
||||
}
|
||||
Value::Error { error, .. } => {
|
||||
write!(f, "Error({:?})", error)
|
||||
}
|
||||
Value::Binary { val, .. } => {
|
||||
write!(f, "Binary({:?})", val)
|
||||
}
|
||||
Value::CellPath { val, .. } => {
|
||||
write!(f, "CellPath({:?})", val.to_string())
|
||||
}
|
||||
Value::CustomValue { val, .. } => {
|
||||
write!(f, "CustomValue({:?})", val)
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let rec = val.collect().map_err(|_| std::fmt::Error)?;
|
||||
write!(f, "LazyRecord({:?})", DebuggableValue(&rec))
|
||||
}
|
||||
Value::MatchPattern { val, .. } => {
|
||||
write!(f, "MatchPattern({:?})", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,19 +5,19 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-color-config"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
[dependencies]
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
|
||||
nu-ansi-term = "0.49.0"
|
||||
nu-utils = { path = "../nu-utils", version = "0.86.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||
nu-json = { path = "../nu-json", version = "0.86.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.87.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.87.1" }
|
||||
nu-json = { path = "../nu-json", version = "0.87.1" }
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.87.1" }
|
||||
|
@ -578,7 +578,7 @@ fn fill_modifiers(attrs: &str, style: &mut Style) {
|
||||
//
|
||||
// since we can combine styles like bold-italic, iterate through the chars
|
||||
// and set the bools for later use in the nu_ansi_term::Style application
|
||||
for ch in attrs.to_lowercase().chars() {
|
||||
for ch in attrs.chars().map(|c| c.to_ascii_lowercase()) {
|
||||
match ch {
|
||||
'l' => style.is_blink = true,
|
||||
'b' => style.is_bold = true,
|
||||
|
@ -57,15 +57,11 @@ impl<'a> StyleComputer<'a> {
|
||||
Some(ComputableStyle::Closure(v)) => {
|
||||
let span = v.span();
|
||||
match v {
|
||||
Value::Closure {
|
||||
val: block_id,
|
||||
captures,
|
||||
..
|
||||
} => {
|
||||
let block = self.engine_state.get_block(*block_id).clone();
|
||||
Value::Closure { val, .. } => {
|
||||
let block = self.engine_state.get_block(val.block_id).clone();
|
||||
// Because captures_to_stack() clones, we don't need to use with_env() here
|
||||
// (contrast with_env() usage in `each` or `do`).
|
||||
let mut stack = self.stack.captures_to_stack(captures);
|
||||
let mut stack = self.stack.captures_to_stack(val.captures.clone());
|
||||
|
||||
// Support 1-argument blocks as well as 0-argument blocks.
|
||||
if let Some(var) = block.signature.get_positional(0) {
|
||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-command"
|
||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
||||
version = "0.86.0"
|
||||
version = "0.87.1"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -14,19 +14,19 @@ bench = false
|
||||
|
||||
[dependencies]
|
||||
nu-ansi-term = "0.49.0"
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.86.0" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.86.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||
nu-glob = { path = "../nu-glob", version = "0.86.0" }
|
||||
nu-json = { path = "../nu-json", version = "0.86.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.86.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.86.0" }
|
||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.86.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
|
||||
nu-system = { path = "../nu-system", version = "0.86.0" }
|
||||
nu-table = { path = "../nu-table", version = "0.86.0" }
|
||||
nu-term-grid = { path = "../nu-term-grid", version = "0.86.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.86.0" }
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.87.1" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.87.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.87.1" }
|
||||
nu-glob = { path = "../nu-glob", version = "0.87.1" }
|
||||
nu-json = { path = "../nu-json", version = "0.87.1" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.87.1" }
|
||||
nu-path = { path = "../nu-path", version = "0.87.1" }
|
||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.87.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
|
||||
nu-system = { path = "../nu-system", version = "0.87.1" }
|
||||
nu-table = { path = "../nu-table", version = "0.87.1" }
|
||||
nu-term-grid = { path = "../nu-term-grid", version = "0.87.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.87.1" }
|
||||
|
||||
alphanumeric-sort = "1.5"
|
||||
base64 = "0.21"
|
||||
@ -37,7 +37,7 @@ chrono = { version = "0.4", features = ["std", "unstable-locales"], default-feat
|
||||
chrono-humanize = "0.2.3"
|
||||
chrono-tz = "0.8"
|
||||
crossterm = "0.27"
|
||||
csv = "1.2"
|
||||
csv = "1.3"
|
||||
dialoguer = { default-features = false, features = ["fuzzy-select"], version = "0.11" }
|
||||
digest = { default-features = false, version = "0.10" }
|
||||
dtparse = "2.0"
|
||||
@ -47,7 +47,7 @@ filesize = "0.2"
|
||||
filetime = "0.2"
|
||||
fs_extra = "1.3"
|
||||
htmlescape = "0.3"
|
||||
indexmap = "2.0"
|
||||
indexmap = "2.1"
|
||||
indicatif = "0.17"
|
||||
itertools = "0.11"
|
||||
log = "0.4"
|
||||
@ -88,9 +88,11 @@ unicode-segmentation = "1.10"
|
||||
ureq = { version = "2.8", default-features = false, features = ["charset", "gzip", "json", "native-tls"] }
|
||||
url = "2.2"
|
||||
uu_cp = "0.0.22"
|
||||
uuid = { version = "1.3", features = ["v4"] }
|
||||
uu_whoami = "0.0.22"
|
||||
uu_mkdir = "0.0.22"
|
||||
uuid = { version = "1.5", features = ["v4"] }
|
||||
wax = { version = "0.6" }
|
||||
which = { version = "4.4", optional = true }
|
||||
which = { version = "5.0", optional = true }
|
||||
bracoxide = "0.1.2"
|
||||
chardetng = "0.1.17"
|
||||
|
||||
@ -110,6 +112,7 @@ version = "3.1"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_System_Environment",
|
||||
"Win32_System_SystemServices",
|
||||
"Win32_Security",
|
||||
"Win32_System_Threading",
|
||||
@ -123,8 +126,8 @@ trash-support = ["trash"]
|
||||
which-support = ["which"]
|
||||
|
||||
[dev-dependencies]
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.86.0" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.87.1" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.87.1" }
|
||||
|
||||
dirs-next = "2.0"
|
||||
mockito = { version = "1.2", default-features = false }
|
||||
|
@ -1,11 +1,11 @@
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_cmd_base::util;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::record;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, Range, Record, ShellError, Signature, Span, SyntaxShape, Type,
|
||||
Value,
|
||||
Category, Example, PipelineData, Range, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -94,44 +94,34 @@ impl Command for BytesAt {
|
||||
Example {
|
||||
description: "Get a subbytes `0x[10 01]` from the bytes `0x[33 44 55 10 01 13]`",
|
||||
example: " 0x[33 44 55 10 01 13] | bytes at 3..<4",
|
||||
result: Some(Value::binary(vec![0x10], Span::test_data())),
|
||||
result: Some(Value::test_binary(vec![0x10])),
|
||||
},
|
||||
Example {
|
||||
description: "Get a subbytes `0x[10 01 13]` from the bytes `0x[33 44 55 10 01 13]`",
|
||||
example: " 0x[33 44 55 10 01 13] | bytes at 3..6",
|
||||
result: Some(Value::binary(vec![0x10, 0x01, 0x13], Span::test_data())),
|
||||
result: Some(Value::test_binary(vec![0x10, 0x01, 0x13])),
|
||||
},
|
||||
Example {
|
||||
description: "Get the remaining characters from a starting index",
|
||||
example: " { data: 0x[33 44 55 10 01 13] } | bytes at 3.. data",
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["data".to_string()],
|
||||
vals: vec![Value::test_binary(vec![0x10, 0x01, 0x13])],
|
||||
result: Some(Value::test_record(record! {
|
||||
"data" => Value::test_binary(vec![0x10, 0x01, 0x13]),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Get the characters from the beginning until ending index",
|
||||
example: " 0x[33 44 55 10 01 13] | bytes at ..<4",
|
||||
result: Some(Value::binary(
|
||||
vec![0x33, 0x44, 0x55, 0x10],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_binary(vec![0x33, 0x44, 0x55, 0x10])),
|
||||
},
|
||||
Example {
|
||||
description:
|
||||
"Or the characters from the beginning until ending index inside a table",
|
||||
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes at 1.. ColB ColC"#,
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||
vals: vec![
|
||||
Value::binary(vec![0x11, 0x12, 0x13], Span::test_data()),
|
||||
Value::binary(vec![0x15, 0x16], Span::test_data()),
|
||||
Value::binary(vec![0x18, 0x19], Span::test_data()),
|
||||
],
|
||||
})],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![Value::test_record(record! {
|
||||
"ColA" => Value::test_binary(vec![0x11, 0x12, 0x13]),
|
||||
"ColB" => Value::test_binary(vec![0x15, 0x16]),
|
||||
"ColC" => Value::test_binary(vec![0x18, 0x19]),
|
||||
})])),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -186,13 +176,12 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
Value::Error { .. } => input.clone(),
|
||||
|
||||
other => Value::error(
|
||||
ShellError::UnsupportedInput(
|
||||
"Only binary values are supported".into(),
|
||||
format!("input type: {:?}", other.get_type()),
|
||||
head,
|
||||
// This line requires the Value::Error match above.
|
||||
other.span(),
|
||||
),
|
||||
ShellError::UnsupportedInput {
|
||||
msg: "Only binary values are supported".into(),
|
||||
input: format!("input type: {:?}", other.get_type()),
|
||||
msg_span: head,
|
||||
input_span: other.span(),
|
||||
},
|
||||
head,
|
||||
),
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::{Call, CellPath};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
struct Arguments {
|
||||
@ -95,33 +95,29 @@ impl Command for BytesIndexOf {
|
||||
Example {
|
||||
description: "Returns all matched index",
|
||||
example: " 0x[33 44 55 10 01 33 44 33 44] | bytes index-of --all 0x[33 44]",
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_int(0), Value::test_int(5), Value::test_int(7)],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_int(0),
|
||||
Value::test_int(5),
|
||||
Value::test_int(7),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
description: "Returns all matched index, searching from end",
|
||||
example: " 0x[33 44 55 10 01 33 44 33 44] | bytes index-of --all --end 0x[33 44]",
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_int(7), Value::test_int(5), Value::test_int(0)],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_int(7),
|
||||
Value::test_int(5),
|
||||
Value::test_int(0),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
description: "Returns index of pattern for specific column",
|
||||
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes index-of 0x[11] ColA ColC"#,
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||
vals: vec![
|
||||
Value::test_int(0),
|
||||
Value::binary(vec![0x14, 0x15, 0x16], Span::test_data()),
|
||||
Value::test_int(-1),
|
||||
],
|
||||
})],
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![Value::test_record(record! {
|
||||
"ColA" => Value::test_int(0),
|
||||
"ColB" => Value::binary(vec![0x14, 0x15, 0x16], Span::test_data()),
|
||||
"ColC" => Value::test_int(-1),
|
||||
})])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape,
|
||||
record, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
@ -87,49 +87,33 @@ impl Command for BytesRemove {
|
||||
Example {
|
||||
description: "Remove contents",
|
||||
example: "0x[10 AA FF AA FF] | bytes remove 0x[10 AA]",
|
||||
result: Some(Value::binary (
|
||||
result: Some(Value::test_binary (
|
||||
vec![0xFF, 0xAA, 0xFF],
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Remove all occurrences of find binary in record field",
|
||||
example: "{ data: 0x[10 AA 10 BB 10] } | bytes remove --all 0x[10] data",
|
||||
result: Some(Value::test_record(Record {
|
||||
cols: vec!["data".to_string()],
|
||||
vals: vec![Value::test_binary(vec![0xAA, 0xBB])]
|
||||
result: Some(Value::test_record(record! {
|
||||
"data" => Value::test_binary(vec![0xAA, 0xBB])
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Remove occurrences of find binary from end",
|
||||
example: "0x[10 AA 10 BB CC AA 10] | bytes remove --end 0x[10]",
|
||||
result: Some(Value::binary (
|
||||
result: Some(Value::test_binary (
|
||||
vec![0x10, 0xAA, 0x10, 0xBB, 0xCC, 0xAA],
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Remove all occurrences of find binary in table",
|
||||
example: "[[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes remove 0x[11] ColA ColC",
|
||||
result: Some(Value::list (
|
||||
vec![Value::test_record(Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||
vals: vec![
|
||||
Value::binary (
|
||||
vec![0x12, 0x13],
|
||||
Span::test_data(),
|
||||
),
|
||||
Value::binary (
|
||||
vec![0x14, 0x15, 0x16],
|
||||
Span::test_data(),
|
||||
),
|
||||
Value::binary (
|
||||
vec![0x17, 0x18, 0x19],
|
||||
Span::test_data(),
|
||||
),
|
||||
]
|
||||
result: Some(Value::test_list (
|
||||
vec![Value::test_record(record! {
|
||||
"ColA" => Value::test_binary ( vec![0x12, 0x13],),
|
||||
"ColB" => Value::test_binary ( vec![0x14, 0x15, 0x16],),
|
||||
"ColC" => Value::test_binary ( vec![0x17, 0x18, 0x19],),
|
||||
})],
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
]
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user