Compare commits

..

6 Commits

Author SHA1 Message Date
Justin Ma
682d593d3f Bump patch version (#16236)
Bump patch version
2025-07-29 22:19:36 +02:00
Piepmatz
c4fed5ea84 Don't import IoError on nu-plugin-core without local-socket (#16279)
When you use `nu-plugin-core` without the `local-socket` feature, you
got the warning:
<img width="1182" height="353" alt="image"
src="https://github.com/user-attachments/assets/dd80af11-4963-4d48-8c93-43e6c2dabafa"
/>

This PR fixes that warning.
2025-07-29 22:19:13 +02:00
Stefan Holderbach
27865b9415 Port unsafe_op_in_unsafe_fn fix to FreeBSD (#16275)
Same general idea as https://github.com/nushell/nushell/pull/16266

Fixes the 2024 edition
[`unsafe_op_in_unsafe_fn`](https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html)
lint for FreeBSD as well

Add safety comments to both implementations and an assertion before
`MaybeUninit::assume_init`
2025-07-29 22:19:13 +02:00
pin
400da3c698 Fix #16261 (#16266)
- this PR should close #16261 
- fixes #16261

Confirmed to build with Rust-1.86 and to yield a working binary.
2025-07-29 22:19:13 +02:00
Bahex
3729fcf667 fix(get): run_const uses --optional flag (#16268)
`Get::run_const()` was not update along `Get::run()` in #16007.

Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com>
2025-07-29 22:19:13 +02:00
Bahex
f390ba1aae fix bare interpolation regression (#16235)
Regression from #16204 

Before:

![](f1995bc71f/before.svg)

After:

![](f1995bc71f/after.svg)

# Tests + Formatting
+1

---------

Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com>
2025-07-29 22:19:13 +02:00
329 changed files with 2757 additions and 5889 deletions

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: Question
url: https://github.com/nushell/nushell/discussions/new?category=q-a
about: Create a new Q&A discussion post

21
.github/ISSUE_TEMPLATE/question.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Question
description: "When you have a question to ask"
labels: "question"
body:
- type: textarea
id: problem
attributes:
label: Question
description: Leave your question here
placeholder: |
A clear and concise question
Example: Is there any equivalent of bash's $CDPATH in Nu?
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional context and details
description: Add any other context, screenshots or other media that will help us understand your question here, if needed.
validations:
required: false

View File

@@ -1,16 +1,40 @@
<!--
Thank you for improving Nushell!
Please, read our contributing guide: https://github.com/nushell/nushell/blob/main/CONTRIBUTING.md
if this PR closes one or more issues, you can automatically link the PR with
them by using one of the [*linking keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), e.g.
- this PR should close #xxxx
- fixes #xxxx
you can also mention related issues, PRs or discussions!
-->
## Release notes summary - What our users need to know
# Description
<!--
This section will be included as part of our release notes. See the contributing guide for more details.
Please include only details relevant for users here. Motivation and technical details can be added above or below this section.
Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes.
You may leave this section blank until your PR is finalized. Ask a core team member if you need help filling this section.
Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience.
-->
## Tasks after submitting
<!-- Remove any tasks which aren't relevant for your PR, or add your own -->
- [ ] Update the [documentation](https://github.com/nushell/nushell.github.io)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. -->
# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.
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 clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make sure to [enable developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the tests for the standard library
> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it automatically
> toolkit check pr
> ```
-->
# After Submitting
<!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. -->

View File

@@ -19,7 +19,7 @@ jobs:
# Prevent sudden announcement of a new advisory from failing ci:
continue-on-error: true
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4.1.7
- uses: rustsec/audit-check@v2.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -34,7 +34,7 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- run: rustup update beta

View File

@@ -34,7 +34,7 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
@@ -62,7 +62,7 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
@@ -91,7 +91,7 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
@@ -142,7 +142,7 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
@@ -183,7 +183,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0

View File

@@ -34,7 +34,7 @@ jobs:
nightly_tag: ${{ steps.vars.outputs.nightly_tag }}
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
if: github.repository == 'nushell/nightly'
with:
ref: main
@@ -158,7 +158,7 @@ jobs:
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
@@ -256,7 +256,7 @@ jobs:
needs: [release, sha256sum]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
ref: main

View File

@@ -1,44 +0,0 @@
name: Checks to perform pre-release (manual)
on:
- workflow_dispatch
env:
NUSHELL_CARGO_PROFILE: ci
NU_LOG_LEVEL: DEBUG
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
jobs:
build-and-test:
strategy:
fail-fast: true
matrix:
platform: [windows-latest, macos-latest, ubuntu-22.04]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v5
- uses: taiki-e/install-action@cargo-hack
- name: Feature power set
run: |
cargo hack --all --feature-powerset --at-least-one-of rustls-tls,native-tls --mutually-exclusive-features rustls-tls,native-tls --mutually-exclusive-features rustls-tls,static-link-openssl --skip default-no-clipboard,stable,mimalloc check
# Don't build fully for now as it will run out of disk space
# - name: Build all crates
# run: cargo hack --all build --clean-per-run
- name: Check for clean repo
shell: bash
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "there are changes";
git status --porcelain
exit 1
else
echo "no changes in working directory";
fi

View File

@@ -42,7 +42,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Install Wix Toolset 6 for Windows
shell: pwsh

View File

@@ -93,12 +93,10 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
cargo-build-nu
}
'loongarch64-unknown-linux-gnu' => {
aria2c https://github.com/loongson/build-tools/releases/download/2024.11.01/x86_64-cross-tools-loongarch64-binutils_2.43.1-gcc_14.2.0-glibc_2.40.tar.xz
aria2c https://github.com/loongson/build-tools/releases/download/2024.08.08/x86_64-cross-tools-loongarch64-binutils_2.43-gcc_14.2.0-glibc_2.40.tar.xz
tar xf x86_64-cross-tools-loongarch64-*.tar.xz
$env.PATH = ($env.PATH | split row (char esep) | prepend $'($env.PWD)/cross-tools/bin')
$env.CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER = 'loongarch64-unknown-linux-gnu-gcc'
# Workaround for Rust 1.87 TLS issues: abort strategy to bypass TLS-dependent panic handling
$env.RUSTFLAGS = "-C panic=abort -C target-feature=+crt-static"
cargo-build-nu
}
'loongarch64-unknown-linux-musl' => {
@@ -107,8 +105,6 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
tar -xf loongarch64-linux-musl-cross.tgz
$env.PATH = ($env.PATH | split row (char esep) | prepend $'($env.PWD)/loongarch64-linux-musl-cross/bin')
$env.CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_MUSL_LINKER = "loongarch64-linux-musl-gcc"
# Workaround for Rust 1.87 TLS issues: abort strategy to bypass TLS-dependent panic handling
$env.RUSTFLAGS = "-C panic=abort -C target-feature=+crt-static"
cargo-build-nu
}
_ => {

View File

@@ -67,7 +67,7 @@ jobs:
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Install Wix Toolset 6 for Windows
shell: pwsh

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v5
uses: actions/checkout@v4.1.7
- name: Check spelling
uses: crate-ci/typos@v1.35.5
uses: crate-ci/typos@v1.34.0

View File

@@ -3,7 +3,6 @@
Welcome to Nushell and thank you for considering contributing!
## Table of contents
- [Tips for submitting PRs](#tips-for-submitting-prs)
- [Proposing design changes](#proposing-design-changes)
- [Developing](#developing)
- [Setup](#setup)
@@ -21,75 +20,6 @@ More resources can be found in the nascent [developer documentation](devdocs/REA
- [Platform support policy](devdocs/PLATFORM_SUPPORT.md)
- [Our Rust style](devdocs/rust_style.md)
## Tips for submitting PRs
Thank you for improving Nushell! We love to see other folks enthusiastic about Nushell, and we can help you work through the design or implementation of your PR. Come talk with us in [Discord](https://discordapp.com/invite/NtAbbGn), or create a GitHub discussion or draft PR and we can help you work out the details from there.
**Please talk to the core team before making major changes!** See the [proposing design changes](#proposing-design-changes) for more details.
### Release notes section
In our PR template, we have a "Release notes summary" section which will be included in our release notes for our blog. This section should include all information about your change which is relevant to a user of Nushell.
You should try to keep it **brief and simple to understand**, and focus on the ways your change directly impacts the user experience. Motivation, technical details, and any other **information which isn't directly relevant to users should be put somewhere else** -- either above or below the release notes section is fine.
The release notes summary section doesn't need to be filled until your PR is finalized. Feel free to leave it blank until the PR has undergone review already. Of course, this section can also be useful to demonstrate to reviewers how your proposed changed will work, so don't hesitate to fill in the section if it helps you explain your PR.
Please make sure to consider both the *intended changes*, such as additions or deliberate breaking changes **and** possible *side effects* that might change how users interact with a command or feature. It's important to think carefully about the ways that your PR might affect any aspect of the user experience, and to document these changes even if they seem minor or aren't directly related to the main purpose of the PR.
If you're not sure what to put here, or need some help, **a core team member would be glad to help you out**. We may also make some tweaks to your release notes section. Please don't take it personally, we just want to make sure our release notes are polished and easy to understand. Once the release notes section is finalized, we'll add the `notes:ready` label to indicate that your summary section is ready to be included in the actual release notes.
#### Structuring your summary
For smaller changes, **a simple one to two sentence explanation** is sufficient. If that's all you need to explain the impact of the change, keep any further details out of the release notes summary. Summaries which are a single line will appear as a bullet point in our release notes.
For larger changes, you can use multiple lines to fully explain your change. We highly encourage adding examples and/or screenshots if this is the case.
Summaries which are multiple lines will appear under their own heading in the release notes. By default, the title for the heading will be the title of your PR. If you'd like to add a more user-friendly title, or your PR encompasses multiple changes that should appear in their own sections, you can manually add one or more third-level headings to your release notes summary section. For example:
````md
## Release notes summary - What our users need to know
### Cool command
This change adds a cool new feature. Here's how you use it:
```nushell
my-cool-command
```
````
In the release notes, this will appear under a heading titled simply "Cool command". A concise title like this might be more appropriate than the PR title, which may have more info than is necessary for the release notes.
This section might not be relevant for all PRs. If your PR is a work in progress, feel free to write "WIP"/"TODO"/etc in this section. You can also write "N/A" or simply leave this section blank if this is a purely technical change which doesn't impact the user experience.
### Tests and formatting checks
Our CI system automatically checks formatting and runs our tests. If you're running into an issue, or just want to make sure everything is ready to go before creating your PR, you can run the checks yourself:
```nushell
use toolkit.nu # or use an `env_change` hook to activate it automatically
toolkit check pr
```
Furthermore, you can also runs these checks individually with the subcommands of `toolkit`, or run the underlying commands yourself:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make sure to enable [developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the tests for the standard library
If the checks are passing on your local system, but CI just won't pass, feel free to ask for help from the core team.
### Linking and mentioning issues
If your PR closes one or more issues, you can automatically link the PR with them by using one of the [linking keywords](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword):
- This PR should close #xxxx
- Fixes #xxxx
You can also mention related issues, PRs or discussions!
## Proposing design changes
First of all, before diving into the code, if you want to create a new feature, change something significantly, and especially if the change is user-facing, it is a good practice to first get an approval from the core team before starting to work on it.

378
Cargo.lock generated
View File

@@ -483,7 +483,7 @@ dependencies = [
"hex",
"hmac",
"http 0.2.12",
"http 1.3.1",
"http 1.2.0",
"once_cell",
"percent-encoding",
"sha2",
@@ -578,7 +578,7 @@ dependencies = [
"aws-smithy-types",
"bytes",
"http 0.2.12",
"http 1.3.1",
"http 1.2.0",
"pin-project-lite",
"tokio",
"tracing",
@@ -595,7 +595,7 @@ dependencies = [
"bytes",
"bytes-utils",
"http 0.2.12",
"http 1.3.1",
"http 1.2.0",
"http-body 0.4.6",
"http-body 1.0.1",
"http-body-util",
@@ -815,15 +815,6 @@ dependencies = [
"serde",
]
[[package]]
name = "buf-trait"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21eaafc770e8c073d6c3facafe7617e774305d4954aa6351b9c452eb37ee17b4"
dependencies = [
"zerocopy 0.7.35",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
@@ -887,15 +878,6 @@ version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659"
[[package]]
name = "byteyarn"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93e51d26468a15ea59f8525e0c13dc405db43e644a0b1e6d44346c72cf4cf7b"
dependencies = [
"buf-trait",
]
[[package]]
name = "calamine"
version = "0.28.0"
@@ -940,12 +922,6 @@ dependencies = [
"shlex",
]
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cexpr"
version = "0.6.0"
@@ -1135,16 +1111,6 @@ dependencies = [
"supports-color",
]
[[package]]
name = "combine"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
"bytes",
"memchr",
]
[[package]]
name = "comfy-table"
version = "7.1.3"
@@ -1913,9 +1879,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fancy-regex"
version = "0.16.1"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf04c5ec15464ace8355a7b440a33aece288993475556d461154d7a62ad9947c"
checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"
dependencies = [
"bit-set",
"regex-automata",
@@ -2313,7 +2279,7 @@ dependencies = [
"fnv",
"futures-core",
"futures-sink",
"http 1.3.1",
"http 1.2.0",
"indexmap",
"slab",
"tokio",
@@ -2455,9 +2421,9 @@ dependencies = [
[[package]]
name = "http"
version = "1.3.1"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
dependencies = [
"bytes",
"fnv",
@@ -2482,7 +2448,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http 1.3.1",
"http 1.2.0",
]
[[package]]
@@ -2493,7 +2459,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
dependencies = [
"bytes",
"futures-util",
"http 1.3.1",
"http 1.2.0",
"http-body 1.0.1",
"pin-project-lite",
]
@@ -2563,7 +2529,7 @@ dependencies = [
"futures-channel",
"futures-util",
"h2 0.4.7",
"http 1.3.1",
"http 1.2.0",
"http-body 1.0.1",
"httparse",
"httpdate",
@@ -2597,10 +2563,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
dependencies = [
"futures-util",
"http 1.3.1",
"http 1.2.0",
"hyper 1.5.1",
"hyper-util",
"rustls 0.23.28",
"rustls 0.23.20",
"rustls-native-certs 0.8.1",
"rustls-pki-types",
"tokio",
@@ -2634,7 +2600,7 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
"http 1.3.1",
"http 1.2.0",
"http-body 1.0.1",
"hyper 1.5.1",
"pin-project-lite",
@@ -2988,28 +2954,6 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "jni"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
dependencies = [
"cesu8",
"cfg-if",
"combine",
"jni-sys",
"log",
"thiserror 1.0.69",
"walkdir",
"windows-sys 0.45.0",
]
[[package]]
name = "jni-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.32"
@@ -3066,18 +3010,6 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "lean_string"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75e49998bd00bfb567a44a6d3c3d1a970ce35665b5ebf68d64e5e96410be58ae"
dependencies = [
"castaway",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "libc"
version = "0.2.174"
@@ -3253,9 +3185,9 @@ checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e"
[[package]]
name = "log"
version = "0.4.27"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lru"
@@ -3339,9 +3271,9 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
[[package]]
name = "mach2"
version = "0.4.3"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44"
checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
dependencies = [
"libc",
]
@@ -3505,7 +3437,7 @@ dependencies = [
"assert-json-diff",
"bytes",
"futures-util",
"http 1.3.1",
"http 1.2.0",
"http-body 1.0.1",
"http-body-util",
"hyper 1.5.1",
@@ -3651,7 +3583,7 @@ dependencies = [
[[package]]
name = "nu"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"assert_cmd",
"crossterm",
@@ -3706,7 +3638,7 @@ dependencies = [
[[package]]
name = "nu-cli"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"chrono",
"crossterm",
@@ -3743,7 +3675,7 @@ dependencies = [
[[package]]
name = "nu-cmd-base"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"indexmap",
"miette",
@@ -3755,7 +3687,7 @@ dependencies = [
[[package]]
name = "nu-cmd-extra"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"fancy-regex",
"heck",
@@ -3781,7 +3713,7 @@ dependencies = [
[[package]]
name = "nu-cmd-lang"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"itertools 0.14.0",
"miette",
@@ -3798,7 +3730,7 @@ dependencies = [
[[package]]
name = "nu-cmd-plugin"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"itertools 0.14.0",
"nu-engine",
@@ -3809,7 +3741,7 @@ dependencies = [
[[package]]
name = "nu-color-config"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"nu-ansi-term",
"nu-engine",
@@ -3821,7 +3753,7 @@ dependencies = [
[[package]]
name = "nu-command"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"alphanumeric-sort",
"base64 0.22.1",
@@ -3846,9 +3778,7 @@ dependencies = [
"fancy-regex",
"filesize",
"filetime",
"fuzzy-matcher",
"getrandom 0.2.15",
"http 1.3.1",
"human-date-parser",
"indexmap",
"indicatif",
@@ -3901,7 +3831,7 @@ dependencies = [
"rstest",
"rstest_reuse",
"rusqlite",
"rustls 0.23.28",
"rustls 0.23.20",
"rustls-native-certs 0.8.1",
"scopeguard",
"serde",
@@ -3919,7 +3849,7 @@ dependencies = [
"unicode-segmentation",
"unicode-width 0.2.0",
"update-informer",
"ureq",
"ureq 2.12.1",
"url",
"uu_cp",
"uu_mkdir",
@@ -3941,7 +3871,7 @@ dependencies = [
[[package]]
name = "nu-derive-value"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"heck",
"proc-macro-error2",
@@ -3952,7 +3882,7 @@ dependencies = [
[[package]]
name = "nu-engine"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"fancy-regex",
"log",
@@ -3964,7 +3894,7 @@ dependencies = [
[[package]]
name = "nu-experimental"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"itertools 0.14.0",
"thiserror 2.0.12",
@@ -3972,7 +3902,7 @@ dependencies = [
[[package]]
name = "nu-explore"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"ansi-str",
"anyhow",
@@ -3996,14 +3926,14 @@ dependencies = [
[[package]]
name = "nu-glob"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"doc-comment",
]
[[package]]
name = "nu-json"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"fancy-regex",
"linked-hash-map",
@@ -4019,11 +3949,10 @@ dependencies = [
[[package]]
name = "nu-lsp"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"assert-json-diff",
"crossbeam-channel",
"fancy-regex",
"lsp-server",
"lsp-textdocument",
"lsp-types",
@@ -4047,7 +3976,7 @@ dependencies = [
[[package]]
name = "nu-parser"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"bytesize",
"chrono",
@@ -4064,7 +3993,7 @@ dependencies = [
[[package]]
name = "nu-path"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"dirs",
"omnipath",
@@ -4074,7 +4003,7 @@ dependencies = [
[[package]]
name = "nu-plugin"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"log",
"nix 0.29.0",
@@ -4090,7 +4019,7 @@ dependencies = [
[[package]]
name = "nu-plugin-core"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"interprocess",
"log",
@@ -4104,7 +4033,7 @@ dependencies = [
[[package]]
name = "nu-plugin-engine"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"log",
"nu-engine",
@@ -4120,7 +4049,7 @@ dependencies = [
[[package]]
name = "nu-plugin-protocol"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"nu-protocol",
"nu-utils",
@@ -4132,7 +4061,7 @@ dependencies = [
[[package]]
name = "nu-plugin-test-support"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"nu-ansi-term",
"nu-cmd-lang",
@@ -4150,7 +4079,7 @@ dependencies = [
[[package]]
name = "nu-pretty-hex"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"heapless",
"nu-ansi-term",
@@ -4159,7 +4088,7 @@ dependencies = [
[[package]]
name = "nu-protocol"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"brotli",
"bytes",
@@ -4201,7 +4130,7 @@ dependencies = [
[[package]]
name = "nu-std"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"log",
"miette",
@@ -4212,7 +4141,7 @@ dependencies = [
[[package]]
name = "nu-system"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"chrono",
"itertools 0.14.0",
@@ -4230,7 +4159,7 @@ dependencies = [
[[package]]
name = "nu-table"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"fancy-regex",
"nu-ansi-term",
@@ -4243,7 +4172,7 @@ dependencies = [
[[package]]
name = "nu-term-grid"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"nu-utils",
"unicode-width 0.2.0",
@@ -4251,7 +4180,7 @@ dependencies = [
[[package]]
name = "nu-test-support"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"nu-glob",
"nu-path",
@@ -4263,16 +4192,13 @@ dependencies = [
[[package]]
name = "nu-utils"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"byteyarn",
"crossterm",
"crossterm_winapi",
"fancy-regex",
"lean_string",
"log",
"lscolors",
"memchr",
"nix 0.29.0",
"num-format",
"serde",
@@ -4295,7 +4221,7 @@ dependencies = [
[[package]]
name = "nu_plugin_example"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"nu-cmd-lang",
"nu-plugin",
@@ -4305,7 +4231,7 @@ dependencies = [
[[package]]
name = "nu_plugin_formats"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"chrono",
"eml-parser",
@@ -4320,7 +4246,7 @@ dependencies = [
[[package]]
name = "nu_plugin_gstat"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"git2",
"nu-plugin",
@@ -4329,7 +4255,7 @@ dependencies = [
[[package]]
name = "nu_plugin_inc"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"nu-plugin",
"nu-protocol",
@@ -4338,7 +4264,7 @@ dependencies = [
[[package]]
name = "nu_plugin_polars"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"aws-config",
"aws-credential-types",
@@ -4377,7 +4303,7 @@ dependencies = [
[[package]]
name = "nu_plugin_query"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"gjson",
"nu-plugin",
@@ -4392,7 +4318,7 @@ dependencies = [
[[package]]
name = "nu_plugin_stress_internals"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"interprocess",
"serde",
@@ -4516,7 +4442,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "nuon"
version = "0.106.2"
version = "0.106.1"
dependencies = [
"chrono",
"nu-engine",
@@ -4664,7 +4590,7 @@ dependencies = [
"chrono",
"form_urlencoded",
"futures",
"http 1.3.1",
"http 1.2.0",
"http-body-util",
"humantime",
"hyper 1.5.1",
@@ -4690,9 +4616,9 @@ dependencies = [
[[package]]
name = "oem_cp"
version = "2.1.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da228ac17ec7684952ec38d602222d7ec74c1255b2505b716c9ed621ab0340a"
checksum = "330138902ab4dab09a86e6b7ab7ddeffb5f8435d52fe0df1bce8b06a17b10ee4"
dependencies = [
"phf",
"phf_codegen",
@@ -5893,7 +5819,7 @@ dependencies = [
"quinn-proto",
"quinn-udp",
"rustc-hash 2.1.0",
"rustls 0.23.28",
"rustls 0.23.20",
"socket2",
"thiserror 2.0.12",
"tokio",
@@ -5911,7 +5837,7 @@ dependencies = [
"rand 0.8.5",
"ring",
"rustc-hash 2.1.0",
"rustls 0.23.28",
"rustls 0.23.20",
"rustls-pki-types",
"slab",
"thiserror 2.0.12",
@@ -6051,9 +5977,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.11.0"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
@@ -6061,9 +5987,9 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.13.0"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
@@ -6118,7 +6044,8 @@ dependencies = [
[[package]]
name = "reedline"
version = "0.41.0"
source = "git+https://github.com/nushell/reedline?branch=main#adeb8ea52ff78917bb5f48d8904adf6cbd29bcf9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b627c435d0189363b15f885f1b07193d310ec9e4e39c5627951c6e0f4d02c93a"
dependencies = [
"arboard",
"chrono",
@@ -6210,7 +6137,7 @@ dependencies = [
"futures-core",
"futures-util",
"h2 0.4.7",
"http 1.3.1",
"http 1.2.0",
"http-body 1.0.1",
"http-body-util",
"hyper 1.5.1",
@@ -6226,7 +6153,7 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls 0.23.28",
"rustls 0.23.20",
"rustls-native-certs 0.8.1",
"rustls-pemfile 2.2.0",
"rustls-pki-types",
@@ -6485,15 +6412,15 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.28"
version = "0.23.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643"
checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki 0.103.3",
"rustls-webpki 0.102.8",
"subtle",
"zeroize",
]
@@ -6542,41 +6469,13 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
version = "1.12.0"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
dependencies = [
"web-time",
"zeroize",
]
[[package]]
name = "rustls-platform-verifier"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1"
dependencies = [
"core-foundation 0.10.0",
"core-foundation-sys",
"jni",
"log",
"once_cell",
"rustls 0.23.28",
"rustls-native-certs 0.8.1",
"rustls-platform-verifier-android",
"rustls-webpki 0.103.3",
"security-framework 3.0.1",
"security-framework-sys",
"webpki-root-certs 0.26.11",
"windows-sys 0.59.0",
]
[[package]]
name = "rustls-platform-verifier-android"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
version = "0.101.7"
@@ -6589,9 +6488,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
version = "0.103.3"
version = "0.102.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435"
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
dependencies = [
"ring",
"rustls-pki-types",
@@ -7274,9 +7173,9 @@ dependencies = [
[[package]]
name = "sysinfo"
version = "0.36.1"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d"
checksum = "aab138f5c1bb35231de19049060a87977ad23e04f2303e953bc5c2947ac7dec4"
dependencies = [
"libc",
"memchr",
@@ -7321,9 +7220,9 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.21.0"
version = "3.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e"
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
dependencies = [
"fastrand",
"getrandom 0.3.1",
@@ -7569,7 +7468,7 @@ version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
dependencies = [
"rustls 0.23.28",
"rustls 0.23.20",
"tokio",
]
@@ -7878,30 +7777,49 @@ dependencies = [
"semver",
"serde",
"serde_json",
"ureq",
"ureq 3.0.3",
]
[[package]]
name = "ureq"
version = "3.0.12"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f0fde9bc91026e381155f8c67cb354bcd35260b2f4a29bcc84639f762760c39"
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
dependencies = [
"base64 0.22.1",
"cookie_store",
"der",
"encoding_rs",
"flate2",
"log",
"native-tls",
"percent-encoding",
"rustls 0.23.28",
"rustls-pemfile 2.2.0",
"once_cell",
"rustls 0.23.20",
"rustls-pki-types",
"rustls-platform-verifier",
"serde",
"serde_json",
"socks",
"url",
"webpki-roots 0.26.8",
]
[[package]]
name = "ureq"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "217751151c53226090391713e533d9a5e904ba2570dabaaace29032687589c3e"
dependencies = [
"base64 0.22.1",
"cc",
"cookie_store",
"der",
"flate2",
"log",
"native-tls",
"percent-encoding",
"rustls 0.23.20",
"rustls-pemfile 2.2.0",
"rustls-pki-types",
"serde",
"serde_json",
"ureq-proto",
"utf-8",
"webpki-root-certs 0.26.11",
@@ -7910,12 +7828,12 @@ dependencies = [
[[package]]
name = "ureq-proto"
version = "0.4.2"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59db78ad1923f2b1be62b6da81fe80b173605ca0d57f85da2e005382adf693f7"
checksum = "ae239d0a3341aebc94259414d1dc67cfce87d41cbebc816772c91b77902fafa4"
dependencies = [
"base64 0.22.1",
"http 1.3.1",
"http 1.2.0",
"httparse",
"log",
]
@@ -8687,15 +8605,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
@@ -8723,21 +8632,6 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
@@ -8778,12 +8672,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
@@ -8796,12 +8684,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
@@ -8814,12 +8696,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
@@ -8838,12 +8714,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
@@ -8856,12 +8726,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
@@ -8874,12 +8738,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
@@ -8892,12 +8750,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"

View File

@@ -10,8 +10,8 @@ homepage = "https://www.nushell.sh"
license = "MIT"
name = "nu"
repository = "https://github.com/nushell/nushell"
rust-version = "1.87.0"
version = "0.106.2"
rust-version = "1.86.0"
version = "0.106.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -72,7 +72,6 @@ brotli = "7.0"
byteorder = "1.5"
bytes = "1"
bytesize = "1.3.3"
byteyarn = "0.5"
calamine = "0.28"
chardetng = "0.1.17"
chrono = { default-features = false, version = "0.4.34" }
@@ -84,24 +83,21 @@ csv = "1.3"
ctrlc = "3.4"
devicons = "0.6.12"
dialoguer = { default-features = false, version = "0.11" }
fuzzy-matcher = { version = "^0.3.7" }
digest = { default-features = false, version = "0.10" }
dirs = "5.0"
dirs-sys = "0.4"
dtparse = "2.0"
encoding_rs = "0.8"
fancy-regex = "0.16"
fancy-regex = "0.14"
filesize = "0.2"
filetime = "0.2"
heck = "0.5.0"
http = "1.3.1"
human-date-parser = "0.3.0"
indexmap = "2.10"
indicatif = "0.17"
interprocess = "2.2.0"
is_executable = "1.0"
itertools = "0.14"
lean_string = { version = "0.5", features = ["serde"] }
libc = "0.2"
libproc = "0.14"
log = "0.4"
@@ -124,7 +120,7 @@ nu-ansi-term = "0.50.1"
nucleo-matcher = "0.3"
num-format = "0.4"
num-traits = "0.2"
oem_cp = "2.1.0"
oem_cp = "2.0.0"
omnipath = "0.1"
open = "5.3"
os_pipe = { version = "1.2", features = ["io_safety"] }
@@ -144,7 +140,7 @@ rand = "0.9"
getrandom = "0.2" # pick same version that rand requires
rand_chacha = "0.9"
ratatui = "0.29"
rayon = "1.11"
rayon = "1.10"
reedline = "0.41.0"
rmp = "0.8"
rmp-serde = "1.3"
@@ -153,10 +149,7 @@ rstest = { version = "0.23", default-features = false }
rstest_reuse = "0.7"
rusqlite = "0.31"
rust-embed = "8.7.0"
# We have to fix rustls and ureq versions
# because we use unversioned api to allow users set up their own
# crypto providers (grep for "unversioned")
rustls = { version = "=0.23.28", default-features = false, features = ["std", "tls12"] }
rustls = { version = "0.23", default-features = false, features = ["std", "tls12"] }
rustls-native-certs = "0.8"
scopeguard = { version = "1.2.0" }
serde = { version = "1.0" }
@@ -170,7 +163,7 @@ strum_macros = "0.26"
syn = "2.0"
sysinfo = "0.36"
tabled = { version = "0.20", default-features = false }
tempfile = "3.21"
tempfile = "3.20"
thiserror = "2.0.12"
titlecase = "3.6"
toml = "0.8"
@@ -179,7 +172,7 @@ update-informer = { version = "1.3.0", default-features = false, features = ["gi
umask = "2.1"
unicode-segmentation = "1.12"
unicode-width = "0.2"
ureq = { version = "=3.0.12", default-features = false, features = ["socks-proxy"] }
ureq = { version = "2.12", default-features = false, features = ["socks-proxy"] }
url = "2.2"
uu_cp = "0.0.30"
uu_mkdir = "0.0.30"
@@ -205,29 +198,28 @@ webpki-roots = "1.0"
# todo = "warn"
unchecked_duration_subtraction = "warn"
used_underscore_binding = "warn"
result_large_err = "allow"
[lints]
workspace = true
[dependencies]
nu-cli = { path = "./crates/nu-cli", version = "0.106.2" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.106.2" }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.106.2" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.106.2" }
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.106.2", optional = true }
nu-command = { path = "./crates/nu-command", version = "0.106.2", default-features = false, features = ["os"] }
nu-engine = { path = "./crates/nu-engine", version = "0.106.2" }
nu-experimental = { path = "./crates/nu-experimental", version = "0.106.2" }
nu-explore = { path = "./crates/nu-explore", version = "0.106.2" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.106.2" }
nu-parser = { path = "./crates/nu-parser", version = "0.106.2" }
nu-path = { path = "./crates/nu-path", version = "0.106.2" }
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.106.2" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.106.2" }
nu-std = { path = "./crates/nu-std", version = "0.106.2" }
nu-system = { path = "./crates/nu-system", version = "0.106.2" }
nu-utils = { path = "./crates/nu-utils", version = "0.106.2" }
nu-cli = { path = "./crates/nu-cli", version = "0.106.1" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.106.1" }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.106.1" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.106.1" }
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.106.1", optional = true }
nu-command = { path = "./crates/nu-command", version = "0.106.1", default-features = false, features = ["os"] }
nu-engine = { path = "./crates/nu-engine", version = "0.106.1" }
nu-experimental = { path = "./crates/nu-experimental", version = "0.106.1" }
nu-explore = { path = "./crates/nu-explore", version = "0.106.1" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.106.1" }
nu-parser = { path = "./crates/nu-parser", version = "0.106.1" }
nu-path = { path = "./crates/nu-path", version = "0.106.1" }
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.106.1" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.106.1" }
nu-std = { path = "./crates/nu-std", version = "0.106.1" }
nu-system = { path = "./crates/nu-system", version = "0.106.1" }
nu-utils = { path = "./crates/nu-utils", version = "0.106.1" }
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
crossterm = { workspace = true }
@@ -256,9 +248,9 @@ nix = { workspace = true, default-features = false, features = [
] }
[dev-dependencies]
nu-test-support = { path = "./crates/nu-test-support", version = "0.106.2" }
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.106.2" }
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.106.2" }
nu-test-support = { path = "./crates/nu-test-support", version = "0.106.1" }
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.106.1" }
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.106.1" }
assert_cmd = "2.0"
dirs = { workspace = true }
tango-bench = "0.6"
@@ -345,7 +337,7 @@ bench = false
# To use a development version of a dependency please use a global override here
# changing versions in each sub-crate of the workspace is tedious
[patch.crates-io]
reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
# reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
# Run all benchmarks with `cargo bench`

View File

@@ -1,13 +1,7 @@
# Tango benchmarks
# Divan benchmarks
These are benchmarks using [tango](https://github.com/bazhenov/tango), a benchmark tool which pairs the executions of two versions of the given benchmarks. This claims to reduce the noise from other factors on the system and allow a more reliable comparison of two different implementation.
These are benchmarks using [Divan](https://github.com/nvzqz/divan), a microbenchmarking tool for Rust.
The easiest way to start is to run:
Run all benchmarks with `cargo bench`
```nushell
use toolkit.nu
# To compare the current branch to main
toolkit benchmark-compare
# or to compare a target git-revision against a reference git-revision
toolkit benchmark-compare <target> <reference>
```
Or run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`

View File

@@ -139,30 +139,6 @@ fn bench_load_standard_lib() -> impl IntoBenchmarks {
})]
}
/// Load all modules of standard library into the engine through a general `use`.
fn bench_load_use_standard_lib() -> impl IntoBenchmarks {
[benchmark_fn("load_use_standard_lib", move |b| {
// We need additional commands like `format number` for the standard library
let engine = nu_cmd_extra::add_extra_command_context(setup_engine());
let commands = Spanned {
item: "use std".into(),
span: Span::unknown(),
};
b.iter(move || {
let mut engine = engine.clone();
let mut stack = Stack::new();
let _ = load_standard_library(&mut engine);
evaluate_commands(
&commands,
&mut engine,
&mut stack,
PipelineData::empty(),
Default::default(),
)
})
})]
}
fn create_flat_record_string(n: usize) -> String {
let mut s = String::from("let record = { ");
for i in 0..n {
@@ -461,7 +437,6 @@ fn decode_msgpack(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
tango_benchmarks!(
bench_load_standard_lib(),
bench_load_use_standard_lib(),
// Data types
// Record
bench_record_create(1),

View File

@@ -5,29 +5,29 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2024"
license = "MIT"
name = "nu-cli"
version = "0.106.2"
version = "0.106.1"
[lib]
bench = false
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.2" }
nu-command = { path = "../nu-command", version = "0.106.2" }
nu-std = { path = "../nu-std", version = "0.106.2" }
nu-test-support = { path = "../nu-test-support", version = "0.106.2" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.1" }
nu-command = { path = "../nu-command", version = "0.106.1" }
nu-std = { path = "../nu-std", version = "0.106.1" }
nu-test-support = { path = "../nu-test-support", version = "0.106.1" }
rstest = { workspace = true, default-features = false }
tempfile = { workspace = true }
[dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.2" }
nu-engine = { path = "../nu-engine", version = "0.106.2", features = ["os"] }
nu-glob = { path = "../nu-glob", version = "0.106.2" }
nu-path = { path = "../nu-path", version = "0.106.2" }
nu-parser = { path = "../nu-parser", version = "0.106.2" }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.106.2", optional = true }
nu-protocol = { path = "../nu-protocol", version = "0.106.2", features = ["os"] }
nu-utils = { path = "../nu-utils", version = "0.106.2" }
nu-color-config = { path = "../nu-color-config", version = "0.106.2" }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.1" }
nu-engine = { path = "../nu-engine", version = "0.106.1", features = ["os"] }
nu-glob = { path = "../nu-glob", version = "0.106.1" }
nu-path = { path = "../nu-path", version = "0.106.1" }
nu-parser = { path = "../nu-parser", version = "0.106.1" }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.106.1", optional = true }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", features = ["os"] }
nu-utils = { path = "../nu-utils", version = "0.106.1" }
nu-color-config = { path = "../nu-color-config", version = "0.106.1" }
nu-ansi-term = { workspace = true }
reedline = { workspace = true, features = ["bashisms", "sqlite"] }

View File

@@ -26,11 +26,6 @@ impl Command for CommandlineEdit {
"replaces the current contents of the buffer (default)",
Some('r'),
)
.switch(
"accept",
"immediately executes the result after edit",
Some('A'),
)
.required(
"str",
SyntaxShape::String,
@@ -66,9 +61,6 @@ impl Command for CommandlineEdit {
repl.buffer = str;
repl.cursor_pos = repl.buffer.len();
}
repl.accept = call.has_flag(engine_state, stack, "accept")?;
Ok(Value::nothing(call.head).into_pipeline_data())
}
}

View File

@@ -49,7 +49,7 @@ impl Command for History {
};
// todo for sqlite history this command should be an alias to `open ~/.config/nushell/history.sqlite3 | get history`
let Some(history_path) = history.file_path() else {
return Err(ShellError::ConfigDirNotFound { span: head });
return Err(ShellError::ConfigDirNotFound { span: Some(head) });
};
if call.has_flag(engine_state, stack, "clear")? {

View File

@@ -78,7 +78,7 @@ Note that history item IDs are ignored when importing from file."#
return ok;
};
let Some(current_history_path) = history.file_path() else {
return Err(ShellError::ConfigDirNotFound { span });
return Err(ShellError::ConfigDirNotFound { span: span.into() });
};
if let Some(bak_path) = backup(&current_history_path, span)? {
println!("Backed history to {}", bak_path.display());
@@ -89,9 +89,8 @@ Note that history item IDs are ignored when importing from file."#
HistoryFileFormat::Sqlite => HistoryFileFormat::Plaintext,
HistoryFileFormat::Plaintext => HistoryFileFormat::Sqlite,
};
let src = new_backend(other_format, None, call.head)?;
let mut dst =
new_backend(history.file_format, Some(current_history_path), call.head)?;
let src = new_backend(other_format, None)?;
let mut dst = new_backend(history.file_format, Some(current_history_path))?;
let items = src
.search(SearchQuery::everything(
reedline::SearchDirection::Forward,
@@ -105,8 +104,7 @@ Note that history item IDs are ignored when importing from file."#
_ => {
let input = input.into_iter().map(item_from_value);
import(
new_backend(history.file_format, Some(current_history_path), call.head)?
.as_mut(),
new_backend(history.file_format, Some(current_history_path))?.as_mut(),
input,
)
}
@@ -119,13 +117,12 @@ Note that history item IDs are ignored when importing from file."#
fn new_backend(
format: HistoryFileFormat,
path: Option<PathBuf>,
span: Span,
) -> Result<Box<dyn History>, ShellError> {
let path = match path {
Some(path) => path,
None => {
let Some(mut path) = nu_path::nu_config_dir() else {
return Err(ShellError::ConfigDirNotFound { span });
return Err(ShellError::ConfigDirNotFound { span: None });
};
path.push(format.default_file_name());
path.into_std_path_buf()

View File

@@ -348,43 +348,8 @@ impl NuCompleter {
for (arg_idx, arg) in call.arguments.iter().enumerate() {
let span = arg.span();
if span.contains(pos) {
// Get custom completion from PositionalArg or Flag
let custom_completion_decl_id = {
// Check PositionalArg or Flag from Signature
let signature = working_set.get_decl(call.decl_id).signature();
match arg {
// For named arguments, check Flag
Argument::Named((name, short, value)) => {
if value.as_ref().is_none_or(|e| !e.span.contains(pos)) {
None
} else {
// If we're completing the value of the flag,
// search for the matching custom completion decl_id (long or short)
let flag =
signature.get_long_flag(&name.item).or_else(|| {
short.as_ref().and_then(|s| {
signature.get_short_flag(
s.item.chars().next().unwrap_or('_'),
)
})
});
flag.and_then(|f| f.custom_completion)
}
}
// For positional arguments, check PositionalArg
Argument::Positional(_) => {
// Find the right positional argument by index
let arg_pos = positional_arg_indices.len();
signature
.get_positional(arg_pos)
.and_then(|pos_arg| pos_arg.custom_completion)
}
_ => None,
}
};
if let Some(decl_id) = custom_completion_decl_id {
// if customized completion specified, it has highest priority
if let Some(decl_id) = arg.expr().and_then(|e| e.custom_completion) {
// for `--foo <tab>` and `--foo=<tab>`, the arg span should be trimmed
let (new_span, prefix) = if matches!(arg, Argument::Named(_)) {
strip_placeholder_with_rsplit(
@@ -753,7 +718,7 @@ impl NuCompleter {
Ok(value) => {
log::error!(
"External completer returned invalid value of type {}",
value.get_type()
value.get_type().to_string()
);
Some(vec![])
}

View File

@@ -140,7 +140,7 @@ impl<T: Completer> Completer for CustomCompletion<T> {
_ => {
log::error!(
"Custom completer returned invalid value of type {}",
value.get_type()
value.get_type().to_string()
);
return vec![];
}

View File

@@ -278,7 +278,7 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState) -> bool {
&mut stack,
&old_contents,
&old_plugin_file_path.to_string_lossy(),
PipelineData::empty(),
PipelineData::Empty,
false,
) != 0
{

View File

@@ -26,23 +26,14 @@ pub fn evaluate_file(
) -> Result<(), ShellError> {
let cwd = engine_state.cwd_as_string(Some(stack))?;
let file_path = {
match canonicalize_with(&path, cwd) {
Ok(t) => Ok(t),
Err(err) => {
let cmdline = format!("nu {path} {}", args.join(" "));
let mut working_set = StateWorkingSet::new(engine_state);
let file_id = working_set.add_file("<commandline>".into(), cmdline.as_bytes());
let span = working_set
.get_span_for_file(file_id)
.subspan(3, path.len() + 3)
.expect("<commandline> to contain script path");
engine_state.merge_delta(working_set.render())?;
let e = IoError::new(err.not_found_as(NotFound::File), span, PathBuf::from(&path));
Err(e)
}
}
}?;
let file_path = canonicalize_with(&path, cwd).map_err(|err| {
IoError::new_internal_with_path(
err.not_found_as(NotFound::File),
"Could not access file",
nu_protocol::location!(),
PathBuf::from(&path),
)
})?;
let file_path_str = file_path
.to_str()
@@ -91,7 +82,7 @@ pub fn evaluate_file(
.expect("internal error: missing filename");
let mut working_set = StateWorkingSet::new(engine_state);
trace!("parsing file: {file_path_str}");
trace!("parsing file: {}", file_path_str);
let block = parse(&mut working_set, Some(file_path_str), &file, false);
if let Some(warning) = working_set.parse_warnings.first() {

View File

@@ -61,7 +61,7 @@ fn get_prompt_string(
.and_then(|v| match v {
Value::Closure { val, .. } => {
let result = ClosureEvalOnce::new(engine_state, stack, val.as_ref().clone())
.run_with_input(PipelineData::empty());
.run_with_input(PipelineData::Empty);
trace!(
"get_prompt_string (block) {}:{}:{}",
@@ -76,7 +76,7 @@ fn get_prompt_string(
})
.ok()
}
Value::String { .. } => Some(PipelineData::value(v.clone(), None)),
Value::String { .. } => Some(PipelineData::Value(v.clone(), None)),
_ => None,
})
.and_then(|pipeline_data| {

View File

@@ -159,7 +159,7 @@ pub(crate) fn add_menus(
engine_state.merge_delta(delta)?;
let mut temp_stack = Stack::new().collect_value();
let input = PipelineData::empty();
let input = PipelineData::Empty;
menu_eval_results.push(eval_block::<WithoutDebug>(
&engine_state,
&mut temp_stack,
@@ -1047,10 +1047,6 @@ fn event_from_record(
ReedlineEvent::ExecuteHostCommand(cmd.to_expanded_string("", config))
}
"openeditor" => ReedlineEvent::OpenEditor,
"vichangemode" => {
let mode = extract_value("mode", record, span)?;
ReedlineEvent::ViChangeMode(mode.as_str()?.to_owned())
}
str => {
return Err(ShellError::InvalidValue {
valid: "a reedline event".into(),

View File

@@ -325,19 +325,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
perf!("reset signals", start_time, use_color);
start_time = std::time::Instant::now();
// Check all the environment variables they ask for
// fire the "env_change" hook
if let Err(error) = hook::eval_env_change_hook(
&engine_state.get_config().hooks.env_change.clone(),
engine_state,
&mut stack,
) {
report_shell_error(engine_state, &error)
}
perf!("env-change hook", start_time, use_color);
start_time = std::time::Instant::now();
// Next, right before we start our prompt and take input from the user, fire the "pre_prompt" hook
// Right before we start our prompt and take input from the user, fire the "pre_prompt" hook
if let Err(err) = hook::eval_hooks(
engine_state,
&mut stack,
@@ -349,6 +337,18 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
}
perf!("pre-prompt hook", start_time, use_color);
start_time = std::time::Instant::now();
// Next, check all the environment variables they ask for
// fire the "env_change" hook
if let Err(error) = hook::eval_env_change_hook(
&engine_state.get_config().hooks.env_change.clone(),
engine_state,
&mut stack,
) {
report_shell_error(engine_state, &error)
}
perf!("env-change hook", start_time, use_color);
let engine_reference = Arc::new(engine_state.clone());
let config = stack.get_config(engine_state);
@@ -450,7 +450,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
start_time = std::time::Instant::now();
if history.sync_on_enter {
if let Err(e) = line_editor.sync_history() {
warn!("Failed to sync history: {e}");
warn!("Failed to sync history: {}", e);
}
}
@@ -491,9 +491,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
// CLEAR STACK-REFERENCE 1
.with_highlighter(Box::<NoOpHighlighter>::default())
// CLEAR STACK-REFERENCE 2
.with_completer(Box::<DefaultCompleter>::default())
// Ensure immediately accept is always cleared
.with_immediately_accept(false);
.with_completer(Box::<DefaultCompleter>::default());
// Let's grab the shell_integration configs
let shell_integration_osc2 = config.shell_integration.osc2;
@@ -673,7 +671,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
run_shell_integration_reset_application_mode();
}
line_editor = flush_engine_state_repl_buffer(engine_state, line_editor);
flush_engine_state_repl_buffer(engine_state, &mut line_editor);
}
Ok(Signal::CtrlC) => {
// `Reedline` clears the line content. New prompt is shown
@@ -936,7 +934,7 @@ fn do_run_cmd(
entry_num: usize,
use_color: bool,
) -> Reedline {
trace!("eval source: {s}");
trace!("eval source: {}", s);
let mut cmds = s.split_whitespace();
@@ -1128,10 +1126,7 @@ fn run_shell_integration_reset_application_mode() {
///
/// Clear the screen and output anything remaining in the EngineState buffer.
///
fn flush_engine_state_repl_buffer(
engine_state: &mut EngineState,
mut line_editor: Reedline,
) -> Reedline {
fn flush_engine_state_repl_buffer(engine_state: &mut EngineState, line_editor: &mut Reedline) {
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
line_editor.run_edit_commands(&[
EditCommand::Clear,
@@ -1141,13 +1136,8 @@ fn flush_engine_state_repl_buffer(
select: false,
},
]);
if repl.accept {
line_editor = line_editor.with_immediately_accept(true)
}
repl.accept = false;
repl.buffer = "".to_string();
repl.cursor_pos = 0;
line_editor
}
///

View File

@@ -23,22 +23,6 @@ impl Highlighter for NuHighlighter {
}
}
// <<<<<<< HEAD
// =======
// let config = self.stack.get_config(&self.engine_state);
// let highlight_resolved_externals = config.highlight_resolved_externals;
// let mut working_set = StateWorkingSet::new(&self.engine_state);
// let block = parse(&mut working_set, None, line.as_bytes(), false);
// let (shapes, global_span_offset) = {
// let mut shapes = flatten_block(&working_set, &block);
// // Highlighting externals has a config point because of concerns that using which to resolve
// // externals may slow down things too much.
// if highlight_resolved_externals {
// for (span, shape) in shapes.iter_mut() {
// if let FlatShape::External(aliased_command_span) = *shape {
// let str_contents = working_set.get_span_contents(aliased_command_span);
// >>>>>>> df798b657 (Fix highlighting of aliases to external commands)
/// Result of a syntax highlight operation
#[derive(Default)]
pub(crate) struct HighlightResult {
@@ -54,7 +38,7 @@ pub(crate) fn highlight_syntax(
line: &str,
cursor: usize,
) -> HighlightResult {
trace!("highlighting: {line}");
trace!("highlighting: {}", line);
let config = stack.get_config(engine_state);
let highlight_resolved_externals = config.highlight_resolved_externals;
@@ -66,8 +50,10 @@ pub(crate) fn highlight_syntax(
// externals may slow down things too much.
if highlight_resolved_externals {
for (span, shape) in shapes.iter_mut() {
if let FlatShape::External(aliased_command_span) = shape {
let str_contents = working_set.get_span_contents(**aliased_command_span);
if *shape == FlatShape::External {
let str_contents =
working_set.get_span_contents(Span::new(span.start, span.end));
let str_word = String::from_utf8_lossy(str_contents).to_string();
let paths = env::path_str(engine_state, stack, *span).ok();
let res = if let Ok(cwd) = engine_state.cwd(Some(stack)) {
@@ -140,7 +126,7 @@ pub(crate) fn highlight_syntax(
FlatShape::Float => 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::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::ExternalResolved => add_colored_token(&shape.1, next_token),
FlatShape::Keyword => add_colored_token(&shape.1, next_token),

View File

@@ -97,11 +97,8 @@ fn extern_completer() -> NuCompleter {
// Add record value as example
let record = r#"
def animals [] { [ "cat", "dog", "eel" ] }
def fruits [] { [ "apple", "banana" ] }
extern spam [
animal: string@animals
fruit?: string@fruits
...rest: string@animals
--foo (-f): string@animals
-b: string@animals
]
@@ -1563,7 +1560,9 @@ fn flag_completions() {
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
// Test completions for the 'ls' flags
let suggestions = completer.complete("ls -", 4);
assert_eq!(18, suggestions.len());
let expected: Vec<_> = vec![
"--all",
"--directory",
@@ -1584,12 +1583,9 @@ fn flag_completions() {
"-s",
"-t",
];
// Match results
match_suggestions(&expected, &suggestions);
// https://github.com/nushell/nushell/issues/16375
let suggestions = completer.complete("table -", 7);
assert_eq!(20, suggestions.len());
}
#[test]
@@ -2141,8 +2137,7 @@ fn run_external_completion_within_pwd(
assert!(engine_state.merge_delta(delta).is_ok());
assert!(
eval_block::<WithoutDebug>(&engine_state, &mut stack, &block, PipelineData::empty())
.is_ok()
eval_block::<WithoutDebug>(&engine_state, &mut stack, &block, PipelineData::Empty).is_ok()
);
// Merge environment into the permanent state
@@ -2266,22 +2261,6 @@ fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn extern_custom_completion_optional(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam foo -f bar ", 16);
let expected: Vec<_> = vec!["apple", "banana"];
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn extern_custom_completion_rest(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam foo -f bar baz ", 20);
let expected: Vec<_> = vec!["cat", "dog", "eel"];
match_suggestions(&expected, &suggestions);
let suggestions = extern_completer.complete("spam foo -f bar baz qux ", 24);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam --foo=", 11);
@@ -2310,17 +2289,6 @@ fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
match_suggestions(&expected, &suggestions);
}
/// When we're completing the flag name itself, not its value,
/// custom completions should not be used
#[rstest]
fn custom_completion_flag_name_not_value(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam --f", 8);
match_suggestions(&vec!["--foo"], &suggestions);
// Also test with partial short flag
let suggestions = extern_completer.complete("spam -f", 7);
match_suggestions(&vec!["-f"], &suggestions);
}
#[rstest]
fn extern_complete_flags(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -", 6);

View File

@@ -199,7 +199,7 @@ pub fn merge_input(
engine_state,
stack,
&block,
PipelineData::value(Value::nothing(Span::unknown()), None),
PipelineData::Value(Value::nothing(Span::unknown()), None),
)
.is_ok()
);

View File

@@ -5,7 +5,7 @@ edition = "2024"
license = "MIT"
name = "nu-cmd-base"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
version = "0.106.2"
version = "0.106.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -13,10 +13,10 @@ version = "0.106.2"
workspace = true
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.106.2", default-features = false }
nu-parser = { path = "../nu-parser", version = "0.106.2" }
nu-path = { path = "../nu-path", version = "0.106.2" }
nu-protocol = { path = "../nu-protocol", version = "0.106.2", default-features = false }
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
nu-parser = { path = "../nu-parser", version = "0.106.1" }
nu-path = { path = "../nu-path", version = "0.106.1" }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
indexmap = { workspace = true }
miette = { workspace = true }

View File

@@ -12,7 +12,7 @@ use nu_protocol::{
/// ```rust
/// # use nu_engine::command_prelude::*;
/// # use nu_cmd_base::WrapCall;
/// # fn do_command_logic(call: WrapCall) -> Result<PipelineData, ShellError> { Ok(PipelineData::empty()) }
/// # fn do_command_logic(call: WrapCall) -> Result<PipelineData, ShellError> { Ok(PipelineData::Empty) }
///
/// # struct Command {}
/// # impl Command {

View File

@@ -5,7 +5,7 @@ edition = "2024"
license = "MIT"
name = "nu-cmd-extra"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
version = "0.106.2"
version = "0.106.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -16,13 +16,13 @@ bench = false
workspace = true
[dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.2" }
nu-engine = { path = "../nu-engine", version = "0.106.2", default-features = false }
nu-json = { version = "0.106.2", path = "../nu-json" }
nu-parser = { path = "../nu-parser", version = "0.106.2" }
nu-pretty-hex = { version = "0.106.2", path = "../nu-pretty-hex" }
nu-protocol = { path = "../nu-protocol", version = "0.106.2", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.106.2", default-features = false }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.1" }
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
nu-json = { version = "0.106.1", path = "../nu-json" }
nu-parser = { path = "../nu-parser", version = "0.106.1" }
nu-pretty-hex = { version = "0.106.1", path = "../nu-pretty-hex" }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.106.1", default-features = false }
# Potential dependencies for extras
heck = { workspace = true }
@@ -37,6 +37,6 @@ itertools = { workspace = true }
mime = { workspace = true }
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.2" }
nu-command = { path = "../nu-command", version = "0.106.2" }
nu-test-support = { path = "../nu-test-support", version = "0.106.2" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.1" }
nu-command = { path = "../nu-command", version = "0.106.1" }
nu-test-support = { path = "../nu-test-support", version = "0.106.1" }

View File

@@ -3,12 +3,7 @@ use nu_protocol::engine::Command;
#[cfg(test)]
pub fn test_examples(cmd: impl Command + 'static) {
test_examples::test_examples(cmd, &[]);
}
#[cfg(test)]
pub fn test_examples_with_commands(cmd: impl Command + 'static, commands: &[&dyn Command]) {
test_examples::test_examples(cmd, commands);
test_examples::test_examples(cmd);
}
#[cfg(test)]
@@ -26,10 +21,10 @@ mod test_examples {
};
use std::collections::HashSet;
pub fn test_examples(cmd: impl Command + 'static, commands: &[&dyn Command]) {
pub fn test_examples(cmd: impl Command + 'static) {
let examples = cmd.examples();
let signature = cmd.signature();
let mut engine_state = make_engine_state(cmd.clone_box(), commands);
let mut engine_state = make_engine_state(cmd.clone_box());
let cwd = std::env::current_dir().expect("Could not get current working directory.");
@@ -43,7 +38,7 @@ mod test_examples {
check_example_input_and_output_types_match_command_signature(
&example,
&cwd,
&mut make_engine_state(cmd.clone_box(), commands),
&mut make_engine_state(cmd.clone_box()),
&signature.input_output_types,
signature.operates_on_cell_paths(),
),
@@ -62,7 +57,7 @@ mod test_examples {
);
}
fn make_engine_state(cmd: Box<dyn Command>, commands: &[&dyn Command]) -> Box<EngineState> {
fn make_engine_state(cmd: Box<dyn Command>) -> Box<EngineState> {
let mut engine_state = Box::new(EngineState::new());
let delta = {
@@ -74,10 +69,6 @@ mod test_examples {
working_set.add_decl(Box::new(nu_cmd_lang::If));
working_set.add_decl(Box::new(nu_command::MathRound));
for command in commands {
working_set.add_decl(command.clone_box());
}
// Adding the command that is being tested to the working set
working_set.add_decl(cmd);
working_set.render()

View File

@@ -72,7 +72,7 @@ impl Command for EachWhile {
let metadata = input.metadata();
match input {
PipelineData::Empty => Ok(PipelineData::empty()),
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::Value(Value::Range { .. }, ..)
| PipelineData::Value(Value::List { .. }, ..)
| PipelineData::ListStream(..) => {
@@ -109,7 +109,7 @@ impl Command for EachWhile {
.fuse()
.into_pipeline_data(head, engine_state.signals().clone()))
} else {
Ok(PipelineData::empty())
Ok(PipelineData::Empty)
}
}
// This match allows non-iterables to be accepted,

View File

@@ -55,7 +55,7 @@ fn from_url(input: PipelineData, head: Span) -> Result<PipelineData, ShellError>
.map(|(k, v)| (k, Value::string(v, head)))
.collect();
Ok(PipelineData::value(Value::record(record, head), metadata))
Ok(PipelineData::Value(Value::record(record, head), metadata))
}
_ => Err(ShellError::UnsupportedInput {
msg: "String not compatible with URL encoding".to_string(),

View File

@@ -109,26 +109,18 @@ impl Command for ToHtml {
"produce a color table of all available themes",
Some('l'),
)
.switch("raw", "do not escape html tags", Some('r'))
.category(Category::Formats)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Outputs an HTML string representing the contents of this table",
description: "Outputs an HTML string representing the contents of this table",
example: "[[foo bar]; [1 2]] | to html",
result: Some(Value::test_string(
r#"<html><style>body { background-color:white;color:black; }</style><body><table><thead><tr><th>foo</th><th>bar</th></tr></thead><tbody><tr><td>1</td><td>2</td></tr></tbody></table></body></html>"#,
)),
},
Example {
description: "Outputs an HTML string using a record of xml data",
example: r#"{tag: a attributes: { style: "color: red" } content: ["hello!"] } | to xml | to html --raw"#,
result: Some(Value::test_string(
r#"<html><style>body { background-color:white;color:black; }</style><body><a style="color: red">hello!</a></body></html>"#,
)),
},
Example {
description: "Optionally, only output the html for the content itself",
example: "[[foo bar]; [1 2]] | to html --partial",
@@ -262,7 +254,6 @@ fn to_html(
let dark = call.has_flag(engine_state, stack, "dark")?;
let partial = call.has_flag(engine_state, stack, "partial")?;
let list = call.has_flag(engine_state, stack, "list")?;
let raw = call.has_flag(engine_state, stack, "raw")?;
let theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?;
let config = &stack.get_config(engine_state);
@@ -328,15 +319,15 @@ fn to_html(
let inner_value = match vec_of_values.len() {
0 => String::default(),
1 => match headers {
Some(headers) => html_table(vec_of_values, headers, raw, config),
Some(headers) => html_table(vec_of_values, headers, config),
None => {
let value = &vec_of_values[0];
html_value(value.clone(), raw, config)
html_value(value.clone(), config)
}
},
_ => match headers {
Some(headers) => html_table(vec_of_values, headers, raw, config),
None => html_list(vec_of_values, raw, config),
Some(headers) => html_table(vec_of_values, headers, config),
None => html_list(vec_of_values, config),
},
};
@@ -404,19 +395,19 @@ fn theme_demo(span: Span) -> PipelineData {
})
}
fn html_list(list: Vec<Value>, raw: bool, config: &Config) -> String {
fn html_list(list: Vec<Value>, config: &Config) -> String {
let mut output_string = String::new();
output_string.push_str("<ol>");
for value in list {
output_string.push_str("<li>");
output_string.push_str(&html_value(value, raw, config));
output_string.push_str(&html_value(value, config));
output_string.push_str("</li>");
}
output_string.push_str("</ol>");
output_string
}
fn html_table(table: Vec<Value>, headers: Vec<String>, raw: bool, config: &Config) -> String {
fn html_table(table: Vec<Value>, headers: Vec<String>, config: &Config) -> String {
let mut output_string = String::new();
output_string.push_str("<table>");
@@ -439,7 +430,7 @@ fn html_table(table: Vec<Value>, headers: Vec<String>, raw: bool, config: &Confi
.cloned()
.unwrap_or_else(|| Value::nothing(span));
output_string.push_str("<td>");
output_string.push_str(&html_value(data, raw, config));
output_string.push_str(&html_value(data, config));
output_string.push_str("</td>");
}
output_string.push_str("</tr>");
@@ -450,7 +441,7 @@ fn html_table(table: Vec<Value>, headers: Vec<String>, raw: bool, config: &Confi
output_string
}
fn html_value(value: Value, raw: bool, config: &Config) -> String {
fn html_value(value: Value, config: &Config) -> String {
let mut output_string = String::new();
match value {
Value::Binary { val, .. } => {
@@ -459,22 +450,11 @@ fn html_value(value: Value, raw: bool, config: &Config) -> String {
output_string.push_str(&output);
output_string.push_str("</pre>");
}
other => {
if raw {
output_string.push_str(
&other
.to_abbreviated_string(config)
.to_string()
.replace('\n', "<br>"),
)
} else {
output_string.push_str(
&v_htmlescape::escape(&other.to_abbreviated_string(config))
.to_string()
.replace('\n', "<br>"),
)
}
}
other => output_string.push_str(
&v_htmlescape::escape(&other.to_abbreviated_string(config))
.to_string()
.replace('\n', "<br>"),
),
}
output_string
}
@@ -737,10 +717,9 @@ mod tests {
#[test]
fn test_examples() {
use crate::test_examples_with_commands;
use nu_command::ToXml;
use crate::test_examples;
test_examples_with_commands(ToHtml {}, &[&ToXml])
test_examples(ToHtml {})
}
#[test]

View File

@@ -75,11 +75,6 @@ impl Command for FormatBits {
example: "1 | format bits",
result: Some(Value::string("00000001", Span::test_data())),
},
Example {
description: "convert an int into a string, padded to 8 places with 0s (big endian)",
example: "258 | format bits",
result: Some(Value::string("00000001 00000010", Span::test_data())),
},
Example {
description: "convert a filesize value into a string, padded to 8 places with 0s",
example: "1b | format bits",
@@ -118,7 +113,7 @@ fn format_bits(
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
if let PipelineData::ByteStream(stream, metadata) = input {
Ok(PipelineData::byte_stream(
Ok(PipelineData::ByteStream(
byte_stream_to_bits(stream, head),
metadata,
))
@@ -163,28 +158,28 @@ fn byte_stream_to_bits(stream: ByteStream, head: Span) -> ByteStream {
fn convert_to_smallest_number_type(num: i64, span: Span) -> Value {
if let Some(v) = num.to_i8() {
let bytes = v.to_be_bytes();
let bytes = v.to_ne_bytes();
let mut raw_string = "".to_string();
for ch in bytes {
raw_string.push_str(&format!("{ch:08b} "));
}
Value::string(raw_string.trim(), span)
} else if let Some(v) = num.to_i16() {
let bytes = v.to_be_bytes();
let bytes = v.to_ne_bytes();
let mut raw_string = "".to_string();
for ch in bytes {
raw_string.push_str(&format!("{ch:08b} "));
}
Value::string(raw_string.trim(), span)
} else if let Some(v) = num.to_i32() {
let bytes = v.to_be_bytes();
let bytes = v.to_ne_bytes();
let mut raw_string = "".to_string();
for ch in bytes {
raw_string.push_str(&format!("{ch:08b} "));
}
Value::string(raw_string.trim(), span)
} else {
let bytes = num.to_be_bytes();
let bytes = num.to_ne_bytes();
let mut raw_string = "".to_string();
for ch in bytes {
raw_string.push_str(&format!("{ch:08b} "));

View File

@@ -191,7 +191,7 @@ fn format(
// We can only handle a Record or a List of Records
match data_as_value {
Value::Record { .. } => match format_record(format_operations, &data_as_value, config) {
Ok(value) => Ok(PipelineData::value(Value::string(value, head_span), None)),
Ok(value) => Ok(PipelineData::Value(Value::string(value, head_span), None)),
Err(value) => Err(value),
},

View File

@@ -4,4 +4,4 @@ pub mod extra;
pub use extra::*;
#[cfg(test)]
pub use example_test::{test_examples, test_examples_with_commands};
pub use example_test::test_examples;

View File

@@ -6,7 +6,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
edition = "2024"
license = "MIT"
name = "nu-cmd-lang"
version = "0.106.2"
version = "0.106.1"
[lib]
bench = false
@@ -15,12 +15,12 @@ bench = false
workspace = true
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.106.2", default-features = false }
nu-experimental = { path = "../nu-experimental", version = "0.106.2" }
nu-parser = { path = "../nu-parser", version = "0.106.2" }
nu-protocol = { path = "../nu-protocol", version = "0.106.2", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.106.2", default-features = false }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.2" }
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
nu-experimental = { path = "../nu-experimental", version = "0.106.1" }
nu-parser = { path = "../nu-parser", version = "0.106.1" }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.106.1", default-features = false }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.1" }
itertools = { workspace = true }
shadow-rs = { version = "1.2", default-features = false }

View File

@@ -83,7 +83,7 @@ impl Command for Do {
#[cfg(not(feature = "os"))]
return Err(ShellError::DisabledOsSupport {
msg: "Cannot create a thread to receive stdout message.".to_string(),
span,
span: Some(span),
});
#[cfg(feature = "os")]
@@ -157,12 +157,12 @@ impl Command for Do {
if !stderr_msg.is_empty() {
child.stderr = Some(ChildPipe::Tee(Box::new(Cursor::new(stderr_msg))));
}
Ok(PipelineData::byte_stream(
Ok(PipelineData::ByteStream(
ByteStream::child(child, span),
metadata,
))
}
Err(stream) => Ok(PipelineData::byte_stream(stream, metadata)),
Err(stream) => Ok(PipelineData::ByteStream(stream, metadata)),
}
}
Ok(PipelineData::ByteStream(mut stream, metadata))
@@ -176,7 +176,7 @@ impl Command for Do {
if let ByteStreamSource::Child(child) = stream.source_mut() {
child.ignore_error(true);
}
Ok(PipelineData::byte_stream(stream, metadata))
Ok(PipelineData::ByteStream(stream, metadata))
}
Ok(PipelineData::Value(Value::Error { .. }, ..)) | Err(_) if ignore_all_errors => {
Ok(PipelineData::empty())
@@ -189,7 +189,7 @@ impl Command for Do {
value
}
});
Ok(PipelineData::list_stream(stream, metadata))
Ok(PipelineData::ListStream(stream, metadata))
}
r => r,
}

View File

@@ -5,7 +5,7 @@ edition = "2024"
license = "MIT"
name = "nu-cmd-plugin"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-plugin"
version = "0.106.2"
version = "0.106.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -13,10 +13,10 @@ version = "0.106.2"
workspace = true
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.106.2" }
nu-path = { path = "../nu-path", version = "0.106.2" }
nu-protocol = { path = "../nu-protocol", version = "0.106.2", features = ["plugin"] }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.106.2" }
nu-engine = { path = "../nu-engine", version = "0.106.1" }
nu-path = { path = "../nu-path", version = "0.106.1" }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", features = ["plugin"] }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.106.1" }
itertools = { workspace = true }

View File

@@ -66,7 +66,7 @@ impl Command for PluginStop {
}
if found {
Ok(PipelineData::empty())
Ok(PipelineData::Empty)
} else {
Err(ShellError::GenericError {
error: format!("Failed to stop the `{}` plugin", name.item),

View File

@@ -5,7 +5,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
edition = "2024"
license = "MIT"
name = "nu-color-config"
version = "0.106.2"
version = "0.106.1"
[lib]
bench = false
@@ -14,12 +14,12 @@ bench = false
workspace = true
[dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.106.2", default-features = false }
nu-engine = { path = "../nu-engine", version = "0.106.2", default-features = false }
nu-json = { path = "../nu-json", version = "0.106.2" }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
nu-json = { path = "../nu-json", version = "0.106.1" }
nu-ansi-term = { workspace = true }
serde = { workspace = true, features = ["derive"] }
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.106.2" }
nu-test-support = { path = "../nu-test-support", version = "0.106.1" }

View File

@@ -5,7 +5,7 @@ edition = "2024"
license = "MIT"
name = "nu-command"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
version = "0.106.2"
version = "0.106.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -17,21 +17,21 @@ workspace = true
[dependencies]
nu-ansi-term = { workspace = true }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.2" }
nu-color-config = { path = "../nu-color-config", version = "0.106.2" }
nu-engine = { path = "../nu-engine", version = "0.106.2", default-features = false }
nu-experimental = { path = "../nu-experimental", version = "0.106.2" }
nu-glob = { path = "../nu-glob", version = "0.106.2" }
nu-json = { path = "../nu-json", version = "0.106.2" }
nu-parser = { path = "../nu-parser", version = "0.106.2" }
nu-path = { path = "../nu-path", version = "0.106.2" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.106.2" }
nu-protocol = { path = "../nu-protocol", version = "0.106.2", default-features = false }
nu-system = { path = "../nu-system", version = "0.106.2" }
nu-table = { path = "../nu-table", version = "0.106.2" }
nu-term-grid = { path = "../nu-term-grid", version = "0.106.2" }
nu-utils = { path = "../nu-utils", version = "0.106.2", default-features = false }
nuon = { path = "../nuon", version = "0.106.2" }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.1" }
nu-color-config = { path = "../nu-color-config", version = "0.106.1" }
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
nu-experimental = { path = "../nu-experimental", version = "0.106.1" }
nu-glob = { path = "../nu-glob", version = "0.106.1" }
nu-json = { path = "../nu-json", version = "0.106.1" }
nu-parser = { path = "../nu-parser", version = "0.106.1" }
nu-path = { path = "../nu-path", version = "0.106.1" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.106.1" }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
nu-system = { path = "../nu-system", version = "0.106.1" }
nu-table = { path = "../nu-table", version = "0.106.1" }
nu-term-grid = { path = "../nu-term-grid", version = "0.106.1" }
nu-utils = { path = "../nu-utils", version = "0.106.1", default-features = false }
nuon = { path = "../nuon", version = "0.106.1" }
alphanumeric-sort = { workspace = true }
base64 = { workspace = true }
@@ -54,14 +54,12 @@ devicons = { workspace = true }
dialoguer = { workspace = true, default-features = false, features = [
"fuzzy-select",
] }
fuzzy-matcher = { workspace = true }
digest = { workspace = true, default-features = false }
dtparse = { workspace = true }
encoding_rs = { workspace = true }
fancy-regex = { workspace = true }
filesize = { workspace = true }
filetime = { workspace = true }
http = {workspace = true}
human-date-parser = { workspace = true }
indexmap = { workspace = true }
indicatif = { workspace = true }
@@ -94,7 +92,6 @@ rusqlite = { workspace = true, features = [
"bundled",
"backup",
"chrono",
"column_decltype",
], optional = true }
rustls = { workspace = true, optional = true, features = ["ring"] }
rustls-native-certs = { workspace = true, optional = true }
@@ -194,7 +191,6 @@ os = [
"uu_uname",
"uu_whoami",
"which",
"ureq/platform-verifier"
]
# The dependencies listed below need 'getrandom'.
@@ -223,7 +219,7 @@ rustls-tls = [
"dep:rustls-native-certs",
"dep:webpki-roots",
"update-informer/rustls-tls",
"ureq/rustls",
"ureq/tls", # ureq 3 will has the feature rustls instead
]
plugin = ["nu-parser/plugin", "os"]
@@ -231,8 +227,8 @@ sqlite = ["rusqlite"]
trash-support = ["trash"]
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.2" }
nu-test-support = { path = "../nu-test-support", version = "0.106.2" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.1" }
nu-test-support = { path = "../nu-test-support", version = "0.106.1" }
dirs = { workspace = true }
mockito = { workspace = true, default-features = false }

View File

@@ -76,7 +76,7 @@ impl Command for BytesAt {
if let PipelineData::ByteStream(stream, metadata) = input {
let stream = stream.slice(call.head, call.arguments_span(), range)?;
Ok(PipelineData::byte_stream(stream, metadata))
Ok(PipelineData::ByteStream(stream, metadata))
} else {
operate(
map_value,

View File

@@ -67,7 +67,7 @@ impl Command for BytesCollect {
ByteStreamType::Binary,
);
Ok(PipelineData::byte_stream(output, metadata))
Ok(PipelineData::ByteStream(output, metadata))
}
fn examples(&self) -> Vec<Example> {

View File

@@ -4,7 +4,6 @@ use nu_engine::command_prelude::*;
struct Arguments {
cell_paths: Option<Vec<CellPath>>,
compact: bool,
little_endian: bool,
}
impl CmdArgument for Arguments {
@@ -36,12 +35,6 @@ impl Command for IntoBinary {
])
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
.switch("compact", "output without padding zeros", Some('c'))
.named(
"endian",
SyntaxShape::String,
"byte encode endian. Does not affect string, date or binary. In containers, only individual elements are affected. Available options: native(default), little, big",
Some('e'),
)
.rest(
"rest",
SyntaxShape::CellPath,
@@ -89,22 +82,6 @@ impl Command for IntoBinary {
Span::test_data(),
)),
},
Example {
description: "convert a number to a nushell binary primitive (big endian)",
example: "258 | into binary --endian big",
result: Some(Value::binary(
i64::from(258).to_be_bytes().to_vec(),
Span::test_data(),
)),
},
Example {
description: "convert a number to a nushell binary primitive (little endian)",
example: "258 | into binary --endian little",
result: Some(Value::binary(
i64::from(258).to_le_bytes().to_vec(),
Span::test_data(),
)),
},
Example {
description: "convert a boolean to a nushell binary primitive",
example: "true | into binary",
@@ -152,33 +129,14 @@ fn into_binary(
if let PipelineData::ByteStream(stream, metadata) = input {
// Just set the type - that should be good enough
Ok(PipelineData::byte_stream(
Ok(PipelineData::ByteStream(
stream.with_type(ByteStreamType::Binary),
metadata,
))
} else {
let endian = call.get_flag::<Spanned<String>>(engine_state, stack, "endian")?;
let little_endian = if let Some(endian) = endian {
match endian.item.as_str() {
"native" => cfg!(target_endian = "little"),
"little" => true,
"big" => false,
_ => {
return Err(ShellError::TypeMismatch {
err_message: "Endian must be one of native, little, big".to_string(),
span: endian.span,
});
}
}
} else {
cfg!(target_endian = "little")
};
let args = Arguments {
cell_paths,
compact: call.has_flag(engine_state, stack, "compact")?,
little_endian,
};
operate(action, args, input, head, engine_state.signals())
}
@@ -187,55 +145,12 @@ fn into_binary(
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
let value = match input {
Value::Binary { .. } => input.clone(),
Value::Int { val, .. } => Value::binary(
if args.little_endian {
val.to_le_bytes()
} else {
val.to_be_bytes()
}
.to_vec(),
span,
),
Value::Float { val, .. } => Value::binary(
if args.little_endian {
val.to_le_bytes()
} else {
val.to_be_bytes()
}
.to_vec(),
span,
),
Value::Filesize { val, .. } => Value::binary(
if args.little_endian {
val.get().to_le_bytes()
} else {
val.get().to_be_bytes()
}
.to_vec(),
span,
),
Value::Int { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
Value::Float { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
Value::Filesize { val, .. } => Value::binary(val.get().to_ne_bytes().to_vec(), span),
Value::String { val, .. } => Value::binary(val.as_bytes().to_vec(), span),
Value::Bool { val, .. } => Value::binary(
{
let as_int = i64::from(*val);
if args.little_endian {
as_int.to_le_bytes()
} else {
as_int.to_be_bytes()
}
.to_vec()
},
span,
),
Value::Duration { val, .. } => Value::binary(
if args.little_endian {
val.to_le_bytes()
} else {
val.to_be_bytes()
}
.to_vec(),
span,
),
Value::Bool { val, .. } => Value::binary(i64::from(*val).to_ne_bytes().to_vec(), span),
Value::Duration { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
Value::Date { val, .. } => {
Value::binary(val.format("%c").to_string().as_bytes().to_vec(), span)
}
@@ -256,7 +171,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
if args.compact {
let val_span = value.span();
if let Value::Binary { val, .. } = value {
let val = if args.little_endian {
let val = if cfg!(target_endian = "little") {
match val.iter().rposition(|&x| x != 0) {
Some(idx) => &val[..idx + 1],
@@ -304,7 +219,6 @@ mod test {
&Arguments {
cell_paths: None,
compact: true,
little_endian: cfg!(target_endian = "little"),
},
Span::test_data(),
);

View File

@@ -82,6 +82,9 @@ impl Command for IntoDatetime {
(Type::List(Box::new(Type::String)), Type::List(Box::new(Type::Date))),
(Type::table(), Type::table()),
(Type::Nothing, Type::table()),
// FIXME: https://github.com/nushell/nushell/issues/15485
// 'record -> any' was added as a temporary workaround to avoid type inference issues. The Any arm needs to be appear first.
(Type::record(), Type::Any),
(Type::record(), Type::record()),
(Type::record(), Type::Date),
// FIXME Type::Any input added to disable pipeline input type checking, as run-time checks can raise undesirable type errors

View File

@@ -53,6 +53,9 @@ impl Command for IntoDuration {
(Type::Float, Type::Duration),
(Type::String, Type::Duration),
(Type::Duration, Type::Duration),
// FIXME: https://github.com/nushell/nushell/issues/15485
// 'record -> any' was added as a temporary workaround to avoid type inference issues. The Any arm needs to be appear first.
(Type::record(), Type::Any),
(Type::record(), Type::record()),
(Type::record(), Type::Duration),
(Type::table(), Type::table()),

View File

@@ -170,7 +170,7 @@ fn string_helper(
// within a string stream is actually valid UTF-8. But refuse to do it if it was already set
// to binary
if stream.type_().is_string_coercible() {
Ok(PipelineData::byte_stream(
Ok(PipelineData::ByteStream(
stream.with_type(ByteStreamType::String),
metadata,
))

View File

@@ -1,12 +1,9 @@
use crate::{
MEMORY_DB,
database::values::sqlite::{open_sqlite_db, values_to_sql},
};
use crate::database::values::sqlite::{open_sqlite_db, values_to_sql};
use nu_engine::command_prelude::*;
use itertools::Itertools;
use nu_protocol::Signals;
use std::{borrow::Cow, path::Path};
use std::path::Path;
pub const DEFAULT_TABLE_NAME: &str = "main";
@@ -79,17 +76,6 @@ impl Command for IntoSqliteDb {
example: "{ foo: bar, baz: quux } | into sqlite filename.db",
result: None,
},
Example {
description: "Insert data that contains records, lists or tables, that will be stored as JSONB columns
These columns will be automatically turned back into nu objects when read directly via cell-path",
example: "{a_record: {foo: bar, baz: quux}, a_list: [1 2 3], a_table: [[a b]; [0 1] [2 3]]} | into sqlite filename.db -t my_table
(open filename.db).my_table.0.a_list",
result: Some(Value::test_list(vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(3)
]))
}
]
}
}
@@ -103,25 +89,15 @@ impl Table {
pub fn new(
db_path: &Spanned<String>,
table_name: Option<Spanned<String>>,
engine_state: &EngineState,
stack: &Stack,
) -> Result<Self, nu_protocol::ShellError> {
let table_name = table_name
.map(|table_name| table_name.item)
.unwrap_or_else(|| DEFAULT_TABLE_NAME.to_string());
let span = db_path.span;
let db_path: Cow<'_, Path> = match db_path.item.as_str() {
MEMORY_DB => Cow::Borrowed(Path::new(&db_path.item)),
item => engine_state
.cwd(Some(stack))?
.join(item)
.to_std_path_buf()
.into(),
let table_name = if let Some(table_name) = table_name {
table_name.item
} else {
DEFAULT_TABLE_NAME.to_string()
};
// create the sqlite database table
let conn = open_sqlite_db(&db_path, span)?;
let conn = open_sqlite_db(Path::new(&db_path.item), db_path.span)?;
Ok(Self { conn, table_name })
}
@@ -206,12 +182,11 @@ fn operate(
let span = call.head;
let file_name: Spanned<String> = call.req(engine_state, stack, 0)?;
let table_name: Option<Spanned<String>> = call.get_flag(engine_state, stack, "table-name")?;
let table = Table::new(&file_name, table_name, engine_state, stack)?;
Ok(action(engine_state, input, table, span, engine_state.signals())?.into_pipeline_data())
let table = Table::new(&file_name, table_name)?;
Ok(action(input, table, span, engine_state.signals())?.into_pipeline_data())
}
fn action(
engine_state: &EngineState,
input: PipelineData,
table: Table,
span: Span,
@@ -219,17 +194,17 @@ fn action(
) -> Result<Value, ShellError> {
match input {
PipelineData::ListStream(stream, _) => {
insert_in_transaction(engine_state, stream.into_iter(), span, table, signals)
insert_in_transaction(stream.into_iter(), span, table, signals)
}
PipelineData::Value(value @ Value::List { .. }, _) => {
let span = value.span();
let vals = value
.into_list()
.expect("Value matched as list above, but is not a list");
insert_in_transaction(engine_state, vals.into_iter(), span, table, signals)
insert_in_transaction(vals.into_iter(), span, table, signals)
}
PipelineData::Value(val, _) => {
insert_in_transaction(engine_state, std::iter::once(val), span, table, signals)
insert_in_transaction(std::iter::once(val), span, table, signals)
}
_ => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "list".into(),
@@ -241,7 +216,6 @@ fn action(
}
fn insert_in_transaction(
engine_state: &EngineState,
stream: impl Iterator<Item = Value>,
span: Span,
mut table: Table,
@@ -298,7 +272,7 @@ fn insert_in_transaction(
inner: Vec::new(),
})?;
let result = insert_value(engine_state, stream_value, span, &mut insert_statement);
let result = insert_value(stream_value, &mut insert_statement);
insert_statement
.finalize()
@@ -325,15 +299,13 @@ fn insert_in_transaction(
}
fn insert_value(
engine_state: &EngineState,
stream_value: Value,
call_span: Span,
insert_statement: &mut rusqlite::Statement<'_>,
) -> Result<(), ShellError> {
match stream_value {
// map each column value into its SQL representation
Value::Record { val, .. } => {
let sql_vals = values_to_sql(engine_state, val.values().cloned(), call_span)?;
let sql_vals = values_to_sql(val.values().cloned())?;
insert_statement
.execute(rusqlite::params_from_iter(sql_vals))
@@ -373,7 +345,6 @@ fn nu_value_to_sqlite_type(val: &Value) -> Result<&'static str, ShellError> {
Type::Date => Ok("DATETIME"),
Type::Duration => Ok("BIGINT"),
Type::Filesize => Ok("INTEGER"),
Type::List(_) | Type::Record(_) | Type::Table(_) => Ok("JSONB"),
// [NOTE] On null values, we just assume TEXT. This could end up
// creating a table where the column type is wrong in the table schema.
@@ -387,8 +358,11 @@ fn nu_value_to_sqlite_type(val: &Value) -> Result<&'static str, ShellError> {
| Type::Closure
| Type::Custom(_)
| Type::Error
| Type::List(_)
| Type::Range
| Type::Glob => Err(ShellError::OnlySupportsThisInputType {
| Type::Record(_)
| Type::Glob
| Type::Table(_) => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "sql".into(),
wrong_type: val.get_type().to_string(),
dst_span: Span::unknown(),
@@ -414,3 +388,17 @@ fn get_columns_with_sqlite_types(
Ok(columns)
}
#[cfg(test)]
mod tests {
use super::*;
// use super::{action, IntoSqliteDb};
// use nu_protocol::Type::Error;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(IntoSqliteDb {})
}
}

View File

@@ -41,7 +41,7 @@ impl Command for QueryDb {
Example {
description: "Execute a SQL statement with parameters",
example: r#"stor create -t my_table -c { first: str, second: int }
stor open | query db "INSERT INTO my_table VALUES (?, ?)" -p [hello 123]"#,
stor open | query db "INSERT INTO my_table VALUES (?, ?)" -p [hello 123]"#,
result: None,
},
Example {
@@ -54,36 +54,6 @@ stor open | query db "SELECT * FROM my_table WHERE second = :search_second" -p {
"second" => Value::test_int(123)
})])),
},
Example {
description: "Execute a SQL query, selecting a declared JSON(B) column that will automatically be parsed",
example: r#"stor create -t my_table -c {data: jsonb}
[{data: {name: Albert, age: 40}} {data: {name: Barnaby, age: 54}}] | stor insert -t my_table
stor open | query db "SELECT data FROM my_table WHERE data->>'age' < 45""#,
result: Some(Value::test_list(vec![Value::test_record(record! {
"data" => Value::test_record(
record! {
"name" => Value::test_string("Albert"),
"age" => Value::test_int(40),
}
)})])),
},
Example {
description: "Execute a SQL query selecting a sub-field of a JSON(B) column.
In this case, results must be parsed afterwards because SQLite does not
return declaration types when a JSON(B) column is not directly selected",
example: r#"stor create -t my_table -c {data: jsonb}
stor insert -t my_table -d {data: {foo: foo, bar: 12, baz: [0 1 2]}}
stor open | query db "SELECT data->'baz' AS baz FROM my_table" | update baz {from json}"#,
result: Some(Value::test_list(vec![Value::test_record(
record! { "baz" =>
Value::test_list(vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(2),
])
},
)])),
},
]
}
@@ -103,7 +73,7 @@ stor open | query db "SELECT data->'baz' AS baz FROM my_table" | update baz {fro
.get_flag(engine_state, stack, "params")?
.unwrap_or_else(|| Value::nothing(Span::unknown()));
let params = nu_value_to_params(engine_state, params_value, call.head)?;
let params = nu_value_to_params(params_value)?;
let db = SQLiteDatabase::try_from_pipeline(input, call.head)?;
db.query(&sql, params, call.head)

View File

@@ -79,7 +79,7 @@ impl Command for SchemaDb {
// TODO: add views and triggers
Ok(PipelineData::value(Value::record(record, span), None))
Ok(PipelineData::Value(Value::record(record, span), None))
}
}

View File

@@ -0,0 +1,7 @@
use super::db_table::DbTable;
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct DbSchema {
pub name: String,
pub tables: Vec<DbTable>,
}

View File

@@ -3,4 +3,5 @@ pub mod db_constraint;
pub mod db_foreignkey;
pub mod db_index;
pub mod db_row;
pub mod db_schema;
pub mod db_table;

View File

@@ -4,7 +4,7 @@ use super::definitions::{
};
use nu_protocol::{
CustomValue, PipelineData, Record, ShellError, Signals, Span, Spanned, Value,
engine::EngineState, shell_error::io::IoError,
shell_error::io::IoError,
};
use rusqlite::{
Connection, DatabaseName, Error as SqliteError, OpenFlags, Row, Statement, ToSql,
@@ -132,7 +132,7 @@ impl SQLiteDatabase {
}
fn sleeper(attempts: i32) -> bool {
log::warn!("SQLITE_BUSY, retrying after 250ms (attempt {attempts})");
log::warn!("SQLITE_BUSY, retrying after 250ms (attempt {})", attempts);
std::thread::sleep(std::time::Duration::from_millis(250));
true
}
@@ -202,7 +202,7 @@ impl SQLiteDatabase {
(p.pagecount - p.remaining) * 100 / p.pagecount
};
if percent % 10 == 0 {
log::trace!("Restoring: {percent} %");
log::trace!("Restoring: {} %", percent);
}
}),
)?;
@@ -431,44 +431,35 @@ fn run_sql_query(
}
// This is taken from to text local_into_string but tweaks it a bit so that certain formatting does not happen
pub fn value_to_sql(
engine_state: &EngineState,
value: Value,
call_span: Span,
) -> Result<Box<dyn rusqlite::ToSql>, ShellError> {
match value {
Value::Bool { val, .. } => Ok(Box::new(val)),
Value::Int { val, .. } => Ok(Box::new(val)),
Value::Float { val, .. } => Ok(Box::new(val)),
Value::Filesize { val, .. } => Ok(Box::new(val.get())),
Value::Duration { val, .. } => Ok(Box::new(val)),
Value::Date { val, .. } => Ok(Box::new(val)),
Value::String { val, .. } => Ok(Box::new(val)),
Value::Binary { val, .. } => Ok(Box::new(val)),
Value::Nothing { .. } => Ok(Box::new(rusqlite::types::Null)),
pub fn value_to_sql(value: Value) -> Result<Box<dyn rusqlite::ToSql>, ShellError> {
Ok(match value {
Value::Bool { val, .. } => Box::new(val),
Value::Int { val, .. } => Box::new(val),
Value::Float { val, .. } => Box::new(val),
Value::Filesize { val, .. } => Box::new(val.get()),
Value::Duration { val, .. } => Box::new(val),
Value::Date { val, .. } => Box::new(val),
Value::String { val, .. } => Box::new(val),
Value::Binary { val, .. } => Box::new(val),
Value::Nothing { .. } => Box::new(rusqlite::types::Null),
val => {
let json_value = crate::value_to_json_value(engine_state, &val, call_span, false)?;
match nu_json::to_string_raw(&json_value) {
Ok(s) => Ok(Box::new(s)),
Err(err) => Err(ShellError::CantConvert {
to_type: "JSON".into(),
from_type: val.get_type().to_string(),
span: val.span(),
help: Some(err.to_string()),
}),
}
return Err(ShellError::OnlySupportsThisInputType {
exp_input_type:
"bool, int, float, filesize, duration, date, string, nothing, binary".into(),
wrong_type: val.get_type().to_string(),
dst_span: Span::unknown(),
src_span: val.span(),
});
}
}
})
}
pub fn values_to_sql(
engine_state: &EngineState,
values: impl IntoIterator<Item = Value>,
call_span: Span,
) -> Result<Vec<Box<dyn rusqlite::ToSql>>, ShellError> {
values
.into_iter()
.map(|v| value_to_sql(engine_state, v, call_span))
.map(value_to_sql)
.collect::<Result<Vec<_>, _>>()
}
@@ -483,17 +474,13 @@ impl Default for NuSqlParams {
}
}
pub fn nu_value_to_params(
engine_state: &EngineState,
value: Value,
call_span: Span,
) -> Result<NuSqlParams, ShellError> {
pub fn nu_value_to_params(value: Value) -> Result<NuSqlParams, ShellError> {
match value {
Value::Record { val, .. } => {
let mut params = Vec::with_capacity(val.len());
for (mut column, value) in val.into_owned().into_iter() {
let sql_type_erased = value_to_sql(engine_state, value, call_span)?;
let sql_type_erased = value_to_sql(value)?;
if !column.starts_with([':', '@', '$']) {
column.insert(0, ':');
@@ -508,7 +495,7 @@ pub fn nu_value_to_params(
let mut params = Vec::with_capacity(vals.len());
for value in vals.into_iter() {
let sql_type_erased = value_to_sql(engine_state, value, call_span)?;
let sql_type_erased = value_to_sql(value)?;
params.push(sql_type_erased);
}
@@ -570,59 +557,31 @@ fn read_single_table(
prepared_statement_to_nu_list(stmt, NuSqlParams::default(), call_span, signals)
}
/// The SQLite type behind a query column returned as some raw type (e.g. 'text')
#[derive(Clone, Copy)]
pub enum DeclType {
Json,
Jsonb,
}
impl DeclType {
pub fn from_str(s: &str) -> Option<Self> {
match s.to_uppercase().as_str() {
"JSON" => Some(DeclType::Json),
"JSONB" => Some(DeclType::Jsonb),
_ => None, // We are only special-casing JSON(B) columns for now
}
}
}
/// A column out of an SQLite query, together with its type
pub struct TypedColumn {
pub name: String,
pub decl_type: Option<DeclType>,
}
impl TypedColumn {
pub fn from_rusqlite_column(c: &rusqlite::Column) -> Self {
Self {
name: c.name().to_owned(),
decl_type: c.decl_type().and_then(DeclType::from_str),
}
}
}
fn prepared_statement_to_nu_list(
mut stmt: Statement,
params: NuSqlParams,
call_span: Span,
signals: &Signals,
) -> Result<Value, SqliteOrShellError> {
let columns: Vec<TypedColumn> = stmt
.columns()
.iter()
.map(TypedColumn::from_rusqlite_column)
.collect();
let column_names = stmt
.column_names()
.into_iter()
.map(String::from)
.collect::<Vec<String>>();
// I'm very sorry for this repetition
// I tried scoping the match arms to the query_map alone, but lifetime and closure reference escapes
// got heavily in the way
let row_values = match params {
NuSqlParams::List(params) => {
let refs: Vec<&dyn ToSql> = params.iter().map(|value| &**value).collect();
let refs: Vec<&dyn ToSql> = params.iter().map(|value| (&**value)).collect();
let row_results = stmt.query_map(refs.as_slice(), |row| {
Ok(convert_sqlite_row_to_nu_value(row, call_span, &columns))
Ok(convert_sqlite_row_to_nu_value(
row,
call_span,
&column_names,
))
})?;
// we collect all rows before returning them. Not ideal but it's hard/impossible to return a stream from a CustomValue
@@ -644,7 +603,11 @@ fn prepared_statement_to_nu_list(
.collect();
let row_results = stmt.query_map(refs.as_slice(), |row| {
Ok(convert_sqlite_row_to_nu_value(row, call_span, &columns))
Ok(convert_sqlite_row_to_nu_value(
row,
call_span,
&column_names,
))
})?;
// we collect all rows before returning them. Not ideal but it's hard/impossible to return a stream from a CustomValue
@@ -687,14 +650,14 @@ fn read_entire_sqlite_db(
Ok(Value::record(tables, call_span))
}
pub fn convert_sqlite_row_to_nu_value(row: &Row, span: Span, columns: &[TypedColumn]) -> Value {
let record = columns
pub fn convert_sqlite_row_to_nu_value(row: &Row, span: Span, column_names: &[String]) -> Value {
let record = column_names
.iter()
.enumerate()
.map(|(i, col)| {
(
col.name.clone(),
convert_sqlite_value_to_nu_value(row.get_ref_unwrap(i), col.decl_type, span),
col.clone(),
convert_sqlite_value_to_nu_value(row.get_ref_unwrap(i), span),
)
})
.collect();
@@ -702,25 +665,18 @@ pub fn convert_sqlite_row_to_nu_value(row: &Row, span: Span, columns: &[TypedCol
Value::record(record, span)
}
pub fn convert_sqlite_value_to_nu_value(
value: ValueRef,
decl_type: Option<DeclType>,
span: Span,
) -> Value {
pub fn convert_sqlite_value_to_nu_value(value: ValueRef, span: Span) -> Value {
match value {
ValueRef::Null => Value::nothing(span),
ValueRef::Integer(i) => Value::int(i, span),
ValueRef::Real(f) => Value::float(f, span),
ValueRef::Text(buf) => match (std::str::from_utf8(buf), decl_type) {
(Ok(txt), Some(DeclType::Json | DeclType::Jsonb)) => {
match crate::convert_json_string_to_value(txt, span) {
Ok(val) => val,
Err(err) => Value::error(err, span),
}
}
(Ok(txt), _) => Value::string(txt.to_string(), span),
(Err(_), _) => Value::error(ShellError::NonUtf8 { span }, span),
},
ValueRef::Text(buf) => {
let s = match std::str::from_utf8(buf) {
Ok(v) => v,
Err(_) => return Value::error(ShellError::NonUtf8 { span }, span),
};
Value::string(s.to_string(), span)
}
ValueRef::Blob(u) => Value::binary(u.to_vec(), span),
}
}

View File

@@ -42,7 +42,7 @@ impl Command for Debug {
let raw = call.has_flag(engine_state, stack, "raw")?;
let raw_value = call.has_flag(engine_state, stack, "raw-value")?;
// Should PipelineData::empty() result in an error here?
// Should PipelineData::Empty result in an error here?
input.map(
move |x| {

View File

@@ -25,7 +25,7 @@ impl Command for DebugEnv {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::value(
Ok(PipelineData::Value(
env_to_strings(engine_state, stack)?.into_value(call.head),
None,
))

View File

@@ -35,7 +35,7 @@ impl Command for DebugExperimentalOptions {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::value(
Ok(PipelineData::Value(
Value::list(
nu_experimental::ALL
.iter()

View File

@@ -52,7 +52,7 @@ pub(super) fn start_editor(
) -> Result<PipelineData, ShellError> {
Err(ShellError::DisabledOsSupport {
msg: "Running external commands is not available without OS support.".to_string(),
span: call.head,
span: Some(call.head),
})
}
@@ -124,7 +124,7 @@ pub(super) fn start_editor(
let post_wait_callback = PostWaitCallback::for_job_control(engine_state, None, None);
// Wrap the output into a `PipelineData::byte_stream`.
// Wrap the output into a `PipelineData::ByteStream`.
let child = nu_protocol::process::ChildProcess::new(
child,
None,
@@ -133,7 +133,7 @@ pub(super) fn start_editor(
Some(post_wait_callback),
)?;
Ok(PipelineData::byte_stream(
Ok(PipelineData::ByteStream(
ByteStream::child(child, call.head),
None,
))

View File

@@ -45,7 +45,7 @@ impl Command for ConfigReset {
let no_backup = call.has_flag(engine_state, stack, "without-backup")?;
let span = call.head;
let Some(config_path) = nu_path::nu_config_dir() else {
return Err(ShellError::ConfigDirNotFound { span: call.head });
return Err(ShellError::ConfigDirNotFound { span: None });
};
if !only_env {
let mut nu_config = config_path.clone();

View File

@@ -33,7 +33,7 @@ impl Command for ConfigUseColors {
.get_config()
.use_ansi_coloring
.get(engine_state);
Ok(PipelineData::value(
Ok(PipelineData::Value(
Value::bool(use_ansi_coloring, call.head),
None,
))

View File

@@ -78,7 +78,9 @@ in no particular order, regardless of the specified timeout parameter.
let tag = tag_arg.map(|it| it.item as FilterTag);
let timeout: Option<Duration> = call.get_flag(engine_state, stack, "timeout")?;
let duration: Option<i64> = call.get_flag(engine_state, stack, "timeout")?;
let timeout = duration.map(|it| Duration::from_nanos(it as u64));
let mut mailbox = engine_state
.current_job
@@ -114,11 +116,6 @@ in no particular order, regardless of the specified timeout parameter.
description: "Get a message or fail if no message is available immediately",
result: None,
},
Example {
example: "job spawn { sleep 1sec; 'hi' | job send 0 }; job recv",
description: "Receive a message from a newly-spawned job",
result: None,
},
]
}
}

View File

@@ -101,17 +101,10 @@ This command never blocks.
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
example: "let id = job spawn { job recv | save sent.txt }; 'hi' | job send $id",
description: "Send a message from the main thread to a newly-spawned job",
result: None,
},
Example {
example: "job spawn { sleep 1sec; 'hi' | job send 0 }; job recv",
description: "Send a message from a newly-spawned job to the main thread (which always has an ID of 0)",
result: None,
},
]
vec![Example {
example: "let id = job spawn { job recv | save sent.txt }; 'hi' | job send $id",
description: "Send a message to a newly spawned job",
result: None,
}]
}
}

View File

@@ -120,6 +120,6 @@ impl Command for Mktemp {
});
}
};
Ok(PipelineData::value(Value::string(res, span), None))
Ok(PipelineData::Value(Value::string(res, span), None))
}
}

View File

@@ -176,7 +176,7 @@ impl Command for Open {
.map_err(|err| IoError::new(err, arg_span, PathBuf::from(path)))?;
// No content_type by default - Is added later if no converter is found
let stream = PipelineData::byte_stream(
let stream = PipelineData::ByteStream(
ByteStream::file(file, call_span, engine_state.signals().clone()),
Some(PipelineMetadata {
data_source: DataSource::FilePath(path.to_path_buf()),
@@ -246,7 +246,7 @@ impl Command for Open {
}
if output.is_empty() {
Ok(PipelineData::empty())
Ok(PipelineData::Empty)
} else if output.len() == 1 {
Ok(output.remove(0))
} else {

View File

@@ -339,7 +339,7 @@ fn rm(
inner: vec![],
});
} else if !confirmed {
return Ok(PipelineData::empty());
return Ok(PipelineData::Empty);
}
}

View File

@@ -191,7 +191,7 @@ impl Command for Save {
}
}
Ok(PipelineData::empty())
Ok(PipelineData::Empty)
}
PipelineData::ListStream(ls, pipeline_metadata)
if raw || prepare_path(&path, append, force)?.0.extension().is_none() =>

View File

@@ -45,7 +45,7 @@ impl Command for Start {
// Attempt to parse the input as a URL
if let Ok(url) = url::Url::parse(path_no_whitespace) {
open_path(url.as_str(), engine_state, stack, path.span)?;
return Ok(PipelineData::empty());
return Ok(PipelineData::Empty);
}
// If it's not a URL, treat it as a file path
let cwd = engine_state.cwd(Some(stack))?;
@@ -54,7 +54,7 @@ impl Command for Start {
// Check if the path exists or if it's a valid file/directory
if full_path.exists() {
open_path(full_path, engine_state, stack, path.span)?;
return Ok(PipelineData::empty());
return Ok(PipelineData::Empty);
}
// If neither file nor URL, return an error
Err(ShellError::GenericError {

View File

@@ -1,5 +1,16 @@
use dialoguer::Input;
use std::error::Error;
use std::{
error::Error,
path::{Path, PathBuf},
};
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Resource {
pub at: usize,
pub location: PathBuf,
}
impl Resource {}
pub fn try_interaction(
interactive: bool,
@@ -45,3 +56,34 @@ fn get_interactive_confirmation(prompt: String) -> Result<bool, Box<dyn Error>>
Ok(false)
}
}
/// Return `Some(true)` if the last change time of the `src` old than the `dst`,
/// otherwisie return `Some(false)`. Return `None` if the `src` or `dst` doesn't exist.
#[allow(dead_code)]
pub fn is_older(src: &Path, dst: &Path) -> Option<bool> {
if !dst.exists() || !src.exists() {
return None;
}
#[cfg(unix)]
{
use std::os::unix::fs::MetadataExt;
let src_ctime = std::fs::metadata(src)
.map(|m| m.ctime())
.unwrap_or(i64::MIN);
let dst_ctime = std::fs::metadata(dst)
.map(|m| m.ctime())
.unwrap_or(i64::MAX);
Some(src_ctime <= dst_ctime)
}
#[cfg(windows)]
{
use std::os::windows::fs::MetadataExt;
let src_ctime = std::fs::metadata(src)
.map(|m| m.last_write_time())
.unwrap_or(u64::MIN);
let dst_ctime = std::fs::metadata(dst)
.map(|m| m.last_write_time())
.unwrap_or(u64::MAX);
Some(src_ctime <= dst_ctime)
}
}

View File

@@ -1,21 +1,15 @@
use itertools::{Either, Itertools};
use notify_debouncer_full::{
DebouncedEvent, Debouncer, FileIdMap, new_debouncer,
new_debouncer,
notify::{
self, EventKind, RecommendedWatcher, RecursiveMode, Watcher,
EventKind, RecursiveMode, Watcher,
event::{DataChange, ModifyKind, RenameMode},
},
};
use nu_engine::{ClosureEval, command_prelude::*};
use nu_protocol::{
DeprecationEntry, DeprecationType, ReportMode, Signals, engine::Closure, report_shell_error,
shell_error::io::IoError,
};
use nu_protocol::{engine::Closure, report_shell_error, shell_error::io::IoError};
use std::{
borrow::Cow,
path::{Path, PathBuf},
sync::mpsc::{Receiver, RecvTimeoutError, channel},
path::PathBuf,
sync::mpsc::{RecvTimeoutError, channel},
time::Duration,
};
@@ -35,55 +29,23 @@ impl Command for Watch {
"Watch for file changes and execute Nu code when they happen."
}
fn extra_description(&self) -> &str {
"When run without a closure, `watch` returns a stream of events instead."
}
fn search_terms(&self) -> Vec<&str> {
vec!["watcher", "reload", "filesystem"]
}
fn deprecation_info(&self) -> Vec<DeprecationEntry> {
vec![DeprecationEntry {
ty: DeprecationType::Flag("debounce-ms".into()),
report_mode: ReportMode::FirstUse,
since: Some("0.107.0".into()),
expected_removal: Some("0.109.0".into()),
help: Some("`--debounce-ms` will be removed in favour of `--debounce`".into()),
}]
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("watch")
.input_output_types(vec![
(Type::Nothing, Type::Nothing),
(
Type::Nothing,
Type::Table(vec![
("operation".into(), Type::String),
("path".into(), Type::String),
("new_path".into(), Type::String),
].into_boxed_slice())
),
])
.input_output_types(vec![(Type::Nothing, Type::table())])
.required("path", SyntaxShape::Filepath, "The path to watch. Can be a file or directory.")
.optional(
"closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::String, SyntaxShape::String, SyntaxShape::String])),
"Some Nu code to run whenever a file changes. The closure will be passed `operation`, `path`, and `new_path` (for renames only) arguments in that order.",
)
.required("closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::String, SyntaxShape::String, SyntaxShape::String])),
"Some Nu code to run whenever a file changes. The closure will be passed `operation`, `path`, and `new_path` (for renames only) arguments in that order.")
.named(
"debounce-ms",
SyntaxShape::Int,
"Debounce changes for this many milliseconds (default: 100). Adjust if you find that single writes are reported as multiple events (deprecated)",
"Debounce changes for this many milliseconds (default: 100). Adjust if you find that single writes are reported as multiple events",
Some('d'),
)
.named(
"debounce",
SyntaxShape::Duration,
"Debounce changes for this duration (default: 100ms). Adjust if you find that single writes are reported as multiple events",
None,
)
.named(
"glob",
SyntaxShape::String, // SyntaxShape::GlobPattern gets interpreted relative to cwd, so use String instead
@@ -112,42 +74,62 @@ impl Command for Watch {
let cwd = engine_state.cwd_as_string(Some(stack))?;
let path_arg: Spanned<String> = call.req(engine_state, stack, 0)?;
let path_no_whitespace = path_arg
let path_no_whitespace = &path_arg
.item
.trim_end_matches(|x| matches!(x, '\x09'..='\x0d'));
let path = nu_path::canonicalize_with(path_no_whitespace, cwd).map_err(|err| {
ShellError::Io(IoError::new(
err,
path_arg.span,
PathBuf::from(path_no_whitespace),
))
})?;
let path = match nu_path::canonicalize_with(path_no_whitespace, cwd) {
Ok(p) => p,
Err(err) => {
return Err(ShellError::Io(IoError::new(
err,
path_arg.span,
PathBuf::from(path_no_whitespace),
)));
}
};
let closure: Closure = call.req(engine_state, stack, 1)?;
let closure: Option<Closure> = call.opt(engine_state, stack, 1)?;
let verbose = call.has_flag(engine_state, stack, "verbose")?;
let quiet = call.has_flag(engine_state, stack, "quiet")?;
let debounce_duration: Duration = resolve_duration_arguments(
call.get_flag(engine_state, stack, "debounce-ms")?,
call.get_flag(engine_state, stack, "debounce")?,
)?;
let debounce_duration_flag: Option<Spanned<i64>> =
call.get_flag(engine_state, stack, "debounce-ms")?;
let debounce_duration = match debounce_duration_flag {
Some(val) => match u64::try_from(val.item) {
Ok(val) => Duration::from_millis(val),
Err(_) => {
return Err(ShellError::TypeMismatch {
err_message: "Debounce duration is invalid".to_string(),
span: val.span,
});
}
},
None => DEFAULT_WATCH_DEBOUNCE_DURATION,
};
let glob_flag: Option<Spanned<String>> = call.get_flag(engine_state, stack, "glob")?;
let glob_pattern = glob_flag
.map(|glob| {
let glob_pattern = match glob_flag {
Some(glob) => {
let absolute_path = path.join(glob.item);
if verbose {
eprintln!("Absolute glob path: {absolute_path:?}");
}
nu_glob::Pattern::new(&absolute_path.to_string_lossy()).map_err(|_| {
ShellError::TypeMismatch {
err_message: "Glob pattern is invalid".to_string(),
span: glob.span,
match nu_glob::Pattern::new(&absolute_path.to_string_lossy()) {
Ok(pattern) => Some(pattern),
Err(_) => {
return Err(ShellError::TypeMismatch {
err_message: "Glob pattern is invalid".to_string(),
span: glob.span,
});
}
})
})
.transpose()?;
}
}
None => None,
};
let recursive_flag: Option<Spanned<bool>> =
call.get_flag(engine_state, stack, "recursive")?;
@@ -164,19 +146,22 @@ impl Command for Watch {
let (tx, rx) = channel();
let mut debouncer =
new_debouncer(debounce_duration, None, tx).map_err(|err| ShellError::GenericError {
error: "Failed to create watcher".to_string(),
msg: err.to_string(),
span: Some(call.head),
help: None,
inner: vec![],
})?;
if let Err(err) = debouncer.watcher().watch(&path, recursive_mode) {
let mut debouncer = match new_debouncer(debounce_duration, None, tx) {
Ok(d) => d,
Err(e) => {
return Err(ShellError::GenericError {
error: "Failed to create watcher".to_string(),
msg: e.to_string(),
span: Some(call.head),
help: None,
inner: vec![],
});
}
};
if let Err(e) = debouncer.watcher().watch(&path, recursive_mode) {
return Err(ShellError::GenericError {
error: "Failed to create watcher".to_string(),
msg: err.to_string(),
msg: e.to_string(),
span: Some(call.head),
help: None,
inner: vec![],
@@ -189,62 +174,107 @@ impl Command for Watch {
eprintln!("Now watching files at {path:?}. Press ctrl+c to abort.");
}
let iter = WatchIterator::new(debouncer, rx, engine_state.signals().clone());
let mut closure = ClosureEval::new(engine_state, stack, closure);
if let Some(closure) = closure {
let mut closure = ClosureEval::new(engine_state, stack, closure);
let mut event_handler = move |operation: &str,
path: PathBuf,
new_path: Option<PathBuf>|
-> Result<(), ShellError> {
let matches_glob = match &glob_pattern {
Some(glob) => glob.matches_path(&path),
None => true,
};
if verbose && glob_pattern.is_some() {
eprintln!("Matches glob: {matches_glob}");
}
for events in iter {
for event in events? {
let matches_glob = match &glob_pattern {
Some(glob) => glob.matches_path(&event.path),
None => true,
};
if verbose && glob_pattern.is_some() {
eprintln!("Matches glob: {matches_glob}");
if matches_glob {
let result = closure
.add_arg(Value::string(operation, head))
.add_arg(Value::string(path.to_string_lossy(), head))
.add_arg(Value::string(
new_path.unwrap_or_else(|| "".into()).to_string_lossy(),
head,
))
.run_with_input(PipelineData::Empty);
match result {
Ok(val) => val.print_table(engine_state, stack, false, false)?,
Err(err) => report_shell_error(engine_state, &err),
};
}
Ok(())
};
loop {
match rx.recv_timeout(CHECK_CTRL_C_FREQUENCY) {
Ok(Ok(events)) => {
if verbose {
eprintln!("{events:?}");
}
if matches_glob {
let result = closure
.add_arg(event.operation.into_value(head))
.add_arg(event.path.to_string_lossy().into_value(head))
.add_arg(
event
.new_path
.as_deref()
.map(Path::to_string_lossy)
.into_value(head),
)
.run_with_input(PipelineData::empty());
match result {
Ok(val) => val.print_table(engine_state, stack, false, false)?,
Err(err) => report_shell_error(engine_state, &err),
for mut one_event in events {
let handle_result = match one_event.event.kind {
// only want to handle event if relative path exists.
EventKind::Create(_) => one_event
.paths
.pop()
.map(|path| event_handler("Create", path, None))
.unwrap_or(Ok(())),
EventKind::Remove(_) => one_event
.paths
.pop()
.map(|path| event_handler("Remove", path, None))
.unwrap_or(Ok(())),
EventKind::Modify(ModifyKind::Data(DataChange::Content))
| EventKind::Modify(ModifyKind::Data(DataChange::Any))
| EventKind::Modify(ModifyKind::Any) => one_event
.paths
.pop()
.map(|path| event_handler("Write", path, None))
.unwrap_or(Ok(())),
EventKind::Modify(ModifyKind::Name(RenameMode::Both)) => one_event
.paths
.pop()
.map(|to| {
one_event
.paths
.pop()
.map(|from| event_handler("Rename", from, Some(to)))
.unwrap_or(Ok(()))
})
.unwrap_or(Ok(())),
_ => Ok(()),
};
handle_result?;
}
}
Ok(Err(_)) => {
return Err(ShellError::GenericError {
error: "Receiving events failed".to_string(),
msg: "Unexpected errors when receiving events".into(),
span: None,
help: None,
inner: vec![],
});
}
Err(RecvTimeoutError::Disconnected) => {
return Err(ShellError::GenericError {
error: "Disconnected".to_string(),
msg: "Unexpected disconnect from file watcher".into(),
span: None,
help: None,
inner: vec![],
});
}
Err(RecvTimeoutError::Timeout) => {}
}
Ok(PipelineData::empty())
} else {
fn glob_filter(glob: Option<&nu_glob::Pattern>, path: &Path) -> bool {
let Some(glob) = glob else { return true };
glob.matches_path(path)
if engine_state.signals().interrupted() {
break;
}
let out = iter
.flat_map(|e| match e {
Ok(events) => Either::Right(events.into_iter().map(Ok)),
Err(err) => Either::Left(std::iter::once(Err(err))),
})
.filter_map(move |e| match e {
Ok(ev) => glob_filter(glob_pattern.as_ref(), &ev.path)
.then(|| WatchEventRecord::from(&ev).into_value(head)),
Err(err) => Some(Value::error(err, head)),
})
.into_pipeline_data(head, engine_state.signals().clone());
Ok(out)
}
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
@@ -260,18 +290,8 @@ impl Command for Watch {
result: None,
},
Example {
description: "`watch` (when run without a closure) can also emit a stream of events it detects.",
example: r#"watch /foo/bar
| where operation == Create
| first 5
| each {|e| $"New file!: ($e.path)" }
| to text
| save --append changes_in_bar.log"#,
result: None,
},
Example {
description: "Print file changes with a debounce time of 5 minutes",
example: r#"watch /foo/bar --debounce 5min { |op, path| $"Registered ($op) on ($path)" | print }"#,
description: "Log all changes in a directory",
example: r#"watch /foo/bar { |op, path| $"($op) - ($path)(char nl)" | save --append changes_in_bar.log }"#,
result: None,
},
Example {
@@ -282,153 +302,3 @@ impl Command for Watch {
]
}
}
fn resolve_duration_arguments(
debounce_duration_flag_ms: Option<Spanned<i64>>,
debounce_duration_flag: Option<Spanned<Duration>>,
) -> Result<Duration, ShellError> {
match (debounce_duration_flag, debounce_duration_flag_ms) {
(None, None) => Ok(DEFAULT_WATCH_DEBOUNCE_DURATION),
(Some(l), Some(r)) => Err(ShellError::IncompatibleParameters {
left_message: "Here".to_string(),
left_span: l.span,
right_message: "and here".to_string(),
right_span: r.span,
}),
(None, Some(val)) => match u64::try_from(val.item) {
Ok(v) => Ok(Duration::from_millis(v)),
Err(_) => Err(ShellError::TypeMismatch {
err_message: "Debounce duration is invalid".to_string(),
span: val.span,
}),
},
(Some(v), None) => Ok(v.item),
}
}
struct WatchEvent {
operation: &'static str,
path: PathBuf,
new_path: Option<PathBuf>,
}
#[derive(IntoValue)]
struct WatchEventRecord<'a> {
operation: &'static str,
path: Cow<'a, str>,
new_path: Option<Cow<'a, str>>,
}
impl<'a> From<&'a WatchEvent> for WatchEventRecord<'a> {
fn from(value: &'a WatchEvent) -> Self {
Self {
operation: value.operation,
path: value.path.to_string_lossy(),
new_path: value.new_path.as_deref().map(Path::to_string_lossy),
}
}
}
impl TryFrom<DebouncedEvent> for WatchEvent {
type Error = ();
fn try_from(mut ev: DebouncedEvent) -> Result<Self, Self::Error> {
// TODO: Maybe we should handle all event kinds?
match ev.event.kind {
EventKind::Create(_) => ev.paths.pop().map(|p| WatchEvent {
operation: "Create",
path: p,
new_path: None,
}),
EventKind::Remove(_) => ev.paths.pop().map(|p| WatchEvent {
operation: "Remove",
path: p,
new_path: None,
}),
EventKind::Modify(
ModifyKind::Data(DataChange::Content | DataChange::Any) | ModifyKind::Any,
) => ev.paths.pop().map(|p| WatchEvent {
operation: "Write",
path: p,
new_path: None,
}),
EventKind::Modify(ModifyKind::Name(RenameMode::Both)) => ev
.paths
.drain(..)
.rev()
.next_array()
.map(|[from, to]| WatchEvent {
operation: "Rename",
path: from,
new_path: Some(to),
}),
_ => None,
}
.ok_or(())
}
}
struct WatchIterator {
/// Debouncer needs to be kept alive for `rx` to keep receiving events.
_debouncer: Debouncer<RecommendedWatcher, FileIdMap>,
rx: Option<Receiver<Result<Vec<DebouncedEvent>, Vec<notify::Error>>>>,
signals: Signals,
}
impl WatchIterator {
fn new(
debouncer: Debouncer<RecommendedWatcher, FileIdMap>,
rx: Receiver<Result<Vec<DebouncedEvent>, Vec<notify::Error>>>,
signals: Signals,
) -> Self {
Self {
_debouncer: debouncer,
rx: Some(rx),
signals,
}
}
}
impl Iterator for WatchIterator {
type Item = Result<Vec<WatchEvent>, ShellError>;
fn next(&mut self) -> Option<Self::Item> {
let rx = self.rx.as_ref()?;
while !self.signals.interrupted() {
let x = match rx.recv_timeout(CHECK_CTRL_C_FREQUENCY) {
Ok(x) => x,
Err(RecvTimeoutError::Timeout) => continue,
Err(RecvTimeoutError::Disconnected) => {
self.rx = None;
return Some(Err(ShellError::GenericError {
error: "Disconnected".to_string(),
msg: "Unexpected disconnect from file watcher".into(),
span: None,
help: None,
inner: vec![],
}));
}
};
let Ok(events) = x else {
self.rx = None;
return Some(Err(ShellError::GenericError {
error: "Receiving events failed".to_string(),
msg: "Unexpected errors when receiving events".into(),
span: None,
help: None,
inner: vec![],
}));
};
let watch_events = events
.into_iter()
.filter_map(|ev| WatchEvent::try_from(ev).ok())
.collect::<Vec<_>>();
return Some(Ok(watch_events));
}
self.rx = None;
None
}
}

View File

@@ -199,7 +199,7 @@ pub fn chunk_by(
let metadata = input.metadata();
match input {
PipelineData::Empty => Ok(PipelineData::empty()),
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::Value(Value::Range { .. }, ..)
| PipelineData::Value(Value::List { .. }, ..)
| PipelineData::ListStream(..) => {

View File

@@ -124,11 +124,11 @@ pub fn chunks(
PipelineData::Value(Value::List { vals, .. }, metadata) => {
let chunks = ChunksIter::new(vals, chunk_size, span);
let stream = ListStream::new(chunks, span, engine_state.signals().clone());
Ok(PipelineData::list_stream(stream, metadata))
Ok(PipelineData::ListStream(stream, metadata))
}
PipelineData::ListStream(stream, metadata) => {
let stream = stream.modify(|iter| ChunksIter::new(iter, chunk_size, span));
Ok(PipelineData::list_stream(stream, metadata))
Ok(PipelineData::ListStream(stream, metadata))
}
PipelineData::Value(Value::Binary { val, .. }, metadata) => {
let chunk_read = ChunkRead {
@@ -148,7 +148,7 @@ pub fn chunks(
}
PipelineData::ByteStream(stream, metadata) => {
let pipeline_data = match stream.reader() {
None => PipelineData::empty(),
None => PipelineData::Empty,
Some(reader) => {
let chunk_read = ChunkRead {
reader,

View File

@@ -74,7 +74,7 @@ impl Command for Columns {
fn getcol(head: Span, input: PipelineData) -> Result<PipelineData, ShellError> {
let metadata = input.metadata();
match input {
PipelineData::Empty => Ok(PipelineData::empty()),
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::Value(v, ..) => {
let span = v.span();
let cols = match v {

View File

@@ -227,7 +227,7 @@ fn default(
// stream's internal state already preserves the original signals config, so if this
// Signals::empty list stream gets interrupted it will be caught by the underlying iterator
let ls = ListStream::new(stream, span, Signals::empty());
Ok(PipelineData::list_stream(ls, metadata))
Ok(PipelineData::ListStream(ls, metadata))
// Otherwise, return the input as is
} else {
Ok(input)
@@ -236,7 +236,7 @@ fn default(
/// A wrapper around the default value to handle closures and caching values
enum DefaultValue {
Uncalculated(Box<Spanned<ClosureEval>>),
Uncalculated(Spanned<ClosureEval>),
Calculated(Value),
}
@@ -258,7 +258,7 @@ impl DefaultValue {
match value {
Value::Closure { val, .. } => {
let closure_eval = ClosureEval::new(engine_state, stack, *val);
DefaultValue::Uncalculated(Box::new(closure_eval.into_spanned(span)))
DefaultValue::Uncalculated(closure_eval.into_spanned(span))
}
_ => DefaultValue::Calculated(value),
}
@@ -269,7 +269,7 @@ impl DefaultValue {
DefaultValue::Uncalculated(closure) => {
let value = closure
.item
.run_with_input(PipelineData::empty())?
.run_with_input(PipelineData::Empty)?
.into_value(closure.span)?;
*self = DefaultValue::Calculated(value.clone());
Ok(value)
@@ -282,7 +282,7 @@ impl DefaultValue {
fn single_run_pipeline_data(self) -> Result<PipelineData, ShellError> {
match self {
DefaultValue::Uncalculated(mut closure) => {
closure.item.run_with_input(PipelineData::empty())
closure.item.run_with_input(PipelineData::Empty)
}
DefaultValue::Calculated(val) => Ok(val.into_pipeline_data()),
}

View File

@@ -108,7 +108,7 @@ fn drop_cols(
metadata,
))
} else {
Ok(PipelineData::empty())
Ok(PipelineData::Empty)
}
}
PipelineData::Value(mut v, ..) => {
@@ -136,7 +136,7 @@ fn drop_cols(
val => Err(unsupported_value_error(&val, head)),
}
}
PipelineData::Empty => Ok(PipelineData::empty()),
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::ByteStream(stream, ..) => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "table or record".into(),
wrong_type: stream.type_().describe().into(),

View File

@@ -90,11 +90,6 @@ with 'transpose' first."#
Value::nothing(Span::test_data()),
])),
},
Example {
example: r#"$env.name? | each { $"hello ($in)" } | default "bye""#,
description: "Update value if not null, otherwise do nothing",
result: None,
},
]
}
@@ -111,8 +106,7 @@ with 'transpose' first."#
let metadata = input.metadata();
match input {
PipelineData::Empty => Ok(PipelineData::empty()),
PipelineData::Value(Value::Nothing { .. }, ..) => Ok(input),
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::Value(Value::Range { .. }, ..)
| PipelineData::Value(Value::List { .. }, ..)
| PipelineData::ListStream(..) => {
@@ -170,7 +164,7 @@ with 'transpose' first."#
})
.into_pipeline_data(head, engine_state.signals().clone()))
} else {
Ok(PipelineData::empty())
Ok(PipelineData::Empty)
}
}
// This match allows non-iterables to be accepted,

View File

@@ -28,7 +28,7 @@ pub fn empty(
}
} else {
match input {
PipelineData::Empty => Ok(PipelineData::empty()),
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::ByteStream(stream, ..) => {
let span = stream.span();
match stream.reader() {

View File

@@ -31,12 +31,12 @@ impl Command for Find {
)
.switch(
"ignore-case",
"case-insensitive; when in regex mode, this is equivalent to (?i)",
"case-insensitive regex mode; equivalent to (?i)",
Some('i'),
)
.switch(
"multiline",
"don't split multi-line strings into lists of lines. you should use this option when using the (?m) or (?s) flags in regex mode",
"multi-line regex mode: ^ and $ match begin/end of line; equivalent to (?m)",
Some('m'),
)
.switch(
@@ -72,8 +72,8 @@ impl Command for Find {
result: None,
},
Example {
description: "Search and highlight text for a term in a string.",
example: r#"'Cargo.toml' | find Cargo"#,
description: "Search and highlight text for a term in a string. Note that regular search is case insensitive",
example: r#"'Cargo.toml' | find cargo"#,
result: Some(Value::test_string(
"\u{1b}[37m\u{1b}[0m\u{1b}[41;37mCargo\u{1b}[0m\u{1b}[37m.toml\u{1b}[0m"
.to_owned(),
@@ -81,7 +81,7 @@ impl Command for Find {
},
Example {
description: "Search a number or a file size in a list of numbers",
example: r#"[1 5 3kb 4 35 3Mb] | find 5 3kb"#,
example: r#"[1 5 3kb 4 3Mb] | find 5 3kb"#,
result: Some(Value::list(
vec![Value::test_int(5), Value::test_filesize(3000)],
Span::test_data(),
@@ -103,16 +103,16 @@ impl Command for Find {
)),
},
Example {
description: "Search using regex",
example: r#"[abc odb arc abf] | find --regex "b.""#,
description: "Find using regex",
example: r#"[abc bde arc abf] | find --regex "ab""#,
result: Some(Value::list(
vec![
Value::test_string(
"\u{1b}[37ma\u{1b}[0m\u{1b}[41;37mbc\u{1b}[0m\u{1b}[37m\u{1b}[0m"
"\u{1b}[37m\u{1b}[0m\u{1b}[41;37mab\u{1b}[0m\u{1b}[37mc\u{1b}[0m"
.to_string(),
),
Value::test_string(
"\u{1b}[37ma\u{1b}[0m\u{1b}[41;37mbf\u{1b}[0m\u{1b}[37m\u{1b}[0m"
"\u{1b}[37m\u{1b}[0m\u{1b}[41;37mab\u{1b}[0m\u{1b}[37mf\u{1b}[0m"
.to_string(),
),
],
@@ -120,8 +120,8 @@ impl Command for Find {
)),
},
Example {
description: "Case insensitive search",
example: r#"[aBc bde Arc abf] | find "ab" -i"#,
description: "Find using regex case insensitive",
example: r#"[aBc bde Arc abf] | find --regex "ab" -i"#,
result: Some(Value::list(
vec![
Value::test_string(
@@ -211,33 +211,11 @@ impl Command for Find {
Span::test_data(),
)),
},
Example {
description: "Find in a multi-line string",
example: r#""Violets are red\nAnd roses are blue\nWhen metamaterials\nAlter their hue" | find "ue""#,
result: Some(Value::list(
vec![
Value::test_string(
"\u{1b}[37mAnd roses are bl\u{1b}[0m\u{1b}[41;37mue\u{1b}[0m\u{1b}[37m\u{1b}[0m",
),
Value::test_string(
"\u{1b}[37mAlter their h\u{1b}[0m\u{1b}[41;37mue\u{1b}[0m\u{1b}[37m\u{1b}[0m",
),
],
Span::test_data(),
)),
},
Example {
description: "Find in a multi-line string without splitting the input into a list of lines",
example: r#""Violets are red\nAnd roses are blue\nWhen metamaterials\nAlter their hue" | find --multiline "ue""#,
result: Some(Value::test_string(
"\u{1b}[37mViolets are red\nAnd roses are bl\u{1b}[0m\u{1b}[41;37mue\u{1b}[0m\u{1b}[37m\nWhen metamaterials\nAlter their h\u{1b}[0m\u{1b}[41;37mue\u{1b}[0m\u{1b}[37m\u{1b}[0m",
)),
},
]
}
fn search_terms(&self) -> Vec<&str> {
vec!["filter", "regex", "search", "condition", "grep"]
vec!["filter", "regex", "search", "condition"]
}
fn run(
@@ -249,25 +227,11 @@ impl Command for Find {
) -> Result<PipelineData, ShellError> {
let pattern = get_match_pattern_from_arguments(engine_state, stack, call)?;
let multiline = call.has_flag(engine_state, stack, "multiline")?;
let columns_to_search: Vec<_> = call
.get_flag(engine_state, stack, "columns")?
.unwrap_or_default();
let input = if multiline {
if let PipelineData::ByteStream(..) = input {
// ByteStream inputs are processed by iterating over the lines, which necessarily
// breaks the multi-line text being streamed into a list of lines.
return Err(ShellError::IncompatibleParametersSingle {
msg: "Flag `--multiline` currently doesn't work for byte stream inputs. Consider using `collect`".into(),
span: call.get_flag_span(stack, "multiline").expect("has flag"),
});
};
input
} else {
split_string_if_multiline(input, call.head)
};
let input = split_string_if_multiline(input, call.head);
find_in_pipelinedata(pattern, columns_to_search, engine_state, stack, input)
}
@@ -278,11 +242,8 @@ struct MatchPattern {
/// the regex to be used for matching in text
regex: Regex,
/// the list of match terms (converted to lowercase if needed), or empty if a regex was provided
search_terms: Vec<String>,
/// case-insensitive match
ignore_case: bool,
/// the list of match terms converted to lowercase strings, or empty if a regex was provided
lower_terms: Vec<String>,
/// return a modified version of the value where matching parts are highlighted
highlight: bool,
@@ -311,10 +272,6 @@ fn get_match_pattern_from_arguments(
let invert = call.has_flag(engine_state, stack, "invert")?;
let highlight = !call.has_flag(engine_state, stack, "no-highlight")?;
let ignore_case = call.has_flag(engine_state, stack, "ignore-case")?;
let dotall = call.has_flag(engine_state, stack, "dotall")?;
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
@@ -323,7 +280,7 @@ fn get_match_pattern_from_arguments(
let highlight_style =
style_computer.compute("search_result", &Value::string("search result", span));
let (regex_str, search_terms) = if let Some(regex) = regex {
let (regex_str, lower_terms) = if let Some(regex) = regex {
if !terms.is_empty() {
return Err(ShellError::IncompatibleParametersSingle {
msg: "Cannot use a `--regex` parameter with additional search terms".into(),
@@ -331,54 +288,47 @@ fn get_match_pattern_from_arguments(
});
}
let flags = match (ignore_case, dotall) {
(false, false) => "",
(true, false) => "(?i)", // case insensitive
(false, true) => "(?s)", // allow . to match \n
(true, true) => "(?is)", // case insensitive and allow . to match \n
let insensitive = call.has_flag(engine_state, stack, "ignore-case")?;
let multiline = call.has_flag(engine_state, stack, "multiline")?;
let dotall = call.has_flag(engine_state, stack, "dotall")?;
let flags = match (insensitive, multiline, dotall) {
(false, false, false) => "",
(true, false, false) => "(?i)", // case insensitive
(false, true, false) => "(?m)", // multi-line mode
(false, false, true) => "(?s)", // allow . to match \n
(true, true, false) => "(?im)", // case insensitive and multi-line mode
(true, false, true) => "(?is)", // case insensitive and allow . to match \n
(false, true, true) => "(?ms)", // multi-line mode and allow . to match \n
(true, true, true) => "(?ims)", // case insensitive, multi-line mode and allow . to match \n
};
(flags.to_string() + regex.as_str(), Vec::new())
} else {
if dotall {
return Err(ShellError::IncompatibleParametersSingle {
msg: "Flag --dotall only works for regex search".into(),
span: call.get_flag_span(stack, "dotall").expect("has flag"),
});
}
let mut regex = String::new();
if ignore_case {
regex += "(?i)";
}
regex += "(?i)";
let search_terms = terms
let lower_terms = terms
.iter()
.map(|v| {
if ignore_case {
v.to_expanded_string("", &config).to_lowercase()
} else {
v.to_expanded_string("", &config)
}
})
.map(|v| escape(&v.to_expanded_string("", &config).to_lowercase()).into())
.collect::<Vec<String>>();
let escaped_terms = search_terms
.iter()
.map(|v| escape(v).into())
.collect::<Vec<String>>();
if let Some(term) = escaped_terms.first() {
if let Some(term) = lower_terms.first() {
regex += term;
}
for term in escaped_terms.iter().skip(1) {
for term in lower_terms.iter().skip(1) {
regex += "|";
regex += term;
}
(regex, search_terms)
let lower_terms = terms
.iter()
.map(|v| v.to_expanded_string("", &config).to_lowercase())
.collect::<Vec<String>>();
(regex, lower_terms)
};
let regex = Regex::new(regex_str.as_str()).map_err(|e| ShellError::TypeMismatch {
@@ -388,8 +338,7 @@ fn get_match_pattern_from_arguments(
Ok(MatchPattern {
regex,
search_terms,
ignore_case,
lower_terms,
invert,
highlight,
string_style,
@@ -499,7 +448,7 @@ fn find_in_pipelinedata(
let map_columns_to_search = columns_to_search.clone();
match input {
PipelineData::Empty => Ok(PipelineData::empty()),
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::Value(_, _) => input
.filter(
move |value| {
@@ -521,7 +470,7 @@ fn find_in_pipelinedata(
.map(move |x| highlight_matches_in_value(&map_pattern, x, &map_columns_to_search))
});
Ok(PipelineData::list_stream(stream, metadata))
Ok(PipelineData::ListStream(stream, metadata))
}
PipelineData::ByteStream(stream, ..) => {
let span = stream.span();
@@ -540,7 +489,7 @@ fn find_in_pipelinedata(
}
Ok(Value::list(output, span).into_pipeline_data())
} else {
Ok(PipelineData::empty())
Ok(PipelineData::Empty)
}
}
}
@@ -558,11 +507,7 @@ fn value_should_be_printed(
columns_to_search: &[String],
config: &Config,
) -> bool {
let value_as_string = if pattern.ignore_case {
value.to_expanded_string("", config).to_lowercase()
} else {
value.to_expanded_string("", config)
};
let lower_value = value.to_expanded_string("", config).to_lowercase();
match value {
Value::Bool { .. }
@@ -574,18 +519,18 @@ fn value_should_be_printed(
| Value::Float { .. }
| Value::Closure { .. }
| Value::Nothing { .. } => {
if !pattern.search_terms.is_empty() {
if !pattern.lower_terms.is_empty() {
// look for exact match when searching with terms
pattern
.search_terms
.lower_terms
.iter()
.any(|term: &String| term == &value_as_string)
.any(|term: &String| term == &lower_value)
} else {
string_should_be_printed(pattern, &value_as_string)
string_should_be_printed(pattern, &lower_value)
}
}
Value::Glob { .. } | Value::CellPath { .. } | Value::Custom { .. } => {
string_should_be_printed(pattern, &value_as_string)
string_should_be_printed(pattern, &lower_value)
}
Value::String { val, .. } => string_should_be_printed(pattern, val),
Value::List { vals, .. } => vals
@@ -652,8 +597,7 @@ pub fn find_internal(
let pattern = MatchPattern {
regex,
search_terms: vec![search_term.to_lowercase()],
ignore_case: true,
lower_terms: vec![search_term.to_lowercase()],
highlight,
invert: false,
string_style,

View File

@@ -167,7 +167,7 @@ fn first_helper(
Err(ShellError::AccessEmptyContent { span: head })
}
} else {
Ok(PipelineData::list_stream(
Ok(PipelineData::ListStream(
stream.modify(|iter| iter.take(rows)),
metadata,
))
@@ -191,7 +191,7 @@ fn first_helper(
}
} else {
// Just take 'rows' bytes off the stream, mimicking the binary behavior
Ok(PipelineData::byte_stream(
Ok(PipelineData::ByteStream(
ByteStream::read(
reader.take(rows as u64),
head,
@@ -202,7 +202,7 @@ fn first_helper(
))
}
} else {
Ok(PipelineData::empty())
Ok(PipelineData::Empty)
}
} else {
Err(ShellError::OnlySupportsThisInputType {

View File

@@ -45,11 +45,6 @@ If multiple cell paths are given, this will produce a list of values."#
"make all cell path members optional (returns `null` for missing values)",
Some('o'),
)
.switch(
"ignore-case",
"make all cell path members case insensitive",
None,
)
.switch(
"ignore-errors",
"ignore missing data (make all cell path members optional) (deprecated)",
@@ -79,30 +74,6 @@ If multiple cell paths are given, this will produce a list of values."#
Span::test_data(),
)),
},
Example {
description: "Get a column from a table where some rows don't have that column, using optional cell-path syntax",
example: "[{A: A0, B: B0}, {B: B1}, {A: A2, B: B2}] | get A?",
result: Some(Value::list(
vec![
Value::test_string("A0"),
Value::test_nothing(),
Value::test_string("A2"),
],
Span::test_data(),
)),
},
Example {
description: "Get a column from a table where some rows don't have that column, using the optional flag",
example: "[{A: A0, B: B0}, {B: B1}, {A: A2, B: B2}] | get -o A",
result: Some(Value::list(
vec![
Value::test_string("A0"),
Value::test_nothing(),
Value::test_string("A2"),
],
Span::test_data(),
)),
},
Example {
description: "Get a cell from a table",
example: "[{A: A0}] | get 0.A",
@@ -119,13 +90,8 @@ If multiple cell paths are given, this will produce a list of values."#
result: None,
},
Example {
description: "Getting environment variables in a case insensitive way, using case insensitive cell-path syntax",
example: "$env | get home! path!",
result: None,
},
Example {
description: "Getting environment variables in a case insensitive way, using the '--ignore-case' flag",
example: "$env | get --ignore-case home path",
description: "Getting Path/PATH in a case insensitive way",
example: "$env | get paTH!",
result: None,
},
Example {
@@ -150,14 +116,12 @@ If multiple cell paths are given, this will produce a list of values."#
let rest: Vec<CellPath> = call.rest_const(working_set, 1)?;
let optional = call.has_flag_const(working_set, "optional")?
|| call.has_flag_const(working_set, "ignore-errors")?;
let ignore_case = call.has_flag_const(working_set, "ignore-case")?;
let metadata = input.metadata();
action(
input,
cell_path,
rest,
optional,
ignore_case,
working_set.permanent().signals().clone(),
call.head,
)
@@ -175,14 +139,12 @@ If multiple cell paths are given, this will produce a list of values."#
let rest: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let optional = call.has_flag(engine_state, stack, "optional")?
|| call.has_flag(engine_state, stack, "ignore-errors")?;
let ignore_case = call.has_flag(engine_state, stack, "ignore-case")?;
let metadata = input.metadata();
action(
input,
cell_path,
rest,
optional,
ignore_case,
engine_state.signals().clone(),
call.head,
)
@@ -214,7 +176,6 @@ fn action(
mut cell_path: CellPath,
mut rest: Vec<CellPath>,
optional: bool,
ignore_case: bool,
signals: Signals,
span: Span,
) -> Result<PipelineData, ShellError> {
@@ -225,13 +186,6 @@ fn action(
}
}
if ignore_case {
cell_path.make_insensitive();
for path in &mut rest {
path.make_insensitive();
}
}
if let PipelineData::Empty = input {
return Err(ShellError::PipelineEmpty { dst_span: span });
}

View File

@@ -264,7 +264,7 @@ fn insert(
value
}
});
Ok(PipelineData::list_stream(stream, metadata))
Ok(PipelineData::ListStream(stream, metadata))
} else {
let stream = stream.map(move |mut value| {
if let Err(e) = value.insert_data_at_cell_path(
@@ -278,7 +278,7 @@ fn insert(
}
});
Ok(PipelineData::list_stream(stream, metadata))
Ok(PipelineData::ListStream(stream, metadata))
}
}
PipelineData::Empty => Err(ShellError::IncompatiblePathAccess {

View File

@@ -120,7 +120,7 @@ interleave
.into_iter()
.chain(closures.into_iter().map(|closure| {
ClosureEvalOnce::new(engine_state, stack, closure)
.run_with_input(PipelineData::empty())
.run_with_input(PipelineData::Empty)
}))
.try_for_each(|stream| {
stream.and_then(|stream| {

View File

@@ -42,7 +42,7 @@ impl Command for Items {
let metadata = input.metadata();
match input {
PipelineData::Empty => Ok(PipelineData::empty()),
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::Value(value, ..) => {
let span = value.span();
match value {
@@ -55,7 +55,7 @@ impl Command for Items {
let result = closure
.add_arg(Value::string(col, span))
.add_arg(val)
.run_with_input(PipelineData::empty())
.run_with_input(PipelineData::Empty)
.and_then(|data| data.into_value(head));
match result {

View File

@@ -85,7 +85,7 @@ impl Command for Join {
Value::String { val: r_on, .. },
) => {
let result = join(rows_1, rows_2, l_on, r_on, join_type, span);
Ok(PipelineData::value(result, metadata))
Ok(PipelineData::Value(result, metadata))
}
_ => Err(ShellError::UnsupportedInput {
msg: "(PipelineData<table>, table, string, string)".into(),

View File

@@ -186,7 +186,7 @@ impl Command for Last {
}
}
} else {
Ok(PipelineData::empty())
Ok(PipelineData::Empty)
}
} else {
Err(ShellError::OnlySupportsThisInputType {

View File

@@ -57,7 +57,7 @@ impl Command for Lines {
src_span: value.span(),
}),
},
PipelineData::Empty => Ok(PipelineData::empty()),
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::ListStream(stream, metadata) => {
let stream = stream.modify(|iter| {
iter.filter_map(move |value| {
@@ -81,7 +81,7 @@ impl Command for Lines {
.flatten()
});
Ok(PipelineData::list_stream(stream, metadata))
Ok(PipelineData::ListStream(stream, metadata))
}
PipelineData::ByteStream(stream, ..) => {
if let Some(lines) = stream.lines() {

View File

@@ -130,7 +130,7 @@ impl Command for ParEach {
};
match input {
PipelineData::Empty => Ok(PipelineData::empty()),
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::Value(value, ..) => {
let span = value.span();
match value {

View File

@@ -123,7 +123,7 @@ impl Command for Reduce {
acc = closure
.add_arg(value)
.add_arg(acc.clone())
.run_with_input(PipelineData::value(acc, None))?
.run_with_input(PipelineData::Value(acc, None))?
.into_value(head)?;
}

View File

@@ -18,11 +18,6 @@ impl Command for Reject {
(Type::list(Type::Any), Type::list(Type::Any)),
])
.switch("optional", "make all cell path members optional", Some('o'))
.switch(
"ignore-case",
"make all cell path members case insensitive",
None,
)
.switch(
"ignore-errors",
"ignore missing data (make all cell path members optional) (deprecated)",
@@ -98,20 +93,12 @@ impl Command for Reject {
let optional = call.has_flag(engine_state, stack, "optional")?
|| call.has_flag(engine_state, stack, "ignore-errors")?;
let ignore_case = call.has_flag(engine_state, stack, "ignore-case")?;
if optional {
for cell_path in &mut new_columns {
cell_path.make_optional();
}
}
if ignore_case {
for cell_path in &mut new_columns {
cell_path.make_insensitive();
}
}
reject(engine_state, span, input, new_columns)
}

View File

@@ -26,11 +26,6 @@ impl Command for Select {
"make all cell path members optional (returns `null` for missing values)",
Some('o'),
)
.switch(
"ignore-case",
"make all cell path members case insensitive",
None,
)
.switch(
"ignore-errors",
"ignore missing data (make all cell path members optional) (deprecated)",
@@ -115,7 +110,6 @@ produce a table, a list will produce a list, and a record will produce a record.
}
let optional = call.has_flag(engine_state, stack, "optional")?
|| call.has_flag(engine_state, stack, "ignore-errors")?;
let ignore_case = call.has_flag(engine_state, stack, "ignore-case")?;
let span = call.head;
if optional {
@@ -124,12 +118,6 @@ produce a table, a list will produce a list, and a record will produce a record.
}
}
if ignore_case {
for cell_path in &mut new_columns {
cell_path.make_insensitive();
}
}
select(engine_state, span, new_columns, input)
}
@@ -155,18 +143,6 @@ produce a table, a list will produce a list, and a record will produce a record.
"a" => Value::test_string("a")
})])),
},
Example {
description: "Select a column even if some rows are missing that column",
example: "[{a: a0 b: b0} {b: b1}] | select -o a",
result: Some(Value::test_list(vec![
Value::test_record(record! {
"a" => Value::test_string("a0")
}),
Value::test_record(record! {
"a" => Value::test_nothing()
}),
])),
},
Example {
description: "Select a field in a record",
example: "{a: a b: b} | select a",

View File

@@ -94,7 +94,7 @@ impl Command for Skip {
PipelineData::ByteStream(stream, metadata) => {
if stream.type_().is_binary_coercible() {
let span = stream.span();
Ok(PipelineData::byte_stream(
Ok(PipelineData::ByteStream(
stream.skip(span, n as u64)?,
metadata,
))

View File

@@ -81,7 +81,7 @@ impl Command for Slice {
};
if count == 0 {
Ok(PipelineData::value(Value::list(vec![], head), None))
Ok(PipelineData::Value(Value::list(vec![], head), None))
} else {
let iter = v.into_iter().skip(from).take(count);
Ok(iter.into_pipeline_data(head, engine_state.signals().clone()))
@@ -102,7 +102,7 @@ impl Command for Slice {
};
if count == 0 {
Ok(PipelineData::value(Value::list(vec![], head), None))
Ok(PipelineData::Value(Value::list(vec![], head), None))
} else {
let iter = input.into_iter().skip(from).take(count);
Ok(iter.into_pipeline_data(head, engine_state.signals().clone()))

View File

@@ -62,7 +62,7 @@ impl Command for Take {
)),
Value::Binary { val, .. } => {
let slice: Vec<u8> = val.into_iter().take(rows_desired).collect();
Ok(PipelineData::value(Value::binary(slice, span), metadata))
Ok(PipelineData::Value(Value::binary(slice, span), metadata))
}
Value::Range { val, .. } => Ok(val
.into_range_iter(span, Signals::empty())
@@ -82,14 +82,14 @@ impl Command for Take {
}),
}
}
PipelineData::ListStream(stream, metadata) => Ok(PipelineData::list_stream(
PipelineData::ListStream(stream, metadata) => Ok(PipelineData::ListStream(
stream.modify(|iter| iter.take(rows_desired)),
metadata,
)),
PipelineData::ByteStream(stream, metadata) => {
if stream.type_().is_binary_coercible() {
let span = stream.span();
Ok(PipelineData::byte_stream(
Ok(PipelineData::ByteStream(
stream.take(span, rows_desired as u64)?,
metadata,
))

View File

@@ -138,7 +138,7 @@ use it in your pipeline."#
let tee_thread = spawn_tee(info, eval_block)?;
let tee = IoTee::new(read, tee_thread);
Ok(PipelineData::byte_stream(
Ok(PipelineData::ByteStream(
ByteStream::read(tee, span, engine_state.signals().clone(), type_),
metadata,
))
@@ -151,7 +151,7 @@ use it in your pipeline."#
let tee_thread = spawn_tee(info, eval_block)?;
let tee = IoTee::new(file, tee_thread);
Ok(PipelineData::byte_stream(
Ok(PipelineData::ByteStream(
ByteStream::read(tee, span, engine_state.signals().clone(), type_),
metadata,
))
@@ -234,7 +234,7 @@ use it in your pipeline."#
};
if child.stdout.is_some() || child.stderr.is_some() {
Ok(PipelineData::byte_stream(
Ok(PipelineData::ByteStream(
ByteStream::child(*child, span),
metadata,
))
@@ -243,7 +243,7 @@ use it in your pipeline."#
thread.join().unwrap_or_else(|_| Err(panic_error()))?;
}
child.wait()?;
Ok(PipelineData::empty())
Ok(PipelineData::Empty)
}
}
}
@@ -439,7 +439,7 @@ fn spawn_tee(
Signals::empty(),
info.type_,
);
eval_block(PipelineData::byte_stream(stream, info.metadata))
eval_block(PipelineData::ByteStream(stream, info.metadata))
})
.map_err(|err| {
IoError::new_with_additional_context(err, info.span, None, "Could not spawn tee")

View File

@@ -293,7 +293,7 @@ pub fn transpose(
})
.collect::<Vec<Value>>();
if result_data.len() == 1 && args.as_record {
Ok(PipelineData::value(
Ok(PipelineData::Value(
result_data
.pop()
.expect("already check result only contains one item"),

View File

@@ -210,7 +210,7 @@ fn update(
}
});
Ok(PipelineData::list_stream(stream, metadata))
Ok(PipelineData::ListStream(stream, metadata))
} else {
let stream = stream.map(move |mut value| {
if let Err(e) =
@@ -222,7 +222,7 @@ fn update(
}
});
Ok(PipelineData::list_stream(stream, metadata))
Ok(PipelineData::ListStream(stream, metadata))
}
}
PipelineData::Empty => Err(ShellError::IncompatiblePathAccess {

Some files were not shown because too many files have changed in this diff Show More