mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 07:00:37 +02:00
Compare commits
115 Commits
Author | SHA1 | Date | |
---|---|---|---|
4e8e03867c | |||
49e8af8ea5 | |||
d5d61d14b3 | |||
f562a4526c | |||
e6c09f2dfc | |||
73a68954c4 | |||
476d543dee | |||
398502b0d6 | |||
62011b6bcc | |||
1214cd57e8 | |||
6cd124ddb2 | |||
d32aec5906 | |||
e919f9a73b | |||
a3c349746f | |||
b5f8f64d79 | |||
1576b959f9 | |||
4096f52003 | |||
7ceb668419 | |||
420aee18ca | |||
15e9c11849 | |||
9fd680ae2b | |||
ad94ed5e13 | |||
1bdcdcca70 | |||
610e3911f6 | |||
ee9eddd851 | |||
c08e145501 | |||
c00853a473 | |||
79c7b20cfd | |||
89cbfd758d | |||
e6e6b730f3 | |||
0fe6a7c1b5 | |||
1794ad51bd | |||
fb197f562a | |||
91c270c14a | |||
3e93ae8af4 | |||
e06df124ca | |||
2590fcbe5c | |||
09691ff866 | |||
16db368232 | |||
df87d90b8c | |||
f2f01b8a4d | |||
6c0190cd38 | |||
b26246bf12 | |||
36a4effbb2 | |||
9fca417f8c | |||
d09e1148b2 | |||
493bc2b1c9 | |||
74b812228c | |||
649b3804c1 | |||
df6a53f52e | |||
c4af5df828 | |||
f94a3e15f5 | |||
75782f0f50 | |||
2b06ce27d3 | |||
72c241348b | |||
ab2d2db987 | |||
07e05ef183 | |||
a986de8ad0 | |||
22cfe4391e | |||
97d17311f4 | |||
0f6fd30619 | |||
e1ebd461d2 | |||
f000d5d0a1 | |||
574c5961c8 | |||
69708f7244 | |||
62c5df5fc6 | |||
92c855a412 | |||
d395816929 | |||
5e34ef6dff | |||
d567c58cc1 | |||
4e0d7bc77c | |||
32581497ef | |||
d6df367c6b | |||
4e6327de1d | |||
b3d8666db0 | |||
1de7c3d033 | |||
962b258cc6 | |||
59697cab63 | |||
349af05da8 | |||
b3b3cf0689 | |||
5d59234f8d | |||
4f7b423f36 | |||
f7043bf690 | |||
1297499d7a | |||
bd0baa961c | |||
4ee536f044 | |||
8581bec891 | |||
8bcbc8eeb3 | |||
c164ef5489 | |||
cc3653cfd9 | |||
7fc65067cf | |||
f9ae882012 | |||
1d80a68f4c | |||
fda69354db | |||
cc5c4d38bb | |||
0fa0c25fb3 | |||
55eafadf02 | |||
51c74eebd0 | |||
ae9f4135c0 | |||
4e2d3ceaaf | |||
c9c6bd4836 | |||
d90420ac4c | |||
260ff99710 | |||
08014c6a98 | |||
66cedf0b3a | |||
707a4ebc15 | |||
d95375d494 | |||
1c1c58e802 | |||
7fe05b8296 | |||
17ef531905 | |||
b8e2bdd6b1 | |||
88817a8f10 | |||
3e8ce43dcb | |||
9d8845d7ad | |||
52578ba483 |
@ -16,7 +16,7 @@ strategy:
|
||||
image: ubuntu-18.04
|
||||
style: 'wasm'
|
||||
macos-stable:
|
||||
image: macos-10.14
|
||||
image: macOS-10.15
|
||||
style: 'unflagged'
|
||||
windows-stable:
|
||||
image: windows-2019
|
||||
|
32
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
32
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,11 +1,11 @@
|
||||
name: Bug Report
|
||||
description: Create a report to help us improve
|
||||
body:
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is.
|
||||
description: Thank you for your bug report. We are working diligently with our community to integrate our latest code base that we call [engine-q](https://github.com/nushell/engine-q). We would like your help with this by checking to see if this bug report is still needed in engine-q. Thank you for your patience while we ready the next version of nushell.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@ -38,22 +38,20 @@ body:
|
||||
id: config
|
||||
attributes:
|
||||
label: Configuration
|
||||
description: "Please run `> version | pivot` and paste the output to show OS, features, etc"
|
||||
description: "Please run `version | pivot key value | to md --pretty` and paste the output to show OS, features, etc."
|
||||
placeholder: |
|
||||
> version | pivot key value | to md
|
||||
╭───┬────────────────────┬───────────────────────────────────────────────────────────────────────╮
|
||||
│ # │ key │ value │
|
||||
├───┼────────────────────┼───────────────────────────────────────────────────────────────────────┤
|
||||
│ 0 │ version │ 0.24.1 │
|
||||
│ 1 │ build_os │ macos-x86_64 │
|
||||
│ 2 │ rust_version │ rustc 1.48.0 │
|
||||
│ 3 │ cargo_version │ cargo 1.48.0 │
|
||||
│ 4 │ pkg_version │ 0.24.1 │
|
||||
│ 5 │ build_time │ 2020-12-18 09:54:09 │
|
||||
│ 6 │ build_rust_channel │ release │
|
||||
│ 7 │ features │ ctrlc, default, directories, dirs, git, ichwh, rich-benchmark, │
|
||||
│ │ │ rustyline, term, uuid, which, zip │
|
||||
╰───┴────────────────────┴───────────────────────────────────────────────────────────────────────╯
|
||||
> version | pivot key value | to md --pretty
|
||||
| key | value |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| version | 0.40.0 |
|
||||
| build_os | linux-x86_64 |
|
||||
| rust_version | rustc 1.56.1 |
|
||||
| cargo_version | cargo 1.56.0 |
|
||||
| pkg_version | 0.40.0 |
|
||||
| build_time | 1980-01-01 00:00:00 +00:00 |
|
||||
| build_rust_channel | release |
|
||||
| features | clipboard-cli, ctrlc, dataframe, default, rustyline, term, trash, uuid, which, zip |
|
||||
| installed_plugins | binaryview, chart bar, chart line, fetch, from bson, from sqlite, inc, match, post, ps, query json, s3, selector, start, sys, textview, to bson, to sqlite, tree, xpath |
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
|
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -5,7 +5,7 @@ body:
|
||||
id: problem
|
||||
attributes:
|
||||
label: Related problem
|
||||
description: Is your feature request related to a problem? Please describe.
|
||||
description: Thank you for your feature request. We are working diligently with our community to integrate our latest code base that we call [engine-q](https://github.com/nushell/engine-q). We would like your help with this by checking to see if this feature request is still needed in engine-q. Thank you for your patience while we ready the next version of nushell.
|
||||
placeholder: |
|
||||
A clear and concise description of what the problem is.
|
||||
Example: I am trying to do [...] but [...]
|
||||
|
118
.github/workflows/docker-publish.yml
vendored
118
.github/workflows/docker-publish.yml
vendored
@ -1,118 +0,0 @@
|
||||
name: Publish consumable Docker images
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ['v?[0-9]+.[0-9]+.[0-9]+*']
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch:
|
||||
- x86_64-unknown-linux-musl
|
||||
- x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install rust-embedded/cross
|
||||
env: { VERSION: v0.1.16 }
|
||||
run: >-
|
||||
wget -nv https://github.com/rust-embedded/cross/releases/download/${VERSION}/cross-${VERSION}-x86_64-unknown-linux-gnu.tar.gz
|
||||
-O- | sudo tar xz -C /usr/local/bin/
|
||||
- name: compile for specific target
|
||||
env: { arch: '${{ matrix.arch }}' }
|
||||
run: |
|
||||
cross build --target ${{ matrix.arch }} --release
|
||||
# leave only the executable file
|
||||
rm -frd target/${{ matrix.arch }}/release/{*/*,*.d,*.rlib,.fingerprint}
|
||||
find . -empty -delete
|
||||
- uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: ${{ matrix.arch }}
|
||||
path: target/${{ matrix.arch }}/release
|
||||
|
||||
docker:
|
||||
name: Build and publish docker images
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_REGISTRY: quay.io/nushell
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_REGISTRY }}
|
||||
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||
strategy:
|
||||
matrix:
|
||||
tag:
|
||||
- alpine
|
||||
- slim
|
||||
- debian
|
||||
- glibc-busybox
|
||||
- musl-busybox
|
||||
- musl-distroless
|
||||
- glibc-distroless
|
||||
- glibc
|
||||
- musl
|
||||
include:
|
||||
- { tag: alpine, base-image: alpine, arch: x86_64-unknown-linux-musl, plugin: true, use-patch: false}
|
||||
- { tag: slim, base-image: 'debian:stable-slim', arch: x86_64-unknown-linux-gnu, plugin: true, use-patch: false}
|
||||
- { tag: debian, base-image: debian, arch: x86_64-unknown-linux-gnu, plugin: true, use-patch: false}
|
||||
- { tag: glibc-busybox, base-image: 'busybox:glibc', arch: x86_64-unknown-linux-gnu, plugin: false, use-patch: true }
|
||||
- { tag: musl-busybox, base-image: 'busybox:musl', arch: x86_64-unknown-linux-musl, plugin: false, use-patch: false}
|
||||
- { tag: musl-distroless, base-image: 'gcr.io/distroless/static', arch: x86_64-unknown-linux-musl, plugin: false, use-patch: false}
|
||||
- { tag: glibc-distroless, base-image: 'gcr.io/distroless/cc', arch: x86_64-unknown-linux-gnu, plugin: false, use-patch: true }
|
||||
- { tag: glibc, base-image: scratch, arch: x86_64-unknown-linux-gnu, plugin: false, use-patch: false}
|
||||
- { tag: musl, base-image: scratch, arch: x86_64-unknown-linux-musl, plugin: false, use-patch: false}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@master
|
||||
with: { name: '${{ matrix.arch }}', path: target/release }
|
||||
- name: Build and publish exact version
|
||||
run: |-
|
||||
export DOCKER_TAG=${GITHUB_REF##*/}-${{ matrix.tag }}
|
||||
export NU_BINS=target/release/$( [ ${{ matrix.plugin }} = true ] && echo nu* || echo nu )
|
||||
export PATCH=$([ ${{ matrix.use-patch }} = true ] && echo .${{ matrix.tag }} || echo '')
|
||||
chmod +x $NU_BINS
|
||||
|
||||
echo ${DOCKER_PASSWORD} | docker login ${DOCKER_REGISTRY} -u ${DOCKER_USER} --password-stdin
|
||||
docker-compose --file docker/docker-compose.package.yml build
|
||||
docker-compose --file docker/docker-compose.package.yml push # exact version
|
||||
env:
|
||||
BASE_IMAGE: ${{ matrix.base-image }}
|
||||
|
||||
#region semantics tagging
|
||||
- name: Retag and push with suffixed version
|
||||
run: |-
|
||||
VERSION=${GITHUB_REF##*/}
|
||||
|
||||
latest_version=${VERSION%%%.*}-${{ matrix.tag }}
|
||||
latest_feature=${VERSION%%.*}-${{ matrix.tag }}
|
||||
latest_patch=${VERSION%.*}-${{ matrix.tag }}
|
||||
exact_version=${VERSION}-${{ matrix.tag }}
|
||||
|
||||
tags=( ${latest_version} ${latest_feature} ${latest_patch} ${exact_version} )
|
||||
|
||||
for tag in ${tags[@]}; do
|
||||
docker tag ${DOCKER_REGISTRY}/nu:${VERSION}-${{ matrix.tag }} ${DOCKER_REGISTRY}/nu:${tag}
|
||||
docker push ${DOCKER_REGISTRY}/nu:${tag}
|
||||
done
|
||||
|
||||
# latest version
|
||||
docker tag ${DOCKER_REGISTRY}/nu:${VERSION}-${{ matrix.tag }} ${DOCKER_REGISTRY}/nu:${{ matrix.tag }}
|
||||
docker push ${DOCKER_REGISTRY}/nu:${{ matrix.tag }}
|
||||
|
||||
- name: Retag and push debian as latest
|
||||
if: matrix.tag == 'debian'
|
||||
run: |-
|
||||
VERSION=${GITHUB_REF##*/}
|
||||
|
||||
# ${latest features} ${latest patch} ${exact version}
|
||||
tags=( ${VERSION%%.*} ${VERSION%.*} ${VERSION} )
|
||||
|
||||
for tag in ${tags[@]}; do
|
||||
docker tag ${DOCKER_REGISTRY}/nu:${VERSION}-${{ matrix.tag }} ${DOCKER_REGISTRY}/nu:${tag}
|
||||
docker push ${DOCKER_REGISTRY}/nu:${tag}
|
||||
done
|
||||
|
||||
# latest version
|
||||
docker tag ${DOCKER_REGISTRY}/nu:${{ matrix.tag }} ${DOCKER_REGISTRY}/nu:latest
|
||||
docker push ${DOCKER_REGISTRY}/nu:latest
|
||||
#endregion semantics tagging
|
169
.github/workflows/release.yml
vendored
169
.github/workflows/release.yml
vendored
@ -1,8 +1,9 @@
|
||||
name: Create Release Draft
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags: ['[0-9]+.[0-9]+.[0-9]+*']
|
||||
tags: ["[0-9]+.[0-9]+.[0-9]+*"]
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
@ -28,6 +29,60 @@ jobs:
|
||||
command: build
|
||||
args: --release --all --features=extra
|
||||
|
||||
# - name: Strip binaries (nu)
|
||||
# run: strip target/release/nu
|
||||
|
||||
# - name: Strip binaries (nu_plugin_inc)
|
||||
# run: strip target/release/nu_plugin_inc
|
||||
|
||||
# - name: Strip binaries (nu_plugin_match)
|
||||
# run: strip target/release/nu_plugin_match
|
||||
|
||||
# - name: Strip binaries (nu_plugin_textview)
|
||||
# run: strip target/release/nu_plugin_textview
|
||||
|
||||
# - name: Strip binaries (nu_plugin_binaryview)
|
||||
# run: strip target/release/nu_plugin_binaryview
|
||||
|
||||
# - name: Strip binaries (nu_plugin_chart_bar)
|
||||
# run: strip target/release/nu_plugin_chart_bar
|
||||
|
||||
# - name: Strip binaries (nu_plugin_chart_line)
|
||||
# run: strip target/release/nu_plugin_chart_line
|
||||
|
||||
# - name: Strip binaries (nu_plugin_from_bson)
|
||||
# run: strip target/release/nu_plugin_from_bson
|
||||
|
||||
# - name: Strip binaries (nu_plugin_from_sqlite)
|
||||
# run: strip target/release/nu_plugin_from_sqlite
|
||||
|
||||
# - name: Strip binaries (nu_plugin_from_mp4)
|
||||
# run: strip target/release/nu_plugin_from_mp4
|
||||
|
||||
# - name: Strip binaries (nu_plugin_query_json)
|
||||
# run: strip target/release/nu_plugin_query_json
|
||||
|
||||
# - name: Strip binaries (nu_plugin_s3)
|
||||
# run: strip target/release/nu_plugin_s3
|
||||
|
||||
# - name: Strip binaries (nu_plugin_selector)
|
||||
# run: strip target/release/nu_plugin_selector
|
||||
|
||||
# - name: Strip binaries (nu_plugin_start)
|
||||
# run: strip target/release/nu_plugin_start
|
||||
|
||||
# - name: Strip binaries (nu_plugin_to_bson)
|
||||
# run: strip target/release/nu_plugin_to_bson
|
||||
|
||||
# - name: Strip binaries (nu_plugin_to_sqlite)
|
||||
# run: strip target/release/nu_plugin_to_sqlite
|
||||
|
||||
# - name: Strip binaries (nu_plugin_tree)
|
||||
# run: strip target/release/nu_plugin_tree
|
||||
|
||||
# - name: Strip binaries (nu_plugin_xpath)
|
||||
# run: strip target/release/nu_plugin_xpath
|
||||
|
||||
- name: Create output directory
|
||||
run: mkdir output
|
||||
|
||||
@ -70,6 +125,60 @@ jobs:
|
||||
command: build
|
||||
args: --release --all --features=extra
|
||||
|
||||
# - name: Strip binaries (nu)
|
||||
# run: strip target/release/nu
|
||||
|
||||
# - name: Strip binaries (nu_plugin_inc)
|
||||
# run: strip target/release/nu_plugin_inc
|
||||
|
||||
# - name: Strip binaries (nu_plugin_match)
|
||||
# run: strip target/release/nu_plugin_match
|
||||
|
||||
# - name: Strip binaries (nu_plugin_textview)
|
||||
# run: strip target/release/nu_plugin_textview
|
||||
|
||||
# - name: Strip binaries (nu_plugin_binaryview)
|
||||
# run: strip target/release/nu_plugin_binaryview
|
||||
|
||||
# - name: Strip binaries (nu_plugin_chart_bar)
|
||||
# run: strip target/release/nu_plugin_chart_bar
|
||||
|
||||
# - name: Strip binaries (nu_plugin_chart_line)
|
||||
# run: strip target/release/nu_plugin_chart_line
|
||||
|
||||
# - name: Strip binaries (nu_plugin_from_bson)
|
||||
# run: strip target/release/nu_plugin_from_bson
|
||||
|
||||
# - name: Strip binaries (nu_plugin_from_sqlite)
|
||||
# run: strip target/release/nu_plugin_from_sqlite
|
||||
|
||||
# - name: Strip binaries (nu_plugin_from_mp4)
|
||||
# run: strip target/release/nu_plugin_from_mp4
|
||||
|
||||
# - name: Strip binaries (nu_plugin_query_json)
|
||||
# run: strip target/release/nu_plugin_query_json
|
||||
|
||||
# - name: Strip binaries (nu_plugin_s3)
|
||||
# run: strip target/release/nu_plugin_s3
|
||||
|
||||
# - name: Strip binaries (nu_plugin_selector)
|
||||
# run: strip target/release/nu_plugin_selector
|
||||
|
||||
# - name: Strip binaries (nu_plugin_start)
|
||||
# run: strip target/release/nu_plugin_start
|
||||
|
||||
# - name: Strip binaries (nu_plugin_to_bson)
|
||||
# run: strip target/release/nu_plugin_to_bson
|
||||
|
||||
# - name: Strip binaries (nu_plugin_to_sqlite)
|
||||
# run: strip target/release/nu_plugin_to_sqlite
|
||||
|
||||
# - name: Strip binaries (nu_plugin_tree)
|
||||
# run: strip target/release/nu_plugin_tree
|
||||
|
||||
# - name: Strip binaries (nu_plugin_xpath)
|
||||
# run: strip target/release/nu_plugin_xpath
|
||||
|
||||
- name: Create output directory
|
||||
run: mkdir output
|
||||
|
||||
@ -106,7 +215,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: install
|
||||
args: cargo-wix
|
||||
args: cargo-wix --version 0.3.1
|
||||
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
@ -114,6 +223,60 @@ jobs:
|
||||
command: build
|
||||
args: --release --all --features=extra
|
||||
|
||||
# - name: Strip binaries (nu.exe)
|
||||
# run: strip target/release/nu.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_inc.exe)
|
||||
# run: strip target/release/nu_plugin_inc.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_match.exe)
|
||||
# run: strip target/release/nu_plugin_match.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_textview.exe)
|
||||
# run: strip target/release/nu_plugin_textview.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_binaryview.exe)
|
||||
# run: strip target/release/nu_plugin_binaryview.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_chart_bar.exe)
|
||||
# run: strip target/release/nu_plugin_chart_bar.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_chart_line.exe)
|
||||
# run: strip target/release/nu_plugin_chart_line.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_from_bson.exe)
|
||||
# run: strip target/release/nu_plugin_from_bson.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_from_sqlite.exe)
|
||||
# run: strip target/release/nu_plugin_from_sqlite.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_from_mp4.exe)
|
||||
# run: strip target/release/nu_plugin_from_mp4.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_query_json.exe)
|
||||
# run: strip target/release/nu_plugin_query_json.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_s3.exe)
|
||||
# run: strip target/release/nu_plugin_s3.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_selector.exe)
|
||||
# run: strip target/release/nu_plugin_selector.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_start.exe)
|
||||
# run: strip target/release/nu_plugin_start.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_to_bson.exe)
|
||||
# run: strip target/release/nu_plugin_to_bson.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_to_sqlite.exe)
|
||||
# run: strip target/release/nu_plugin_to_sqlite.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_tree.exe)
|
||||
# run: strip target/release/nu_plugin_tree.exe
|
||||
|
||||
# - name: Strip binaries (nu_plugin_xpath.exe)
|
||||
# run: strip target/release/nu_plugin_xpath.exe
|
||||
|
||||
- name: Create output directory
|
||||
run: mkdir output
|
||||
|
||||
@ -274,7 +437,7 @@ jobs:
|
||||
with:
|
||||
name: windows-installer
|
||||
path: ./
|
||||
|
||||
|
||||
- name: Upload Windows installer
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
|
11
.github/workflows/stale.yml
vendored
11
.github/workflows/stale.yml
vendored
@ -19,11 +19,10 @@ jobs:
|
||||
operations-per-run: 520
|
||||
enable-statistics: true
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue is being marked stale because it has been open for 90 days without activity. If you feel that this is in error, please comment below and we will keep it marked as active.'
|
||||
stale-pr-message: 'This PR is being marked stale because it has been open for 45 days without activity. If this PR is still active, please comment below and we will keep it marked as active.'
|
||||
close-issue-message: 'This issue has been marked stale for more than 10 days without activity. Closing this issue, but if you find that the issue is still valid, please reopen.'
|
||||
close-pr-message: 'This PR has been marked stale for more than 10 days without activity. Closing this PR, but if you are still working on it, please reopen.'
|
||||
close-issue-message: 'This issue has been marked stale for more than 100000 days without activity. Closing this issue, but if you find that the issue is still valid, please reopen.'
|
||||
close-pr-message: 'This PR has been marked stale for more than 100 days without activity. Closing this PR, but if you are still working on it, please reopen.'
|
||||
days-before-issue-stale: 90
|
||||
days-before-pr-stale: 45
|
||||
days-before-issue-close: 10
|
||||
days-before-pr-close: 10
|
||||
days-before-issue-close: 100000
|
||||
days-before-pr-close: 100
|
||||
exempt-issue-labels: 'exempt,keep'
|
||||
|
2
.gitpod.Dockerfile
vendored
2
.gitpod.Dockerfile
vendored
@ -2,7 +2,7 @@ FROM gitpod/workspace-full
|
||||
|
||||
# Gitpod will not rebuild Nushell's dev image unless *some* change is made to this Dockerfile.
|
||||
# To force a rebuild, simply increase this counter:
|
||||
ENV TRIGGER_REBUILD 1
|
||||
ENV TRIGGER_REBUILD 2
|
||||
|
||||
USER gitpod
|
||||
|
||||
|
3550
Cargo.lock
generated
3550
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
103
Cargo.toml
103
Cargo.toml
@ -10,7 +10,7 @@ license = "MIT"
|
||||
name = "nu"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
version = "0.36.0"
|
||||
version = "0.43.0"
|
||||
|
||||
[workspace]
|
||||
members = ["crates/*/"]
|
||||
@ -18,38 +18,34 @@ members = ["crates/*/"]
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nu-cli = { version = "0.36.0", path="./crates/nu-cli", default-features=false }
|
||||
nu-command = { version = "0.36.0", path="./crates/nu-command" }
|
||||
nu-completion = { version = "0.36.0", path="./crates/nu-completion" }
|
||||
nu-data = { version = "0.36.0", path="./crates/nu-data" }
|
||||
nu-engine = { version = "0.36.0", path="./crates/nu-engine" }
|
||||
nu-errors = { version = "0.36.0", path="./crates/nu-errors" }
|
||||
nu-parser = { version = "0.36.0", path="./crates/nu-parser" }
|
||||
nu-path = { version = "0.36.0", path="./crates/nu-path" }
|
||||
nu-plugin = { version = "0.36.0", path="./crates/nu-plugin" }
|
||||
nu-protocol = { version = "0.36.0", path="./crates/nu-protocol" }
|
||||
nu-source = { version = "0.36.0", path="./crates/nu-source" }
|
||||
nu-value-ext = { version = "0.36.0", path="./crates/nu-value-ext" }
|
||||
nu-cli = { version = "0.43.0", path="./crates/nu-cli", default-features=false }
|
||||
nu-command = { version = "0.43.0", path="./crates/nu-command" }
|
||||
nu-completion = { version = "0.43.0", path="./crates/nu-completion" }
|
||||
nu-data = { version = "0.43.0", path="./crates/nu-data" }
|
||||
nu-engine = { version = "0.43.0", path="./crates/nu-engine" }
|
||||
nu-errors = { version = "0.43.0", path="./crates/nu-errors" }
|
||||
nu-parser = { version = "0.43.0", path="./crates/nu-parser" }
|
||||
nu-path = { version = "0.43.0", path="./crates/nu-path" }
|
||||
nu-plugin = { version = "0.43.0", path="./crates/nu-plugin" }
|
||||
nu-protocol = { version = "0.43.0", path="./crates/nu-protocol" }
|
||||
nu-source = { version = "0.43.0", path="./crates/nu-source" }
|
||||
nu-value-ext = { version = "0.43.0", path="./crates/nu-value-ext" }
|
||||
|
||||
nu_plugin_binaryview = { version = "0.36.0", path="./crates/nu_plugin_binaryview", optional=true }
|
||||
nu_plugin_chart = { version = "0.36.0", path="./crates/nu_plugin_chart", optional=true }
|
||||
nu_plugin_fetch = { version = "0.36.0", path="./crates/nu_plugin_fetch", optional=true }
|
||||
nu_plugin_from_bson = { version = "0.36.0", path="./crates/nu_plugin_from_bson", optional=true }
|
||||
nu_plugin_from_sqlite = { version = "0.36.0", path="./crates/nu_plugin_from_sqlite", optional=true }
|
||||
nu_plugin_inc = { version = "0.36.0", path="./crates/nu_plugin_inc", optional=true }
|
||||
nu_plugin_match = { version = "0.36.0", path="./crates/nu_plugin_match", optional=true }
|
||||
nu_plugin_post = { version = "0.36.0", path="./crates/nu_plugin_post", optional=true }
|
||||
nu_plugin_ps = { version = "0.36.0", path="./crates/nu_plugin_ps", optional=true }
|
||||
nu_plugin_query_json = { version = "0.36.0", path="./crates/nu_plugin_query_json", optional=true }
|
||||
nu_plugin_s3 = { version = "0.36.0", path="./crates/nu_plugin_s3", optional=true }
|
||||
nu_plugin_selector = { version = "0.36.0", path="./crates/nu_plugin_selector", optional=true }
|
||||
nu_plugin_start = { version = "0.36.0", path="./crates/nu_plugin_start", optional=true }
|
||||
nu_plugin_sys = { version = "0.36.0", path="./crates/nu_plugin_sys", optional=true }
|
||||
nu_plugin_textview = { version = "0.36.0", path="./crates/nu_plugin_textview", optional=true }
|
||||
nu_plugin_to_bson = { version = "0.36.0", path="./crates/nu_plugin_to_bson", optional=true }
|
||||
nu_plugin_to_sqlite = { version = "0.36.0", path="./crates/nu_plugin_to_sqlite", optional=true }
|
||||
nu_plugin_tree = { version = "0.36.0", path="./crates/nu_plugin_tree", optional=true }
|
||||
nu_plugin_xpath = { version = "0.36.0", path="./crates/nu_plugin_xpath", optional=true }
|
||||
nu_plugin_binaryview = { version = "0.43.0", path="./crates/nu_plugin_binaryview", optional=true }
|
||||
nu_plugin_chart = { version = "0.43.0", path="./crates/nu_plugin_chart", optional=true }
|
||||
nu_plugin_from_bson = { version = "0.43.0", path="./crates/nu_plugin_from_bson", optional=true }
|
||||
nu_plugin_from_sqlite = { version = "0.43.0", path="./crates/nu_plugin_from_sqlite", optional=true }
|
||||
nu_plugin_inc = { version = "0.43.0", path="./crates/nu_plugin_inc", optional=true }
|
||||
nu_plugin_match = { version = "0.43.0", path="./crates/nu_plugin_match", optional=true }
|
||||
nu_plugin_query_json = { version = "0.43.0", path="./crates/nu_plugin_query_json", optional=true }
|
||||
nu_plugin_s3 = { version = "0.43.0", path="./crates/nu_plugin_s3", optional=true }
|
||||
nu_plugin_selector = { version = "0.43.0", path="./crates/nu_plugin_selector", optional=true }
|
||||
nu_plugin_start = { version = "0.43.0", path="./crates/nu_plugin_start", optional=true }
|
||||
nu_plugin_textview = { version = "0.43.0", path="./crates/nu_plugin_textview", optional=true }
|
||||
nu_plugin_to_bson = { version = "0.43.0", path="./crates/nu_plugin_to_bson", optional=true }
|
||||
nu_plugin_to_sqlite = { version = "0.43.0", path="./crates/nu_plugin_to_sqlite", optional=true }
|
||||
nu_plugin_tree = { version = "0.43.0", path="./crates/nu_plugin_tree", optional=true }
|
||||
nu_plugin_xpath = { version = "0.43.0", path="./crates/nu_plugin_xpath", optional=true }
|
||||
|
||||
# Required to bootstrap the main binary
|
||||
ctrlc = { version="3.1.7", optional=true }
|
||||
@ -57,8 +53,7 @@ futures = { version="0.3.12", features=["compat", "io-compat"] }
|
||||
itertools = "0.10.0"
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { version = "0.36.0", path="./crates/nu-test-support" }
|
||||
dunce = "1.0.1"
|
||||
nu-test-support = { version = "0.43.0", path="./crates/nu-test-support" }
|
||||
serial_test = "0.5.1"
|
||||
hamcrest2 = "0.3.0"
|
||||
rstest = "0.10.0"
|
||||
@ -66,6 +61,8 @@ rstest = "0.10.0"
|
||||
[build-dependencies]
|
||||
|
||||
[features]
|
||||
fetch-support = ["nu-command/fetch", "nu-command/post"]
|
||||
sys-support = ["nu-command/sys", "nu-command/ps"]
|
||||
ctrlc-support = ["nu-cli/ctrlc", "nu-command/ctrlc"]
|
||||
rustyline-support = ["nu-cli/rustyline-support", "nu-command/rustyline-support"]
|
||||
term-support = ["nu-command/term"]
|
||||
@ -74,15 +71,13 @@ which-support = ["nu-command/which", "nu-engine/which"]
|
||||
|
||||
default = [
|
||||
"nu-cli/shadow-rs",
|
||||
"sys",
|
||||
"ps",
|
||||
"sys-support",
|
||||
"ctrlc-support",
|
||||
"which-support",
|
||||
"term-support",
|
||||
"rustyline-support",
|
||||
"match",
|
||||
"post",
|
||||
"fetch",
|
||||
"fetch-support",
|
||||
"zip-support",
|
||||
"dataframe",
|
||||
]
|
||||
@ -94,7 +89,6 @@ extra = [
|
||||
"inc",
|
||||
"tree",
|
||||
"textview",
|
||||
"clipboard-cli",
|
||||
"trash-support",
|
||||
"uuid-support",
|
||||
"start",
|
||||
@ -110,19 +104,14 @@ extra = [
|
||||
wasi = ["inc", "match", "match", "tree", "rustyline-support"]
|
||||
|
||||
# Stable (Default)
|
||||
fetch = ["nu_plugin_fetch"]
|
||||
inc = ["nu_plugin_inc"]
|
||||
match = ["nu_plugin_match"]
|
||||
post = ["nu_plugin_post"]
|
||||
ps = ["nu_plugin_ps"]
|
||||
sys = ["nu_plugin_sys"]
|
||||
textview = ["nu_plugin_textview"]
|
||||
|
||||
# Extra
|
||||
binaryview = ["nu_plugin_binaryview"]
|
||||
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
|
||||
chart = ["nu_plugin_chart"]
|
||||
clipboard-cli = ["nu-command/clipboard-cli"]
|
||||
query-json = ["nu_plugin_query_json"]
|
||||
s3 = ["nu_plugin_s3"]
|
||||
selector = ["nu_plugin_selector"]
|
||||
@ -136,9 +125,6 @@ tree = ["nu_plugin_tree"]
|
||||
xpath = ["nu_plugin_xpath"]
|
||||
zip-support = ["nu-command/zip"]
|
||||
|
||||
#This is disabled in extra for now
|
||||
table-pager = ["nu-command/table-pager"]
|
||||
|
||||
#dataframe feature for nushell
|
||||
dataframe = [
|
||||
"nu-engine/dataframe",
|
||||
@ -146,12 +132,11 @@ dataframe = [
|
||||
"nu-command/dataframe",
|
||||
"nu-value-ext/dataframe",
|
||||
"nu-data/dataframe",
|
||||
"nu_plugin_post/dataframe",
|
||||
"nu_plugin_to_bson/dataframe",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z" # Optimize for size.
|
||||
opt-level = "s" # Optimize for size.
|
||||
|
||||
# Core plugins that ship with `cargo install nu` by default
|
||||
# Currently, Cargo limits us to installing only one binary
|
||||
@ -166,31 +151,11 @@ name = "nu_plugin_core_inc"
|
||||
path = "src/plugins/nu_plugin_core_inc.rs"
|
||||
required-features = ["inc"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_core_ps"
|
||||
path = "src/plugins/nu_plugin_core_ps.rs"
|
||||
required-features = ["ps"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_core_sys"
|
||||
path = "src/plugins/nu_plugin_core_sys.rs"
|
||||
required-features = ["sys"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_core_fetch"
|
||||
path = "src/plugins/nu_plugin_core_fetch.rs"
|
||||
required-features = ["fetch"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_core_match"
|
||||
path = "src/plugins/nu_plugin_core_match.rs"
|
||||
required-features = ["match"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_core_post"
|
||||
path = "src/plugins/nu_plugin_core_post.rs"
|
||||
required-features = ["post"]
|
||||
|
||||
# Extra plugins
|
||||
|
||||
[[bin]]
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 - 2021 Yehuda Katz, Jonathan Turner
|
||||
Copyright (c) 2019 - 2021 Nushell Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
49
README.md
49
README.md
@ -68,7 +68,7 @@ cargo install nu
|
||||
To install Nu via the [Windows Package Manager](https://aka.ms/winget-cli):
|
||||
|
||||
```shell
|
||||
winget install nu
|
||||
winget install nushell
|
||||
```
|
||||
|
||||
You can also build Nu yourself with all the bells and whistles (be sure to have installed the [dependencies](https://www.nushell.sh/book/installation.html#dependencies) for your platform), once you have checked out this repo with git:
|
||||
@ -76,53 +76,6 @@ You can also build Nu yourself with all the bells and whistles (be sure to have
|
||||
```shell
|
||||
cargo build --workspace --features=extra
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
#### Quickstart
|
||||
|
||||
Want to try Nu right away? Execute the following to get started.
|
||||
|
||||
```shell
|
||||
docker run -it quay.io/nushell/nu:latest
|
||||
```
|
||||
|
||||
#### Guide
|
||||
|
||||
If you want to pull a pre-built container, you can browse tags for the [nushell organization](https://quay.io/organization/nushell)
|
||||
on Quay.io. Pulling a container would come down to:
|
||||
|
||||
```shell
|
||||
docker pull quay.io/nushell/nu
|
||||
docker pull quay.io/nushell/nu-base
|
||||
```
|
||||
|
||||
Both "nu-base" and "nu" provide the nu binary, however, nu-base also includes the source code at `/code`
|
||||
in the container and all dependencies.
|
||||
|
||||
Optionally, you can also build the containers locally using the [dockerfiles provided](docker):
|
||||
To build the base image:
|
||||
|
||||
```shell
|
||||
docker build -f docker/Dockerfile.nu-base -t nushell/nu-base .
|
||||
```
|
||||
|
||||
And then to build the smaller container (using a Multistage build):
|
||||
|
||||
```shell
|
||||
docker build -f docker/Dockerfile -t nushell/nu .
|
||||
```
|
||||
|
||||
Either way, you can run either container as follows:
|
||||
|
||||
```shell
|
||||
docker run -it nushell/nu-base
|
||||
docker run -it nushell/nu
|
||||
/> exit
|
||||
```
|
||||
|
||||
The second container is a bit smaller if the size is important to you.
|
||||
|
||||
### Packaging status
|
||||
|
||||
[](https://repology.org/project/nushell/versions)
|
||||
|
@ -10,4 +10,4 @@ Foundational libraries are split into two kinds of crates:
|
||||
Plugins are likewise also split into two types:
|
||||
|
||||
* Core plugins - plugins that provide part of the default experience of Nu, including access to the system properties, processes, and web-connectivity features.
|
||||
* Extra plugins - these plugins run a wide range of differnt capabilities like working with different file types, charting, viewing binary data, and more.
|
||||
* Extra plugins - these plugins run a wide range of different capabilities like working with different file types, charting, viewing binary data, and more.
|
||||
|
@ -9,7 +9,7 @@ description = "Library for ANSI terminal colors and styles (bold, underline)"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-ansi-term"
|
||||
version = "0.36.0"
|
||||
version = "0.43.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
@ -21,7 +21,6 @@ derive_serde_style = ["serde"]
|
||||
[dependencies]
|
||||
overload = "0.1.1"
|
||||
serde = { version="1.0.90", features=["derive"], optional=true }
|
||||
itertools = "0.10.0"
|
||||
|
||||
# [dependencies.serde]
|
||||
# version = "1.0.90"
|
||||
|
@ -93,7 +93,7 @@ pub static RESET: &str = "\x1B[0m";
|
||||
|
||||
impl Color {
|
||||
fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
|
||||
match *self {
|
||||
match self {
|
||||
Color::Black => write!(f, "30"),
|
||||
Color::Red => write!(f, "31"),
|
||||
Color::Green => write!(f, "32"),
|
||||
@ -103,8 +103,8 @@ impl Color {
|
||||
Color::Magenta => write!(f, "35"),
|
||||
Color::Cyan => write!(f, "36"),
|
||||
Color::White => write!(f, "37"),
|
||||
Color::Fixed(num) => write!(f, "38;5;{}", &num),
|
||||
Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
|
||||
Color::Fixed(num) => write!(f, "38;5;{}", num),
|
||||
Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", r, g, b),
|
||||
Color::DarkGray => write!(f, "90"),
|
||||
Color::LightRed => write!(f, "91"),
|
||||
Color::LightGreen => write!(f, "92"),
|
||||
@ -118,7 +118,7 @@ impl Color {
|
||||
}
|
||||
|
||||
fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
|
||||
match *self {
|
||||
match self {
|
||||
Color::Black => write!(f, "40"),
|
||||
Color::Red => write!(f, "41"),
|
||||
Color::Green => write!(f, "42"),
|
||||
@ -128,8 +128,8 @@ impl Color {
|
||||
Color::Magenta => write!(f, "45"),
|
||||
Color::Cyan => write!(f, "46"),
|
||||
Color::White => write!(f, "47"),
|
||||
Color::Fixed(num) => write!(f, "48;5;{}", &num),
|
||||
Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
|
||||
Color::Fixed(num) => write!(f, "48;5;{}", num),
|
||||
Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", r, g, b),
|
||||
Color::DarkGray => write!(f, "100"),
|
||||
Color::LightRed => write!(f, "101"),
|
||||
Color::LightGreen => write!(f, "102"),
|
||||
|
@ -297,7 +297,7 @@ mod tests {
|
||||
fn no_control_codes_for_plain() {
|
||||
let one = Style::default().paint("one");
|
||||
let two = Style::default().paint("two");
|
||||
let output = format!("{}", AnsiStrings(&[one, two]));
|
||||
assert_eq!(&*output, "onetwo");
|
||||
let output = AnsiStrings(&[one, two]).to_string();
|
||||
assert_eq!(output, "onetwo");
|
||||
}
|
||||
}
|
||||
|
@ -231,7 +231,6 @@
|
||||
|
||||
#![crate_name = "nu_ansi_term"]
|
||||
#![crate_type = "rlib"]
|
||||
#![crate_type = "dylib"]
|
||||
#![warn(missing_copy_implementations)]
|
||||
// #![warn(missing_docs)]
|
||||
#![warn(trivial_casts, trivial_numeric_casts)]
|
||||
|
@ -602,18 +602,18 @@ mod serde_json_tests {
|
||||
|
||||
#[test]
|
||||
fn color_deserialization() {
|
||||
let colors = &[
|
||||
let colors = [
|
||||
Color::Red,
|
||||
Color::Blue,
|
||||
Color::Rgb(123, 123, 123),
|
||||
Color::Fixed(255),
|
||||
];
|
||||
|
||||
for color in colors.iter() {
|
||||
for color in colors {
|
||||
let serialized = serde_json::to_string(&color).unwrap();
|
||||
let deserialized: Color = serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
assert_eq!(color, &deserialized);
|
||||
assert_eq!(color, deserialized);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,6 @@ mod test {
|
||||
assert_eq!(unstyled_len(&a), 18);
|
||||
|
||||
let l2 = [Black.paint("st"), Red.paint("-second"), White.paint("-t")];
|
||||
assert_eq!(sub_string(3, 11, &a).as_slice(), &l2);
|
||||
assert_eq!(sub_string(3, 11, &a), l2);
|
||||
}
|
||||
}
|
||||
|
@ -4,23 +4,24 @@ description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-cli"
|
||||
version = "0.36.0"
|
||||
version = "0.43.0"
|
||||
build = "build.rs"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-completion = { version = "0.36.0", path="../nu-completion" }
|
||||
nu-command = { version = "0.36.0", path="../nu-command" }
|
||||
nu-data = { version = "0.36.0", path="../nu-data" }
|
||||
nu-engine = { version = "0.36.0", path="../nu-engine" }
|
||||
nu-errors = { version = "0.36.0", path="../nu-errors" }
|
||||
nu-parser = { version = "0.36.0", path="../nu-parser" }
|
||||
nu-protocol = { version = "0.36.0", path="../nu-protocol" }
|
||||
nu-source = { version = "0.36.0", path="../nu-source" }
|
||||
nu-stream = { version = "0.36.0", path="../nu-stream" }
|
||||
nu-ansi-term = { version = "0.36.0", path="../nu-ansi-term" }
|
||||
nu-completion = { version = "0.43.0", path="../nu-completion" }
|
||||
nu-command = { version = "0.43.0", path="../nu-command" }
|
||||
nu-data = { version = "0.43.0", path="../nu-data" }
|
||||
nu-engine = { version = "0.43.0", path="../nu-engine" }
|
||||
nu-errors = { version = "0.43.0", path="../nu-errors" }
|
||||
nu-parser = { version = "0.43.0", path="../nu-parser" }
|
||||
nu-protocol = { version = "0.43.0", path="../nu-protocol" }
|
||||
nu-source = { version = "0.43.0", path="../nu-source" }
|
||||
nu-stream = { version = "0.43.0", path="../nu-stream" }
|
||||
nu-ansi-term = { version = "0.43.0", path="../nu-ansi-term" }
|
||||
nu-path = { version = "0.43.0", path="../nu-path" }
|
||||
|
||||
indexmap ="1.6.1"
|
||||
log = "0.4.14"
|
||||
@ -28,13 +29,13 @@ pretty_env_logger = "0.4.0"
|
||||
strip-ansi-escapes = "0.1.0"
|
||||
rustyline = { version="9.0.0", optional=true }
|
||||
ctrlc = { version="3.1.7", optional=true }
|
||||
shadow-rs = { version="0.6", default-features=false, optional=true }
|
||||
shadow-rs = { version = "0.8.1", default-features = false, optional = true }
|
||||
serde = { version="1.0.123", features=["derive"] }
|
||||
serde_yaml = "0.8.16"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = "0.6"
|
||||
shadow-rs = "0.8.1"
|
||||
|
||||
[features]
|
||||
default = ["shadow-rs"]
|
||||
|
@ -508,14 +508,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_use_loglevels() -> Result<(), ShellError> {
|
||||
for level in &["error", "warn", "info", "debug", "trace"] {
|
||||
for level in ["error", "warn", "info", "debug", "trace"] {
|
||||
let ui = cli_app();
|
||||
let args = format!("nu --loglevel={}", *level);
|
||||
ui.parse(&args)?;
|
||||
assert_eq!(ui.loglevel().unwrap(), Ok(level.to_string()));
|
||||
|
||||
let ui = cli_app();
|
||||
let args = format!("nu -l {}", *level);
|
||||
let args = format!("nu --loglevel={}", level);
|
||||
ui.parse(&args)?;
|
||||
assert_eq!(ui.loglevel().unwrap(), Ok(level.to_string()));
|
||||
}
|
||||
@ -530,6 +525,17 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_be_login() -> Result<(), ShellError> {
|
||||
let ui = cli_app();
|
||||
ui.parse("nu -l")?;
|
||||
|
||||
let ui = cli_app();
|
||||
ui.parse("nu --login")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_be_passed_nu_scripts() -> Result<(), ShellError> {
|
||||
let ui = cli_app();
|
||||
@ -541,11 +547,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_use_test_binaries() -> Result<(), ShellError> {
|
||||
for binarie_name in &[
|
||||
for binarie_name in [
|
||||
"echo_env", "cococo", "iecho", "fail", "nonu", "chop", "repeater", "meow",
|
||||
] {
|
||||
let ui = cli_app();
|
||||
let args = format!("nu --testbin={}", *binarie_name);
|
||||
let args = format!("nu --testbin={}", binarie_name);
|
||||
ui.parse(&args)?;
|
||||
assert_eq!(ui.testbin().unwrap(), Ok(binarie_name.to_string()));
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ impl Options {
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &str) -> Option<Value> {
|
||||
self.inner.borrow().get(key).map(Clone::clone)
|
||||
self.inner.borrow().get(key).cloned()
|
||||
}
|
||||
|
||||
pub fn put(&self, key: &str, value: Value) {
|
||||
|
@ -67,48 +67,37 @@ impl OptionsParser for NuParser {
|
||||
}
|
||||
};
|
||||
|
||||
let value =
|
||||
value
|
||||
.map(|v| match k.as_ref() {
|
||||
"testbin" => {
|
||||
if let Ok(name) = v.as_string() {
|
||||
if testbins().iter().any(|n| name == *n) {
|
||||
Some(v)
|
||||
} else {
|
||||
Some(
|
||||
UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error(
|
||||
format!("{} is not supported.", name),
|
||||
),
|
||||
)
|
||||
.into_value(v.tag),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Some(v)
|
||||
}
|
||||
let value = value.map(|v| match k.as_ref() {
|
||||
"testbin" => {
|
||||
if let Ok(name) = v.as_string() {
|
||||
if testbins().iter().any(|n| name == *n) {
|
||||
v
|
||||
} else {
|
||||
UntaggedValue::Error(ShellError::untagged_runtime_error(
|
||||
format!("{} is not supported.", name),
|
||||
))
|
||||
.into_value(v.tag)
|
||||
}
|
||||
"loglevel" => {
|
||||
if let Ok(name) = v.as_string() {
|
||||
if loglevels().iter().any(|n| name == *n) {
|
||||
Some(v)
|
||||
} else {
|
||||
Some(
|
||||
UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error(
|
||||
format!("{} is not supported.", name),
|
||||
),
|
||||
)
|
||||
.into_value(v.tag),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Some(v)
|
||||
}
|
||||
} else {
|
||||
v
|
||||
}
|
||||
}
|
||||
"loglevel" => {
|
||||
if let Ok(name) = v.as_string() {
|
||||
if loglevels().iter().any(|n| name == *n) {
|
||||
v
|
||||
} else {
|
||||
UntaggedValue::Error(ShellError::untagged_runtime_error(
|
||||
format!("{} is not supported.", name),
|
||||
))
|
||||
.into_value(v.tag)
|
||||
}
|
||||
_ => Some(v),
|
||||
})
|
||||
.flatten();
|
||||
} else {
|
||||
v
|
||||
}
|
||||
}
|
||||
_ => v,
|
||||
});
|
||||
|
||||
if let Some(value) = value {
|
||||
options.put(k, value);
|
||||
|
@ -24,6 +24,7 @@ use rustyline::{self, error::ReadlineError};
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::ParserScope;
|
||||
use nu_path::expand_tilde;
|
||||
use nu_protocol::{hir::ExternalRedirection, ConfigPath, UntaggedValue, Value};
|
||||
|
||||
use log::trace;
|
||||
@ -33,7 +34,7 @@ use std::path::PathBuf;
|
||||
|
||||
// Name of environment variable where the prompt could be stored
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
const PROMPT_STRING: &str = "PROMPT_STRING";
|
||||
const PROMPT_COMMAND: &str = "PROMPT_COMMAND";
|
||||
|
||||
pub fn search_paths() -> Vec<std::path::PathBuf> {
|
||||
use std::env;
|
||||
@ -54,7 +55,7 @@ pub fn search_paths() -> Vec<std::path::PathBuf> {
|
||||
{
|
||||
for pipeline in pipelines {
|
||||
if let Ok(plugin_dir) = pipeline.as_string() {
|
||||
search_paths.push(PathBuf::from(plugin_dir));
|
||||
search_paths.push(expand_tilde(plugin_dir));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,10 +91,10 @@ pub fn run_script_file(
|
||||
fn default_prompt_string(cwd: &str) -> String {
|
||||
format!(
|
||||
"{}{}{}{}{}{}> ",
|
||||
Color::Green.bold().prefix().to_string(),
|
||||
Color::Green.bold().prefix(),
|
||||
cwd,
|
||||
nu_ansi_term::ansi::RESET,
|
||||
Color::Cyan.bold().prefix().to_string(),
|
||||
Color::Cyan.bold().prefix(),
|
||||
current_branch(),
|
||||
nu_ansi_term::ansi::RESET
|
||||
)
|
||||
@ -165,7 +166,10 @@ pub fn cli(
|
||||
// Store cmd duration in an env var
|
||||
context.scope.add_env_var(
|
||||
"CMD_DURATION_MS",
|
||||
format!("{}", startup_commands_start_time.elapsed().as_millis()),
|
||||
startup_commands_start_time
|
||||
.elapsed()
|
||||
.as_millis()
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
if options.perf {
|
||||
@ -298,9 +302,9 @@ pub fn cli(
|
||||
|
||||
let cwd = context.shell_manager().path();
|
||||
|
||||
// Check if the PROMPT_STRING env variable is set. This env variable
|
||||
// Check if the PROMPT_COMMAND env variable is set. This env variable
|
||||
// contains nu code that is used to overwrite the prompt
|
||||
let colored_prompt = match context.scope.get_env(PROMPT_STRING) {
|
||||
let colored_prompt = match context.scope.get_env(PROMPT_COMMAND) {
|
||||
Some(env_prompt) => evaluate_prompt_string(&env_prompt, &context, &cwd),
|
||||
None => {
|
||||
if let Some(prompt) = &prompt {
|
||||
@ -353,7 +357,7 @@ pub fn cli(
|
||||
// Store cmd duration in an env var
|
||||
context.scope.add_env_var(
|
||||
"CMD_DURATION_MS",
|
||||
format!("{}", cmd_start_time.elapsed().as_millis()),
|
||||
cmd_start_time.elapsed().as_millis().to_string(),
|
||||
);
|
||||
|
||||
match line {
|
||||
@ -368,7 +372,7 @@ pub fn cli(
|
||||
LineResult::ClearHistory => {
|
||||
if options.save_history {
|
||||
rl.clear_history();
|
||||
let _ = rl.append_history(&history_path);
|
||||
std::fs::remove_file(&history_path)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -397,8 +401,7 @@ pub fn cli(
|
||||
.lock()
|
||||
.global_config
|
||||
.as_ref()
|
||||
.map(|cfg| cfg.var("ctrlc_exit"))
|
||||
.flatten()
|
||||
.and_then(|cfg| cfg.var("ctrlc_exit"))
|
||||
.map(|ctrl_c| ctrl_c.is_true())
|
||||
.unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells
|
||||
|
||||
|
@ -461,7 +461,7 @@ pub(crate) fn load_keybindings(
|
||||
if let Ok(contents) = contents {
|
||||
let keybindings: Keybindings = serde_yaml::from_str(&contents)?;
|
||||
// eprintln!("{:#?}", keybindings);
|
||||
for keybinding in keybindings.into_iter() {
|
||||
for keybinding in keybindings {
|
||||
let (k, b) = convert_keybinding(keybinding);
|
||||
// eprintln!("{:?} {:?}", k, b);
|
||||
|
||||
|
@ -1,57 +1,51 @@
|
||||
[package]
|
||||
authors = ["The Nu Project Contributors"]
|
||||
build = "build.rs"
|
||||
description = "CLI for nushell"
|
||||
description = "Commands for Nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-command"
|
||||
version = "0.36.0"
|
||||
version = "0.43.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-data = { version = "0.36.0", path="../nu-data" }
|
||||
nu-engine = { version = "0.36.0", path="../nu-engine" }
|
||||
nu-errors = { version = "0.36.0", path="../nu-errors" }
|
||||
nu-json = { version = "0.36.0", path="../nu-json" }
|
||||
nu-path = { version = "0.36.0", path="../nu-path" }
|
||||
nu-parser = { version = "0.36.0", path="../nu-parser" }
|
||||
nu-plugin = { version = "0.36.0", path="../nu-plugin" }
|
||||
nu-protocol = { version = "0.36.0", path="../nu-protocol" }
|
||||
nu-serde = { version = "0.36.0", path="../nu-serde" }
|
||||
nu-source = { version = "0.36.0", path="../nu-source" }
|
||||
nu-stream = { version = "0.36.0", path="../nu-stream" }
|
||||
nu-table = { version = "0.36.0", path="../nu-table" }
|
||||
nu-test-support = { version = "0.36.0", path="../nu-test-support" }
|
||||
nu-value-ext = { version = "0.36.0", path="../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.36.0", path="../nu-ansi-term" }
|
||||
nu-pretty-hex = { version = "0.36.0", path="../nu-pretty-hex" }
|
||||
nu-data = { version = "0.43.0", path="../nu-data" }
|
||||
nu-engine = { version = "0.43.0", path="../nu-engine" }
|
||||
nu-errors = { version = "0.43.0", path="../nu-errors" }
|
||||
nu-json = { version = "0.43.0", path="../nu-json" }
|
||||
nu-path = { version = "0.43.0", path="../nu-path" }
|
||||
nu-parser = { version = "0.43.0", path="../nu-parser" }
|
||||
nu-plugin = { version = "0.43.0", path="../nu-plugin" }
|
||||
nu-protocol = { version = "0.43.0", path="../nu-protocol" }
|
||||
nu-serde = { version = "0.43.0", path="../nu-serde" }
|
||||
nu-source = { version = "0.43.0", path="../nu-source" }
|
||||
nu-stream = { version = "0.43.0", path="../nu-stream" }
|
||||
nu-table = { version = "0.43.0", path="../nu-table" }
|
||||
nu-test-support = { version = "0.43.0", path="../nu-test-support" }
|
||||
nu-value-ext = { version = "0.43.0", path="../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.43.0", path="../nu-ansi-term" }
|
||||
nu-pretty-hex = { version = "0.43.0", path="../nu-pretty-hex" }
|
||||
|
||||
Inflector = "0.11"
|
||||
arboard = { version="1.1.0", optional=true }
|
||||
url = "2.2.1"
|
||||
mime = "0.3.16"
|
||||
heck = "0.4.0"
|
||||
base64 = "0.13.0"
|
||||
bigdecimal = { package = "bigdecimal-rs", version = "0.2.1", features = ["serde"] }
|
||||
byte-unit = "4.0.9"
|
||||
bytes = "1.0.1"
|
||||
bigdecimal = { version = "0.3.0", features = ["serde"] }
|
||||
calamine = "0.18.0"
|
||||
chrono = { version="0.4.19", features=["serde"] }
|
||||
chrono-tz = "0.5.3"
|
||||
codespan-reporting = "0.11.0"
|
||||
crossterm = { version="0.19.0", optional=true }
|
||||
csv = "1.1.3"
|
||||
ctrlc = { version="3.1.7", optional=true }
|
||||
derive-new = "0.5.8"
|
||||
directories-next = "2.0.0"
|
||||
dirs-next = "2.0.0"
|
||||
dtparse = "1.2.0"
|
||||
dunce = "1.0.1"
|
||||
eml-parser = "0.1.0"
|
||||
encoding_rs = "0.8.28"
|
||||
filesize = "0.2.0"
|
||||
fs_extra = "1.2.0"
|
||||
futures = { version="0.3.12", features=["compat", "io-compat"] }
|
||||
getset = "0.1.1"
|
||||
glob = "0.3.0"
|
||||
htmlescape = "0.3.1"
|
||||
ical = "0.7.0"
|
||||
@ -61,49 +55,43 @@ lazy_static = "1.*"
|
||||
log = "0.4.14"
|
||||
md-5 = "0.9.1"
|
||||
meval = "0.2.0"
|
||||
minus = { version="3.4.0", optional=true, features=["async_std_lib", "search"] }
|
||||
num-bigint = { version="0.3.1", features=["serde"] }
|
||||
num-bigint = { version="0.4.3", features=["serde"] }
|
||||
num-format = { version="0.4.0", features=["with-num-bigint"] }
|
||||
num-traits = "0.2.14"
|
||||
parking_lot = "0.11.1"
|
||||
pin-utils = "0.1.0"
|
||||
query_interface = "0.3.5"
|
||||
quick-xml = "0.22"
|
||||
rand = "0.8"
|
||||
rayon = "1.5.0"
|
||||
regex = "1.4.3"
|
||||
reqwest = {version = "0.11", optional = true }
|
||||
roxmltree = "0.14.0"
|
||||
rust-embed = "5.9.0"
|
||||
rustyline = { version="9.0.0", optional=true }
|
||||
serde = { version="1.0.123", features=["derive"] }
|
||||
serde_bytes = "0.11.5"
|
||||
serde_ini = "0.2.0"
|
||||
serde_json = "1.0.61"
|
||||
serde_urlencoded = "0.7.0"
|
||||
serde_yaml = "0.8.16"
|
||||
sha2 = "0.9.3"
|
||||
strip-ansi-escapes = "0.1.0"
|
||||
sxd-document = "0.3.2"
|
||||
sxd-xpath = "0.4.2"
|
||||
sysinfo = { version = "0.23.0", optional = true }
|
||||
thiserror = "1.0.26"
|
||||
tempfile = "3.2.0"
|
||||
term = { version="0.7.0", optional=true }
|
||||
term_size = "0.3.2"
|
||||
termcolor = "1.1.2"
|
||||
titlecase = "1.1.0"
|
||||
tokio = { version = "1", features = ["rt-multi-thread"], optional = true }
|
||||
toml = "0.5.8"
|
||||
trash = { version="1.3.0", optional=true }
|
||||
trash = { version = "2.0.2", optional = true }
|
||||
unicode-segmentation = "1.8"
|
||||
url = "2.2.0"
|
||||
uuid_crate = { package="uuid", version="0.8.2", features=["v4"], optional=true }
|
||||
which = { version="4.1.0", optional=true }
|
||||
zip = { version="0.5.9", optional=true }
|
||||
digest = "0.9.0"
|
||||
|
||||
[dependencies.polars]
|
||||
version = "0.15.1"
|
||||
version = "0.17.0"
|
||||
optional = true
|
||||
features = ["parquet", "json", "random", "pivot", "strings", "is_in", "temporal"]
|
||||
default-features = false
|
||||
features = ["docs", "zip_with", "csv-file", "temporal", "performant", "pretty_fmt", "dtype-slim", "parquet", "json", "random", "pivot", "strings", "is_in", "cum_agg", "rolling_window"]
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
umask = "1.0.0"
|
||||
@ -112,16 +100,11 @@ users = "0.11.0"
|
||||
# TODO this will be possible with new dependency resolver
|
||||
# (currently on nightly behind -Zfeatures=itarget):
|
||||
# https://github.com/rust-lang/cargo/issues/7914
|
||||
#[target.'cfg(not(windows))'.dependencies]
|
||||
#num-format = {version = "0.4", features = ["with-system-locale"]}
|
||||
|
||||
[dependencies.rusqlite]
|
||||
features = ["bundled", "blob"]
|
||||
optional = true
|
||||
version = "0.25.3"
|
||||
# [target.'cfg(not(windows))'.dependencies]
|
||||
# num-format = { version = "0.4", features = ["with-system-locale"] }
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = "0.6"
|
||||
shadow-rs = "0.8.1"
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = "1.0.3"
|
||||
@ -129,9 +112,11 @@ quickcheck_macros = "1.0.0"
|
||||
hamcrest2 = "0.3.0"
|
||||
|
||||
[features]
|
||||
clipboard-cli = ["arboard"]
|
||||
rustyline-support = ["rustyline"]
|
||||
stable = []
|
||||
trash-support = ["trash"]
|
||||
table-pager = ["minus", "crossterm"]
|
||||
dataframe = ["nu-protocol/dataframe", "polars"]
|
||||
fetch = ["reqwest", "tokio"]
|
||||
post = ["reqwest", "tokio"]
|
||||
sys = ["sysinfo"]
|
||||
ps = ["sysinfo"]
|
||||
|
@ -1,7 +0,0 @@
|
||||
use derive_new::new;
|
||||
use nu_protocol::hir;
|
||||
|
||||
#[derive(new, Debug)]
|
||||
pub(crate) struct Command {
|
||||
pub(crate) args: hir::Call,
|
||||
}
|
@ -1,10 +1,15 @@
|
||||
use crate::prelude::*;
|
||||
use lazy_static::lazy_static;
|
||||
use nu_engine::{evaluate_baseline_expr, BufCodecReader};
|
||||
use nu_engine::{MaybeTextCodec, StringOrBinary};
|
||||
use nu_test_support::NATIVE_PATH_ENV_VAR;
|
||||
use parking_lot::Mutex;
|
||||
use regex::Regex;
|
||||
|
||||
#[allow(unused)]
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::mpsc;
|
||||
use std::{borrow::Cow, io::BufReader};
|
||||
@ -17,6 +22,9 @@ use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
|
||||
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use which::which_in;
|
||||
|
||||
pub(crate) fn run_external_command(
|
||||
command: ExternalCommand,
|
||||
context: &mut EvaluationContext,
|
||||
@ -38,20 +46,16 @@ pub(crate) fn run_external_command(
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn trim_double_quotes(input: &str) -> String {
|
||||
fn trim_enclosing_quotes(input: &str) -> String {
|
||||
let mut chars = input.chars();
|
||||
|
||||
match (chars.next(), chars.next_back()) {
|
||||
(Some('"'), Some('"')) => chars.collect(),
|
||||
(Some('\''), Some('\'')) => chars.collect(),
|
||||
_ => input.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn escape_where_needed(input: &str) -> String {
|
||||
input.split(' ').join("\\ ").split('\'').join("\\'")
|
||||
}
|
||||
|
||||
fn run_with_stdin(
|
||||
command: ExternalCommand,
|
||||
context: &mut EvaluationContext,
|
||||
@ -104,20 +108,14 @@ fn run_with_stdin(
|
||||
let process_args = command_args
|
||||
.iter()
|
||||
.map(|(arg, _is_literal)| {
|
||||
let arg = nu_path::expand_tilde_string(Cow::Borrowed(arg));
|
||||
let arg = nu_path::expand_tilde(arg).to_string_lossy().to_string();
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
if !_is_literal {
|
||||
let escaped = escape_double_quotes(&arg);
|
||||
add_double_quotes(&escaped)
|
||||
arg
|
||||
} else {
|
||||
let trimmed = trim_double_quotes(&arg);
|
||||
if trimmed != arg {
|
||||
escape_where_needed(&trimmed)
|
||||
} else {
|
||||
trimmed
|
||||
}
|
||||
trim_enclosing_quotes(&arg)
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
@ -125,7 +123,7 @@ fn run_with_stdin(
|
||||
if let Some(unquoted) = remove_quotes(&arg) {
|
||||
unquoted.to_string()
|
||||
} else {
|
||||
arg.to_string()
|
||||
arg
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -141,6 +139,99 @@ fn run_with_stdin(
|
||||
)
|
||||
}
|
||||
|
||||
/// Spawn a direct exe
|
||||
#[allow(unused)]
|
||||
fn spawn_exe(full_path: PathBuf, args: &[String]) -> Command {
|
||||
let mut process = Command::new(full_path);
|
||||
for arg in args {
|
||||
process.arg(&arg);
|
||||
}
|
||||
process
|
||||
}
|
||||
|
||||
/// Spawn a cmd command with `cmd /c args...`
|
||||
fn spawn_cmd_command(command: &ExternalCommand, args: &[String]) -> Command {
|
||||
let mut process = Command::new("cmd");
|
||||
process.arg("/c");
|
||||
process.arg(&command.name);
|
||||
for arg in args {
|
||||
// Clean the args before we use them:
|
||||
// https://stackoverflow.com/questions/1200235/how-to-pass-a-quoted-pipe-character-to-cmd-exe
|
||||
// cmd.exe needs to have a caret to escape a pipe
|
||||
let arg = arg.replace("|", "^|");
|
||||
process.arg(&arg);
|
||||
}
|
||||
process
|
||||
}
|
||||
|
||||
fn has_unsafe_shell_characters(arg: &str) -> bool {
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new(r"[^\w@%+=:,./-]").expect("regex to be valid");
|
||||
}
|
||||
|
||||
RE.is_match(arg)
|
||||
}
|
||||
|
||||
fn shell_arg_escape(arg: &str) -> String {
|
||||
match arg {
|
||||
"" => String::from("''"),
|
||||
s if !has_unsafe_shell_characters(s) => String::from(s),
|
||||
_ => {
|
||||
let single_quotes_escaped = arg.split('\'').join("'\"'\"'");
|
||||
format!("'{}'", single_quotes_escaped)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a sh command with `sh -c args...`
|
||||
fn spawn_sh_command(command: &ExternalCommand, args: &[String]) -> Command {
|
||||
let joined_and_escaped_arguments = args.iter().map(|arg| shell_arg_escape(arg)).join(" ");
|
||||
let cmd_with_args = vec![command.name.clone(), joined_and_escaped_arguments].join(" ");
|
||||
let mut process = Command::new("sh");
|
||||
process.arg("-c").arg(cmd_with_args);
|
||||
process
|
||||
}
|
||||
|
||||
/// a function to spawn any external command
|
||||
#[allow(unused)] // for minimal builds cwd is unused
|
||||
fn spawn_any(command: &ExternalCommand, args: &[String], cwd: &str) -> Command {
|
||||
// resolve the executable name if it is spawnable directly
|
||||
#[cfg(feature = "which")]
|
||||
// TODO add more available paths to `env::var_os("PATH")`?
|
||||
if let Result::Ok(full_path) = which_in(&command.name, env::var_os("PATH"), cwd) {
|
||||
if let Some(extension) = full_path.extension() {
|
||||
#[cfg(windows)]
|
||||
if extension.eq_ignore_ascii_case("exe") {
|
||||
// if exe spawn it directly
|
||||
return spawn_exe(full_path, args);
|
||||
} else {
|
||||
// TODO implement special care for various executable types such as .bat, .ps1, .cmd, etc
|
||||
// https://github.com/mklement0/Native/blob/e0e0b8785cad39a73053e35084d1f60d87fbac58/Native.psm1#L749
|
||||
// otherwise shell out to cmd
|
||||
return spawn_cmd_command(command, args);
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
if !["sh", "bash"]
|
||||
.iter()
|
||||
.any(|ext| extension.eq_ignore_ascii_case(ext))
|
||||
{
|
||||
// if exe spawn it directly
|
||||
return spawn_exe(full_path, args);
|
||||
} else {
|
||||
// otherwise shell out to sh
|
||||
return spawn_sh_command(command, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
// in all the other cases shell out
|
||||
if cfg!(windows) {
|
||||
spawn_cmd_command(command, args)
|
||||
} else {
|
||||
// TODO what happens if that os doesn't support spawning sh?
|
||||
spawn_sh_command(command, args)
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn(
|
||||
command: &ExternalCommand,
|
||||
path: &str,
|
||||
@ -151,31 +242,7 @@ fn spawn(
|
||||
) -> Result<InputStream, ShellError> {
|
||||
let command = command.clone();
|
||||
|
||||
let mut process = {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let mut process = Command::new("cmd");
|
||||
process.arg("/c");
|
||||
process.arg(&command.name);
|
||||
for arg in args {
|
||||
// Clean the args before we use them:
|
||||
// https://stackoverflow.com/questions/1200235/how-to-pass-a-quoted-pipe-character-to-cmd-exe
|
||||
// cmd.exe needs to have a caret to escape a pipe
|
||||
let arg = arg.replace("|", "^|");
|
||||
process.arg(&arg);
|
||||
}
|
||||
process
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let cmd_with_args = vec![command.name.clone(), args.join(" ")].join(" ");
|
||||
let mut process = Command::new("sh");
|
||||
process.arg("-c").arg(cmd_with_args);
|
||||
process
|
||||
}
|
||||
};
|
||||
|
||||
let mut process = spawn_any(&command, args, path);
|
||||
process.current_dir(path);
|
||||
trace!(target: "nu::run::external", "cwd = {:?}", &path);
|
||||
|
||||
@ -454,7 +521,7 @@ fn spawn(
|
||||
Ok(stream.into_input_stream())
|
||||
}
|
||||
Err(e) => Err(ShellError::labeled_error(
|
||||
format!("{}", e),
|
||||
e.to_string(),
|
||||
"failed to spawn",
|
||||
&command.name_tag,
|
||||
)),
|
||||
|
@ -1,5 +1 @@
|
||||
mod dynamic;
|
||||
pub(crate) mod external;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use dynamic::Command as DynamicCommand;
|
||||
|
@ -22,6 +22,7 @@ impl WholeStreamCommand for Histogram {
|
||||
None,
|
||||
)
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column name to give the histogram's frequency column",
|
||||
)
|
||||
|
@ -38,7 +38,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
},
|
||||
Example {
|
||||
description: "Set coloring options",
|
||||
example: "config set color_config [[header_align header_bold]; [left $true]]",
|
||||
example: "config set color_config [[header_align header_color]; [left white_bold]]",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
|
@ -13,6 +13,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into binary").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to binary (for table input)",
|
||||
)
|
||||
|
118
crates/nu-command/src/commands/conversions/into/column_path.rs
Normal file
118
crates/nu-command/src/commands/conversions/into/column_path.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into column-path"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into column-path").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"values to convert to column path",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to column path"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_filepath(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Convert string to column path in table",
|
||||
example: "echo [[name]; ['/dev/null'] ['C:\\Program Files'] ['../../Cargo.toml']] | into column-path name",
|
||||
result: Some(vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::column_path("/dev/null", Span::unknown()).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::column_path("C:\\Program Files", Span::unknown()).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::column_path("../../Cargo.toml", Span::unknown()).into(),
|
||||
})
|
||||
.into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert string to column path",
|
||||
example: "echo 'Cargo.toml' | into column-path",
|
||||
result: Some(vec![UntaggedValue::column_path("Cargo.toml", Span::unknown()).into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_filepath(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag())),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.into_input_stream())
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::column_path(
|
||||
match prim {
|
||||
Primitive::String(a_string) => a_string,
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"'into column-path' for non-string primitives",
|
||||
))
|
||||
}
|
||||
},
|
||||
Span::unknown(),
|
||||
)
|
||||
.into_value(&tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column name to use, with 'into column-path COLUMN'",
|
||||
"found table",
|
||||
tag,
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented(
|
||||
"'into column-path' for unsupported type",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into path").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to filepath (for table input)",
|
||||
)
|
||||
|
182
crates/nu-command/src/commands/conversions/into/filesize.rs
Normal file
182
crates/nu-command/src/commands/conversions/into/filesize.rs
Normal file
@ -0,0 +1,182 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use num_bigint::ToBigInt;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into filesize"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into filesize").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to filesize (for table input)",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to filesize"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_filesize(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Convert string to filesize in table",
|
||||
example: "echo [[bytes]; ['5'] [3.2] [4] [2kb]] | into filesize bytes",
|
||||
result: Some(vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"bytes".to_string() => UntaggedValue::filesize(5).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"bytes".to_string() => UntaggedValue::filesize(3).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"bytes".to_string() => UntaggedValue::filesize(4).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"bytes".to_string() => UntaggedValue::filesize(2000).into(),
|
||||
})
|
||||
.into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert string to filesize",
|
||||
example: "echo '2' | into filesize",
|
||||
result: Some(vec![UntaggedValue::filesize(2).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert decimal to filesize",
|
||||
example: "echo 8.3 | into filesize",
|
||||
result: Some(vec![UntaggedValue::filesize(8).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert int to filesize",
|
||||
example: "echo 5 | into filesize",
|
||||
result: Some(vec![UntaggedValue::filesize(5).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert file size to filesize",
|
||||
example: "echo 4KB | into filesize",
|
||||
result: Some(vec![UntaggedValue::filesize(4000).into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_filesize(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag())),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.into_input_stream())
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::filesize(match prim {
|
||||
Primitive::String(a_string) => match int_from_string(a_string.trim(), &tag) {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
},
|
||||
Primitive::Decimal(dec) => match dec.to_bigint() {
|
||||
Some(n) => match n.to_u64() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to filesize",
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to filesize",
|
||||
));
|
||||
}
|
||||
},
|
||||
Primitive::Int(n_ref) => (*n_ref).try_into().map_err(|_| {
|
||||
ShellError::unimplemented("cannot convert negative integer to filesize")
|
||||
})?,
|
||||
Primitive::Filesize(a_filesize) => *a_filesize,
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"'into filesize' for non-numeric primitives",
|
||||
))
|
||||
}
|
||||
})
|
||||
.into_value(&tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column name to use, with 'into filesize COLUMN'",
|
||||
"found table",
|
||||
tag,
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented(
|
||||
"'into filesize' for unsupported type",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn int_from_string(a_string: &str, tag: &Tag) -> Result<u64, ShellError> {
|
||||
match a_string.parse::<u64>() {
|
||||
Ok(n) => Ok(n),
|
||||
Err(_) => match a_string.parse::<f64>() {
|
||||
Ok(f) => match f.to_u64() {
|
||||
Some(i) => Ok(i),
|
||||
None => Err(ShellError::labeled_error(
|
||||
"Could not convert string value to filesize",
|
||||
"original value",
|
||||
tag.clone(),
|
||||
)),
|
||||
},
|
||||
Err(_) => Err(ShellError::labeled_error(
|
||||
"Could not convert string value to filesize",
|
||||
"original value",
|
||||
tag.clone(),
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into int").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to int (for table input)",
|
||||
)
|
||||
|
@ -1,10 +1,14 @@
|
||||
mod binary;
|
||||
mod column_path;
|
||||
mod command;
|
||||
mod filepath;
|
||||
mod filesize;
|
||||
mod int;
|
||||
pub mod string;
|
||||
|
||||
pub use self::filesize::SubCommand as IntoFilesize;
|
||||
pub use binary::SubCommand as IntoBinary;
|
||||
pub use column_path::SubCommand as IntoColumnPath;
|
||||
pub use command::Command as Into;
|
||||
pub use filepath::SubCommand as IntoFilepath;
|
||||
pub use int::SubCommand as IntoInt;
|
||||
|
@ -20,6 +20,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into string")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to string (for table input)",
|
||||
)
|
||||
@ -158,7 +159,7 @@ pub fn action(
|
||||
}
|
||||
|
||||
fn format_int(int: i64) -> String {
|
||||
format!("{}", int)
|
||||
int.to_string()
|
||||
|
||||
// TODO once platform-specific dependencies are stable (see Cargo.toml)
|
||||
// #[cfg(windows)]
|
||||
@ -175,7 +176,7 @@ fn format_int(int: i64) -> String {
|
||||
}
|
||||
|
||||
fn format_bigint(int: &BigInt) -> String {
|
||||
format!("{}", int)
|
||||
int.to_string()
|
||||
|
||||
// TODO once platform-specific dependencies are stable (see Cargo.toml)
|
||||
// #[cfg(windows)]
|
||||
@ -229,7 +230,7 @@ fn format_decimal(mut decimal: BigDecimal, digits: Option<u64>, group_digits: bo
|
||||
let format_default_loc = |int_part: BigInt| {
|
||||
let loc = Locale::en;
|
||||
//TODO: when num_format is available for recent bigint, replace this with the locale-based format
|
||||
let (int_str, sep) = (format!("{}", int_part), String::from(loc.decimal()));
|
||||
let (int_str, sep) = (int_part.to_string(), String::from(loc.decimal()));
|
||||
|
||||
format!("{}{}{}", int_str, sep, dec_str)
|
||||
};
|
||||
|
@ -15,7 +15,7 @@ impl WholeStreamCommand for Alias {
|
||||
Signature::build("alias")
|
||||
.required("name", SyntaxShape::String, "the name of the alias")
|
||||
.required("equals", SyntaxShape::String, "the equals sign")
|
||||
.rest(SyntaxShape::Any, "the expansion for the alias")
|
||||
.rest("rest", SyntaxShape::Any, "the expansion for the alias")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -27,7 +27,7 @@ impl WholeStreamCommand for Do {
|
||||
"ignore errors as the block runs",
|
||||
Some('i'),
|
||||
)
|
||||
.rest(SyntaxShape::Any, "the parameter(s) for the block")
|
||||
.rest("rest", SyntaxShape::Any, "the parameter(s) for the block")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -12,7 +12,7 @@ impl WholeStreamCommand for Echo {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("echo").rest(SyntaxShape::Any, "the values to echo")
|
||||
Signature::build("echo").rest("rest", SyntaxShape::Any, "the values to echo")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
109
crates/nu-command/src/commands/core_commands/error/make.rs
Normal file
109
crates/nu-command/src/commands/core_commands/error/make.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"error make"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("error make")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Create an error."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let input = args.input;
|
||||
|
||||
Ok(input
|
||||
.map(|value| {
|
||||
make_error(&value)
|
||||
.map(|err| UntaggedValue::Error(err).into_value(value.tag()))
|
||||
.unwrap_or_else(|| {
|
||||
UntaggedValue::Error(ShellError::untagged_runtime_error(
|
||||
"Creating error value not supported.",
|
||||
))
|
||||
.into_value(value.tag())
|
||||
})
|
||||
})
|
||||
.into_output_stream())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Creates a labeled error",
|
||||
example: r#"[
|
||||
[ msg, labels, span];
|
||||
["The message", "Helpful message here", ([[start, end]; [0, 141]])]
|
||||
] | error make"#,
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn make_error(value: &Value) -> Option<ShellError> {
|
||||
if let Value {
|
||||
value: UntaggedValue::Row(dict),
|
||||
..
|
||||
} = value
|
||||
{
|
||||
let msg = dict.get_data_by_key("msg".spanned_unknown());
|
||||
|
||||
let labels =
|
||||
dict.get_data_by_key("labels".spanned_unknown())
|
||||
.and_then(|table| match &table.value {
|
||||
UntaggedValue::Table(_) => table
|
||||
.table_entries()
|
||||
.map(|value| value.as_string().ok())
|
||||
.collect(),
|
||||
UntaggedValue::Primitive(Primitive::String(label)) => {
|
||||
Some(vec![label.to_string()])
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let _anchor = dict.get_data_by_key("tag".spanned_unknown());
|
||||
let span = dict.get_data_by_key("span".spanned_unknown());
|
||||
|
||||
if msg.is_none() || labels.is_none() || span.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let msg = msg.and_then(|msg| msg.as_string().ok());
|
||||
|
||||
if let Some(labels) = labels {
|
||||
if labels.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some(ShellError::labeled_error(
|
||||
msg.expect("Message will always be present."),
|
||||
&labels[0],
|
||||
span.map(|data| match data {
|
||||
Value {
|
||||
value: UntaggedValue::Row(vals),
|
||||
..
|
||||
} => match (vals.entries.get("start"), vals.entries.get("end")) {
|
||||
(Some(start), Some(end)) => {
|
||||
let start = start.as_usize().ok().unwrap_or(0);
|
||||
let end = end.as_usize().ok().unwrap_or(0);
|
||||
|
||||
Span::new(start, end)
|
||||
}
|
||||
(_, _) => Span::unknown(),
|
||||
},
|
||||
_ => Span::unknown(),
|
||||
})
|
||||
.unwrap_or_else(Span::unknown),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
mod make;
|
||||
|
||||
pub use make::SubCommand as ErrorMake;
|
110
crates/nu-command/src/commands/core_commands/find.rs
Normal file
110
crates/nu-command/src/commands/core_commands/find.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct Find;
|
||||
|
||||
impl WholeStreamCommand for Find {
|
||||
fn name(&self) -> &str {
|
||||
"find"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("find").rest("rest", SyntaxShape::String, "search term")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Find text in the output of a previous command"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
find(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Search pipeline output for multiple terms",
|
||||
example: r#"ls | find toml md sh"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Search strings for term(s)",
|
||||
example: r#"echo Cargo.toml | find toml"#,
|
||||
result: Some(vec![Value::from("Cargo.toml")]),
|
||||
},
|
||||
Example {
|
||||
description: "Search a number list for term(s)",
|
||||
example: r#"[1 2 3 4 5] | find 5"#,
|
||||
result: Some(vec![UntaggedValue::int(5).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Search string list for term(s)",
|
||||
example: r#"[moe larry curly] | find l"#,
|
||||
result: Some(vec![Value::from("larry"), Value::from("curly")]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn row_contains(row: &Dictionary, search_terms: Vec<String>) -> bool {
|
||||
for term in search_terms {
|
||||
for (k, v) in &row.entries {
|
||||
let key = k.to_string().trim().to_lowercase();
|
||||
let value = v.convert_to_string().trim().to_lowercase();
|
||||
if key.contains(&term) || value.contains(&term) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn find(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let rest: Vec<Value> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.filter(move |row| match &row.value {
|
||||
UntaggedValue::Row(row) => {
|
||||
let sterms: Vec<String> = rest
|
||||
.iter()
|
||||
.map(|t| t.convert_to_string().trim().to_lowercase())
|
||||
.collect();
|
||||
row_contains(row, sterms)
|
||||
}
|
||||
UntaggedValue::Primitive(_p) => {
|
||||
// eprint!("prim {}", p.type_name());
|
||||
let sterms: Vec<String> = rest
|
||||
.iter()
|
||||
.map(|t| t.convert_to_string().trim().to_lowercase())
|
||||
.collect();
|
||||
|
||||
let prim_string = &row.convert_to_string().trim().to_lowercase();
|
||||
for term in sterms {
|
||||
if prim_string.contains(&term) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
.into_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Find;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Find {})
|
||||
}
|
||||
}
|
@ -18,7 +18,11 @@ impl WholeStreamCommand for Help {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("help")
|
||||
.rest(SyntaxShape::String, "the name of command to get help on")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::String,
|
||||
"the name of command to get help on",
|
||||
)
|
||||
.named(
|
||||
"find",
|
||||
SyntaxShape::String,
|
||||
@ -298,7 +302,7 @@ pub fn signature_dict(signature: Signature, tag: impl Into<Tag>) -> Value {
|
||||
let tag = tag.into();
|
||||
let mut sig = TaggedListBuilder::new(&tag);
|
||||
|
||||
for arg in signature.positional.iter() {
|
||||
for arg in &signature.positional {
|
||||
let is_required = matches!(arg.0, PositionalType::Mandatory(_, _));
|
||||
|
||||
sig.push_value(for_spec(arg.0.name(), "argument", is_required, &tag));
|
||||
@ -309,7 +313,7 @@ pub fn signature_dict(signature: Signature, tag: impl Into<Tag>) -> Value {
|
||||
sig.push_value(for_spec("rest", "argument", is_required, &tag));
|
||||
}
|
||||
|
||||
for (name, ty) in signature.named.iter() {
|
||||
for (name, ty) in &signature.named {
|
||||
match ty.0 {
|
||||
NamedType::Mandatory(_, _) => sig.push_value(for_spec(name, "flag", true, &tag)),
|
||||
NamedType::Optional(_, _) => sig.push_value(for_spec(name, "flag", false, &tag)),
|
||||
|
@ -100,18 +100,15 @@ fn if_command(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
context.scope.add_vars(&condition.captured.entries);
|
||||
|
||||
//FIXME: should we use the scope that's brought in as well?
|
||||
let condition = evaluate_baseline_expr(cond, &*context);
|
||||
match condition {
|
||||
let condition = evaluate_baseline_expr(cond, &context);
|
||||
let result = match condition {
|
||||
Ok(condition) => match condition.as_bool() {
|
||||
Ok(b) => {
|
||||
let result = if b {
|
||||
run_block(&then_case.block, &*context, input, external_redirection)
|
||||
if b {
|
||||
run_block(&then_case.block, &context, input, external_redirection)
|
||||
} else {
|
||||
run_block(&else_case.block, &*context, input, external_redirection)
|
||||
};
|
||||
context.scope.exit_scope();
|
||||
|
||||
result
|
||||
run_block(&else_case.block, &context, input, external_redirection)
|
||||
}
|
||||
}
|
||||
Err(e) => Ok(OutputStream::from_stream(
|
||||
vec![UntaggedValue::Error(e).into_untagged_value()].into_iter(),
|
||||
@ -120,13 +117,16 @@ fn if_command(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Err(e) => Ok(OutputStream::from_stream(
|
||||
vec![UntaggedValue::Error(e).into_untagged_value()].into_iter(),
|
||||
)),
|
||||
}
|
||||
};
|
||||
context.scope.exit_scope();
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::If;
|
||||
use super::ShellError;
|
||||
use nu_test_support::nu;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
@ -134,4 +134,21 @@ mod tests {
|
||||
|
||||
test_examples(If {})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_doesnt_leak_on_error() {
|
||||
let actual = nu!(
|
||||
".",
|
||||
r#"
|
||||
def test-leak [] {
|
||||
let var = "hello"
|
||||
if 0 == "" {echo ok} {echo not}
|
||||
}
|
||||
test-leak
|
||||
echo $var
|
||||
"#
|
||||
);
|
||||
|
||||
assert!(actual.err.contains("unknown variable"));
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ mod def;
|
||||
mod describe;
|
||||
mod do_;
|
||||
pub(crate) mod echo;
|
||||
mod error;
|
||||
mod find;
|
||||
mod help;
|
||||
mod history;
|
||||
mod if_;
|
||||
@ -27,6 +29,8 @@ pub use def::Def;
|
||||
pub use describe::Describe;
|
||||
pub use do_::Do;
|
||||
pub use echo::Echo;
|
||||
pub use error::*;
|
||||
pub use find::Find;
|
||||
pub use help::Help;
|
||||
pub use history::History;
|
||||
pub use if_::If;
|
||||
|
@ -4,7 +4,7 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_path::canonicalize;
|
||||
use nu_path::canonicalize_with;
|
||||
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
|
||||
@ -56,7 +56,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
tag,
|
||||
}) = load_path
|
||||
{
|
||||
let path = canonicalize(shell_manager.path(), load_path).map_err(|_| {
|
||||
let path = canonicalize_with(load_path, shell_manager.path()).map_err(|_| {
|
||||
ShellError::labeled_error(
|
||||
"Cannot load plugins from directory",
|
||||
"directory not found",
|
||||
|
@ -15,6 +15,7 @@ impl WholeStreamCommand for Command {
|
||||
.switch("skip-plugins", "do not load plugins", None)
|
||||
.switch("no-history", "don't save history", None)
|
||||
.switch("perf", "show startup performance metrics", None)
|
||||
.switch("login", "start Nu as if it was a login shell", Some('l'))
|
||||
.named(
|
||||
"commands",
|
||||
SyntaxShape::String,
|
||||
@ -33,7 +34,7 @@ impl WholeStreamCommand for Command {
|
||||
"loglevel",
|
||||
SyntaxShape::String,
|
||||
"LEVEL: error, warn, info, debug, trace",
|
||||
Some('l'),
|
||||
None,
|
||||
)
|
||||
.named(
|
||||
"config-file",
|
||||
@ -41,7 +42,7 @@ impl WholeStreamCommand for Command {
|
||||
"custom configuration source file",
|
||||
None,
|
||||
)
|
||||
.rest(SyntaxShape::String, "source file(s) to run")
|
||||
.rest("rest", SyntaxShape::String, "source file(s) to run")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -2,11 +2,11 @@ use crate::prelude::*;
|
||||
use nu_engine::{script, WholeStreamCommand};
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_path::expand_path;
|
||||
use nu_path::{canonicalize, canonicalize_with};
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
|
||||
use std::{borrow::Cow, path::Path};
|
||||
use std::path::Path;
|
||||
|
||||
pub struct Source;
|
||||
|
||||
@ -32,7 +32,7 @@ impl WholeStreamCommand for Source {
|
||||
"Runs a script file in the current context."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
source(args)
|
||||
}
|
||||
|
||||
@ -41,14 +41,66 @@ impl WholeStreamCommand for Source {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
pub fn source(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let ctx = &args.context;
|
||||
let filename: Tagged<String> = args.req(0)?;
|
||||
|
||||
let source_file = Path::new(&filename.item);
|
||||
|
||||
// Note: this is a special case for setting the context from a command
|
||||
// In this case, if we don't set it now, we'll lose the scope that this
|
||||
// variable should be set into.
|
||||
let contents = std::fs::read_to_string(&expand_path(Cow::Borrowed(Path::new(&filename.item))));
|
||||
|
||||
let lib_dirs = &ctx
|
||||
.configs()
|
||||
.lock()
|
||||
.global_config
|
||||
.as_ref()
|
||||
.map(|configuration| match configuration.var("lib_dirs") {
|
||||
Some(paths) => paths
|
||||
.table_entries()
|
||||
.cloned()
|
||||
.map(|path| path.as_string())
|
||||
.collect(),
|
||||
None => vec![],
|
||||
});
|
||||
|
||||
if let Some(dir) = lib_dirs {
|
||||
for lib_path in dir {
|
||||
match lib_path {
|
||||
Ok(name) => {
|
||||
let path = if let Ok(p) = canonicalize_with(&source_file, name) {
|
||||
p
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Ok(contents) = std::fs::read_to_string(path) {
|
||||
let result = script::run_script_standalone(contents, true, ctx, false);
|
||||
|
||||
if let Err(err) = result {
|
||||
ctx.error(err);
|
||||
}
|
||||
return Ok(OutputStream::empty());
|
||||
}
|
||||
}
|
||||
Err(reason) => {
|
||||
ctx.error(reason.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let path = canonicalize(source_file).map_err(|e| {
|
||||
ShellError::labeled_error(
|
||||
format!("Can't load source file. Reason: {}", e),
|
||||
"Can't load this file",
|
||||
filename.span(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let contents = std::fs::read_to_string(path);
|
||||
|
||||
match contents {
|
||||
Ok(contents) => {
|
||||
let result = script::run_script_standalone(contents, true, ctx, false);
|
||||
@ -56,16 +108,16 @@ pub fn source(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
if let Err(err) = result {
|
||||
ctx.error(err);
|
||||
}
|
||||
Ok(ActionStream::empty())
|
||||
Ok(OutputStream::empty())
|
||||
}
|
||||
Err(_) => {
|
||||
Err(e) => {
|
||||
ctx.error(ShellError::labeled_error(
|
||||
"Can't load file to source",
|
||||
"can't load file",
|
||||
format!("Can't load source file. Reason: {}", e),
|
||||
"Can't load this file",
|
||||
filename.span(),
|
||||
));
|
||||
|
||||
Ok(ActionStream::empty())
|
||||
Ok(OutputStream::empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
|
||||
pub struct Tags;
|
||||
|
||||
@ -18,37 +18,64 @@ impl WholeStreamCommand for Tags {
|
||||
"Read the tags (metadata) for values."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(tags(args))
|
||||
}
|
||||
}
|
||||
|
||||
fn tags(args: CommandArgs) -> ActionStream {
|
||||
args.input
|
||||
.map(move |v| {
|
||||
let mut tags = TaggedDictBuilder::new(v.tag());
|
||||
{
|
||||
let anchor = v.anchor();
|
||||
let span = v.tag.span;
|
||||
let mut dict = TaggedDictBuilder::new(v.tag());
|
||||
dict.insert_untagged("start", UntaggedValue::int(span.start() as i64));
|
||||
dict.insert_untagged("end", UntaggedValue::int(span.end() as i64));
|
||||
tags.insert_value("span", dict.into_value());
|
||||
fn build_tag_table(tag: impl Into<Tag>) -> Value {
|
||||
let tag = tag.into();
|
||||
let span = tag.span;
|
||||
|
||||
match anchor {
|
||||
Some(AnchorLocation::File(source)) => {
|
||||
tags.insert_untagged("anchor", UntaggedValue::string(source));
|
||||
}
|
||||
Some(AnchorLocation::Url(source)) => {
|
||||
tags.insert_untagged("anchor", UntaggedValue::string(source));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
TaggedDictBuilder::build(tag.clone(), |tags| {
|
||||
if let Some(anchor) = anchor_as_value(&tag) {
|
||||
tags.insert_value("anchor", anchor);
|
||||
}
|
||||
|
||||
tags.insert_value(
|
||||
"span",
|
||||
TaggedDictBuilder::build(tag.clone(), |span_dict| {
|
||||
span_dict.insert_untagged("start", UntaggedValue::int(span.start() as i64));
|
||||
span_dict.insert_untagged("end", UntaggedValue::int(span.end() as i64));
|
||||
}),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
fn tags(args: CommandArgs) -> OutputStream {
|
||||
if args.input.is_empty() {
|
||||
OutputStream::one(build_tag_table(&args.name_tag()))
|
||||
} else {
|
||||
args.input
|
||||
.map(move |v| build_tag_table(v.tag()))
|
||||
.into_output_stream()
|
||||
}
|
||||
}
|
||||
|
||||
fn anchor_as_value(tag: &Tag) -> Option<Value> {
|
||||
let anchor = tag.anchor.as_ref();
|
||||
|
||||
anchor.as_ref()?;
|
||||
|
||||
Some(TaggedDictBuilder::build(tag, |table| {
|
||||
let value = match anchor {
|
||||
Some(AnchorLocation::File(path)) => {
|
||||
Some(("file", UntaggedValue::from(path.to_string())))
|
||||
}
|
||||
Some(AnchorLocation::Url(destination)) => {
|
||||
Some(("url", UntaggedValue::from(destination.to_string())))
|
||||
}
|
||||
Some(AnchorLocation::Source(text)) => Some((
|
||||
"source",
|
||||
UntaggedValue::Primitive(Primitive::String(text.to_string())),
|
||||
)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
tags.into_value()
|
||||
})
|
||||
.into_action_stream()
|
||||
if let Some((key, value)) = value {
|
||||
table.insert_untagged(key, value);
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -56,7 +56,7 @@ fn tutor(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let search: Option<String> = args.opt(0).unwrap_or(None);
|
||||
let find: Option<String> = args.get_flag("find")?;
|
||||
|
||||
let search_space = vec![
|
||||
let search_space = [
|
||||
(vec!["begin"], begin_tutor()),
|
||||
(
|
||||
vec!["table", "tables", "row", "rows", "column", "columns"],
|
||||
@ -81,6 +81,7 @@ fn tutor(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
vec!["var", "vars", "variable", "variables"],
|
||||
variable_tutor(),
|
||||
),
|
||||
(vec!["engine-q", "e-q"], engineq_tutor()),
|
||||
(vec!["block", "blocks"], block_tutor()),
|
||||
(vec!["shorthand", "shorthands"], shorthand_tutor()),
|
||||
];
|
||||
@ -88,7 +89,7 @@ fn tutor(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
if let Some(find) = find {
|
||||
let mut results = vec![];
|
||||
for search_group in search_space {
|
||||
if search_group.1.contains(&find.as_str()) {
|
||||
if search_group.1.contains(&find) {
|
||||
results.push(search_group.0[0].to_string())
|
||||
}
|
||||
}
|
||||
@ -166,7 +167,7 @@ This will get the 3rd (note that `nth` is zero-based) row in the table created
|
||||
by the `ls` command. You can use `nth` on any table created by other commands
|
||||
as well.
|
||||
|
||||
You can also access the column of data in one of two ways. If you want to want
|
||||
You can also access the column of data in one of two ways. If you want
|
||||
to keep the column as part of a new table, you can use `select`.
|
||||
```
|
||||
ls | select name
|
||||
@ -274,7 +275,7 @@ This can be helpful if you want to later processes these values.
|
||||
The `echo` command can pair well with the `each` command which can run
|
||||
code on each row, or item, of input.
|
||||
|
||||
You can continue to learn more about the `echo` command by running:
|
||||
You can continue to learn more about the `each` command by running:
|
||||
```
|
||||
tutor each
|
||||
```
|
||||
@ -370,6 +371,29 @@ same value using:
|
||||
"#
|
||||
}
|
||||
|
||||
fn engineq_tutor() -> &'static str {
|
||||
r#"
|
||||
Engine-q is the upcoming engine for Nushell. Build for speed and correctness,
|
||||
it also comes with a set of changes from Nushell versions prior to 0.60. To
|
||||
get ready for engine-q look for some of these changes that might impact your
|
||||
current scripts:
|
||||
|
||||
* Engine-q now uses a few new data structures, including a record syntax
|
||||
that allows you to model key-value pairs similar to JSON objects.
|
||||
* Environment variables can now contain more than just strings. Structured
|
||||
values are converted to strings for external commands using converters.
|
||||
* `if` will now use an `else` keyword before the else block.
|
||||
* We're moving from "config.toml" to "config.nu". This means startup will
|
||||
now be a script file.
|
||||
* `config` and its subcommands are being replaced by a record that you can
|
||||
update in the shell which contains all the settings under the variable
|
||||
`$config`.
|
||||
* bigint/bigdecimal values are now machine i64 and f64 values
|
||||
* And more, you can read more about upcoming changes in the up-to-date list
|
||||
at: https://github.com/nushell/engine-q/issues/522
|
||||
"#
|
||||
}
|
||||
|
||||
fn display(tag: Tag, scope: &Scope, help: &str) -> OutputStream {
|
||||
let help = help.split('`');
|
||||
|
||||
@ -383,7 +407,7 @@ fn display(tag: Tag, scope: &Scope, help: &str) -> OutputStream {
|
||||
|
||||
//TODO: support no-color mode
|
||||
let colored_example = nu_engine::Painter::paint_string(item, scope, &palette);
|
||||
build.push_str(&format!("{}", colored_example));
|
||||
build.push_str(&colored_example);
|
||||
} else {
|
||||
code_mode = true;
|
||||
build.push_str(item);
|
||||
|
@ -221,11 +221,6 @@ fn features_enabled() -> Vec<String> {
|
||||
names.push("zip".to_string());
|
||||
}
|
||||
|
||||
#[cfg(feature = "clipboard-cli")]
|
||||
{
|
||||
names.push("clipboard-cli".to_string());
|
||||
}
|
||||
|
||||
#[cfg(feature = "trash-support")]
|
||||
{
|
||||
names.push("trash".to_string());
|
||||
|
@ -245,7 +245,7 @@ fn perform_groupby_aggregation(
|
||||
None => &col[..],
|
||||
};
|
||||
|
||||
res.rename(col.as_str(), new_col)
|
||||
res.rename(&col, new_col)
|
||||
.expect("Column is always there. Looping with known names");
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let other: Value = args.req_named("other")?;
|
||||
let axis: Tagged<String> = args.req_named("axis")?;
|
||||
|
||||
let axis = Axis::try_from_str(axis.item.as_str(), &axis.tag.span)?;
|
||||
let axis = Axis::try_from_str(&axis.item, &axis.tag.span)?;
|
||||
|
||||
let df_other = match other.value {
|
||||
UntaggedValue::DataFrame(df) => Ok(df),
|
||||
|
@ -53,7 +53,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let res = df
|
||||
.as_ref()
|
||||
.column(column.item.as_ref())
|
||||
.column(&column.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &column.tag.span, None))?;
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.clone()], &tag.span)?;
|
||||
|
@ -121,7 +121,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tail = df.as_ref().get_columns().iter().map(|col| {
|
||||
let count = col.len() as f64;
|
||||
|
||||
let sum = match col.sum_as_series().cast_with_dtype(&DataType::Float64) {
|
||||
let sum = match col.sum_as_series().cast(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
@ -144,7 +144,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let min = match col.min_as_series().cast_with_dtype(&DataType::Float64) {
|
||||
let min = match col.min_as_series().cast(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
@ -153,7 +153,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
};
|
||||
|
||||
let q_25 = match col.quantile_as_series(0.25) {
|
||||
Ok(ca) => match ca.cast_with_dtype(&DataType::Float64) {
|
||||
Ok(ca) => match ca.cast(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
@ -164,7 +164,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
};
|
||||
|
||||
let q_50 = match col.quantile_as_series(0.50) {
|
||||
Ok(ca) => match ca.cast_with_dtype(&DataType::Float64) {
|
||||
Ok(ca) => match ca.cast(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
@ -175,7 +175,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
};
|
||||
|
||||
let q_75 = match col.quantile_as_series(0.75) {
|
||||
Ok(ca) => match ca.cast_with_dtype(&DataType::Float64) {
|
||||
Ok(ca) => match ca.cast(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
@ -185,7 +185,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let max = match col.max_as_series().cast_with_dtype(&DataType::Float64) {
|
||||
let max = match col.max_as_series().cast(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
@ -195,7 +195,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let name = format!("{} ({})", col.name(), col.dtype());
|
||||
ChunkedArray::<Float64Type>::new_from_opt_slice(
|
||||
name.as_str(),
|
||||
&name,
|
||||
&[
|
||||
Some(count),
|
||||
sum,
|
||||
|
@ -20,7 +20,11 @@ impl WholeStreamCommand for DataFrame {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe drop").rest(SyntaxShape::Any, "column names to be dropped")
|
||||
Signature::build("dataframe drop").rest(
|
||||
"rest",
|
||||
SyntaxShape::Any,
|
||||
"column names to be dropped",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
@ -72,7 +72,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
.expect("using name from list of names from dataframe")
|
||||
.dtype();
|
||||
|
||||
let dtype_str = format!("{}", dtype);
|
||||
let dtype_str = dtype.to_string();
|
||||
dtypes.push(Value {
|
||||
value: dtype_str.into(),
|
||||
tag: Tag::default(),
|
||||
|
@ -19,7 +19,11 @@ impl WholeStreamCommand for DataFrame {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get").rest(SyntaxShape::Any, "column names to sort dataframe")
|
||||
Signature::build("dataframe get").rest(
|
||||
"rest",
|
||||
SyntaxShape::Any,
|
||||
"column names to sort dataframe",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
@ -20,7 +20,7 @@ impl WholeStreamCommand for DataFrame {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe group-by").rest(SyntaxShape::Any, "groupby columns")
|
||||
Signature::build("dataframe group-by").rest("rest", SyntaxShape::Any, "groupby columns")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
@ -44,6 +44,12 @@ impl WholeStreamCommand for DataFrame {
|
||||
"type of join. Inner by default",
|
||||
Some('t'),
|
||||
)
|
||||
.named(
|
||||
"suffix",
|
||||
SyntaxShape::String,
|
||||
"suffix for the columns of the right dataframe",
|
||||
Some('s'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
@ -104,6 +110,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let r_df: Value = args.req(0)?;
|
||||
let l_col: Vec<Value> = args.req_named("left")?;
|
||||
let r_col: Vec<Value> = args.req_named("right")?;
|
||||
let r_suffix: Option<Tagged<String>> = args.get_flag("suffix")?;
|
||||
let join_type_op: Option<Tagged<String>> = args.get_flag("type")?;
|
||||
|
||||
let join_type = match join_type_op {
|
||||
@ -124,6 +131,8 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
},
|
||||
};
|
||||
|
||||
let suffix = r_suffix.map(|s| s.item);
|
||||
|
||||
let (l_col_string, l_col_span) = convert_columns(&l_col, &tag)?;
|
||||
let (r_col_string, r_col_span) = convert_columns(&r_col, &tag)?;
|
||||
|
||||
@ -142,7 +151,13 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
)?;
|
||||
|
||||
df.as_ref()
|
||||
.join(r_df.as_ref(), &l_col_string, &r_col_string, join_type)
|
||||
.join(
|
||||
r_df.as_ref(),
|
||||
&l_col_string,
|
||||
&r_col_string,
|
||||
join_type,
|
||||
suffix,
|
||||
)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &l_col_span, None))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
@ -177,7 +192,7 @@ fn check_column_datatypes<T: AsRef<str>>(
|
||||
));
|
||||
}
|
||||
|
||||
for (l, r) in l_cols.iter().zip(r_cols.iter()) {
|
||||
for (l, r) in l_cols.iter().zip(r_cols) {
|
||||
let l_series = df_l
|
||||
.column(l.as_ref())
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, l_col_span, None))?;
|
||||
|
@ -5,6 +5,7 @@ use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
|
||||
use super::utils::convert_columns;
|
||||
|
||||
@ -33,6 +34,18 @@ impl WholeStreamCommand for DataFrame {
|
||||
"column names used as value columns",
|
||||
Some('v'),
|
||||
)
|
||||
.named(
|
||||
"variable_name",
|
||||
SyntaxShape::String,
|
||||
"optional name for variable column",
|
||||
Some('r'),
|
||||
)
|
||||
.named(
|
||||
"value_name",
|
||||
SyntaxShape::String,
|
||||
"optional name for value column",
|
||||
Some('l'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
@ -105,6 +118,9 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let id_col: Vec<Value> = args.req_named("columns")?;
|
||||
let val_col: Vec<Value> = args.req_named("values")?;
|
||||
|
||||
let value_name: Option<Tagged<String>> = args.get_flag("value_name")?;
|
||||
let variable_name: Option<Tagged<String>> = args.get_flag("variable_name")?;
|
||||
|
||||
let (id_col_string, id_col_span) = convert_columns(&id_col, &tag)?;
|
||||
let (val_col_string, val_col_span) = convert_columns(&val_col, &tag)?;
|
||||
|
||||
@ -113,11 +129,21 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
check_column_datatypes(df.as_ref(), &id_col_string, &id_col_span)?;
|
||||
check_column_datatypes(df.as_ref(), &val_col_string, &val_col_span)?;
|
||||
|
||||
let res = df
|
||||
let mut res = df
|
||||
.as_ref()
|
||||
.melt(&id_col_string, &val_col_string)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
|
||||
if let Some(name) = &variable_name {
|
||||
res.rename("variable", &name.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &name.tag.span, None))?;
|
||||
}
|
||||
|
||||
if let Some(name) = &value_name {
|
||||
res.rename("value", &name.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &name.tag.span, None))?;
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ pub mod list;
|
||||
pub mod melt;
|
||||
pub mod open;
|
||||
pub mod pivot;
|
||||
pub mod rename;
|
||||
pub mod sample;
|
||||
pub mod select;
|
||||
pub mod shape;
|
||||
@ -52,6 +53,7 @@ pub use list::DataFrame as DataFrameList;
|
||||
pub use melt::DataFrame as DataFrameMelt;
|
||||
pub use open::DataFrame as DataFrameOpen;
|
||||
pub use pivot::DataFrame as DataFramePivot;
|
||||
pub use rename::DataFrame as DataFrameRename;
|
||||
pub use sample::DataFrame as DataFrameSample;
|
||||
pub use select::DataFrame as DataFrameSelect;
|
||||
pub use shape::DataFrame as DataFrameShape;
|
||||
|
@ -8,7 +8,7 @@ use nu_protocol::{
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
use polars::prelude::{CsvEncoding, CsvReader, JsonReader, ParquetReader, PolarsError, SerReader};
|
||||
use polars::prelude::{CsvEncoding, CsvReader, JsonReader, ParquetReader, SerReader};
|
||||
use std::fs::File;
|
||||
|
||||
pub struct DataFrame;
|
||||
@ -206,15 +206,6 @@ fn from_csv(args: CommandArgs) -> Result<polars::prelude::DataFrame, ShellError>
|
||||
|
||||
match csv_reader.finish() {
|
||||
Ok(df) => Ok(df),
|
||||
Err(e) => match e {
|
||||
PolarsError::Other(_) => Err(ShellError::labeled_error_with_secondary(
|
||||
"Schema error",
|
||||
"Error with the inferred schema",
|
||||
&file.tag.span,
|
||||
"You can use the argument 'infer_schema' with a number of rows large enough to better infer the schema",
|
||||
&file.tag.span,
|
||||
)),
|
||||
_ => Err(parse_polars_error::<&str>(&e, &file.tag.span, None)),
|
||||
},
|
||||
Err(e) => Err(parse_polars_error::<&str>(&e, &file.tag.span, None)),
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let mut groupby = nu_groupby.to_groupby()?;
|
||||
|
||||
let pivot = groupby.pivot(pivot_col.item.as_ref(), value_col.item.as_ref());
|
||||
let pivot = groupby.pivot(&pivot_col.item, &value_col.item);
|
||||
|
||||
let res = match op {
|
||||
Operation::Mean => pivot.mean(),
|
||||
@ -120,7 +120,7 @@ fn check_pivot_column(
|
||||
col: &Tagged<String>,
|
||||
) -> Result<(), ShellError> {
|
||||
let series = df
|
||||
.column(col.item.as_ref())
|
||||
.column(&col.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col.tag.span, None))?;
|
||||
|
||||
match series.dtype() {
|
||||
@ -146,7 +146,7 @@ fn check_value_column(
|
||||
col: &Tagged<String>,
|
||||
) -> Result<(), ShellError> {
|
||||
let series = df
|
||||
.column(col.item.as_ref())
|
||||
.column(&col.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col.tag.span, None))?;
|
||||
|
||||
match series.dtype() {
|
||||
|
81
crates/nu-command/src/commands/dataframe/rename.rs
Normal file
81
crates/nu-command/src/commands/dataframe/rename.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
|
||||
use super::utils::parse_polars_error;
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe rename-col"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] rename a dataframe column"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe rename-col")
|
||||
.required("from", SyntaxShape::String, "column name to be renamed")
|
||||
.required("to", SyntaxShape::String, "new column name")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Renames a dataframe column",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe rename-col a ab",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"ab".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let from: Tagged<String> = args.req(0)?;
|
||||
let to: Tagged<String> = args.req(1)?;
|
||||
|
||||
let (mut df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
df.as_mut()
|
||||
.rename(&from.item, &to.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ impl WholeStreamCommand for DataFrame {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe select").rest(SyntaxShape::Any, "selected column names")
|
||||
Signature::build("dataframe select").rest("rest", SyntaxShape::Any, "selected column names")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
@ -68,7 +68,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
})?;
|
||||
|
||||
let res = chunked
|
||||
.contains(pattern.as_str())
|
||||
.contains(&pattern.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
|
@ -99,11 +99,11 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
));
|
||||
}
|
||||
|
||||
let cum_type = CumType::from_str(cum_type.item.as_str(), &cum_type.tag.span)?;
|
||||
let cum_type = CumType::from_str(&cum_type.item, &cum_type.tag.span)?;
|
||||
let mut res = match cum_type {
|
||||
CumType::Max => series.cum_max(reverse),
|
||||
CumType::Min => series.cum_min(reverse),
|
||||
CumType::Sum => series.cum_sum(reverse),
|
||||
CumType::Max => series.cummax(reverse),
|
||||
CumType::Min => series.cummin(reverse),
|
||||
CumType::Sum => series.cumsum(reverse),
|
||||
};
|
||||
|
||||
let name = format!("{}_{}", series.name(), cum_type.to_str());
|
||||
|
@ -53,7 +53,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.day().into_series();
|
||||
|
@ -53,7 +53,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.hour().into_series();
|
||||
|
@ -53,7 +53,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.minute().into_series();
|
||||
|
@ -53,7 +53,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.month().into_series();
|
||||
|
@ -53,7 +53,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.nanosecond().into_series();
|
||||
|
@ -56,7 +56,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.ordinal().into_series();
|
||||
|
@ -53,7 +53,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.second().into_series();
|
||||
|
@ -53,7 +53,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.week().into_series();
|
||||
|
@ -53,7 +53,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.weekday().into_series();
|
||||
|
@ -56,7 +56,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.year().into_series();
|
||||
|
@ -60,7 +60,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let mut series = df.as_series(&df_tag.span)?;
|
||||
|
||||
series.rename(name.item.as_ref());
|
||||
series.rename(&name.item);
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![series], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
|
@ -77,7 +77,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
})?;
|
||||
|
||||
let mut res = chunked
|
||||
.replace(pattern.as_str(), replace.as_str())
|
||||
.replace(&pattern.item, &replace.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
|
||||
res.rename(series.name());
|
||||
|
@ -77,7 +77,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
})?;
|
||||
|
||||
let mut res = chunked
|
||||
.replace_all(pattern.as_str(), replace.as_str())
|
||||
.replace_all(&pattern.item, &replace.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
|
||||
res.rename(series.name());
|
||||
|
@ -6,7 +6,7 @@ use nu_protocol::{
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use polars::prelude::DataType;
|
||||
use polars::prelude::{DataType, RollingOptions};
|
||||
|
||||
enum RollType {
|
||||
Min,
|
||||
@ -57,7 +57,6 @@ impl WholeStreamCommand for DataFrame {
|
||||
Signature::build("dataframe rolling")
|
||||
.required("type", SyntaxShape::String, "rolling operation")
|
||||
.required("window", SyntaxShape::Int, "Window size for rolling")
|
||||
.switch("ignore_nulls", "Ignore nulls in column", Some('i'))
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
@ -112,7 +111,6 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let roll_type: Tagged<String> = args.req(0)?;
|
||||
let window_size: Tagged<i64> = args.req(1)?;
|
||||
let ignore_nulls = args.has_flag("ignore_nulls");
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
@ -125,32 +123,18 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
));
|
||||
}
|
||||
|
||||
let roll_type = RollType::from_str(roll_type.item.as_str(), &roll_type.tag.span)?;
|
||||
let roll_type = RollType::from_str(&roll_type.item, &roll_type.tag.span)?;
|
||||
let rolling_opts = RollingOptions {
|
||||
window_size: window_size.item as usize,
|
||||
min_periods: window_size.item as usize,
|
||||
weights: None,
|
||||
center: false,
|
||||
};
|
||||
let res = match roll_type {
|
||||
RollType::Max => series.rolling_max(
|
||||
window_size.item as u32,
|
||||
None,
|
||||
ignore_nulls,
|
||||
window_size.item as u32,
|
||||
),
|
||||
RollType::Min => series.rolling_min(
|
||||
window_size.item as u32,
|
||||
None,
|
||||
ignore_nulls,
|
||||
window_size.item as u32,
|
||||
),
|
||||
RollType::Sum => series.rolling_sum(
|
||||
window_size.item as u32,
|
||||
None,
|
||||
ignore_nulls,
|
||||
window_size.item as u32,
|
||||
),
|
||||
RollType::Mean => series.rolling_mean(
|
||||
window_size.item as u32,
|
||||
None,
|
||||
ignore_nulls,
|
||||
window_size.item as u32,
|
||||
),
|
||||
RollType::Max => series.rolling_max(rolling_opts),
|
||||
RollType::Min => series.rolling_min(rolling_opts),
|
||||
RollType::Sum => series.rolling_sum(rolling_opts),
|
||||
RollType::Mean => series.rolling_mean(rolling_opts),
|
||||
};
|
||||
|
||||
let mut res = res.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
@ -78,7 +78,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let casted = match indices.dtype() {
|
||||
DataType::UInt32 | DataType::UInt64 | DataType::Int32 | DataType::Int64 => indices
|
||||
.as_ref()
|
||||
.cast_with_dtype(&DataType::UInt32)
|
||||
.cast(&DataType::UInt32)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &value.tag.span, None)),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Incorrect type",
|
||||
|
@ -58,10 +58,10 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.datetime()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.strftime(fmt.item.as_str()).into_series();
|
||||
let res = casted.strftime(&fmt.item).into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe sort")
|
||||
.switch("reverse", "invert sort", Some('r'))
|
||||
.rest(SyntaxShape::Any, "column names to sort dataframe")
|
||||
.rest("rest", SyntaxShape::Any, "column names to sort dataframe")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
@ -92,7 +92,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let casted = match series.dtype() {
|
||||
DataType::UInt32 | DataType::UInt64 | DataType::Int32 | DataType::Int64 => series
|
||||
.as_ref()
|
||||
.cast_with_dtype(&DataType::UInt32)
|
||||
.cast(&DataType::UInt32)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &value.tag.span, None)),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Incorrect type",
|
||||
|
@ -64,22 +64,18 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let delimiter: Option<Tagged<String>> = args.get_flag("delimiter")?;
|
||||
let no_header: bool = args.has_flag("no_header");
|
||||
|
||||
let (mut df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let mut file = File::create(&file_name.item).map_err(|e| {
|
||||
ShellError::labeled_error(
|
||||
"Error with file name",
|
||||
format!("{}", e),
|
||||
&file_name.tag.span,
|
||||
)
|
||||
ShellError::labeled_error("Error with file name", e.to_string(), &file_name.tag.span)
|
||||
})?;
|
||||
|
||||
let writer = CsvWriter::new(&mut file);
|
||||
|
||||
let writer = if no_header {
|
||||
writer.has_headers(false)
|
||||
writer.has_header(false)
|
||||
} else {
|
||||
writer.has_headers(true)
|
||||
writer.has_header(true)
|
||||
};
|
||||
|
||||
let writer = match delimiter {
|
||||
@ -103,7 +99,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
};
|
||||
|
||||
writer
|
||||
.finish(df.as_mut())
|
||||
.finish(df.as_ref())
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &file_name.tag.span, None))?;
|
||||
|
||||
let tagged_value = Value {
|
||||
|
@ -48,18 +48,14 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let file_name: Tagged<PathBuf> = args.req(0)?;
|
||||
|
||||
let (mut df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let file = File::create(&file_name.item).map_err(|e| {
|
||||
ShellError::labeled_error(
|
||||
"Error with file name",
|
||||
format!("{}", e),
|
||||
&file_name.tag.span,
|
||||
)
|
||||
ShellError::labeled_error("Error with file name", e.to_string(), &file_name.tag.span)
|
||||
})?;
|
||||
|
||||
ParquetWriter::new(file)
|
||||
.finish(df.as_mut())
|
||||
.finish(df.as_ref())
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &file_name.tag.span, None))?;
|
||||
|
||||
let tagged_value = Value {
|
||||
|
@ -46,30 +46,31 @@ pub(crate) fn parse_polars_error<T: AsRef<str>>(
|
||||
span: &Span,
|
||||
secondary: Option<T>,
|
||||
) -> ShellError {
|
||||
let (msg, label) = match e {
|
||||
PolarsError::PolarsArrowError(_) => ("PolarsArrow Error", format!("{}", e)),
|
||||
PolarsError::ArrowError(_) => ("Arrow Error", format!("{}", e)),
|
||||
PolarsError::InvalidOperation(_) => ("Invalid Operation", format!("{}", e)),
|
||||
PolarsError::DataTypeMisMatch(_) => ("Data Type Mismatch", format!("{}", e)),
|
||||
PolarsError::NotFound(_) => ("Not Found", format!("{}", e)),
|
||||
PolarsError::ShapeMisMatch(_) => ("Shape Mismatch", format!("{}", e)),
|
||||
PolarsError::Other(_) => ("Other", format!("{}", e)),
|
||||
PolarsError::OutOfBounds(_) => ("Out Of Bounds", format!("{}", e)),
|
||||
PolarsError::NoSlice => ("No Slice", format!("{}", e)),
|
||||
PolarsError::NoData(_) => ("No Data", format!("{}", e)),
|
||||
PolarsError::ValueError(_) => ("Value Error", format!("{}", e)),
|
||||
PolarsError::MemoryNotAligned => ("Memory Not Aligned", format!("{}", e)),
|
||||
PolarsError::ParquetError(_) => ("Parquet Error", format!("{}", e)),
|
||||
PolarsError::RandError(_) => ("Rand Error", format!("{}", e)),
|
||||
PolarsError::HasNullValues(_) => ("Has Null Values", format!("{}", e)),
|
||||
PolarsError::UnknownSchema(_) => ("Unknown Schema", format!("{}", e)),
|
||||
PolarsError::Various(_) => ("Various", format!("{}", e)),
|
||||
PolarsError::Io(_) => ("Io Error", format!("{}", e)),
|
||||
PolarsError::Regex(_) => ("Regex Error", format!("{}", e)),
|
||||
PolarsError::Duplicate(_) => ("Duplicate Error", format!("{}", e)),
|
||||
PolarsError::ImplementationError => ("Implementation Error", format!("{}", e)),
|
||||
let msg = match e {
|
||||
PolarsError::PolarsArrowError(_) => "PolarsArrow Error",
|
||||
PolarsError::ArrowError(_) => "Arrow Error",
|
||||
PolarsError::InvalidOperation(_) => "Invalid Operation",
|
||||
PolarsError::DataTypeMisMatch(_) => "Data Type Mismatch",
|
||||
PolarsError::NotFound(_) => "Not Found",
|
||||
PolarsError::ShapeMisMatch(_) => "Shape Mismatch",
|
||||
PolarsError::ComputeError(_) => "Computer error",
|
||||
PolarsError::OutOfBounds(_) => "Out Of Bounds",
|
||||
PolarsError::NoSlice => "No Slice",
|
||||
PolarsError::NoData(_) => "No Data",
|
||||
PolarsError::ValueError(_) => "Value Error",
|
||||
PolarsError::MemoryNotAligned => "Memory Not Aligned",
|
||||
PolarsError::RandError(_) => "Rand Error",
|
||||
PolarsError::HasNullValues(_) => "Has Null Values",
|
||||
PolarsError::UnknownSchema(_) => "Unknown Schema",
|
||||
PolarsError::Various(_) => "Various",
|
||||
PolarsError::Io(_) => "Io Error",
|
||||
PolarsError::Regex(_) => "Regex Error",
|
||||
PolarsError::Duplicate(_) => "Duplicate Error",
|
||||
PolarsError::ImplementationError => "Implementation Error",
|
||||
};
|
||||
|
||||
let label = e.to_string();
|
||||
|
||||
match secondary {
|
||||
None => ShellError::labeled_error(msg, label, span),
|
||||
Some(s) => ShellError::labeled_error_with_secondary(msg, label, span, s.as_ref(), span),
|
||||
|
@ -82,7 +82,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let mut series = df.as_series(&value.tag.span)?;
|
||||
|
||||
let series = series.rename(name.item.as_ref()).clone();
|
||||
let series = series.rename(&name.item).clone();
|
||||
|
||||
let (mut df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
|
@ -31,12 +31,12 @@ impl WholeStreamCommand for AutoenvTrust {
|
||||
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
||||
tag: _,
|
||||
}) => {
|
||||
let mut dir = fs::canonicalize(path)?;
|
||||
let mut dir = nu_path::canonicalize(path)?;
|
||||
dir.push(".nu-env");
|
||||
dir
|
||||
}
|
||||
_ => {
|
||||
let mut dir = fs::canonicalize(std::env::current_dir()?)?;
|
||||
let mut dir = nu_path::canonicalize(std::env::current_dir()?)?;
|
||||
dir.push(".nu-env");
|
||||
dir
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ impl WholeStreamCommand for AutoenvUntrust {
|
||||
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
||||
tag: _,
|
||||
}) => {
|
||||
let mut dir = fs::canonicalize(path)?;
|
||||
let mut dir = nu_path::canonicalize(path)?;
|
||||
dir.push(".nu-env");
|
||||
dir
|
||||
}
|
||||
@ -61,7 +61,7 @@ impl WholeStreamCommand for AutoenvUntrust {
|
||||
let mut doc = String::new();
|
||||
file.read_to_string(&mut doc)?;
|
||||
|
||||
let mut allowed: Trusted = toml::from_str(doc.as_str()).unwrap_or_else(|_| Trusted::new());
|
||||
let mut allowed: Trusted = toml::from_str(&doc).unwrap_or_else(|_| Trusted::new());
|
||||
|
||||
let file_to_untrust = file_to_untrust.to_string_lossy().to_string();
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_engine::{evaluate_baseline_expr, WholeStreamCommand};
|
||||
use nu_engine::{evaluate_baseline_expr, EnvVar, WholeStreamCommand};
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::CapturedBlock, hir::ClassifiedCommand, Signature, SyntaxShape};
|
||||
@ -90,9 +92,7 @@ pub fn set_env(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
|
||||
ctx.scope.exit_scope();
|
||||
|
||||
let value = value?;
|
||||
let value = value.as_string()?;
|
||||
|
||||
let value: EnvVar = value?.try_into()?;
|
||||
let name = name.item;
|
||||
|
||||
// Note: this is a special case for setting the context from a command
|
||||
|
13
crates/nu-command/src/commands/env/load_env.rs
vendored
13
crates/nu-command/src/commands/env/load_env.rs
vendored
@ -1,5 +1,7 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_engine::{EnvVar, WholeStreamCommand};
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||
@ -60,14 +62,17 @@ fn load_env_from_table(
|
||||
|
||||
for (key, value) in value.row_entries() {
|
||||
if key == "name" {
|
||||
var_name = Some(value.as_string()?);
|
||||
var_name = Some(value);
|
||||
} else if key == "value" {
|
||||
var_value = Some(value.as_string()?);
|
||||
var_value = Some(value);
|
||||
}
|
||||
}
|
||||
|
||||
match (var_name, var_value) {
|
||||
(Some(name), Some(value)) => ctx.scope.add_env_var(name, value),
|
||||
(Some(name), Some(value)) => {
|
||||
let env_var: EnvVar = value.try_into()?;
|
||||
ctx.scope.add_env_var(name.as_string()?, env_var);
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
r#"Expected each row in the table to have a "name" and "value" field."#,
|
||||
|
17
crates/nu-command/src/commands/env/with_env.rs
vendored
17
crates/nu-command/src/commands/env/with_env.rs
vendored
@ -1,5 +1,8 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::EnvVar;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
@ -8,12 +11,6 @@ use nu_protocol::{
|
||||
|
||||
pub struct WithEnv;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct WithEnvArgs {
|
||||
variable: Value,
|
||||
block: CapturedBlock,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for WithEnv {
|
||||
fn name(&self) -> &str {
|
||||
"with-env"
|
||||
@ -73,20 +70,20 @@ fn with_env(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let variable: Value = args.req(0)?;
|
||||
let block: CapturedBlock = args.req(1)?;
|
||||
|
||||
let mut env = IndexMap::new();
|
||||
let mut env: IndexMap<String, EnvVar> = IndexMap::new();
|
||||
|
||||
match &variable.value {
|
||||
UntaggedValue::Table(table) => {
|
||||
if table.len() == 1 {
|
||||
// single row([[X W]; [Y Z]])
|
||||
for (k, v) in table[0].row_entries() {
|
||||
env.insert(k.clone(), v.convert_to_string());
|
||||
env.insert(k.clone(), v.try_into()?);
|
||||
}
|
||||
} else {
|
||||
// primitive values([X Y W Z])
|
||||
for row in table.chunks(2) {
|
||||
if row.len() == 2 && row[0].is_primitive() && row[1].is_primitive() {
|
||||
env.insert(row[0].convert_to_string(), row[1].convert_to_string());
|
||||
env.insert(row[0].convert_to_string(), (&row[1]).try_into()?);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,7 +91,7 @@ fn with_env(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
// when get object by `open x.json` or `from json`
|
||||
UntaggedValue::Row(row) => {
|
||||
for (k, v) in &row.entries {
|
||||
env.insert(k.clone(), v.convert_to_string());
|
||||
env.insert(k.clone(), v.try_into()?);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
@ -12,6 +12,7 @@ impl WholeStreamCommand for Mkdir {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("mkdir")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::FilePath,
|
||||
"the name(s) of the path(s) to create",
|
||||
)
|
||||
|
@ -6,6 +6,7 @@ use log::debug;
|
||||
use nu_engine::StringOrBinary;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_path::canonicalize;
|
||||
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::{AnchorLocation, Span, Tagged};
|
||||
use std::path::{Path, PathBuf};
|
||||
@ -172,9 +173,9 @@ fn open(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(StringOrBinary::String(s)) => {
|
||||
ReturnSuccess::value(UntaggedValue::string(s).into_value(file_tag))
|
||||
}
|
||||
Ok(StringOrBinary::Binary(b)) => ReturnSuccess::value(
|
||||
UntaggedValue::binary(b.into_iter().collect()).into_value(file_tag),
|
||||
),
|
||||
Ok(StringOrBinary::Binary(b)) => {
|
||||
ReturnSuccess::value(UntaggedValue::binary(b).into_value(file_tag))
|
||||
}
|
||||
Err(se) => Err(se),
|
||||
}
|
||||
});
|
||||
@ -193,7 +194,7 @@ pub fn fetch(
|
||||
// TODO: I don't understand the point of this? Maybe for better error reporting
|
||||
let mut cwd = PathBuf::from(cwd);
|
||||
cwd.push(location);
|
||||
let nice_location = dunce::canonicalize(&cwd).map_err(|e| match e.kind() {
|
||||
let nice_location = canonicalize(&cwd).map_err(|e| match e.kind() {
|
||||
std::io::ErrorKind::NotFound => ShellError::labeled_error(
|
||||
format!("Cannot find file {:?}", cwd),
|
||||
"cannot find file",
|
||||
|
@ -26,7 +26,11 @@ impl WholeStreamCommand for Remove {
|
||||
)
|
||||
.switch("recursive", "delete subdirectories recursively", Some('r'))
|
||||
.switch("force", "suppress error when no file", Some('f'))
|
||||
.rest(SyntaxShape::GlobPattern, "the file path(s) to remove")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::GlobPattern,
|
||||
"the file path(s) to remove",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -142,6 +142,7 @@ impl WholeStreamCommand for Save {
|
||||
"treat values as-is rather than auto-converting based on file extension",
|
||||
Some('r'),
|
||||
)
|
||||
.switch("append", "append values rather than overriding", Some('a'))
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -165,6 +166,7 @@ fn save(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let path: Option<Tagged<PathBuf>> = args.opt(0)?;
|
||||
let save_raw = args.has_flag("raw");
|
||||
let append = args.has_flag("append");
|
||||
|
||||
let input: Vec<Value> = args.input.collect();
|
||||
if path.is_none() {
|
||||
@ -231,7 +233,7 @@ fn save(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
};
|
||||
};
|
||||
|
||||
shell_manager.save(&full_path, &content?, name.span)
|
||||
shell_manager.save(&full_path, &content?, name.span, append)
|
||||
}
|
||||
|
||||
fn string_from(input: &[Value]) -> String {
|
||||
@ -239,7 +241,7 @@ fn string_from(input: &[Value]) -> String {
|
||||
|
||||
if !input.is_empty() {
|
||||
let mut first = true;
|
||||
for i in input.iter() {
|
||||
for i in input {
|
||||
if !first {
|
||||
save_data.push('\n');
|
||||
} else {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user