mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 15:11:52 +02:00
Compare commits
148 Commits
Author | SHA1 | Date | |
---|---|---|---|
2ec2028637 | |||
b84a01cb1d | |||
ca4d8008d4 | |||
a256f6d0d1 | |||
0aa6954f33 | |||
68d98fcf24 | |||
87086262f3 | |||
3fab427383 | |||
61fa826159 | |||
0b9fc4ff3a | |||
1817d5e01e | |||
0ca0f8ec17 | |||
6be5631477 | |||
678e942bd8 | |||
83ddf0ebe2 | |||
eaea00366b | |||
0788fe5e72 | |||
b2257a5ca3 | |||
3bf5999ef4 | |||
8a85299575 | |||
3db0aed9f7 | |||
09276db2a5 | |||
0e496f900d | |||
1ed645c6c2 | |||
bc6948dc89 | |||
e9c17daecd | |||
53beba7acc | |||
ed0fce9aa6 | |||
995603b08c | |||
a49e5b30ff | |||
97e7d550c8 | |||
bc54930bc6 | |||
0d6e43097d | |||
393717dbb4 | |||
da8cb14f8b | |||
9f01cf333c | |||
d391e912ff | |||
8b185a4008 | |||
90b65018b6 | |||
7ec5f2f2eb | |||
332f1192a6 | |||
944cad35bf | |||
86ae27b0c1 | |||
d9a888528a | |||
05f1b41275 | |||
5b03bca138 | |||
d409171ba8 | |||
0567407f85 | |||
6872d2ac2a | |||
ad4450f9e8 | |||
d8478ca690 | |||
aab31833a2 | |||
c0648a83be | |||
744a28b31d | |||
b4b68afa17 | |||
dd22647fcd | |||
f66136bc86 | |||
8cf9bc9993 | |||
d0aa69bfcb | |||
4a1d12462f | |||
8d5fbc6fcb | |||
85bfdba1e2 | |||
546c753d1e | |||
2c3aade057 | |||
be52f7fb07 | |||
ec5396a352 | |||
403bf1a734 | |||
05ff7a9925 | |||
5c2a767987 | |||
35798ce0cc | |||
47d6a66fbf | |||
616f065324 | |||
1e39a1a7a3 | |||
66ad83c15c | |||
c48e9cdf5b | |||
6a274b860a | |||
2f8a52d256 | |||
0f4a073eaf | |||
626410b2aa | |||
a193b85123 | |||
0f40c44ed2 | |||
cd6f86052d | |||
b9858ea8f8 | |||
cb1eefd24a | |||
a1840e9d20 | |||
b01cbf82c3 | |||
e89c796b41 | |||
758351c732 | |||
77d33766f1 | |||
b0be6c3013 | |||
93e5d8edc9 | |||
9c6bfc0be9 | |||
77e73cef66 | |||
7d963776a0 | |||
ecc153cbef | |||
d1309a36b2 | |||
1d3f6105f5 | |||
e36a2947b9 | |||
64f50a179e | |||
f9cf1d943c | |||
10a42de64f | |||
c66bd5e809 | |||
3f224db990 | |||
491a9c019c | |||
77e9f8d7df | |||
14bf0b000e | |||
0ca49091c0 | |||
bb8949f2b2 | |||
ef7fbf4bf9 | |||
eb2e2e6370 | |||
a8eef9af33 | |||
400a9d3b1e | |||
7095d8994e | |||
2d41613039 | |||
1552eb921a | |||
0ac3f7a1c8 | |||
bddb63ccb5 | |||
7625aed200 | |||
19beafa865 | |||
8543b0789d | |||
bdaa01165e | |||
b2a557d4ed | |||
0903a891e4 | |||
1b2916988e | |||
d74a260883 | |||
31d9c0889c | |||
222c0f11c3 | |||
106ca65c58 | |||
4c97b3dd28 | |||
e672689a76 | |||
2579a827fc | |||
8c487edf62 | |||
0b97f52a8b | |||
21b84a6d65 | |||
d3be5ec750 | |||
4b16406050 | |||
b0ce602e4b | |||
aa876ce24f | |||
71fdf717a8 | |||
4de0347fdc | |||
f34ac9be62 | |||
12652f897a | |||
24ee381fea | |||
494a07f6f3 | |||
61455b457d | |||
57ce6a7c66 | |||
0bd4d27e8d | |||
1701303279 |
1
.github/pull_request_template.md
vendored
1
.github/pull_request_template.md
vendored
@ -18,6 +18,7 @@ Make sure you've run and fixed any issues with these commands:
|
|||||||
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
||||||
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
|
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
|
||||||
- `cargo test --workspace` to check that all tests pass
|
- `cargo test --workspace` to check that all tests pass
|
||||||
|
- `cargo run -- crates/nu-utils/standard_library/tests.nu` to run the tests for the standard library
|
||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
> from `nushell` you can also use the `toolkit` as follows
|
> from `nushell` you can also use the `toolkit` as follows
|
||||||
|
88
.github/workflows/ci.yml
vendored
88
.github/workflows/ci.yml
vendored
@ -40,7 +40,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.4.3
|
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
|
||||||
|
|
||||||
- name: cargo fmt
|
- name: cargo fmt
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
@ -77,14 +77,14 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.4.3
|
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
||||||
|
|
||||||
python-virtualenv:
|
std-lib-and-python-virtualenv:
|
||||||
env:
|
env:
|
||||||
NUSHELL_CARGO_TARGET: ci
|
NU_LOG_LEVEL: DEBUG
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
@ -101,10 +101,22 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.4.3
|
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
|
||||||
|
|
||||||
- name: Install Nushell
|
- name: Install Nushell
|
||||||
run: cargo install --locked --path=. --profile ci --no-default-features
|
# prior to [*standard library: bring the tests into the main CI*](#8525)
|
||||||
|
# there was a `--profile ci` here in the `cargo install`, as well as
|
||||||
|
# `NUSHELL_CARGO_TARGET: ci` in the prelude above.
|
||||||
|
#
|
||||||
|
# this caused a "stackoverflow" error in the CI on windows,
|
||||||
|
# see [this failing job](https://github.com/nushell/nushell/actions/runs/4512034615/jobs/7944945590)
|
||||||
|
#
|
||||||
|
# the CI profile has been removed in 00b820de9021227d1910a9ea388297ee7aee308e
|
||||||
|
# as part of #8525.
|
||||||
|
run: cargo install --path . --locked --no-default-features
|
||||||
|
|
||||||
|
- name: Standard library tests
|
||||||
|
run: nu crates/nu-utils/standard_library/tests.nu
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@ -148,7 +160,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.4.3
|
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
|
||||||
|
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
run: cargo clippy --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
@ -157,40 +169,40 @@ jobs:
|
|||||||
run: cargo test --profile ci --package nu_plugin_*
|
run: cargo test --profile ci --package nu_plugin_*
|
||||||
|
|
||||||
|
|
||||||
nu-coverage:
|
# nu-coverage:
|
||||||
needs: nu-tests
|
# needs: nu-tests
|
||||||
env:
|
# env:
|
||||||
NUSHELL_CARGO_TARGET: ci
|
# NUSHELL_CARGO_TARGET: ci
|
||||||
|
|
||||||
strategy:
|
# strategy:
|
||||||
fail-fast: true
|
# fail-fast: true
|
||||||
matrix:
|
# matrix:
|
||||||
# disabled mac due to problems with merging coverage and similarity to linux
|
# # disabled mac due to problems with merging coverage and similarity to linux
|
||||||
# disabled windows due to running out of disk space when having too many crates or tests
|
# # disabled windows due to running out of disk space when having too many crates or tests
|
||||||
platform: [ubuntu-20.04] # windows-latest
|
# platform: [ubuntu-20.04] # windows-latest
|
||||||
rust:
|
# rust:
|
||||||
- stable
|
# - stable
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
# runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
# steps:
|
||||||
- uses: actions/checkout@v3
|
# - uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
# - name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.4.3
|
# uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
|
||||||
- name: Install cargo-llvm-cov
|
# - name: Install cargo-llvm-cov
|
||||||
uses: taiki-e/install-action@cargo-llvm-cov
|
# uses: taiki-e/install-action@cargo-llvm-cov
|
||||||
|
|
||||||
- name: Tests
|
# - name: Tests
|
||||||
shell: bash
|
# shell: bash
|
||||||
run: |
|
# run: |
|
||||||
source <(cargo llvm-cov show-env --export-prefix) # Set the environment variables needed to get coverage.
|
# source <(cargo llvm-cov show-env --export-prefix) # Set the environment variables needed to get coverage.
|
||||||
cargo llvm-cov clean --workspace # Remove artifacts that may affect the coverage results.
|
# cargo llvm-cov clean --workspace # Remove artifacts that may affect the coverage results.
|
||||||
cargo build --workspace --profile ci
|
# cargo build --workspace --profile ci
|
||||||
cargo test --workspace --profile ci
|
# cargo test --workspace --profile ci
|
||||||
cargo llvm-cov report --profile ci --lcov --output-path lcov.info
|
# cargo llvm-cov report --profile ci --lcov --output-path lcov.info
|
||||||
|
|
||||||
- name: Upload coverage reports to Codecov with GitHub Action
|
# - name: Upload coverage reports to Codecov with GitHub Action
|
||||||
uses: codecov/codecov-action@v3
|
# uses: codecov/codecov-action@v3
|
||||||
with:
|
# with:
|
||||||
files: lcov.info
|
# files: lcov.info
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -70,7 +70,7 @@ jobs:
|
|||||||
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.4.3
|
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -22,6 +22,9 @@ debian/nu/
|
|||||||
# VSCode's IDE items
|
# VSCode's IDE items
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|
||||||
|
# JetBrains' Fleet IDE
|
||||||
|
.fleet/*
|
||||||
|
|
||||||
# Visual Studio Extension SourceGear Rust items
|
# Visual Studio Extension SourceGear Rust items
|
||||||
VSWorkspaceSettings.json
|
VSWorkspaceSettings.json
|
||||||
unstable_cargo_features.txt
|
unstable_cargo_features.txt
|
||||||
|
@ -99,3 +99,90 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
|
|||||||
cargo run --release -- --log-level trace --log-target file
|
cargo run --release -- --log-level trace --log-target file
|
||||||
open $"($nu.temp-path)/nu-($nu.pid).log"
|
open $"($nu.temp-path)/nu-($nu.pid).log"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Git etiquette
|
||||||
|
|
||||||
|
As nushell thrives on its broad base of volunteer contributors and maintainers with different backgrounds we have a few guidelines for how we best utilize git and GitHub for our contributions. We strive to balance three goals with those recommendations:
|
||||||
|
|
||||||
|
1. The **volunteer maintainers and contributors** can easily follow the changes you propose, gauge the impact, and come to help you or make a decision.
|
||||||
|
2. **You as a contributor** can focus most of your time on improving the quality of the nushell project and contributing your expertise to the code or documentation.
|
||||||
|
3. Making sure we can trace back *why* decisions were made in the past.
|
||||||
|
This includes discarded approaches. Also we want to quickly identify regressions and fix when something broke.
|
||||||
|
|
||||||
|
### How we merge PRs
|
||||||
|
|
||||||
|
In general the maintainers **squash** all changes of your PR into a single commit when merging.
|
||||||
|
|
||||||
|
This keeps a clean enough linear history, while not forcing you to conform to a too strict style while iterating in your PR or fixing small problems. As an added benefit the commits on the `main` branch are tied to the discussion that happened in the PR through their `#1234` issue number.
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
> **Pro advice:** In some circumstances, we can agree on rebase-merging a particularly large but connected PR as a series of atomic commits onto the `main` branch to ensure we can more easily revert or bisect particular aspects.
|
||||||
|
|
||||||
|
### A good PR makes a change!
|
||||||
|
|
||||||
|
As a result of this PR-centric strategy and the general goal that the reviewers should easily understand your change, the **PR title and description matters** a great deal!
|
||||||
|
|
||||||
|
Make sure your description is **concise** but contains all relevant information and context.
|
||||||
|
This means demonstrating what changes, ideally through nushell code or output **examples**.
|
||||||
|
Furthermore links to technical documentation or instructions for folks that want to play with your change make the review process much easier.
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
> Try to follow the suggestions in our PR message template to make sure we can quickly focus on the technical merits and impact on the users.
|
||||||
|
|
||||||
|
#### A PR should limit itself to a single functional change or related set of same changes.
|
||||||
|
|
||||||
|
Mixing different changes in the same PR will make the review process much harder. A PR might get stuck on one aspect while we would actually like to land another change. Furthermore, if we are forced to revert a change, mixing and matching different aspects makes fixing bugs or regressions much harder.
|
||||||
|
|
||||||
|
Thus, please try to **separate out unrelated changes**!
|
||||||
|
**Don't** mix unrelated refactors with a potentially contested change.
|
||||||
|
Stylistic fixes and housekeeping can be bundled up into singular PRs.
|
||||||
|
|
||||||
|
#### Guidelines for the PR title
|
||||||
|
|
||||||
|
The PR title should be concise but contain everything for a contributor to know if they should help out in the review of this particular change.
|
||||||
|
|
||||||
|
**DON'T**
|
||||||
|
- `Update file/in/some/deeply/nested/path.rs`
|
||||||
|
- Why are you making this change?
|
||||||
|
- `Fix 2134`
|
||||||
|
- What has to be fixed?
|
||||||
|
- Hard to follow when not online on GitHub.
|
||||||
|
- ``Ignore `~` expansion``
|
||||||
|
- In what context should this change take effect?
|
||||||
|
- `[feature] refactor the whole parser and also make nushell indentation-sensitive, upgrade to using Cpython. Let me know what you think!`
|
||||||
|
- Be concise
|
||||||
|
- Maybe break up into smaller commits or PRs if the title already appears too long?
|
||||||
|
|
||||||
|
**DO**
|
||||||
|
- Mention the nushell feature or command that is affected.
|
||||||
|
- ``Fix URL parsing in `http get` (issue #1234)``
|
||||||
|
- You can mention the issue number if other context is there.
|
||||||
|
- In general, mention all related issues in the description to crosslink (e.g. `Fixes #1234`, `Closes #6789`)
|
||||||
|
- For internal changes mention the area or symbols affected if it helps to clarify
|
||||||
|
- ``Factor out `quote_string()` from parser to reuse in `explore` ``
|
||||||
|
|
||||||
|
### Review process / Merge conflicts
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
> Keep in mind that the maintainers are volunteers that need to allocate their attention to several different areas and active PRs. We will try to get back to you as soon as possible.
|
||||||
|
|
||||||
|
You can help us to make the review process a smooth experience:
|
||||||
|
- Testing:
|
||||||
|
- We generally review in detail after all the tests pass. Let us know if there is a problem you want to discuss to fix a test failure or forces us to accept a breaking change.
|
||||||
|
- If you fix a bug, it is highly recommended that you add a test that reproduces the original issue/panic in a minimal form.
|
||||||
|
- In general, added tests help us to understand which assumptions go into a particular addition/change.
|
||||||
|
- Try to also test corner cases where those assumptions might break. This can be more valuable than simply adding many similar tests.
|
||||||
|
- Commit history inside a PR during code review:
|
||||||
|
- Good **atomic commits** can help follow larger changes, but we are not pedantic.
|
||||||
|
- We don't shame fixup commits while you try to figure out a problem. They can help others see what you tried and what didn't work. (see our [squash policy](#how-we-merge-prs))
|
||||||
|
- During active review constant **force pushing** just to amend changes can be confusing!
|
||||||
|
- GitHub's UI presents reviewers with less options to compare diffs
|
||||||
|
- fetched branches for experimentation become invalid!
|
||||||
|
- the notification a maintainer receives has a low signal-to-noise ratio
|
||||||
|
- Git pros *can* use their judgement to rebase/squash to clean up the history *if it aids the understanding* of a larger change during review
|
||||||
|
- Merge conflicts:
|
||||||
|
- In general you should take care of resolving merge conflicts.
|
||||||
|
- Use your judgement whether to `git merge main` or to `git rebase main`
|
||||||
|
- Choose what simplifies having confidence in the conflict resolution and the review. **Merge commits in your branch are OK** in the squash model.
|
||||||
|
- Feel free to notify your reviewers or affected PR authors if your change might cause larger conflicts with another change.
|
||||||
|
- During the rollup of multiple PRs, we may choose to resolve merge conflicts and CI failures ourselves. (Allow maintainers to push to your branch to enable us to do this quickly.)
|
||||||
|
246
Cargo.lock
generated
246
Cargo.lock
generated
@ -68,9 +68,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alphanumeric-sort"
|
name = "alphanumeric-sort"
|
||||||
version = "1.4.4"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77e9c9abb82613923ec78d7a461595d52491ba7240f3c64c0bbe0e6d98e0fce0"
|
checksum = "5e972aa01d34ded9a7fd0d838a1b6f73e70daf78ff77221c82bd411afa1f3fa9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android_system_properties"
|
name = "android_system_properties"
|
||||||
@ -1739,6 +1739,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hex"
|
name = "hex"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@ -1963,6 +1969,18 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.3.1",
|
||||||
|
"io-lifetimes",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.45.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_ci"
|
name = "is_ci"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
@ -2266,9 +2284,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru"
|
name = "lru"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17"
|
checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.13.2",
|
"hashbrown 0.13.2",
|
||||||
]
|
]
|
||||||
@ -2381,11 +2399,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miette"
|
name = "miette"
|
||||||
version = "5.5.0"
|
version = "5.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4afd9b301defa984bbdbe112b4763e093ed191750a0d914a78c1106b2d0fe703"
|
checksum = "07749fb52853e739208049fb513287c6f448de9103dfa78b05ae01f2fc5809bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"is-terminal",
|
||||||
"miette-derive",
|
"miette-derive",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
@ -2400,9 +2418,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miette-derive"
|
name = "miette-derive"
|
||||||
version = "5.5.0"
|
version = "5.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97c2401ab7ac5282ca5c8b518a87635b1a93762b0b90b9990c509888eeccba29"
|
checksum = "2a07ad93a80d1b92bb44cb42d7c49b49c9aab1778befefad49cceb5e4c5bf460"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2497,9 +2515,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mockito"
|
name = "mockito"
|
||||||
version = "0.32.5"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "406f43768da5a859ce19bb0978fd8dc2167a7d9a52f3935c6a187242e1a4ff9f"
|
checksum = "8c1eecc3baf782e3c8d6803cc8780268da1f32df6eb88c016c1d80b0df7944cf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert-json-diff",
|
"assert-json-diff",
|
||||||
"colored",
|
"colored",
|
||||||
@ -2649,7 +2667,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu"
|
name = "nu"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
"atty",
|
"atty",
|
||||||
@ -2704,7 +2722,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -2733,7 +2751,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-cmd-lang"
|
name = "nu-cmd-lang"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
"itertools",
|
"itertools",
|
||||||
@ -2746,11 +2764,12 @@ dependencies = [
|
|||||||
"nu-test-support",
|
"nu-test-support",
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"shadow-rs",
|
"shadow-rs",
|
||||||
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-color-config"
|
name = "nu-color-config"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
@ -2764,7 +2783,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-command"
|
name = "nu-command"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Inflector",
|
"Inflector",
|
||||||
"alphanumeric-sort",
|
"alphanumeric-sort",
|
||||||
@ -2797,6 +2816,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"lscolors",
|
"lscolors",
|
||||||
"md-5",
|
"md-5",
|
||||||
|
"miette",
|
||||||
"mime",
|
"mime",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
"mockito",
|
"mockito",
|
||||||
@ -2828,8 +2848,7 @@ dependencies = [
|
|||||||
"polars",
|
"polars",
|
||||||
"powierza-coefficient",
|
"powierza-coefficient",
|
||||||
"print-positions",
|
"print-positions",
|
||||||
"proptest",
|
"quick-xml 0.28.1",
|
||||||
"quick-xml 0.27.1",
|
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
"quickcheck_macros",
|
"quickcheck_macros",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
@ -2862,13 +2881,13 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
"wax",
|
"wax",
|
||||||
"which",
|
"which",
|
||||||
"windows",
|
"windows 0.46.0",
|
||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-engine"
|
name = "nu-engine"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"nu-glob",
|
"nu-glob",
|
||||||
@ -2881,7 +2900,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-explore"
|
name = "nu-explore"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi-str 0.7.2",
|
"ansi-str 0.7.2",
|
||||||
"crossterm 0.24.0",
|
"crossterm 0.24.0",
|
||||||
@ -2901,14 +2920,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-glob"
|
name = "nu-glob"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"doc-comment",
|
"doc-comment",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-json"
|
name = "nu-json"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@ -2917,7 +2936,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-parser"
|
name = "nu-parser"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytesize",
|
"bytesize",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -2934,7 +2953,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-path"
|
name = "nu-path"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs-next",
|
"dirs-next",
|
||||||
"omnipath",
|
"omnipath",
|
||||||
@ -2943,7 +2962,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-plugin"
|
name = "nu-plugin"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
@ -2956,7 +2975,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-pretty-hex"
|
name = "nu-pretty-hex"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heapless",
|
"heapless",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
@ -2965,7 +2984,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-protocol"
|
name = "nu-protocol"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byte-unit",
|
"byte-unit",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -2988,7 +3007,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-system"
|
name = "nu-system"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -3006,9 +3025,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-table"
|
name = "nu-table"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
|
||||||
"json_to_table",
|
"json_to_table",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"nu-color-config",
|
"nu-color-config",
|
||||||
@ -3021,7 +3039,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-term-grid"
|
name = "nu-term-grid"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
@ -3029,7 +3047,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-test-support"
|
name = "nu-test-support"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getset",
|
"getset",
|
||||||
"hamcrest2",
|
"hamcrest2",
|
||||||
@ -3037,14 +3055,13 @@ dependencies = [
|
|||||||
"nu-path",
|
"nu-path",
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"num-format",
|
"num-format",
|
||||||
"once_cell",
|
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"which",
|
"which",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-utils"
|
name = "nu-utils"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
"log",
|
"log",
|
||||||
@ -3066,7 +3083,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_example"
|
name = "nu_plugin_example"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-plugin",
|
"nu-plugin",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
@ -3074,7 +3091,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_formats"
|
name = "nu_plugin_formats"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"eml-parser",
|
"eml-parser",
|
||||||
"ical",
|
"ical",
|
||||||
@ -3087,7 +3104,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_gstat"
|
name = "nu_plugin_gstat"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"git2",
|
"git2",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
@ -3097,7 +3114,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_inc"
|
name = "nu_plugin_inc"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-plugin",
|
"nu-plugin",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
@ -3106,7 +3123,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_query"
|
name = "nu_plugin_query"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gjson",
|
"gjson",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
@ -3305,18 +3322,18 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "open"
|
name = "open"
|
||||||
version = "3.4.0"
|
version = "4.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21ecf2487e799604735754d2c5896106785987b441b5aee58f242e4d4963179a"
|
checksum = "bd61e3bf9d78956c72ee864bba52431f7f43994b21a17e9e72596a81bd61075b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pathdiff",
|
"pathdiff",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl"
|
name = "openssl"
|
||||||
version = "0.10.45"
|
version = "0.10.48"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1"
|
checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
@ -3355,9 +3372,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.80"
|
version = "0.9.83"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7"
|
checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cc",
|
"cc",
|
||||||
@ -4034,27 +4051,6 @@ dependencies = [
|
|||||||
"rustix",
|
"rustix",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proptest"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70"
|
|
||||||
dependencies = [
|
|
||||||
"bit-set",
|
|
||||||
"bitflags",
|
|
||||||
"byteorder",
|
|
||||||
"lazy_static",
|
|
||||||
"num-traits",
|
|
||||||
"quick-error 2.0.1",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"rand_chacha 0.3.1",
|
|
||||||
"rand_xorshift",
|
|
||||||
"regex-syntax",
|
|
||||||
"rusty-fork",
|
|
||||||
"tempfile",
|
|
||||||
"unarray",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pure-rust-locales"
|
name = "pure-rust-locales"
|
||||||
version = "0.5.6"
|
version = "0.5.6"
|
||||||
@ -4077,12 +4073,6 @@ version = "1.2.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quick-error"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.25.0"
|
version = "0.25.0"
|
||||||
@ -4095,9 +4085,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.27.1"
|
version = "0.28.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ffc053f057dd768a56f62cd7e434c42c831d296968997e9ac1f76ea7c2d14c41"
|
checksum = "e5c1a97b1bc42b1d550bfb48d4262153fe400a12bab1511821736f7eac76d7e2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -4224,15 +4214,6 @@ dependencies = [
|
|||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_xorshift"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.6.4",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
@ -4277,9 +4258,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reedline"
|
name = "reedline"
|
||||||
version = "0.17.0"
|
version = "0.18.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3afcd7fc553d129e9a03d17f15c4b4748baa589570f01a372caae2de81989b8"
|
checksum = "8df9be1b4ddfacccf35ea8b4a8d3f7ec9e494ea22969c4156d4b2a393c1b9cef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossterm 0.24.0",
|
"crossterm 0.24.0",
|
||||||
@ -4353,9 +4334,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rstest"
|
name = "rstest"
|
||||||
version = "0.16.0"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b07f2d176c472198ec1e6551dc7da28f1c089652f66a7b722676c2238ebc0edf"
|
checksum = "de1bb486a691878cd320c2f0d319ba91eeaa2e894066d8b5f8f117c000e9d962"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rstest_macros",
|
"rstest_macros",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
@ -4363,9 +4344,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rstest_macros"
|
name = "rstest_macros"
|
||||||
version = "0.16.0"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7229b505ae0706e64f37ffc54a9c163e11022a6636d58fe1f3f52018257ff9f7"
|
checksum = "290ca1a1c8ca7edb7c3283bd44dc35dd54fdec6253a3912e201ba1072018fca8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -4478,18 +4459,6 @@ version = "1.0.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rusty-fork"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
|
|
||||||
dependencies = [
|
|
||||||
"fnv",
|
|
||||||
"quick-error 1.2.3",
|
|
||||||
"tempfile",
|
|
||||||
"wait-timeout",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.13"
|
version = "1.0.13"
|
||||||
@ -4860,9 +4829,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlparser"
|
name = "sqlparser"
|
||||||
version = "0.30.0"
|
version = "0.32.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db67dc6ef36edb658196c3fef0464a80b53dbbc194a904e81f9bd4190f9ecc5b"
|
checksum = "0366f270dbabb5cc2e4c88427dc4c08bba144f81e32fbd459a013f26a4d16aa0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
@ -4957,30 +4926,30 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "supports-color"
|
name = "supports-color"
|
||||||
version = "1.3.1"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ba6faf2ca7ee42fdd458f4347ae0a9bd6bcc445ad7cb57ad82b383f18870d6f"
|
checksum = "4950e7174bffabe99455511c39707310e7e9b440364a2fcb1cc21521be57b354"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"is-terminal",
|
||||||
"is_ci",
|
"is_ci",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "supports-hyperlinks"
|
name = "supports-hyperlinks"
|
||||||
version = "1.2.0"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406"
|
checksum = "4b4806e0b03b9906e76b018a5d821ebf198c8e9dc0829ed3328eeeb5094aed60"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"is-terminal",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "supports-unicode"
|
name = "supports-unicode"
|
||||||
version = "1.0.2"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8b945e45b417b125a8ec51f1b7df2f8df7920367700d1f98aedd21e5735f8b2"
|
checksum = "4b6c2cb240ab5dd21ed4906895ee23fe5a48acdbd15a3ce388e7b62a9b66baf7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"is-terminal",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5000,7 +4969,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "36e39da5d30887b5690e29de4c5ebb8ddff64ebd9933f98a01daaa4fd11b36ea"
|
checksum = "36e39da5d30887b5690e29de4c5ebb8ddff64ebd9933f98a01daaa4fd11b36ea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"peresil",
|
"peresil",
|
||||||
"quick-error 1.2.3",
|
"quick-error",
|
||||||
"sxd-document",
|
"sxd-document",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5400,7 +5369,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
"url",
|
"url",
|
||||||
"windows",
|
"windows 0.44.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5464,12 +5433,6 @@ version = "2.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46b0c16eadfb312c7acd6970fc97d1f3152eb536714a2ff72ca09a92cae6fa67"
|
checksum = "46b0c16eadfb312c7acd6970fc97d1f3152eb536714a2ff72ca09a92cae6fa67"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unarray"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
@ -5874,6 +5837,15 @@ dependencies = [
|
|||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.42.0"
|
version = "0.42.0"
|
||||||
@ -5900,9 +5872,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.42.1"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
|
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc",
|
||||||
@ -5915,45 +5887,45 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.42.1"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.42.1"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.42.1"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.42.1"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.42.1"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.42.1"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.42.1"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
|
42
Cargo.toml
42
Cargo.toml
@ -10,7 +10,7 @@ license = "MIT"
|
|||||||
name = "nu"
|
name = "nu"
|
||||||
repository = "https://github.com/nushell/nushell"
|
repository = "https://github.com/nushell/nushell"
|
||||||
rust-version = "1.60"
|
rust-version = "1.60"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -45,25 +45,25 @@ chrono = { version = "0.4.23", features = ["serde"] }
|
|||||||
crossterm = "0.24.0"
|
crossterm = "0.24.0"
|
||||||
ctrlc = "3.2.1"
|
ctrlc = "3.2.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
|
miette = { version = "5.6.0", features = ["fancy-no-backtrace"] }
|
||||||
nu-cli = { path = "./crates/nu-cli", version = "0.77.0" }
|
nu-cli = { path = "./crates/nu-cli", version = "0.78.0" }
|
||||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.77.0" }
|
nu-color-config = { path = "./crates/nu-color-config", version = "0.78.0" }
|
||||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.77.0" }
|
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.78.0" }
|
||||||
nu-command = { path = "./crates/nu-command", version = "0.77.0" }
|
nu-command = { path = "./crates/nu-command", version = "0.78.0" }
|
||||||
nu-engine = { path = "./crates/nu-engine", version = "0.77.0" }
|
nu-engine = { path = "./crates/nu-engine", version = "0.78.0" }
|
||||||
nu-json = { path = "./crates/nu-json", version = "0.77.0" }
|
nu-json = { path = "./crates/nu-json", version = "0.78.0" }
|
||||||
nu-parser = { path = "./crates/nu-parser", version = "0.77.0" }
|
nu-parser = { path = "./crates/nu-parser", version = "0.78.0" }
|
||||||
nu-path = { path = "./crates/nu-path", version = "0.77.0" }
|
nu-path = { path = "./crates/nu-path", version = "0.78.0" }
|
||||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.77.0" }
|
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.78.0" }
|
||||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.77.0" }
|
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.78.0" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.77.0" }
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.78.0" }
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.77.0" }
|
nu-system = { path = "./crates/nu-system", version = "0.78.0" }
|
||||||
nu-table = { path = "./crates/nu-table", version = "0.77.0" }
|
nu-table = { path = "./crates/nu-table", version = "0.78.0" }
|
||||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.77.0" }
|
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.78.0" }
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.77.0" }
|
nu-utils = { path = "./crates/nu-utils", version = "0.78.0" }
|
||||||
|
|
||||||
nu-ansi-term = "0.47.0"
|
nu-ansi-term = "0.47.0"
|
||||||
reedline = { version = "0.17.0", features = ["bashisms", "sqlite"] }
|
reedline = { version = "0.18.0", features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
rayon = "1.7.0"
|
rayon = "1.7.0"
|
||||||
is_executable = "1.0.1"
|
is_executable = "1.0.1"
|
||||||
@ -72,7 +72,7 @@ time = "0.3.12"
|
|||||||
|
|
||||||
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
||||||
# Our dependencies don't use OpenSSL on Windows
|
# Our dependencies don't use OpenSSL on Windows
|
||||||
openssl = { version = "0.10.38", features = ["vendored"], optional = true }
|
openssl = { version = "0.10.48", features = ["vendored"], optional = true }
|
||||||
signal-hook = { version = "0.3.14", default-features = false }
|
signal-hook = { version = "0.3.14", default-features = false }
|
||||||
|
|
||||||
|
|
||||||
@ -89,14 +89,14 @@ nix = { version = "0.26", default-features = false, features = [
|
|||||||
atty = "0.2"
|
atty = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.77.0" }
|
nu-test-support = { path = "./crates/nu-test-support", version = "0.78.0" }
|
||||||
tempfile = "3.4.0"
|
tempfile = "3.4.0"
|
||||||
assert_cmd = "2.0.2"
|
assert_cmd = "2.0.2"
|
||||||
criterion = "0.4"
|
criterion = "0.4"
|
||||||
pretty_assertions = "1.0.0"
|
pretty_assertions = "1.0.0"
|
||||||
serial_test = "1.0.0"
|
serial_test = "1.0.0"
|
||||||
hamcrest2 = "0.3.0"
|
hamcrest2 = "0.3.0"
|
||||||
rstest = { version = "0.16.0", default-features = false }
|
rstest = { version = "0.17.0", default-features = false }
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 - 2022 The Nushell Project Developers
|
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
[](https://twitter.com/nu_shell)
|
[](https://twitter.com/nu_shell)
|
||||||
[](https://github.com/nushell/nushell/graphs/commit-activity)
|
[](https://github.com/nushell/nushell/graphs/commit-activity)
|
||||||
[](https://github.com/nushell/nushell/graphs/contributors)
|
[](https://github.com/nushell/nushell/graphs/contributors)
|
||||||
[](https://codecov.io/gh/nushell/nushell)
|
<!-- [](https://codecov.io/gh/nushell/nushell) -->
|
||||||
|
|
||||||
A new type of shell.
|
A new type of shell.
|
||||||
|
|
||||||
@ -175,7 +175,8 @@ These binaries interact with nu via a simple JSON-RPC protocol where the command
|
|||||||
If the plugin is a filter, data streams to it one element at a time, and it can stream data back in return via stdin/stdout.
|
If the plugin is a filter, data streams to it one element at a time, and it can stream data back in return via stdin/stdout.
|
||||||
If the plugin is a sink, it is given the full vector of final data and is given free reign over stdin/stdout to use as it pleases.
|
If the plugin is a sink, it is given the full vector of final data and is given free reign over stdin/stdout to use as it pleases.
|
||||||
|
|
||||||
The [awesome-nu repo](https://github.com/nushell/awesome-nu#plugins) lists a variety of nu-plugins.
|
The [awesome-nu repo](https://github.com/nushell/awesome-nu#plugins) lists a variety of nu-plugins while the [showcase repo](https://github.com/nushell/showcase) *shows* off informative blog posts that have been written about Nushell along with videos that highlight technical
|
||||||
|
topics that have been presented.
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
||||||
|
@ -20,5 +20,6 @@ for plugin in $plugins {
|
|||||||
'----------------------------'
|
'----------------------------'
|
||||||
cd $'crates/($plugin)'
|
cd $'crates/($plugin)'
|
||||||
cargo build
|
cargo build
|
||||||
|
cd ../../
|
||||||
ignore
|
ignore
|
||||||
}
|
}
|
||||||
|
@ -5,26 +5,26 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.77.0" }
|
nu-test-support = { path = "../nu-test-support", version = "0.78.0" }
|
||||||
nu-command = { path = "../nu-command", version = "0.77.0" }
|
rstest = { version = "0.17.0", default-features = false }
|
||||||
rstest = { version = "0.16.0", default-features = false }
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.77.0" }
|
nu-command = { path = "../nu-command", version = "0.78.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.77.0" }
|
nu-engine = { path = "../nu-engine", version = "0.78.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.77.0" }
|
nu-path = { path = "../nu-path", version = "0.78.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.77.0" }
|
nu-parser = { path = "../nu-parser", version = "0.78.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.77.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.78.0" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.77.0" }
|
nu-utils = { path = "../nu-utils", version = "0.78.0" }
|
||||||
|
nu-color-config = { path = "../nu-color-config", version = "0.78.0" }
|
||||||
|
|
||||||
nu-ansi-term = "0.47.0"
|
nu-ansi-term = "0.47.0"
|
||||||
reedline = { version = "0.17.0", features = ["bashisms", "sqlite"] }
|
reedline = { version = "0.18.0", features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
atty = "0.2.14"
|
atty = "0.2.14"
|
||||||
chrono = { default-features = false, features = ["std"], version = "0.4.23" }
|
chrono = { default-features = false, features = ["std"], version = "0.4.23" }
|
||||||
@ -34,7 +34,7 @@ fuzzy-matcher = "0.3.7"
|
|||||||
is_executable = "1.0.1"
|
is_executable = "1.0.1"
|
||||||
once_cell = "1.17.0"
|
once_cell = "1.17.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
|
miette = { version = "5.6.0", features = ["fancy-no-backtrace"] }
|
||||||
percent-encoding = "2"
|
percent-encoding = "2"
|
||||||
sysinfo = "0.28.2"
|
sysinfo = "0.28.2"
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.31"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 - 2022 The Nushell Project Developers
|
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::util::report_error;
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use miette::Result;
|
use miette::Result;
|
||||||
|
use nu_command::util::report_error;
|
||||||
use nu_engine::{convert_env_values, eval_block};
|
use nu_engine::{convert_env_values, eval_block};
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::engine::Stack;
|
use nu_protocol::engine::Stack;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::util::{eval_source, report_error};
|
use crate::util::eval_source;
|
||||||
|
use nu_command::util::report_error;
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_parser::ParseError;
|
use nu_parser::ParseError;
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::util::{eval_source, report_error};
|
use crate::util::eval_source;
|
||||||
use log::info;
|
use log::info;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
|
use nu_command::util::report_error;
|
||||||
use nu_engine::{convert_env_values, current_dir};
|
use nu_engine::{convert_env_values, current_dir};
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
|
@ -18,13 +18,13 @@ pub use completions::{FileCompletion, NuCompleter};
|
|||||||
pub use config_files::eval_config_contents;
|
pub use config_files::eval_config_contents;
|
||||||
pub use eval_file::evaluate_file;
|
pub use eval_file::evaluate_file;
|
||||||
pub use menus::{DescriptionMenu, NuHelpCompleter};
|
pub use menus::{DescriptionMenu, NuHelpCompleter};
|
||||||
|
pub use nu_command::util::{get_init_cwd, report_error, report_error_new};
|
||||||
pub use nu_highlight::NuHighlight;
|
pub use nu_highlight::NuHighlight;
|
||||||
pub use print::Print;
|
pub use print::Print;
|
||||||
pub use prompt::NushellPrompt;
|
pub use prompt::NushellPrompt;
|
||||||
pub use repl::evaluate_repl;
|
pub use repl::evaluate_repl;
|
||||||
pub use repl::{eval_env_change_hook, eval_hook};
|
|
||||||
pub use syntax_highlight::NuHighlighter;
|
pub use syntax_highlight::NuHighlighter;
|
||||||
pub use util::{eval_source, gather_parent_env_vars, get_init_cwd, report_error, report_error_new};
|
pub use util::{eval_source, gather_parent_env_vars};
|
||||||
pub use validation::NuValidator;
|
pub use validation::NuValidator;
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::util::report_error;
|
|
||||||
use crate::NushellPrompt;
|
use crate::NushellPrompt;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
use nu_command::util::report_error;
|
||||||
use nu_engine::eval_subexpression;
|
use nu_engine::eval_subexpression;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
|
@ -626,9 +626,12 @@ fn add_parsed_keybinding(
|
|||||||
"shift" => KeyModifiers::SHIFT,
|
"shift" => KeyModifiers::SHIFT,
|
||||||
"alt" => KeyModifiers::ALT,
|
"alt" => KeyModifiers::ALT,
|
||||||
"none" => KeyModifiers::NONE,
|
"none" => KeyModifiers::NONE,
|
||||||
"control | shift" => KeyModifiers::CONTROL | KeyModifiers::SHIFT,
|
"shift_alt" | "alt_shift" => KeyModifiers::SHIFT | KeyModifiers::ALT,
|
||||||
"control | alt" => KeyModifiers::CONTROL | KeyModifiers::ALT,
|
"control_shift" | "shift_control" => KeyModifiers::CONTROL | KeyModifiers::SHIFT,
|
||||||
"control | alt | shift" => KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SHIFT,
|
"control_alt" | "alt_control" => KeyModifiers::CONTROL | KeyModifiers::ALT,
|
||||||
|
"control_alt_shift" | "control_shift_alt" => {
|
||||||
|
KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SHIFT
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
"CONTROL, SHIFT, ALT or NONE".to_string(),
|
"CONTROL, SHIFT, ALT or NONE".to_string(),
|
||||||
|
@ -2,21 +2,21 @@ use crate::{
|
|||||||
completions::NuCompleter,
|
completions::NuCompleter,
|
||||||
prompt_update,
|
prompt_update,
|
||||||
reedline_config::{add_menus, create_keybindings, KeybindingsMode},
|
reedline_config::{add_menus, create_keybindings, KeybindingsMode},
|
||||||
util::{eval_source, get_guaranteed_cwd, report_error, report_error_new},
|
util::eval_source,
|
||||||
NuHighlighter, NuValidator, NushellPrompt,
|
NuHighlighter, NuValidator, NushellPrompt,
|
||||||
};
|
};
|
||||||
use crossterm::cursor::CursorShape;
|
use crossterm::cursor::CursorShape;
|
||||||
use log::{trace, warn};
|
use log::{trace, warn};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use nu_color_config::StyleComputer;
|
use nu_color_config::StyleComputer;
|
||||||
use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return};
|
use nu_command::hook::eval_hook;
|
||||||
|
use nu_command::util::{get_guaranteed_cwd, report_error, report_error_new};
|
||||||
|
use nu_engine::{convert_env_values, eval_block};
|
||||||
use nu_parser::{lex, parse, trim_quotes_str};
|
use nu_parser::{lex, parse, trim_quotes_str};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::PathMember,
|
|
||||||
config::NuCursorShape,
|
config::NuCursorShape,
|
||||||
engine::{EngineState, ReplOperation, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
format_duration, BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span,
|
format_duration, HistoryFileFormat, PipelineData, ShellError, Span, Spanned, Value,
|
||||||
Spanned, Type, Value, VarId,
|
|
||||||
};
|
};
|
||||||
use nu_utils::utils::perf;
|
use nu_utils::utils::perf;
|
||||||
use reedline::{CursorConfig, DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi};
|
use reedline::{CursorConfig, DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi};
|
||||||
@ -44,6 +44,7 @@ pub fn evaluate_repl(
|
|||||||
prerun_command: Option<Spanned<String>>,
|
prerun_command: Option<Spanned<String>>,
|
||||||
entire_start_time: Instant,
|
entire_start_time: Instant,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
use nu_command::hook;
|
||||||
use reedline::{FileBackedHistory, Reedline, Signal};
|
use reedline::{FileBackedHistory, Reedline, Signal};
|
||||||
let use_color = engine_state.get_config().use_ansi_coloring;
|
let use_color = engine_state.get_config().use_ansi_coloring;
|
||||||
|
|
||||||
@ -400,7 +401,7 @@ pub fn evaluate_repl(
|
|||||||
// fire the "env_change" hook
|
// fire the "env_change" hook
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
if let Err(error) =
|
if let Err(error) =
|
||||||
eval_env_change_hook(config.hooks.env_change.clone(), engine_state, stack)
|
hook::eval_env_change_hook(config.hooks.env_change.clone(), engine_state, stack)
|
||||||
{
|
{
|
||||||
report_error_new(engine_state, &error)
|
report_error_new(engine_state, &error)
|
||||||
}
|
}
|
||||||
@ -459,20 +460,29 @@ pub fn evaluate_repl(
|
|||||||
.into_diagnostic()?; // todo: don't stop repl if error here?
|
.into_diagnostic()?; // todo: don't stop repl if error here?
|
||||||
}
|
}
|
||||||
|
|
||||||
engine_state
|
// Right before we start running the code the user gave us, fire the `pre_execution`
|
||||||
.repl_buffer_state
|
// hook
|
||||||
.lock()
|
|
||||||
.expect("repl buffer state mutex")
|
|
||||||
.replace(line_editor.current_buffer_contents().to_string());
|
|
||||||
|
|
||||||
// Right before we start running the code the user gave us,
|
|
||||||
// fire the "pre_execution" hook
|
|
||||||
if let Some(hook) = config.hooks.pre_execution.clone() {
|
if let Some(hook) = config.hooks.pre_execution.clone() {
|
||||||
|
// Set the REPL buffer to the current command for the "pre_execution" hook
|
||||||
|
let mut repl_buffer = engine_state
|
||||||
|
.repl_buffer_state
|
||||||
|
.lock()
|
||||||
|
.expect("repl buffer state mutex");
|
||||||
|
*repl_buffer = s.to_string();
|
||||||
|
drop(repl_buffer);
|
||||||
|
|
||||||
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
|
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
|
||||||
report_error_new(engine_state, &err);
|
report_error_new(engine_state, &err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut repl_buffer = engine_state
|
||||||
|
.repl_buffer_state
|
||||||
|
.lock()
|
||||||
|
.expect("repl buffer state mutex");
|
||||||
|
*repl_buffer = line_editor.current_buffer_contents().to_string();
|
||||||
|
drop(repl_buffer);
|
||||||
|
|
||||||
if shell_integration {
|
if shell_integration {
|
||||||
run_ansi_sequence(PRE_EXECUTE_MARKER)?;
|
run_ansi_sequence(PRE_EXECUTE_MARKER)?;
|
||||||
}
|
}
|
||||||
@ -628,23 +638,23 @@ pub fn evaluate_repl(
|
|||||||
run_ansi_sequence(RESET_APPLICATION_MODE)?;
|
run_ansi_sequence(RESET_APPLICATION_MODE)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ops = engine_state
|
let mut repl_buffer = engine_state
|
||||||
.repl_operation_queue
|
.repl_buffer_state
|
||||||
.lock()
|
.lock()
|
||||||
.expect("repl op queue mutex");
|
.expect("repl buffer state mutex");
|
||||||
while let Some(op) = ops.pop_front() {
|
let mut repl_cursor_pos = engine_state
|
||||||
match op {
|
.repl_cursor_pos
|
||||||
ReplOperation::Append(s) => line_editor.run_edit_commands(&[
|
.lock()
|
||||||
EditCommand::MoveToEnd,
|
.expect("repl cursor pos mutex");
|
||||||
EditCommand::InsertString(s),
|
line_editor.run_edit_commands(&[
|
||||||
]),
|
EditCommand::Clear,
|
||||||
ReplOperation::Insert(s) => {
|
EditCommand::InsertString(repl_buffer.to_string()),
|
||||||
line_editor.run_edit_commands(&[EditCommand::InsertString(s)])
|
EditCommand::MoveToPosition(*repl_cursor_pos),
|
||||||
}
|
]);
|
||||||
ReplOperation::Replace(s) => line_editor
|
*repl_buffer = "".to_string();
|
||||||
.run_edit_commands(&[EditCommand::Clear, EditCommand::InsertString(s)]),
|
drop(repl_buffer);
|
||||||
}
|
*repl_cursor_pos = 0;
|
||||||
}
|
drop(repl_cursor_pos);
|
||||||
}
|
}
|
||||||
Ok(Signal::CtrlC) => {
|
Ok(Signal::CtrlC) => {
|
||||||
// `Reedline` clears the line content. New prompt is shown
|
// `Reedline` clears the line content. New prompt is shown
|
||||||
@ -810,341 +820,6 @@ pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) ->
|
|||||||
format!("\x1b]133;D;{}\x1b\\", exit_code.unwrap_or(0))
|
format!("\x1b]133;D;{}\x1b\\", exit_code.unwrap_or(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_env_change_hook(
|
|
||||||
env_change_hook: Option<Value>,
|
|
||||||
engine_state: &mut EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
) -> Result<(), ShellError> {
|
|
||||||
if let Some(hook) = env_change_hook {
|
|
||||||
match hook {
|
|
||||||
Value::Record {
|
|
||||||
cols: env_names,
|
|
||||||
vals: hook_values,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
for (env_name, hook_value) in env_names.iter().zip(hook_values.iter()) {
|
|
||||||
let before = engine_state
|
|
||||||
.previous_env_vars
|
|
||||||
.get(env_name)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let after = stack
|
|
||||||
.get_env_var(engine_state, env_name)
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
if before != after {
|
|
||||||
eval_hook(
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
None,
|
|
||||||
vec![("$before".into(), before), ("$after".into(), after.clone())],
|
|
||||||
hook_value,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
engine_state
|
|
||||||
.previous_env_vars
|
|
||||||
.insert(env_name.to_string(), after);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x => {
|
|
||||||
return Err(ShellError::TypeMismatch {
|
|
||||||
err_message: "record for the 'env_change' hook".to_string(),
|
|
||||||
span: x.span()?,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn eval_hook(
|
|
||||||
engine_state: &mut EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
input: Option<PipelineData>,
|
|
||||||
arguments: Vec<(String, Value)>,
|
|
||||||
value: &Value,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let value_span = value.span()?;
|
|
||||||
|
|
||||||
// Hooks can optionally be a record in this form:
|
|
||||||
// {
|
|
||||||
// condition: {|before, after| ... } # block that evaluates to true/false
|
|
||||||
// code: # block or a string
|
|
||||||
// }
|
|
||||||
// The condition block will be run to check whether the main hook (in `code`) should be run.
|
|
||||||
// If it returns true (the default if a condition block is not specified), the hook should be run.
|
|
||||||
let condition_path = PathMember::String {
|
|
||||||
val: "condition".to_string(),
|
|
||||||
span: value_span,
|
|
||||||
};
|
|
||||||
let mut output = PipelineData::empty();
|
|
||||||
|
|
||||||
let code_path = PathMember::String {
|
|
||||||
val: "code".to_string(),
|
|
||||||
span: value_span,
|
|
||||||
};
|
|
||||||
|
|
||||||
match value {
|
|
||||||
Value::List { vals, .. } => {
|
|
||||||
for val in vals {
|
|
||||||
eval_hook(engine_state, stack, None, arguments.clone(), val)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Record { .. } => {
|
|
||||||
let do_run_hook = if let Ok(condition) =
|
|
||||||
value
|
|
||||||
.clone()
|
|
||||||
.follow_cell_path(&[condition_path], false, false)
|
|
||||||
{
|
|
||||||
match condition {
|
|
||||||
Value::Block {
|
|
||||||
val: block_id,
|
|
||||||
span: block_span,
|
|
||||||
..
|
|
||||||
}
|
|
||||||
| Value::Closure {
|
|
||||||
val: block_id,
|
|
||||||
span: block_span,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
match run_hook_block(
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
block_id,
|
|
||||||
None,
|
|
||||||
arguments.clone(),
|
|
||||||
block_span,
|
|
||||||
) {
|
|
||||||
Ok(pipeline_data) => {
|
|
||||||
if let PipelineData::Value(Value::Bool { val, .. }, ..) =
|
|
||||||
pipeline_data
|
|
||||||
{
|
|
||||||
val
|
|
||||||
} else {
|
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
|
||||||
"boolean output".to_string(),
|
|
||||||
"other PipelineData variant".to_string(),
|
|
||||||
block_span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
other => {
|
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
|
||||||
"block".to_string(),
|
|
||||||
format!("{}", other.get_type()),
|
|
||||||
other.span()?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// always run the hook
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
if do_run_hook {
|
|
||||||
match value.clone().follow_cell_path(&[code_path], false, false)? {
|
|
||||||
Value::String {
|
|
||||||
val,
|
|
||||||
span: source_span,
|
|
||||||
} => {
|
|
||||||
let (block, delta, vars) = {
|
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
|
||||||
|
|
||||||
let mut vars: Vec<(VarId, Value)> = vec![];
|
|
||||||
|
|
||||||
for (name, val) in arguments {
|
|
||||||
let var_id = working_set.add_variable(
|
|
||||||
name.as_bytes().to_vec(),
|
|
||||||
val.span()?,
|
|
||||||
Type::Any,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
vars.push((var_id, val));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (output, err) =
|
|
||||||
parse(&mut working_set, Some("hook"), val.as_bytes(), false, &[]);
|
|
||||||
if let Some(err) = err {
|
|
||||||
report_error(&working_set, &err);
|
|
||||||
|
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
|
||||||
"valid source code".into(),
|
|
||||||
"source code with syntax errors".into(),
|
|
||||||
source_span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
(output, working_set.render(), vars)
|
|
||||||
};
|
|
||||||
|
|
||||||
engine_state.merge_delta(delta)?;
|
|
||||||
let input = PipelineData::empty();
|
|
||||||
|
|
||||||
let var_ids: Vec<VarId> = vars
|
|
||||||
.into_iter()
|
|
||||||
.map(|(var_id, val)| {
|
|
||||||
stack.add_var(var_id, val);
|
|
||||||
var_id
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
match eval_block(engine_state, stack, &block, input, false, false) {
|
|
||||||
Ok(pipeline_data) => {
|
|
||||||
output = pipeline_data;
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
report_error_new(engine_state, &err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for var_id in var_ids.iter() {
|
|
||||||
stack.vars.remove(var_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Block {
|
|
||||||
val: block_id,
|
|
||||||
span: block_span,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
run_hook_block(
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
block_id,
|
|
||||||
input,
|
|
||||||
arguments,
|
|
||||||
block_span,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
Value::Closure {
|
|
||||||
val: block_id,
|
|
||||||
span: block_span,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
run_hook_block(
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
block_id,
|
|
||||||
input,
|
|
||||||
arguments,
|
|
||||||
block_span,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
other => {
|
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
|
||||||
"block or string".to_string(),
|
|
||||||
format!("{}", other.get_type()),
|
|
||||||
other.span()?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Block {
|
|
||||||
val: block_id,
|
|
||||||
span: block_span,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
output = run_hook_block(
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
*block_id,
|
|
||||||
input,
|
|
||||||
arguments,
|
|
||||||
*block_span,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
Value::Closure {
|
|
||||||
val: block_id,
|
|
||||||
span: block_span,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
output = run_hook_block(
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
*block_id,
|
|
||||||
input,
|
|
||||||
arguments,
|
|
||||||
*block_span,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
other => {
|
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
|
||||||
"block, record, or list of records".into(),
|
|
||||||
format!("{}", other.get_type()),
|
|
||||||
other.span()?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let cwd = get_guaranteed_cwd(engine_state, stack);
|
|
||||||
engine_state.merge_env(stack, cwd)?;
|
|
||||||
|
|
||||||
Ok(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_hook_block(
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
block_id: BlockId,
|
|
||||||
optional_input: Option<PipelineData>,
|
|
||||||
arguments: Vec<(String, Value)>,
|
|
||||||
span: Span,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
|
|
||||||
let input = optional_input.unwrap_or_else(PipelineData::empty);
|
|
||||||
|
|
||||||
let mut callee_stack = stack.gather_captures(&block.captures);
|
|
||||||
|
|
||||||
for (idx, PositionalArg { var_id, .. }) in
|
|
||||||
block.signature.required_positional.iter().enumerate()
|
|
||||||
{
|
|
||||||
if let Some(var_id) = var_id {
|
|
||||||
if let Some(arg) = arguments.get(idx) {
|
|
||||||
callee_stack.add_var(*var_id, arg.1.clone())
|
|
||||||
} else {
|
|
||||||
return Err(ShellError::IncompatibleParametersSingle {
|
|
||||||
msg: "This hook block has too many parameters".into(),
|
|
||||||
span,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pipeline_data =
|
|
||||||
eval_block_with_early_return(engine_state, &mut callee_stack, block, input, false, false)?;
|
|
||||||
|
|
||||||
if let PipelineData::Value(Value::Error { error }, _) = pipeline_data {
|
|
||||||
return Err(*error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If all went fine, preserve the environment of the called block
|
|
||||||
let caller_env_vars = stack.get_env_var_names(engine_state);
|
|
||||||
|
|
||||||
// remove env vars that are present in the caller but not in the callee
|
|
||||||
// (the callee hid them)
|
|
||||||
for var in caller_env_vars.iter() {
|
|
||||||
if !callee_stack.has_env_var(engine_state, var) {
|
|
||||||
stack.remove_env_var(engine_state, var);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add new env vars from callee to caller
|
|
||||||
for (var, value) in callee_stack.get_stack_env_vars() {
|
|
||||||
stack.add_env_var(var, value);
|
|
||||||
}
|
|
||||||
Ok(pipeline_data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
|
fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
|
||||||
io::stdout().write_all(seq.as_bytes()).map_err(|e| {
|
io::stdout().write_all(seq.as_bytes()).map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
|
@ -79,29 +79,27 @@ impl Highlighter for NuHighlighter {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! add_colored_token {
|
let mut add_colored_token = |shape: &FlatShape, text: String| {
|
||||||
($shape:expr, $text:expr) => {
|
output.push((get_shape_color(shape.to_string(), &self.config), text));
|
||||||
output.push((get_shape_color($shape.to_string(), &self.config), $text))
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
match shape.1 {
|
match shape.1 {
|
||||||
FlatShape::Garbage => add_colored_token!(shape.1, next_token),
|
FlatShape::Garbage => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Nothing => add_colored_token!(shape.1, next_token),
|
FlatShape::Nothing => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Binary => add_colored_token!(shape.1, next_token),
|
FlatShape::Binary => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Bool => add_colored_token!(shape.1, next_token),
|
FlatShape::Bool => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Int => add_colored_token!(shape.1, next_token),
|
FlatShape::Int => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Float => add_colored_token!(shape.1, next_token),
|
FlatShape::Float => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Range => add_colored_token!(shape.1, next_token),
|
FlatShape::Range => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::InternalCall => add_colored_token!(shape.1, next_token),
|
FlatShape::InternalCall => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::External => add_colored_token!(shape.1, next_token),
|
FlatShape::External => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::ExternalArg => add_colored_token!(shape.1, next_token),
|
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Literal => add_colored_token!(shape.1, next_token),
|
FlatShape::Literal => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Operator => add_colored_token!(shape.1, next_token),
|
FlatShape::Operator => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Signature => add_colored_token!(shape.1, next_token),
|
FlatShape::Signature => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::String => add_colored_token!(shape.1, next_token),
|
FlatShape::String => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::StringInterpolation => add_colored_token!(shape.1, next_token),
|
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::DateTime => add_colored_token!(shape.1, next_token),
|
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::List => {
|
FlatShape::List => {
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
}
|
}
|
||||||
@ -116,16 +114,17 @@ impl Highlighter for NuHighlighter {
|
|||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatShape::Filepath => add_colored_token!(shape.1, next_token),
|
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Directory => add_colored_token!(shape.1, next_token),
|
FlatShape::Directory => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::GlobPattern => add_colored_token!(shape.1, next_token),
|
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Variable => add_colored_token!(shape.1, next_token),
|
FlatShape::Variable => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Flag => add_colored_token!(shape.1, next_token),
|
FlatShape::Flag => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Pipe => add_colored_token!(shape.1, next_token),
|
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::And => add_colored_token!(shape.1, next_token),
|
FlatShape::And => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Or => add_colored_token!(shape.1, next_token),
|
FlatShape::Or => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Redirection => add_colored_token!(shape.1, next_token),
|
FlatShape::Redirection => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Custom(..) => add_colored_token!(shape.1, next_token),
|
FlatShape::Custom(..) => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::MatchPattern => add_colored_token(&shape.1, next_token),
|
||||||
}
|
}
|
||||||
last_seen_span = shape.0.end;
|
last_seen_span = shape.0.end;
|
||||||
}
|
}
|
||||||
@ -308,6 +307,8 @@ fn find_matching_block_end_in_expr(
|
|||||||
Expr::ImportPattern(_) => None,
|
Expr::ImportPattern(_) => None,
|
||||||
Expr::Overlay(_) => None,
|
Expr::Overlay(_) => None,
|
||||||
Expr::Signature(_) => None,
|
Expr::Signature(_) => None,
|
||||||
|
Expr::MatchPattern(_) => None,
|
||||||
|
Expr::MatchBlock(_) => None,
|
||||||
Expr::Nothing => None,
|
Expr::Nothing => None,
|
||||||
Expr::Garbage => None,
|
Expr::Garbage => None,
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::repl::eval_hook;
|
use nu_command::hook::eval_hook;
|
||||||
|
use nu_command::util::{report_error, report_error_new};
|
||||||
use nu_engine::{eval_block, eval_block_with_early_return};
|
use nu_engine::{eval_block, eval_block_with_early_return};
|
||||||
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
|
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
use nu_protocol::CliError;
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
print_if_stream, PipelineData, ShellError, Span, Value,
|
print_if_stream, PipelineData, ShellError, Span, Value,
|
||||||
@ -10,7 +10,7 @@ use nu_protocol::{
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use nu_utils::enable_vt_processing;
|
use nu_utils::enable_vt_processing;
|
||||||
use nu_utils::utils::perf;
|
use nu_utils::utils::perf;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::Path;
|
||||||
|
|
||||||
// This will collect environment variables from std::env and adds them to a stack.
|
// This will collect environment variables from std::env and adds them to a stack.
|
||||||
//
|
//
|
||||||
@ -310,43 +310,6 @@ fn set_last_exit_code(stack: &mut Stack, exit_code: i64) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_error(
|
|
||||||
working_set: &StateWorkingSet,
|
|
||||||
error: &(dyn miette::Diagnostic + Send + Sync + 'static),
|
|
||||||
) {
|
|
||||||
eprintln!("Error: {:?}", CliError(error, working_set));
|
|
||||||
// reset vt processing, aka ansi because illbehaved externals can break it
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
let _ = nu_utils::enable_vt_processing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn report_error_new(
|
|
||||||
engine_state: &EngineState,
|
|
||||||
error: &(dyn miette::Diagnostic + Send + Sync + 'static),
|
|
||||||
) {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
|
|
||||||
report_error(&working_set, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_init_cwd() -> PathBuf {
|
|
||||||
std::env::current_dir().unwrap_or_else(|_| {
|
|
||||||
std::env::var("PWD")
|
|
||||||
.map(Into::into)
|
|
||||||
.unwrap_or_else(|_| nu_path::home_dir().unwrap_or_default())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
|
|
||||||
nu_engine::env::current_dir(engine_state, stack).unwrap_or_else(|e| {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(&working_set, &e);
|
|
||||||
get_init_cwd()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -6,17 +6,17 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-lang"
|
name = "nu-cmd-lang"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.77.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.78.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.77.0" }
|
nu-engine = { path = "../nu-engine", version = "0.78.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.77.0" }
|
nu-parser = { path = "../nu-parser", version = "0.78.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.77.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.78.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.77.0" }
|
nu-utils = { path = "../nu-utils", version = "0.78.0" }
|
||||||
|
|
||||||
nu-ansi-term = "0.47.0"
|
nu-ansi-term = "0.47.0"
|
||||||
|
|
||||||
@ -24,9 +24,10 @@ fancy-regex = "0.11.0"
|
|||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
shadow-rs = { version = "0.21.0", default-features = false }
|
shadow-rs = { version = "0.21.0", default-features = false }
|
||||||
|
unicode-segmentation = "1.10.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "0.21.0", default-features = false }
|
shadow-rs = { version = "0.21.0", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path="../nu-test-support", version = "0.77.0" }
|
nu-test-support = { path="../nu-test-support", version = "0.78.0" }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 - 2022 The Nushell Project Developers
|
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -72,10 +72,10 @@ impl Command for Collect {
|
|||||||
// for when we support `data | let x = $in;`
|
// for when we support `data | let x = $in;`
|
||||||
// remove the variables added earlier
|
// remove the variables added earlier
|
||||||
for var_id in capture_block.captures.keys() {
|
for var_id in capture_block.captures.keys() {
|
||||||
stack_captures.vars.remove(var_id);
|
stack_captures.remove_var(*var_id);
|
||||||
}
|
}
|
||||||
if let Some(u) = saved_positional {
|
if let Some(u) = saved_positional {
|
||||||
stack_captures.vars.remove(&u);
|
stack_captures.remove_var(u);
|
||||||
}
|
}
|
||||||
// add any new variables to the stack
|
// add any new variables to the stack
|
||||||
stack.vars.extend(stack_captures.vars);
|
stack.vars.extend(stack_captures.vars);
|
@ -1,10 +1,10 @@
|
|||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::ReplOperation;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::Category;
|
use nu_protocol::Category;
|
||||||
use nu_protocol::IntoPipelineData;
|
use nu_protocol::IntoPipelineData;
|
||||||
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Type, Value};
|
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Type, Value};
|
||||||
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Commandline;
|
pub struct Commandline;
|
||||||
@ -17,6 +17,11 @@ impl Command for Commandline {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("commandline")
|
Signature::build("commandline")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
|
.switch(
|
||||||
|
"cursor",
|
||||||
|
"Set or get the current cursor position",
|
||||||
|
Some('c'),
|
||||||
|
)
|
||||||
.switch(
|
.switch(
|
||||||
"append",
|
"append",
|
||||||
"appends the string to the end of the buffer",
|
"appends the string to the end of the buffer",
|
||||||
@ -56,30 +61,77 @@ impl Command for Commandline {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
if let Some(cmd) = call.opt::<Value>(engine_state, stack, 0)? {
|
if let Some(cmd) = call.opt::<Value>(engine_state, stack, 0)? {
|
||||||
let mut ops = engine_state
|
let mut buffer = engine_state
|
||||||
.repl_operation_queue
|
.repl_buffer_state
|
||||||
.lock()
|
.lock()
|
||||||
.expect("repl op queue mutex");
|
.expect("repl buffer state mutex");
|
||||||
ops.push_back(if call.has_flag("append") {
|
let mut cursor_pos = engine_state
|
||||||
ReplOperation::Append(cmd.as_string()?)
|
.repl_cursor_pos
|
||||||
|
.lock()
|
||||||
|
.expect("repl cursor pos mutex");
|
||||||
|
|
||||||
|
if call.has_flag("cursor") {
|
||||||
|
let cmd_str = cmd.as_string()?;
|
||||||
|
match cmd_str.parse::<i64>() {
|
||||||
|
Ok(n) => {
|
||||||
|
*cursor_pos = if n <= 0 {
|
||||||
|
0usize
|
||||||
|
} else {
|
||||||
|
buffer
|
||||||
|
.grapheme_indices(true)
|
||||||
|
.map(|(i, _c)| i)
|
||||||
|
.nth(n as usize)
|
||||||
|
.unwrap_or(buffer.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
return Err(ShellError::CantConvert {
|
||||||
|
to_type: "int".to_string(),
|
||||||
|
from_type: "string".to_string(),
|
||||||
|
span: cmd.span()?,
|
||||||
|
help: Some(format!(
|
||||||
|
r#"string "{cmd_str}" does not represent a valid integer"#
|
||||||
|
)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if call.has_flag("append") {
|
||||||
|
buffer.push_str(&cmd.as_string()?);
|
||||||
} else if call.has_flag("insert") {
|
} else if call.has_flag("insert") {
|
||||||
ReplOperation::Insert(cmd.as_string()?)
|
let cmd_str = cmd.as_string()?;
|
||||||
|
buffer.insert_str(*cursor_pos, &cmd_str);
|
||||||
|
*cursor_pos += cmd_str.len();
|
||||||
} else {
|
} else {
|
||||||
ReplOperation::Replace(cmd.as_string()?)
|
*buffer = cmd.as_string()?;
|
||||||
});
|
*cursor_pos = buffer.len();
|
||||||
Ok(Value::Nothing { span: call.head }.into_pipeline_data())
|
|
||||||
} else if let Some(ref cmd) = *engine_state
|
|
||||||
.repl_buffer_state
|
|
||||||
.lock()
|
|
||||||
.expect("repl buffer state mutex")
|
|
||||||
{
|
|
||||||
Ok(Value::String {
|
|
||||||
val: cmd.clone(),
|
|
||||||
span: call.head,
|
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
|
||||||
} else {
|
|
||||||
Ok(Value::Nothing { span: call.head }.into_pipeline_data())
|
Ok(Value::Nothing { span: call.head }.into_pipeline_data())
|
||||||
|
} else {
|
||||||
|
let buffer = engine_state
|
||||||
|
.repl_buffer_state
|
||||||
|
.lock()
|
||||||
|
.expect("repl buffer state mutex");
|
||||||
|
if call.has_flag("cursor") {
|
||||||
|
let cursor_pos = engine_state
|
||||||
|
.repl_cursor_pos
|
||||||
|
.lock()
|
||||||
|
.expect("repl cursor pos mutex");
|
||||||
|
let char_pos = buffer
|
||||||
|
.grapheme_indices(true)
|
||||||
|
.position(|(i, _c)| i == *cursor_pos)
|
||||||
|
.unwrap_or(buffer.len());
|
||||||
|
Ok(Value::String {
|
||||||
|
val: char_pos.to_string(),
|
||||||
|
span: call.head,
|
||||||
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
|
} else {
|
||||||
|
Ok(Value::String {
|
||||||
|
val: buffer.to_string(),
|
||||||
|
span: call.head,
|
||||||
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ impl Command for Const {
|
|||||||
.required("const_name", SyntaxShape::VarWithOptType, "constant name")
|
.required("const_name", SyntaxShape::VarWithOptType, "constant name")
|
||||||
.required(
|
.required(
|
||||||
"initial_value",
|
"initial_value",
|
||||||
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
|
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::MathExpression)),
|
||||||
"equals sign followed by constant value",
|
"equals sign followed by constant value",
|
||||||
)
|
)
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
|
@ -22,7 +22,11 @@ impl Command for Do {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("do")
|
Signature::build("do")
|
||||||
.required("closure", SyntaxShape::Any, "the closure to run")
|
.required(
|
||||||
|
"closure",
|
||||||
|
SyntaxShape::OneOf(vec![SyntaxShape::Closure(None), SyntaxShape::Any]),
|
||||||
|
"the closure to run",
|
||||||
|
)
|
||||||
.input_output_types(vec![(Type::Any, Type::Any)])
|
.input_output_types(vec![(Type::Any, Type::Any)])
|
||||||
.switch(
|
.switch(
|
||||||
"ignore-errors",
|
"ignore-errors",
|
||||||
|
@ -44,43 +44,66 @@ impl Command for ErrorMake {
|
|||||||
let arg: Value = call.req(engine_state, stack, 0)?;
|
let arg: Value = call.req(engine_state, stack, 0)?;
|
||||||
let unspanned = call.has_flag("unspanned");
|
let unspanned = call.has_flag("unspanned");
|
||||||
|
|
||||||
if unspanned {
|
let throw_error = if unspanned { None } else { Some(span) };
|
||||||
Err(make_error(&arg, None).unwrap_or_else(|| {
|
Err(make_error(&arg, throw_error).unwrap_or_else(|| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Creating error value not supported.".into(),
|
"Creating error value not supported.".into(),
|
||||||
"unsupported error format".into(),
|
"unsupported error format".into(),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
} else {
|
|
||||||
Err(make_error(&arg, Some(span)).unwrap_or_else(|| {
|
|
||||||
ShellError::GenericError(
|
|
||||||
"Creating error value not supported.".into(),
|
|
||||||
"unsupported error format".into(),
|
|
||||||
Some(span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Create a custom error for a custom command",
|
description: "Create a simple custom error",
|
||||||
example: r#"def foo [x] {
|
example: r#"error make {msg: "my custom error message"}"#,
|
||||||
let span = (metadata $x).span;
|
result: Some(Value::Error {
|
||||||
error make {msg: "this is fishy", label: {text: "fish right here", start: $span.start, end: $span.end } }
|
error: Box::new(ShellError::GenericError(
|
||||||
}"#,
|
"my custom error message".to_string(),
|
||||||
result: None,
|
"".to_string(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Create a simple custom error for a custom command",
|
description: "Create a more complex custom error",
|
||||||
|
example: r#"error make {
|
||||||
|
msg: "my custom error message"
|
||||||
|
label: {
|
||||||
|
text: "my custom label text" # not mandatory unless $.label exists
|
||||||
|
start: 123 # not mandatory unless $.label.end is set
|
||||||
|
end: 456 # not mandatory unless $.label.start is set
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
result: Some(Value::Error {
|
||||||
|
error: Box::new(ShellError::GenericError(
|
||||||
|
"my custom error message".to_string(),
|
||||||
|
"my custom label text".to_string(),
|
||||||
|
Some(Span::new(123, 456)),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description:
|
||||||
|
"Create a custom error for a custom command that shows the span of the argument",
|
||||||
example: r#"def foo [x] {
|
example: r#"def foo [x] {
|
||||||
error make {msg: "this is fishy"}
|
let span = (metadata $x).span;
|
||||||
|
error make {
|
||||||
|
msg: "this is fishy"
|
||||||
|
label: {
|
||||||
|
text: "fish right here"
|
||||||
|
start: $span.start
|
||||||
|
end: $span.end
|
||||||
|
}
|
||||||
|
}
|
||||||
}"#,
|
}"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
@ -89,7 +112,7 @@ impl Command for ErrorMake {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
||||||
if let Value::Record { .. } = &value {
|
if let Value::Record { span, .. } = &value {
|
||||||
let msg = value.get_data_by_key("msg");
|
let msg = value.get_data_by_key("msg");
|
||||||
let label = value.get_data_by_key("label");
|
let label = value.get_data_by_key("label");
|
||||||
|
|
||||||
@ -99,6 +122,11 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
|||||||
let label_end = label.get_data_by_key("end");
|
let label_end = label.get_data_by_key("end");
|
||||||
let label_text = label.get_data_by_key("text");
|
let label_text = label.get_data_by_key("text");
|
||||||
|
|
||||||
|
let label_span = match label.span() {
|
||||||
|
Ok(lspan) => Some(lspan),
|
||||||
|
Err(_) => None,
|
||||||
|
};
|
||||||
|
|
||||||
match (label_start, label_end, label_text) {
|
match (label_start, label_end, label_text) {
|
||||||
(
|
(
|
||||||
Some(Value::Int { val: start, .. }),
|
Some(Value::Int { val: start, .. }),
|
||||||
@ -106,13 +134,25 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
|||||||
Some(Value::String {
|
Some(Value::String {
|
||||||
val: label_text, ..
|
val: label_text, ..
|
||||||
}),
|
}),
|
||||||
) => Some(ShellError::GenericError(
|
) => {
|
||||||
message,
|
if start > end {
|
||||||
label_text,
|
Some(ShellError::GenericError(
|
||||||
Some(Span::new(start as usize, end as usize)),
|
"invalid error format.".into(),
|
||||||
None,
|
"`$.label.start` should be smaller than `$.label.end`".into(),
|
||||||
Vec::new(),
|
label_span,
|
||||||
)),
|
Some(format!("{} > {}", start, end)),
|
||||||
|
Vec::new(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Some(ShellError::GenericError(
|
||||||
|
message,
|
||||||
|
label_text,
|
||||||
|
Some(Span::new(start as usize, end as usize)),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
@ -126,6 +166,27 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
|||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
)),
|
)),
|
||||||
|
(_, _, None) => Some(ShellError::GenericError(
|
||||||
|
"Unable to parse error format.".into(),
|
||||||
|
"missing required member `$.label.text`".into(),
|
||||||
|
label_span,
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)),
|
||||||
|
(Some(Value::Int { .. }), None, _) => Some(ShellError::GenericError(
|
||||||
|
"Unable to parse error format.".into(),
|
||||||
|
"missing required member `$.label.end`".into(),
|
||||||
|
label_span,
|
||||||
|
Some("required because `$.label.start` is set".to_string()),
|
||||||
|
Vec::new(),
|
||||||
|
)),
|
||||||
|
(None, Some(Value::Int { .. }), _) => Some(ShellError::GenericError(
|
||||||
|
"Unable to parse error format.".into(),
|
||||||
|
"missing required member `$.label.start`".into(),
|
||||||
|
label_span,
|
||||||
|
Some("required because `$.label.end` is set".to_string()),
|
||||||
|
Vec::new(),
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,6 +197,13 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
|||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
)),
|
)),
|
||||||
|
(None, _) => Some(ShellError::GenericError(
|
||||||
|
"Unable to parse error format.".into(),
|
||||||
|
"missing required member `$.msg`".into(),
|
||||||
|
Some(*span),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -162,7 +162,7 @@ impl Command for For {
|
|||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
Ok(pipeline) => {
|
Ok(pipeline) => {
|
||||||
let exit_code = pipeline.print(&engine_state, stack, false, false)?;
|
let exit_code = pipeline.drain_with_exit_code()?;
|
||||||
if exit_code != 0 {
|
if exit_code != 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
152
crates/nu-cmd-lang/src/core_commands/help_externs.rs
Normal file
152
crates/nu-cmd-lang/src/core_commands/help_externs.rs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
use crate::help::highlight_search_in_table;
|
||||||
|
use nu_color_config::StyleComputer;
|
||||||
|
use nu_engine::{get_full_help, scope::ScopeData, CallExt};
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||||
|
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct HelpExterns;
|
||||||
|
|
||||||
|
impl Command for HelpExterns {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"help externs"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Show help on nushell externs."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("help externs")
|
||||||
|
.category(Category::Core)
|
||||||
|
.rest(
|
||||||
|
"rest",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"the name of extern to get help on",
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"find",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"string to find in extern names and usage",
|
||||||
|
Some('f'),
|
||||||
|
)
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
|
||||||
|
.allow_variants_without_examples(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "show all externs",
|
||||||
|
example: "help externs",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "show help for single extern",
|
||||||
|
example: "help externs smth",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "search for string in extern names and usages",
|
||||||
|
example: "help externs --find smth",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
help_externs(engine_state, stack, call)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn help_externs(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let head = call.head;
|
||||||
|
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
|
||||||
|
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
|
||||||
|
|
||||||
|
// 🚩The following two-lines are copied from filters/find.rs:
|
||||||
|
let style_computer = StyleComputer::from_config(engine_state, stack);
|
||||||
|
// Currently, search results all use the same style.
|
||||||
|
// Also note that this sample string is passed into user-written code (the closure that may or may not be
|
||||||
|
// defined for "string").
|
||||||
|
let string_style = style_computer.compute("string", &Value::string("search result", head));
|
||||||
|
|
||||||
|
if let Some(f) = find {
|
||||||
|
let all_cmds_vec = build_help_externs(engine_state, stack, head);
|
||||||
|
let found_cmds_vec =
|
||||||
|
highlight_search_in_table(all_cmds_vec, &f.item, &["name", "usage"], &string_style)?;
|
||||||
|
|
||||||
|
return Ok(found_cmds_vec
|
||||||
|
.into_iter()
|
||||||
|
.into_pipeline_data(engine_state.ctrlc.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if rest.is_empty() {
|
||||||
|
let found_cmds_vec = build_help_externs(engine_state, stack, head);
|
||||||
|
|
||||||
|
Ok(found_cmds_vec
|
||||||
|
.into_iter()
|
||||||
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
|
} else {
|
||||||
|
let mut name = String::new();
|
||||||
|
|
||||||
|
for r in &rest {
|
||||||
|
if !name.is_empty() {
|
||||||
|
name.push(' ');
|
||||||
|
}
|
||||||
|
name.push_str(&r.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = engine_state
|
||||||
|
.get_signatures_with_examples(false)
|
||||||
|
.iter()
|
||||||
|
.filter(|(signature, _, _, _, _)| signature.name == name)
|
||||||
|
.map(|(signature, examples, _, _, is_parser_keyword)| {
|
||||||
|
get_full_help(signature, examples, engine_state, stack, *is_parser_keyword)
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
if !output.is_empty() {
|
||||||
|
Ok(Value::String {
|
||||||
|
val: output.join("======================\n\n"),
|
||||||
|
span: call.head,
|
||||||
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
|
} else {
|
||||||
|
Err(ShellError::CommandNotFound(span(&[
|
||||||
|
rest[0].span,
|
||||||
|
rest[rest.len() - 1].span,
|
||||||
|
])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_help_externs(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec<Value> {
|
||||||
|
let mut scope = ScopeData::new(engine_state, stack);
|
||||||
|
scope.populate_all();
|
||||||
|
scope.collect_externs(span)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use super::HelpExterns;
|
||||||
|
use crate::test_examples;
|
||||||
|
test_examples(HelpExterns {})
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ impl Command for If {
|
|||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("if")
|
Signature::build("if")
|
||||||
.input_output_types(vec![(Type::Any, Type::Any)])
|
.input_output_types(vec![(Type::Any, Type::Any)])
|
||||||
.required("cond", SyntaxShape::Expression, "condition to check")
|
.required("cond", SyntaxShape::MathExpression, "condition to check")
|
||||||
.required(
|
.required(
|
||||||
"then_block",
|
"then_block",
|
||||||
SyntaxShape::Block,
|
SyntaxShape::Block,
|
||||||
|
@ -22,7 +22,7 @@ impl Command for Let {
|
|||||||
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
|
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
|
||||||
.required(
|
.required(
|
||||||
"initial_value",
|
"initial_value",
|
||||||
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
|
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::MathExpression)),
|
||||||
"equals sign followed by value",
|
"equals sign followed by value",
|
||||||
)
|
)
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
|
@ -67,7 +67,7 @@ impl Command for Loop {
|
|||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
Ok(pipeline) => {
|
Ok(pipeline) => {
|
||||||
let exit_code = pipeline.print(engine_state, stack, false, false)?;
|
let exit_code = pipeline.drain_with_exit_code()?;
|
||||||
if exit_code != 0 {
|
if exit_code != 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
133
crates/nu-cmd-lang/src/core_commands/match_.rs
Normal file
133
crates/nu-cmd-lang/src/core_commands/match_.rs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
use nu_engine::{eval_block, eval_expression_with_input, CallExt};
|
||||||
|
use nu_protocol::ast::{Call, Expr, Expression};
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Matcher, Stack};
|
||||||
|
use nu_protocol::{
|
||||||
|
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Match;
|
||||||
|
|
||||||
|
impl Command for Match {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"match"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Conditionally run a block on a matched value."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("match")
|
||||||
|
.input_output_types(vec![(Type::Any, Type::Any)])
|
||||||
|
.required("value", SyntaxShape::Any, "value to check")
|
||||||
|
.required(
|
||||||
|
"match_block",
|
||||||
|
SyntaxShape::MatchBlock,
|
||||||
|
"block to run if check succeeds",
|
||||||
|
)
|
||||||
|
.category(Category::Core)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"This command is a parser keyword. For details, check:
|
||||||
|
https://www.nushell.sh/book/thinking_in_nu.html"#
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_parser_keyword(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let value: Value = call.req(engine_state, stack, 0)?;
|
||||||
|
let block = call.positional_nth(1);
|
||||||
|
|
||||||
|
if let Some(Expression {
|
||||||
|
expr: Expr::MatchBlock(matches),
|
||||||
|
..
|
||||||
|
}) = block
|
||||||
|
{
|
||||||
|
for match_ in matches {
|
||||||
|
let mut match_variables = vec![];
|
||||||
|
if match_.0.match_value(&value, &mut match_variables) {
|
||||||
|
// This case does match, go ahead and return the evaluated expression
|
||||||
|
for match_variable in match_variables {
|
||||||
|
stack.add_var(match_variable.0, match_variable.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(block_id) = match_.1.as_block() {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
return eval_block(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
block,
|
||||||
|
input,
|
||||||
|
call.redirect_stdout,
|
||||||
|
call.redirect_stderr,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return eval_expression_with_input(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
&match_.1,
|
||||||
|
input,
|
||||||
|
call.redirect_stdout,
|
||||||
|
call.redirect_stderr,
|
||||||
|
)
|
||||||
|
.map(|x| x.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(PipelineData::Empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Match on a value in range",
|
||||||
|
example: "match 3 { 1..10 => 'yes!' }",
|
||||||
|
result: Some(Value::test_string("yes!")),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Match on a field in a record",
|
||||||
|
example: "match {a: 100} { {a: $my_value} => { $my_value } }",
|
||||||
|
result: Some(Value::test_int(100)),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Match with a catch-all",
|
||||||
|
example: "match 3 { 1 => { 'yes!' }, _ => { 'no!' } }",
|
||||||
|
result: Some(Value::test_string("no!")),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Match against a list",
|
||||||
|
example: "match [1, 2, 3] { [$a, $b, $c] => { $a + $b + $c }, _ => 0 }",
|
||||||
|
result: Some(Value::test_int(6)),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Match against pipeline input",
|
||||||
|
example: "{a: {b: 3}} | match $in {{a: { $b }} => ($b + 10) }",
|
||||||
|
result: Some(Value::test_int(13)),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(Match {})
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
mod alias;
|
mod alias;
|
||||||
mod break_;
|
mod break_;
|
||||||
|
mod collect;
|
||||||
mod commandline;
|
mod commandline;
|
||||||
mod const_;
|
mod const_;
|
||||||
mod continue_;
|
mod continue_;
|
||||||
@ -20,6 +21,7 @@ mod for_;
|
|||||||
pub mod help;
|
pub mod help;
|
||||||
pub mod help_aliases;
|
pub mod help_aliases;
|
||||||
pub mod help_commands;
|
pub mod help_commands;
|
||||||
|
pub mod help_externs;
|
||||||
pub mod help_modules;
|
pub mod help_modules;
|
||||||
mod help_operators;
|
mod help_operators;
|
||||||
mod hide;
|
mod hide;
|
||||||
@ -28,6 +30,7 @@ mod if_;
|
|||||||
mod ignore;
|
mod ignore;
|
||||||
mod let_;
|
mod let_;
|
||||||
mod loop_;
|
mod loop_;
|
||||||
|
mod match_;
|
||||||
mod module;
|
mod module;
|
||||||
mod mut_;
|
mod mut_;
|
||||||
pub(crate) mod overlay;
|
pub(crate) mod overlay;
|
||||||
@ -39,6 +42,7 @@ mod while_;
|
|||||||
|
|
||||||
pub use alias::Alias;
|
pub use alias::Alias;
|
||||||
pub use break_::Break;
|
pub use break_::Break;
|
||||||
|
pub use collect::Collect;
|
||||||
pub use commandline::Commandline;
|
pub use commandline::Commandline;
|
||||||
pub use const_::Const;
|
pub use const_::Const;
|
||||||
pub use continue_::Continue;
|
pub use continue_::Continue;
|
||||||
@ -59,6 +63,7 @@ pub use for_::For;
|
|||||||
pub use help::Help;
|
pub use help::Help;
|
||||||
pub use help_aliases::HelpAliases;
|
pub use help_aliases::HelpAliases;
|
||||||
pub use help_commands::HelpCommands;
|
pub use help_commands::HelpCommands;
|
||||||
|
pub use help_externs::HelpExterns;
|
||||||
pub use help_modules::HelpModules;
|
pub use help_modules::HelpModules;
|
||||||
pub use help_operators::HelpOperators;
|
pub use help_operators::HelpOperators;
|
||||||
pub use hide::Hide;
|
pub use hide::Hide;
|
||||||
@ -67,6 +72,7 @@ pub use if_::If;
|
|||||||
pub use ignore::Ignore;
|
pub use ignore::Ignore;
|
||||||
pub use let_::Let;
|
pub use let_::Let;
|
||||||
pub use loop_::Loop;
|
pub use loop_::Loop;
|
||||||
|
pub use match_::Match;
|
||||||
pub use module::Module;
|
pub use module::Module;
|
||||||
pub use mut_::Mut;
|
pub use mut_::Mut;
|
||||||
pub use overlay::*;
|
pub use overlay::*;
|
||||||
|
@ -22,7 +22,7 @@ impl Command for Mut {
|
|||||||
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
|
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
|
||||||
.required(
|
.required(
|
||||||
"initial_value",
|
"initial_value",
|
||||||
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
|
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::MathExpression)),
|
||||||
"equals sign followed by value",
|
"equals sign followed by value",
|
||||||
)
|
)
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
|
@ -63,7 +63,7 @@ impl Command for Register {
|
|||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Register `nu_plugin_query` plugin from `nu -c`(plugin will be available in that nu session only)",
|
description: "Register `nu_plugin_query` plugin from `nu -c` (writes/updates $nu.plugin-path)",
|
||||||
example: r#"let plugin = ((which nu).path.0 | path dirname | path join 'nu_plugin_query'); nu -c $'register ($plugin); version'"#,
|
example: r#"let plugin = ((which nu).path.0 | path dirname | path join 'nu_plugin_query'); nu -c $'register ($plugin); version'"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Block, Closure, Command, EngineState, Stack};
|
use nu_protocol::engine::{Block, Closure, Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
Value,
|
Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -26,7 +26,10 @@ impl Command for Try {
|
|||||||
"catch_block",
|
"catch_block",
|
||||||
SyntaxShape::Keyword(
|
SyntaxShape::Keyword(
|
||||||
b"catch".to_vec(),
|
b"catch".to_vec(),
|
||||||
Box::new(SyntaxShape::Closure(Some(vec![SyntaxShape::Any]))),
|
Box::new(SyntaxShape::OneOf(vec![
|
||||||
|
SyntaxShape::Closure(None),
|
||||||
|
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
||||||
|
])),
|
||||||
),
|
),
|
||||||
"block to run if try block fails",
|
"block to run if try block fails",
|
||||||
)
|
)
|
||||||
@ -59,17 +62,13 @@ impl Command for Try {
|
|||||||
match result {
|
match result {
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let error = intercept_block_control(error)?;
|
let error = intercept_block_control(error)?;
|
||||||
let err_value = Value::Error {
|
let err_record = err_to_record(error, call.head);
|
||||||
error: Box::new(error),
|
handle_catch(err_record, catch_block, engine_state, stack)
|
||||||
};
|
|
||||||
handle_catch(err_value, catch_block, engine_state, stack)
|
|
||||||
}
|
}
|
||||||
Ok(PipelineData::Value(Value::Error { error }, ..)) => {
|
Ok(PipelineData::Value(Value::Error { error }, ..)) => {
|
||||||
let error = intercept_block_control(*error)?;
|
let error = intercept_block_control(*error)?;
|
||||||
let err_value = Value::Error {
|
let err_record = err_to_record(error, call.head);
|
||||||
error: Box::new(error),
|
handle_catch(err_record, catch_block, engine_state, stack)
|
||||||
};
|
|
||||||
handle_catch(err_value, catch_block, engine_state, stack)
|
|
||||||
}
|
}
|
||||||
// external command may fail to run
|
// external command may fail to run
|
||||||
Ok(pipeline) => {
|
Ok(pipeline) => {
|
||||||
@ -145,6 +144,19 @@ fn intercept_block_control(error: ShellError) -> Result<ShellError, ShellError>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert from `error` to [`Value::Record`] so the error information can be easily accessed in catch.
|
||||||
|
fn err_to_record(error: ShellError, head: Span) -> Value {
|
||||||
|
let cols = vec!["msg".to_string(), "debug".to_string(), "raw".to_string()];
|
||||||
|
let vals = vec![
|
||||||
|
Value::string(error.to_string(), head),
|
||||||
|
Value::string(format!("{error:?}"), head),
|
||||||
|
Value::Error {
|
||||||
|
error: Box::new(error),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
Value::record(cols, vals, head)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -21,7 +21,7 @@ impl Command for While {
|
|||||||
Signature::build("while")
|
Signature::build("while")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.required("cond", SyntaxShape::Expression, "condition to check")
|
.required("cond", SyntaxShape::MathExpression, "condition to check")
|
||||||
.required(
|
.required(
|
||||||
"block",
|
"block",
|
||||||
SyntaxShape::Block,
|
SyntaxShape::Block,
|
||||||
@ -77,8 +77,7 @@ impl Command for While {
|
|||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
Ok(pipeline) => {
|
Ok(pipeline) => {
|
||||||
let exit_code =
|
let exit_code = pipeline.drain_with_exit_code()?;
|
||||||
pipeline.print(engine_state, stack, false, false)?;
|
|
||||||
if exit_code != 0 {
|
if exit_code != 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ pub fn create_default_context() -> EngineState {
|
|||||||
bind_command! {
|
bind_command! {
|
||||||
Alias,
|
Alias,
|
||||||
Break,
|
Break,
|
||||||
|
Collect,
|
||||||
Commandline,
|
Commandline,
|
||||||
Const,
|
Const,
|
||||||
Continue,
|
Continue,
|
||||||
@ -39,6 +40,7 @@ pub fn create_default_context() -> EngineState {
|
|||||||
HelpAliases,
|
HelpAliases,
|
||||||
HelpCommands,
|
HelpCommands,
|
||||||
HelpModules,
|
HelpModules,
|
||||||
|
HelpExterns,
|
||||||
HelpOperators,
|
HelpOperators,
|
||||||
Hide,
|
Hide,
|
||||||
HideEnv,
|
HideEnv,
|
||||||
@ -51,6 +53,7 @@ pub fn create_default_context() -> EngineState {
|
|||||||
OverlayHide,
|
OverlayHide,
|
||||||
Let,
|
Let,
|
||||||
Loop,
|
Loop,
|
||||||
|
Match,
|
||||||
Module,
|
Module,
|
||||||
Mut,
|
Mut,
|
||||||
Return,
|
Return,
|
||||||
|
@ -13,7 +13,7 @@ mod test_examples {
|
|||||||
check_example_evaluates_to_expected_output,
|
check_example_evaluates_to_expected_output,
|
||||||
check_example_input_and_output_types_match_command_signature,
|
check_example_input_and_output_types_match_command_signature,
|
||||||
};
|
};
|
||||||
use crate::{Break, Describe, Mut};
|
use crate::{Break, Collect, Describe, Mut};
|
||||||
use crate::{Echo, If, Let};
|
use crate::{Echo, If, Let};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Command, EngineState, StateWorkingSet},
|
engine::{Command, EngineState, StateWorkingSet},
|
||||||
@ -66,6 +66,7 @@ mod test_examples {
|
|||||||
working_set.add_decl(Box::new(If));
|
working_set.add_decl(Box::new(If));
|
||||||
working_set.add_decl(Box::new(Let));
|
working_set.add_decl(Box::new(Let));
|
||||||
working_set.add_decl(Box::new(Mut));
|
working_set.add_decl(Box::new(Mut));
|
||||||
|
working_set.add_decl(Box::new(Collect));
|
||||||
|
|
||||||
// Adding the command that is being tested to the working set
|
// Adding the command that is being tested to the working set
|
||||||
working_set.add_decl(cmd);
|
working_set.add_decl(cmd);
|
||||||
|
@ -5,7 +5,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-color-config"
|
name = "nu-color-config"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
@ -15,11 +15,11 @@ serde = { version="1.0.123", features=["derive"] }
|
|||||||
# used only for text_style Alignments
|
# used only for text_style Alignments
|
||||||
tabled = { version = "0.10.0", features = ["color"], default-features = false }
|
tabled = { version = "0.10.0", features = ["color"], default-features = false }
|
||||||
|
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.77.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.78.0" }
|
||||||
nu-ansi-term = "0.47.0"
|
nu-ansi-term = "0.47.0"
|
||||||
nu-utils = { path = "../nu-utils", version = "0.77.0" }
|
nu-utils = { path = "../nu-utils", version = "0.78.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.77.0" }
|
nu-engine = { path = "../nu-engine", version = "0.78.0" }
|
||||||
nu-json = { path="../nu-json", version = "0.77.0" }
|
nu-json = { path="../nu-json", version = "0.78.0" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path="../nu-test-support", version = "0.77.0" }
|
nu-test-support = { path="../nu-test-support", version = "0.78.0" }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 - 2022 The Nushell Project Developers
|
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -20,22 +20,26 @@ impl From<Style> for NuStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn style_get_attr(s: Style) -> Option<String> {
|
fn style_get_attr(s: Style) -> Option<String> {
|
||||||
macro_rules! check {
|
|
||||||
($attrs:expr, $b:expr, $c:expr) => {
|
|
||||||
if $b {
|
|
||||||
$attrs.push($c);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut attrs = String::new();
|
let mut attrs = String::new();
|
||||||
|
|
||||||
check!(attrs, s.is_blink, 'l');
|
if s.is_blink {
|
||||||
check!(attrs, s.is_bold, 'b');
|
attrs.push('l');
|
||||||
check!(attrs, s.is_dimmed, 'd');
|
};
|
||||||
check!(attrs, s.is_reverse, 'r');
|
if s.is_bold {
|
||||||
check!(attrs, s.is_strikethrough, 's');
|
attrs.push('b');
|
||||||
check!(attrs, s.is_underline, 'u');
|
};
|
||||||
|
if s.is_dimmed {
|
||||||
|
attrs.push('d');
|
||||||
|
};
|
||||||
|
if s.is_reverse {
|
||||||
|
attrs.push('r');
|
||||||
|
};
|
||||||
|
if s.is_strikethrough {
|
||||||
|
attrs.push('s');
|
||||||
|
};
|
||||||
|
if s.is_underline {
|
||||||
|
attrs.push('u');
|
||||||
|
};
|
||||||
|
|
||||||
if attrs.is_empty() {
|
if attrs.is_empty() {
|
||||||
None
|
None
|
||||||
|
@ -23,6 +23,7 @@ pub fn default_shape_color(shape: String) -> Style {
|
|||||||
"shape_internalcall" => Style::new().fg(Color::Cyan).bold(),
|
"shape_internalcall" => Style::new().fg(Color::Cyan).bold(),
|
||||||
"shape_list" => Style::new().fg(Color::Cyan).bold(),
|
"shape_list" => Style::new().fg(Color::Cyan).bold(),
|
||||||
"shape_literal" => Style::new().fg(Color::Blue),
|
"shape_literal" => Style::new().fg(Color::Blue),
|
||||||
|
"shape_match_pattern" => Style::new().fg(Color::Green),
|
||||||
"shape_nothing" => Style::new().fg(Color::LightCyan),
|
"shape_nothing" => Style::new().fg(Color::LightCyan),
|
||||||
"shape_operator" => Style::new().fg(Color::Yellow),
|
"shape_operator" => Style::new().fg(Color::Yellow),
|
||||||
"shape_or" => Style::new().fg(Color::Purple).bold(),
|
"shape_or" => Style::new().fg(Color::Purple).bold(),
|
||||||
|
@ -21,13 +21,6 @@ pub enum ComputableStyle {
|
|||||||
Closure(Value),
|
Closure(Value),
|
||||||
}
|
}
|
||||||
|
|
||||||
// macro used for adding initial values to the style hashmap
|
|
||||||
macro_rules! initial {
|
|
||||||
($a:expr, $b:expr) => {
|
|
||||||
($a.to_string(), ComputableStyle::Static($b))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// An alias for the mapping used internally by StyleComputer.
|
// An alias for the mapping used internally by StyleComputer.
|
||||||
pub type StyleMapping = HashMap<String, ComputableStyle>;
|
pub type StyleMapping = HashMap<String, ComputableStyle>;
|
||||||
//
|
//
|
||||||
@ -153,6 +146,12 @@ impl<'a> StyleComputer<'a> {
|
|||||||
pub fn from_config(engine_state: &'a EngineState, stack: &'a Stack) -> StyleComputer<'a> {
|
pub fn from_config(engine_state: &'a EngineState, stack: &'a Stack) -> StyleComputer<'a> {
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
|
|
||||||
|
macro_rules! initial {
|
||||||
|
($a:expr, $b:expr) => {
|
||||||
|
($a.to_string(), ComputableStyle::Static($b))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Create the hashmap
|
// Create the hashmap
|
||||||
let mut map: StyleMapping = HashMap::from([
|
let mut map: StyleMapping = HashMap::from([
|
||||||
initial!("separator", Color::White.normal()),
|
initial!("separator", Color::White.normal()),
|
||||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-command"
|
name = "nu-command"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
||||||
version = "0.77.0"
|
version = "0.78.0"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -13,27 +13,27 @@ version = "0.77.0"
|
|||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.77.0" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.78.0" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.77.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.78.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.77.0" }
|
nu-engine = { path = "../nu-engine", version = "0.78.0" }
|
||||||
nu-explore = { path = "../nu-explore", version = "0.77.0" }
|
nu-explore = { path = "../nu-explore", version = "0.78.0" }
|
||||||
nu-glob = { path = "../nu-glob", version = "0.77.0" }
|
nu-glob = { path = "../nu-glob", version = "0.78.0" }
|
||||||
nu-json = { path = "../nu-json", version = "0.77.0" }
|
nu-json = { path = "../nu-json", version = "0.78.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.77.0" }
|
nu-parser = { path = "../nu-parser", version = "0.78.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.77.0" }
|
nu-path = { path = "../nu-path", version = "0.78.0" }
|
||||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.77.0" }
|
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.78.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.77.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.78.0" }
|
||||||
nu-system = { path = "../nu-system", version = "0.77.0" }
|
nu-system = { path = "../nu-system", version = "0.78.0" }
|
||||||
nu-table = { path = "../nu-table", version = "0.77.0" }
|
nu-table = { path = "../nu-table", version = "0.78.0" }
|
||||||
nu-term-grid = { path = "../nu-term-grid", version = "0.77.0" }
|
nu-term-grid = { path = "../nu-term-grid", version = "0.78.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.77.0" }
|
nu-utils = { path = "../nu-utils", version = "0.78.0" }
|
||||||
num-format = { version = "0.4.3" }
|
num-format = { version = "0.4.3" }
|
||||||
|
|
||||||
nu-ansi-term = "0.47.0"
|
nu-ansi-term = "0.47.0"
|
||||||
|
|
||||||
# Potential dependencies for extras
|
# Potential dependencies for extras
|
||||||
Inflector = "0.11"
|
Inflector = "0.11"
|
||||||
alphanumeric-sort = "1.4.4"
|
alphanumeric-sort = "1.5.0"
|
||||||
atty = "0.2.14"
|
atty = "0.2.14"
|
||||||
base64 = "0.21.0"
|
base64 = "0.21.0"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
@ -60,16 +60,17 @@ itertools = "0.10.0"
|
|||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
|
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
|
||||||
md5 = { package = "md-5", version = "0.10.0" }
|
md5 = { package = "md-5", version = "0.10.0" }
|
||||||
|
miette = { version = "5.6.0", features = ["fancy-no-backtrace"] }
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
mime_guess = "2.0.4"
|
mime_guess = "2.0.4"
|
||||||
notify = "4.0.17"
|
notify = "4.0.17"
|
||||||
num = { version = "0.4.0", optional = true }
|
num = { version = "0.4.0", optional = true }
|
||||||
num-traits = "0.2.14"
|
num-traits = "0.2.14"
|
||||||
once_cell = "1.17"
|
once_cell = "1.17"
|
||||||
open = "3.2.0"
|
open = "4.0.0"
|
||||||
pathdiff = "0.2.1"
|
pathdiff = "0.2.1"
|
||||||
powierza-coefficient = "1.0.2"
|
powierza-coefficient = "1.0.2"
|
||||||
quick-xml = "0.27"
|
quick-xml = "0.28"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
rayon = "1.7.0"
|
rayon = "1.7.0"
|
||||||
regex = "1.7.1"
|
regex = "1.7.1"
|
||||||
@ -84,9 +85,9 @@ serde_yaml = "0.9.4"
|
|||||||
sha2 = "0.10.0"
|
sha2 = "0.10.0"
|
||||||
# Disable default features b/c the default features build Git (very slow to compile)
|
# Disable default features b/c the default features build Git (very slow to compile)
|
||||||
percent-encoding = "2.2.0"
|
percent-encoding = "2.2.0"
|
||||||
reedline = { version = "0.17.0", features = ["bashisms", "sqlite"] }
|
reedline = { version = "0.18.0", features = ["bashisms", "sqlite"] }
|
||||||
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
||||||
sqlparser = { version = "0.30.0", features = ["serde"], optional = true }
|
sqlparser = { version = "0.32.0", features = ["serde"], optional = true }
|
||||||
sysinfo = "0.28.2"
|
sysinfo = "0.28.2"
|
||||||
tabled = "0.10.0"
|
tabled = "0.10.0"
|
||||||
terminal_size = "0.2.1"
|
terminal_size = "0.2.1"
|
||||||
@ -146,7 +147,7 @@ version = "0.27.2"
|
|||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.windows]
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_SystemServices"]
|
features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_SystemServices"]
|
||||||
version = "0.44.0"
|
version = "0.46.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dataframe = ["num", "polars", "sqlparser"]
|
dataframe = ["num", "polars", "sqlparser"]
|
||||||
@ -156,12 +157,11 @@ trash-support = ["trash"]
|
|||||||
which-support = ["which"]
|
which-support = ["which"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.77.0" }
|
nu-test-support = { path = "../nu-test-support", version = "0.78.0" }
|
||||||
mockito = "0.32.3"
|
mockito = "1.0.0"
|
||||||
|
|
||||||
dirs-next = "2.0.0"
|
dirs-next = "2.0.0"
|
||||||
hamcrest2 = "0.3.0"
|
hamcrest2 = "0.3.0"
|
||||||
proptest = "1.1.0"
|
|
||||||
quickcheck = "1.0.3"
|
quickcheck = "1.0.3"
|
||||||
quickcheck_macros = "1.0.0"
|
quickcheck_macros = "1.0.0"
|
||||||
rstest = { version = "0.16.0", default-features = false }
|
rstest = { version = "0.17.0", default-features = false }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 - 2022 The Nushell Project Developers
|
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
# Seeds for failure cases proptest has generated in the past. It is
|
|
||||||
# automatically read and these particular cases re-run before any
|
|
||||||
# novel cases are generated.
|
|
||||||
#
|
|
||||||
# It is recommended to check this file in to source control so that
|
|
||||||
# everyone who runs the test benefits from these saved cases.
|
|
||||||
cc 96a80ecd19729fb43a7b7bb2766b37d6083ba73b16abb97075875e3cfcdc763f # shrinks to c = '"'
|
|
||||||
cc 4146602559ea717a02bcef3c6d73cdf613c30d0c3f92c48e26c79b9a1544e027 # shrinks to c = '\\'
|
|
||||||
cc 80532a0ee73df456a719b9e3cce1ae5f3d26009dde819cbaf16f8e0cb6709705 # shrinks to c = ':'
|
|
||||||
cc cdb88505686eea3c74c36f282fd29b2b68bc118ded4ebfc36f9838d174bd7653 # shrinks to c = '`'
|
|
||||||
cc 0f534d55f9771e8810b9c4252a4168abfaec1a35e1b0cac12dbaf726d295a08c # shrinks to c = '\0'
|
|
||||||
cc 5d31bcbab722acd1f4e23ca3a4f95ff309a636b45a73ca8ae9f820d93ff57acc # shrinks to c = '{'
|
|
||||||
cc 5afec063bc96160d681d77f90041b67ef5cfdea4dcbd12d984fd828fbeb4b421 # shrinks to c = '#'
|
|
||||||
cc f919beb3ee5c70e756a15635d65ded7d44f3ae58b5e86b6c09e814d5d8cdd506 # shrinks to c = ';'
|
|
||||||
cc ec00f39b8d45dfd8808947a56af5e50ba5a0ef7c951723b45377815a02e515b1 # shrinks to c = '('
|
|
||||||
cc 25b773cdf4c24179151fa86244c7de4136e05df9e94e6ee77a336ebfd8764444 # shrinks to c = '|'
|
|
||||||
cc 94dc0d54b97d59e1c0f4cb11bdccb3823a1bb908cbc3fd643ee8f067169fad72 # shrinks to c = '0'
|
|
||||||
cc c9d0051fb1e5a8bdc1d4f5a3dceac1b4b465827d1dff4fc3a3755baae6a7bb48 # shrinks to c = '$'
|
|
||||||
cc 14ec40d2eb5bd2663e9b11bb49fb2120852f9ea71678c69d732161412b55a3ec # shrinks to s = ""
|
|
||||||
cc d4afccc51ed9d421bdb7e1723e273dfb6e77c3a449489671a496db234e87c5ed # shrinks to c = '\r'
|
|
||||||
cc 515a56d73eb1b69290ef4c714b629421989879aebd57991bd2c2bf11294353b1 # shrinks to s = "\\\\𐊀{"
|
|
||||||
cc 111566990fffa432acd2dbc845141b0e7870f97125c7621e3ddf142204568246 # shrinks to s = "'\":`"
|
|
||||||
cc 0424c33000d9188be96b3049046eb052770b2158bf5ebb0c98656d7145e8aca9 # shrinks to s = "0"
|
|
@ -90,7 +90,7 @@ impl Command for SubCommand {
|
|||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
"Apply logical negation to a list of numbers, treat input as 2 bytes number",
|
"Apply logical negation to a list of numbers, treat input as 2 bytes number",
|
||||||
example: "[4 3 2] | bits not -n 2",
|
example: "[4 3 2] | bits not -n '2'",
|
||||||
result: Some(Value::List {
|
result: Some(Value::List {
|
||||||
vals: vec![
|
vals: vec![
|
||||||
Value::test_int(65531),
|
Value::test_int(65531),
|
||||||
|
@ -85,7 +85,7 @@ impl Command for SubCommand {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Rotate right a list of numbers of one byte",
|
description: "Rotate right a list of numbers of one byte",
|
||||||
example: "[15 33 92] | bits ror 2 -n 1",
|
example: "[15 33 92] | bits ror 2 -n '1'",
|
||||||
result: Some(Value::List {
|
result: Some(Value::List {
|
||||||
vals: vec![
|
vals: vec![
|
||||||
Value::test_int(195),
|
Value::test_int(195),
|
||||||
|
@ -85,7 +85,7 @@ impl Command for SubCommand {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Shift left a number with 1 byte by 7 bits",
|
description: "Shift left a number with 1 byte by 7 bits",
|
||||||
example: "2 | bits shl 7 -n 1",
|
example: "2 | bits shl 7 -n '1'",
|
||||||
result: Some(Value::test_int(0)),
|
result: Some(Value::test_int(0)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
use crate::input_handler::{operate, CmdArgument};
|
use crate::{
|
||||||
use nu_engine::CallExt;
|
input_handler::{operate, CmdArgument},
|
||||||
use nu_protocol::ast::Call;
|
util,
|
||||||
use nu_protocol::ast::CellPath;
|
};
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
ast::{Call, CellPath},
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, Example, PipelineData, Range, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BytesAt;
|
pub struct BytesAt;
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
start: isize,
|
indexes: Subbytes,
|
||||||
end: isize,
|
|
||||||
arg_span: Span,
|
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,104 +23,15 @@ impl CmdArgument for Arguments {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ensure given `range` is valid, and returns [start, end, val_span] pair.
|
impl From<(isize, isize)> for Subbytes {
|
||||||
fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellError> {
|
fn from(input: (isize, isize)) -> Self {
|
||||||
let (start, end, span) = match range {
|
Self(input.0, input.1)
|
||||||
Value::List { mut vals, span } => {
|
}
|
||||||
if vals.len() != 2 {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"More than two indices in range".to_string(),
|
|
||||||
"value originates from here".to_string(),
|
|
||||||
head,
|
|
||||||
span,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
let end = vals.pop().expect("Already check has size 2");
|
|
||||||
let end = match end {
|
|
||||||
Value::Int { val, .. } => val.to_string(),
|
|
||||||
Value::String { val, .. } => val,
|
|
||||||
// Explicitly propagate errors instead of dropping them.
|
|
||||||
Value::Error { error } => return Err(*error),
|
|
||||||
other => {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"Only string or list<int> ranges are supported".into(),
|
|
||||||
format!("input type: {:?}", other.get_type()),
|
|
||||||
head,
|
|
||||||
other.expect_span(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let start = vals.pop().expect("Already check has size 1");
|
|
||||||
let start = match start {
|
|
||||||
Value::Int { val, .. } => val.to_string(),
|
|
||||||
Value::String { val, .. } => val,
|
|
||||||
// Explicitly propagate errors instead of dropping them.
|
|
||||||
Value::Error { error } => return Err(*error),
|
|
||||||
other => {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"Only string or list<int> ranges are supported".into(),
|
|
||||||
format!("input type: {:?}", other.get_type()),
|
|
||||||
head,
|
|
||||||
other.expect_span(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(start, end, span)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::String { val, span } => {
|
|
||||||
let split_result = val.split_once(',');
|
|
||||||
match split_result {
|
|
||||||
Some((start, end)) => (start.to_string(), end.to_string(), span),
|
|
||||||
None => {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"could not perform subbytes".to_string(),
|
|
||||||
"with this range".to_string(),
|
|
||||||
head,
|
|
||||||
span,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Explicitly propagate errors instead of dropping them.
|
|
||||||
Value::Error { error } => return Err(*error),
|
|
||||||
other => {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"could not perform subbytes".to_string(),
|
|
||||||
"with this range".to_string(),
|
|
||||||
head,
|
|
||||||
other.expect_span(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let start: isize = if start.is_empty() || start == "_" {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
start.trim().parse().map_err(|_| {
|
|
||||||
ShellError::UnsupportedInput(
|
|
||||||
"could not perform subbytes".to_string(),
|
|
||||||
"with this range".to_string(),
|
|
||||||
head,
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
})?
|
|
||||||
};
|
|
||||||
let end: isize = if end.is_empty() || end == "_" {
|
|
||||||
isize::max_value()
|
|
||||||
} else {
|
|
||||||
end.trim().parse().map_err(|_| {
|
|
||||||
ShellError::UnsupportedInput(
|
|
||||||
"could not perform subbytes".to_string(),
|
|
||||||
"with this range".to_string(),
|
|
||||||
head,
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
})?
|
|
||||||
};
|
|
||||||
Ok((start, end, span))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Subbytes(isize, isize);
|
||||||
|
|
||||||
impl Command for BytesAt {
|
impl Command for BytesAt {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"bytes at"
|
"bytes at"
|
||||||
@ -131,7 +41,7 @@ impl Command for BytesAt {
|
|||||||
Signature::build("bytes at")
|
Signature::build("bytes at")
|
||||||
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
||||||
.vectorizes_over_list(true)
|
.vectorizes_over_list(true)
|
||||||
.required("range", SyntaxShape::Any, "the indexes to get bytes")
|
.required("range", SyntaxShape::Range, "the range to get bytes")
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
@ -141,7 +51,7 @@ impl Command for BytesAt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Get bytes defined by a range. Note that the start is included but the end is excluded, and that the first byte is index 0."
|
"Get bytes defined by a range"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -155,48 +65,45 @@ impl Command for BytesAt {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let range: Value = call.req(engine_state, stack, 0)?;
|
let range: Range = call.req(engine_state, stack, 0)?;
|
||||||
let (start, end, arg_span) = parse_range(range, call.head)?;
|
let indexes = match util::process_range(&range) {
|
||||||
|
Ok(idxs) => idxs.into(),
|
||||||
|
Err(processing_error) => {
|
||||||
|
return Err(processing_error("could not perform subbytes", call.head));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
let arg = Arguments {
|
let args = Arguments {
|
||||||
start,
|
indexes,
|
||||||
end,
|
|
||||||
arg_span,
|
|
||||||
cell_paths,
|
cell_paths,
|
||||||
};
|
};
|
||||||
operate(at, arg, input, call.head, engine_state.ctrlc.clone())
|
|
||||||
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Get a subbytes `0x[10 01]` from the bytes `0x[33 44 55 10 01 13]`",
|
description: "Get a subbytes `0x[10 01]` from the bytes `0x[33 44 55 10 01 13]`",
|
||||||
example: " 0x[33 44 55 10 01 13] | bytes at [3 4]",
|
example: " 0x[33 44 55 10 01 13] | bytes at 3..<4",
|
||||||
result: Some(Value::Binary {
|
result: Some(Value::Binary {
|
||||||
val: vec![0x10],
|
val: vec![0x10],
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Alternatively, you can use the form",
|
description: "Get a subbytes `0x[10 01 13]` from the bytes `0x[33 44 55 10 01 13]`",
|
||||||
example: " 0x[33 44 55 10 01 13] | bytes at '3,4'",
|
example: " 0x[33 44 55 10 01 13] | bytes at 3..6",
|
||||||
result: Some(Value::Binary {
|
result: Some(Value::Binary {
|
||||||
val: vec![0x10],
|
val: vec![0x10, 0x01, 0x13],
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Drop the last `n` characters from the string",
|
|
||||||
example: " 0x[33 44 55 10 01 13] | bytes at ',-3'",
|
|
||||||
result: Some(Value::Binary {
|
|
||||||
val: vec![0x33, 0x44, 0x55],
|
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Get the remaining characters from a starting index",
|
description: "Get the remaining characters from a starting index",
|
||||||
example: " 0x[33 44 55 10 01 13] | bytes at '3,'",
|
example: " 0x[33 44 55 10 01 13] | bytes at 3..",
|
||||||
result: Some(Value::Binary {
|
result: Some(Value::Binary {
|
||||||
val: vec![0x10, 0x01, 0x13],
|
val: vec![0x10, 0x01, 0x13],
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
@ -204,7 +111,7 @@ impl Command for BytesAt {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Get the characters from the beginning until ending index",
|
description: "Get the characters from the beginning until ending index",
|
||||||
example: " 0x[33 44 55 10 01 13] | bytes at ',4'",
|
example: " 0x[33 44 55 10 01 13] | bytes at ..<4",
|
||||||
result: Some(Value::Binary {
|
result: Some(Value::Binary {
|
||||||
val: vec![0x33, 0x44, 0x55, 0x10],
|
val: vec![0x33, 0x44, 0x55, 0x10],
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
@ -213,7 +120,7 @@ impl Command for BytesAt {
|
|||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
"Or the characters from the beginning until ending index inside a table",
|
"Or the characters from the beginning until ending index inside a table",
|
||||||
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes at "1," ColB ColC"#,
|
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes at 1.. ColB ColC"#,
|
||||||
result: Some(Value::List {
|
result: Some(Value::List {
|
||||||
vals: vec![Value::Record {
|
vals: vec![Value::Record {
|
||||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||||
@ -240,73 +147,66 @@ impl Command for BytesAt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn at(val: &Value, args: &Arguments, span: Span) -> Value {
|
fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||||
match val {
|
let range = &args.indexes;
|
||||||
Value::Binary {
|
match input {
|
||||||
val,
|
Value::Binary { val, .. } => {
|
||||||
span: val_span,
|
use std::cmp::{self, Ordering};
|
||||||
} => at_impl(val, args, *val_span),
|
let len = val.len() as isize;
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => val.clone(),
|
let start = if range.0 < 0 { range.0 + len } else { range.0 };
|
||||||
|
|
||||||
|
let end = if range.1 < 0 {
|
||||||
|
cmp::max(range.1 + len, 0)
|
||||||
|
} else {
|
||||||
|
range.1
|
||||||
|
};
|
||||||
|
|
||||||
|
if start < len && end >= 0 {
|
||||||
|
match start.cmp(&end) {
|
||||||
|
Ordering::Equal => Value::Binary {
|
||||||
|
val: vec![],
|
||||||
|
span: head,
|
||||||
|
},
|
||||||
|
Ordering::Greater => Value::Error {
|
||||||
|
error: Box::new(ShellError::TypeMismatch {
|
||||||
|
err_message: "End must be greater than or equal to Start".to_string(),
|
||||||
|
span: head,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Ordering::Less => Value::Binary {
|
||||||
|
val: {
|
||||||
|
if end == isize::max_value() {
|
||||||
|
val.iter().skip(start as usize).copied().collect()
|
||||||
|
} else {
|
||||||
|
val.iter()
|
||||||
|
.skip(start as usize)
|
||||||
|
.take((end - start) as usize)
|
||||||
|
.copied()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
span: head,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Value::Binary {
|
||||||
|
val: vec![],
|
||||||
|
span: head,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Error { .. } => input.clone(),
|
||||||
|
|
||||||
other => Value::Error {
|
other => Value::Error {
|
||||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
error: Box::new(ShellError::UnsupportedInput(
|
||||||
exp_input_type: "binary".into(),
|
"Only binary values are supported".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
format!("input type: {:?}", other.get_type()),
|
||||||
dst_span: span,
|
head,
|
||||||
src_span: other.expect_span(),
|
// This line requires the Value::Error match above.
|
||||||
}),
|
other.expect_span(),
|
||||||
|
)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn at_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
|
||||||
let len: isize = input.len() as isize;
|
|
||||||
|
|
||||||
let start: isize = if arg.start < 0 {
|
|
||||||
arg.start + len
|
|
||||||
} else {
|
|
||||||
arg.start
|
|
||||||
};
|
|
||||||
let end: isize = if arg.end < 0 {
|
|
||||||
std::cmp::max(len + arg.end, 0)
|
|
||||||
} else {
|
|
||||||
arg.end
|
|
||||||
};
|
|
||||||
|
|
||||||
if start < len && end >= 0 {
|
|
||||||
match start.cmp(&end) {
|
|
||||||
Ordering::Equal => Value::Binary { val: vec![], span },
|
|
||||||
Ordering::Greater => Value::Error {
|
|
||||||
error: Box::new(ShellError::TypeMismatch {
|
|
||||||
err_message: "End must be greater than or equal to Start".to_string(),
|
|
||||||
span: arg.arg_span,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Ordering::Less => Value::Binary {
|
|
||||||
val: {
|
|
||||||
let input_iter = input.iter().copied().skip(start as usize);
|
|
||||||
if end == isize::max_value() {
|
|
||||||
input_iter.collect()
|
|
||||||
} else {
|
|
||||||
input_iter.take((end - start) as usize).collect()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Value::Binary { val: vec![], span }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(BytesAt {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -49,12 +49,12 @@ impl Command for BytesLen {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Return the lengths of multiple strings",
|
description: "Return the length of a binary",
|
||||||
example: "0x[1F FF AA AB] | bytes length",
|
example: "0x[1F FF AA AB] | bytes length",
|
||||||
result: Some(Value::test_int(4)),
|
result: Some(Value::test_int(4)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Return the lengths of multiple strings",
|
description: "Return the lengths of multiple binaries",
|
||||||
example: "[0x[1F FF AA AB] 0x[1F]] | bytes length",
|
example: "[0x[1F FF AA AB] 0x[1F]] | bytes length",
|
||||||
result: Some(Value::List {
|
result: Some(Value::List {
|
||||||
vals: vec![Value::test_int(4), Value::test_int(1)],
|
vals: vec![Value::test_int(4), Value::test_int(1)],
|
||||||
|
@ -240,7 +240,11 @@ mod test {
|
|||||||
},
|
},
|
||||||
Value::CellPath {
|
Value::CellPath {
|
||||||
val: CellPath {
|
val: CellPath {
|
||||||
members: vec![PathMember::Int { val: 0, span }],
|
members: vec![PathMember::Int {
|
||||||
|
val: 0,
|
||||||
|
span,
|
||||||
|
optional: false,
|
||||||
|
}],
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
|
@ -98,11 +98,17 @@ impl Command for Histogram {
|
|||||||
let frequency_name_arg = call.opt::<Spanned<String>>(engine_state, stack, 1)?;
|
let frequency_name_arg = call.opt::<Spanned<String>>(engine_state, stack, 1)?;
|
||||||
let frequency_column_name = match frequency_name_arg {
|
let frequency_column_name = match frequency_name_arg {
|
||||||
Some(inner) => {
|
Some(inner) => {
|
||||||
if ["value", "count", "quantile", "percentage"].contains(&inner.item.as_str()) {
|
let forbidden_column_names = ["value", "count", "quantile", "percentage"];
|
||||||
|
if forbidden_column_names.contains(&inner.item.as_str()) {
|
||||||
return Err(ShellError::TypeMismatch {
|
return Err(ShellError::TypeMismatch {
|
||||||
err_message:
|
err_message: format!(
|
||||||
"frequency-column-name can't be 'value', 'count' or 'percentage'"
|
"frequency-column-name can't be {}",
|
||||||
.to_string(),
|
forbidden_column_names
|
||||||
|
.iter()
|
||||||
|
.map(|val| format!("'{}'", val))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
span: inner.span,
|
span: inner.span,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ impl Command for Fill {
|
|||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
"Fill a number on the left side to a width of 5 with the character '0'",
|
"Fill a number on the left side to a width of 5 with the character '0'",
|
||||||
example: "1 | fill --alignment right --character 0 --width 5",
|
example: "1 | fill --alignment right --character '0' --width 5",
|
||||||
result: Some(Value::String {
|
result: Some(Value::String {
|
||||||
val: "00001".into(),
|
val: "00001".into(),
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
@ -113,7 +113,7 @@ impl Command for Fill {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Fill a number on both sides to a width of 5 with the character '0'",
|
description: "Fill a number on both sides to a width of 5 with the character '0'",
|
||||||
example: "1.1 | fill --alignment center --character 0 --width 5",
|
example: "1.1 | fill --alignment center --character '0' --width 5",
|
||||||
result: Some(Value::String {
|
result: Some(Value::String {
|
||||||
val: "01.10".into(),
|
val: "01.10".into(),
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
@ -122,7 +122,7 @@ impl Command for Fill {
|
|||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
"Fill a filesize on the left side to a width of 5 with the character '0'",
|
"Fill a filesize on the left side to a width of 5 with the character '0'",
|
||||||
example: "1kib | fill --alignment middle --character 0 --width 10",
|
example: "1kib | fill --alignment middle --character '0' --width 10",
|
||||||
result: Some(Value::String {
|
result: Some(Value::String {
|
||||||
val: "0001024000".into(),
|
val: "0001024000".into(),
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
|
@ -145,6 +145,30 @@ impl Command for SubCommand {
|
|||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Convert µs duration to the requested duration as a string",
|
||||||
|
example: "1000000µs | into duration --convert sec",
|
||||||
|
result: Some(Value::String {
|
||||||
|
val: "1 sec".to_string(),
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Convert duration to the µs duration as a string",
|
||||||
|
example: "1sec | into duration --convert µs",
|
||||||
|
result: Some(Value::String {
|
||||||
|
val: "1000000 µs".to_string(),
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Convert duration to µs as a string if unit asked for was us",
|
||||||
|
example: "1sec | into duration --convert us",
|
||||||
|
result: Some(Value::String {
|
||||||
|
val: "1000000 µs".to_string(),
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,6 +221,8 @@ fn convert_str_from_unit_to_unit(
|
|||||||
match (from_unit, to_unit) {
|
match (from_unit, to_unit) {
|
||||||
("ns", "ns") => Ok(val as f64),
|
("ns", "ns") => Ok(val as f64),
|
||||||
("ns", "us") => Ok(val as f64 / 1000.0),
|
("ns", "us") => Ok(val as f64 / 1000.0),
|
||||||
|
("ns", "µs") => Ok(val as f64 / 1000.0), // Micro sign
|
||||||
|
("ns", "μs") => Ok(val as f64 / 1000.0), // Greek small letter
|
||||||
("ns", "ms") => Ok(val as f64 / 1000.0 / 1000.0),
|
("ns", "ms") => Ok(val as f64 / 1000.0 / 1000.0),
|
||||||
("ns", "sec") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0),
|
("ns", "sec") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0),
|
||||||
("ns", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0),
|
("ns", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0),
|
||||||
@ -211,6 +237,8 @@ fn convert_str_from_unit_to_unit(
|
|||||||
|
|
||||||
("us", "ns") => Ok(val as f64 * 1000.0),
|
("us", "ns") => Ok(val as f64 * 1000.0),
|
||||||
("us", "us") => Ok(val as f64),
|
("us", "us") => Ok(val as f64),
|
||||||
|
("us", "µs") => Ok(val as f64), // Micro sign
|
||||||
|
("us", "μs") => Ok(val as f64), // Greek small letter
|
||||||
("us", "ms") => Ok(val as f64 / 1000.0),
|
("us", "ms") => Ok(val as f64 / 1000.0),
|
||||||
("us", "sec") => Ok(val as f64 / 1000.0 / 1000.0),
|
("us", "sec") => Ok(val as f64 / 1000.0 / 1000.0),
|
||||||
("us", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0),
|
("us", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0),
|
||||||
@ -221,8 +249,40 @@ fn convert_str_from_unit_to_unit(
|
|||||||
("us", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
|
("us", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
|
||||||
("us", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
|
("us", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
|
||||||
|
|
||||||
|
// Micro sign
|
||||||
|
("µs", "ns") => Ok(val as f64 * 1000.0),
|
||||||
|
("µs", "us") => Ok(val as f64),
|
||||||
|
("µs", "µs") => Ok(val as f64), // Micro sign
|
||||||
|
("µs", "μs") => Ok(val as f64), // Greek small letter
|
||||||
|
("µs", "ms") => Ok(val as f64 / 1000.0),
|
||||||
|
("µs", "sec") => Ok(val as f64 / 1000.0 / 1000.0),
|
||||||
|
("µs", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0),
|
||||||
|
("µs", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0),
|
||||||
|
("µs", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0),
|
||||||
|
("µs", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
|
||||||
|
("µs", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
|
||||||
|
("µs", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
|
||||||
|
("µs", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
|
||||||
|
|
||||||
|
// Greek small letter
|
||||||
|
("μs", "ns") => Ok(val as f64 * 1000.0),
|
||||||
|
("μs", "us") => Ok(val as f64),
|
||||||
|
("μs", "µs") => Ok(val as f64), // Micro sign
|
||||||
|
("μs", "μs") => Ok(val as f64), // Greek small letter
|
||||||
|
("μs", "ms") => Ok(val as f64 / 1000.0),
|
||||||
|
("μs", "sec") => Ok(val as f64 / 1000.0 / 1000.0),
|
||||||
|
("μs", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0),
|
||||||
|
("μs", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0),
|
||||||
|
("μs", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0),
|
||||||
|
("μs", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
|
||||||
|
("μs", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
|
||||||
|
("μs", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
|
||||||
|
("μs", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
|
||||||
|
|
||||||
("ms", "ns") => Ok(val as f64 * 1000.0 * 1000.0),
|
("ms", "ns") => Ok(val as f64 * 1000.0 * 1000.0),
|
||||||
("ms", "us") => Ok(val as f64 * 1000.0),
|
("ms", "us") => Ok(val as f64 * 1000.0),
|
||||||
|
("ms", "µs") => Ok(val as f64 * 1000.0), // Micro sign
|
||||||
|
("ms", "μs") => Ok(val as f64 * 1000.0), // Greek small letter
|
||||||
("ms", "ms") => Ok(val as f64),
|
("ms", "ms") => Ok(val as f64),
|
||||||
("ms", "sec") => Ok(val as f64 / 1000.0),
|
("ms", "sec") => Ok(val as f64 / 1000.0),
|
||||||
("ms", "min") => Ok(val as f64 / 1000.0 / 60.0),
|
("ms", "min") => Ok(val as f64 / 1000.0 / 60.0),
|
||||||
@ -235,6 +295,8 @@ fn convert_str_from_unit_to_unit(
|
|||||||
|
|
||||||
("sec", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0),
|
("sec", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0),
|
||||||
("sec", "us") => Ok(val as f64 * 1000.0 * 1000.0),
|
("sec", "us") => Ok(val as f64 * 1000.0 * 1000.0),
|
||||||
|
("sec", "µs") => Ok(val as f64 * 1000.0 * 1000.0), // Micro sign
|
||||||
|
("sec", "μs") => Ok(val as f64 * 1000.0 * 1000.0), // Greek small letter
|
||||||
("sec", "ms") => Ok(val as f64 * 1000.0),
|
("sec", "ms") => Ok(val as f64 * 1000.0),
|
||||||
("sec", "sec") => Ok(val as f64),
|
("sec", "sec") => Ok(val as f64),
|
||||||
("sec", "min") => Ok(val as f64 / 60.0),
|
("sec", "min") => Ok(val as f64 / 60.0),
|
||||||
@ -247,6 +309,8 @@ fn convert_str_from_unit_to_unit(
|
|||||||
|
|
||||||
("min", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0),
|
("min", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0),
|
||||||
("min", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0),
|
("min", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0),
|
||||||
|
("min", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0), // Micro sign
|
||||||
|
("min", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0), // Greek small letter
|
||||||
("min", "ms") => Ok(val as f64 * 1000.0 * 60.0),
|
("min", "ms") => Ok(val as f64 * 1000.0 * 60.0),
|
||||||
("min", "sec") => Ok(val as f64 * 60.0),
|
("min", "sec") => Ok(val as f64 * 60.0),
|
||||||
("min", "min") => Ok(val as f64),
|
("min", "min") => Ok(val as f64),
|
||||||
@ -259,6 +323,8 @@ fn convert_str_from_unit_to_unit(
|
|||||||
|
|
||||||
("hr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0),
|
("hr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0),
|
||||||
("hr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0),
|
("hr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0),
|
||||||
|
("hr", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0), // Micro sign
|
||||||
|
("hr", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0), // Greek small letter
|
||||||
("hr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0),
|
("hr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0),
|
||||||
("hr", "sec") => Ok(val as f64 * 60.0 * 60.0),
|
("hr", "sec") => Ok(val as f64 * 60.0 * 60.0),
|
||||||
("hr", "min") => Ok(val as f64 * 60.0),
|
("hr", "min") => Ok(val as f64 * 60.0),
|
||||||
@ -271,6 +337,8 @@ fn convert_str_from_unit_to_unit(
|
|||||||
|
|
||||||
("day", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0),
|
("day", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0),
|
||||||
("day", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0),
|
("day", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0),
|
||||||
|
("day", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0), // Micro sign
|
||||||
|
("day", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0), // Greek small letter
|
||||||
("day", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0),
|
("day", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0),
|
||||||
("day", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0),
|
("day", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0),
|
||||||
("day", "min") => Ok(val as f64 * 60.0 * 24.0),
|
("day", "min") => Ok(val as f64 * 60.0 * 24.0),
|
||||||
@ -283,6 +351,8 @@ fn convert_str_from_unit_to_unit(
|
|||||||
|
|
||||||
("wk", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
|
("wk", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
|
||||||
("wk", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
|
("wk", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
|
||||||
|
("wk", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0), // Micro sign
|
||||||
|
("wk", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0), // Greek small letter
|
||||||
("wk", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
|
("wk", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
|
||||||
("wk", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 7.0),
|
("wk", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 7.0),
|
||||||
("wk", "min") => Ok(val as f64 * 60.0 * 24.0 * 7.0),
|
("wk", "min") => Ok(val as f64 * 60.0 * 24.0 * 7.0),
|
||||||
@ -295,6 +365,8 @@ fn convert_str_from_unit_to_unit(
|
|||||||
|
|
||||||
("month", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
|
("month", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
|
||||||
("month", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
|
("month", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
|
||||||
|
("month", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0), // Micro sign
|
||||||
|
("month", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0), // Greek small letter
|
||||||
("month", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
|
("month", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
|
||||||
("month", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 30.0),
|
("month", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 30.0),
|
||||||
("month", "min") => Ok(val as f64 * 60.0 * 24.0 * 30.0),
|
("month", "min") => Ok(val as f64 * 60.0 * 24.0 * 30.0),
|
||||||
@ -307,6 +379,8 @@ fn convert_str_from_unit_to_unit(
|
|||||||
|
|
||||||
("yr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
|
("yr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
|
||||||
("yr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
|
("yr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
|
||||||
|
("yr", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0), // Micro sign
|
||||||
|
("yr", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0), // Greek small letter
|
||||||
("yr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
|
("yr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
|
||||||
("yr", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 365.0),
|
("yr", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 365.0),
|
||||||
("yr", "min") => Ok(val as f64 * 60.0 * 24.0 * 365.0),
|
("yr", "min") => Ok(val as f64 * 60.0 * 24.0 * 365.0),
|
||||||
@ -324,7 +398,7 @@ fn convert_str_from_unit_to_unit(
|
|||||||
dst_span: span,
|
dst_span: span,
|
||||||
src_span: value_span,
|
src_span: value_span,
|
||||||
help: Some(
|
help: Some(
|
||||||
"supported units are ns, us, ms, sec, min, hr, day, wk, month, yr and dec"
|
"supported units are ns, us/µs, ms, sec, min, hr, day, wk, month, yr, and dec"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
@ -357,7 +431,8 @@ fn string_to_duration(s: &str, span: Span, value_span: Span) -> Result<i64, Shel
|
|||||||
dst_span: span,
|
dst_span: span,
|
||||||
src_span: value_span,
|
src_span: value_span,
|
||||||
help: Some(
|
help: Some(
|
||||||
"supported units are ns, us, ms, sec, min, hr, day, wk, month, yr and dec".to_string(),
|
"supported units are ns, us/µs, ms, sec, min, hr, day, wk, month, yr, and dec"
|
||||||
|
.to_string(),
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -372,7 +447,7 @@ fn string_to_unit_duration(
|
|||||||
if let Expr::Int(x) = value.expr {
|
if let Expr::Int(x) = value.expr {
|
||||||
match unit.item {
|
match unit.item {
|
||||||
Unit::Nanosecond => return Ok(("ns", x)),
|
Unit::Nanosecond => return Ok(("ns", x)),
|
||||||
Unit::Microsecond => return Ok(("us", x)),
|
Unit::Microsecond => return Ok(("µs", x)),
|
||||||
Unit::Millisecond => return Ok(("ms", x)),
|
Unit::Millisecond => return Ok(("ms", x)),
|
||||||
Unit::Second => return Ok(("sec", x)),
|
Unit::Second => return Ok(("sec", x)),
|
||||||
Unit::Minute => return Ok(("min", x)),
|
Unit::Minute => return Ok(("min", x)),
|
||||||
@ -393,7 +468,8 @@ fn string_to_unit_duration(
|
|||||||
dst_span: span,
|
dst_span: span,
|
||||||
src_span: value_span,
|
src_span: value_span,
|
||||||
help: Some(
|
help: Some(
|
||||||
"supported units are ns, us, ms, sec, min, hr, day, wk, month, yr and dec".to_string(),
|
"supported units are ns, us/µs, ms, sec, min, hr, day, wk, month, yr, and dec"
|
||||||
|
.to_string(),
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -420,14 +496,19 @@ fn action(
|
|||||||
*value_span,
|
*value_span,
|
||||||
) {
|
) {
|
||||||
Ok(d) => {
|
Ok(d) => {
|
||||||
|
let unit = if &to_unit.item == "us" {
|
||||||
|
"µs"
|
||||||
|
} else {
|
||||||
|
&to_unit.item
|
||||||
|
};
|
||||||
if d.fract() == 0.0 {
|
if d.fract() == 0.0 {
|
||||||
Value::String {
|
Value::String {
|
||||||
val: format!("{} {}", d, &to_unit.item),
|
val: format!("{} {}", d, unit),
|
||||||
span: *value_span,
|
span: *value_span,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Value::String {
|
Value::String {
|
||||||
val: format!("{:.float_precision$} {}", d, &to_unit.item),
|
val: format!("{:.float_precision$} {}", d, unit),
|
||||||
span: *value_span,
|
span: *value_span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,14 +535,19 @@ fn action(
|
|||||||
*value_span,
|
*value_span,
|
||||||
) {
|
) {
|
||||||
Ok(d) => {
|
Ok(d) => {
|
||||||
|
let unit = if &to_unit.item == "us" {
|
||||||
|
"µs"
|
||||||
|
} else {
|
||||||
|
&to_unit.item
|
||||||
|
};
|
||||||
if d.fract() == 0.0 {
|
if d.fract() == 0.0 {
|
||||||
Value::String {
|
Value::String {
|
||||||
val: format!("{} {}", d, &to_unit.item),
|
val: format!("{} {}", d, unit),
|
||||||
span: *value_span,
|
span: *value_span,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Value::String {
|
Value::String {
|
||||||
val: format!("{:.float_precision$} {}", d, &to_unit.item),
|
val: format!("{:.float_precision$} {}", d, unit),
|
||||||
span: *value_span,
|
span: *value_span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -536,6 +622,34 @@ mod test {
|
|||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn turns_micro_sign_s_to_duration() {
|
||||||
|
let span = Span::new(0, 2);
|
||||||
|
let word = Value::test_string("4\u{00B5}s");
|
||||||
|
let expected = Value::Duration {
|
||||||
|
val: 4 * 1000,
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
let convert_duration = None;
|
||||||
|
|
||||||
|
let actual = action(&word, &convert_duration, 2, span);
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn turns_mu_s_to_duration() {
|
||||||
|
let span = Span::new(0, 2);
|
||||||
|
let word = Value::test_string("4\u{03BC}s");
|
||||||
|
let expected = Value::Duration {
|
||||||
|
val: 4 * 1000,
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
let convert_duration = None;
|
||||||
|
|
||||||
|
let actual = action(&word, &convert_duration, 2, span);
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn turns_ms_to_duration() {
|
fn turns_ms_to_duration() {
|
||||||
let span = Span::new(0, 2);
|
let span = Span::new(0, 2);
|
||||||
|
@ -270,6 +270,7 @@ fn nu_value_to_string(value: Value, separator: &str) -> String {
|
|||||||
Value::Binary { val, .. } => format!("{val:?}"),
|
Value::Binary { val, .. } => format!("{val:?}"),
|
||||||
Value::CellPath { val, .. } => val.into_string(),
|
Value::CellPath { val, .. } => val.into_string(),
|
||||||
Value::CustomValue { val, .. } => val.value_string(),
|
Value::CustomValue { val, .. } => val.value_string(),
|
||||||
|
Value::MatchPattern { val, .. } => format!("{:?}", val),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ impl Command for OpenDataFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Opens csv, json, arrow, or parquet file to create dataframe."
|
"Opens CSV, JSON, arrow, or parquet file to create dataframe."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
|
@ -19,7 +19,7 @@ impl Command for ToCSV {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Saves dataframe to csv file."
|
"Saves dataframe to CSV file."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
@ -40,12 +40,12 @@ impl Command for ToCSV {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Saves dataframe to csv file",
|
description: "Saves dataframe to CSV file",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr to-csv test.csv",
|
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr to-csv test.csv",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Saves dataframe to csv file using other delimiter",
|
description: "Saves dataframe to CSV file using other delimiter",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr to-csv test.csv -d '|'",
|
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr to-csv test.csv -d '|'",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
@ -76,7 +76,8 @@ impl Command for Debug {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Debug print a table",
|
description: "Debug print a table",
|
||||||
example: "[[version patch]; [0.1.0 false] [0.1.1 true] [0.2.0 false]] | debug",
|
example:
|
||||||
|
"[[version patch]; ['0.1.0' false] ['0.1.1' true] ['0.2.0' false]] | debug",
|
||||||
result: Some(Value::List {
|
result: Some(Value::List {
|
||||||
vals: vec![
|
vals: vec![
|
||||||
Value::test_string("{version: 0.1.0, patch: false}"),
|
Value::test_string("{version: 0.1.0, patch: false}"),
|
||||||
|
@ -328,5 +328,6 @@ pub fn debug_string_without_formatting(value: &Value) -> String {
|
|||||||
Value::Binary { val, .. } => format!("{val:?}"),
|
Value::Binary { val, .. } => format!("{val:?}"),
|
||||||
Value::CellPath { val, .. } => val.into_string(),
|
Value::CellPath { val, .. } => val.into_string(),
|
||||||
Value::CustomValue { val, .. } => val.value_string(),
|
Value::CustomValue { val, .. } => val.value_string(),
|
||||||
|
Value::MatchPattern { val, .. } => format!("{:?}", val),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,9 @@ use tabled::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
global_horizontal_char::SetHorizontalChar, peak2::Peak2, table_column_width::GetColumnWidths,
|
global_horizontal_char::SetHorizontalCharOnFirstRow, peak2::Peak2,
|
||||||
truncate_table::TruncateTable, width_increase::IncWidth,
|
table_column_width::get_first_cell_width, truncate_table::TruncateTable,
|
||||||
|
width_increase::IncWidth,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn build_table(value: Value, description: String, termsize: usize) -> String {
|
pub fn build_table(value: Value, description: String, termsize: usize) -> String {
|
||||||
@ -23,7 +24,8 @@ pub fn build_table(value: Value, description: String, termsize: usize) -> String
|
|||||||
let mut desc_table = Builder::from(desc).build();
|
let mut desc_table = Builder::from(desc).build();
|
||||||
let desc_table_width = desc_table.total_width();
|
let desc_table_width = desc_table.total_width();
|
||||||
|
|
||||||
let width = val_table_width.clamp(desc_table_width, termsize);
|
#[allow(clippy::manual_clamp)]
|
||||||
|
let width = val_table_width.max(desc_table_width).min(termsize);
|
||||||
|
|
||||||
desc_table
|
desc_table
|
||||||
.with(Style::rounded().off_bottom())
|
.with(Style::rounded().off_bottom())
|
||||||
@ -36,10 +38,11 @@ pub fn build_table(value: Value, description: String, termsize: usize) -> String
|
|||||||
.with(Wrap::new(width).priority::<PriorityMax>())
|
.with(Wrap::new(width).priority::<PriorityMax>())
|
||||||
.with(IncWidth(width));
|
.with(IncWidth(width));
|
||||||
|
|
||||||
let mut desc_widths = GetColumnWidths(Vec::new());
|
// we use only 1, cause left border considered 0 position
|
||||||
desc_table.with(&mut desc_widths);
|
let count_split_lines = 1;
|
||||||
|
let desc_width = get_first_cell_width(&mut desc_table) + count_split_lines;
|
||||||
|
|
||||||
val_table.with(SetHorizontalChar::new('┼', '┴', 0, desc_widths.0[0]));
|
val_table.with(SetHorizontalCharOnFirstRow::new('┼', '┴', desc_width));
|
||||||
|
|
||||||
format!("{desc_table}\n{val_table}")
|
format!("{desc_table}\n{val_table}")
|
||||||
}
|
}
|
||||||
@ -136,7 +139,7 @@ mod util {
|
|||||||
let mut columns = get_columns(&vals);
|
let mut columns = get_columns(&vals);
|
||||||
let data = convert_records_to_dataset(&columns, vals);
|
let data = convert_records_to_dataset(&columns, vals);
|
||||||
|
|
||||||
if columns.is_empty() && !data.is_empty() {
|
if columns.is_empty() {
|
||||||
columns = vec![String::from("")];
|
columns = vec![String::from("")];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,10 +211,11 @@ mod util {
|
|||||||
let path = PathMember::String {
|
let path = PathMember::String {
|
||||||
val: header.to_owned(),
|
val: header.to_owned(),
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
|
optional: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
item.clone()
|
item.clone()
|
||||||
.follow_cell_path(&[path], false, false)
|
.follow_cell_path(&[path], false)
|
||||||
.unwrap_or_else(|_| item.clone())
|
.unwrap_or_else(|_| item.clone())
|
||||||
}
|
}
|
||||||
item => item.clone(),
|
item => item.clone(),
|
||||||
@ -260,18 +264,30 @@ mod peak2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod table_column_width {
|
mod table_column_width {
|
||||||
use tabled::papergrid::{records::Records, Estimate};
|
use tabled::{
|
||||||
|
papergrid::{records::Records, width::CfgWidthFunction},
|
||||||
|
Table,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct GetColumnWidths(pub Vec<usize>);
|
pub fn get_first_cell_width<R: Records>(table: &mut Table<R>) -> usize {
|
||||||
|
let mut opt = GetFirstCellWidth(0);
|
||||||
|
table.with(&mut opt);
|
||||||
|
opt.0
|
||||||
|
}
|
||||||
|
|
||||||
impl<R> tabled::TableOption<R> for GetColumnWidths
|
struct GetFirstCellWidth(pub usize);
|
||||||
where
|
|
||||||
R: Records,
|
impl<R: Records> tabled::TableOption<R> for GetFirstCellWidth {
|
||||||
{
|
|
||||||
fn change(&mut self, table: &mut tabled::Table<R>) {
|
fn change(&mut self, table: &mut tabled::Table<R>) {
|
||||||
let mut evaluator = tabled::papergrid::width::WidthEstimator::default();
|
let w = table
|
||||||
evaluator.estimate(table.get_records(), table.get_config());
|
.get_records()
|
||||||
self.0 = evaluator.into();
|
.get_width((0, 0), CfgWidthFunction::default());
|
||||||
|
let pad = table
|
||||||
|
.get_config()
|
||||||
|
.get_padding(tabled::papergrid::Entity::Cell(0, 0));
|
||||||
|
let pad = pad.left.size + pad.right.size;
|
||||||
|
|
||||||
|
self.0 = w + pad;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -282,91 +298,65 @@ mod global_horizontal_char {
|
|||||||
Table, TableOption,
|
Table, TableOption,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SetHorizontalChar {
|
pub struct SetHorizontalCharOnFirstRow {
|
||||||
c1: char,
|
c1: char,
|
||||||
c2: char,
|
c2: char,
|
||||||
line: usize,
|
pos: usize,
|
||||||
position: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetHorizontalChar {
|
impl SetHorizontalCharOnFirstRow {
|
||||||
pub fn new(c1: char, c2: char, line: usize, position: usize) -> Self {
|
pub fn new(c1: char, c2: char, pos: usize) -> Self {
|
||||||
Self {
|
Self { c1, c2, pos }
|
||||||
c1,
|
|
||||||
c2,
|
|
||||||
line,
|
|
||||||
position,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> TableOption<R> for SetHorizontalChar
|
impl<R> TableOption<R> for SetHorizontalCharOnFirstRow
|
||||||
where
|
where
|
||||||
R: Records,
|
R: Records,
|
||||||
{
|
{
|
||||||
fn change(&mut self, table: &mut Table<R>) {
|
fn change(&mut self, table: &mut Table<R>) {
|
||||||
let shape = table.shape();
|
if table.is_empty() {
|
||||||
|
return;
|
||||||
let is_last_line = self.line == (shape.0 * 2);
|
|
||||||
let mut row = self.line;
|
|
||||||
if is_last_line {
|
|
||||||
row = self.line - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let shape = table.shape();
|
||||||
|
|
||||||
let mut evaluator = WidthEstimator::default();
|
let mut evaluator = WidthEstimator::default();
|
||||||
evaluator.estimate(table.get_records(), table.get_config());
|
evaluator.estimate(table.get_records(), table.get_config());
|
||||||
let widths: Vec<_> = evaluator.into();
|
let widths: Vec<_> = evaluator.into();
|
||||||
|
|
||||||
let mut i = 0;
|
let has_vertical = table.get_config().has_vertical(0, shape.1);
|
||||||
|
if has_vertical && self.pos == 0 {
|
||||||
|
let mut border = table.get_config().get_border((0, 0), shape);
|
||||||
|
border.left_top_corner = Some(self.c1);
|
||||||
|
table.get_config_mut().set_border((0, 0), border);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut i = 1;
|
||||||
#[allow(clippy::needless_range_loop)]
|
#[allow(clippy::needless_range_loop)]
|
||||||
for column in 0..shape.1 {
|
for (col, width) in widths.into_iter().enumerate() {
|
||||||
let has_vertical = table.get_config().has_vertical(column, shape.1);
|
if self.pos < i + width {
|
||||||
|
let o = self.pos - i;
|
||||||
|
table
|
||||||
|
.get_config_mut()
|
||||||
|
.override_horizontal_border((0, col), self.c2, Begin(o));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += width;
|
||||||
|
|
||||||
|
let has_vertical = table.get_config().has_vertical(col, shape.1);
|
||||||
if has_vertical {
|
if has_vertical {
|
||||||
if self.position == i {
|
if self.pos == i {
|
||||||
let mut border = table.get_config().get_border((row, column), shape);
|
let mut border = table.get_config().get_border((0, col), shape);
|
||||||
if is_last_line {
|
border.right_top_corner = Some(self.c1);
|
||||||
border.left_bottom_corner = Some(self.c1);
|
table.get_config_mut().set_border((0, col), border);
|
||||||
} else {
|
|
||||||
border.left_top_corner = Some(self.c1);
|
|
||||||
}
|
|
||||||
|
|
||||||
table.get_config_mut().set_border((row, column), border);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let width = widths[column];
|
|
||||||
|
|
||||||
if self.position < i + width {
|
|
||||||
let offset = self.position + 1 - i;
|
|
||||||
// let offset = width - offset;
|
|
||||||
|
|
||||||
table.get_config_mut().override_horizontal_border(
|
|
||||||
(self.line, column),
|
|
||||||
self.c2,
|
|
||||||
Begin(offset),
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
i += width;
|
|
||||||
}
|
|
||||||
|
|
||||||
let has_vertical = table.get_config().has_vertical(shape.1, shape.1);
|
|
||||||
if self.position == i && has_vertical {
|
|
||||||
let mut border = table.get_config().get_border((row, shape.1), shape);
|
|
||||||
if is_last_line {
|
|
||||||
border.left_bottom_corner = Some(self.c1);
|
|
||||||
} else {
|
|
||||||
border.left_top_corner = Some(self.c1);
|
|
||||||
}
|
|
||||||
|
|
||||||
table.get_config_mut().set_border((row, shape.1), border);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use nu_engine::{eval_block, CallExt};
|
use nu_engine::{eval_block, eval_expression_with_input};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Closure, Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type,
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
@ -16,15 +16,15 @@ impl Command for TimeIt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Time the running time of a closure."
|
"Time the running time of a block."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("timeit")
|
Signature::build("timeit")
|
||||||
.required(
|
.required(
|
||||||
"closure",
|
"command",
|
||||||
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
SyntaxShape::OneOf(vec![SyntaxShape::Block, SyntaxShape::Expression]),
|
||||||
"the closure to run",
|
"the command or block to run",
|
||||||
)
|
)
|
||||||
.input_output_types(vec![
|
.input_output_types(vec![
|
||||||
(Type::Any, Type::Duration),
|
(Type::Any, Type::Duration),
|
||||||
@ -45,37 +45,36 @@ impl Command for TimeIt {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let capture_block: Closure = call.req(engine_state, stack, 0)?;
|
let command_to_run = call.positional_nth(0);
|
||||||
let block = engine_state.get_block(capture_block.block_id);
|
|
||||||
|
|
||||||
let redirect_stdout = call.redirect_stdout;
|
|
||||||
let redirect_stderr = call.redirect_stderr;
|
|
||||||
|
|
||||||
let mut stack = stack.captures_to_stack(&capture_block.captures);
|
|
||||||
|
|
||||||
// In order to provide the pipeline as a positional, it must be converted into a value.
|
|
||||||
// But because pipelines do not have Clone, this one has to be cloned as a value
|
|
||||||
// and then converted back into a pipeline for eval_block().
|
|
||||||
// So, the metadata must be saved here and restored at that point.
|
|
||||||
let input_metadata = input.metadata();
|
|
||||||
let input_val = input.into_value(call.head);
|
|
||||||
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
|
||||||
if let Some(var_id) = &var.var_id {
|
|
||||||
stack.add_var(*var_id, input_val.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the start time after all other computation has been done.
|
// Get the start time after all other computation has been done.
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
eval_block(
|
|
||||||
engine_state,
|
if let Some(command_to_run) = command_to_run {
|
||||||
&mut stack,
|
if let Some(block_id) = command_to_run.as_block() {
|
||||||
block,
|
let block = engine_state.get_block(block_id);
|
||||||
input_val.into_pipeline_data_with_metadata(input_metadata),
|
eval_block(
|
||||||
redirect_stdout,
|
engine_state,
|
||||||
redirect_stderr,
|
stack,
|
||||||
)?
|
block,
|
||||||
|
input,
|
||||||
|
call.redirect_stdout,
|
||||||
|
call.redirect_stderr,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
eval_expression_with_input(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
command_to_run,
|
||||||
|
input,
|
||||||
|
call.redirect_stdout,
|
||||||
|
call.redirect_stderr,
|
||||||
|
)
|
||||||
|
.map(|res| res.0)?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PipelineData::empty()
|
||||||
|
}
|
||||||
.into_value(call.head);
|
.into_value(call.head);
|
||||||
|
|
||||||
let end_time = Instant::now();
|
let end_time = Instant::now();
|
||||||
@ -100,6 +99,11 @@ impl Command for TimeIt {
|
|||||||
example: "http get https://www.nushell.sh/book/ | timeit { split chars }",
|
example: "http get https://www.nushell.sh/book/ | timeit { split chars }",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Times a command invocation",
|
||||||
|
example: "timeit ls -la",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,11 +111,11 @@ impl Command for TimeIt {
|
|||||||
#[test]
|
#[test]
|
||||||
// Due to difficulty in observing side-effects from time closures,
|
// Due to difficulty in observing side-effects from time closures,
|
||||||
// checks that the closures have run correctly must use the filesystem.
|
// checks that the closures have run correctly must use the filesystem.
|
||||||
fn test_time_closure() {
|
fn test_time_block() {
|
||||||
use nu_test_support::{nu, nu_repl_code, playground::Playground};
|
use nu_test_support::{nu, nu_repl_code, playground::Playground};
|
||||||
Playground::setup("test_time_closure", |dirs, _| {
|
Playground::setup("test_time_block", |dirs, _| {
|
||||||
let inp = [
|
let inp = [
|
||||||
r#"[2 3 4] | timeit { to nuon | save foo.txt }"#,
|
r#"[2 3 4] | timeit {to nuon | save foo.txt }"#,
|
||||||
"open foo.txt",
|
"open foo.txt",
|
||||||
];
|
];
|
||||||
let actual_repl = nu!(cwd: dirs.test(), nu_repl_code(&inp));
|
let actual_repl = nu!(cwd: dirs.test(), nu_repl_code(&inp));
|
||||||
@ -121,11 +125,11 @@ fn test_time_closure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_time_closure_2() {
|
fn test_time_block_2() {
|
||||||
use nu_test_support::{nu, nu_repl_code, playground::Playground};
|
use nu_test_support::{nu, nu_repl_code, playground::Playground};
|
||||||
Playground::setup("test_time_closure", |dirs, _| {
|
Playground::setup("test_time_block", |dirs, _| {
|
||||||
let inp = [
|
let inp = [
|
||||||
r#"[2 3 4] | timeit {|e| {result: $e} | to nuon | save foo.txt }"#,
|
r#"[2 3 4] | timeit {{result: $in} | to nuon | save foo.txt }"#,
|
||||||
"open foo.txt",
|
"open foo.txt",
|
||||||
];
|
];
|
||||||
let actual_repl = nu!(cwd: dirs.test(), nu_repl_code(&inp));
|
let actual_repl = nu!(cwd: dirs.test(), nu_repl_code(&inp));
|
||||||
|
@ -36,7 +36,6 @@ pub fn create_default_context() -> EngineState {
|
|||||||
All,
|
All,
|
||||||
Any,
|
Any,
|
||||||
Append,
|
Append,
|
||||||
Collect,
|
|
||||||
Columns,
|
Columns,
|
||||||
Compact,
|
Compact,
|
||||||
Default,
|
Default,
|
||||||
@ -57,6 +56,7 @@ pub fn create_default_context() -> EngineState {
|
|||||||
GroupBy,
|
GroupBy,
|
||||||
Headers,
|
Headers,
|
||||||
Insert,
|
Insert,
|
||||||
|
Join,
|
||||||
SplitBy,
|
SplitBy,
|
||||||
Take,
|
Take,
|
||||||
Merge,
|
Merge,
|
||||||
@ -168,6 +168,8 @@ pub fn create_default_context() -> EngineState {
|
|||||||
Encode,
|
Encode,
|
||||||
DecodeBase64,
|
DecodeBase64,
|
||||||
EncodeBase64,
|
EncodeBase64,
|
||||||
|
DecodeHex,
|
||||||
|
EncodeHex,
|
||||||
DetectColumns,
|
DetectColumns,
|
||||||
Format,
|
Format,
|
||||||
FileSize,
|
FileSize,
|
||||||
@ -181,7 +183,6 @@ pub fn create_default_context() -> EngineState {
|
|||||||
Str,
|
Str,
|
||||||
StrCamelCase,
|
StrCamelCase,
|
||||||
StrCapitalize,
|
StrCapitalize,
|
||||||
StrCollect,
|
|
||||||
StrContains,
|
StrContains,
|
||||||
StrDistance,
|
StrDistance,
|
||||||
StrDowncase,
|
StrDowncase,
|
||||||
@ -386,6 +387,7 @@ pub fn create_default_context() -> EngineState {
|
|||||||
MathPi,
|
MathPi,
|
||||||
MathTau,
|
MathTau,
|
||||||
MathEuler,
|
MathEuler,
|
||||||
|
MathExp,
|
||||||
MathLn,
|
MathLn,
|
||||||
MathLog,
|
MathLog,
|
||||||
};
|
};
|
||||||
@ -440,17 +442,18 @@ pub fn create_default_context() -> EngineState {
|
|||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
bind_command! {
|
bind_command! {
|
||||||
|
ExportOldAlias,
|
||||||
HashBase64,
|
HashBase64,
|
||||||
LPadDeprecated,
|
LPadDeprecated,
|
||||||
RPadDeprecated,
|
|
||||||
Source,
|
|
||||||
StrDatetimeDeprecated,
|
|
||||||
StrDecimalDeprecated,
|
|
||||||
StrIntDeprecated,
|
|
||||||
StrFindReplaceDeprecated,
|
|
||||||
MathEvalDeprecated,
|
MathEvalDeprecated,
|
||||||
OldAlias,
|
OldAlias,
|
||||||
ExportOldAlias,
|
RPadDeprecated,
|
||||||
|
Source,
|
||||||
|
StrCollectDeprecated,
|
||||||
|
StrDatetimeDeprecated,
|
||||||
|
StrDecimalDeprecated,
|
||||||
|
StrFindReplaceDeprecated,
|
||||||
|
StrIntDeprecated,
|
||||||
};
|
};
|
||||||
|
|
||||||
working_set.render()
|
working_set.render()
|
||||||
@ -460,5 +463,9 @@ pub fn create_default_context() -> EngineState {
|
|||||||
eprintln!("Error creating default context: {err:?}");
|
eprintln!("Error creating default context: {err:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cache the table decl id so we don't have to look it up later
|
||||||
|
let table_decl_id = engine_state.find_decl("table".as_bytes(), &[]);
|
||||||
|
engine_state.table_decl_id = table_decl_id;
|
||||||
|
|
||||||
engine_state
|
engine_state
|
||||||
}
|
}
|
||||||
|
34
crates/nu-command/src/deprecated/collect.rs
Normal file
34
crates/nu-command/src/deprecated/collect.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{Category, PipelineData, ShellError, Signature};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct StrCollectDeprecated;
|
||||||
|
|
||||||
|
impl Command for StrCollectDeprecated {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"str collect"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name()).category(Category::Deprecated)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Deprecated command."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_: &EngineState,
|
||||||
|
_: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
Err(nu_protocol::ShellError::DeprecatedCommand(
|
||||||
|
self.name().to_string(),
|
||||||
|
"str join".to_owned(),
|
||||||
|
call.head,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
@ -23,5 +23,6 @@ pub fn deprecated_commands() -> HashMap<String, String> {
|
|||||||
("str lpad".to_string(), "fill".to_string()),
|
("str lpad".to_string(), "fill".to_string()),
|
||||||
("str rpad".to_string(), "fill".to_string()),
|
("str rpad".to_string(), "fill".to_string()),
|
||||||
("benchmark".to_string(), "timeit".to_string()),
|
("benchmark".to_string(), "timeit".to_string()),
|
||||||
|
("str collect".to_string(), "str join".to_string()),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
mod collect;
|
||||||
mod deprecated_commands;
|
mod deprecated_commands;
|
||||||
mod export_old_alias;
|
mod export_old_alias;
|
||||||
mod hash_base64;
|
mod hash_base64;
|
||||||
@ -11,6 +12,7 @@ mod str_decimal;
|
|||||||
mod str_find_replace;
|
mod str_find_replace;
|
||||||
mod str_int;
|
mod str_int;
|
||||||
|
|
||||||
|
pub use collect::StrCollectDeprecated;
|
||||||
pub use deprecated_commands::*;
|
pub use deprecated_commands::*;
|
||||||
pub use export_old_alias::ExportOldAlias;
|
pub use export_old_alias::ExportOldAlias;
|
||||||
pub use hash_base64::HashBase64;
|
pub use hash_base64::HashBase64;
|
||||||
|
5
crates/nu-command/src/env/config/utils.rs
vendored
5
crates/nu-command/src/env/config/utils.rs
vendored
@ -35,10 +35,7 @@ pub(crate) fn get_editor(
|
|||||||
if let Some((a, b)) = editor.split_once(' ') {
|
if let Some((a, b)) = editor.split_once(' ') {
|
||||||
Ok((
|
Ok((
|
||||||
a.to_string(),
|
a.to_string(),
|
||||||
b.split(' ')
|
b.split(' ').map(|s| s.to_string()).collect::<Vec<String>>(),
|
||||||
.into_iter()
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
.collect::<Vec<String>>(),
|
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok((editor, Vec::new()))
|
Ok((editor, Vec::new()))
|
||||||
|
2
crates/nu-command/src/env/let_env.rs
vendored
2
crates/nu-command/src/env/let_env.rs
vendored
@ -24,7 +24,7 @@ impl Command for LetEnv {
|
|||||||
.required("var_name", SyntaxShape::String, "variable name")
|
.required("var_name", SyntaxShape::String, "variable name")
|
||||||
.required(
|
.required(
|
||||||
"initial_value",
|
"initial_value",
|
||||||
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
|
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::MathExpression)),
|
||||||
"equals sign followed by value",
|
"equals sign followed by value",
|
||||||
)
|
)
|
||||||
.category(Category::Env)
|
.category(Category::Env)
|
||||||
|
@ -129,7 +129,7 @@ impl Command for Ls {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
if is_empty_dir(&expanded) {
|
if is_empty_dir(&expanded) {
|
||||||
return Ok(Value::nothing(call_span).into_pipeline_data());
|
return Ok(Value::list(vec![], call_span).into_pipeline_data());
|
||||||
}
|
}
|
||||||
p.push("*");
|
p.push("*");
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ impl Command for Ls {
|
|||||||
if directory {
|
if directory {
|
||||||
(PathBuf::from("."), call_span, false)
|
(PathBuf::from("."), call_span, false)
|
||||||
} else if is_empty_dir(current_dir(engine_state, stack)?) {
|
} else if is_empty_dir(current_dir(engine_state, stack)?) {
|
||||||
return Ok(Value::nothing(call_span).into_pipeline_data());
|
return Ok(Value::list(vec![], call_span).into_pipeline_data());
|
||||||
} else {
|
} else {
|
||||||
(PathBuf::from("./*"), call_span, false)
|
(PathBuf::from("./*"), call_span, false)
|
||||||
}
|
}
|
||||||
|
@ -87,8 +87,8 @@ impl Command for Mv {
|
|||||||
|
|
||||||
if sources.is_empty() {
|
if sources.is_empty() {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"Invalid file or pattern".into(),
|
"File(s) not found".into(),
|
||||||
"invalid file or pattern".into(),
|
"could not find any files matching this glob pattern".into(),
|
||||||
Some(spanned_source.span),
|
Some(spanned_source.span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use nu_engine::{eval_block, CallExt};
|
use nu_engine::{current_dir, eval_block, CallExt};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::util::BufferedReader;
|
use nu_protocol::util::BufferedReader;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, RawStream, ShellError, Signature, Spanned, SyntaxShape, Type,
|
Category, Example, IntoInterruptiblePipelineData, PipelineData, RawStream, ShellError,
|
||||||
Value,
|
Signature, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
|
|
||||||
@ -38,6 +38,11 @@ impl Command for Open {
|
|||||||
Signature::build("open")
|
Signature::build("open")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Any), (Type::String, Type::Any)])
|
.input_output_types(vec![(Type::Nothing, Type::Any), (Type::String, Type::Any)])
|
||||||
.optional("filename", SyntaxShape::Filepath, "the filename to use")
|
.optional("filename", SyntaxShape::Filepath, "the filename to use")
|
||||||
|
.rest(
|
||||||
|
"filenames",
|
||||||
|
SyntaxShape::Filepath,
|
||||||
|
"optional additional files to open",
|
||||||
|
)
|
||||||
.switch("raw", "open file as raw binary", Some('r'))
|
.switch("raw", "open file as raw binary", Some('r'))
|
||||||
.category(Category::FileSystem)
|
.category(Category::FileSystem)
|
||||||
}
|
}
|
||||||
@ -52,24 +57,16 @@ impl Command for Open {
|
|||||||
let raw = call.has_flag("raw");
|
let raw = call.has_flag("raw");
|
||||||
let call_span = call.head;
|
let call_span = call.head;
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let path = call.opt::<Spanned<String>>(engine_state, stack, 0)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
|
let req_path = call.opt::<Spanned<String>>(engine_state, stack, 0)?;
|
||||||
|
let mut path_params = call.rest::<Spanned<String>>(engine_state, stack, 1)?;
|
||||||
|
|
||||||
let path = {
|
// FIXME: JT: what is this doing here?
|
||||||
if let Some(path_val) = path {
|
|
||||||
Some(Spanned {
|
|
||||||
item: nu_utils::strip_ansi_string_unlikely(path_val.item),
|
|
||||||
span: path_val.span,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
path
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let path = if let Some(path) = path {
|
if let Some(filename) = req_path {
|
||||||
path
|
path_params.insert(0, filename);
|
||||||
} else {
|
} else {
|
||||||
// Collect a filename from the input
|
let filename = match input {
|
||||||
match input {
|
|
||||||
PipelineData::Value(Value::Nothing { .. }, ..) => {
|
PipelineData::Value(Value::Nothing { .. }, ..) => {
|
||||||
return Err(ShellError::MissingParameter {
|
return Err(ShellError::MissingParameter {
|
||||||
param_name: "needs filename".to_string(),
|
param_name: "needs filename".to_string(),
|
||||||
@ -83,104 +80,143 @@ impl Command for Open {
|
|||||||
span: call.head,
|
span: call.head,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
let arg_span = path.span;
|
|
||||||
let path_no_whitespace = &path.item.trim_end_matches(|x| matches!(x, '\x09'..='\x0d'));
|
|
||||||
let path = Path::new(path_no_whitespace);
|
|
||||||
|
|
||||||
if permission_denied(path) {
|
|
||||||
#[cfg(unix)]
|
|
||||||
let error_msg = match path.metadata() {
|
|
||||||
Ok(md) => format!(
|
|
||||||
"The permissions of {:o} does not allow access for this user",
|
|
||||||
md.permissions().mode() & 0o0777
|
|
||||||
),
|
|
||||||
Err(e) => e.to_string(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
path_params.insert(0, filename);
|
||||||
let error_msg = String::from("Permission denied");
|
}
|
||||||
Err(ShellError::GenericError(
|
|
||||||
"Permission denied".into(),
|
|
||||||
error_msg,
|
|
||||||
Some(arg_span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
if !raw {
|
|
||||||
let res = SQLiteDatabase::try_from_path(path, arg_span, ctrlc.clone())
|
|
||||||
.map(|db| db.into_value(call.head).into_pipeline_data());
|
|
||||||
|
|
||||||
if res.is_ok() {
|
let mut output = vec![];
|
||||||
return res;
|
|
||||||
|
for path in path_params.into_iter() {
|
||||||
|
//FIXME: `open` should not have to do this
|
||||||
|
let path = {
|
||||||
|
Spanned {
|
||||||
|
item: nu_utils::strip_ansi_string_unlikely(path.item),
|
||||||
|
span: path.span,
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
let file = match std::fs::File::open(path) {
|
let arg_span = path.span;
|
||||||
Ok(file) => file,
|
// let path_no_whitespace = &path.item.trim_end_matches(|x| matches!(x, '\x09'..='\x0d'));
|
||||||
Err(err) => {
|
|
||||||
|
for path in nu_engine::glob_from(&path, &cwd, call_span, None)?.1 {
|
||||||
|
let path = path?;
|
||||||
|
let path = Path::new(&path);
|
||||||
|
|
||||||
|
if permission_denied(path) {
|
||||||
|
#[cfg(unix)]
|
||||||
|
let error_msg = match path.metadata() {
|
||||||
|
Ok(md) => format!(
|
||||||
|
"The permissions of {:o} does not allow access for this user",
|
||||||
|
md.permissions().mode() & 0o0777
|
||||||
|
),
|
||||||
|
Err(e) => e.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
let error_msg = String::from("Permission denied");
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"Permission denied".into(),
|
"Permission denied".into(),
|
||||||
err.to_string(),
|
error_msg,
|
||||||
Some(arg_span),
|
Some(arg_span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
));
|
));
|
||||||
}
|
} else {
|
||||||
};
|
#[cfg(feature = "sqlite")]
|
||||||
|
if !raw {
|
||||||
|
let res = SQLiteDatabase::try_from_path(path, arg_span, ctrlc.clone())
|
||||||
|
.map(|db| db.into_value(call.head).into_pipeline_data());
|
||||||
|
|
||||||
let buf_reader = BufReader::new(file);
|
if res.is_ok() {
|
||||||
|
return res;
|
||||||
let output = PipelineData::ExternalStream {
|
|
||||||
stdout: Some(RawStream::new(
|
|
||||||
Box::new(BufferedReader { input: buf_reader }),
|
|
||||||
ctrlc,
|
|
||||||
call_span,
|
|
||||||
None,
|
|
||||||
)),
|
|
||||||
stderr: None,
|
|
||||||
exit_code: None,
|
|
||||||
span: call_span,
|
|
||||||
metadata: None,
|
|
||||||
trim_end_newline: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let ext = if raw {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
path.extension()
|
|
||||||
.map(|name| name.to_string_lossy().to_string())
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(ext) = ext {
|
|
||||||
match engine_state.find_decl(format!("from {ext}").as_bytes(), &[]) {
|
|
||||||
Some(converter_id) => {
|
|
||||||
let decl = engine_state.get_decl(converter_id);
|
|
||||||
if let Some(block_id) = decl.get_block_id() {
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
eval_block(engine_state, stack, block, output, false, false)
|
|
||||||
} else {
|
|
||||||
decl.run(engine_state, stack, &Call::new(call_span), output)
|
|
||||||
}
|
}
|
||||||
.map_err(|inner| {
|
|
||||||
ShellError::GenericError(
|
|
||||||
format!("Error while parsing as {ext}"),
|
|
||||||
format!("Could not parse '{}' with `from {}`", path.display(), ext),
|
|
||||||
Some(arg_span),
|
|
||||||
Some(format!("Check out `help from {}` or `help from` for more options or open raw data with `open --raw '{}'`", ext, path.display())),
|
|
||||||
vec![inner],
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
None => Ok(output),
|
|
||||||
|
let file = match std::fs::File::open(path) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(ShellError::GenericError(
|
||||||
|
"Permission denied".into(),
|
||||||
|
err.to_string(),
|
||||||
|
Some(arg_span),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let buf_reader = BufReader::new(file);
|
||||||
|
|
||||||
|
let file_contents = PipelineData::ExternalStream {
|
||||||
|
stdout: Some(RawStream::new(
|
||||||
|
Box::new(BufferedReader { input: buf_reader }),
|
||||||
|
ctrlc.clone(),
|
||||||
|
call_span,
|
||||||
|
None,
|
||||||
|
)),
|
||||||
|
stderr: None,
|
||||||
|
exit_code: None,
|
||||||
|
span: call_span,
|
||||||
|
metadata: None,
|
||||||
|
trim_end_newline: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let ext = if raw {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
path.extension()
|
||||||
|
.map(|name| name.to_string_lossy().to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(ext) = ext {
|
||||||
|
match engine_state.find_decl(format!("from {ext}").as_bytes(), &[]) {
|
||||||
|
Some(converter_id) => {
|
||||||
|
let decl = engine_state.get_decl(converter_id);
|
||||||
|
let command_output = if let Some(block_id) = decl.get_block_id() {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
eval_block(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
block,
|
||||||
|
file_contents,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
decl.run(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
&Call::new(call_span),
|
||||||
|
file_contents,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
output.push(command_output.map_err(|inner| {
|
||||||
|
ShellError::GenericError(
|
||||||
|
format!("Error while parsing as {ext}"),
|
||||||
|
format!("Could not parse '{}' with `from {}`", path.display(), ext),
|
||||||
|
Some(arg_span),
|
||||||
|
Some(format!("Check out `help from {}` or `help from` for more options or open raw data with `open --raw '{}'`", ext, path.display())),
|
||||||
|
vec![inner],
|
||||||
|
)
|
||||||
|
})?);
|
||||||
|
}
|
||||||
|
None => output.push(file_contents),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output.push(file_contents)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Ok(output)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if output.is_empty() {
|
||||||
|
Ok(PipelineData::Empty)
|
||||||
|
} else if output.len() == 1 {
|
||||||
|
Ok(output.remove(0))
|
||||||
|
} else {
|
||||||
|
Ok(output.into_iter().flatten().into_pipeline_data(ctrlc))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<nu_protocol::Example> {
|
fn examples(&self) -> Vec<nu_protocol::Example> {
|
||||||
|
@ -16,7 +16,7 @@ impl Command for Start {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Open a folder,file or website in the default application or viewer."
|
"Open a folder, file or website in the default application or viewer."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use nu_engine::{eval_block, CallExt};
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Closure, Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type,
|
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||||
Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::utils;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct All;
|
pub struct All;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ impl Command for All {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Check that each item is a string",
|
description: "Check that each item is a string",
|
||||||
example: "[foo bar 2 baz] | all { ($in | describe) == 'string' }",
|
example: "[foo bar 2 baz] | all {|| ($in | describe) == 'string' }",
|
||||||
result: Some(Value::test_bool(false)),
|
result: Some(Value::test_bool(false)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
@ -60,9 +60,7 @@ impl Command for All {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
// This is almost entirely a copy-paste of `any`'s run(), so make sure any changes to this are
|
|
||||||
// reflected in the other!! (Or, you could figure out a way for both of them to use
|
|
||||||
// the same function...)
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -70,51 +68,7 @@ impl Command for All {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let span = call.head;
|
utils::boolean_fold(engine_state, stack, call, input, false)
|
||||||
|
|
||||||
let capture_block: Closure = call.req(engine_state, stack, 0)?;
|
|
||||||
let block_id = capture_block.block_id;
|
|
||||||
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id);
|
|
||||||
let mut stack = stack.captures_to_stack(&capture_block.captures);
|
|
||||||
|
|
||||||
let orig_env_vars = stack.env_vars.clone();
|
|
||||||
let orig_env_hidden = stack.env_hidden.clone();
|
|
||||||
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
|
||||||
let engine_state = engine_state.clone();
|
|
||||||
|
|
||||||
for value in input.into_interruptible_iter(ctrlc) {
|
|
||||||
// with_env() is used here to ensure that each iteration uses
|
|
||||||
// a different set of environment variables.
|
|
||||||
// Hence, a 'cd' in the first loop won't affect the next loop.
|
|
||||||
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
|
||||||
|
|
||||||
if let Some(var_id) = var_id {
|
|
||||||
stack.add_var(var_id, value.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let eval = eval_block(
|
|
||||||
&engine_state,
|
|
||||||
&mut stack,
|
|
||||||
block,
|
|
||||||
value.into_pipeline_data(),
|
|
||||||
call.redirect_stdout,
|
|
||||||
call.redirect_stderr,
|
|
||||||
);
|
|
||||||
match eval {
|
|
||||||
Err(e) => {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
Ok(pipeline_data) => {
|
|
||||||
if !pipeline_data.into_value(span).is_true() {
|
|
||||||
return Ok(Value::Bool { val: false, span }.into_pipeline_data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Value::Bool { val: true, span }.into_pipeline_data())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use nu_engine::{eval_block, CallExt};
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Closure, Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type,
|
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||||
Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::utils;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Any;
|
pub struct Any;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ impl Command for Any {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Check that any item is a string",
|
description: "Check that any item is a string",
|
||||||
example: "[1 2 3 4] | any { ($in | describe) == 'string' }",
|
example: "[1 2 3 4] | any {|| ($in | describe) == 'string' }",
|
||||||
result: Some(Value::test_bool(false)),
|
result: Some(Value::test_bool(false)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
@ -60,9 +60,7 @@ impl Command for Any {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
// This is almost entirely a copy-paste of `all`'s run(), so make sure any changes to this are
|
|
||||||
// reflected in the other!! Or, you could figure out a way for both of them to use
|
|
||||||
// the same function...
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -70,51 +68,7 @@ impl Command for Any {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let span = call.head;
|
utils::boolean_fold(engine_state, stack, call, input, true)
|
||||||
|
|
||||||
let capture_block: Closure = call.req(engine_state, stack, 0)?;
|
|
||||||
let block_id = capture_block.block_id;
|
|
||||||
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id);
|
|
||||||
let mut stack = stack.captures_to_stack(&capture_block.captures);
|
|
||||||
|
|
||||||
let orig_env_vars = stack.env_vars.clone();
|
|
||||||
let orig_env_hidden = stack.env_hidden.clone();
|
|
||||||
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
|
||||||
let engine_state = engine_state.clone();
|
|
||||||
|
|
||||||
for value in input.into_interruptible_iter(ctrlc) {
|
|
||||||
// with_env() is used here to ensure that each iteration uses
|
|
||||||
// a different set of environment variables.
|
|
||||||
// Hence, a 'cd' in the first loop won't affect the next loop.
|
|
||||||
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
|
||||||
|
|
||||||
if let Some(var_id) = var_id {
|
|
||||||
stack.add_var(var_id, value.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let eval = eval_block(
|
|
||||||
&engine_state,
|
|
||||||
&mut stack,
|
|
||||||
block,
|
|
||||||
value.into_pipeline_data(),
|
|
||||||
call.redirect_stdout,
|
|
||||||
call.redirect_stderr,
|
|
||||||
);
|
|
||||||
match eval {
|
|
||||||
Err(e) => {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
Ok(pipeline_data) => {
|
|
||||||
if pipeline_data.into_value(span).is_true() {
|
|
||||||
return Ok(Value::Bool { val: true, span }.into_pipeline_data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Value::Bool { val: false, span }.into_pipeline_data())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,10 +109,7 @@ fn dropcol(
|
|||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
|
||||||
for path in &keep_columns {
|
for path in &keep_columns {
|
||||||
let fetcher =
|
let fetcher = input_val.clone().follow_cell_path(&path.members, false)?;
|
||||||
input_val
|
|
||||||
.clone()
|
|
||||||
.follow_cell_path(&path.members, false, false)?;
|
|
||||||
cols.push(path.into_string());
|
cols.push(path.into_string());
|
||||||
vals.push(fetcher);
|
vals.push(fetcher);
|
||||||
}
|
}
|
||||||
@ -136,10 +133,7 @@ fn dropcol(
|
|||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
|
||||||
for path in &keep_columns {
|
for path in &keep_columns {
|
||||||
let fetcher =
|
let fetcher = input_val.clone().follow_cell_path(&path.members, false)?;
|
||||||
input_val
|
|
||||||
.clone()
|
|
||||||
.follow_cell_path(&path.members, false, false)?;
|
|
||||||
cols.push(path.into_string());
|
cols.push(path.into_string());
|
||||||
vals.push(fetcher);
|
vals.push(fetcher);
|
||||||
}
|
}
|
||||||
@ -155,9 +149,7 @@ fn dropcol(
|
|||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
|
||||||
for cell_path in &keep_columns {
|
for cell_path in &keep_columns {
|
||||||
let result = v
|
let result = v.clone().follow_cell_path(&cell_path.members, false)?;
|
||||||
.clone()
|
|
||||||
.follow_cell_path(&cell_path.members, false, false)?;
|
|
||||||
|
|
||||||
cols.push(cell_path.into_string());
|
cols.push(cell_path.into_string());
|
||||||
vals.push(result);
|
vals.push(result);
|
||||||
|
@ -74,7 +74,7 @@ with 'transpose' first."#
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
example: "{major:2, minor:1, patch:4} | values | each { into string }",
|
example: "{major:2, minor:1, patch:4} | values | each {|| into string }",
|
||||||
description: "Produce a list of values in the record, converted to string",
|
description: "Produce a list of values in the record, converted to string",
|
||||||
result: Some(Value::List {
|
result: Some(Value::List {
|
||||||
vals: vec![
|
vals: vec![
|
||||||
|
@ -74,7 +74,7 @@ fn empty(
|
|||||||
for val in input {
|
for val in input {
|
||||||
for column in &columns {
|
for column in &columns {
|
||||||
let val = val.clone();
|
let val = val.clone();
|
||||||
match val.follow_cell_path(&column.members, false, false) {
|
match val.follow_cell_path(&column.members, false) {
|
||||||
Ok(Value::Nothing { .. }) => {}
|
Ok(Value::Nothing { .. }) => {}
|
||||||
Ok(_) => return Ok(Value::boolean(false, head).into_pipeline_data()),
|
Ok(_) => return Ok(Value::boolean(false, head).into_pipeline_data()),
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
|
@ -119,7 +119,7 @@ impl Command for Find {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Find value in records",
|
description: "Find value in records",
|
||||||
example: r#"[[version name]; [0.1.0 nushell] [0.1.1 fish] [0.2.0 zsh]] | find -r "nu""#,
|
example: r#"[[version name]; ['0.1.0' nushell] ['0.1.1' fish] ['0.2.0' zsh]] | find -r "nu""#,
|
||||||
result: Some(Value::List {
|
result: Some(Value::List {
|
||||||
vals: vec![Value::test_record(
|
vals: vec![Value::test_record(
|
||||||
vec!["version", "name"],
|
vec!["version", "name"],
|
||||||
@ -404,6 +404,7 @@ fn find_with_rest_and_highlight(
|
|||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
},
|
},
|
||||||
Value::Binary { .. } => false,
|
Value::Binary { .. } => false,
|
||||||
|
Value::MatchPattern { .. } => false,
|
||||||
}) != invert
|
}) != invert
|
||||||
},
|
},
|
||||||
ctrlc,
|
ctrlc,
|
||||||
@ -484,6 +485,7 @@ fn find_with_rest_and_highlight(
|
|||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
},
|
},
|
||||||
Value::Binary { .. } => false,
|
Value::Binary { .. } => false,
|
||||||
|
Value::MatchPattern { .. } => false,
|
||||||
}) != invert
|
}) != invert
|
||||||
}),
|
}),
|
||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
|
@ -279,7 +279,7 @@ fn flat_value(columns: &[CellPath], item: &Value, _name_tag: Span, all: bool) ->
|
|||||||
if !columns.is_empty() {
|
if !columns.is_empty() {
|
||||||
let cell_path =
|
let cell_path =
|
||||||
column_requested.and_then(|x| match x.members.first() {
|
column_requested.and_then(|x| match x.members.first() {
|
||||||
Some(PathMember::String { val, span: _ }) => Some(val),
|
Some(PathMember::String { val, span: _, .. }) => Some(val),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ If multiple cell paths are given, this will produce a list of values."#
|
|||||||
.rest("rest", SyntaxShape::CellPath, "additional cell paths")
|
.rest("rest", SyntaxShape::CellPath, "additional cell paths")
|
||||||
.switch(
|
.switch(
|
||||||
"ignore-errors",
|
"ignore-errors",
|
||||||
"when there are empty cells, instead of erroring out, replace them with nothing",
|
"ignore missing data (make all cell path members optional)",
|
||||||
Some('i'),
|
Some('i'),
|
||||||
)
|
)
|
||||||
.switch(
|
.switch(
|
||||||
@ -62,16 +62,20 @@ If multiple cell paths are given, this will produce a list of values."#
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
let cell_path: CellPath = call.req(engine_state, stack, 0)?;
|
let mut cell_path: CellPath = call.req(engine_state, stack, 0)?;
|
||||||
let rest: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let rest: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let sensitive = call.has_flag("sensitive");
|
|
||||||
let ignore_errors = call.has_flag("ignore-errors");
|
let ignore_errors = call.has_flag("ignore-errors");
|
||||||
|
let sensitive = call.has_flag("sensitive");
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let metadata = input.metadata();
|
let metadata = input.metadata();
|
||||||
|
|
||||||
|
if ignore_errors {
|
||||||
|
cell_path.make_optional();
|
||||||
|
}
|
||||||
|
|
||||||
if rest.is_empty() {
|
if rest.is_empty() {
|
||||||
input
|
input
|
||||||
.follow_cell_path(&cell_path.members, call.head, !sensitive, ignore_errors)
|
.follow_cell_path(&cell_path.members, call.head, !sensitive)
|
||||||
.map(|x| x.into_pipeline_data())
|
.map(|x| x.into_pipeline_data())
|
||||||
} else {
|
} else {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
@ -81,9 +85,7 @@ If multiple cell paths are given, this will produce a list of values."#
|
|||||||
let input = input.into_value(span);
|
let input = input.into_value(span);
|
||||||
|
|
||||||
for path in paths {
|
for path in paths {
|
||||||
let val = input
|
let val = input.clone().follow_cell_path(&path.members, !sensitive);
|
||||||
.clone()
|
|
||||||
.follow_cell_path(&path.members, !sensitive, false);
|
|
||||||
|
|
||||||
output.push(val?);
|
output.push(val?);
|
||||||
}
|
}
|
||||||
|
422
crates/nu-command/src/filters/join.rs
Normal file
422
crates/nu-command/src/filters/join.rs
Normal file
@ -0,0 +1,422 @@
|
|||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{
|
||||||
|
Config, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
|
};
|
||||||
|
use std::cmp::max;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Join;
|
||||||
|
|
||||||
|
enum JoinType {
|
||||||
|
Inner,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Outer,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum IncludeInner {
|
||||||
|
No,
|
||||||
|
Yes,
|
||||||
|
}
|
||||||
|
|
||||||
|
type RowEntries<'a> = Vec<(&'a Vec<String>, &'a Vec<Value>)>;
|
||||||
|
|
||||||
|
const EMPTY_COL_NAMES: &Vec<String> = &vec![];
|
||||||
|
|
||||||
|
impl Command for Join {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"join"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("join")
|
||||||
|
.required(
|
||||||
|
"right-table",
|
||||||
|
SyntaxShape::List(Box::new(SyntaxShape::Any)),
|
||||||
|
"The right table in the join",
|
||||||
|
)
|
||||||
|
.required(
|
||||||
|
"left-on",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"Name of column in input (left) table to join on",
|
||||||
|
)
|
||||||
|
.optional(
|
||||||
|
"right-on",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"Name of column in right table to join on. Defaults to same column as left table.",
|
||||||
|
)
|
||||||
|
.switch("inner", "Inner join (default)", Some('i'))
|
||||||
|
.switch("left", "Left-outer join", Some('l'))
|
||||||
|
.switch("right", "Right-outer join", Some('r'))
|
||||||
|
.switch("outer", "Outer join", Some('o'))
|
||||||
|
.input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Join two tables"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["sql"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
let table_2: Value = call.req(engine_state, stack, 0)?;
|
||||||
|
let l_on: Value = call.req(engine_state, stack, 1)?;
|
||||||
|
let r_on: Value = call
|
||||||
|
.opt(engine_state, stack, 2)?
|
||||||
|
.unwrap_or_else(|| l_on.clone());
|
||||||
|
let span = call.head;
|
||||||
|
let join_type = join_type(call)?;
|
||||||
|
|
||||||
|
// FIXME: we should handle ListStreams properly instead of collecting
|
||||||
|
let collected_input = input.into_value(span);
|
||||||
|
|
||||||
|
match (&collected_input, &table_2, &l_on, &r_on) {
|
||||||
|
(
|
||||||
|
Value::List { vals: rows_1, .. },
|
||||||
|
Value::List { vals: rows_2, .. },
|
||||||
|
Value::String { val: l_on, .. },
|
||||||
|
Value::String { val: r_on, .. },
|
||||||
|
) => {
|
||||||
|
let result = join(rows_1, rows_2, l_on, r_on, join_type, span);
|
||||||
|
Ok(PipelineData::Value(result, None))
|
||||||
|
}
|
||||||
|
_ => Err(ShellError::UnsupportedInput(
|
||||||
|
"(PipelineData<table>, table, string, string)".into(),
|
||||||
|
format!(
|
||||||
|
"({:?}, {:?}, {:?} {:?})",
|
||||||
|
collected_input,
|
||||||
|
table_2.get_type(),
|
||||||
|
l_on.get_type(),
|
||||||
|
r_on.get_type(),
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
span,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Join two tables",
|
||||||
|
example: "[{a: 1 b: 2}] | join [{a: 1 c: 3}] a",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![Value::Record {
|
||||||
|
cols: vec!["a".into(), "b".into(), "c".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int {
|
||||||
|
val: 1,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::Int {
|
||||||
|
val: 2,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::Int {
|
||||||
|
val: 3,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn join_type(call: &Call) -> Result<JoinType, nu_protocol::ShellError> {
|
||||||
|
match (
|
||||||
|
call.has_flag("inner"),
|
||||||
|
call.has_flag("left"),
|
||||||
|
call.has_flag("right"),
|
||||||
|
call.has_flag("outer"),
|
||||||
|
) {
|
||||||
|
(_, false, false, false) => Ok(JoinType::Inner),
|
||||||
|
(false, true, false, false) => Ok(JoinType::Left),
|
||||||
|
(false, false, true, false) => Ok(JoinType::Right),
|
||||||
|
(false, false, false, true) => Ok(JoinType::Outer),
|
||||||
|
_ => Err(ShellError::UnsupportedInput(
|
||||||
|
"Choose one of: --inner, --left, --right, --outer".into(),
|
||||||
|
"".into(),
|
||||||
|
call.head,
|
||||||
|
call.head,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn join(
|
||||||
|
left: &Vec<Value>,
|
||||||
|
right: &Vec<Value>,
|
||||||
|
left_join_key: &str,
|
||||||
|
right_join_key: &str,
|
||||||
|
join_type: JoinType,
|
||||||
|
span: Span,
|
||||||
|
) -> Value {
|
||||||
|
// Inner / Right Join
|
||||||
|
// ------------------
|
||||||
|
// Make look-up table from rows on left
|
||||||
|
// For each row r on right:
|
||||||
|
// If any matching rows on left:
|
||||||
|
// For each matching row l on left:
|
||||||
|
// Emit (l, r)
|
||||||
|
// Else if RightJoin:
|
||||||
|
// Emit (null, r)
|
||||||
|
|
||||||
|
// Left Join
|
||||||
|
// ----------
|
||||||
|
// Make look-up table from rows on right
|
||||||
|
// For each row l on left:
|
||||||
|
// If any matching rows on right:
|
||||||
|
// For each matching row r on right:
|
||||||
|
// Emit (l, r)
|
||||||
|
// Else:
|
||||||
|
// Emit (l, null)
|
||||||
|
|
||||||
|
// Outer Join
|
||||||
|
// ----------
|
||||||
|
// Perform Left Join procedure
|
||||||
|
// Perform Right Join procedure, but excluding rows in Inner Join
|
||||||
|
|
||||||
|
let config = Config::default();
|
||||||
|
let sep = ",";
|
||||||
|
let cap = max(left.len(), right.len());
|
||||||
|
let shared_join_key = if left_join_key == right_join_key {
|
||||||
|
Some(left_join_key)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// For the "other" table, create a map from value in `on` column to a list of the
|
||||||
|
// rows having that value.
|
||||||
|
let mut result: Vec<Value> = Vec::new();
|
||||||
|
let is_outer = matches!(join_type, JoinType::Outer);
|
||||||
|
let (this, this_join_key, other, other_keys, join_type) = match join_type {
|
||||||
|
JoinType::Left | JoinType::Outer => (
|
||||||
|
left,
|
||||||
|
left_join_key,
|
||||||
|
lookup_table(right, right_join_key, sep, cap, &config),
|
||||||
|
column_names(right),
|
||||||
|
// For Outer we do a Left pass and a Right pass; this is the Left
|
||||||
|
// pass.
|
||||||
|
JoinType::Left,
|
||||||
|
),
|
||||||
|
JoinType::Inner | JoinType::Right => (
|
||||||
|
right,
|
||||||
|
right_join_key,
|
||||||
|
lookup_table(left, left_join_key, sep, cap, &config),
|
||||||
|
column_names(left),
|
||||||
|
join_type,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
join_rows(
|
||||||
|
&mut result,
|
||||||
|
this,
|
||||||
|
this_join_key,
|
||||||
|
other,
|
||||||
|
other_keys,
|
||||||
|
shared_join_key,
|
||||||
|
&join_type,
|
||||||
|
IncludeInner::Yes,
|
||||||
|
sep,
|
||||||
|
&config,
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
if is_outer {
|
||||||
|
let (this, this_join_key, other, other_names, join_type) = (
|
||||||
|
right,
|
||||||
|
right_join_key,
|
||||||
|
lookup_table(left, left_join_key, sep, cap, &config),
|
||||||
|
column_names(left),
|
||||||
|
JoinType::Right,
|
||||||
|
);
|
||||||
|
join_rows(
|
||||||
|
&mut result,
|
||||||
|
this,
|
||||||
|
this_join_key,
|
||||||
|
other,
|
||||||
|
other_names,
|
||||||
|
shared_join_key,
|
||||||
|
&join_type,
|
||||||
|
IncludeInner::No,
|
||||||
|
sep,
|
||||||
|
&config,
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Value::List { vals: result, span }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join rows of `this` (a nushell table) to rows of `other` (a lookup-table
|
||||||
|
// containing rows of a nushell table).
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn join_rows(
|
||||||
|
result: &mut Vec<Value>,
|
||||||
|
this: &Vec<Value>,
|
||||||
|
this_join_key: &str,
|
||||||
|
other: HashMap<String, RowEntries>,
|
||||||
|
other_keys: &Vec<String>,
|
||||||
|
shared_join_key: Option<&str>,
|
||||||
|
join_type: &JoinType,
|
||||||
|
include_inner: IncludeInner,
|
||||||
|
sep: &str,
|
||||||
|
config: &Config,
|
||||||
|
span: Span,
|
||||||
|
) {
|
||||||
|
for this_row in this {
|
||||||
|
if let Value::Record {
|
||||||
|
cols: this_cols,
|
||||||
|
vals: this_vals,
|
||||||
|
..
|
||||||
|
} = this_row
|
||||||
|
{
|
||||||
|
if let Some(this_valkey) = this_row.get_data_by_key(this_join_key) {
|
||||||
|
if let Some(other_rows) = other.get(&this_valkey.into_string(sep, config)) {
|
||||||
|
if matches!(include_inner, IncludeInner::Yes) {
|
||||||
|
for (other_cols, other_vals) in other_rows {
|
||||||
|
// `other` table contains rows matching `this` row on the join column
|
||||||
|
let (res_cols, res_vals) = match join_type {
|
||||||
|
JoinType::Inner | JoinType::Right => merge_records(
|
||||||
|
(other_cols, other_vals), // `other` (lookup) is the left input table
|
||||||
|
(this_cols, this_vals),
|
||||||
|
shared_join_key,
|
||||||
|
),
|
||||||
|
JoinType::Left => merge_records(
|
||||||
|
(this_cols, this_vals), // `this` is the left input table
|
||||||
|
(other_cols, other_vals),
|
||||||
|
shared_join_key,
|
||||||
|
),
|
||||||
|
_ => panic!("not implemented"),
|
||||||
|
};
|
||||||
|
result.push(Value::Record {
|
||||||
|
cols: res_cols,
|
||||||
|
vals: res_vals,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if !matches!(join_type, JoinType::Inner) {
|
||||||
|
// `other` table did not contain any rows matching
|
||||||
|
// `this` row on the join column; emit a single joined
|
||||||
|
// row with null values for columns not present,
|
||||||
|
let other_vals = other_keys
|
||||||
|
.iter()
|
||||||
|
.map(|key| {
|
||||||
|
if Some(key.as_ref()) == shared_join_key {
|
||||||
|
this_row
|
||||||
|
.get_data_by_key(key)
|
||||||
|
.unwrap_or_else(|| Value::nothing(span))
|
||||||
|
} else {
|
||||||
|
Value::nothing(span)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let (res_cols, res_vals) = match join_type {
|
||||||
|
JoinType::Inner | JoinType::Right => merge_records(
|
||||||
|
(other_keys, &other_vals),
|
||||||
|
(this_cols, this_vals),
|
||||||
|
shared_join_key,
|
||||||
|
),
|
||||||
|
JoinType::Left => merge_records(
|
||||||
|
(this_cols, this_vals),
|
||||||
|
(other_keys, &other_vals),
|
||||||
|
shared_join_key,
|
||||||
|
),
|
||||||
|
_ => panic!("not implemented"),
|
||||||
|
};
|
||||||
|
|
||||||
|
result.push(Value::Record {
|
||||||
|
cols: res_cols,
|
||||||
|
vals: res_vals,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} // else { a row is missing a value for the join column }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return column names (i.e. ordered keys from the first row; we assume that
|
||||||
|
// these are the same for all rows).
|
||||||
|
fn column_names(table: &[Value]) -> &Vec<String> {
|
||||||
|
table
|
||||||
|
.iter()
|
||||||
|
.find_map(|val| match val {
|
||||||
|
Value::Record { cols, .. } => Some(cols),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.unwrap_or(EMPTY_COL_NAMES)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a map from value in `on` column to a list of the rows having that
|
||||||
|
// value.
|
||||||
|
fn lookup_table<'a>(
|
||||||
|
rows: &'a Vec<Value>,
|
||||||
|
on: &str,
|
||||||
|
sep: &str,
|
||||||
|
cap: usize,
|
||||||
|
config: &Config,
|
||||||
|
) -> HashMap<String, RowEntries<'a>> {
|
||||||
|
let mut map = HashMap::<String, RowEntries>::with_capacity(cap);
|
||||||
|
for row in rows {
|
||||||
|
if let Value::Record { cols, vals, .. } = row {
|
||||||
|
if let Some(val) = &row.get_data_by_key(on) {
|
||||||
|
let valkey = val.into_string(sep, config);
|
||||||
|
map.entry(valkey).or_default().push((cols, vals));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge `left` and `right` records, renaming keys in `right` where they clash
|
||||||
|
// with keys in `left`. If `shared_key` is supplied then it is the name of a key
|
||||||
|
// that should not be renamed (its values are guaranteed to be equal).
|
||||||
|
fn merge_records(
|
||||||
|
left: (&Vec<String>, &Vec<Value>),
|
||||||
|
right: (&Vec<String>, &Vec<Value>),
|
||||||
|
shared_key: Option<&str>,
|
||||||
|
) -> (Vec<String>, Vec<Value>) {
|
||||||
|
let ((l_keys, l_vals), (r_keys, r_vals)) = (left, right);
|
||||||
|
let cap = max(l_keys.len(), r_keys.len());
|
||||||
|
let mut seen = HashSet::with_capacity(cap);
|
||||||
|
let (mut res_keys, mut res_vals) = (Vec::with_capacity(cap), Vec::with_capacity(cap));
|
||||||
|
for (k, v) in l_keys.iter().zip(l_vals) {
|
||||||
|
res_keys.push(k.clone());
|
||||||
|
res_vals.push(v.clone());
|
||||||
|
seen.insert(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k, v) in r_keys.iter().zip(r_vals) {
|
||||||
|
let k_seen = seen.contains(k);
|
||||||
|
let k_shared = shared_key == Some(k);
|
||||||
|
// Do not output shared join key twice
|
||||||
|
if !(k_seen && k_shared) {
|
||||||
|
res_keys.push(if k_seen { format!("{}_", k) } else { k.clone() });
|
||||||
|
res_vals.push(v.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(res_keys, res_vals)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(Join {})
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
mod all;
|
mod all;
|
||||||
mod any;
|
mod any;
|
||||||
mod append;
|
mod append;
|
||||||
mod collect;
|
|
||||||
mod columns;
|
mod columns;
|
||||||
mod compact;
|
mod compact;
|
||||||
mod default;
|
mod default;
|
||||||
@ -20,6 +19,7 @@ mod group;
|
|||||||
mod group_by;
|
mod group_by;
|
||||||
mod headers;
|
mod headers;
|
||||||
mod insert;
|
mod insert;
|
||||||
|
mod join;
|
||||||
mod last;
|
mod last;
|
||||||
mod length;
|
mod length;
|
||||||
mod lines;
|
mod lines;
|
||||||
@ -57,7 +57,6 @@ mod zip;
|
|||||||
pub use all::All;
|
pub use all::All;
|
||||||
pub use any::Any;
|
pub use any::Any;
|
||||||
pub use append::Append;
|
pub use append::Append;
|
||||||
pub use collect::Collect;
|
|
||||||
pub use columns::Columns;
|
pub use columns::Columns;
|
||||||
pub use compact::Compact;
|
pub use compact::Compact;
|
||||||
pub use default::Default;
|
pub use default::Default;
|
||||||
@ -76,6 +75,7 @@ pub use group::Group;
|
|||||||
pub use group_by::GroupBy;
|
pub use group_by::GroupBy;
|
||||||
pub use headers::Headers;
|
pub use headers::Headers;
|
||||||
pub use insert::Insert;
|
pub use insert::Insert;
|
||||||
|
pub use join::Join;
|
||||||
pub use last::Last;
|
pub use last::Last;
|
||||||
pub use length::Length;
|
pub use length::Length;
|
||||||
pub use lines::Lines;
|
pub use lines::Lines;
|
||||||
|
@ -30,6 +30,12 @@ impl Command for ParEach {
|
|||||||
),
|
),
|
||||||
(Type::Table(vec![]), Type::List(Box::new(Type::Any))),
|
(Type::Table(vec![]), Type::List(Box::new(Type::Any))),
|
||||||
])
|
])
|
||||||
|
.named(
|
||||||
|
"threads",
|
||||||
|
SyntaxShape::Int,
|
||||||
|
"the number of threads to use",
|
||||||
|
Some('t'),
|
||||||
|
)
|
||||||
.required(
|
.required(
|
||||||
"closure",
|
"closure",
|
||||||
SyntaxShape::Closure(Some(vec![SyntaxShape::Any, SyntaxShape::Int])),
|
SyntaxShape::Closure(Some(vec![SyntaxShape::Any, SyntaxShape::Int])),
|
||||||
@ -85,8 +91,27 @@ impl Command for ParEach {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let capture_block: Closure = call.req(engine_state, stack, 0)?;
|
fn create_pool(num_threads: usize) -> Result<rayon::ThreadPool, ShellError> {
|
||||||
|
match rayon::ThreadPoolBuilder::new()
|
||||||
|
.num_threads(num_threads)
|
||||||
|
.build()
|
||||||
|
{
|
||||||
|
Err(e) => Err(e).map_err(|e| {
|
||||||
|
ShellError::GenericError(
|
||||||
|
"Error creating thread pool".into(),
|
||||||
|
e.to_string(),
|
||||||
|
Some(Span::unknown()),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
Ok(pool) => Ok(pool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let capture_block: Closure = call.req(engine_state, stack, 0)?;
|
||||||
|
let threads: Option<usize> = call.get_flag(engine_state, stack, "threads")?;
|
||||||
|
let max_threads = threads.unwrap_or(0);
|
||||||
let metadata = input.metadata();
|
let metadata = input.metadata();
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let block_id = capture_block.block_id;
|
let block_id = capture_block.block_id;
|
||||||
@ -96,156 +121,165 @@ impl Command for ParEach {
|
|||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::Empty => Ok(PipelineData::Empty),
|
PipelineData::Empty => Ok(PipelineData::Empty),
|
||||||
PipelineData::Value(Value::Range { val, .. }, ..) => Ok(val
|
PipelineData::Value(Value::Range { val, .. }, ..) => Ok(create_pool(max_threads)?
|
||||||
.into_range_iter(ctrlc.clone())?
|
.install(|| {
|
||||||
.par_bridge()
|
val.into_range_iter(ctrlc.clone())
|
||||||
.map(move |x| {
|
.expect("unable to create a range iterator")
|
||||||
let block = engine_state.get_block(block_id);
|
.par_bridge()
|
||||||
|
.map(move |x| {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
let mut stack = stack.clone();
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
if let Some(var_id) = &var.var_id {
|
if let Some(var_id) = &var.var_id {
|
||||||
stack.add_var(*var_id, x.clone());
|
stack.add_var(*var_id, x.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let val_span = x.span();
|
||||||
|
match eval_block_with_early_return(
|
||||||
|
engine_state,
|
||||||
|
&mut stack,
|
||||||
|
block,
|
||||||
|
x.into_pipeline_data(),
|
||||||
|
redirect_stdout,
|
||||||
|
redirect_stderr,
|
||||||
|
) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(error) => Value::Error {
|
||||||
|
error: Box::new(chain_error_with_input(error, val_span)),
|
||||||
|
}
|
||||||
|
.into_pipeline_data(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.into_pipeline_data(ctrlc)
|
||||||
|
})),
|
||||||
|
PipelineData::Value(Value::List { vals: val, .. }, ..) => Ok(create_pool(max_threads)?
|
||||||
|
.install(|| {
|
||||||
|
val.par_iter()
|
||||||
|
.map(move |x| {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
stack.add_var(*var_id, x.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let val_span = x.span();
|
||||||
|
match eval_block_with_early_return(
|
||||||
|
engine_state,
|
||||||
|
&mut stack,
|
||||||
|
block,
|
||||||
|
x.clone().into_pipeline_data(),
|
||||||
|
redirect_stdout,
|
||||||
|
redirect_stderr,
|
||||||
|
) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(error) => Value::Error {
|
||||||
|
error: Box::new(chain_error_with_input(error, val_span)),
|
||||||
|
}
|
||||||
|
.into_pipeline_data(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.into_pipeline_data(ctrlc)
|
||||||
|
})),
|
||||||
|
PipelineData::ListStream(stream, ..) => Ok(create_pool(max_threads)?.install(|| {
|
||||||
|
stream
|
||||||
|
.par_bridge()
|
||||||
|
.map(move |x| {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
stack.add_var(*var_id, x.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let val_span = x.span();
|
let val_span = x.span();
|
||||||
match eval_block_with_early_return(
|
match eval_block_with_early_return(
|
||||||
engine_state,
|
engine_state,
|
||||||
&mut stack,
|
&mut stack,
|
||||||
block,
|
block,
|
||||||
x.into_pipeline_data(),
|
x.into_pipeline_data(),
|
||||||
redirect_stdout,
|
redirect_stdout,
|
||||||
redirect_stderr,
|
redirect_stderr,
|
||||||
) {
|
) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(error) => Value::Error {
|
Err(error) => Value::Error {
|
||||||
error: Box::new(chain_error_with_input(error, val_span)),
|
error: Box::new(chain_error_with_input(error, val_span)),
|
||||||
|
}
|
||||||
|
.into_pipeline_data(),
|
||||||
}
|
}
|
||||||
.into_pipeline_data(),
|
})
|
||||||
}
|
.collect::<Vec<_>>()
|
||||||
})
|
.into_iter()
|
||||||
.collect::<Vec<_>>()
|
.flatten()
|
||||||
.into_iter()
|
.into_pipeline_data(ctrlc)
|
||||||
.flatten()
|
})),
|
||||||
.into_pipeline_data(ctrlc)),
|
|
||||||
PipelineData::Value(Value::List { vals: val, .. }, ..) => Ok(val
|
|
||||||
.into_iter()
|
|
||||||
.par_bridge()
|
|
||||||
.map(move |x| {
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
|
|
||||||
let mut stack = stack.clone();
|
|
||||||
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
|
||||||
if let Some(var_id) = &var.var_id {
|
|
||||||
stack.add_var(*var_id, x.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let val_span = x.span();
|
|
||||||
match eval_block_with_early_return(
|
|
||||||
engine_state,
|
|
||||||
&mut stack,
|
|
||||||
block,
|
|
||||||
x.into_pipeline_data(),
|
|
||||||
redirect_stdout,
|
|
||||||
redirect_stderr,
|
|
||||||
) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(error) => Value::Error {
|
|
||||||
error: Box::new(chain_error_with_input(error, val_span)),
|
|
||||||
}
|
|
||||||
.into_pipeline_data(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.into_pipeline_data(ctrlc)),
|
|
||||||
PipelineData::ListStream(stream, ..) => Ok(stream
|
|
||||||
.par_bridge()
|
|
||||||
.map(move |x| {
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
|
|
||||||
let mut stack = stack.clone();
|
|
||||||
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
|
||||||
if let Some(var_id) = &var.var_id {
|
|
||||||
stack.add_var(*var_id, x.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let val_span = x.span();
|
|
||||||
match eval_block_with_early_return(
|
|
||||||
engine_state,
|
|
||||||
&mut stack,
|
|
||||||
block,
|
|
||||||
x.into_pipeline_data(),
|
|
||||||
redirect_stdout,
|
|
||||||
redirect_stderr,
|
|
||||||
) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(error) => Value::Error {
|
|
||||||
error: Box::new(chain_error_with_input(error, val_span)),
|
|
||||||
}
|
|
||||||
.into_pipeline_data(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.into_pipeline_data(ctrlc)),
|
|
||||||
PipelineData::ExternalStream { stdout: None, .. } => Ok(PipelineData::empty()),
|
PipelineData::ExternalStream { stdout: None, .. } => Ok(PipelineData::empty()),
|
||||||
PipelineData::ExternalStream {
|
PipelineData::ExternalStream {
|
||||||
stdout: Some(stream),
|
stdout: Some(stream),
|
||||||
..
|
..
|
||||||
} => Ok(stream
|
} => Ok(create_pool(max_threads)?.install(|| {
|
||||||
.par_bridge()
|
stream
|
||||||
.map(move |x| {
|
.par_bridge()
|
||||||
let x = match x {
|
.map(move |x| {
|
||||||
Ok(x) => x,
|
let x = match x {
|
||||||
Err(err) => {
|
Ok(x) => x,
|
||||||
return Value::Error {
|
Err(err) => {
|
||||||
error: Box::new(err),
|
return Value::Error {
|
||||||
|
error: Box::new(err),
|
||||||
|
}
|
||||||
|
.into_pipeline_data()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
stack.add_var(*var_id, x.clone());
|
||||||
}
|
}
|
||||||
.into_pipeline_data()
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let block = engine_state.get_block(block_id);
|
match eval_block_with_early_return(
|
||||||
|
engine_state,
|
||||||
let mut stack = stack.clone();
|
&mut stack,
|
||||||
|
block,
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
x.into_pipeline_data(),
|
||||||
if let Some(var_id) = &var.var_id {
|
redirect_stdout,
|
||||||
stack.add_var(*var_id, x.clone());
|
redirect_stderr,
|
||||||
|
) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(error) => Value::Error {
|
||||||
|
error: Box::new(error),
|
||||||
|
}
|
||||||
|
.into_pipeline_data(),
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
match eval_block_with_early_return(
|
.into_iter()
|
||||||
engine_state,
|
.flatten()
|
||||||
&mut stack,
|
.into_pipeline_data(ctrlc)
|
||||||
block,
|
})),
|
||||||
x.into_pipeline_data(),
|
|
||||||
redirect_stdout,
|
|
||||||
redirect_stderr,
|
|
||||||
) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(error) => Value::Error {
|
|
||||||
error: Box::new(error),
|
|
||||||
}
|
|
||||||
.into_pipeline_data(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.into_pipeline_data(ctrlc)),
|
|
||||||
// This match allows non-iterables to be accepted,
|
// This match allows non-iterables to be accepted,
|
||||||
// which is currently considered undesirable (Nov 2022).
|
// which is currently considered undesirable (Nov 2022).
|
||||||
PipelineData::Value(x, ..) => {
|
PipelineData::Value(x, ..) => {
|
||||||
|
eprint!("value");
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
@ -24,7 +24,7 @@ impl Command for Select {
|
|||||||
])
|
])
|
||||||
.switch(
|
.switch(
|
||||||
"ignore-errors",
|
"ignore-errors",
|
||||||
"when an error occurs, instead of erroring out, suppress the error message",
|
"ignore missing data (make all cell path members optional)",
|
||||||
Some('i'),
|
Some('i'),
|
||||||
)
|
)
|
||||||
.rest(
|
.rest(
|
||||||
@ -56,11 +56,17 @@ produce a table, a list will produce a list, and a record will produce a record.
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let columns: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let mut columns: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
let span = call.head;
|
|
||||||
let ignore_errors = call.has_flag("ignore-errors");
|
let ignore_errors = call.has_flag("ignore-errors");
|
||||||
|
let span = call.head;
|
||||||
|
|
||||||
select(engine_state, span, columns, input, ignore_errors)
|
if ignore_errors {
|
||||||
|
for cell_path in &mut columns {
|
||||||
|
cell_path.make_optional();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select(engine_state, span, columns, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -97,7 +103,6 @@ fn select(
|
|||||||
call_span: Span,
|
call_span: Span,
|
||||||
columns: Vec<CellPath>,
|
columns: Vec<CellPath>,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
ignore_errors: bool,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let mut unique_rows: HashSet<usize> = HashSet::new();
|
let mut unique_rows: HashSet<usize> = HashSet::new();
|
||||||
|
|
||||||
@ -106,11 +111,8 @@ fn select(
|
|||||||
for column in columns {
|
for column in columns {
|
||||||
let CellPath { ref members } = column;
|
let CellPath { ref members } = column;
|
||||||
match members.get(0) {
|
match members.get(0) {
|
||||||
Some(PathMember::Int { val, span }) => {
|
Some(PathMember::Int { val, span, .. }) => {
|
||||||
if members.len() > 1 {
|
if members.len() > 1 {
|
||||||
if ignore_errors {
|
|
||||||
return Ok(Value::nothing(call_span).into_pipeline_data());
|
|
||||||
}
|
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"Select only allows row numbers for rows".into(),
|
"Select only allows row numbers for rows".into(),
|
||||||
"extra after row number".into(),
|
"extra after row number".into(),
|
||||||
@ -165,20 +167,14 @@ fn select(
|
|||||||
) => {
|
) => {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
let mut columns_with_value = Vec::new();
|
let mut columns_with_value = Vec::new();
|
||||||
let mut allempty = true;
|
|
||||||
for input_val in input_vals {
|
for input_val in input_vals {
|
||||||
if !columns.is_empty() {
|
if !columns.is_empty() {
|
||||||
let mut cols = vec![];
|
let mut cols = vec![];
|
||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
for path in &columns {
|
for path in &columns {
|
||||||
//FIXME: improve implementation to not clone
|
//FIXME: improve implementation to not clone
|
||||||
match input_val.clone().follow_cell_path(
|
match input_val.clone().follow_cell_path(&path.members, false) {
|
||||||
&path.members,
|
|
||||||
false,
|
|
||||||
ignore_errors,
|
|
||||||
) {
|
|
||||||
Ok(fetcher) => {
|
Ok(fetcher) => {
|
||||||
allempty = false;
|
|
||||||
cols.push(path.into_string().replace('.', "_"));
|
cols.push(path.into_string().replace('.', "_"));
|
||||||
vals.push(fetcher);
|
vals.push(fetcher);
|
||||||
if !columns_with_value.contains(&path) {
|
if !columns_with_value.contains(&path) {
|
||||||
@ -196,14 +192,11 @@ fn select(
|
|||||||
output.push(input_val)
|
output.push(input_val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if allempty {
|
|
||||||
Ok(Value::nothing(call_span).into_pipeline_data())
|
Ok(output
|
||||||
} else {
|
.into_iter()
|
||||||
Ok(output
|
.into_pipeline_data(engine_state.ctrlc.clone())
|
||||||
.into_iter()
|
.set_metadata(metadata))
|
||||||
.into_pipeline_data(engine_state.ctrlc.clone())
|
|
||||||
.set_metadata(metadata))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PipelineData::ListStream(stream, metadata, ..) => {
|
PipelineData::ListStream(stream, metadata, ..) => {
|
||||||
let mut values = vec![];
|
let mut values = vec![];
|
||||||
@ -214,10 +207,7 @@ fn select(
|
|||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
for path in &columns {
|
for path in &columns {
|
||||||
//FIXME: improve implementation to not clone
|
//FIXME: improve implementation to not clone
|
||||||
match x
|
match x.clone().follow_cell_path(&path.members, false) {
|
||||||
.clone()
|
|
||||||
.follow_cell_path(&path.members, false, ignore_errors)
|
|
||||||
{
|
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
cols.push(path.into_string().replace('.', "_"));
|
cols.push(path.into_string().replace('.', "_"));
|
||||||
vals.push(value);
|
vals.push(value);
|
||||||
@ -246,10 +236,7 @@ fn select(
|
|||||||
|
|
||||||
for cell_path in columns {
|
for cell_path in columns {
|
||||||
// FIXME: remove clone
|
// FIXME: remove clone
|
||||||
match v
|
match v.clone().follow_cell_path(&cell_path.members, false) {
|
||||||
.clone()
|
|
||||||
.follow_cell_path(&cell_path.members, false, ignore_errors)
|
|
||||||
{
|
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
cols.push(cell_path.into_string().replace('.', "_"));
|
cols.push(cell_path.into_string().replace('.', "_"));
|
||||||
vals.push(result);
|
vals.push(result);
|
||||||
|
@ -271,22 +271,12 @@ pub fn sort(
|
|||||||
insensitive: bool,
|
insensitive: bool,
|
||||||
natural: bool,
|
natural: bool,
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<(), ShellError> {
|
||||||
if vec.is_empty() {
|
match vec.first() {
|
||||||
return Err(ShellError::GenericError(
|
Some(Value::Record {
|
||||||
"no values to work with".to_string(),
|
|
||||||
"".to_string(),
|
|
||||||
None,
|
|
||||||
Some("no values to work with".to_string()),
|
|
||||||
Vec::new(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
match &vec[0] {
|
|
||||||
Value::Record {
|
|
||||||
cols,
|
cols,
|
||||||
vals: _input_vals,
|
vals: _input_vals,
|
||||||
..
|
..
|
||||||
} => {
|
}) => {
|
||||||
let columns = cols.clone();
|
let columns = cols.clone();
|
||||||
vec.sort_by(|a, b| process(a, b, &columns, span, insensitive, natural));
|
vec.sort_by(|a, b| process(a, b, &columns, span, insensitive, natural));
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,7 @@ fn sort_attributes(val: Value) -> Value {
|
|||||||
|
|
||||||
fn generate_key(item: &ValueCounter) -> Result<String, ShellError> {
|
fn generate_key(item: &ValueCounter) -> Result<String, ShellError> {
|
||||||
let value = sort_attributes(item.val_to_compare.clone()); //otherwise, keys could be different for Records
|
let value = sort_attributes(item.val_to_compare.clone()); //otherwise, keys could be different for Records
|
||||||
value_to_string(&value, Span::unknown())
|
value_to_string(&value, Span::unknown(), 0, &None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_results_with_count(head: Span, uniq_values: Vec<ValueCounter>) -> Vec<Value> {
|
fn generate_results_with_count(head: Span, uniq_values: Vec<ValueCounter>) -> Vec<Value> {
|
||||||
|
@ -107,21 +107,11 @@ impl Command for UniqBy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate(vec: Vec<Value>, columns: &Vec<String>, span: Span) -> Result<(), ShellError> {
|
fn validate(vec: Vec<Value>, columns: &Vec<String>, span: Span) -> Result<(), ShellError> {
|
||||||
if vec.is_empty() {
|
if let Some(Value::Record {
|
||||||
return Err(ShellError::GenericError(
|
|
||||||
"no values to work with".to_string(),
|
|
||||||
"".to_string(),
|
|
||||||
None,
|
|
||||||
Some("no values to work with".to_string()),
|
|
||||||
Vec::new(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Value::Record {
|
|
||||||
cols,
|
cols,
|
||||||
vals: _input_vals,
|
vals: _input_vals,
|
||||||
span: val_span,
|
span: val_span,
|
||||||
} = &vec[0]
|
}) = vec.first()
|
||||||
{
|
{
|
||||||
if columns.is_empty() {
|
if columns.is_empty() {
|
||||||
// This uses the same format as the 'requires a column name' error in split_by.rs
|
// This uses the same format as the 'requires a column name' error in split_by.rs
|
||||||
|
@ -143,7 +143,7 @@ fn update(
|
|||||||
ctrlc,
|
ctrlc,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
if let Some(PathMember::Int { val, span }) = cell_path.members.get(0) {
|
if let Some(PathMember::Int { val, span, .. }) = cell_path.members.get(0) {
|
||||||
let mut input = input.into_iter();
|
let mut input = input.into_iter();
|
||||||
let mut pre_elems = vec![];
|
let mut pre_elems = vec![];
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ fn upsert(
|
|||||||
ctrlc,
|
ctrlc,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
if let Some(PathMember::Int { val, span }) = cell_path.members.get(0) {
|
if let Some(PathMember::Int { val, span, .. }) = cell_path.members.get(0) {
|
||||||
let mut input = input.into_iter();
|
let mut input = input.into_iter();
|
||||||
let mut pre_elems = vec![];
|
let mut pre_elems = vec![];
|
||||||
|
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
use nu_protocol::{ShellError, Span};
|
use nu_engine::{eval_block, CallExt};
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Closure, EngineState, Stack},
|
||||||
|
IntoPipelineData, PipelineData, ShellError, Span, Value,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn chain_error_with_input(
|
pub fn chain_error_with_input(
|
||||||
error_source: ShellError,
|
error_source: ShellError,
|
||||||
@ -9,3 +14,66 @@ pub fn chain_error_with_input(
|
|||||||
}
|
}
|
||||||
error_source
|
error_source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn boolean_fold(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
accumulator: bool,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let span = call.head;
|
||||||
|
|
||||||
|
let capture_block: Closure = call.req(engine_state, stack, 0)?;
|
||||||
|
let block_id = capture_block.block_id;
|
||||||
|
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id);
|
||||||
|
let mut stack = stack.captures_to_stack(&capture_block.captures);
|
||||||
|
|
||||||
|
let orig_env_vars = stack.env_vars.clone();
|
||||||
|
let orig_env_hidden = stack.env_hidden.clone();
|
||||||
|
|
||||||
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
|
let engine_state = engine_state.clone();
|
||||||
|
|
||||||
|
for value in input.into_interruptible_iter(ctrlc) {
|
||||||
|
// with_env() is used here to ensure that each iteration uses
|
||||||
|
// a different set of environment variables.
|
||||||
|
// Hence, a 'cd' in the first loop won't affect the next loop.
|
||||||
|
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
||||||
|
|
||||||
|
if let Some(var_id) = var_id {
|
||||||
|
stack.add_var(var_id, value.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let eval = eval_block(
|
||||||
|
&engine_state,
|
||||||
|
&mut stack,
|
||||||
|
block,
|
||||||
|
value.into_pipeline_data(),
|
||||||
|
call.redirect_stdout,
|
||||||
|
call.redirect_stderr,
|
||||||
|
);
|
||||||
|
match eval {
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
Ok(pipeline_data) => {
|
||||||
|
if pipeline_data.into_value(span).is_true() == accumulator {
|
||||||
|
return Ok(Value::Bool {
|
||||||
|
val: accumulator,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.into_pipeline_data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Bool {
|
||||||
|
val: !accumulator,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::delimited::{from_delimited_data, trim_from_str};
|
use super::delimited::{from_delimited_data, trim_from_str, DelimitedReaderConfig};
|
||||||
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
@ -24,11 +24,34 @@ impl Command for FromCsv {
|
|||||||
"a character to separate columns, defaults to ','",
|
"a character to separate columns, defaults to ','",
|
||||||
Some('s'),
|
Some('s'),
|
||||||
)
|
)
|
||||||
|
.named(
|
||||||
|
"comment",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"a comment character to ignore lines starting with it",
|
||||||
|
Some('c'),
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"quote",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"a quote character to ignore separators in strings, defaults to '\"'",
|
||||||
|
Some('q'),
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"escape",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"an escape character for strings containing the quote character",
|
||||||
|
Some('e'),
|
||||||
|
)
|
||||||
.switch(
|
.switch(
|
||||||
"noheaders",
|
"noheaders",
|
||||||
"don't treat the first row as column names",
|
"don't treat the first row as column names",
|
||||||
Some('n'),
|
Some('n'),
|
||||||
)
|
)
|
||||||
|
.switch(
|
||||||
|
"flexible",
|
||||||
|
"allow the number of fields in records to be variable",
|
||||||
|
None,
|
||||||
|
)
|
||||||
.switch("no-infer", "no field type inferencing", None)
|
.switch("no-infer", "no field type inferencing", None)
|
||||||
.named(
|
.named(
|
||||||
"trim",
|
"trim",
|
||||||
@ -75,28 +98,28 @@ impl Command for FromCsv {
|
|||||||
example: "open data.txt | from csv --noheaders",
|
example: "open data.txt | from csv --noheaders",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
|
||||||
description: "Convert comma-separated data to a table, ignoring headers",
|
|
||||||
example: "open data.txt | from csv -n",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
Example {
|
||||||
description: "Convert semicolon-separated data to a table",
|
description: "Convert semicolon-separated data to a table",
|
||||||
example: "open data.txt | from csv --separator ';'",
|
example: "open data.txt | from csv --separator ';'",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert semicolon-separated data to a table, dropping all possible whitespaces around header names and field values",
|
description: "Convert comma-separated data to a table, ignoring lines starting with '#'",
|
||||||
|
example: "open data.txt | from csv --comment '#'",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Convert comma-separated data to a table, dropping all possible whitespaces around header names and field values",
|
||||||
example: "open data.txt | from csv --trim all",
|
example: "open data.txt | from csv --trim all",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert semicolon-separated data to a table, dropping all possible whitespaces around header names",
|
description: "Convert comma-separated data to a table, dropping all possible whitespaces around header names",
|
||||||
example: "open data.txt | from csv --trim headers",
|
example: "open data.txt | from csv --trim headers",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert semicolon-separated data to a table, dropping all possible whitespaces around field values",
|
description: "Convert comma-separated data to a table, dropping all possible whitespaces around field values",
|
||||||
example: "open data.txt | from csv --trim fields",
|
example: "open data.txt | from csv --trim fields",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
@ -112,32 +135,41 @@ fn from_csv(
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let name = call.head;
|
let name = call.head;
|
||||||
|
|
||||||
|
let separator = call
|
||||||
|
.get_flag(engine_state, stack, "separator")?
|
||||||
|
.map(|v: Value| v.as_char())
|
||||||
|
.transpose()?
|
||||||
|
.unwrap_or(',');
|
||||||
|
let comment = call
|
||||||
|
.get_flag(engine_state, stack, "comment")?
|
||||||
|
.map(|v: Value| v.as_char())
|
||||||
|
.transpose()?;
|
||||||
|
let quote = call
|
||||||
|
.get_flag(engine_state, stack, "quote")?
|
||||||
|
.map(|v: Value| v.as_char())
|
||||||
|
.transpose()?
|
||||||
|
.unwrap_or('"');
|
||||||
|
let escape = call
|
||||||
|
.get_flag(engine_state, stack, "escape")?
|
||||||
|
.map(|v: Value| v.as_char())
|
||||||
|
.transpose()?;
|
||||||
let no_infer = call.has_flag("no-infer");
|
let no_infer = call.has_flag("no-infer");
|
||||||
let noheaders = call.has_flag("noheaders");
|
let noheaders = call.has_flag("noheaders");
|
||||||
let separator: Option<Value> = call.get_flag(engine_state, stack, "separator")?;
|
let flexible = call.has_flag("flexible");
|
||||||
let trim: Option<Value> = call.get_flag(engine_state, stack, "trim")?;
|
let trim = trim_from_str(call.get_flag(engine_state, stack, "trim")?)?;
|
||||||
|
|
||||||
let sep = match separator {
|
let config = DelimitedReaderConfig {
|
||||||
Some(Value::String { val: s, span }) => {
|
separator,
|
||||||
if s == r"\t" {
|
comment,
|
||||||
'\t'
|
quote,
|
||||||
} else {
|
escape,
|
||||||
let vec_s: Vec<char> = s.chars().collect();
|
noheaders,
|
||||||
if vec_s.len() != 1 {
|
flexible,
|
||||||
return Err(ShellError::MissingParameter {
|
no_infer,
|
||||||
param_name: "single character separator".into(),
|
trim,
|
||||||
span,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
vec_s[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => ',',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let trim = trim_from_str(trim)?;
|
from_delimited_data(config, input, name)
|
||||||
|
|
||||||
from_delimited_data(noheaders, no_infer, sep, trim, input, name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -2,16 +2,26 @@ use csv::{ReaderBuilder, Trim};
|
|||||||
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Value};
|
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Value};
|
||||||
|
|
||||||
fn from_delimited_string_to_value(
|
fn from_delimited_string_to_value(
|
||||||
|
DelimitedReaderConfig {
|
||||||
|
separator,
|
||||||
|
comment,
|
||||||
|
quote,
|
||||||
|
escape,
|
||||||
|
noheaders,
|
||||||
|
flexible,
|
||||||
|
no_infer,
|
||||||
|
trim,
|
||||||
|
}: DelimitedReaderConfig,
|
||||||
s: String,
|
s: String,
|
||||||
noheaders: bool,
|
|
||||||
no_infer: bool,
|
|
||||||
separator: char,
|
|
||||||
trim: Trim,
|
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<Value, csv::Error> {
|
) -> Result<Value, csv::Error> {
|
||||||
let mut reader = ReaderBuilder::new()
|
let mut reader = ReaderBuilder::new()
|
||||||
.has_headers(!noheaders)
|
.has_headers(!noheaders)
|
||||||
|
.flexible(flexible)
|
||||||
.delimiter(separator as u8)
|
.delimiter(separator as u8)
|
||||||
|
.comment(comment.map(|c| c as u8))
|
||||||
|
.quote(quote as u8)
|
||||||
|
.escape(escape.map(|c| c as u8))
|
||||||
.trim(trim)
|
.trim(trim)
|
||||||
.from_reader(s.as_bytes());
|
.from_reader(s.as_bytes());
|
||||||
|
|
||||||
@ -56,24 +66,30 @@ fn from_delimited_string_to_value(
|
|||||||
Ok(Value::List { vals: rows, span })
|
Ok(Value::List { vals: rows, span })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_delimited_data(
|
pub(super) struct DelimitedReaderConfig {
|
||||||
noheaders: bool,
|
pub separator: char,
|
||||||
no_infer: bool,
|
pub comment: Option<char>,
|
||||||
sep: char,
|
pub quote: char,
|
||||||
trim: Trim,
|
pub escape: Option<char>,
|
||||||
|
pub noheaders: bool,
|
||||||
|
pub flexible: bool,
|
||||||
|
pub no_infer: bool,
|
||||||
|
pub trim: Trim,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn from_delimited_data(
|
||||||
|
config: DelimitedReaderConfig,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
name: Span,
|
name: Span,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let (concat_string, _span, metadata) = input.collect_string_strict(name)?;
|
let (concat_string, _span, metadata) = input.collect_string_strict(name)?;
|
||||||
|
|
||||||
Ok(
|
Ok(from_delimited_string_to_value(config, concat_string, name)
|
||||||
from_delimited_string_to_value(concat_string, noheaders, no_infer, sep, trim, name)
|
.map_err(|x| ShellError::DelimiterError {
|
||||||
.map_err(|x| ShellError::DelimiterError {
|
msg: x.to_string(),
|
||||||
msg: x.to_string(),
|
span: name,
|
||||||
span: name,
|
})?
|
||||||
})?
|
.into_pipeline_data_with_metadata(metadata))
|
||||||
.into_pipeline_data_with_metadata(metadata),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trim_from_str(trim: Option<Value>) -> Result<Trim, ShellError> {
|
pub fn trim_from_str(trim: Option<Value>) -> Result<Trim, ShellError> {
|
||||||
|
@ -249,6 +249,12 @@ fn convert_to_value(
|
|||||||
"extra tokens in input file".into(),
|
"extra tokens in input file".into(),
|
||||||
expr.span,
|
expr.span,
|
||||||
)),
|
)),
|
||||||
|
Expr::MatchPattern(..) => Err(ShellError::OutsideSpannedLabeledError(
|
||||||
|
original_text.to_string(),
|
||||||
|
"Error when loading".into(),
|
||||||
|
"extra tokens in input file".into(),
|
||||||
|
expr.span,
|
||||||
|
)),
|
||||||
Expr::GlobPattern(val) => Ok(Value::String { val, span }),
|
Expr::GlobPattern(val) => Ok(Value::String { val, span }),
|
||||||
Expr::ImportPattern(..) => Err(ShellError::OutsideSpannedLabeledError(
|
Expr::ImportPattern(..) => Err(ShellError::OutsideSpannedLabeledError(
|
||||||
original_text.to_string(),
|
original_text.to_string(),
|
||||||
@ -277,6 +283,12 @@ fn convert_to_value(
|
|||||||
|
|
||||||
Ok(Value::List { vals: output, span })
|
Ok(Value::List { vals: output, span })
|
||||||
}
|
}
|
||||||
|
Expr::MatchBlock(..) => Err(ShellError::OutsideSpannedLabeledError(
|
||||||
|
original_text.to_string(),
|
||||||
|
"Error when loading".into(),
|
||||||
|
"match blocks not supported in nuon".into(),
|
||||||
|
expr.span,
|
||||||
|
)),
|
||||||
Expr::Nothing => Ok(Value::Nothing { span }),
|
Expr::Nothing => Ok(Value::Nothing { span }),
|
||||||
Expr::Operator(..) => Err(ShellError::OutsideSpannedLabeledError(
|
Expr::Operator(..) => Err(ShellError::OutsideSpannedLabeledError(
|
||||||
original_text.to_string(),
|
original_text.to_string(),
|
||||||
|
@ -85,15 +85,23 @@ fn parse_aligned_columns<'a>(
|
|||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, (header_name, start_position))| {
|
.map(|(i, (header_name, start_position))| {
|
||||||
|
let char_index_start = match l.char_indices().nth(*start_position) {
|
||||||
|
Some(idx) => idx.0,
|
||||||
|
None => *start_position,
|
||||||
|
};
|
||||||
let val = match headers.get(i + 1) {
|
let val = match headers.get(i + 1) {
|
||||||
Some((_, end)) => {
|
Some((_, end)) => {
|
||||||
if *end < l.len() {
|
if *end < l.len() {
|
||||||
l.get(*start_position..*end)
|
let char_index_end = match l.char_indices().nth(*end) {
|
||||||
|
Some(idx) => idx.0,
|
||||||
|
None => *end,
|
||||||
|
};
|
||||||
|
l.get(char_index_start..char_index_end)
|
||||||
} else {
|
} else {
|
||||||
l.get(*start_position..)
|
l.get(char_index_start..)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => l.get(*start_position..),
|
None => l.get(char_index_start..),
|
||||||
}
|
}
|
||||||
.unwrap_or("")
|
.unwrap_or("")
|
||||||
.trim()
|
.trim()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::delimited::{from_delimited_data, trim_from_str};
|
use super::delimited::{from_delimited_data, trim_from_str, DelimitedReaderConfig};
|
||||||
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
@ -18,11 +18,34 @@ impl Command for FromTsv {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("from tsv")
|
Signature::build("from tsv")
|
||||||
.input_output_types(vec![(Type::String, Type::Table(vec![]))])
|
.input_output_types(vec![(Type::String, Type::Table(vec![]))])
|
||||||
|
.named(
|
||||||
|
"comment",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"a comment character to ignore lines starting with it",
|
||||||
|
Some('c'),
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"quote",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"a quote character to ignore separators in strings, defaults to '\"'",
|
||||||
|
Some('q'),
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"escape",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"an escape character for strings containing the quote character",
|
||||||
|
Some('e'),
|
||||||
|
)
|
||||||
.switch(
|
.switch(
|
||||||
"noheaders",
|
"noheaders",
|
||||||
"don't treat the first row as column names",
|
"don't treat the first row as column names",
|
||||||
Some('n'),
|
Some('n'),
|
||||||
)
|
)
|
||||||
|
.switch(
|
||||||
|
"flexible",
|
||||||
|
"allow the number of fields in records to be variable",
|
||||||
|
None,
|
||||||
|
)
|
||||||
.switch("no-infer", "no field type inferencing", None)
|
.switch("no-infer", "no field type inferencing", None)
|
||||||
.named(
|
.named(
|
||||||
"trim",
|
"trim",
|
||||||
@ -101,12 +124,36 @@ fn from_tsv(
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let name = call.head;
|
let name = call.head;
|
||||||
|
|
||||||
|
let comment = call
|
||||||
|
.get_flag(engine_state, stack, "comment")?
|
||||||
|
.map(|v: Value| v.as_char())
|
||||||
|
.transpose()?;
|
||||||
|
let quote = call
|
||||||
|
.get_flag(engine_state, stack, "quote")?
|
||||||
|
.map(|v: Value| v.as_char())
|
||||||
|
.transpose()?
|
||||||
|
.unwrap_or('"');
|
||||||
|
let escape = call
|
||||||
|
.get_flag(engine_state, stack, "escape")?
|
||||||
|
.map(|v: Value| v.as_char())
|
||||||
|
.transpose()?;
|
||||||
let no_infer = call.has_flag("no-infer");
|
let no_infer = call.has_flag("no-infer");
|
||||||
let noheaders = call.has_flag("noheaders");
|
let noheaders = call.has_flag("noheaders");
|
||||||
let trim: Option<Value> = call.get_flag(engine_state, stack, "trim")?;
|
let flexible = call.has_flag("flexible");
|
||||||
let trim = trim_from_str(trim)?;
|
let trim = trim_from_str(call.get_flag(engine_state, stack, "trim")?)?;
|
||||||
|
|
||||||
from_delimited_data(noheaders, no_infer, '\t', trim, input, name)
|
let config = DelimitedReaderConfig {
|
||||||
|
separator: '\t',
|
||||||
|
comment,
|
||||||
|
quote,
|
||||||
|
escape,
|
||||||
|
noheaders,
|
||||||
|
flexible,
|
||||||
|
no_infer,
|
||||||
|
trim,
|
||||||
|
};
|
||||||
|
|
||||||
|
from_delimited_data(config, input, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -35,10 +35,10 @@ impl Command for FromXml {
|
|||||||
fn extra_usage(&self) -> &str {
|
fn extra_usage(&self) -> &str {
|
||||||
r#"Every XML entry is represented via a record with tag, attribute and content fields.
|
r#"Every XML entry is represented via a record with tag, attribute and content fields.
|
||||||
To represent different types of entries different values are written to this fields:
|
To represent different types of entries different values are written to this fields:
|
||||||
1. Tag entry: {tag: <tag name> attrs: {<attr name>: "<string value>" ...} content: [<entries>]}
|
1. Tag entry: `{tag: <tag name> attrs: {<attr name>: "<string value>" ...} content: [<entries>]}`
|
||||||
2. Comment entry: {tag: '!' attrs: null content: "<comment string>"}
|
2. Comment entry: `{tag: '!' attrs: null content: "<comment string>"}`
|
||||||
3. Processing instruction (PI): {tag: '?<pi name>' attrs: null content: "<pi content string>"}
|
3. Processing instruction (PI): `{tag: '?<pi name>' attrs: null content: "<pi content string>"}`
|
||||||
4. Text: {tag: null attrs: null content: "<text>"}.
|
4. Text: `{tag: null attrs: null content: "<text>"}`.
|
||||||
|
|
||||||
Unlike to xml command all null values are always present and text is never represented via plain
|
Unlike to xml command all null values are always present and text is never represented via plain
|
||||||
string. This way content of every tag is always a table and is easier to parse"#
|
string. This way content of every tag is always a table and is easier to parse"#
|
||||||
@ -141,7 +141,6 @@ fn element_to_value(n: &roxmltree::Node, info: &ParsingInfo) -> Value {
|
|||||||
|
|
||||||
let content: Vec<Value> = n
|
let content: Vec<Value> = n
|
||||||
.children()
|
.children()
|
||||||
.into_iter()
|
|
||||||
.filter_map(|node| from_node_to_value(&node, info))
|
.filter_map(|node| from_node_to_value(&node, info))
|
||||||
.collect();
|
.collect();
|
||||||
let content = Value::list(content, span);
|
let content = Value::list(content, span);
|
||||||
@ -395,7 +394,7 @@ mod tests {
|
|||||||
fn parses_empty_element() -> Result<(), roxmltree::Error> {
|
fn parses_empty_element() -> Result<(), roxmltree::Error> {
|
||||||
let source = "<nu></nu>";
|
let source = "<nu></nu>";
|
||||||
|
|
||||||
assert_eq!(parse(source)?, content_tag("nu", indexmap! {}, &vec![]));
|
assert_eq!(parse(source)?, content_tag("nu", indexmap! {}, &[]));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -409,7 +408,7 @@ mod tests {
|
|||||||
content_tag(
|
content_tag(
|
||||||
"nu",
|
"nu",
|
||||||
indexmap! {},
|
indexmap! {},
|
||||||
&vec![content_string("La era de los tres caballeros")]
|
&[content_string("La era de los tres caballeros")]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -421,7 +420,7 @@ mod tests {
|
|||||||
let source = "\
|
let source = "\
|
||||||
<nu>
|
<nu>
|
||||||
<dev>Andrés</dev>
|
<dev>Andrés</dev>
|
||||||
<dev>Jonathan</dev>
|
<dev>JT</dev>
|
||||||
<dev>Yehuda</dev>
|
<dev>Yehuda</dev>
|
||||||
</nu>";
|
</nu>";
|
||||||
|
|
||||||
@ -431,9 +430,9 @@ mod tests {
|
|||||||
"nu",
|
"nu",
|
||||||
indexmap! {},
|
indexmap! {},
|
||||||
&vec![
|
&vec![
|
||||||
content_tag("dev", indexmap! {}, &vec![content_string("Andrés")]),
|
content_tag("dev", indexmap! {}, &[content_string("Andrés")]),
|
||||||
content_tag("dev", indexmap! {}, &vec![content_string("Jonathan")]),
|
content_tag("dev", indexmap! {}, &[content_string("JT")]),
|
||||||
content_tag("dev", indexmap! {}, &vec![content_string("Yehuda")])
|
content_tag("dev", indexmap! {}, &[content_string("Yehuda")])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -449,7 +448,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(source)?,
|
parse(source)?,
|
||||||
content_tag("nu", indexmap! {"version" => "2.0"}, &vec![])
|
content_tag("nu", indexmap! {"version" => "2.0"}, &[])
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -467,10 +466,10 @@ mod tests {
|
|||||||
content_tag(
|
content_tag(
|
||||||
"nu",
|
"nu",
|
||||||
indexmap! {"version" => "2.0"},
|
indexmap! {"version" => "2.0"},
|
||||||
&vec![content_tag(
|
&[content_tag(
|
||||||
"version",
|
"version",
|
||||||
indexmap! {},
|
indexmap! {},
|
||||||
&vec![content_string("2.0")]
|
&[content_string("2.0")]
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -486,7 +485,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(source)?,
|
parse(source)?,
|
||||||
content_tag("nu", indexmap! {"version" => "2.0", "age" => "25"}, &vec![])
|
content_tag("nu", indexmap! {"version" => "2.0", "age" => "25"}, &[])
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -174,6 +174,30 @@ fn convert_yaml_value_to_nu_value(
|
|||||||
|
|
||||||
Value::from(collected)
|
Value::from(collected)
|
||||||
}
|
}
|
||||||
|
serde_yaml::Value::Tagged(t) => {
|
||||||
|
let tag = &t.tag;
|
||||||
|
let value = match &t.value {
|
||||||
|
serde_yaml::Value::String(s) => {
|
||||||
|
let val = format!("{} {}", tag, s).trim().to_string();
|
||||||
|
Value::String { val, span }
|
||||||
|
}
|
||||||
|
serde_yaml::Value::Number(n) => {
|
||||||
|
let val = format!("{} {}", tag, n).trim().to_string();
|
||||||
|
Value::String { val, span }
|
||||||
|
}
|
||||||
|
serde_yaml::Value::Bool(b) => {
|
||||||
|
let val = format!("{} {}", tag, b).trim().to_string();
|
||||||
|
Value::String { val, span }
|
||||||
|
}
|
||||||
|
serde_yaml::Value::Null => {
|
||||||
|
let val = format!("{}", tag).trim().to_string();
|
||||||
|
Value::String { val, span }
|
||||||
|
}
|
||||||
|
v => convert_yaml_value_to_nu_value(v, span, val_span)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
value
|
||||||
|
}
|
||||||
serde_yaml::Value::Null => Value::nothing(span),
|
serde_yaml::Value::Null => Value::nothing(span),
|
||||||
x => unimplemented!("Unsupported YAML case: {:?}", x),
|
x => unimplemented!("Unsupported YAML case: {:?}", x),
|
||||||
})
|
})
|
||||||
@ -314,4 +338,63 @@ mod test {
|
|||||||
|
|
||||||
test_examples(FromYaml {})
|
test_examples(FromYaml {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_convert_yaml_value_to_nu_value_for_tagged_values() {
|
||||||
|
struct TestCase {
|
||||||
|
input: &'static str,
|
||||||
|
expected: Result<Value, ShellError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let test_cases: Vec<TestCase> = vec![
|
||||||
|
TestCase {
|
||||||
|
input: "Key: !Value ${TEST}-Test-role",
|
||||||
|
expected: Ok(Value::Record {
|
||||||
|
cols: vec!["Key".to_string()],
|
||||||
|
vals: vec![Value::test_string("!Value ${TEST}-Test-role")],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
input: "Key: !Value test-${TEST}",
|
||||||
|
expected: Ok(Value::Record {
|
||||||
|
cols: vec!["Key".to_string()],
|
||||||
|
vals: vec![Value::test_string("!Value test-${TEST}")],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
input: "Key: !Value",
|
||||||
|
expected: Ok(Value::Record {
|
||||||
|
cols: vec!["Key".to_string()],
|
||||||
|
vals: vec![Value::test_string("!Value")],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
input: "Key: !True",
|
||||||
|
expected: Ok(Value::Record {
|
||||||
|
cols: vec!["Key".to_string()],
|
||||||
|
vals: vec![Value::test_string("!True")],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
input: "Key: !123",
|
||||||
|
expected: Ok(Value::Record {
|
||||||
|
cols: vec!["Key".to_string()],
|
||||||
|
vals: vec![Value::test_string("!123")],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for test_case in test_cases {
|
||||||
|
let doc = serde_yaml::Deserializer::from_str(test_case.input);
|
||||||
|
let v: serde_yaml::Value = serde_yaml::Value::deserialize(doc.last().unwrap()).unwrap();
|
||||||
|
let result = convert_yaml_value_to_nu_value(&v, Span::test_data(), Span::test_data());
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert!(result.ok().unwrap() == test_case.expected.ok().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user