Compare commits

...

138 Commits

Author SHA1 Message Date
3d631490bc Bump version to 0.87.1 (#11056) 2023-11-18 18:46:36 +01:00
68211dea3e Send only absolute paths to uu_cp (#11080)
# Description
Fixes https://github.com/nushell/nushell/issues/10832

Replaces: https://github.com/nushell/nushell/pull/10843
2023-11-18 17:57:49 +01:00
b8e9293c45 Fix rm path handling (#11064)
# Description
Fixes issue #11061 where `rm` fails to find a file after a `cd`. It
looks like the new glob functions do not return absolute file paths
which we forgot to account for.

# Tests
Added a test (fails on current main, but passes with this PR).

---------

Co-authored-by: Jakub Žádník <kubouch@gmail.com>
2023-11-18 17:57:49 +01:00
77a1c3c7b2 Bump version for 0.87.0 release (#11031)
# Release checklist

- [x] reedline
  - [x] released
  - [x] pinned
- [x] crate graph check
- [x] release notes
- [x] release script update (new crate `nu-lsp`)
- [ ] permission management `nu-lsp` on crates.io
2023-11-14 21:01:19 +01:00
82b3ae826f Pin reedline to 0.26 release (#11053)
See full release notes:
https://github.com/nushell/reedline/releases/tag/v0.26.0
2023-11-14 20:47:25 +01:00
1b3092ae7c Move to clearer reedline keyboard enhancement API (#11045)
Go from the ill-defined `enable/disable` pairs to `.use_...` builders
This alleviates unclear properties when the underlying enhancements are
enabled. Now they are enabed when entering `Reedline::read_line` and
disabled when exiting that.

Furthermore allow setting `$env.config.use_kitty_protocol` to have an
effect when toggling during runtime. Previously it was only enabled when
receiving a value from `config.nu`. I kept the warning code there to not
pollute the log. We could move it into the REPL-loop if desired

Not sure if we should actively block the enabling of `bracketed_paste`
on Windows. Need to test what happens if it just doesn't do anything we
could remove the `cfg!` switch. At least for WSL2 Windows Terminal
already supports bracketed paste. `target_os = windows` is a bad
predictor for `conhost.exe`.

Depends on https://github.com/nushell/reedline/pull/659
(pointing to personal fork)

Closes https://github.com/nushell/nushell/issues/10982
Supersedes https://github.com/nushell/nushell/pull/10998
2023-11-14 20:27:14 +01:00
942ff7df4d fix custom command's default value (#11043)
# Description
Fixes: #11033

Sorry for the issue, it's a regression which introduce by this pr:
#10456.
And this pr is going to fix it.

About the change: create a new field named `type_annotated` for
`Arg::Flag` and `Arg::Signature` instead of `arg_explicit_type`
variable.
When we meet a type in `TypeMode`, we set `type_annotated` field of the
argument to be true, then we know that if the arg have a annotated type
easily
2023-11-14 13:46:05 +01:00
51abe4c262 Bump rustix patch versions (#11044)
We have three different `rustix` minor versions. For two of them with
their previous patch versions a security advisory has been released:

https://github.com/advisories/GHSA-c827-hfw6-qwvm

Bump the patch versions, to resolve the potential vulnerability.

At the moment we haven't  assessed the potential impact on nushell,
whether we are exposed to the underlying issue.
2023-11-13 15:09:37 +01:00
e8e0526f57 Bump crate-ci/typos from 1.16.22 to 1.16.23 (#11038) 2023-11-13 11:28:28 +00:00
0b25385109 Revert "add color-backtrace crate (#10942)" (#11032)
The `color-backtrace` crate does not seem to either handle the terminal
modes well or operate in a way that the unwinding has not yet succeeded
to reach the backup disablement of the terminal raw mode in
`reedline::Reedline`'s `Drop` implementation.

This reverts commit d838871063.

Fixes #11029
2023-11-11 16:03:33 -06:00
415b1273b4 Fix (http) get HTTP_PROXY from $env (#11026)
# Description

This PR closes this
[issue](https://github.com/nushell/nushell/issues/11025)

# User-Facing Changes

Setting the environment variable HTTP_PROXY using $env.HTTP_PROXY will
work.

# Before

```bash
~> $env.HTTP_PROXY = http://127.0.0.1:7890 | http get https://lumtest.com/myip.json | get country
IR # (direct)
```

# After

```bash
~> $env.HTTP_PROXY = http://127.0.0.1:7890 | http get https://lumtest.com/myip.json | get country
DE # (with proxy)
```
2023-11-11 10:16:17 -06:00
6bee80dcd7 make reject support list input directly (#11024)
# Description
Fixes: #10895 

It's because `reject` and `select` command can't handle list of CellPath
input directly.
After this pr, the following should be ok:
```nushell
❯ [{'a': 1, 'b': 2, 'c': 3}, {'a': 1, 'b': 2, 'c': 3}] | reject ['a', 'b']
╭───┬───╮
│ # │ c │
├───┼───┤
│ 0 │ 3 │
│ 1 │ 3 │
╰───┴───╯
❯ [{'a': 1, 'b': 2, 'c': 3}, {'a': 1, 'b': 2, 'c': 3}] | select ['a', 'b']
╭───┬───┬───╮
│ # │ a │ b │
├───┼───┼───┤
│ 0 │ 1 │ 2 │
│ 1 │ 1 │ 2 │
╰───┴───┴───╯
```
2023-11-11 10:15:11 -06:00
588a078872 Fix tests for cargo.exe check command (#11022)
This pull request fixes the tests for the `cargo.exe check` command. The
tests were failing due `cargo check -h` sometimes reporting `cargo.exe`
as the binary and thus not containing `cargo check` in the output.

The fix involves using the `Command` module from the `std::process`
library to run the command and comparing its output to the expected
output. No changes were made to the codebase itself.
2023-11-10 21:15:11 +01:00
93096a07aa Implement Display for CellPath (#11023)
# Description
Because `CellPath::into_string` takes a borrowed `self`, I renamed it to
`to_string` to follow Rust [API
guidelines](https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv).
This then triggered the clippy lint
[inherent_to_string](https://rust-lang.github.io/rust-clippy/master/index.html#/inherent_to_string),
which is... correct! The current `CellPath::into_string` is being used
as if it were the `Display` implementation for `CellPath`.

# User-Facing Changes
Breaking API change for `nu-protocol`, since `CellPath::into_string` was
removed.
2023-11-10 21:12:51 +01:00
523d0bca16 Refactor flatten command (#11017)
# Description
Refactors the `flatten` command to remove a bunch of cloning. This was
down by passing ownership of the `Value` to `flat_value`, removing the
lifetime on `TableInside`, and using `Vec<Record>` in `FlattenedRows`
instead of a pair of `Vec` of columns and values.

For the quick benchmark below, it seems to be twice as fast now:
```nushell
let data = ls crates | where type == dir | each { ls $'($in.name)/**/*' }
timeit { for x in 0..1000 { $data | flatten } }
```
This took 550ms on v0.86.0 and only 230ms on this PR.
But considering that
```nushell
timeit { for x in 0..1000 { $data } }
```
takes 200ms on both versions, then the difference for `flatten` itself
is really 250ms vs 30ms -- 8x faster.
2023-11-10 13:18:02 +01:00
fe92051bb3 Adding support for Polars structs (#10943)
Provides support for reading Polars structs. This allows opening of
supported files (jsonl, parquet, etc) that contain rows with structured
data.

The following attached json lines
file([receipts.jsonl.gz](https://github.com/nushell/nushell/files/13311476/receipts.jsonl.gz))
contains a customer column with structured data. This json lines file
can now be loaded via `dfr open` and will render as follows:

<img width="525" alt="Screenshot 2023-11-09 at 10 09 18"
src="https://github.com/nushell/nushell/assets/56345/4b26ccdc-c230-43ae-a8d5-8af88a1b72de">


This also addresses some cleanup of date handling and utilizing
timezones where provided.

This pull request only addresses reading data from polars structs. I
will address converting nushell data to polars structs in a future
request as this change is large enough as it is.

---------

Co-authored-by: Jack Wright <jack.wright@disqo.com>
2023-11-09 19:00:59 -06:00
ee648ecb7d Refactor transpose and improve perf (#11013)
# Description

Generally elide a bunch of unnecessary clones. Both globally stopping to
clone the whole input data in a bunch of places where we need to read it
but also some minor places where we currently cloned.

As part of that, we can make the overwriting with `keep-all` and
`keep-last` inplace so the items don't need to be removed and repushed
to the record. 

# Benchmarking

```nu
timeit { scope commands | transpose -r }
```

Before ~24 ms now just ~5 ms

# User-Facing Changes
This can change the order of apperance in the transposed record with
`--keep-last`/`--keep-all`. Now the
order is determined by the first appearance and not by the last
appearance in the ingoing columns.
This mirrors the behavior when not passed `keep-all` or `keep-last`.

# Tests + Formatting
Sadly the `transpose` command is so far undertested for more complex
operations.
2023-11-09 22:41:38 +01:00
91920373b5 Match toolkit clippy settings to CI clippy settings (#10984)
# Description

I've had a few PRs fail clippy in CI after they pass `toolkit check pr`
because the clippy settings are different. This brings `toolkit.nu` into
alignment with CI and leaves notes to prompt future synchronization.

# User-Facing Changes

N/A

# Tests + Formatting

`cargo` output elided:

```
❯ toolkit check pr
running `toolkit fmt`
running `toolkit clippy`
running `toolkit clippy` on tests
running `toolkit clippy` on plugins
running `toolkit test`
running `toolkit test stdlib`
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`
```

# After Submitting

N/A
2023-11-09 19:07:15 +01:00
33a7bc405f Refactor drop columns to fix issues (#10903)
# Description
This PR refactors `drop columns` and fixes issues #10902 and #6846.
Tables with "holes" are now handled consistently, although still
somewhat awkwardly. That is, the columns in the first row are used to
determine which columns to drop, meaning that the columns displayed all
the way to the right by `table` may not be the columns actually being
dropped. For example, `[{a: 1}, {b: 2}] | drop column` will drop column
`a` instead of `b`. Before, this would give a list of empty records.

# User-Facing Changes
`drop columns` can now take records as input.
2023-11-09 13:51:46 +01:00
cd75640a90 Add Record::truncate for trimming based on len (#11004)
# Description
Compatible with `Vec::truncate` and `indexmap::IndexMap::truncate`

Found useful in #10903 for `drop column`

# Tests + Formatting
Doctest with the relevant edge-cases
2023-11-09 00:00:20 +01:00
0f600bc3f5 Improve case insensitivity consistency (#10884)
# Description

Add an extension trait `IgnoreCaseExt` to nu_utils which adds some case
insensitivity helpers, and use them throughout nu to improve the
handling of case insensitivity. Proper case folding is done via unicase,
which is already a dependency via mime_guess from nu-command.

In actuality a lot of code still does `to_lowercase`, because unicase
only provides immediate comparison and doesn't expose a `to_folded_case`
yet. And since we do a lot of `contains`/`starts_with`/`ends_with`, it's
not sufficient to just have `eq_ignore_case`. But if we get access in
the future, this makes us ready to use it with a change in one place.

Plus, it's clearer what the purpose is at the call site to call
`to_folded_case` instead of `to_lowercase` if it's exclusively for the
purpose of case insensitive comparison, even if it just does
`to_lowercase` still.

# User-Facing Changes

- Some commands that were supposed to be case insensitive remained only
insensitive to ASCII case (a-z), and now are case insensitive w.r.t.
non-ASCII characters as well.

# Tests + Formatting

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

---------

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2023-11-08 23:58:54 +01:00
aed4b626b8 Refactor env conversion, yeet Value::follow_cell_path_not... (#10926)
# Description
Replaces the only usage of `Value::follow_cell_path_not_from_user_input`
with some `Record::get`s.

# User-Facing Changes
Breaking change for `nu-protocol`, since
`Value::follow_cell_path_not_from_user_input` was deleted.

Nushell now reports errors for when environment conversions are not
closures.
2023-11-08 23:57:24 +01:00
92503e6571 Use record API in more parts of nu-protocol (#10928)
# Description

This is pretty complementary/orthogonal to @IanManske 's changes to
`Value` cellpath accessors in:
- #10925
- to a lesser extent #10926

## Steps
- Use `R.remove` in `Value.remove_data_at_cell_path`
- Pretty sound after #10875 (tests mentioned in commit message have been
removed by that)
- Update `did_you_mean` helper to use iterator
- Change `Value::columns` to return iterator
  - This is not a place of honor
- Use `Record::get` in `Value::get_data_by_key`
# User-Facing Changes
None intentional, potential edge cases on duplicated columns could
change (considered undefined behavior)

# Tests + Formatting
(-)
2023-11-08 23:03:08 +01:00
44c0db46e1 Add Record::drain to take out elements by range (#11002)
Matches the general behavior of `Vec::drain` or
`indexmap::IndexMap::drain`:
- Drop the remaining elements (implementing the unstable `keep_rest()`
would not be compatible with something like `indexmap`)
- No `AsRef<[T]>` or `Drain::as_slice()` behavior as this would make
layout assumptions.
- `Drain: DoubleEndedIterator`

Found useful in #10903
2023-11-08 22:54:02 +01:00
1fd3bc1ba6 Add exec command for Windows (#11001)
# Description
Based of the work and discussion in #10844, this PR adds the `exec`
command for Windows. This is done by simply spawning a
`std::process::Command` and then immediately exiting via
`std::process::exit` once the child process is finished. The child
process's exit code is passed to `exit`.

# User-Facing Changes
The `exec` command is now available on Windows, and there should be no
change in behaviour for Unix systems.
2023-11-08 14:50:25 -06:00
59ea28cf06 Use Record::get instead of Value functions (#10925)
# Description
Where appropriate, this PR replaces instances of
`Value::get_data_by_key` and `Value::follow_cell_path` with
`Record::get`. This avoids some unnecessary clones and simplifies the
code in some places.
2023-11-08 21:47:37 +01:00
435abadd8a Add special error case for alias (#10975)
Adds a special error, which is triggered by `alias foo=bar` style
commands. It adds a help string which recommends adding spaces.

Resolve #10958

---------

Co-authored-by: Jakub Žádník <kubouch@gmail.com>
2023-11-08 13:35:40 -06:00
86cd387439 Refactor and fix Config<->Value mechanism (#10896)
# Description
Our config exists both as a `Config` struct for internal consumption and
as a `Value`. The latter is exposed through `$env.config` and can be
both set and read.
Thus we have a complex bug-prone mechanism, that reads a `Value` and
then tries to plug anything where the value is unrepresentable in
`Config` with the correct state from `Config`.

The parsing involves therefore mutation of the `Value` in a nested
`Record` structure. Previously this was wholy done manually, with
indices.
To enable deletion for example, things had to be iterated over from the
back. Also things were indexed in a bunch of places. This was hard to
read and an invitation for bugs.

With #10876 we can now use `Record::retain_mut` to traverse the records,
modify anything that needs fixing, and drop invalid fields.

# Parts:

- Error messages now consistently use the correct spans pointing to the
problematic value and the paths displayed in some messages are also
aligned with the keys used for lookup.
- Reconstruction of values has been fixed for:
	- `table.padding`
	- `buffer_editor`
	- `hooks.command_not_found`
	- `datetime_format` (partial solution)
- Fix validation of `table.padding` input so value is not set (and
underflows `usize` causing `table` to run forever with negative values)
- New proper types for settings. Fully validated enums instead of
strings:
  - `config.edit_mode` -> `EditMode` 
  	- Don't fall back to vi-mode on invalid string
  - `config.table.mode` -> `TableMode`
- there is still a fall back to `rounded` if given an invalid
`TableMode` as argument to the `nu` binary
  - `config.completions.algorithm` -> `CompletionAlgorithm`
  - `config.error_style` -> `ErrorStyle`
    - don't implicitly fall back to `fancy` when given an invalid value.
- This should also shrink the size of `Config` as instead of 4x24 bytes
those fields now need only 4x1 bytes in `Config`
- Completely removed macros relying on the scope of `Value::into_config`
so we can break it up into smaller parts in the future.
- Factored everything into smaller files with the types and helpers for
particular topics.
- `NuCursorShape` now explicitly expresses the `Inherit` setting.
conversion to option only happens at the interface to `reedline`
2023-11-08 20:31:30 +01:00
edbf3aaccb Use Record's public API in a bunch of places (#10927)
# Description
Since #10841 the goal is to remove the implementation details of
`Record` outside of core operations.

To this end use Record iterators and map-like accessors in a bunch of
places. In this PR I try to collect the boring cases where I don't
expect any dramatic performance impacts or don't have doubts about the
correctness afterwards

- Use checked record construction in `nu_plugin_example`
- Use `Record::into_iter` in `columns`
- Use `Record` iterators in `headers` cmd
- Use explicit record iterators in `split-by`
- Use `Record::into_iter` in variable completions
- Use `Record::values` iterator in `into sqlite`
- Use `Record::iter_mut` for-loop in `default`
- Change `nu_engine::nonexistent_column` to use iterator
- Use `Record::columns` iter in `nu-cmd-base`
- Use `Record::get_index` in `nu-command/network/http`
- Use `Record.insert()` in `merge`
- Refactor `move` to use encapsulated record API
- Use `Record.insert()` in `explore`
- Use proper `Record` API in `explore`
- Remove defensiveness around record in `explore`
- Use encapsulated record API in more `nu-command`s

# User-Facing Changes
None intentional

# Tests + Formatting
(-)
2023-11-08 14:24:00 +01:00
b03ef56bcb Fix alpine docker file (#10992)
<!--
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!
-->

# Description

Fix docker file. For more detail see:
https://github.com/nushell/nushell/issues/8307
2023-11-08 06:30:34 -06:00
55316a9f27 Convert ShellError::DatetimeParseError to named fields (#10991)
# Description

Part of #10700

# User-Facing Changes

None

# Tests + Formatting

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting

N/A
2023-11-08 13:04:02 +01:00
d3ec3dc66b allow vscode-specific ansi escape sequence to set path (#10990)
# Description

This change allows the vscode-specific ansi escape sequence of
633;P;Cwd= to be run when nushell detects that it's running inside of
vscode's terminal. Otherwise the standard OSC7 will run. This is helpful
with ctrl+g inside of vscode terminal as well.

closed #10989 

/cc @CAD97 

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-11-07 19:38:30 -06:00
7ebae0b5f7 Refactor table cmd and nu-table with Record API (#10930)
# Description
- Simplify `table` record highlight with `.get_mut`
  - pretty straight forward
- Use record iterators in `table` abbreviation logic
- This required some rework if we go from guaranted contiguous arrays to
iterators
- Refactor `nu-table` internals to new record API
# User-Facing Changes
None intened

# Tests + Formatting
(-)
2023-11-08 01:22:47 +01:00
f45aed257f Refactor find in terms of clean Record API (#10929)
# Description
Rewrite `find` internals with the same principles as in #10927.

Here we can remove an unnecessary lookup accross all columns when not
narrowing find to particular columns

- Change `find` internal fns to use iterators
- Remove unnecessary quadratic lookup in `find`
- Refactor `find` record highlight logic
# User-Facing Changes
Should provide a small speedup when not providing `find --columns`

# Tests + Formatting
(-)
2023-11-08 01:06:22 +01:00
60da7abbc7 Use Vec for Closure captures (#10940)
# Description
Changes the `captures` field in `Closure` from a `HashMap` to a `Vec`
and makes `Stack::captures_to_stack` take an owned `Vec` instead of a
borrowed `HashMap`.

This eliminates the conversion to a `Vec` inside `captures_to_stack` and
makes it possible to avoid clones altogether when using an owned
`Closure` (which is the case for most commands). Additionally, using a
`Vec` reduces the size of `Value` by 8 bytes (down to 72).

# User-Facing Changes
Breaking API change for `nu-protocol`.
2023-11-08 00:43:28 +01:00
7a3cbf43e8 Convert ShellError::UnsupportedInput to named fields (#10971)
# Description

This is easy to do with rust-analyzer, but I didn't want to just pump
these all out without feedback.

Part of #10700

# User-Facing Changes

None

# Tests + Formatting

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting

N/A

---------

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2023-11-07 23:25:32 +01:00
45b02ce2ab Bump indexmap from 2.0.2 to 2.1.0 (#10969) 2023-11-07 15:36:34 +00:00
c039e4b3d0 Update description and error types for split-by (#10865)
# Description

`split-by` only works on a `Record`, the error type was updated to
match, and now uses a more-specific type. (Two type fixes for the price
of one!)

The `usage` was updated to say "record" as well

# User-Facing Changes

* Providing the wrong type to `split-by` now gives an error messages
with the correct required input type

Previously:

```
❯ ls | get name | split-by type
Error:   × unsupported input
   ╭─[entry #267:1:1]
 1 │ ls | get name | split-by type
   ·      ─┬─
   ·       ╰── requires a table with one row for splitting
   ╰────
```

With this PR:

```
❯ ls | get name | split-by type
Error: nu:🐚:type_mismatch

  × Type mismatch.
   ╭─[entry #1:1:1]
 1 │ ls | get name | split-by type
   ·      ─┬─
   ·       ╰── requires a record to split
   ╰────
```

# Tests + Formatting

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting

Only generated commands need to be updated

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-11-07 16:27:10 +01:00
9b202d560d Limit run-external --redirect-combine sh test to not(Windows) (#10905)
# Description

Limit the test `-p nu-command --test main
commands::run_external::redirect_combine` which uses `sh` to running on
`not(Windows)` like is done for other tests assuming unixy CLI items;
`sh` doesn't exist on Windows.

# User-Facing Changes

None; this is a change to tests only.

# Tests + Formatting

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`
2023-11-07 16:35:24 +08:00
1874082a2c allow items to properly evaluate block settings (#10980)
# Description

@jntrnr discovered that `items` wasn't properly setting the
`eval_block_with_early_return()` block settings. This change fixes that
which allows `echo` to be redirected and therefore pass data through the
pipeline.

Without `echo`
```nushell
❯ { new: york, san: francisco } | items {|key, value| $'($key) ($value)' }
╭─┬─────────────╮
│0│new york     │
│1│san francisco│
╰─┴─────────────╯
```
With `echo`
```nushell
❯ { new: york, san: francisco } | items {|key, value| echo $'($key) ($value)' }
╭─┬─────────────╮
│0│new york     │
│1│san francisco│
╰─┴─────────────╯
```

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-11-06 12:15:37 -06:00
1359b26da2 update items example to send data through the pipeline (#10976)
# Description

This PR updates the `items` example so that it doesn't use `echo`.
`echo` now works like print unless it's being redirected, so it doesn't
send values through the pipeline anymore like the example showed.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-11-06 06:59:13 -06:00
b9bc527d27 Bump crate-ci/typos from 1.16.21 to 1.16.22 (#10968) 2023-11-06 00:57:55 +00:00
51cdd9fbb2 Bump hustcer/setup-nu from 3.7 to 3.8 (#10967) 2023-11-06 00:56:59 +00:00
d838871063 add color-backtrace crate for more intuitive backtraces (#10942)
# Description

Just throwing up this PR because color-backtrace seemed to produce more
useful backtraces. Just curious what others think.

Did this:
1. RUST_BACKTRACE=full cargo r
2. ❯ def test01 [] {
     let sorted = [storm]
     $sorted | range 1.. | zip ($sorted | range ..(-2))
  }
3. test01

I like how it shows the code snippet.


![image](https://github.com/nushell/nushell/assets/343840/1302e766-dfac-4749-a465-85bf53060532)

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-11-05 17:19:06 -06:00
4d16d92847 remove unnecessary files (#10966)
# Description

I committed things in my nushell folder that I shouldn't have. oops.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-11-05 15:23:21 -06:00
fc01701a41 updates trash dependency to 3.1.2 (#10965)
# Description

This PR updates the trash dependency from 3.1.0 to 3.1.2 for better
support on FreeBSD.

closes #10961

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-11-05 15:00:13 -06:00
51d5d0eac8 Restore test_config tests (#10954)
# Description

These tests got orphaned and they would be a good place to test behavior
I want to add for #10867

# User-Facing Changes

None

# Tests + Formatting

Tests were updated to account for removed test infrastructure

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting

N/A
2023-11-04 15:18:57 -05:00
81d00f71a9 Show plugin extra usage and search terms (#10952)
# Description

The `PluginSignature` type supports extra usage but this was not
available in `plugin_name --help`. It also supports search terms but
these did not appear in `help commands`

New behavior show below is the "Extra usage for nu-example-1" line and
the "Search terms:" line

```
❯ nu-example-1 --help
PluginSignature test 1 for plugin. Returns Value::Nothing

Extra usage for nu-example-1

Search terms: example

Usage:
  > nu-example-1 {flags} <a> <b> (opt) ...(rest)

Flags:
  -h, --help - Display the help message for this command
  -f, --flag - a flag for the signature
  -n, --named <String> - named string

Parameters:
  a <int>: required integer value
  b <string>: required string value
  opt <int>: Optional number (optional)
  ...rest <string>: rest value string

Examples:
  running example with an int value and string value
  > nu-example-1 3 bb
```

Search terms are also available in `help commands`:

```
❯ help commands | where name == "nu-example-1" | select name search_terms
╭──────────────┬──────────────╮
│     name     │ search_terms │
├──────────────┼──────────────┤
│ nu-example-1 │ example      │
╰──────────────┴──────────────╯
```

# User-Facing Changes

Users can now see plugin extra usage and search terms 

# Tests + Formatting

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting

N/A
2023-11-04 15:12:58 -05:00
77fbf3e2d2 better help message for MissingPositional error (#10949)
Added "Use `--help` for more information." to the help of
MissingPositional error
- this PR should close
[#10946](https://github.com/nushell/nushell/issues/10946)

**Before:**

![image](https://github.com/nushell/nushell/assets/1835944/629aeaae-e985-41aa-a791-05ef062e988e)

**After:**

![image](https://github.com/nushell/nushell/assets/1835944/0bc1868c-ffed-4440-ad98-2cf29aa8c656)



<!--
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!
-->

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->

---------

Co-authored-by: Denis Zorya <denis.zorya@trafigura.com>
2023-11-04 12:24:21 -05:00
f565661f42 Fix issues with error make refactor (#10950)
- Replaced one error return `None` with a `help` option.
- Fixed example.
- Added comments highlighting correct early returns.

Fix #10947
2023-11-04 17:23:43 +01:00
1a864ea6f4 Refactor error make (#10923)
- Replaced `start`/`end` with span.
- Fixed standard library.
- Add `help` option.
- Add a couple more errors for invalid record types.

Resolve #10914


# Description



# User-Facing Changes

- **BREAKING CHANGE:** `error make` now takes in `span` instead of
`start`/`end`:

  ```Nushell
  error make {
      msg: "Message"
      label: {
          text: "Label text"
          span: (metadata $var).span
      }
  }
  ```
- `error make` now has a `help` argument for custom error help.
2023-11-03 10:09:33 -05:00
c1738620e3 remove unwraps in registry_query command (#10936)
# Description

After talking to @CAD97, I decided to change these unwraps to expects.
See the comments. The bigger question is, how did unwrap pass the CI?

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-11-03 08:12:36 -05:00
56e35fc3f9 Reduce element shifting in Record::retain_mut (#10915)
# Description
Replaces the `Vec::remove` in `Record::retain_mut` with some swaps which
should eliminate the `O(n^2)` complexity due to repeated shifting of
elements.
2023-11-02 20:01:46 +01:00
29591c97a7 Add "shape_keyword" to default config (#10922)
Keep it in sync with Rust code.
Mainly so that people know they can style keywords.

I discovered this while trying to change color of "else".
2023-11-02 19:59:17 +01:00
697dee6750 Change input list to return null (#10913)
Now the `input list` command, when nothing is selected, will return a
null instead of empty string or an empty list.

Resolves #10909.


# User-Facing Changes

`input list` now returns a `null` when nothing is selected.
2023-11-02 19:57:06 +01:00
0ca8fcf58c Integrated Language Server (#10723)
# Description

This commit integrates a language server into nushell so that IDEs don't
have to convert CLI option back and forth.

- fixes https://github.com/nushell/vscode-nushell-lang/issues/117
- fixes https://github.com/jokeyrhyme/nuls/issues/8

Tracking tasks


- [x]
[textDocument/hover](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover)
-> `nu --ide-hover`
- [x]
[textDocument/completion](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion)
-> `nu --ide-complete`
- [x]
[textDocument/definition](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition)
-> `nu --ide-goto-def`
- ~~[ ]
[textDocument/didChange](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didChange),
[textDocument/didClose](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didClose),
and
[textDocument/didOpen](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didOpen)~~
(will be done in a follow-up PR)
- ~~[ ]
[textDocument/inlayHint](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_inlayHint)
-> `nu --ide-check`~~ (will be done in a follow-up PR)
- ~~[ ]
[textDocument/publishDiagnostics](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_publishDiagnostics)
-> `nu --ide-check`~~ (will be done in a follow-up PR)
- ~~[ ]
[workspace/configuration](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_configuration)~~
(will be done in a follow-up PR)
- ~~[ ]
[workspace/didChangeConfiguration](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_didChangeConfiguration)~~
(will be done in a follow-up PR)


# User-Facing Changes

The command line options `--lsp` will start a LSP server.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-11-02 10:18:57 -05:00
a46048f362 Use Record APIs in nu-protocol/nu-engine (#10917)
# Description
Consequences of #10841

This does not yet make the assumption that columns are always
duplicated. Follow the existing logic here

- Use saner record API in `nu-engine/src/eval.rs`
- Use checked record construction in `nu-engine/src/scope.rs`
- Use `values` iterator in `nu-engine/src/scope.rs`
- Use `columns` iterator in `nu_engine::get_columns()`
- Start using record API in `value/mod.rs`
- Use `.insert` in `eval_const.rs` Record code
- Record API for `eval_const.rs` table code

# User-Facing Changes
None

# Tests + Formatting
None
2023-11-01 23:19:58 +01:00
0569a9c92e Disallow duplicated columns in table literals (#10875)
# Description
Pretty much all operations/commands in Nushell assume that the column
names/keys in a record and thus also in a table (which consists of a
list of records) are unique.
Access through a string-like cell path should refer to a single column
or key/value pair and our output through `table` will only show the last
mention of a repeated column name.

```nu
[[a a]; [1 2]]
╭─#─┬─a─╮
│ 0 │ 2 │
╰───┴───╯
```

While the record parsing already either errors with the
`ShellError::ColumnDefinedTwice` or silently overwrites the first
occurence with the second occurence, the table literal syntax `[[header
columns]; [val1 val2]]` currently still allowed the creation of tables
(and internally records with more than one entry with the same name.

This is not only confusing, but also breaks some assumptions around how
we can efficiently perform operations or in the past lead to outright
bugs (e.g. #8431 fixed by #8446).

This PR proposes to make this an error.
After this change another hole which allowed the construction of records
with non-unique column names will be plugged.

## Parts
- Fix `SE::ColumnDefinedTwice` error code
- Remove previous tests permitting duplicate columns
- Deny duplicate column in table literal eval
- Deny duplicate column in const eval
- Deny duplicate column in `from nuon`

# User-Facing Changes
`[[a a]; [1 2]]` will now return an error:

```
Error: nu:🐚:column_defined_twice

  × Record field or table column used twice
   ╭─[entry #2:1:1]
 1 │ [[a a]; [1 2]]
   ·   ┬ ┬
   ·   │ ╰── field redefined here
   ·   ╰── field first defined here
   ╰────
```

this may under rare circumstances block code from evaluating.

Furthermore this makes some NUON files invalid if they previously
contained tables with repeated column names.

# Tests + Formatting
Added tests for each of the different evaluation paths that materialize
tables.
2023-11-01 21:25:35 +01:00
c1ca10ffd1 allow compact to also compact empty strings (#10912)
# Description

This change allows `compact` to also compact things with empty strings,
empty lists, and empty records if the `--empty` switch is used. Let's
add a quality-of-life improvement here to just compact all this mess. If
this is a bad idea, please cite examples demonstrating why.

```
❯ [[name position]; [Francis Lead] [Igor TechLead] [Aya null]] | compact position
╭#┬─name──┬position╮
│0│Francis│Lead    │
│1│Igor   │TechLead│
╰─┴───────┴────────╯
❯ [[name position]; [Francis Lead] [Igor TechLead] [Aya ""]] | compact position --empty
╭#┬─name──┬position╮
│0│Francis│Lead    │
│1│Igor   │TechLead│
╰─┴───────┴────────╯
❯ [1, null, 2, "", 3, [], 4, {}, 5] | compact
╭─┬─────────────────╮
│0│                1│
│1│                2│
│2│                 │
│3│                3│
│4│[list 0 items]   │
│5│                4│
│6│{record 0 fields}│
│7│                5│
╰─┴─────────────────╯
❯ [1, null, 2, "", 3, [], 4, {}, 5] | compact --empty
╭─┬─╮
│0│1│
│1│2│
│2│3│
│3│4│
│4│5│
╰─┴─╯
```

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-11-01 13:56:25 -05:00
15c22db8f4 Make FromValue take owned Values (#10900)
# Description
Changes `FromValue` to take owned `Value`s instead of borrowed `Value`s.
This eliminates some unnecessary clones (e.g., in `call_ext.rs`).

# User-Facing Changes
Breaking API change for `nu_protocol`.
2023-10-31 19:47:00 +01:00
1c52b112c8 Return external file completions if not empty (#10898)
<!--
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!
-->

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->
If an external completer is used and it returns no completions for a
filepath, we fall back to the builtin path completer.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

Path completions will remain consistent with the use of an external
completer.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-10-31 11:32:21 -05:00
275dba82d5 fix: preserve path when completing intermediate directory (#10831)
<!--
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!
-->

- fixes #10766

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

If the partial supplied to the completion function is shorter than the
span, the cursor is in between the path, we are trying to complete an
intermediate directory. In such a case we:
- only suggest directory names
- don't append the slash since it is already present
- only complete the path till the component the cursor is on

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

Intermediate directories can be completed without erasing the rest of
the path.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-10-31 07:39:14 -05:00
cf7040a215 Refactor: introduce a gen_save_call function to reduce duplicate code (#10852)
make a little refactor on relative code to make it more
readable.
2023-10-31 10:45:36 +08:00
72cb4b6032 Reuse Closure type in Value::Closure (#10894)
# Description
Reuses the existing `Closure` type in `Value::Closure`. This will help
with the span refactoring for `Value`. Additionally, this allows us to
more easily box or unbox the `Closure` case should we chose to do so in
the future.

# User-Facing Changes
Breaking API change for `nu_protocol`.
2023-10-30 23:34:23 +01:00
d4cbab454e Change category of scope commands to core (#10892)
close #10802
2023-10-30 19:55:40 +01:00
3645178ff1 Convert "pure" macros to pure fn in config.rs (#10893)
# Description
These macros simply took a `Span` and a shared reference to `Config` and
returned a Value, for better readability and reasoning about their
behavior convert them to simple function as they don't do anything
relevant with their macro powers.


# User-Facing Changes
None

# Tests + Formatting
(-)
2023-10-30 19:54:59 +01:00
005180f269 Add Record::remove/retain/retain_mut (#10876)
# Description
While we have now a few ways to add items or iterate over the
collection, we don't have a way to cleanly remove items from `Record`.

This PR fixes that:

- Add `Record.remove()` to remove by key
- makes the assumption that keys are unique, so can not be used
universally, yet (see #10875 for an important example)
- Add naive `Record.retain()` for inplace removal
- This follows the two separate `retain`/`retain_mut` in the Rust std
library types, compared to the value-mutating `retain` in `indexmap`
- Add `Record.retain_mut()` for one-pass pruning

Continuation of #10841 

# User-Facing Changes
None yet.

# Tests + Formatting
Doctests for the `retain`ing fun
2023-10-30 19:51:28 +01:00
72f7b9b7cc Add umkdir command (#10785)
A `mkdir` command, which uses `uu_mkdir` as backend.

close #10515.
2023-10-30 07:59:48 -05:00
3f61ca19f0 Bump hustcer/setup-nu from 3.6 to 3.7 (#10878) 2023-10-30 12:53:22 +00:00
d8c59eddb3 Bump rust-ini from 0.19.0 to 0.20.0 (#10882) 2023-10-30 11:56:13 +00:00
ac43372618 Bump scraper from 0.17.1 to 0.18.1 (#10879) 2023-10-30 11:53:26 +00:00
dccb2b48f3 Bump crate-ci/typos from 1.16.20 to 1.16.21 (#10877) 2023-10-30 10:19:51 +00:00
2e68e6ddbf allow sort-by to work with records (#10870)
# Description

This PR restores and old functionality that must of been broken with the
input_output_types() updating. It allows commands like this to work
again.

```nushell
open $nu.history-path | 
  get history.command_line | 
  split column ' ' cmd | 
  group-by cmd --to-table | 
  update items {|u| $u.items | length} | 
  sort-by items -r | 
  first 10 | 
  table -n 1
```
output
```
╭#─┬group─┬items╮
│1 │exit  │ 3004│
│2 │ls    │ 2591│
│3 │git   │ 1678│
│4 │help  │ 1549│
│5 │open  │ 1374│
│6 │cd    │ 1186│
│7 │cargo │  944│
│8 │let   │  784│
│9 │source│  755│
│10│z     │  486│
╰#─┴group─┴items╯
```

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-10-29 08:22:20 -05:00
3dfe1a4f0e group-by now returns a table instead of a record (#10848)
# Description

Previously `group-by` returned a record containing each group as a
column. This data layout is hard to work with for some tasks because you
have to further manipulate the result to do things like determine the
number of items in each group, or the number of groups. `transpose` will
turn the record returned by `group-by` into a table, but this is
expensive when `group-by` is run on a large input.

In a discussion with @fdncred [several
workarounds](https://github.com/nushell/nushell/discussions/10462) to
common tasks were discussed, but they seem unsatisfying in general.

Now when `group-by --to-table` is used a table is returned with the
columns "groups" and "items" making it easier to do things like count
the number of groups (`| length`) or count the number of items in each
group (`| each {|g| $g.items | length`)

# User-Facing Changes

* `group-by` returns a `table` with "group" and "items" columns instead
of a `record` with one column per group name

# Tests + Formatting

Tests for `group-by` were updated

# After Submitting

* No breaking changes were made. The new `--to-table` switch should be
added automatically to the [`group-by`
documentation](https://www.nushell.sh/commands/docs/group-by.html)
2023-10-28 14:15:14 -05:00
c87bac04c0 Add common map-like API to nu_protocol::Record (#10841)
# Description

> Our `Record` looks like a map, quacks like a map, so let's treat it
with the API for a map

Implement common methods found on e.g. `std::collections::HashMap` or
the insertion-ordered [indexmap](https://docs.rs/indexmap).

This allows contributors to not have to worry about how to get to the
relevant items and not mess up the assumptions of a Nushell record.

## Record assumptions
- `cols` and `vals` are of equal length
- for all practical purposes, keys/columns should be unique

## End goal

The end goal of the upcoming series of PR's is to allow us to make
`cols` and `vals` private.
Then it would be possible to exchange the backing datastructure to best
fit the expected workload.
This could be statically (by finding the best balance) or dynamically by
using an `enum` of potential representations.

## Parts
- Add validating explicit part constructor
`Record::from_raw_cols_vals()`
- Add `Record.columns()` iterator
- Add `Record.values()` iterator
- Add consuming `Record.into_values()` iterator
- Add `Record.contains()` helper
- Add `Record.insert()` that respects existing keys
- Add key-based `.get()`/`.get_mut()` to `Record`
- Add `Record.get_index()` for index-based access
- Implement `Extend` for `Record` naively
- Use checked constructor in `record!` macro
- Add `Record.index_of()` to get index by key

# User-Facing Changes
None directly

# Developer facing changes
You don't have to roll your own record handling and can use a familiar
API

# Tests + Formatting
No explicit unit tests yet. Wouldn't be too tricky to validate core
properties directly.
Will be exercised by the following PRs using the new
methods/traits/iterators.
2023-10-28 15:18:41 +02:00
4b301710d3 Convert more examples and tests to record! macro (#10840)
# Description
Use `record!` macro instead of defining two separate `vec!` for `cols`
and `vals` when appropriate.
This visually aligns the key with the value.
Further more you don't have to deal with the construction of `Record {
cols, vals }` so we can hide the implementation details in the future.

## State

Not covering all possible commands yet, also some tests/examples are
better expressed by creating cols and vals separately.

# User/Developer-Facing Changes
The examples and tests should read more natural. No relevant functional
change

# Bycatch

Where I noticed it I replaced usage of `Value` constructors with
`Span::test_data()` or `Span::unknown()` to the `Value::test_...`
constructors. This should make things more readable and also simplify
changes to the `Span` system in the future.
2023-10-28 14:52:31 +02:00
7d67ca3652 [nu-cmd-base] add missing LICENSE text (#10855)
# Description
Adds license text to the `nu-cmd-base` crate, copied from `nu-cmd-lang`

# User-Facing Changes
N/A

# Tests + Formatting
```
$ cargo package --allow-dirty --no-verify
$ bsdtar tf ../../target/package/nu-cmd-base-0.86.1.crate | grep LICENSE
```

# After Submitting
N/A

Signed-off-by: Michel Lind <salimma@fedoraproject.org>
2023-10-27 15:55:03 -05:00
01d8961eb7 use to_lowercase in str downcase (#10850)
# Description
as we can see in the [documentation of
`str.to_lowercase`](https://doc.rust-lang.org/std/primitive.str.html#method.to_lowercase),
not only ASCII symbols have lower and upper variants.

- `str upcase` uses the correct method to convert the string

7ac5a01e2f/crates/nu-command/src/strings/str_/case/upcase.rs (L93)
- `str downcase` incorrectly converts only ASCII characters

7ac5a01e2f/crates/nu-command/src/strings/str_/case/downcase.rs (L124)

this PR uses `str.to_lower_case` instead of `str.to_ascii_lowercase` in
`str downcase`.

# User-Facing Changes
- upcase still works fine
```nushell
~ l> "ὀδυσσεύς" | str upcase
ὈΔΥΣΣΕΎΣ
```
- downcase now works

👉 before
```nushell
~ l> "ὈΔΥΣΣΕΎΣ" | str downcase
ὈΔΥΣΣΕΎΣ
```
👉 after
```nushell
~ l> "ὈΔΥΣΣΕΎΣ" | str downcase
ὀδυσσεύς
```

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

adds two tests
- `non_ascii_upcase`
- `non_ascii_downcase`

# After Submitting
2023-10-27 19:16:17 +02:00
7ac5a01e2f deprecate glob --not in favor of glob --exclude (#10827)
# Description
looking at the [Wax documentation about
`wax::Walk.not`](https://docs.rs/wax/latest/wax/struct.Walk.html#examples),
especially
> therefore does not read directory trees from the file system when a
directory matches an [exhaustive glob
expression](https://docs.rs/wax/latest/wax/trait.Pattern.html#tymethod.is_exhaustive)

> **Important**
> in the following of this PR description, i talk about *pruning* and a
`--prune` option, but this has been changed to *exclusion* and
`--exclude` after a discussion with @fdncred.

this looks like a *pruning* operation to me, right? 😮 
i wanted to make the `glob` option `--not` clearer about that, because
>   -n, --not <List(String)> - Patterns to exclude from the results

from `help glob` is not very explicit about whether the search is pruned
when entering a directory matching a pattern in `--not` or just removing
it from the output 😕

## changelog
this PR proposes to rename the `glob --not` option to `glob --prune` and
make it's documentation more explicit 😋

## benchmarking
to support the *pruning* behaviour put forward above, i've run a
benchmark
1. define two closures to compare the behaviour between removing
patterns manually or using `--not`
```nushell
let where = {
    [.*/\.local/.*, .*/documents/.*, .*/\.config/.*]
        | reduce --fold (glob **) {|pat, acc| $acc | where $it !~ $pat}
        | length
}
```
```nushell
let not = { glob ** --not [**/.local/**, **/documents/**, **/.config/**] | length }
```
2. run the two to make sure they give similar results
```nushell
> do $where
33424
```
```nushell
> do $not
33420
```
👌 
3. measure the performance
```nushell
use std bench
```
```nushell
> bench --verbose --pretty --rounds 25 $not
44ms 52µs 285ns +/- 977µs 571ns
```
```nushell
> bench --verbose --pretty --rounds 5 $where
1sec 250ms 187µs 99ns +/- 8ms 538µs 57ns
```

👉 we can see that the results are (almost) the same but
`--not` is much faster, looks like pruning 😋

# User-Facing Changes
- `--not` will give a warning message but still work
- `--prune` will work just as `--not` without warning and with a more
explicit doc
- `--prune` and `--not` at the same time will give an error

# Tests + Formatting
this PR fixes the examples of `glob` using the `--not` option.

# After Submitting
prepare the removal PR and mention in release notes.
2023-10-25 17:11:57 +02:00
e2fb0e5b82 implement whoami using uutils (#10488)
<!--
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!
-->

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

Implements `whoami` using the `whoami` command from uutils as backend.
This is a draft because it depends on
https://github.com/uutils/coreutils/pull/5310 and a new release of
uutils needs to be made (and the paths in `Cargo.toml` should be
updated). At this point, this is more of a proof of concept 😄

Additionally, this implements a (simple and naive) conversion from the
uutils `UResult` to the nushell `ShellError`, which should help with the
integration of other utils, too. I can split that off into a separate PR
if desired.

I put this command in the "platform" category. If it should go somewhere
else, let me know!

The tests will currently fail, because I've used a local path to uutils.
Once the PR on the uutils side is merged, I'll update it to a git path
so that it can be tested and runs on more machines than just mine.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

New `whoami` command. This might break some users who expect the system
`whoami` command. However, the result of this new command should be very
close, just with a nicer help message, at least for Linux users. The
default `whoami` on Windows is quite different from this implementation:
https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/whoami

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-10-25 09:53:52 -05:00
a11e41332c expand paths and split PATH in std path add (#10710)
related to
-
https://discord.com/channels/601130461678272522/614593951969574961/1162406310155923626

# Description
this PR
- does a bit of minor refactoring
- makes sure the input paths get expanded
- makes sure the input PATH gets split on ":"
- adds a test
- fixes the other tests

# User-Facing Changes
should give a better overall experience with `std path add`

# Tests + Formatting
adds a new test case to the `path_add` test and fixes the others.

# After Submitting
2023-10-25 16:43:27 +02:00
f3656f7822 sync $env.config.filesize.metric (#10277)
# Description
just noticed `$env.config.filesize.metric` is not the same in
`default_config.nu` and `config.rs`

# User-Facing Changes
filesizes will show in "binary" mode by default when using the default
config files, i.e. `kib` instead of `kb`.

# Tests + Formatting

# After Submitting
2023-10-25 16:42:24 +02:00
38f4ab0bc9 updated NU_LIB_DIRS delimiter for command line (#10837)
# Description

This PR corrects some help text by stating what the real delimiter is
for the `--include-path` command line parameter which is `char
record_sep` aka `\x1e`. Up to this point, this has really only been used
for the vscode extension to setup the NU_LIB_DIRS env var correctly. We
tried `:` and `;` and neither would work so we had to choose something
that wouldn't be confused so easily.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-10-25 09:38:20 -05:00
f35741d50e redirection: fix internal commands error with o+e> redirection (#10816)
# Description
Currently the following command is broken:
```nushell
echo a o+e> 1.txt
```

It's because we don't redirect output of `echo` command. This pr is
trying to fix it.
2023-10-25 16:35:51 +02:00
d93315d8f5 Fix describe -d for lazy records (#10836)
<!--
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!
-->

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->
This PR fixes an overlook from a previous PR. It now correctly returns
the details on lazy records.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
Describe detailed now returns the expected result.
2023-10-25 08:04:37 -05:00
8429aec57f readd update flag to cp command (#10824)
# Description
- this PR should close #10819


# User-Facing Changes
Behaviour is similar to pre 0.86.0 behaviour of the cp command and
should as such not have a user-facing change, only compared to the
current version, were the option is readded.


# After Submitting
I guess the documentation will be automatically updated and as this
feature is no further highlighted, probably, no more work will be needed
here.

# Considerations
coreutils actually allows a third option:
```
pub enum UpdateMode {
    // --update=`all`,
    ReplaceAll,
    // --update=`none`
    ReplaceNone,
    // --update=`older`
    // -u
    ReplaceIfOlder,
}
```
namely `ReplaceNone`, which I have not added. Also I think that
specifying `--update 'abc'` is non functional.
2023-10-25 11:30:13 +02:00
f043a8a8ff redirect should have a target (#10835)
# Description
Fixes:  #10830 

The issue happened during lite-parsing, when we want to put a
`LiteElement` to a `LitePipeline`, we do nothing if relative redirection
target is empty.

So the command `echo aaa o> | ignore` will be interpreted to `echo aaa |
ignore`.

This pr is going to check and return an error if redirection target is
empty.

# User-Facing Changes
## Before
```
❯ echo aaa o> | ignore   # nothing happened
```

## After
```nushell
❯ echo aaa o> | ignore
Error: nu::parser::parse_mismatch

  × Parse mismatch during operation.
   ╭─[entry #1:1:1]
 1 │ echo aaa o> | ignore
   ·          ─┬
   ·           ╰── expected redirection target
   ╰────
```
2023-10-25 11:19:35 +02:00
c6016d7659 Dataframe support for small int types (#10828)
Turned features to allow signed and unsigned 8 and 16 bit types.

---------

Co-authored-by: Jack Wright <jack.wright@disqo.com>
2023-10-24 21:25:21 -05:00
78b4472b32 Support pattern matching null literals (#10829)
# Description
Support pattern matching against the `null` literal.  Fixes #10799 

### Before
```nushell
> match null { null => "success", _ => "failure" }
failure
```

### After
```nushell
> match null { null => "success", _ => "failure" }
success
```

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
Users can pattern match against a `null` literal.
2023-10-25 06:30:45 +08:00
cb754befe9 fix: Ensure consistent vals and cols when parsing with --flexible (#10814)
# Description
`from tsv` and `from csv` both support a `--flexible` flag. This flag
can be used to "allow the number of fields in records to be variable".

Previously, a record's invariant that `rec.cols.len() == rec.vals.len()`
could be broken during parsing. This can cause runtime errors as in
#10693. Other commands, like `select` were also affected.

The inconsistencies are somewhat hard to see, as most nushell code
assumes an equal number of columns and values.

# Before

### Fewer values than columns
```nushell
> let record = (echo "one,two\n1" | from csv --flexible | first)
# There are two columns
> $record | columns | to nuon
[one, two]
# But only one value
> $record | values | to nuon
[1]
# And printing the record doesn't show the second column!
> $record | to nuon
{one: 1}
```

### More values than columns
```nushell
> let record = (echo "one,two\n1,2,3" | from csv --flexible | first)
# There are two columns
> $record | columns | to nuon
[one, two]
# But three values
> $record | values | to nuon
[1, 2, 3]
# And printing the record doesn't show the third value!
> $record | to nuon
{one: 1, two: 2}
```
# After

### Fewer values than columns
```nushell
> let record = (echo "one,two\n1" | from csv --flexible | first)
# There are two columns
> $record | columns | to nuon
[one, two]
# And a matching number of values
> $record | values | to nuon
[1, null]
# And printing the record works as expected
> $record | to nuon
{one: 1, two: null}
```

### More values than columns
```nushell
> let record = (echo "one,two\n1,2,3" | from csv --flexible | first)
# There are two columns
> $record | columns | to nuon
[one, two]
# And a matching number of values
> $record | values | to nuon
[1, 2]
# And printing the record works as expected
> $record | to nuon
{one: 1, two: 2}
```

# User-Facing Changes
Using the `--flexible` flag with `from csv` and `from tsv` will not
result in corrupted record state.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-10-24 15:54:26 -05:00
0588a4fc19 Make debug info lazy (#10728)
<!--
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!
-->

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

* Makes the `debug info` lazy which greatly improves performance.
* Adds a `thread id` attribute

![Screenshot 2023-10-15
211940](https://github.com/nushell/nushell/assets/25441359/b8457a30-ebf7-4731-9e13-17635501f029)

![image](https://github.com/nushell/nushell/assets/25441359/010ed35b-9f50-4fc6-8650-b68b29d5a9cd)


# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

`threadid` column added.

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-10-24 12:48:05 -05:00
ff3a0a0de3 fix main not building due to errors later found in describe (#10821)
# Description

This is just a fixup PR. There was a describe PR that passed CI but then
later didn't pass main. This PR fixes that issue.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-10-23 13:22:32 -05:00
c799f77577 Add detailed flag for describe (#10795)
- Add `detailed` flag for `describe`

- Improve detailed describe and better format when running examples.

# Rationale

For now, neither `describe` nor any of the `debug` commands provide an
easy and structured way of inspecting the data's type and more. This
flag provides a structured way of getting such information. Allows also
to avoid the rather hacky solution
```nu
$in | describe | str replace --regex '<.*' ''
```

# User-facing changes

Adds a new flag to ``describe`.
2023-10-23 09:12:11 -05:00
d3182a6737 Revert "Bump regex from 1.9.6 to 1.10.2" (#10818)
Reverts nushell/nushell#10812

This goes back to a version of `regex` and its dependencies that is
shared with a lot of our other dependencies. Before this we did not
duplicate big dependencies of `regex` that affect binary size and
compile time.

As there is no known bug or security problem we suffer from, we can wait
on receiving the performance improvements to `regex` with the rest of
our `regex` dependents.
2023-10-23 09:11:32 -05:00
b5e09b8a30 Improve registry value return types (#10806)
r? @fdncred
Last one, I hope. At least short of completely redesigning `registry
query`'s interface. (Which I wouldn't implement without asking around
first.)

# Description

User-Facing Changes has the general overview. Inline comments provide a
lot of justification on specific choices. Most of the type conversions
should be reasonably noncontroversial, but expanding `REG_EXPAND_SZ`
needs some justification. First, an example of the behavior there:

```shell
> # release nushell:
> version | select version commit_hash | to md --pretty
| version | commit_hash                              |
| ------- | ---------------------------------------- |
| 0.85.0  | a6f62e05ae |
> registry query --hkcu Environment TEMP | get value
%USERPROFILE%\AppData\Local\Temp

> # with this patch:
> version | select version commit_hash | to md --pretty
| version | commit_hash                              |
| ------- | ---------------------------------------- |
| 0.86.1  | 0c5a4c991f |
> registry query --hkcu Environment TEMP | get value
C:\Users\CAD\AppData\Local\Temp

> # Microsoft CLI tooling behavior:
> ^pwsh -c `(Get-ItemProperty HKCU:\Environment).TEMP`
C:\Users\CAD\AppData\Local\Temp
> ^reg query HKCU\Environment /v TEMP
HKEY_CURRENT_USER\Environment
    TEMP    REG_EXPAND_SZ    %USERPROFILE%\AppData\Local\Temp
```

As noted in the inline comments, I'm arguing that it makes more sense to
eagerly expand the %EnvironmentString% placeholders, as none of
Nushell's path functionality will interpret these placeholders. This
makes the behavior of `registry query` match the behavior of pwsh's
`Get-ItemProperty` registry access, and means that paths (the most
common use of `REG_EXPAND_SZ`) are actually usable.

This does *not* break nu_script's
[`update-path`](https://github.com/nushell/nu_scripts/blob/main/sourced/update-path.nu);
it will just be slightly inefficient as it will not find any
`%Placeholder%`s to manually expand anymore. But also, note that
`update-path` is currently *wrong*, as a path including
`%LocalAppData%Low` is perfectly valid and sometimes used (to go to
`Appdata\LocalLow`); expansion isn't done solely on a path segment
basis, as is implemented by `update-path`.

I believe that the type conversions implemented by this patch are
essentially always desired. But if we want to keep `registry query`
"pure", we could easily introduce a `registry get`[^get] which does the
more complete interpretation of registry types, and leave `registry
query` alone as doing the bare minimum. Or we could teach `path expand`
to do `ExpandEnvironmentStringsW`. But REG_EXPAND_SZ being the odd one
out of not getting its registry type semantics decoded by `registry
query` seems wrong.

[^get]: This is the potential redesign I alluded to at the top. One
potential change could be to make `registry get Environment` produce
`record<Path: string, TEMP: string, TMP: string>` instead of `registry
query`'s `table<name: string, value: string, type: string>`, the idea
being to make it feel as native as possible. We could even translate
between Nu's cell-path and registry paths -- cell paths with spaces do
actually work, if a bit awkwardly -- or even introduce lazy records so
the registry can be traversed with normal data manipulation ... but that
all seems a bit much.

# User-Facing Changes

- `registry query`'s produced `value` has changed. Specifically:
-  Rows `where type == REG_EXPAND_SZ` now expand `%EnvironmentVarable%`
placeholders for you. For example, `registry query --hkcu Environment
TEMP | get value` returns `C:\Users\CAD\AppData\Local\Temp` instead of
`%USERPROFILE%\AppData\Local\Temp`.
- You can restore the old behavior and preserve the placeholders by
passing a new `--no-expand` switch.
- Rows `where type == REG_MULTI_SZ` now provide a `list<string>` value.
They previously had that same list, but `| str join "\n"`.
- Rows `where type == REG_DWORD_BIG_ENDIAN` now provide the correct
numeric value instead of a byte-swapped value.
- Rows `where type == REG_QWORD` now provide the correct numeric
value[^sign] instead of the value modulo 2<sup>32</sup>.
- Rows `where type == REG_LINK` now provide a string value of the link
target registry path instead of an internal debug string representation.
(This should never be visible, as links should be transparently
followed.)
- Rows `where type =~ RESOURCE` now provide a binary value instead of an
internal debug string representation.

[^sign]: Nu's `int` is a signed 64-bit integer. As such, values >=
2<sup>63</sup> will be reported as their negative two's compliment
value. This might sometimes be the correct interpretation -- the
registry does not distinguish between signed and unsigned integer values
-- but regedit and pwsh display all values as unsigned.
2023-10-23 07:21:27 -05:00
05efd735b9 Bump which from 4.4.2 to 5.0.0 (#10811) 2023-10-23 14:14:28 +08:00
5e0499fcf9 Bump uuid from 1.4.1 to 1.5.0 (#10810) 2023-10-23 14:14:08 +08:00
74d3f3c1d6 Bump regex from 1.9.6 to 1.10.2 (#10812) 2023-10-23 14:13:57 +08:00
de6edf18d9 Bump crate-ci/typos from 1.16.19 to 1.16.20 (#10813) 2023-10-23 14:13:44 +08:00
a35ecb4837 Finish removing profile command and related data (#10807) 2023-10-22 14:06:53 +03:00
a01ef85bda Remove registry clean_string hack (#10804)
# Description

Remove the `clean_string` hack used in `registry query`.

This was a workaround for a [bug][gentoo90/winreg-rs#52] in winreg which
has since [been fixed][edf9eef] and released in [winreg v0.12.0].

winreg now properly displays strings in RegKey's Display impl instead of
outputting their debug representation. We remove our `clean_string` such
that registry entries which happen to start/end with `"` or contain `\\`
won't get mangled. This is very important for entries in UNC path format
as those begin with a double backslash.

[gentoo90/winreg-rs#52]:
<https://github.com/gentoo90/winreg-rs/issues/52>
[edf9eef]:
<edf9eef38f>
[winreg v0.12.0]:
<https://github.com/gentoo90/winreg-rs/releases/tag/v0.12.0>

# User-Facing Changes

- `registry query` used to accidentally mangle values that contain a
literal `\\`, such as UNC paths. It no longer does so.

# Tests + Formatting

- [X] `toolkit check pr`
  - 🟢 `toolkit fmt`
  - 🟢 `toolkit clippy`
  - 🟢 `toolkit test`
  - 🟢 `toolkit test stdlib`
2023-10-21 18:50:34 -05:00
6445c4e7de Do not use white text in the default light theme (#10796)
Use instead 'dark_gray', the default fg color for the other primitives.

fixes #10636
2023-10-21 16:31:46 -05:00
066c9118ae Update Nushell version to v0.86 for release script (#10797)
# Description
Follow up https://github.com/nushell/nushell/pull/10762, align with the
latest version
2023-10-21 19:13:25 +02:00
52e8b0afb2 Deprecate size to str stats (#10798)
<!--
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!
-->

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

Rename `str size` to `str stats`, for more detail see:
https://github.com/nushell/nushell/pull/10772

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-10-21 11:21:34 -05:00
db3f3eaf5a Move ansi link from extra to default feature, close #10792 (#10801)
<!--
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!
-->

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

Move `ansi link` from extra to default feature, close #10792

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-10-21 11:04:37 -05:00
878f0cf6e1 Add long options for viewers (#10787)
![](http://s2.quickmeme.com/img/7f/7f77546945f948560cdc26b12b99d5ccd390c2e39d2849d3423ae7608dac066a.jpg)
2023-10-20 11:43:42 -05:00
7caf27b665 add toolkit run to run a Nushell revision (#10687)
# Description
when i try out a PR, i always end up running `cargo run` but then i
never know if i'm inside my install of Nushell or a PR 👀

in this PR i propose to add a `run` command to the `toolkit.nu` which
- runs the current revision inside `cargo run`
- adds a clear right prompt to make sure one knows they are in `cargo
run`

# User-Facing Changes
an example after running `toolkit run`

![tk-run](https://github.com/nushell/nushell/assets/44101798/1039f406-e413-495a-8e31-5aea99700aa4)


# Tests + Formatting

# After Submitting
2023-10-20 15:55:46 +02:00
22b375ccd4 Spell out our platform support policy (#10778)
This reflects how we currently distributed our efforts to supporting the
different platforms and clarifies which things get run by the Nushell
team and which rely on the help of third-parties or individual
contributors.

Feel free to propose improvements, as long as they can be backed up by
implemented practice as a result.
2023-10-20 11:35:16 +02:00
6a2539534f deprecate size to str size (#10772)
related to
-
https://discord.com/channels/601130461678272522/614613939334152217/1164530991931605062

# Description
it appears `size` is a command that operates on `string`s only and gives
the user information about the chars, graphemes and bytes of a string.
this looks like a command that should be a subcommand to `str` 😏 

this PR
- adds `str size`
- deprecates `size`

`size` is planned to be removed in 0.88

# User-Facing Changes
`str size` can be used for the same result as `size`.

# Tests + Formatting

# After Submitting
write a removal PR for `size`
2023-10-20 11:34:55 +02:00
f310a9be8c Make hints aware of the current directory (#10780)
This commit uses the new `CwdAwareHinter` in reedline. Closes #8883.

# Description

Currently, the history based hints show results from all directories,
while most commands make sense only in the directory they were run in.
This PR makes hints take the current directory into account.

# User-Facing Changes

Described above.

I haven't yet added a config option for this, because I personally
believe folks won't be against it once they try it out. We can add it if
people complain, there's some time before the next release.

Fish has this without a config option too.

# Tests + Formatting

If tests are needed, I'll need help as I'm not well versed with the
codebase.
2023-10-20 11:21:58 +02:00
d0dc6986dd Use long options for string (#10777) 2023-10-19 22:08:09 +02:00
11480c77be Add long options for path (#10775) 2023-10-19 22:07:01 +02:00
4fd2b702ee Add long options for platform and random (#10776) 2023-10-19 22:04:33 +02:00
030e55acbf add unfold back with a deprecation warning (#10771)
related to
- https://github.com/nushell/nushell/pull/10770

# Description
because some people look into `unfold` already (myself included lol) and
there will be 4 weeks with that new command which has a decent section
in the release note, i fear that
https://github.com/nushell/nushell/pull/10770 is a bit too brutal,
removing `unfold` without any warning...

this PR brings `unfold` back to life.
the `unfold` command will have a deprecation warning and will be removed
in 0.88.

# User-Facing Changes
`unfold` is only deprecated, not removed.

# Tests + Formatting

# After Submitting
2023-10-19 19:23:06 +02:00
c5e1b64b40 remove random integer in favor of random int (#10568)
related to
- https://github.com/nushell/nushell/pull/10520

# Description
this PR is a followup to https://github.com/nushell/nushell/pull/10520
and removes the `random integer` command completely, in favor of `random
int`.

# User-Facing Changes
`random integer` has been fully moved to `random int`
```nushell
> random integer 0..1
Error: nu::parser::extra_positional

  × Extra positional argument.
   ╭─[entry #1:1:1]
 1 │ random integer 0..1
   ·        ───┬───
   ·           ╰── extra positional argument
   ╰────
  help: Usage: random
```

# Tests + Formatting
tests have been moved from
`crates/nu-command/tests/commands/random/integer.rs` to
`crates/nu-command/tests/commands/random/int.rs`

# After Submitting
mention in 0.87.0 release notes
2023-10-19 18:42:07 +02:00
999f7b229f Remove to xml --pretty (#10668)
followup to
- https://github.com/nushell/nushell/pull/10660
2023-10-19 18:41:54 +02:00
de1c7bb39f remove the $nothing variable (#10567)
related to 
- https://github.com/nushell/nushell/pull/10478

# Description
this PR is the followup removal to
https://github.com/nushell/nushell/pull/10478.

# User-Facing Changes
`$nothing` is now an undefined variable, unless define by the user.
```nushell
> $nothing
Error: nu::parser::variable_not_found

  × Variable not found.
   ╭─[entry #1:1:1]
 1 │ $nothing
   · ────┬───
   ·     ╰── variable not found.
   ╰────
```

# Tests + Formatting

# After Submitting
mention that in release notes
2023-10-19 18:41:38 +02:00
54bc662e0e Add long options for generators and math (#10752) 2023-10-19 18:17:42 +02:00
5f2089a15b Add long options for misc and network (#10753) 2023-10-19 18:16:44 +02:00
adb99938f7 rename unfold to generate (#10770)
# Description

This PR renames the `unfold` command to `generate`.
closes #10760

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-10-19 09:30:34 -05:00
b907939916 Extract common logic for setting error in parse_short_flags (#10709)
# Description

Since the `else` clause for the nested branches check for the first
unmatched argument, this PR brings together all the conditions where the
positional argument shape is numeric using the `matches!` keyword. This
also allows us to and (`&&`) the condition with when no short flags are
found unlike the `if let ...` statements. Finally, we can handle any
`unmatched_short_flags` at one place.

# User-Facing Changes

No user facing changes.
2023-10-19 13:24:57 +02:00
27e6271402 Implement modulo for duration (#10745)
# Description
This PR adds the ability to use modulo with durations:

```nu
(2min + 31sec) mod 20sec # 11sec
```

# User-Facing Changes

Allows to use `<duration> mod <duration>`
2023-10-19 12:27:00 +02:00
0a8f27f6f2 Allow empty list inputs in group-by and return empty record (#10730)
# Description

Changed `group-by` behavior to accept empty list as input and return an
empty record instead of throwing an error. I also replaced
`errors_if_input_empty()` test to reflect the new expected behavior.

See #10713 

# User-Facing Changes
`[] | group-by` or `[] | group-by a` now returns empty record


# Tests + Formatting
1 test for emptied table i.e. list

---------

Signed-off-by: Oscar <71343264+0scvr@users.noreply.github.com>
2023-10-19 12:20:52 +02:00
1662e61ecb deprecate def-env and export def-env (#10715)
follow-up to
- https://github.com/nushell/nushell/pull/10566

# Description
this PR deprecates the use of `def-env` and `export def-env`

these two core commands will be removed in 0.88

# User-Facing Changes
using `def-env` will give a warning
```nushell
> def-env foo [] { print "foo" }; foo
Error:   × Deprecated command
   ╭─[entry #1:1:1]
 1 │ def-env foo [] { print "foo" }; foo
   · ───┬───
   ·    ╰── `def-env` is deprecated and will be removed in 0.88.
   ╰────
  help: Use `def --env` instead


foo
```

# Tests + Formatting

# After Submitting
2023-10-19 13:50:16 +08:00
b58819d51e deprecate extern-wrapped and export extern-wrapped (#10716)
follow-up to
- https://github.com/nushell/nushell/pull/10566

# Description
this PR deprecates the use of `extern-wrapped` and `export
extern-wrapped`

these two core commands will be removed in 0.88

# User-Facing Changes
using `extern-wrapped` will give a warning
```nushell
> extern-wrapped foo [...args] { print "foo" }; foo
Error:   × Deprecated command
   ╭─[entry #2:1:1]
 1 │ extern-wrapped foo [...args] { print "foo" }; foo
   · ───────┬──────
   ·        ╰── `extern-wrapped` is deprecated and will be removed in 0.88.
   ╰────
  help: Use `def --wrapped` instead


foo
```

# Tests + Formatting

# After Submitting
2023-10-19 13:50:00 +08:00
9692240b4f Add --ignore-error to reject (#10737)
Add `--ignore-errors` flag to reject.

This is a PR in reference to #10215 as select has the flag, but reject
hasn't

user can now add `-i` or `--ignore-errors` flag to turn every cell path
into option.

```nushell
> let arg = [0 5 a c]
> [[a b];[1 2] [3 4] [5 6]] | reject $a | to nuon
error index to large
# ----
> let arg = [0 5 a c]
> [[a b];[1 2] [3 4] [5 6]] | reject $a -i | to nuon
[[a, b]; [1, 2], [3, 4], [5, 6]]
```
2023-10-19 06:28:47 +08:00
d204defb68 Refactor: remove duplication to simplify lite_parsing logic. (#10735)
When looking into `lite_parse` function, I found that it contains some
duplicate code, and they can be expressed as an action called
`push_command_to(pipeline)`.

And I believe it will make our life easier to support something like
`o>> a.txt`, `e>> a.txt`.
2023-10-18 23:24:40 +02:00
9e7f84afb0 Refactor: simplify lex_item impl (#10744)
In the final match of `lex_item`, we'll return `Err(ParseError)` in rare
case, normally we'll return None.

So I think making error part mutable can reduce some code, and it's
better if we want to add more lex items.
2023-10-18 23:23:17 +02:00
ed8dee04b6 remove the last mention to let-env (#10718)
# Description
just caught a last mention to `let-env` in the `CONTRIBUTING.md`
document 😋

# User-Facing Changes

# Tests + Formatting

# After Submitting
2023-10-18 23:15:04 +02:00
4171c654a5 update release-pkg.nu with updated manual instructions (#10759)
# Description

This PR updates the release-pkg.nu script with updated instructions for
performing the release manually. These are there specifically because
winget fails so often and creating a x86_64 windows msi is not trivial.
There are also a couple minor changes to the script's syntax.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->
2023-10-18 16:04:59 -05:00
JT
22ee041002 fix the flag type on release-pkg.nu (#10762)
# Description

Fix a breaking change based on how we do boolean flags in release-pkg.nu
2023-10-18 23:03:21 +02:00
7162d4d9aa Escape path that could be a flag (#10721)
# Description
Files that begin with dashes can be ambiguous when passed to commands
like `ls`. For example if there exists a file `--help`, it might be
considered a flag if not properly escaped. This PR escapes any file that
begins with a dash.

# User-Facing Changes

Files beginning with dashes will be escaped.

# Tests + Formatting

Tests are added.
2023-10-18 23:02:11 +02:00
9c70c68914 Bump csv from 1.2.2 to 1.3.0 (#10733) 2023-10-18 21:01:14 +00:00
93b4aa5fcf Bump lru from 0.11.1 to 0.12.0 (#10732) 2023-10-18 21:00:11 +00:00
71b0e12b57 Bump rustix from 0.36.15 to 0.36.16 (#10761) 2023-10-18 20:59:05 +00:00
09b3dab35d Allow filesystem commands to access files with glob metachars in name (#10694)
(squashed version of #10557, clean commit history and review thread)

Fixes #10571, also potentially: #10364, #10211, #9558, #9310,


# Description
Changes processing of arguments to filesystem commands that are source
paths or globs.
Applies to `cp, cp-old, mv, rm, du` but not `ls` (because it uses a
different globbing interface) or `glob` (because it uses a different
globbing library).

The core of the change is to lookup the argument first as a file and
only glob if it is not. That way,
a path containing glob metacharacters can be referenced without glob
quoting, though it will have to be single quoted to avoid nushell
parsing.

Before: A file path that looks like a glob is not matched by the glob
specified as a (source) argument and takes some thinking about to
access. You might say the glob pattern shadows a file with the same
spelling.
```
> ls a*
╭───┬────────┬──────┬──────┬────────────────╮
│ # │  name  │ type │ size │    modified    │
├───┼────────┼──────┼──────┼────────────────┤
│ 0 │ a[bc]d │ file │  0 B │ 34 seconds ago │
│ 1 │ abd    │ file │  0 B │ now            │
│ 2 │ acd    │ file │  0 B │ now            │
╰───┴────────┴──────┴──────┴────────────────╯

> cp --verbose 'a[bc]d' dest
copied /home/bobhy/src/rust/work/r4/abd to /home/bobhy/src/rust/work/r4/dest/abd
copied /home/bobhy/src/rust/work/r4/acd to /home/bobhy/src/rust/work/r4/dest/acd

> ## Note -- a[bc]d *not* copied, and seemingly hard to access.
> cp --verbose 'a\[bc\]d' dest
Error:   × No matches found
   ╭─[entry #33:1:1]
 1 │ cp --verbose 'a\[bc\]d' dest
   ·              ─────┬────
   ·                   ╰── no matches found
   ╰────

> #.. but is accessible with enough glob quoting.
> cp --verbose 'a[[]bc[]]d' dest
copied /home/bobhy/src/rust/work/r4/a[bc]d to /home/bobhy/src/rust/work/r4/dest/a[bc]d
```
Before_2: if file has glob metachars but isn't a valid pattern, user
gets a confusing error:

```
> touch 'a[b'
> cp 'a[b' dest
Error:   × Pattern syntax error near position 30: invalid range pattern
   ╭─[entry #13:1:1]
 1 │ cp 'a[b' dest
   ·    ──┬──
   ·      ╰── invalid pattern
   ╰────
```

After: Args to cp, mv, etc. are tried first as literal files, and only
as globs if not found to be files.

```
> cp --verbose 'a[bc]d' dest
copied /home/bobhy/src/rust/work/r4/a[bc]d to /home/bobhy/src/rust/work/r4/dest/a[bc]d
> cp --verbose '[a][bc]d' dest
copied /home/bobhy/src/rust/work/r4/abd to /home/bobhy/src/rust/work/r4/dest/abd
copied /home/bobhy/src/rust/work/r4/acd to /home/bobhy/src/rust/work/r4/dest/acd
```
After_2: file with glob metachars but invalid pattern just works.
(though Windows does not allow file name to contain `*`.).

```
> cp --verbose 'a[b' dest
copied /home/bobhy/src/rust/work/r4/a[b to /home/bobhy/src/rust/work/r4/dest/a[b
```

So, with this fix, a file shadows a glob pattern with the same spelling.
If you have such a file and really want to use the glob pattern, you
will have to glob quote some of the characters in the pattern. I think
that's less confusing to the user: if ls shows a file with a weird name,
s/he'll still be able to copy, rename or delete it.

# User-Facing Changes
Could break some existing scripts. If user happened to have a file with
a globbish name but was using a glob pattern with the same spelling, the
new version will process the file and not expand the glob.

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-10-18 13:31:15 -05:00
88a87158c2 Bump version to 0.86.1 (#10755)
To dev or to patch that is the question
2023-10-18 13:00:51 -05:00
faf84e69b4 Fix winget release submission error (#10757)
<!--
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!
-->

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

Fix winget release submission error, more details could be found here:
https://github.com/nushell/nushell/pull/5812#issuecomment-1768294811

@fdncred @wolimst 

# 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 std testing; testing run-tests --path
crates/nu-std"` 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.
-->

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2023-10-18 13:00:19 -05:00
425 changed files with 11695 additions and 8339 deletions

View File

@ -9,6 +9,7 @@ name: continuous-integration
env:
NUSHELL_CARGO_PROFILE: ci
NU_LOG_LEVEL: DEBUG
# If changing these settings also change toolkit.nu
CLIPPY_OPTIONS: "-D warnings -D clippy::unwrap_used"
jobs:
@ -47,6 +48,7 @@ jobs:
- name: cargo fmt
run: cargo fmt --all -- --check
# If changing these settings also change toolkit.nu
- name: Clippy
run: cargo clippy --workspace ${{ matrix.flags }} --exclude nu_plugin_* -- $CLIPPY_OPTIONS

View File

@ -36,10 +36,10 @@ jobs:
token: ${{ secrets.WORKFLOW_TOKEN }}
- name: Setup Nushell
uses: hustcer/setup-nu@v3.6
uses: hustcer/setup-nu@v3.8
if: github.repository == 'nushell/nightly'
with:
version: 0.85.0
version: 0.86.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -136,11 +136,14 @@ jobs:
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with:
rustflags: ''
- name: Setup Nushell
uses: hustcer/setup-nu@v3.6
uses: hustcer/setup-nu@v3.8
with:
version: 0.85.0
version: 0.86.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -247,11 +250,14 @@ jobs:
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with:
rustflags: ''
- name: Setup Nushell
uses: hustcer/setup-nu@v3.6
uses: hustcer/setup-nu@v3.8
with:
version: 0.85.0
version: 0.86.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -315,9 +321,9 @@ jobs:
ref: main
- name: Setup Nushell
uses: hustcer/setup-nu@v3.6
uses: hustcer/setup-nu@v3.8
with:
version: 0.85.0
version: 0.86.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -9,34 +9,36 @@
# Instructions for manually creating an MSI for Winget Releases when they fail
# Added 2022-11-29 when Windows packaging wouldn't work
# Updated again on 2023-02-23 because msis are still failing validation
# Update on 2023-10-18 to use RELEASE_TYPE env var to determine if full or not
# To run this manual for windows here are the steps I take
# checkout the release you want to publish
# 1. git checkout 0.76.0
# 1. git checkout 0.86.0
# unset CARGO_TARGET_DIR if set (I have to do this in the parent shell to get it to work)
# 2. $env:CARGO_TARGET_DIR = ""
# 2. hide-env CARGO_TARGET_DIR
# 3. $env.TARGET = 'x86_64-pc-windows-msvc'
# 4. $env.TARGET_RUSTFLAGS = ''
# 5. $env.GITHUB_WORKSPACE = 'C:\Users\dschroeder\source\repos\forks\nushell'
# 6. $env.GITHUB_OUTPUT = 'C:\Users\dschroeder\source\repos\forks\nushell\output\out.txt'
# 5. $env.GITHUB_WORKSPACE = 'D:\nushell'
# 6. $env.GITHUB_OUTPUT = 'D:\nushell\output\out.txt'
# 7. $env.OS = 'windows-latest'
# 8. $env.RELEASE_TYPE = '' # There is full and '' for normal releases
# make sure 7z.exe is in your path https://www.7-zip.org/download.html
# 8. $env.Path = ($env.Path | append 'c:\apps\7-zip')
# 9. $env.Path = ($env.Path | append 'c:\apps\7-zip')
# make sure aria2c.exe is in your path https://github.com/aria2/aria2
# 9. $env.Path = ($env.Path | append 'c:\path\to\aria2c')
# 10. $env.Path = ($env.Path | append 'c:\path\to\aria2c')
# make sure you have the wixtools installed https://wixtoolset.org/
# 10. $env.Path = ($env.Path | append 'C:\Users\dschroeder\AppData\Local\tauri\WixTools')
# 11. $env.Path = ($env.Path | append 'C:\Users\dschroeder\AppData\Local\tauri\WixTools')
# You need to run the release-pkg twice. The first pass, with _EXTRA_ as 'bin', makes the output
# folder and builds everything. The second pass, that generates the msi file, with _EXTRA_ as 'msi'
# 11. $env._EXTRA_ = 'bin'
# 12. source .github\workflows\release-pkg.nu
# 13. cd ..
# 14. $env._EXTRA_ = 'msi'
# 15. source .github\workflows\release-pkg.nu
# 12. $env._EXTRA_ = 'bin'
# 13. source .github\workflows\release-pkg.nu
# 14. cd ..
# 15. $env._EXTRA_ = 'msi'
# 16. source .github\workflows\release-pkg.nu
# After msi is generated, you have to update winget-pkgs repo, you'll need to patch the release
# by deleting the existing msi and uploading this new msi. Then you'll need to update the hash
# on the winget-pkgs PR. To generate the hash, run this command
# 16. open target\wix\nu-0.74.0-x86_64-pc-windows-msvc.msi | hash sha256
# 17. open target\wix\nu-0.74.0-x86_64-pc-windows-msvc.msi | hash sha256
# Then, just take the output and put it in the winget-pkgs PR for the hash on the msi
@ -134,7 +136,7 @@ print (ls -f $executable); sleep 1sec
print $'(char nl)Copying release files...'; hr-line
"To use Nu plugins, use the register command to tell Nu where to find the plugin. For example:
> register ./nu_plugin_query" | save $'($dist)/README.txt'
> register ./nu_plugin_query" | save $'($dist)/README.txt' -f
[LICENSE $executable] | each {|it| cp -rv $it $dist } | flatten
# Sleep a few seconds to make sure the cp process finished successfully
sleep 3sec
@ -225,7 +227,7 @@ def 'cargo-build-nu' [ options: string ] {
# Print a horizontal line marker
def 'hr-line' [
--blank-line(-b): bool
--blank-line(-b)
] {
print $'(ansi g)---------------------------------------------------------------------------->(ansi reset)'
if $blank_line { char nl }

View File

@ -80,11 +80,14 @@ jobs:
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with:
rustflags: ''
- name: Setup Nushell
uses: hustcer/setup-nu@v3.6
uses: hustcer/setup-nu@v3.8
with:
version: 0.85.0
version: 0.86.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -168,11 +171,14 @@ jobs:
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with:
rustflags: ''
- name: Setup Nushell
uses: hustcer/setup-nu@v3.6
uses: hustcer/setup-nu@v3.8
with:
version: 0.85.0
version: 0.86.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -10,6 +10,6 @@ jobs:
uses: actions/checkout@v4
- name: Check spelling
uses: crate-ci/typos@v1.16.19
uses: crate-ci/typos@v1.16.23
with:
config: ./.github/.typos.toml

269
Cargo.lock generated
View File

@ -997,9 +997,9 @@ dependencies = [
[[package]]
name = "csv"
version = "1.2.2"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086"
checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
dependencies = [
"csv-core",
"itoa",
@ -1009,9 +1009,9 @@ dependencies = [
[[package]]
name = "csv-core"
version = "0.1.10"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
dependencies = [
"memchr",
]
@ -1306,7 +1306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
dependencies = [
"cfg-if",
"rustix 0.38.15",
"rustix 0.38.21",
"windows-sys 0.48.0",
]
@ -1856,9 +1856,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.0.2"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown 0.14.1",
@ -1946,7 +1946,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi",
"rustix 0.38.15",
"rustix 0.38.21",
"windows-sys 0.48.0",
]
@ -2293,9 +2293,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "lru"
version = "0.11.1"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21"
checksum = "1efa59af2ddfad1854ae27d75009d538d0998b4b2fd47083e743ac1a10e46c60"
dependencies = [
"hashbrown 0.14.1",
]
@ -2309,6 +2309,31 @@ dependencies = [
"nu-ansi-term",
]
[[package]]
name = "lsp-server"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b52dccdf3302eefab8c8a1273047f0a3c3dca4b527c8458d00c09484c8371928"
dependencies = [
"crossbeam-channel",
"log",
"serde",
"serde_json",
]
[[package]]
name = "lsp-types"
version = "0.94.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1"
dependencies = [
"bitflags 1.3.2",
"serde",
"serde_json",
"serde_repr",
"url",
]
[[package]]
name = "lz4"
version = "1.24.0"
@ -2620,7 +2645,7 @@ dependencies = [
[[package]]
name = "nu"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"assert_cmd",
"criterion",
@ -2641,6 +2666,7 @@ dependencies = [
"nu-engine",
"nu-explore",
"nu-json",
"nu-lsp",
"nu-parser",
"nu-path",
"nu-plugin",
@ -2676,7 +2702,7 @@ dependencies = [
[[package]]
name = "nu-cli"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"chrono",
"crossterm 0.27.0",
@ -2708,23 +2734,28 @@ dependencies = [
[[package]]
name = "nu-cmd-base"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"indexmap 2.0.2",
"indexmap 2.1.0",
"miette",
"nu-engine",
"nu-glob",
"nu-parser",
"nu-path",
"nu-protocol",
"nu-test-support",
"nu-utils",
"rstest",
]
[[package]]
name = "nu-cmd-dataframe"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"chrono",
"chrono-tz",
"fancy-regex",
"indexmap 2.0.2",
"indexmap 2.1.0",
"nu-cmd-lang",
"nu-engine",
"nu-parser",
@ -2739,7 +2770,7 @@ dependencies = [
[[package]]
name = "nu-cmd-extra"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"ahash",
"fancy-regex",
@ -2764,7 +2795,7 @@ dependencies = [
[[package]]
name = "nu-cmd-lang"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"fancy-regex",
"itertools 0.11.0",
@ -2778,7 +2809,7 @@ dependencies = [
[[package]]
name = "nu-color-config"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"nu-ansi-term",
"nu-engine",
@ -2791,7 +2822,7 @@ dependencies = [
[[package]]
name = "nu-command"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"alphanumeric-sort",
"base64",
@ -2815,7 +2846,7 @@ dependencies = [
"filetime",
"fs_extra",
"htmlescape",
"indexmap 2.0.2",
"indexmap 2.1.0",
"indicatif",
"itertools 0.11.0",
"libc",
@ -2880,16 +2911,18 @@ dependencies = [
"ureq",
"url",
"uu_cp",
"uu_mkdir",
"uu_whoami",
"uuid",
"wax",
"which",
"which 5.0.0",
"windows 0.48.0",
"winreg",
]
[[package]]
name = "nu-engine"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"nu-glob",
"nu-path",
@ -2899,7 +2932,7 @@ dependencies = [
[[package]]
name = "nu-explore"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"ansi-str",
"crossterm 0.27.0",
@ -2920,23 +2953,44 @@ dependencies = [
[[package]]
name = "nu-glob"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"doc-comment",
]
[[package]]
name = "nu-json"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"linked-hash-map",
"num-traits",
"serde",
]
[[package]]
name = "nu-lsp"
version = "0.87.1"
dependencies = [
"assert-json-diff",
"lsp-server",
"lsp-types",
"miette",
"nu-cli",
"nu-cmd-lang",
"nu-command",
"nu-parser",
"nu-protocol",
"nu-test-support",
"reedline",
"ropey",
"serde",
"serde_json",
"tempfile",
]
[[package]]
name = "nu-parser"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"bytesize",
"chrono",
@ -2952,7 +3006,7 @@ dependencies = [
[[package]]
name = "nu-path"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"dirs-next",
"omnipath",
@ -2961,7 +3015,7 @@ dependencies = [
[[package]]
name = "nu-plugin"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"bincode",
"nu-engine",
@ -2973,7 +3027,7 @@ dependencies = [
[[package]]
name = "nu-pretty-hex"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"heapless",
"nu-ansi-term",
@ -2982,13 +3036,13 @@ dependencies = [
[[package]]
name = "nu-protocol"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"byte-unit",
"chrono",
"chrono-humanize",
"fancy-regex",
"indexmap 2.0.2",
"indexmap 2.1.0",
"lru",
"miette",
"nu-path",
@ -3007,7 +3061,7 @@ dependencies = [
[[package]]
name = "nu-std"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"miette",
"nu-engine",
@ -3017,7 +3071,7 @@ dependencies = [
[[package]]
name = "nu-system"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"chrono",
"libc",
@ -3034,7 +3088,7 @@ dependencies = [
[[package]]
name = "nu-table"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"fancy-regex",
"nu-ansi-term",
@ -3048,7 +3102,7 @@ dependencies = [
[[package]]
name = "nu-term-grid"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"nu-utils",
"unicode-width",
@ -3056,7 +3110,7 @@ dependencies = [
[[package]]
name = "nu-test-support"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"hamcrest2",
"nu-glob",
@ -3064,12 +3118,12 @@ dependencies = [
"nu-utils",
"num-format",
"tempfile",
"which",
"which 4.4.2",
]
[[package]]
name = "nu-utils"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"crossterm_winapi",
"log",
@ -3077,6 +3131,7 @@ dependencies = [
"num-format",
"strip-ansi-escapes",
"sys-locale",
"unicase",
]
[[package]]
@ -3091,7 +3146,7 @@ dependencies = [
[[package]]
name = "nu_plugin_example"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"nu-plugin",
"nu-protocol",
@ -3099,11 +3154,11 @@ dependencies = [
[[package]]
name = "nu_plugin_formats"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"eml-parser",
"ical",
"indexmap 2.0.2",
"indexmap 2.1.0",
"nu-plugin",
"nu-protocol",
"rust-ini",
@ -3111,7 +3166,7 @@ dependencies = [
[[package]]
name = "nu_plugin_gstat"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"git2",
"nu-plugin",
@ -3120,7 +3175,7 @@ dependencies = [
[[package]]
name = "nu_plugin_inc"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"nu-plugin",
"nu-protocol",
@ -3129,7 +3184,7 @@ dependencies = [
[[package]]
name = "nu_plugin_query"
version = "0.86.0"
version = "0.87.1"
dependencies = [
"gjson",
"nu-engine",
@ -3402,12 +3457,12 @@ dependencies = [
[[package]]
name = "ordered-multimap"
version = "0.6.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
checksum = "a4d6a8c22fc714f0c2373e6091bf6f5e9b37b1bc0b1184874b7e0a4e303d318f"
dependencies = [
"dlv-list",
"hashbrown 0.13.2",
"hashbrown 0.14.1",
]
[[package]]
@ -3728,7 +3783,7 @@ dependencies = [
"comfy-table",
"either",
"hashbrown 0.14.1",
"indexmap 2.0.2",
"indexmap 2.1.0",
"num-traits",
"once_cell",
"polars-arrow",
@ -3801,7 +3856,7 @@ dependencies = [
"arrow2",
"fallible-streaming-iterator",
"hashbrown 0.14.1",
"indexmap 2.0.2",
"indexmap 2.1.0",
"num-traits",
"polars-arrow",
"polars-error",
@ -3842,7 +3897,7 @@ dependencies = [
"argminmax",
"arrow2",
"either",
"indexmap 2.0.2",
"indexmap 2.1.0",
"memchr",
"polars-arrow",
"polars-core",
@ -4064,7 +4119,7 @@ dependencies = [
"flate2",
"hex",
"lazy_static",
"rustix 0.36.15",
"rustix 0.36.17",
]
[[package]]
@ -4244,9 +4299,9 @@ dependencies = [
[[package]]
name = "reedline"
version = "0.25.0"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7dc1d1d369c194cf79acc204397aca1fecc4248df3e1c1eabb15e5ef2d16991"
checksum = "d0a093a20a6c473247c2e9971aaf4cedf9041bcd3f444dc7fad667d3b6b7a5fd"
dependencies = [
"chrono",
"crossterm 0.27.0",
@ -4327,6 +4382,16 @@ dependencies = [
"serde",
]
[[package]]
name = "ropey"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5"
dependencies = [
"smallvec",
"str_indices",
]
[[package]]
name = "roxmltree"
version = "0.18.1"
@ -4415,9 +4480,9 @@ dependencies = [
[[package]]
name = "rust-ini"
version = "0.19.0"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a"
dependencies = [
"cfg-if",
"ordered-multimap",
@ -4456,9 +4521,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.36.15"
version = "0.36.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941"
checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed"
dependencies = [
"bitflags 1.3.2",
"errno",
@ -4470,9 +4535,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.37.24"
version = "0.37.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4279d76516df406a8bd37e7dff53fd37d1a093f997a3c34a5c21658c126db06d"
checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2"
dependencies = [
"bitflags 1.3.2",
"errno",
@ -4484,9 +4549,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.15"
version = "0.38.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f9da0cbd88f9f09e7814e388301c8414c51c62aa6ce1e4b5c551d49d96e531"
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
dependencies = [
"bitflags 2.4.0",
"errno",
@ -4533,9 +4598,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "scraper"
version = "0.17.1"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c95a930e03325234c18c7071fd2b60118307e025d6fff3e12745ffbf63a3d29c"
checksum = "585480e3719b311b78a573db1c9d9c4c1f8010c2dee4cc59c2efe58ea4dbc3e1"
dependencies = [
"ahash",
"cssparser",
@ -4543,7 +4608,6 @@ dependencies = [
"html5ever",
"once_cell",
"selectors",
"smallvec",
"tendril",
]
@ -4632,6 +4696,17 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_repr"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]]
name = "serde_spanned"
version = "0.6.3"
@ -4659,7 +4734,7 @@ version = "0.9.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574"
dependencies = [
"indexmap 2.0.2",
"indexmap 2.1.0",
"itoa",
"ryu",
"serde",
@ -4899,6 +4974,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "str_indices"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8eeaedde8e50d8a331578c9fa9a288df146ce5e16173ad26ce82f6e263e2be4"
[[package]]
name = "streaming-decompression"
version = "0.1.2"
@ -5124,7 +5205,7 @@ dependencies = [
"cfg-if",
"fastrand",
"redox_syscall 0.3.5",
"rustix 0.38.15",
"rustix 0.38.21",
"windows-sys 0.48.0",
]
@ -5164,7 +5245,7 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
dependencies = [
"rustix 0.37.24",
"rustix 0.37.27",
"windows-sys 0.48.0",
]
@ -5174,7 +5255,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
dependencies = [
"rustix 0.38.15",
"rustix 0.38.21",
"windows-sys 0.48.0",
]
@ -5383,7 +5464,7 @@ version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap 2.0.2",
"indexmap 2.1.0",
"serde",
"serde_spanned",
"toml_datetime",
@ -5396,7 +5477,7 @@ version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca676d9ba1a322c1b64eb8045a5ec5c0cfb0c9d08e15e9ff622589ad5221c8fe"
dependencies = [
"indexmap 2.0.2",
"indexmap 2.1.0",
"serde",
"serde_spanned",
"toml_datetime",
@ -5431,9 +5512,9 @@ dependencies = [
[[package]]
name = "trash"
version = "3.1.0"
version = "3.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7b23f2b0cf93f537bbe90cbb59ea9176cc8ce9b010a36dcd5b726facd82825e"
checksum = "8c646008e5144d988005bec12b1e56f5e0a951e957176686815eba8b025e0418"
dependencies = [
"chrono",
"libc",
@ -5582,6 +5663,7 @@ dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
@ -5618,6 +5700,28 @@ dependencies = [
"xattr",
]
[[package]]
name = "uu_mkdir"
version = "0.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4776960a036a4ec375f0701004a41013d66d2e3e46a19e9216fd18d4d92f88f3"
dependencies = [
"clap",
"uucore",
]
[[package]]
name = "uu_whoami"
version = "0.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585d70c283e4c741889ec8575c23f91b350e4d12bfe39b938d18083816830c5d"
dependencies = [
"clap",
"libc",
"uucore",
"windows-sys 0.48.0",
]
[[package]]
name = "uucore"
version = "0.0.22"
@ -5657,9 +5761,9 @@ checksum = "6de61731e36d52d3babb63e2dce8fe648e2644e9ddfe2621e0eea699022051a6"
[[package]]
name = "uuid"
version = "1.4.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
dependencies = [
"getrandom",
]
@ -5841,7 +5945,20 @@ dependencies = [
"either",
"home",
"once_cell",
"rustix 0.38.15",
"rustix 0.38.21",
]
[[package]]
name = "which"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14"
dependencies = [
"either",
"home",
"once_cell",
"rustix 0.38.21",
"windows-sys 0.48.0",
]
[[package]]

View File

@ -11,7 +11,7 @@ license = "MIT"
name = "nu"
repository = "https://github.com/nushell/nushell"
rust-version = "1.60"
version = "0.86.0"
version = "0.87.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -33,6 +33,7 @@ members = [
"crates/nu-cmd-lang",
"crates/nu-cmd-dataframe",
"crates/nu-command",
"crates/nu-lsp",
"crates/nu-protocol",
"crates/nu-plugin",
"crates/nu_plugin_inc",
@ -46,28 +47,29 @@ members = [
]
[dependencies]
nu-cli = { path = "./crates/nu-cli", version = "0.86.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.86.0" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.86.0" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.86.0" }
nu-cmd-dataframe = { path = "./crates/nu-cmd-dataframe", version = "0.86.0", features = ["dataframe"], optional = true }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.86.0", optional = true }
nu-command = { path = "./crates/nu-command", version = "0.86.0" }
nu-engine = { path = "./crates/nu-engine", version = "0.86.0" }
nu-explore = { path = "./crates/nu-explore", version = "0.86.0" }
nu-json = { path = "./crates/nu-json", version = "0.86.0" }
nu-parser = { path = "./crates/nu-parser", version = "0.86.0" }
nu-path = { path = "./crates/nu-path", version = "0.86.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.86.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.86.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.86.0" }
nu-system = { path = "./crates/nu-system", version = "0.86.0" }
nu-table = { path = "./crates/nu-table", version = "0.86.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.86.0" }
nu-std = { path = "./crates/nu-std", version = "0.86.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.86.0" }
nu-cli = { path = "./crates/nu-cli", version = "0.87.1" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.87.1" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.87.1" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.87.1" }
nu-cmd-dataframe = { path = "./crates/nu-cmd-dataframe", version = "0.87.1", features = ["dataframe"], optional = true }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.87.1", optional = true }
nu-command = { path = "./crates/nu-command", version = "0.87.1" }
nu-engine = { path = "./crates/nu-engine", version = "0.87.1" }
nu-explore = { path = "./crates/nu-explore", version = "0.87.1" }
nu-json = { path = "./crates/nu-json", version = "0.87.1" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.87.1" }
nu-parser = { path = "./crates/nu-parser", version = "0.87.1" }
nu-path = { path = "./crates/nu-path", version = "0.87.1" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.87.1" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.87.1" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.87.1" }
nu-system = { path = "./crates/nu-system", version = "0.87.1" }
nu-table = { path = "./crates/nu-table", version = "0.87.1" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.87.1" }
nu-std = { path = "./crates/nu-std", version = "0.87.1" }
nu-utils = { path = "./crates/nu-utils", version = "0.87.1" }
nu-ansi-term = "0.49.0"
reedline = { version = "0.25.0", features = ["bashisms", "sqlite"] }
reedline = { version = "0.26.0", features = ["bashisms", "sqlite"] }
crossterm = "0.27"
ctrlc = "3.4"
@ -95,7 +97,7 @@ nix = { version = "0.27", default-features = false, features = [
] }
[dev-dependencies]
nu-test-support = { path = "./crates/nu-test-support", version = "0.86.0" }
nu-test-support = { path = "./crates/nu-test-support", version = "0.87.1" }
assert_cmd = "2.0"
criterion = "0.5"
pretty_assertions = "1.4"

70
PLATFORM_SUPPORT.md Normal file
View File

@ -0,0 +1,70 @@
# Nushell platform support policy
Nushell envisions to be a cross-platform shell, despite taking some strong design inspiration from UNIX and POSIX command names and style conventions we explicitly support Windows.
## cross-platform design
This commitment to a cross-platform Nushell forces us to make provisions so users on Windows can have the generally same pleasant experience: e.g. supporting paths with backslash as the directory separator, forces us to support string literals that accept those.
In general our design strives to have a consistent behavior across all platforms if defining the semantics is possible for Nushell.
In some cases where the platform requirements dominate we may choose to follow the platform specific defaults. (some nuances around the file system)
Only rarely do we want to accept commands/language features that only support a single platform, only to access common system behavior of this particular platform (e.g. `registry query` command for the windows registry, `exec` for Linux and MacOS)
## cross-platform builds and testing
The Nushell team runs **testing of Nushell for the following platforms** through our CI:
- macOS (latest version available through GitHub CI)
- Windows (10 and 11)
- Linux (our test runners use `ubuntu-20.04` to represent distributions with not the latest glibc versions.)
All PR level tests are performed on x86/AMD64 (at least at the time of writing the default macOS runner was not yet using arm64).
As an additional layer of validation we perform [nightly builds](https://github.com/nushell/nightly/releases).
Those target **additional build targets**:
- **aarch64 for all platforms**
- musl as an alternative to Glibc on linux
- riscv only for linux
- armv7 only for linux
We will try to provide builds for all of them but a standard configuration for x86-64 or aarch64 will take priority for us should we face technical challenges in a release cycle.
### Supported feature flags
We have features of Nushell behind flags that can be passed at compilation time.
The design focus of Nushell is primarily expressed by everything accessible without passing additional feature flag. This provides a standard command set and receives the most attention.
Two other feature flags are actively tested but are not guaranteed to express the stable design direction of Nushell:
- `extra`
- This includes commands where we are not convinced that they are ready to be stabilized for 1.0 or popular enough
- `dataframe`
- This includes dataframe support via `polars` and `arrow2`. Introduces a significant additional compilation and binary size.
- Due to the use of SIMD extensions may not be compatible with every minimal architecture.
## Passively supported platforms
These platforms are not actively managed through our CI so may encounter unintended regressions.
Furthermore certain features may not yet be available, even though we are willing to accept PRs trying to close that gap.
- OpenBSD
- e.g. missing the `ps` command
- FreeBSD
- e.g. missing the `ps` command
- Android via Termux
Help from the community to make sure they get tested and improved so they can become first class targets would be greatly appreciated!
## Providing builds and packaging
The Nushell team only provides a select few distribution sources and so far encourages community members to maintain the individual packages for particular package managers:
We provide:
- source code distribution via `crates.io` -> `cargo install nu`
- GitHub builds with each release: (following the build matrix of the nightly builds)
- the setup for `winget` packaging
### For package maintainers:
We aim to support the rust version that is two releases behind the most recent version of stable Rust so the build infrastructure of your packaging environment can already be proven out.

View File

@ -54,6 +54,7 @@ Detailed installation instructions can be found in the [installation chapter of
[![Packaging status](https://repology.org/badge/vertical-allrepos/nushell.svg)](https://repology.org/project/nushell/versions)
For details about which platforms the Nushell team actively supports, see [our platform support policy](PLATFORM_SUPPORT.md).
## Configuration
@ -198,7 +199,7 @@ topics that have been presented.
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.
- First and foremost, Nu is cross-platform. Commands and techniques should work across platforms and Nu has first-class support for Windows, macOS, and Linux.
- First and foremost, Nu is cross-platform. Commands and techniques should work across platforms and Nu has [first-class support for Windows, macOS, and Linux](PLATFORM_SUPPORT.md).
- Nu ensures compatibility with existing platform-specific executables.

View File

@ -5,27 +5,27 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2021"
license = "MIT"
name = "nu-cli"
version = "0.86.0"
version = "0.87.1"
[lib]
bench = false
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.86.0" }
nu-command = { path = "../nu-command", version = "0.86.0" }
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.87.1" }
nu-command = { path = "../nu-command", version = "0.87.1" }
nu-test-support = { path = "../nu-test-support", version = "0.87.1" }
rstest = { version = "0.18.1", default-features = false }
[dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.86.0" }
nu-engine = { path = "../nu-engine", version = "0.86.0" }
nu-path = { path = "../nu-path", version = "0.86.0" }
nu-parser = { path = "../nu-parser", version = "0.86.0" }
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
nu-utils = { path = "../nu-utils", version = "0.86.0" }
nu-color-config = { path = "../nu-color-config", version = "0.86.0" }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.87.1" }
nu-engine = { path = "../nu-engine", version = "0.87.1" }
nu-path = { path = "../nu-path", version = "0.87.1" }
nu-parser = { path = "../nu-parser", version = "0.87.1" }
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
nu-utils = { path = "../nu-utils", version = "0.87.1" }
nu-color-config = { path = "../nu-color-config", version = "0.87.1" }
nu-ansi-term = "0.49.0"
reedline = { version = "0.25.0", features = ["bashisms", "sqlite"] }
reedline = { version = "0.26.0", features = ["bashisms", "sqlite"] }
chrono = { default-features = false, features = ["std"], version = "0.4" }
crossterm = "0.27"
@ -39,7 +39,7 @@ percent-encoding = "2"
pathdiff = "0.2"
sysinfo = "0.29"
unicode-segmentation = "1.10"
uuid = { version = "1.4.1", features = ["v4"] }
uuid = { version = "1.5.0", features = ["v4"] }
[features]
plugin = []

View File

@ -36,7 +36,7 @@ impl Command for KeybindingsList {
vec![
Example {
description: "Get list of key modifiers",
example: "keybindings list -m",
example: "keybindings list --modifiers",
result: None,
},
Example {

View File

@ -1,6 +1,6 @@
use crate::completions::{
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
DotNuCompletion, FileCompletion, FlagCompletion, MatchAlgorithm, VariableCompletion,
DotNuCompletion, FileCompletion, FlagCompletion, VariableCompletion,
};
use nu_engine::eval_block;
use nu_parser::{flatten_expression, parse, FlatShape};
@ -39,15 +39,12 @@ impl NuCompleter {
) -> Vec<Suggestion> {
let config = self.engine_state.get_config();
let mut options = CompletionOptions {
let options = CompletionOptions {
case_sensitive: config.case_sensitive_completions,
match_algorithm: config.completion_algorithm.into(),
..Default::default()
};
if config.completion_algorithm == "fuzzy" {
options.match_algorithm = MatchAlgorithm::Fuzzy;
}
// Fetch
let mut suggestions =
completer.fetch(working_set, prefix.clone(), new_span, offset, pos, &options);
@ -347,7 +344,9 @@ impl NuCompleter {
if let Some(external_result) = self.external_completion(
block_id, &spans, offset, new_span,
) {
return external_result;
if !external_result.is_empty() {
return external_result;
}
}
}

View File

@ -1,5 +1,6 @@
use crate::completions::{matches, CompletionOptions};
use nu_path::home_dir;
use nu_protocol::{engine::StateWorkingSet, Span};
use std::path::{is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP};
fn complete_rec(
@ -152,9 +153,44 @@ pub fn complete_item(
pub fn escape_path(path: String, dir: bool) -> String {
let filename_contaminated = !dir && path.contains(['\'', '"', ' ', '#', '(', ')']);
let dirname_contaminated = dir && path.contains(['\'', '"', ' ', '#']);
if filename_contaminated || dirname_contaminated || path.parse::<f64>().is_ok() {
let maybe_flag = path.starts_with('-');
let maybe_number = path.parse::<f64>().is_ok();
if filename_contaminated || dirname_contaminated || maybe_flag || maybe_number {
format!("`{path}`")
} else {
path
}
}
pub struct AdjustView {
pub prefix: String,
pub span: Span,
pub readjusted: bool,
}
pub fn adjust_if_intermediate(
prefix: &[u8],
working_set: &StateWorkingSet,
mut span: nu_protocol::Span,
) -> AdjustView {
let span_contents = String::from_utf8_lossy(working_set.get_span_contents(span)).to_string();
let mut prefix = String::from_utf8_lossy(prefix).to_string();
// A difference of 1 because of the cursor's unicode code point in between.
// Using .chars().count() because unicode and Windows.
let readjusted = span_contents.chars().count() - prefix.chars().count() > 1;
if readjusted {
let remnant: String = span_contents
.chars()
.skip(prefix.chars().count() + 1)
.take_while(|&c| !is_separator(c))
.collect();
prefix.push_str(&remnant);
span = Span::new(span.start, span.start + prefix.chars().count() + 1);
}
AdjustView {
prefix,
span,
readjusted,
}
}

View File

@ -2,6 +2,7 @@ use std::fmt::Display;
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
use nu_parser::trim_quotes_str;
use nu_protocol::CompletionAlgorithm;
#[derive(Copy, Clone)]
pub enum SortBy {
@ -55,6 +56,15 @@ impl MatchAlgorithm {
}
}
impl From<CompletionAlgorithm> for MatchAlgorithm {
fn from(value: CompletionAlgorithm) -> Self {
match value {
CompletionAlgorithm::Prefix => MatchAlgorithm::Prefix,
CompletionAlgorithm::Fuzzy => MatchAlgorithm::Fuzzy,
}
}
}
impl TryFrom<String> for MatchAlgorithm {
type Error = InvalidMatchAlgorithm;

View File

@ -5,6 +5,7 @@ use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
PipelineData, Span, Type, Value,
};
use nu_utils::IgnoreCaseExt;
use reedline::Suggestion;
use std::collections::HashMap;
use std::sync::Arc;
@ -79,21 +80,20 @@ impl Completer for CustomCompletion {
.map(|pd| {
let value = pd.into_value(span);
match &value {
Value::Record { .. } => {
let completions = value
.get_data_by_key("completions")
Value::Record { val, .. } => {
let completions = val
.get("completions")
.and_then(|val| {
val.as_list()
.ok()
.map(|it| map_value_completions(it.iter(), span, offset))
})
.unwrap_or_default();
let options = value.get_data_by_key("options");
let options = val.get("options");
if let Some(Value::Record { .. }) = &options {
let options = options.unwrap_or_default();
if let Some(Value::Record { val: options, .. }) = &options {
let should_sort = options
.get_data_by_key("sort")
.get("sort")
.and_then(|val| val.as_bool().ok())
.unwrap_or(false);
@ -103,11 +103,11 @@ impl Completer for CustomCompletion {
custom_completion_options = Some(CompletionOptions {
case_sensitive: options
.get_data_by_key("case_sensitive")
.get("case_sensitive")
.and_then(|val| val.as_bool().ok())
.unwrap_or(true),
positional: options
.get_data_by_key("positional")
.get("positional")
.and_then(|val| val.as_bool().ok())
.unwrap_or(true),
sort_by: if should_sort {
@ -115,9 +115,7 @@ impl Completer for CustomCompletion {
} else {
SortBy::None
},
match_algorithm: match options
.get_data_by_key("completion_algorithm")
{
match_algorithm: match options.get("completion_algorithm") {
Some(option) => option
.as_string()
.ok()
@ -156,8 +154,8 @@ fn filter(prefix: &[u8], items: Vec<Suggestion>, options: &CompletionOptions) ->
(true, true) => it.value.as_bytes().starts_with(prefix),
(true, false) => it.value.contains(std::str::from_utf8(prefix).unwrap_or("")),
(false, positional) => {
let value = it.value.to_lowercase();
let prefix = std::str::from_utf8(prefix).unwrap_or("").to_lowercase();
let value = it.value.to_folded_case();
let prefix = std::str::from_utf8(prefix).unwrap_or("").to_folded_case();
if positional {
value.starts_with(&prefix)
} else {

View File

@ -1,4 +1,7 @@
use crate::completions::{completion_common::complete_item, Completer, CompletionOptions, SortBy};
use crate::completions::{
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
Completer, CompletionOptions, SortBy,
};
use nu_protocol::{
engine::{EngineState, StateWorkingSet},
levenshtein_distance, Span,
@ -21,19 +24,19 @@ impl DirectoryCompletion {
impl Completer for DirectoryCompletion {
fn fetch(
&mut self,
_: &StateWorkingSet,
working_set: &StateWorkingSet,
prefix: Vec<u8>,
span: Span,
offset: usize,
_: usize,
options: &CompletionOptions,
) -> Vec<Suggestion> {
let partial = String::from_utf8_lossy(&prefix).to_string();
let AdjustView { prefix, span, .. } = adjust_if_intermediate(&prefix, working_set, span);
// Filter only the folders
let output: Vec<_> = directory_completion(
span,
&partial,
&prefix,
&self.engine_state.current_work_dir(),
options,
)

View File

@ -1,8 +1,12 @@
use crate::completions::{completion_common::complete_item, Completer, CompletionOptions, SortBy};
use crate::completions::{
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
Completer, CompletionOptions, SortBy,
};
use nu_protocol::{
engine::{EngineState, StateWorkingSet},
levenshtein_distance, Span,
};
use nu_utils::IgnoreCaseExt;
use reedline::Suggestion;
use std::path::{Path, MAIN_SEPARATOR as SEP};
use std::sync::Arc;
@ -21,15 +25,21 @@ impl FileCompletion {
impl Completer for FileCompletion {
fn fetch(
&mut self,
_: &StateWorkingSet,
working_set: &StateWorkingSet,
prefix: Vec<u8>,
span: Span,
offset: usize,
_: usize,
options: &CompletionOptions,
) -> Vec<Suggestion> {
let prefix = String::from_utf8_lossy(&prefix).to_string();
let output: Vec<_> = file_path_completion(
let AdjustView {
prefix,
span,
readjusted,
} = adjust_if_intermediate(&prefix, working_set, span);
let output: Vec<_> = complete_item(
readjusted,
span,
&prefix,
&self.engine_state.current_work_dir(),
@ -116,7 +126,7 @@ pub fn matches(partial: &str, from: &str, options: &CompletionOptions) -> bool {
if !options.case_sensitive {
return options
.match_algorithm
.matches_str(&from.to_ascii_lowercase(), &partial.to_ascii_lowercase());
.matches_str(&from.to_folded_case(), &partial.to_folded_case());
}
options.match_algorithm.matches_str(from, partial)

View File

@ -43,10 +43,8 @@ impl Completer for VariableCompletion {
options: &CompletionOptions,
) -> Vec<Suggestion> {
let mut output = vec![];
let builtins = ["$nu", "$in", "$env", "$nothing"];
let var_str = std::str::from_utf8(&self.var_context.0)
.unwrap_or("")
.to_lowercase();
let builtins = ["$nu", "$in", "$env"];
let var_str = std::str::from_utf8(&self.var_context.0).unwrap_or("");
let var_id = working_set.find_variable(&self.var_context.0);
let current_span = reedline::Span {
start: span.start - offset,
@ -57,7 +55,7 @@ impl Completer for VariableCompletion {
// Completions for the given variable
if !var_str.is_empty() {
// Completion for $env.<tab>
if var_str.as_str() == "$env" {
if var_str == "$env" {
let env_vars = self.stack.get_env_vars(&self.engine_state);
// Return nested values
@ -109,7 +107,7 @@ impl Completer for VariableCompletion {
}
// Completions for $nu.<tab>
if var_str.as_str() == "$nu" {
if var_str == "$nu" {
// Eval nu var
if let Ok(nuval) = eval_variable(
&self.engine_state,
@ -237,9 +235,9 @@ fn nested_suggestions(
match value {
Value::Record { val, .. } => {
// Add all the columns as completion
for item in val.cols {
for (col, _) in val.into_iter() {
output.push(Suggestion {
value: item,
value: col,
description: None,
extra: None,
span: current_span,

View File

@ -28,8 +28,8 @@ pub fn evaluate_commands(
let (block, delta) = {
if let Some(ref t_mode) = table_mode {
let mut config = engine_state.get_config().clone();
config.table_mode = t_mode.as_string()?;
engine_state.set_config(&config);
config.table_mode = t_mode.as_string()?.parse().unwrap_or_default();
engine_state.set_config(config);
}
let mut working_set = StateWorkingSet::new(engine_state);
@ -55,7 +55,7 @@ pub fn evaluate_commands(
Ok(pipeline_data) => {
let mut config = engine_state.get_config().clone();
if let Some(t_mode) = table_mode {
config.table_mode = t_mode.as_string()?;
config.table_mode = t_mode.as_string()?.parse().unwrap_or_default();
}
crate::eval_file::print_table_or_error(engine_state, stack, pipeline_data, &mut config)
}

View File

@ -195,7 +195,7 @@ pub(crate) fn print_table_or_error(
};
// Change the engine_state config to use the passed in configuration
engine_state.set_config(config);
engine_state.set_config(config.clone());
if let PipelineData::Value(Value::Error { error, .. }, ..) = &pipeline_data {
let working_set = StateWorkingSet::new(engine_state);

View File

@ -1,5 +1,6 @@
use nu_engine::documentation::get_flags_section;
use nu_protocol::{engine::EngineState, levenshtein_distance};
use nu_utils::IgnoreCaseExt;
use reedline::{Completer, Suggestion};
use std::fmt::Write;
use std::sync::Arc;
@ -13,21 +14,19 @@ impl NuHelpCompleter {
fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> {
let full_commands = self.0.get_signatures_with_examples(false);
let folded_line = line.to_folded_case();
//Vec<(Signature, Vec<Example>, bool, bool)> {
let mut commands = full_commands
.iter()
.filter(|(sig, _, _, _, _)| {
sig.name.to_lowercase().contains(&line.to_lowercase())
|| sig.usage.to_lowercase().contains(&line.to_lowercase())
sig.name.to_folded_case().contains(&folded_line)
|| sig.usage.to_folded_case().contains(&folded_line)
|| sig
.search_terms
.iter()
.any(|term| term.to_lowercase().contains(&line.to_lowercase()))
|| sig
.extra_usage
.to_lowercase()
.contains(&line.to_lowercase())
.any(|term| term.to_folded_case().contains(&folded_line))
|| sig.extra_usage.to_folded_case().contains(&folded_line)
})
.collect::<Vec<_>>();

View File

@ -80,24 +80,18 @@ fn convert_to_suggestions(
only_buffer_difference: bool,
) -> Vec<Suggestion> {
match value {
Value::Record { .. } => {
let text = value
.get_data_by_key("value")
Value::Record { val, .. } => {
let text = val
.get("value")
.and_then(|val| val.as_string().ok())
.unwrap_or_else(|| "No value key".to_string());
let description = value
.get_data_by_key("description")
.and_then(|val| val.as_string().ok());
let description = val.get("description").and_then(|val| val.as_string().ok());
let span = match value.get_data_by_key("span") {
Some(span @ Value::Record { .. }) => {
let start = span
.get_data_by_key("start")
.and_then(|val| val.as_int().ok());
let end = span
.get_data_by_key("end")
.and_then(|val| val.as_int().ok());
let span = match val.get("span") {
Some(Value::Record { val: span, .. }) => {
let start = span.get("start").and_then(|val| val.as_int().ok());
let end = span.get("end").and_then(|val| val.as_int().ok());
match (start, end) {
(Some(start), Some(end)) => {
let start = start.min(end);
@ -126,12 +120,12 @@ fn convert_to_suggestions(
},
};
let extra = match value.get_data_by_key("extra") {
let extra = match val.get("extra") {
Some(Value::List { vals, .. }) => {
let extra: Vec<String> = vals
.into_iter()
.iter()
.filter_map(|extra| match extra {
Value::String { val, .. } => Some(val),
Value::String { val, .. } => Some(val.clone()),
_ => None,
})
.collect();

View File

@ -40,13 +40,9 @@ fn get_prompt_string(
stack
.get_env_var(engine_state, prompt)
.and_then(|v| match v {
Value::Closure {
val: block_id,
captures,
..
} => {
let block = engine_state.get_block(block_id);
let mut stack = stack.captures_to_stack(&captures);
Value::Closure { val, .. } => {
let block = engine_state.get_block(val.block_id);
let mut stack = stack.captures_to_stack(val.captures);
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
let ret_val =
eval_subexpression(engine_state, &mut stack, block, PipelineData::empty());

View File

@ -7,8 +7,8 @@ use nu_parser::parse;
use nu_protocol::{
create_menus,
engine::{EngineState, Stack, StateWorkingSet},
extract_value, Config, ParsedKeybinding, ParsedMenu, PipelineData, Record, ShellError, Span,
Value,
extract_value, Config, EditBindings, ParsedKeybinding, ParsedMenu, PipelineData, Record,
ShellError, Span, Value,
};
use reedline::{
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
@ -248,11 +248,11 @@ pub(crate) fn add_columnar_menu(
Value::Nothing { .. } => {
Ok(line_editor.with_menu(ReedlineMenu::EngineCompleter(Box::new(columnar_menu))))
}
Value::Closure { val, captures, .. } => {
Value::Closure { val, .. } => {
let menu_completer = NuMenuCompleter::new(
*val,
val.block_id,
span,
stack.captures_to_stack(captures),
stack.captures_to_stack(val.captures.clone()),
engine_state,
only_buffer_difference,
);
@ -330,11 +330,11 @@ pub(crate) fn add_list_menu(
Value::Nothing { .. } => {
Ok(line_editor.with_menu(ReedlineMenu::HistoryMenu(Box::new(list_menu))))
}
Value::Closure { val, captures, .. } => {
Value::Closure { val, .. } => {
let menu_completer = NuMenuCompleter::new(
*val,
val.block_id,
span,
stack.captures_to_stack(captures),
stack.captures_to_stack(val.captures.clone()),
engine_state,
only_buffer_difference,
);
@ -448,11 +448,11 @@ pub(crate) fn add_description_menu(
completer,
}))
}
Value::Closure { val, captures, .. } => {
Value::Closure { val, .. } => {
let menu_completer = NuMenuCompleter::new(
*val,
val.block_id,
span,
stack.captures_to_stack(captures),
stack.captures_to_stack(val.captures.clone()),
engine_state,
only_buffer_difference,
);
@ -537,11 +537,11 @@ pub(crate) fn create_keybindings(config: &Config) -> Result<KeybindingsMode, She
let mut insert_keybindings = default_vi_insert_keybindings();
let mut normal_keybindings = default_vi_normal_keybindings();
match config.edit_mode.as_str() {
"emacs" => {
match config.edit_mode {
EditBindings::Emacs => {
add_menu_keybindings(&mut emacs_keybindings);
}
_ => {
EditBindings::Vi => {
add_menu_keybindings(&mut insert_keybindings);
add_menu_keybindings(&mut normal_keybindings);
}
@ -557,9 +557,9 @@ pub(crate) fn create_keybindings(config: &Config) -> Result<KeybindingsMode, She
)?
}
match config.edit_mode.as_str() {
"emacs" => Ok(KeybindingsMode::Emacs(emacs_keybindings)),
_ => Ok(KeybindingsMode::Vi {
match config.edit_mode {
EditBindings::Emacs => Ok(KeybindingsMode::Emacs(emacs_keybindings)),
EditBindings::Vi => Ok(KeybindingsMode::Vi {
insert_keybindings,
normal_keybindings,
}),
@ -616,7 +616,7 @@ fn add_parsed_keybinding(
let modifier = match keybinding
.modifier
.into_string("", config)
.to_lowercase()
.to_ascii_lowercase()
.as_str()
{
"control" => KeyModifiers::CONTROL,
@ -641,7 +641,7 @@ fn add_parsed_keybinding(
let keycode = match keybinding
.keycode
.into_string("", config)
.to_lowercase()
.to_ascii_lowercase()
.as_str()
{
"backspace" => KeyCode::Backspace,
@ -728,7 +728,7 @@ fn parse_event(value: &Value, config: &Config) -> Result<Option<ReedlineEvent>,
match value {
Value::Record { val: record, .. } => match EventType::try_from_record(record, span)? {
EventType::Send(value) => event_from_record(
value.into_string("", config).to_lowercase().as_str(),
value.into_string("", config).to_ascii_lowercase().as_str(),
record,
config,
span,
@ -736,7 +736,7 @@ fn parse_event(value: &Value, config: &Config) -> Result<Option<ReedlineEvent>,
.map(Some),
EventType::Edit(value) => {
let edit = edit_from_record(
value.into_string("", config).to_lowercase().as_str(),
value.into_string("", config).to_ascii_lowercase().as_str(),
record,
config,
span,
@ -976,13 +976,15 @@ fn extract_char(value: &Value, config: &Config) -> Result<char, ShellError> {
#[cfg(test)]
mod test {
use nu_protocol::record;
use super::*;
#[test]
fn test_send_event() {
let cols = vec!["send".to_string()];
let vals = vec![Value::test_string("Enter")];
let event = Record { vals, cols };
let event = record! {
"send" => Value::test_string("Enter"),
};
let span = Span::test_data();
let b = EventType::try_from_record(&event, span).unwrap();
@ -997,9 +999,9 @@ mod test {
#[test]
fn test_edit_event() {
let cols = vec!["edit".to_string()];
let vals = vec![Value::test_string("Clear")];
let event = Record { vals, cols };
let event = record! {
"edit" => Value::test_string("Clear"),
};
let span = Span::test_data();
let b = EventType::try_from_record(&event, span).unwrap();
@ -1017,12 +1019,10 @@ mod test {
#[test]
fn test_send_menu() {
let cols = vec!["send".to_string(), "name".to_string()];
let vals = vec![
Value::test_string("Menu"),
Value::test_string("history_menu"),
];
let event = Record { vals, cols };
let event = record! {
"send" => Value::test_string("Menu"),
"name" => Value::test_string("history_menu"),
};
let span = Span::test_data();
let b = EventType::try_from_record(&event, span).unwrap();
@ -1040,28 +1040,19 @@ mod test {
#[test]
fn test_until_event() {
// Menu event
let cols = vec!["send".to_string(), "name".to_string()];
let vals = vec![
Value::test_string("Menu"),
Value::test_string("history_menu"),
];
let menu_event = Value::test_record(Record { cols, vals });
// Enter event
let cols = vec!["send".to_string()];
let vals = vec![Value::test_string("Enter")];
let enter_event = Value::test_record(Record { cols, vals });
// Until event
let cols = vec!["until".to_string()];
let vals = vec![Value::list(
vec![menu_event, enter_event],
Span::test_data(),
)];
let event = Record { cols, vals };
let menu_event = Value::test_record(record! {
"send" => Value::test_string("Menu"),
"name" => Value::test_string("history_menu"),
});
let enter_event = Value::test_record(record! {
"send" => Value::test_string("Enter"),
});
let event = record! {
"until" => Value::list(
vec![menu_event, enter_event],
Span::test_data(),
),
};
let span = Span::test_data();
let b = EventType::try_from_record(&event, span).unwrap();
@ -1082,22 +1073,13 @@ mod test {
#[test]
fn test_multiple_event() {
// Menu event
let cols = vec!["send".to_string(), "name".to_string()];
let vals = vec![
Value::test_string("Menu"),
Value::test_string("history_menu"),
];
let menu_event = Value::test_record(Record { cols, vals });
// Enter event
let cols = vec!["send".to_string()];
let vals = vec![Value::test_string("Enter")];
let enter_event = Value::test_record(Record { cols, vals });
// Multiple event
let menu_event = Value::test_record(record! {
"send" => Value::test_string("Menu"),
"name" => Value::test_string("history_menu"),
});
let enter_event = Value::test_record(record! {
"send" => Value::test_string("Enter"),
});
let event = Value::list(vec![menu_event, enter_event], Span::test_data());
let config = Config::default();
@ -1113,9 +1095,9 @@ mod test {
#[test]
fn test_error() {
let cols = vec!["not_exist".to_string()];
let vals = vec![Value::test_string("Enter")];
let event = Record { cols, vals };
let event = record! {
"not_exist" => Value::test_string("Enter"),
};
let span = Span::test_data();
let b = EventType::try_from_record(&event, span);

View File

@ -22,8 +22,8 @@ use nu_protocol::{
};
use nu_utils::utils::perf;
use reedline::{
CursorConfig, DefaultHinter, EditCommand, Emacs, FileBackedHistory, HistorySessionId, Reedline,
SqliteBackedHistory, Vi,
CursorConfig, CwdAwareHinter, EditCommand, Emacs, FileBackedHistory, HistorySessionId,
Reedline, SqliteBackedHistory, Vi,
};
use std::{
env::temp_dir,
@ -108,16 +108,8 @@ pub fn evaluate_repl(
use_color,
);
let config = engine_state.get_config();
if config.bracketed_paste {
// try to enable bracketed paste
// It doesn't work on windows system: https://github.com/crossterm-rs/crossterm/issues/737
#[cfg(not(target_os = "windows"))]
let _ = line_editor.enable_bracketed_paste();
}
// Setup history_isolation aka "history per session"
let history_isolation = config.history_isolation;
let history_isolation = engine_state.get_config().history_isolation;
let history_session_id = if history_isolation {
Reedline::create_history_session_id()
} else {
@ -182,12 +174,8 @@ pub fn evaluate_repl(
);
}
if engine_state.get_config().use_kitty_protocol {
if line_editor.can_use_kitty_protocol() {
line_editor.enable_kitty_protocol();
} else {
warn!("Terminal doesn't support use_kitty_protocol config");
}
if engine_state.get_config().use_kitty_protocol && !reedline::kitty_protocol_available() {
warn!("Terminal doesn't support use_kitty_protocol config");
}
loop {
@ -245,15 +233,9 @@ pub fn evaluate_repl(
// Find the configured cursor shapes for each mode
let cursor_config = CursorConfig {
vi_insert: config
.cursor_shape_vi_insert
.map(map_nucursorshape_to_cursorshape),
vi_normal: config
.cursor_shape_vi_normal
.map(map_nucursorshape_to_cursorshape),
emacs: config
.cursor_shape_emacs
.map(map_nucursorshape_to_cursorshape),
vi_insert: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_insert),
vi_normal: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_normal),
emacs: map_nucursorshape_to_cursorshape(config.cursor_shape_emacs),
};
perf(
"get config/cursor config",
@ -267,6 +249,10 @@ pub fn evaluate_repl(
start_time = std::time::Instant::now();
line_editor = line_editor
.use_kitty_keyboard_enhancement(config.use_kitty_protocol)
// try to enable bracketed paste
// It doesn't work on windows system: https://github.com/crossterm-rs/crossterm/issues/737
.use_bracketed_paste(cfg!(not(target_os = "windows")) && config.bracketed_paste)
.with_highlighter(Box::new(NuHighlighter {
engine_state: engine_reference.clone(),
config: config.clone(),
@ -302,7 +288,7 @@ pub fn evaluate_repl(
line_editor.with_hinter(Box::new({
// As of Nov 2022, "hints" color_config closures only get `null` passed in.
let style = style_computer.compute("hints", &Value::nothing(Span::unknown()));
DefaultHinter::default().with_style(style)
CwdAwareHinter::default().with_style(style)
}))
} else {
line_editor.disable_hints()
@ -596,10 +582,6 @@ pub fn evaluate_repl(
PipelineData::empty(),
false,
);
if engine_state.get_config().bracketed_paste {
#[cfg(not(target_os = "windows"))]
let _ = line_editor.enable_bracketed_paste();
}
}
let cmd_duration = start_time.elapsed();
@ -626,19 +608,29 @@ pub fn evaluate_repl(
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
let path = cwd.as_string()?;
// Communicate the path as OSC 7 (often used for spawning new tabs in the same dir)
run_ansi_sequence(&format!(
"\x1b]7;file://{}{}{}\x1b\\",
percent_encoding::utf8_percent_encode(
&hostname.unwrap_or_else(|| "localhost".to_string()),
percent_encoding::CONTROLS
),
if path.starts_with('/') { "" } else { "/" },
percent_encoding::utf8_percent_encode(
&path,
percent_encoding::CONTROLS
)
))?;
// Supported escape sequences of Microsoft's Visual Studio Code (vscode)
// https://code.visualstudio.com/docs/terminal/shell-integration#_supported-escape-sequences
if stack.get_env_var(engine_state, "TERM_PROGRAM")
== Some(Value::test_string("vscode"))
{
// If we're in vscode, run their specific ansi escape sequence.
// This is helpful for ctrl+g to change directories in the terminal.
run_ansi_sequence(&format!("\x1b]633;P;Cwd={}\x1b\\", path))?;
} else {
// Otherwise, communicate the path as OSC 7 (often used for spawning new tabs in the same dir)
run_ansi_sequence(&format!(
"\x1b]7;file://{}{}{}\x1b\\",
percent_encoding::utf8_percent_encode(
&hostname.unwrap_or_else(|| "localhost".to_string()),
percent_encoding::CONTROLS
),
if path.starts_with('/') { "" } else { "/" },
percent_encoding::utf8_percent_encode(
&path,
percent_encoding::CONTROLS
)
))?;
}
// Try to abbreviate string for windows title
let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
@ -760,14 +752,15 @@ fn update_line_editor_history(
Ok(line_editor)
}
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> SetCursorStyle {
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> Option<SetCursorStyle> {
match shape {
NuCursorShape::Block => SetCursorStyle::SteadyBlock,
NuCursorShape::UnderScore => SetCursorStyle::SteadyUnderScore,
NuCursorShape::Line => SetCursorStyle::SteadyBar,
NuCursorShape::BlinkBlock => SetCursorStyle::BlinkingBlock,
NuCursorShape::BlinkUnderScore => SetCursorStyle::BlinkingUnderScore,
NuCursorShape::BlinkLine => SetCursorStyle::BlinkingBar,
NuCursorShape::Block => Some(SetCursorStyle::SteadyBlock),
NuCursorShape::UnderScore => Some(SetCursorStyle::SteadyUnderScore),
NuCursorShape::Line => Some(SetCursorStyle::SteadyBar),
NuCursorShape::BlinkBlock => Some(SetCursorStyle::BlinkingBlock),
NuCursorShape::BlinkUnderScore => Some(SetCursorStyle::BlinkingUnderScore),
NuCursorShape::BlinkLine => Some(SetCursorStyle::BlinkingBar),
NuCursorShape::Inherit => None,
}
}

View File

@ -527,6 +527,10 @@ fn file_completion_quoted() {
let suggestions = completer.complete(target_dir, target_dir.len());
let expected_paths: Vec<String> = vec![
"`--help`".to_string(),
"`-42`".to_string(),
"`-inf`".to_string(),
"`4.2`".to_string(),
"`te st.txt`".to_string(),
"`te#st.txt`".to_string(),
"`te'st.txt`".to_string(),
@ -791,7 +795,7 @@ fn run_external_completion(block: &str, input: &str) -> Vec<Suggestion> {
// Change config adding the external completer
let mut config = engine_state.get_config().clone();
config.external_completer = Some(latest_block_id);
engine_state.set_config(&config);
engine_state.set_config(config);
// Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine_state), stack);

View File

@ -5,14 +5,21 @@ edition = "2021"
license = "MIT"
name = "nu-cmd-base"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
version = "0.86.0"
version = "0.87.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.86.0" }
nu-parser = { path = "../nu-parser", version = "0.86.0" }
nu-path = { path = "../nu-path", version = "0.86.0" }
nu-protocol = { version = "0.86.0", path = "../nu-protocol" }
indexmap = { version = "2.0" }
miette = { version = "5.10", features = ["fancy-no-backtrace"] }
nu-engine = { path = "../nu-engine", version = "0.87.1" }
nu-glob = { path = "../nu-glob", version = "0.87.1" }
nu-parser = { path = "../nu-parser", version = "0.87.1" }
nu-path = { path = "../nu-path", version = "0.87.1" }
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
nu-utils = { path = "../nu-utils", version = "0.87.1" }
indexmap = "2.1"
miette = "5.10.0"
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.87.1" }
rstest = "0.18.2"

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 - 2023 The Nushell Project Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,207 @@
// utilities for expanding globs in command arguments
use nu_glob::{glob_with_parent, MatchOptions, Paths};
use nu_protocol::{ShellError, Spanned};
use std::fs;
use std::path::{Path, PathBuf};
// standard glob options to use for filesystem command arguments
const GLOB_PARAMS: MatchOptions = MatchOptions {
case_sensitive: true,
require_literal_separator: false,
require_literal_leading_dot: false,
recursive_match_hidden_dir: true,
};
// handle an argument that could be a literal path or a glob.
// if literal path, return just that (whether user can access it or not).
// if glob, expand into matching paths, using GLOB_PARAMS options.
pub fn arg_glob(
pattern: &Spanned<String>, // alleged path or glob
cwd: &Path, // current working directory
) -> Result<Paths, ShellError> {
arg_glob_opt(pattern, cwd, GLOB_PARAMS)
}
// variant of [arg_glob] that requires literal dot prefix in pattern to match dot-prefixed path.
pub fn arg_glob_leading_dot(pattern: &Spanned<String>, cwd: &Path) -> Result<Paths, ShellError> {
arg_glob_opt(
pattern,
cwd,
MatchOptions {
require_literal_leading_dot: true,
..GLOB_PARAMS
},
)
}
fn arg_glob_opt(
pattern: &Spanned<String>,
cwd: &Path,
options: MatchOptions,
) -> Result<Paths, ShellError> {
// remove ansi coloring (?)
let pattern = {
Spanned {
item: nu_utils::strip_ansi_string_unlikely(pattern.item.clone()),
span: pattern.span,
}
};
// if there's a file with same path as the pattern, just return that.
let pp = cwd.join(&pattern.item);
let md = fs::metadata(pp);
#[allow(clippy::single_match)]
match md {
Ok(_metadata) => {
return Ok(Paths::single(&PathBuf::from(pattern.item), cwd));
}
// file not found, but also "invalid chars in file" (e.g * on Windows). Fall through and glob
Err(_) => {}
}
// user wasn't referring to a specific thing in filesystem, try to glob it.
match glob_with_parent(&pattern.item, options, cwd) {
Ok(p) => Ok(p),
Err(pat_err) => {
Err(ShellError::InvalidGlobPattern(
pat_err.msg.into(),
pattern.span, // improve specificity
))
}
}
}
#[cfg(test)]
mod test {
use super::*;
use nu_glob::GlobResult;
use nu_protocol::{Span, Spanned};
use nu_test_support::fs::Stub::EmptyFile;
use nu_test_support::playground::Playground;
use rstest::rstest;
fn spanned_string(str: &str) -> Spanned<String> {
Spanned {
item: str.to_string(),
span: Span::test_data(),
}
}
#[test]
fn does_something() {
let act = arg_glob(&spanned_string("*"), &PathBuf::from("."));
assert!(act.is_ok());
for f in act.expect("checked ok") {
match f {
Ok(p) => {
assert!(!p.to_str().unwrap().is_empty());
}
Err(e) => panic!("unexpected error {:?}", e),
};
}
}
#[test]
fn glob_format_error() {
let act = arg_glob(&spanned_string(r#"ab]c[def"#), &PathBuf::from("."));
assert!(act.is_err());
}
#[rstest]
#[case("*", 4, "no dirs")]
#[case("**/*", 7, "incl dirs")]
fn glob_subdirs(#[case] pat: &str, #[case] exp_count: usize, #[case] case: &str) {
Playground::setup("glob_subdirs", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("yehuda.txt"),
EmptyFile("jttxt"),
EmptyFile("andres.txt"),
]);
sandbox.mkdir(".children");
sandbox.within(".children").with_files(vec![
EmptyFile("timothy.txt"),
EmptyFile("tiffany.txt"),
EmptyFile("trish.txt"),
]);
let p: Vec<GlobResult> = arg_glob(&spanned_string(pat), &dirs.test)
.expect("no error")
.collect();
assert_eq!(
exp_count,
p.iter().filter(|i| i.is_ok()).count(),
" case: {case} ",
);
// expected behavior -- that directories are included in results (if name matches pattern)
let t = p
.iter()
.any(|i| i.as_ref().unwrap().to_string_lossy().contains(".children"));
assert!(t, "check for dir, case {case}");
})
}
#[rstest]
#[case("yehuda.txt", true, 1, "matches literal path")]
#[case("*", false, 3, "matches glob")]
#[case(r#"bad[glob.foo"#, true, 1, "matches literal, would be bad glob pat")]
fn exact_vs_glob(
#[case] pat: &str,
#[case] exp_matches_input: bool,
#[case] exp_count: usize,
#[case] case: &str,
) {
Playground::setup("exact_vs_glob", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("yehuda.txt"),
EmptyFile("jttxt"),
EmptyFile("bad[glob.foo"),
]);
let res = arg_glob(&spanned_string(pat), &dirs.test)
.expect("no error")
.collect::<Vec<GlobResult>>();
eprintln!("res: {:?}", res);
if exp_matches_input {
assert_eq!(
exp_count,
res.len(),
" case {case}: matches input, but count not 1? "
);
assert_eq!(
&res[0].as_ref().unwrap().to_string_lossy(),
pat, // todo: is it OK for glob to return relative paths (not to current cwd, but to arg cwd of arg_glob)?
);
} else {
assert_eq!(exp_count, res.len(), " case: {}: matched glob", case);
}
})
}
#[rstest]
#[case(r#"realbad[glob.foo"#, true, 1, "error, bad glob")]
fn exact_vs_bad_glob(
// if path doesn't exist but pattern is not valid glob, should get error.
#[case] pat: &str,
#[case] _exp_matches_input: bool,
#[case] _exp_count: usize,
#[case] _tag: &str,
) {
Playground::setup("exact_vs_bad_glob", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("yehuda.txt"),
EmptyFile("jttxt"),
EmptyFile("bad[glob.foo"),
]);
let res = arg_glob(&spanned_string(pat), &dirs.test);
assert!(res
.expect_err("expected error")
.to_string()
.contains("Invalid glob pattern"));
})
}
}

View File

@ -6,7 +6,7 @@ pub fn merge_descriptors(values: &[Value]) -> Vec<String> {
let mut seen: IndexSet<String> = indexset! {};
for value in values {
let data_descriptors = match value {
Value::Record { val, .. } => val.cols.clone(),
Value::Record { val, .. } => val.columns().cloned().collect(),
_ => vec!["".to_string()],
};
for desc in data_descriptors {

View File

@ -2,7 +2,6 @@ use crate::util::get_guaranteed_cwd;
use miette::Result;
use nu_engine::{eval_block, eval_block_with_early_return};
use nu_parser::parse;
use nu_protocol::ast::PathMember;
use nu_protocol::cli_error::{report_error, report_error_new};
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
use nu_protocol::{BlockId, PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId};
@ -62,28 +61,8 @@ pub fn eval_hook(
value: &Value,
hook_name: &str,
) -> Result<PipelineData, ShellError> {
let value_span = value.span();
// Hooks can optionally be a record in this form:
// {
// condition: {|before, after| ... } # block that evaluates to true/false
// code: # block or a string
// }
// The condition block will be run to check whether the main hook (in `code`) should be run.
// If it returns true (the default if a condition block is not specified), the hook should be run.
let condition_path = PathMember::String {
val: "condition".to_string(),
span: value_span,
optional: false,
};
let mut output = PipelineData::empty();
let code_path = PathMember::String {
val: "code".to_string(),
span: value_span,
optional: false,
};
let span = value.span();
match value {
Value::String { val, .. } => {
@ -161,46 +140,47 @@ pub fn eval_hook(
)?;
}
}
Value::Record { .. } => {
let do_run_hook = if let Ok(condition) =
value.clone().follow_cell_path(&[condition_path], false)
{
Value::Record { val, .. } => {
// Hooks can optionally be a record in this form:
// {
// condition: {|before, after| ... } # block that evaluates to true/false
// code: # block or a string
// }
// The condition block will be run to check whether the main hook (in `code`) should be run.
// If it returns true (the default if a condition block is not specified), the hook should be run.
let do_run_hook = if let Some(condition) = val.get("condition") {
let other_span = condition.span();
match condition {
Value::Block { val: block_id, .. } | Value::Closure { val: block_id, .. } => {
match run_hook_block(
engine_state,
stack,
block_id,
None,
arguments.clone(),
other_span,
) {
Ok(pipeline_data) => {
if let PipelineData::Value(Value::Bool { val, .. }, ..) =
pipeline_data
{
val
} else {
return Err(ShellError::UnsupportedConfigValue(
"boolean output".to_string(),
"other PipelineData variant".to_string(),
other_span,
));
}
}
Err(err) => {
return Err(err);
if let Ok(block_id) = condition.as_block() {
match run_hook_block(
engine_state,
stack,
block_id,
None,
arguments.clone(),
other_span,
) {
Ok(pipeline_data) => {
if let PipelineData::Value(Value::Bool { val, .. }, ..) = pipeline_data
{
val
} else {
return Err(ShellError::UnsupportedConfigValue(
"boolean output".to_string(),
"other PipelineData variant".to_string(),
other_span,
));
}
}
Err(err) => {
return Err(err);
}
}
other => {
return Err(ShellError::UnsupportedConfigValue(
"block".to_string(),
format!("{}", other.get_type()),
other_span,
));
}
} else {
return Err(ShellError::UnsupportedConfigValue(
"block".to_string(),
format!("{}", condition.get_type()),
other_span,
));
}
} else {
// always run the hook
@ -208,7 +188,13 @@ pub fn eval_hook(
};
if do_run_hook {
let follow = value.clone().follow_cell_path(&[code_path], false)?;
let Some(follow) = val.get("code") else {
return Err(ShellError::CantFindColumn {
col_name: "code".into(),
span,
src_span: span,
});
};
let source_span = follow.span();
match follow {
Value::String { val, .. } => {
@ -274,17 +260,17 @@ pub fn eval_hook(
run_hook_block(
engine_state,
stack,
block_id,
*block_id,
input,
arguments,
source_span,
)?;
}
Value::Closure { val: block_id, .. } => {
Value::Closure { val, .. } => {
run_hook_block(
engine_state,
stack,
block_id,
val.block_id,
input,
arguments,
source_span,
@ -303,8 +289,8 @@ pub fn eval_hook(
Value::Block { val: block_id, .. } => {
output = run_hook_block(engine_state, stack, *block_id, input, arguments, span)?;
}
Value::Closure { val: block_id, .. } => {
output = run_hook_block(engine_state, stack, *block_id, input, arguments, span)?;
Value::Closure { val, .. } => {
output = run_hook_block(engine_state, stack, val.block_id, input, arguments, span)?;
}
other => {
return Err(ShellError::UnsupportedConfigValue(

View File

@ -1,4 +1,7 @@
mod arg_glob;
pub mod formats;
pub mod hook;
pub mod input_handler;
pub mod util;
pub use arg_glob::arg_glob;
pub use arg_glob::arg_glob_leading_dot;

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT"
name = "nu-cmd-dataframe"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-dataframe"
version = "0.86.0"
version = "0.87.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,14 +13,15 @@ version = "0.86.0"
bench = false
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.86.0" }
nu-parser = { path = "../nu-parser", version = "0.86.0" }
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
nu-engine = { path = "../nu-engine", version = "0.87.1" }
nu-parser = { path = "../nu-parser", version = "0.87.1" }
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
# Potential dependencies for extras
chrono = { version = "0.4", features = ["std", "unstable-locales"], default-features = false }
chrono-tz = "0.8"
fancy-regex = "0.11"
indexmap = { version = "2.0" }
indexmap = { version = "2.1" }
num = { version = "0.4", optional = true }
serde = { version = "1.0", features = ["derive"] }
sqlparser = { version = "0.36.1", optional = true }
@ -38,6 +39,10 @@ features = [
"dtype-categorical",
"dtype-datetime",
"dtype-struct",
"dtype-i8",
"dtype-i16",
"dtype-u8",
"dtype-u16",
"dynamic_group_by",
"ipc",
"is_in",
@ -61,5 +66,5 @@ dataframe = ["num", "polars", "polars-io", "sqlparser"]
default = []
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.86.0" }
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.87.1" }
nu-test-support = { path = "../nu-test-support", version = "0.87.1" }

View File

@ -1,10 +1,10 @@
use super::super::values::{Column, NuDataFrame};
use super::super::values::NuDataFrame;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
Category, Example, PipelineData, ShellError, Signature, Span, Type,
};
use polars::prelude::DataFrameOps;
use polars::{prelude::*, series::Series};
#[derive(Clone)]
pub struct Dummies;
@ -34,24 +34,15 @@ impl Command for Dummies {
description: "Create new dataframe with dummy variables from a dataframe",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr dummies",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a_1".to_string(),
vec![Value::test_int(1), Value::test_int(0)],
),
Column::new(
"a_3".to_string(),
vec![Value::test_int(0), Value::test_int(1)],
),
Column::new(
"b_2".to_string(),
vec![Value::test_int(1), Value::test_int(0)],
),
Column::new(
"b_4".to_string(),
vec![Value::test_int(0), Value::test_int(1)],
),
])
NuDataFrame::try_from_series(
vec![
Series::new("a_1", &[1_u8, 0]),
Series::new("a_3", &[0_u8, 1]),
Series::new("b_2", &[1_u8, 0]),
Series::new("b_4", &[0_u8, 1]),
],
Span::test_data(),
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),
@ -60,38 +51,14 @@ impl Command for Dummies {
description: "Create new dataframe with dummy variables from a series",
example: "[1 2 2 3 3] | dfr into-df | dfr dummies",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"0_1".to_string(),
vec![
Value::test_int(1),
Value::test_int(0),
Value::test_int(0),
Value::test_int(0),
Value::test_int(0),
],
),
Column::new(
"0_2".to_string(),
vec![
Value::test_int(0),
Value::test_int(1),
Value::test_int(1),
Value::test_int(0),
Value::test_int(0),
],
),
Column::new(
"0_3".to_string(),
vec![
Value::test_int(0),
Value::test_int(0),
Value::test_int(0),
Value::test_int(1),
Value::test_int(1),
],
),
])
NuDataFrame::try_from_series(
vec![
Series::new("0_1", &[1_u8, 0, 0, 0, 0]),
Series::new("0_2", &[0_u8, 1, 1, 0, 0]),
Series::new("0_3", &[0_u8, 0, 0, 1, 1]),
],
Span::test_data(),
)
.expect("simple df for test should not fail")
.into_value(Span::test_data()),
),

View File

@ -152,7 +152,7 @@ fn apply_window_spec(expr: Expr, window_type: Option<&WindowType>) -> Result<Exp
fn parse_sql_function(sql_function: &SQLFunction) -> Result<Expr> {
use sqlparser::ast::{FunctionArg, FunctionArgExpr};
// Function name mostly do not have name space, so it mostly take the first args
let function_name = sql_function.name.0[0].value.to_lowercase();
let function_name = sql_function.name.0[0].value.to_ascii_lowercase();
let args = sql_function
.args
.iter()

View File

@ -2,7 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
use crate::dataframe::values::NuExpression;
@ -39,18 +39,20 @@ impl Command for ToNu {
}
fn examples(&self) -> Vec<Example> {
let cols = vec!["index".into(), "a".into(), "b".into()];
let rec_1 = Value::test_record(Record {
cols: cols.clone(),
vals: vec![Value::test_int(0), Value::test_int(1), Value::test_int(2)],
let rec_1 = Value::test_record(record! {
"index" => Value::test_int(0),
"a" => Value::test_int(1),
"b" => Value::test_int(2),
});
let rec_2 = Value::test_record(Record {
cols: cols.clone(),
vals: vec![Value::test_int(1), Value::test_int(3), Value::test_int(4)],
let rec_2 = Value::test_record(record! {
"index" => Value::test_int(1),
"a" => Value::test_int(3),
"b" => Value::test_int(4),
});
let rec_3 = Value::test_record(Record {
cols,
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(4)],
let rec_3 = Value::test_record(record! {
"index" => Value::test_int(2),
"a" => Value::test_int(3),
"b" => Value::test_int(4),
});
vec![
@ -67,9 +69,9 @@ impl Command for ToNu {
Example {
description: "Convert a col expression into a nushell value",
example: "dfr col a | dfr into-nu",
result: Some(Value::test_record(Record {
cols: vec!["expr".into(), "value".into()],
vals: vec![Value::test_string("column"), Value::test_string("a")],
result: Some(Value::test_record(record! {
"expr" => Value::test_string("column"),
"value" => Value::test_string("a"),
})),
},
]

View File

@ -4,7 +4,7 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, Record, ShellError, Signature, SyntaxShape, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -38,20 +38,12 @@ impl Command for ExprAlias {
description: "Creates and alias expression",
example: "dfr col a | dfr as new_a | dfr into-nu",
result: {
let cols = vec!["expr".into(), "value".into()];
let expr = Value::test_string("column");
let value = Value::test_string("a");
let expr = Value::test_record(Record {
cols,
vals: vec![expr, value],
});
let cols = vec!["expr".into(), "alias".into()];
let value = Value::test_string("new_a");
let record = Value::test_record(Record {
cols,
vals: vec![expr, value],
let record = Value::test_record(record! {
"expr" => Value::test_record(record! {
"expr" => Value::test_string("column"),
"value" => Value::test_string("a"),
}),
"alias" => Value::test_string("new_a"),
});
Some(record)

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, Record, ShellError, Signature, SyntaxShape, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use polars::prelude::col;
@ -34,9 +34,9 @@ impl Command for ExprCol {
vec![Example {
description: "Creates a named column expression and converts it to a nu object",
example: "dfr col a | dfr into-nu",
result: Some(Value::test_record(Record {
cols: vec!["expr".into(), "value".into()],
vals: vec![Value::test_string("column"), Value::test_string("a")],
result: Some(Value::test_record(record! {
"expr" => Value::test_string("column"),
"value" => Value::test_string("a"),
})),
}]
}

View File

@ -124,12 +124,12 @@ impl Command for ExprDatePart {
"microsecond" => expr_dt.microsecond(),
"nanosecond" => expr_dt.nanosecond(),
_ => {
return Err(ShellError::UnsupportedInput(
format!("{} is not a valid datepart, expected one of year, month, day, hour, minute, second, millisecond, microsecond, nanosecond", part.item),
"value originates from here".to_string(),
call.head,
part.span,
));
return Err(ShellError::UnsupportedInput {
msg: format!("{} is not a valid datepart, expected one of year, month, day, hour, minute, second, millisecond, microsecond, nanosecond", part.item),
input: "value originates from here".to_string(),
msg_span: call.head,
input_span: part.span,
});
}
}.into();

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, Record, ShellError, Signature, SyntaxShape, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -33,9 +33,9 @@ impl Command for ExprLit {
vec![Example {
description: "Created a literal expression and converts it to a nu object",
example: "dfr lit 2 | dfr into-nu",
result: Some(Value::test_record(Record {
cols: vec!["expr".into(), "value".into()],
vals: vec![Value::test_string("literal"), Value::test_string("2")],
result: Some(Value::test_record(record! {
"expr" => Value::test_string("literal"),
"value" => Value::test_string("2"),
})),
}]
}

View File

@ -98,12 +98,12 @@ fn command(
let value = NuDataFrame::dataframe_into_value(res, call.head);
Ok(PipelineData::Value(value, None))
}
_ => Err(ShellError::UnsupportedInput(
"Expected the dataframe to have a column".to_string(),
"".to_string(),
call.head,
call.head,
)),
_ => Err(ShellError::UnsupportedInput {
msg: "Expected the dataframe to have a column".to_string(),
input: "".to_string(),
msg_span: call.head,
input_span: call.head,
}),
}
}

View File

@ -1,15 +1,16 @@
use nu_protocol::{FromValue, ShellError, Value};
pub fn extract_strings(value: Value) -> Result<Vec<String>, ShellError> {
let span = value.span();
match (
<String as FromValue>::from_value(&value),
<Vec<String> as FromValue>::from_value(&value),
<String as FromValue>::from_value(value.clone()),
<Vec<String> as FromValue>::from_value(value),
) {
(Ok(col), Err(_)) => Ok(vec![col]),
(Err(_), Ok(cols)) => Ok(cols),
_ => Err(ShellError::IncompatibleParametersSingle {
msg: "Expected a string or list of strings".into(),
span: value.span(),
span,
}),
}
}

View File

@ -1,23 +1,31 @@
use super::{DataFrameValue, NuDataFrame};
use std::ops::{Deref, DerefMut};
use chrono::{DateTime, FixedOffset, NaiveDateTime};
use chrono::{DateTime, Duration, FixedOffset, NaiveTime, TimeZone, Utc};
use chrono_tz::Tz;
use indexmap::map::{Entry, IndexMap};
use nu_protocol::{Record, ShellError, Span, Value};
use polars::chunked_array::builder::AnonymousOwnedListBuilder;
use polars::chunked_array::object::builder::ObjectChunkedBuilder;
use polars::chunked_array::ChunkedArray;
use polars::datatypes::AnyValue;
use polars::export::arrow::array::{
Array, BooleanArray, Float32Array, Float64Array, Int16Array, Int32Array, Int64Array, Int8Array,
UInt16Array, UInt32Array, UInt64Array, UInt8Array,
};
use polars::export::arrow::Either;
use polars::prelude::{
DataFrame, DataType, DatetimeChunked, Float64Type, Int64Type, IntoSeries,
ListBooleanChunkedBuilder, ListBuilderTrait, ListPrimitiveChunkedBuilder, ListType,
ListUtf8ChunkedBuilder, NamedFrom, NewChunkedArray, ObjectType, Series, TemporalMethods,
TimeUnit,
ArrayRef, DataFrame, DataType, DatetimeChunked, Float64Type, Int64Type, IntoSeries,
LargeBinaryArray, LargeListArray, LargeStringArray, ListBooleanChunkedBuilder,
ListBuilderTrait, ListPrimitiveChunkedBuilder, ListType, ListUtf8ChunkedBuilder, NamedFrom,
NewChunkedArray, ObjectType, Series, StructArray, TemporalMethods, TimeUnit,
};
use std::ops::{Deref, DerefMut};
const SECS_PER_DAY: i64 = 86_400;
use nu_protocol::{Record, ShellError, Span, Value};
// The values capacity is for the size of an internal vec.
use super::{DataFrameValue, NuDataFrame};
const NANOS_PER_DAY: i64 = 86_400_000_000_000;
// The values capacity is for the size of an vec.
// Since this is impossible to determine without traversing every value
// I just picked one. Since this is for converting back and forth
// between nushell tables the values shouldn't be too extremely large for
@ -199,7 +207,7 @@ fn value_to_input_type(value: &Value) -> InputType {
Value::Filesize { .. } => InputType::Filesize,
Value::List { vals, .. } => {
// We need to determined the type inside of the list.
// Since Value::List does not have any kind of internal
// Since Value::List does not have any kind of
// type information, we need to look inside the list.
// This will cause errors if lists have inconsistent types.
// Basically, if a list column needs to be converted to dataframe,
@ -805,28 +813,21 @@ fn series_to_values(
)),
Some(ca) => {
let it = ca.into_iter();
let values: Vec<Value> =
if let (Some(size), Some(from_row)) = (maybe_size, maybe_from_row) {
Either::Left(it.skip(from_row).take(size))
if let (Some(size), Some(from_row)) = (maybe_size, maybe_from_row) {
Either::Left(it.skip(from_row).take(size))
} else {
Either::Right(it)
}
.map(|ca| {
let sublist: Vec<Value> = if let Some(ref s) = ca {
series_to_values(s, None, None, Span::unknown())?
} else {
Either::Right(it)
}
.map(|ca| {
let sublist = ca
.map(|ref s| {
match series_to_values(s, None, None, Span::unknown()) {
Ok(v) => v,
Err(e) => {
eprintln!("Error list values: {e}");
vec![]
}
}
})
.unwrap_or(vec![]);
Value::list(sublist, span)
})
.collect::<Vec<Value>>();
Ok(values)
// empty item
vec![]
};
Ok(Value::list(sublist, span))
})
.collect::<Result<Vec<Value>, ShellError>>()
}
}
}
@ -849,51 +850,16 @@ fn series_to_values(
}
.map(|v| match v {
Some(a) => {
// elapsed time in day since 1970-01-01
let seconds = a as i64 * SECS_PER_DAY;
let naive_datetime = match NaiveDateTime::from_timestamp_opt(seconds, 0) {
Some(val) => val,
None => {
return Value::error(
ShellError::UnsupportedInput(
"The given local datetime representation is invalid."
.to_string(),
format!("timestamp is {a:?}"),
span,
Span::unknown(),
),
span,
)
}
};
// Zero length offset
let offset = match FixedOffset::east_opt(0) {
Some(val) => val,
None => {
return Value::error(
ShellError::UnsupportedInput(
"The given local datetime representation is invalid."
.to_string(),
format!("timestamp is {a:?}"),
span,
Span::unknown(),
),
span,
)
}
};
let datetime =
DateTime::<FixedOffset>::from_naive_utc_and_offset(naive_datetime, offset);
Value::date(datetime, span)
let nanos = nanos_per_day(a);
let datetime = datetime_from_epoch_nanos(nanos, &None, span)?;
Ok(Value::date(datetime, span))
}
None => Value::nothing(span),
None => Ok(Value::nothing(span)),
})
.collect::<Vec<Value>>();
.collect::<Result<Vec<Value>, ShellError>>()?;
Ok(values)
}
DataType::Datetime(time_unit, _) => {
DataType::Datetime(time_unit, tz) => {
let casted = series.datetime().map_err(|e| {
ShellError::GenericError(
"Error casting column to datetime".into(),
@ -912,55 +878,48 @@ fn series_to_values(
}
.map(|v| match v {
Some(a) => {
let unit_divisor = match time_unit {
TimeUnit::Nanoseconds => 1_000_000_000,
TimeUnit::Microseconds => 1_000_000,
TimeUnit::Milliseconds => 1_000,
};
// elapsed time in nano/micro/milliseconds since 1970-01-01
let seconds = a / unit_divisor;
let naive_datetime = match NaiveDateTime::from_timestamp_opt(seconds, 0) {
Some(val) => val,
None => {
return Value::error(
ShellError::UnsupportedInput(
"The given local datetime representation is invalid."
.to_string(),
format!("timestamp is {a:?}"),
span,
Span::unknown(),
),
span,
)
}
};
// Zero length offset
let offset = match FixedOffset::east_opt(0) {
Some(val) => val,
None => {
return Value::error(
ShellError::UnsupportedInput(
"The given local datetime representation is invalid."
.to_string(),
format!("timestamp is {a:?}"),
span,
Span::unknown(),
),
span,
)
}
};
let datetime =
DateTime::<FixedOffset>::from_naive_utc_and_offset(naive_datetime, offset);
Value::date(datetime, span)
let nanos = nanos_from_timeunit(a, *time_unit);
let datetime = datetime_from_epoch_nanos(nanos, tz, span)?;
Ok(Value::date(datetime, span))
}
None => Value::nothing(span),
None => Ok(Value::nothing(span)),
})
.collect::<Vec<Value>>();
.collect::<Result<Vec<Value>, ShellError>>()?;
Ok(values)
}
DataType::Struct(polar_fields) => {
let casted = series.struct_().map_err(|e| {
ShellError::GenericError(
"Error casting column to struct".into(),
"".to_string(),
None,
Some(e.to_string()),
Vec::new(),
)
})?;
let it = casted.into_iter();
let values: Result<Vec<Value>, ShellError> =
if let (Some(size), Some(from_row)) = (maybe_size, maybe_from_row) {
Either::Left(it.skip(from_row).take(size))
} else {
Either::Right(it)
}
.map(|any_values| {
let vals: Result<Vec<Value>, ShellError> = any_values
.iter()
.map(|v| any_value_to_value(v, span))
.collect();
let cols: Vec<String> = polar_fields
.iter()
.map(|field| field.name.to_string())
.collect();
let record = Record { cols, vals: vals? };
Ok(Value::record(record, span))
})
.collect();
values
}
DataType::Time => {
let casted = series.timestamp(TimeUnit::Nanoseconds).map_err(|e| {
ShellError::GenericError(
@ -996,10 +955,252 @@ fn series_to_values(
}
}
fn any_value_to_value(any_value: &AnyValue, span: Span) -> Result<Value, ShellError> {
match any_value {
AnyValue::Null => Ok(Value::nothing(span)),
AnyValue::Boolean(b) => Ok(Value::bool(*b, span)),
AnyValue::Utf8(s) => Ok(Value::string(s.to_string(), span)),
AnyValue::UInt8(i) => Ok(Value::int(*i as i64, span)),
AnyValue::UInt16(i) => Ok(Value::int(*i as i64, span)),
AnyValue::UInt32(i) => Ok(Value::int(*i as i64, span)),
AnyValue::UInt64(i) => Ok(Value::int(*i as i64, span)),
AnyValue::Int8(i) => Ok(Value::int(*i as i64, span)),
AnyValue::Int16(i) => Ok(Value::int(*i as i64, span)),
AnyValue::Int32(i) => Ok(Value::int(*i as i64, span)),
AnyValue::Int64(i) => Ok(Value::int(*i, span)),
AnyValue::Float32(f) => Ok(Value::float(*f as f64, span)),
AnyValue::Float64(f) => Ok(Value::float(*f, span)),
AnyValue::Date(d) => {
let nanos = nanos_per_day(*d);
datetime_from_epoch_nanos(nanos, &None, span)
.map(|datetime| Value::date(datetime, span))
}
AnyValue::Datetime(a, time_unit, tz) => {
let nanos = nanos_from_timeunit(*a, *time_unit);
datetime_from_epoch_nanos(nanos, tz, span).map(|datetime| Value::date(datetime, span))
}
AnyValue::Duration(a, time_unit) => {
let nanos = match time_unit {
TimeUnit::Nanoseconds => *a,
TimeUnit::Microseconds => *a * 1_000,
TimeUnit::Milliseconds => *a * 1_000_000,
};
Ok(Value::duration(nanos, span))
}
// AnyValue::Time represents the current time since midnight.
// Unfortunately, there is no timezone related information.
// Given this, calculate the current date from UTC and add the time.
AnyValue::Time(nanos) => time_from_midnight(*nanos, span),
AnyValue::List(series) => {
series_to_values(series, None, None, span).map(|values| Value::list(values, span))
}
AnyValue::Struct(idx, struct_array, s_fields) => {
let cols: Vec<String> = s_fields.iter().map(|f| f.name().to_string()).collect();
let vals: Result<Vec<Value>, ShellError> = struct_array
.values()
.iter()
.enumerate()
.map(|(pos, v)| {
let f = &s_fields[pos];
arr_to_value(&f.dtype, &**v, *idx, span)
})
.collect();
let record = Record { cols, vals: vals? };
Ok(Value::record(record, span))
}
AnyValue::StructOwned(struct_tuple) => {
let values: Result<Vec<Value>, ShellError> = struct_tuple
.0
.iter()
.map(|s| any_value_to_value(s, span))
.collect();
let fields = struct_tuple
.1
.iter()
.map(|f| f.name().to_string())
.collect();
Ok(Value::Record {
val: Record {
cols: fields,
vals: values?,
},
internal_span: span,
})
}
AnyValue::Utf8Owned(s) => Ok(Value::string(s.to_string(), span)),
AnyValue::Binary(bytes) => Ok(Value::binary(*bytes, span)),
AnyValue::BinaryOwned(bytes) => Ok(Value::binary(bytes.to_owned(), span)),
e => Err(ShellError::GenericError(
"Error creating Value".into(),
"".to_string(),
None,
Some(format!("Value not supported in nushell: {e}")),
Vec::new(),
)),
}
}
#[inline]
fn arr_to_value(
dt: &DataType,
arr: &dyn Array,
idx: usize,
span: Span,
) -> Result<Value, ShellError> {
macro_rules! downcast {
($casttype:ident) => {{
let arr = &*(arr as *const dyn Array as *const $casttype);
arr.value_unchecked(idx)
}};
}
// Not loving the unsafe here, however this largely based off the one
// example I found for converting Array values in:
// polars_core::chunked_array::ops::any_value::arr_to_any_value
unsafe {
match dt {
DataType::Boolean => Ok(Value::bool(downcast!(BooleanArray), span)),
DataType::UInt8 => Ok(Value::int(downcast!(UInt8Array) as i64, span)),
DataType::UInt16 => Ok(Value::int(downcast!(UInt16Array) as i64, span)),
DataType::UInt32 => Ok(Value::int(downcast!(UInt32Array) as i64, span)),
DataType::UInt64 => Ok(Value::int(downcast!(UInt64Array) as i64, span)),
DataType::Int8 => Ok(Value::int(downcast!(Int8Array) as i64, span)),
DataType::Int16 => Ok(Value::int(downcast!(Int16Array) as i64, span)),
DataType::Int32 => Ok(Value::int(downcast!(Int32Array) as i64, span)),
DataType::Int64 => Ok(Value::int(downcast!(Int64Array), span)),
DataType::Float32 => Ok(Value::float(downcast!(Float32Array) as f64, span)),
DataType::Float64 => Ok(Value::float(downcast!(Float64Array), span)),
// DataType::Decimal(_, _) => {}
DataType::Utf8 => Ok(Value::string(downcast!(LargeStringArray).to_string(), span)),
DataType::Binary => Ok(Value::binary(downcast!(LargeBinaryArray).to_owned(), span)),
DataType::Date => {
let date = downcast!(Int32Array);
let nanos = nanos_per_day(date);
datetime_from_epoch_nanos(nanos, &None, span)
.map(|datetime| Value::date(datetime, span))
}
DataType::Datetime(time_unit, tz) => {
let nanos = nanos_from_timeunit(downcast!(Int64Array), *time_unit);
datetime_from_epoch_nanos(nanos, tz, span)
.map(|datetime| Value::date(datetime, span))
}
// DataType::Duration(_) => {}
DataType::Time => {
let t = downcast!(Int64Array);
time_from_midnight(t, span)
}
DataType::List(dt) => {
let v: ArrayRef = downcast!(LargeListArray);
let values_result = if dt.is_primitive() {
let s = Series::from_chunks_and_dtype_unchecked("", vec![v], dt);
series_to_values(&s, None, None, span)
} else {
let s = Series::from_chunks_and_dtype_unchecked("", vec![v], &dt.to_physical())
.cast_unchecked(dt)
.map_err(|e| {
ShellError::GenericError(
"Error creating Value from polars LargeListArray".into(),
e.to_string(),
Some(span),
None,
Vec::new(),
)
})?;
series_to_values(&s, None, None, span)
};
values_result.map(|values| Value::list(values, span))
}
DataType::Null => Ok(Value::nothing(span)),
DataType::Struct(fields) => {
let arr = &*(arr as *const dyn Array as *const StructArray);
let vals: Result<Vec<Value>, ShellError> = arr
.values()
.iter()
.enumerate()
.map(|(pos, v)| {
let f = &fields[pos];
arr_to_value(&f.dtype, &**v, 0, span)
})
.collect();
let cols = fields.iter().map(|f| f.name().to_string()).collect();
Ok(Value::record(Record { cols, vals: vals? }, span))
}
DataType::Unknown => Ok(Value::nothing(span)),
_ => Err(ShellError::CantConvert {
to_type: dt.to_string(),
from_type: "polars array".to_string(),
span,
help: Some(format!(
"Could not convert polars array of type {:?} to value",
dt
)),
}),
}
}
}
fn nanos_per_day(days: i32) -> i64 {
days as i64 * NANOS_PER_DAY
}
fn nanos_from_timeunit(a: i64, time_unit: TimeUnit) -> i64 {
a * match time_unit {
TimeUnit::Microseconds => 1_000, // Convert microseconds to nanoseconds
TimeUnit::Milliseconds => 1_000_000, // Convert milliseconds to nanoseconds
TimeUnit::Nanoseconds => 1, // Already in nanoseconds
}
}
fn datetime_from_epoch_nanos(
nanos: i64,
timezone: &Option<String>,
span: Span,
) -> Result<DateTime<FixedOffset>, ShellError> {
let tz: Tz = if let Some(polars_tz) = timezone {
polars_tz.parse::<Tz>().map_err(|_| {
ShellError::GenericError(
format!("Could not parse polars timezone: {polars_tz}"),
"".to_string(),
Some(span),
None,
vec![],
)
})?
} else {
Tz::UTC
};
Ok(tz.timestamp_nanos(nanos).fixed_offset())
}
fn time_from_midnight(nanos: i64, span: Span) -> Result<Value, ShellError> {
let today = Utc::now().date_naive();
NaiveTime::from_hms_opt(0, 0, 0) // midnight
.map(|time| time + Duration::nanoseconds(nanos)) // current time
.map(|time| today.and_time(time)) // current date and time
.and_then(|datetime| {
FixedOffset::east_opt(0) // utc
.map(|offset| {
DateTime::<FixedOffset>::from_naive_utc_and_offset(datetime, offset)
})
})
.map(|datetime| Value::date(datetime, span)) // current date and time
.ok_or(ShellError::CantConvert {
to_type: "datetime".to_string(),
from_type: "polars time".to_string(),
span,
help: Some("Could not convert polars time of {nanos} to datetime".to_string()),
})
}
#[cfg(test)]
mod tests {
use super::*;
use indexmap::indexmap;
use polars::export::arrow::array::{ListArray, NullArray, PrimitiveArray};
use polars::export::arrow::buffer::Buffer;
use polars::prelude::Field;
use super::*;
#[test]
fn test_parsed_column_string_list() -> Result<(), Box<dyn std::error::Error>> {
@ -1034,4 +1235,375 @@ mod tests {
Ok(())
}
#[test]
fn test_any_value_to_value() -> Result<(), Box<dyn std::error::Error>> {
let span = Span::test_data();
assert_eq!(
any_value_to_value(&AnyValue::Null, span)?,
Value::nothing(span)
);
let test_bool = true;
assert_eq!(
any_value_to_value(&AnyValue::Boolean(test_bool), span)?,
Value::bool(test_bool, span)
);
let test_str = "foo";
assert_eq!(
any_value_to_value(&AnyValue::Utf8(test_str), span)?,
Value::string(test_str.to_string(), span)
);
assert_eq!(
any_value_to_value(&AnyValue::Utf8Owned(test_str.into()), span)?,
Value::string(test_str.to_owned(), span)
);
let tests_uint8 = 4;
assert_eq!(
any_value_to_value(&AnyValue::UInt8(tests_uint8), span)?,
Value::int(tests_uint8 as i64, span)
);
let tests_uint16 = 233;
assert_eq!(
any_value_to_value(&AnyValue::UInt16(tests_uint16), span)?,
Value::int(tests_uint16 as i64, span)
);
let tests_uint32 = 897688233;
assert_eq!(
any_value_to_value(&AnyValue::UInt32(tests_uint32), span)?,
Value::int(tests_uint32 as i64, span)
);
let tests_uint64 = 903225135897388233;
assert_eq!(
any_value_to_value(&AnyValue::UInt64(tests_uint64), span)?,
Value::int(tests_uint64 as i64, span)
);
let tests_float32 = 903225135897388233.3223353;
assert_eq!(
any_value_to_value(&AnyValue::Float32(tests_float32), span)?,
Value::float(tests_float32 as f64, span)
);
let tests_float64 = 9064251358973882322333.64233533232;
assert_eq!(
any_value_to_value(&AnyValue::Float64(tests_float64), span)?,
Value::float(tests_float64, span)
);
let test_days = 10_957;
let comparison_date = Utc
.with_ymd_and_hms(2000, 1, 1, 0, 0, 0)
.unwrap()
.fixed_offset();
assert_eq!(
any_value_to_value(&AnyValue::Date(test_days), span)?,
Value::date(comparison_date, span)
);
let test_millis = 946_684_800_000;
assert_eq!(
any_value_to_value(
&AnyValue::Datetime(test_millis, TimeUnit::Milliseconds, &None),
span
)?,
Value::date(comparison_date, span)
);
let test_duration_millis = 99_999;
let test_duration_micros = 99_999_000;
let test_duration_nanos = 99_999_000_000;
assert_eq!(
any_value_to_value(
&AnyValue::Duration(test_duration_nanos, TimeUnit::Nanoseconds),
span
)?,
Value::duration(test_duration_nanos, span)
);
assert_eq!(
any_value_to_value(
&AnyValue::Duration(test_duration_micros, TimeUnit::Microseconds),
span
)?,
Value::duration(test_duration_nanos, span)
);
assert_eq!(
any_value_to_value(
&AnyValue::Duration(test_duration_millis, TimeUnit::Milliseconds),
span
)?,
Value::duration(test_duration_nanos, span)
);
let test_binary = b"sdf2332f32q3f3afwaf3232f32";
assert_eq!(
any_value_to_value(&AnyValue::Binary(test_binary), span)?,
Value::binary(test_binary.to_vec(), span)
);
assert_eq!(
any_value_to_value(&AnyValue::BinaryOwned(test_binary.to_vec()), span)?,
Value::binary(test_binary.to_vec(), span)
);
let test_time_nanos = 54_000_000_000_000;
let test_time = DateTime::<FixedOffset>::from_naive_utc_and_offset(
Utc::now()
.date_naive()
.and_time(NaiveTime::from_hms_opt(15, 00, 00).unwrap()),
FixedOffset::east_opt(0).unwrap(),
);
assert_eq!(
any_value_to_value(&AnyValue::Time(test_time_nanos), span)?,
Value::date(test_time, span)
);
let test_list_series = Series::new("int series", &[1, 2, 3]);
let comparison_list_series = Value::list(
vec![
Value::int(1, span),
Value::int(2, span),
Value::int(3, span),
],
span,
);
assert_eq!(
any_value_to_value(&AnyValue::List(test_list_series), span)?,
comparison_list_series
);
let field_value_0 = AnyValue::Int32(1);
let field_value_1 = AnyValue::Boolean(true);
let values = vec![field_value_0, field_value_1];
let field_name_0 = "num_field";
let field_name_1 = "bool_field";
let fields = vec![
Field::new(field_name_0, DataType::Int32),
Field::new(field_name_1, DataType::Boolean),
];
let test_owned_struct = AnyValue::StructOwned(Box::new((values, fields.clone())));
let comparison_owned_record = Value::record(
Record {
cols: vec![field_name_0.to_owned(), field_name_1.to_owned()],
vals: vec![Value::int(1, span), Value::bool(true, span)],
},
span,
);
assert_eq!(
any_value_to_value(&test_owned_struct, span)?,
comparison_owned_record.clone()
);
let test_int_arr = PrimitiveArray::from([Some(1_i32)]);
let test_bool_arr = BooleanArray::from([Some(true)]);
let test_struct_arr = StructArray::new(
DataType::Struct(fields.clone()).to_arrow(),
vec![Box::new(test_int_arr), Box::new(test_bool_arr)],
None,
);
assert_eq!(
any_value_to_value(
&AnyValue::Struct(0, &test_struct_arr, fields.as_slice()),
span
)?,
comparison_owned_record
);
Ok(())
}
#[test]
fn test_arr_to_value() -> Result<(), Box<dyn std::error::Error>> {
let test_bool_arr = BooleanArray::from([Some(true)]);
assert_eq!(
arr_to_value(&DataType::Boolean, &test_bool_arr, 0, Span::test_data())?,
Value::bool(true, Span::test_data())
);
let test_uint8_arr = PrimitiveArray::from([Some(9_u8)]);
assert_eq!(
arr_to_value(&DataType::UInt8, &test_uint8_arr, 0, Span::test_data())?,
Value::int(9, Span::test_data())
);
let test_uint16_arr = PrimitiveArray::from([Some(3223_u16)]);
assert_eq!(
arr_to_value(&DataType::UInt16, &test_uint16_arr, 0, Span::test_data())?,
Value::int(3223, Span::test_data())
);
let test_uint32_arr = PrimitiveArray::from([Some(33_u32)]);
assert_eq!(
arr_to_value(&DataType::UInt32, &test_uint32_arr, 0, Span::test_data())?,
Value::int(33, Span::test_data())
);
let test_uint64_arr = PrimitiveArray::from([Some(33_3232_u64)]);
assert_eq!(
arr_to_value(&DataType::UInt64, &test_uint64_arr, 0, Span::test_data())?,
Value::int(33_3232, Span::test_data())
);
let test_int8_arr = PrimitiveArray::from([Some(9_i8)]);
assert_eq!(
arr_to_value(&DataType::Int8, &test_int8_arr, 0, Span::test_data())?,
Value::int(9, Span::test_data())
);
let test_int16_arr = PrimitiveArray::from([Some(3223_i16)]);
assert_eq!(
arr_to_value(&DataType::Int16, &test_int16_arr, 0, Span::test_data())?,
Value::int(3223, Span::test_data())
);
let test_int32_arr = PrimitiveArray::from([Some(33_i32)]);
assert_eq!(
arr_to_value(&DataType::Int32, &test_int32_arr, 0, Span::test_data())?,
Value::int(33, Span::test_data())
);
let test_int64_arr = PrimitiveArray::from([Some(33_3232_i64)]);
assert_eq!(
arr_to_value(&DataType::Int64, &test_int64_arr, 0, Span::test_data())?,
Value::int(33_3232, Span::test_data())
);
let test_float32_arr = PrimitiveArray::from([Some(33.32_f32)]);
assert_eq!(
arr_to_value(&DataType::Float32, &test_float32_arr, 0, Span::test_data())?,
Value::float(33.32_f32 as f64, Span::test_data())
);
let test_float64_arr = PrimitiveArray::from([Some(33_3232.999_f64)]);
assert_eq!(
arr_to_value(&DataType::Float64, &test_float64_arr, 0, Span::test_data())?,
Value::float(33_3232.999, Span::test_data())
);
let test_str = "hello world";
let test_str_arr = LargeStringArray::from(vec![Some(test_str.to_string())]);
assert_eq!(
arr_to_value(&DataType::Utf8, &test_str_arr, 0, Span::test_data())?,
Value::string(test_str.to_string(), Span::test_data())
);
let test_bin = b"asdlfkjadsf";
let test_bin_arr = LargeBinaryArray::from(vec![Some(test_bin.to_vec())]);
assert_eq!(
arr_to_value(&DataType::Binary, &test_bin_arr, 0, Span::test_data())?,
Value::binary(test_bin.to_vec(), Span::test_data())
);
let test_days = 10_957_i32;
let comparison_date = Utc
.with_ymd_and_hms(2000, 1, 1, 0, 0, 0)
.unwrap()
.fixed_offset();
let test_date_arr = PrimitiveArray::from([Some(test_days)]);
assert_eq!(
arr_to_value(&DataType::Date, &test_date_arr, 0, Span::test_data())?,
Value::date(comparison_date, Span::test_data())
);
let test_dt_nanos = 1_357_488_900_000_000_000_i64;
let test_dt_arr = PrimitiveArray::from([Some(test_dt_nanos)]);
let test_dt = Utc.timestamp_nanos(test_dt_nanos).fixed_offset();
assert_eq!(
arr_to_value(
&DataType::Datetime(TimeUnit::Nanoseconds, Some("UTC".to_owned())),
&test_dt_arr,
0,
Span::test_data()
)?,
Value::date(test_dt, Span::test_data())
);
let test_time_nanos = 54_000_000_000_000_i64;
let test_dt_arr = PrimitiveArray::from([Some(test_time_nanos)]);
let test_time = DateTime::<FixedOffset>::from_naive_utc_and_offset(
Utc::now()
.date_naive()
.and_time(NaiveTime::from_hms_opt(15, 00, 00).unwrap()),
FixedOffset::east_opt(0).unwrap(),
);
assert_eq!(
arr_to_value(&DataType::Time, &test_dt_arr, 0, Span::test_data())?,
Value::date(test_time, Span::test_data())
);
let values = Buffer::from(vec![1, 2, 3]);
let values = PrimitiveArray::<i64>::new(DataType::Int64.to_arrow(), values, None);
let data_type = ListArray::<i64>::default_datatype(DataType::Int64.to_arrow());
let array = ListArray::<i64>::new(
data_type,
vec![0, 3].try_into().unwrap(),
Box::new(values),
None,
);
let comparison_list_series = Value::list(
vec![
Value::int(1, Span::test_data()),
Value::int(2, Span::test_data()),
Value::int(3, Span::test_data()),
],
Span::test_data(),
);
assert_eq!(
arr_to_value(
&DataType::List(Box::new(DataType::Int64)),
&array,
0,
Span::test_data()
)?,
comparison_list_series
);
let field_name_0 = "num_field";
let field_name_1 = "bool_field";
let fields = vec![
Field::new(field_name_0, DataType::Int32),
Field::new(field_name_1, DataType::Boolean),
];
let test_int_arr = PrimitiveArray::from([Some(1_i32)]);
let test_struct_arr = StructArray::new(
DataType::Struct(fields.clone()).to_arrow(),
vec![Box::new(test_int_arr), Box::new(test_bool_arr)],
None,
);
let comparison_owned_record = Value::record(
Record {
cols: vec![field_name_0.to_owned(), field_name_1.to_owned()],
vals: vec![
Value::int(1, Span::test_data()),
Value::bool(true, Span::test_data()),
],
},
Span::test_data(),
);
assert_eq!(
arr_to_value(
&DataType::Struct(fields),
&test_struct_arr,
0,
Span::test_data(),
)?,
comparison_owned_record
);
assert_eq!(
arr_to_value(
&DataType::Null,
&NullArray::new(DataType::Null.to_arrow(), 0),
0,
Span::test_data()
)?,
Value::nothing(Span::test_data())
);
Ok(())
}
}

View File

@ -427,11 +427,11 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Result<Value, ShellError> {
}
// the parameter polars_plan::dsl::selector::Selector is not publicly exposed.
// I am not sure what we can meaningfully do with this at this time.
Expr::Selector(_) => Err(ShellError::UnsupportedInput(
"Expressions of type Selector to Nu Values is not yet supported".to_string(),
format!("Expression is {expr:?}"),
span,
Span::unknown(),
)),
Expr::Selector(_) => Err(ShellError::UnsupportedInput {
msg: "Expressions of type Selector to Nu Values is not yet supported".to_string(),
input: format!("Expression is {expr:?}"),
msg_span: span,
input_span: Span::unknown(),
}),
}
}

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT"
name = "nu-cmd-extra"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
version = "0.86.0"
version = "0.87.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,11 +13,11 @@ version = "0.86.0"
bench = false
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.86.0" }
nu-parser = { path = "../nu-parser", version = "0.86.0" }
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.86.0" }
nu-utils = { path = "../nu-utils", version = "0.86.0" }
nu-engine = { path = "../nu-engine", version = "0.87.1" }
nu-parser = { path = "../nu-parser", version = "0.87.1" }
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.87.1" }
nu-utils = { path = "../nu-utils", version = "0.87.1" }
# Potential dependencies for extras
heck = "0.4.1"
@ -27,8 +27,8 @@ nu-ansi-term = "0.49.0"
fancy-regex = "0.11.0"
rust-embed = "8.0.0"
serde = "1.0.164"
nu-pretty-hex = { version = "0.86.0", path = "../nu-pretty-hex" }
nu-json = { version = "0.86.0", path = "../nu-json" }
nu-pretty-hex = { version = "0.87.1", path = "../nu-pretty-hex" }
nu-json = { version = "0.87.1", path = "../nu-json" }
serde_urlencoded = "0.7.1"
htmlescape = "0.3.1"
@ -37,6 +37,6 @@ extra = ["default"]
default = []
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.86.0" }
nu-command = { path = "../nu-command", version = "0.86.0" }
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.87.1" }
nu-command = { path = "../nu-command", version = "0.87.1" }
nu-test-support = { path = "../nu-test-support", version = "0.87.1" }

View File

@ -60,12 +60,12 @@ impl Command for BitsNot {
let bytes_len = get_number_bytes(number_bytes.as_ref());
if let NumberBytes::Invalid = bytes_len {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span,
));
return Err(ShellError::UnsupportedInput {
msg: "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
input: "value originates from here".to_string(),
msg_span: head,
input_span: val.span,
});
}
}

View File

@ -63,12 +63,12 @@ impl Command for BitsRol {
let bytes_len = get_number_bytes(number_bytes.as_ref());
if let NumberBytes::Invalid = bytes_len {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span,
));
return Err(ShellError::UnsupportedInput {
msg: "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
input: "value originates from here".to_string(),
msg_span: head,
input_span: val.span,
});
}
}
// This doesn't match explicit nulls

View File

@ -63,12 +63,12 @@ impl Command for BitsRor {
let bytes_len = get_number_bytes(number_bytes.as_ref());
if let NumberBytes::Invalid = bytes_len {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span,
));
return Err(ShellError::UnsupportedInput {
msg: "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
input: "value originates from here".to_string(),
msg_span: head,
input_span: val.span,
});
}
}
// This doesn't match explicit nulls

View File

@ -63,12 +63,12 @@ impl Command for BitsShl {
let bytes_len = get_number_bytes(number_bytes.as_ref());
if let NumberBytes::Invalid = bytes_len {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span,
));
return Err(ShellError::UnsupportedInput {
msg: "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
input: "value originates from here".to_string(),
msg_span: head,
input_span: val.span,
});
}
}
// This doesn't match explicit nulls

View File

@ -63,12 +63,12 @@ impl Command for BitsShr {
let bytes_len = get_number_bytes(number_bytes.as_ref());
if let NumberBytes::Invalid = bytes_len {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span,
));
return Err(ShellError::UnsupportedInput {
msg: "Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
input: "value originates from here".to_string(),
msg_span: head,
input_span: val.span,
});
}
}
// This doesn't match explicit nulls

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
engine::{Command, EngineState, Stack},
record, Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
};
#[derive(Clone)]
@ -32,27 +32,15 @@ impl Command for Fmt {
vec![Example {
description: "Get a record containing multiple formats for the number 42",
example: "42 | fmt",
result: Some(Value::test_record(Record {
cols: vec![
"binary".into(),
"debug".into(),
"display".into(),
"lowerexp".into(),
"lowerhex".into(),
"octal".into(),
"upperexp".into(),
"upperhex".into(),
],
vals: vec![
Value::test_string("0b101010"),
Value::test_string("42"),
Value::test_string("42"),
Value::test_string("4.2e1"),
Value::test_string("0x2a"),
Value::test_string("0o52"),
Value::test_string("4.2E1"),
Value::test_string("0x2A"),
],
result: Some(Value::test_record(record! {
"binary" => Value::test_string("0b101010"),
"debug" => Value::test_string("42"),
"display" => Value::test_string("42"),
"lowerexp" => Value::test_string("4.2e1"),
"lowerhex" => Value::test_string("0x2a"),
"octal" => Value::test_string("0o52"),
"upperexp" => Value::test_string("4.2E1"),
"upperhex" => Value::test_string("0x2A"),
})),
}]
}

View File

@ -81,7 +81,7 @@ impl Command for EachWhile {
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
let block = engine_state.get_block(capture_block.block_id).clone();
let mut stack = stack.captures_to_stack(&capture_block.captures);
let mut stack = stack.captures_to_stack(capture_block.captures);
let orig_env_vars = stack.env_vars.clone();
let orig_env_hidden = stack.env_hidden.clone();
let span = call.head;

View File

@ -2,8 +2,8 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
SyntaxShape, Type, Value,
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
Type, Value,
};
use super::{vertical_rotate_value, VerticalDirection};
@ -33,27 +33,23 @@ impl Command for RollDown {
}
fn examples(&self) -> Vec<Example> {
let columns = vec!["a".to_string(), "b".to_string()];
vec![Example {
description: "Rolls rows down of a table",
example: "[[a b]; [1 2] [3 4] [5 6]] | roll down",
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: columns.clone(),
vals: vec![Value::test_int(5), Value::test_int(6)],
}),
Value::test_record(Record {
cols: columns.clone(),
vals: vec![Value::test_int(1), Value::test_int(2)],
}),
Value::test_record(Record {
cols: columns,
vals: vec![Value::test_int(3), Value::test_int(4)],
}),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_record(record! {
"a" => Value::test_int(5),
"b" => Value::test_int(6),
}),
Value::test_record(record! {
"a" => Value::test_int(1),
"b" => Value::test_int(2),
}),
Value::test_record(record! {
"a" => Value::test_int(3),
"b" => Value::test_int(4),
}),
])),
}]
}

View File

@ -2,8 +2,8 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
SyntaxShape, Type, Value,
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
Type, Value,
};
use super::{horizontal_rotate_value, HorizontalDirection};
@ -45,50 +45,47 @@ impl Command for RollLeft {
}
fn examples(&self) -> Vec<Example> {
let columns = vec!["a".to_string(), "b".to_string(), "c".to_string()];
let rotated_columns = vec!["b".to_string(), "c".to_string(), "a".to_string()];
vec![
Example {
description: "Rolls columns of a record to the left",
example: "{a:1 b:2 c:3} | roll left",
result: Some(Value::test_record(Record {
cols: rotated_columns.clone(),
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(1)],
result: Some(Value::test_record(record! {
"b" => Value::test_int(2),
"c" => Value::test_int(3),
"a" => Value::test_int(1),
})),
},
Example {
description: "Rolls columns of a table to the left",
example: "[[a b c]; [1 2 3] [4 5 6]] | roll left",
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: rotated_columns.clone(),
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(1)],
}),
Value::test_record(Record {
cols: rotated_columns,
vals: vec![Value::test_int(5), Value::test_int(6), Value::test_int(4)],
}),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_record(record! {
"b" => Value::test_int(2),
"c" => Value::test_int(3),
"a" => Value::test_int(1),
}),
Value::test_record(record! {
"b" => Value::test_int(5),
"c" => Value::test_int(6),
"a" => Value::test_int(4),
}),
])),
},
Example {
description: "Rolls columns to the left without changing column names",
example: "[[a b c]; [1 2 3] [4 5 6]] | roll left --cells-only",
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: columns.clone(),
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(1)],
}),
Value::test_record(Record {
cols: columns,
vals: vec![Value::test_int(5), Value::test_int(6), Value::test_int(4)],
}),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_record(record! {
"a" => Value::test_int(2),
"b" => Value::test_int(3),
"c" => Value::test_int(1),
}),
Value::test_record(record! {
"a" => Value::test_int(5),
"b" => Value::test_int(6),
"c" => Value::test_int(4),
}),
])),
},
]
}

View File

@ -2,8 +2,8 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
SyntaxShape, Type, Value,
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
Type, Value,
};
use super::{horizontal_rotate_value, HorizontalDirection};
@ -45,50 +45,47 @@ impl Command for RollRight {
}
fn examples(&self) -> Vec<Example> {
let columns = vec!["a".to_string(), "b".to_string(), "c".to_string()];
let rotated_columns = vec!["c".to_string(), "a".to_string(), "b".to_string()];
vec![
Example {
description: "Rolls columns of a record to the right",
example: "{a:1 b:2 c:3} | roll right",
result: Some(Value::test_record(Record {
cols: rotated_columns.clone(),
vals: vec![Value::test_int(3), Value::test_int(1), Value::test_int(2)],
result: Some(Value::test_record(record! {
"c" => Value::test_int(3),
"a" => Value::test_int(1),
"b" => Value::test_int(2),
})),
},
Example {
description: "Rolls columns to the right",
example: "[[a b c]; [1 2 3] [4 5 6]] | roll right",
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: rotated_columns.clone(),
vals: vec![Value::test_int(3), Value::test_int(1), Value::test_int(2)],
}),
Value::test_record(Record {
cols: rotated_columns,
vals: vec![Value::test_int(6), Value::test_int(4), Value::test_int(5)],
}),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_record(record! {
"c" => Value::test_int(3),
"a" => Value::test_int(1),
"b" => Value::test_int(2),
}),
Value::test_record(record! {
"c" => Value::test_int(6),
"a" => Value::test_int(4),
"b" => Value::test_int(5),
}),
])),
},
Example {
description: "Rolls columns to the right with fixed headers",
example: "[[a b c]; [1 2 3] [4 5 6]] | roll right --cells-only",
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: columns.clone(),
vals: vec![Value::test_int(3), Value::test_int(1), Value::test_int(2)],
}),
Value::test_record(Record {
cols: columns,
vals: vec![Value::test_int(6), Value::test_int(4), Value::test_int(5)],
}),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_record(record! {
"a" => Value::test_int(3),
"b" => Value::test_int(1),
"c" => Value::test_int(2),
}),
Value::test_record(record! {
"a" => Value::test_int(6),
"b" => Value::test_int(4),
"c" => Value::test_int(5),
}),
])),
},
]
}

View File

@ -2,8 +2,8 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
SyntaxShape, Type, Value,
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
Type, Value,
};
use super::{vertical_rotate_value, VerticalDirection};
@ -33,27 +33,23 @@ impl Command for RollUp {
}
fn examples(&self) -> Vec<Example> {
let columns = vec!["a".to_string(), "b".to_string()];
vec![Example {
description: "Rolls rows up",
example: "[[a b]; [1 2] [3 4] [5 6]] | roll up",
result: Some(Value::list(
vec![
Value::test_record(Record {
cols: columns.clone(),
vals: vec![Value::test_int(3), Value::test_int(4)],
}),
Value::test_record(Record {
cols: columns.clone(),
vals: vec![Value::test_int(5), Value::test_int(6)],
}),
Value::test_record(Record {
cols: columns,
vals: vec![Value::test_int(1), Value::test_int(2)],
}),
],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_record(record! {
"a" => Value::test_int(3),
"b" => Value::test_int(4),
}),
Value::test_record(record! {
"a" => Value::test_int(5),
"b" => Value::test_int(6),
}),
Value::test_record(record! {
"a" => Value::test_int(1),
"b" => Value::test_int(2),
}),
])),
}]
}

View File

@ -1,9 +1,10 @@
use nu_engine::CallExt;
use nu_protocol::IntoPipelineData;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
SyntaxShape, Type, Value,
record, Category, Example, PipelineData, Record, ShellError, Signature, SyntaxShape, Type,
Value,
};
#[derive(Clone)]
@ -38,142 +39,104 @@ impl Command for Rotate {
Example {
description: "Rotate a record clockwise, producing a table (like `transpose` but with column order reversed)",
example: "{a:1, b:2} | rotate",
result: Some(Value::list(vec![
Value::test_record(Record {
cols: vec!["column0".to_string(), "column1".to_string()],
vals: vec![Value::test_int(1), Value::test_string("a")],
result: Some(Value::test_list(vec![
Value::test_record(record! {
"column0" => Value::test_int(1),
"column1" => Value::test_string("a"),
}),
Value::test_record(Record {
cols: vec!["column0".to_string(), "column1".to_string()],
vals: vec![Value::test_int(2), Value::test_string("b")],
Value::test_record(record! {
"column0" => Value::test_int(2),
"column1" => Value::test_string("b"),
}),
],
Span::test_data(),
)),
},
Example {
description: "Rotate 2x3 table clockwise",
example: "[[a b]; [1 2] [3 4] [5 6]] | rotate",
result: Some(Value::list(
result: Some(Value::test_list(
vec![
Value::test_record(Record {
cols: vec![
"column0".to_string(),
"column1".to_string(),
"column2".to_string(),
"column3".to_string(),
],
vals: vec![
Value::test_int(5),
Value::test_int(3),
Value::test_int(1),
Value::test_string("a"),
],
Value::test_record(record! {
"column0" => Value::test_int(5),
"column1" => Value::test_int(3),
"column2" => Value::test_int(1),
"column3" => Value::test_string("a"),
}),
Value::test_record(Record {
cols: vec![
"column0".to_string(),
"column1".to_string(),
"column2".to_string(),
"column3".to_string(),
],
vals: vec![
Value::test_int(6),
Value::test_int(4),
Value::test_int(2),
Value::test_string("b"),
],
Value::test_record(record! {
"column0" => Value::test_int(6),
"column1" => Value::test_int(4),
"column2" => Value::test_int(2),
"column3" => Value::test_string("b"),
}),
],
Span::test_data(),
)),
},
Example {
description: "Rotate table clockwise and change columns names",
example: "[[a b]; [1 2]] | rotate col_a col_b",
result: Some(Value::list(
result: Some(Value::test_list(
vec![
Value::test_record(Record {
cols: vec!["col_a".to_string(), "col_b".to_string()],
vals: vec![Value::test_int(1), Value::test_string("a")],
Value::test_record(record! {
"col_a" => Value::test_int(1),
"col_b" => Value::test_string("a"),
}),
Value::test_record(Record {
cols: vec!["col_a".to_string(), "col_b".to_string()],
vals: vec![Value::test_int(2), Value::test_string("b")],
Value::test_record(record! {
"col_a" => Value::test_int(2),
"col_b" => Value::test_string("b"),
}),
],
Span::test_data(),
)),
},
Example {
description: "Rotate table counter clockwise",
example: "[[a b]; [1 2]] | rotate --ccw",
result: Some(Value::list(
result: Some(Value::test_list(
vec![
Value::test_record(Record {
cols: vec!["column0".to_string(), "column1".to_string()],
vals: vec![Value::test_string("b"), Value::test_int(2)],
Value::test_record(record! {
"column0" => Value::test_string("b"),
"column1" => Value::test_int(2),
}),
Value::test_record(Record {
cols: vec!["column0".to_string(), "column1".to_string()],
vals: vec![Value::test_string("a"), Value::test_int(1)],
Value::test_record(record! {
"column0" => Value::test_string("a"),
"column1" => Value::test_int(1),
}),
],
Span::test_data(),
)),
},
Example {
description: "Rotate table counter-clockwise",
example: "[[a b]; [1 2] [3 4] [5 6]] | rotate --ccw",
result: Some(Value::list(
result: Some(Value::test_list(
vec![
Value::test_record(Record {
cols: vec![
"column0".to_string(),
"column1".to_string(),
"column2".to_string(),
"column3".to_string(),
],
vals: vec![
Value::test_string("b"),
Value::test_int(2),
Value::test_int(4),
Value::test_int(6),
],
Value::test_record(record! {
"column0" => Value::test_string("b"),
"column1" => Value::test_int(2),
"column2" => Value::test_int(4),
"column3" => Value::test_int(6),
}),
Value::test_record(Record {
cols: vec![
"column0".to_string(),
"column1".to_string(),
"column2".to_string(),
"column3".to_string(),
],
vals: vec![
Value::test_string("a"),
Value::test_int(1),
Value::test_int(3),
Value::test_int(5),
],
Value::test_record(record! {
"column0" => Value::test_string("a"),
"column1" => Value::test_int(1),
"column2" => Value::test_int(3),
"column3" => Value::test_int(5),
}),
],
Span::test_data(),
)),
},
Example {
description: "Rotate table counter-clockwise and change columns names",
example: "[[a b]; [1 2]] | rotate --ccw col_a col_b",
result: Some(Value::list(
result: Some(Value::test_list(
vec![
Value::test_record(Record {
cols: vec!["col_a".to_string(), "col_b".to_string()],
vals: vec![Value::test_string("b"), Value::test_int(2)],
Value::test_record(record! {
"col_a" => Value::test_string("b"),
"col_b" => Value::test_int(2),
}),
Value::test_record(Record {
cols: vec!["col_a".to_string(), "col_b".to_string()],
vals: vec![Value::test_string("a"), Value::test_int(1)],
Value::test_record(record! {
"col_a" => Value::test_string("a"),
"col_b" => Value::test_int(1),
}),
],
Span::test_data(),
)),
},
]
@ -237,13 +200,12 @@ pub fn rotate(
}
}
} else {
return Err(ShellError::UnsupportedInput(
"list input is empty".to_string(),
"value originates from here".into(),
call.head,
// TODO: Maybe make all Pipelines have spans, so that this doesn't need to be unwrapped.
span.unwrap_or(call.head),
));
return Err(ShellError::UnsupportedInput {
msg: "list input is empty".to_string(),
input: "value originates from here".into(),
msg_span: call.head,
input_span: span.unwrap_or(call.head),
});
}
let total_columns = &old_column_names.len();

View File

@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::{Block, Call};
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
PipelineIterator, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
PipelineIterator, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
use std::collections::HashSet;
use std::iter::FromIterator;
@ -51,29 +51,15 @@ impl Command for UpdateCells {
$value
}
}"#,
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec![
"2021-04-16".into(),
"2021-06-10".into(),
"2021-09-18".into(),
"2021-10-15".into(),
"2021-11-16".into(),
"2021-11-17".into(),
"2021-11-18".into(),
],
vals: vec![
Value::test_int(37),
Value::test_string(""),
Value::test_string(""),
Value::test_string(""),
Value::test_int(37),
Value::test_string(""),
Value::test_string(""),
],
})],
Span::test_data(),
)),
result: Some(Value::test_list(vec![Value::test_record(record! {
"2021-04-16" => Value::test_int(37),
"2021-06-10" => Value::test_string(""),
"2021-09-18" => Value::test_string(""),
"2021-10-15" => Value::test_string(""),
"2021-11-16" => Value::test_int(37),
"2021-11-17" => Value::test_string(""),
"2021-11-18" => Value::test_string(""),
})])),
},
Example {
description: "Update the zero value cells to empty strings in 2 last columns.",
@ -87,29 +73,15 @@ impl Command for UpdateCells {
$value
}
}"#,
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec![
"2021-04-16".into(),
"2021-06-10".into(),
"2021-09-18".into(),
"2021-10-15".into(),
"2021-11-16".into(),
"2021-11-17".into(),
"2021-11-18".into(),
],
vals: vec![
Value::test_int(37),
Value::test_int(0),
Value::test_int(0),
Value::test_int(0),
Value::test_int(37),
Value::test_string(""),
Value::test_string(""),
],
})],
Span::test_data(),
)),
result: Some(Value::test_list(vec![Value::test_record(record! {
"2021-04-16" => Value::test_int(37),
"2021-06-10" => Value::test_int(0),
"2021-09-18" => Value::test_int(0),
"2021-10-15" => Value::test_int(0),
"2021-11-16" => Value::test_int(37),
"2021-11-17" => Value::test_string(""),
"2021-11-18" => Value::test_string(""),
})])),
},
]
}
@ -124,7 +96,7 @@ impl Command for UpdateCells {
// the block to run on each cell
let engine_state = engine_state.clone();
let block: Closure = call.req(&engine_state, stack, 0)?;
let mut stack = stack.captures_to_stack(&block.captures);
let mut stack = stack.captures_to_stack(block.captures);
let orig_env_vars = stack.env_vars.clone();
let orig_env_hidden = stack.env_hidden.clone();
@ -186,7 +158,7 @@ impl Iterator for UpdateCellIterator {
match self.input.next() {
Some(val) => {
if let Some(ref cols) = self.columns {
if !val.columns().iter().any(|c| cols.contains(c)) {
if !val.columns().any(|c| cols.contains(c)) {
return Some(val);
}
}

View File

@ -1,7 +1,7 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, Record, ShellError, Signature, Span, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
};
#[derive(Clone)]
@ -37,19 +37,11 @@ impl Command for FromUrl {
vec![Example {
example: "'bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter' | from url",
description: "Convert url encoded string into a record",
result: Some(Value::test_record(Record {
cols: vec![
"bread".to_string(),
"cheese".to_string(),
"meat".to_string(),
"fat".to_string(),
],
vals: vec![
Value::test_string("baguette"),
Value::test_string("comté"),
Value::test_string("ham"),
Value::test_string("butter"),
],
result: Some(Value::test_record(record! {
"bread" => Value::test_string("baguette"),
"cheese" => Value::test_string("comté"),
"meat" => Value::test_string("ham"),
"fat" => Value::test_string("butter"),
})),
}]
}
@ -69,12 +61,12 @@ fn from_url(input: PipelineData, head: Span) -> Result<PipelineData, ShellError>
Ok(PipelineData::Value(Value::record(record, head), metadata))
}
_ => Err(ShellError::UnsupportedInput(
"String not compatible with URL encoding".to_string(),
"value originates from here".into(),
head,
span,
)),
_ => Err(ShellError::UnsupportedInput {
msg: "String not compatible with URL encoding".to_string(),
input: "value originates from here".into(),
msg_span: head,
input_span: span,
}),
}
}

View File

@ -7,6 +7,7 @@ use nu_protocol::{
record, Category, Config, DataSource, Example, IntoPipelineData, PipelineData,
PipelineMetadata, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
};
use nu_utils::IgnoreCaseExt;
use rust_embed::RustEmbed;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
@ -180,7 +181,7 @@ fn get_theme_from_asset_file(
let th = asset
.themes
.into_iter()
.find(|n| n.name.to_lowercase() == theme_name.to_lowercase()) // case insensitive search
.find(|n| n.name.eq_ignore_case(theme_name)) // case insensitive search
.unwrap_or_default();
Ok(convert_html_theme_to_hash_map(is_dark, &th))
@ -289,11 +290,9 @@ fn to_html(
})
.collect();
return Ok(
Value::list(result, head).into_pipeline_data_with_metadata(Box::new(
PipelineMetadata {
data_source: DataSource::HtmlThemes,
},
)),
Value::list(result, head).into_pipeline_data_with_metadata(PipelineMetadata {
data_source: DataSource::HtmlThemes,
}),
);
} else {
let theme_span = match &theme {
@ -404,15 +403,15 @@ fn html_table(table: Vec<Value>, headers: Vec<String>, config: &Config) -> Strin
for row in table {
let span = row.span();
if let Value::Record { .. } = row {
if let Value::Record { val: row, .. } = row {
output_string.push_str("<tr>");
for header in &headers {
let data = row.get_data_by_key(header);
let data = row
.get(header)
.cloned()
.unwrap_or_else(|| Value::nothing(span));
output_string.push_str("<td>");
output_string.push_str(&html_value(
data.unwrap_or_else(|| Value::nothing(span)),
config,
));
output_string.push_str(&html_value(data, config));
output_string.push_str("</td>");
}
output_string.push_str("</tr>");

View File

@ -60,7 +60,7 @@ impl Command for SubCommand {
},
Example {
description: "Get the arccosine of -1 in degrees",
example: "-1 | math arccos -d",
example: "-1 | math arccos --degrees",
result: Some(Value::test_float(180.0)),
},
]
@ -84,12 +84,13 @@ fn operate(value: Value, head: Span, use_degrees: bool) -> Value {
Value::float(val, span)
} else {
Value::error(
ShellError::UnsupportedInput(
"'arccos' undefined for values outside the closed interval [-1, 1].".into(),
"value originates from here".into(),
head,
span,
),
ShellError::UnsupportedInput {
msg: "'arccos' undefined for values outside the closed interval [-1, 1]."
.into(),
input: "value originates from here".into(),
msg_span: head,
input_span: span,
},
span,
)
}

View File

@ -74,12 +74,12 @@ fn operate(value: Value, head: Span) -> Value {
Value::float(val, span)
} else {
Value::error(
ShellError::UnsupportedInput(
"'arccosh' undefined for values below 1.".into(),
"value originates from here".into(),
head,
span,
),
ShellError::UnsupportedInput {
msg: "'arccosh' undefined for values below 1.".into(),
input: "value originates from here".into(),
msg_span: head,
input_span: span,
},
span,
)
}

View File

@ -61,7 +61,7 @@ impl Command for SubCommand {
},
Example {
description: "Get the arcsine of 1 in degrees",
example: "1 | math arcsin -d",
example: "1 | math arcsin --degrees",
result: Some(Value::test_float(90.0)),
},
]
@ -85,12 +85,13 @@ fn operate(value: Value, head: Span, use_degrees: bool) -> Value {
Value::float(val, span)
} else {
Value::error(
ShellError::UnsupportedInput(
"'arcsin' undefined for values outside the closed interval [-1, 1].".into(),
"value originates from here".into(),
head,
span,
),
ShellError::UnsupportedInput {
msg: "'arcsin' undefined for values outside the closed interval [-1, 1]."
.into(),
input: "value originates from here".into(),
msg_span: head,
input_span: span,
},
span,
)
}

View File

@ -61,7 +61,7 @@ impl Command for SubCommand {
},
Example {
description: "Get the arctangent of -1 in degrees",
example: "-1 | math arctan -d",
example: "-1 | math arctan --degrees",
result: Some(Value::test_float(-45.0)),
},
]

View File

@ -74,12 +74,13 @@ fn operate(value: Value, head: Span) -> Value {
Value::float(val, span)
} else {
Value::error(
ShellError::UnsupportedInput(
"'arctanh' undefined for values outside the open interval (-1, 1).".into(),
"value originates from here".into(),
head,
span,
),
ShellError::UnsupportedInput {
msg: "'arctanh' undefined for values outside the open interval (-1, 1)."
.into(),
input: "value originates from here".into(),
msg_span: head,
input_span: span,
},
head,
)
}

View File

@ -59,7 +59,7 @@ impl Command for SubCommand {
},
Example {
description: "Apply the cosine to a list of angles in degrees",
example: "[0 90 180 270 360] | math cos -d",
example: "[0 90 180 270 360] | math cos --degrees",
result: Some(Value::list(
vec![
Value::test_float(1f64),

View File

@ -74,12 +74,12 @@ fn operate(value: Value, head: Span) -> Value {
Value::float(val, span)
} else {
Value::error(
ShellError::UnsupportedInput(
"'ln' undefined for values outside the open interval (0, Inf).".into(),
"value originates from here".into(),
head,
span,
),
ShellError::UnsupportedInput {
msg: "'ln' undefined for values outside the open interval (0, Inf).".into(),
input: "value originates from here".into(),
msg_span: head,
input_span: span,
},
span,
)
}

View File

@ -59,7 +59,7 @@ impl Command for SubCommand {
},
Example {
description: "Apply the tangent to a list of angles in degrees",
example: "[-45 0 45] | math tan -d",
example: "[-45 0 45] | math tan --degrees",
result: Some(Value::list(
vec![
Value::test_float(-1f64),

View File

@ -62,7 +62,7 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
filters::Rotate
);
bind_command!(platform::ansi::Gradient, platform::ansi::Link);
bind_command!(platform::ansi::Gradient);
bind_command!(
strings::format::Format,

View File

@ -1,5 +1,3 @@
mod gradient;
mod link;
pub(crate) use gradient::SubCommand as Gradient;
pub(crate) use link::SubCommand as Link;

View File

@ -108,26 +108,14 @@ fn action(
Value::Binary { val, .. } => match hex_config.action_type {
ActionType::Encode => Value::string(hex_encode(val.as_ref()), command_span),
ActionType::Decode => Value::error(
ShellError::UnsupportedInput(
"Binary data can only be encoded".to_string(),
"value originates from here".into(),
command_span,
// This line requires the Value::Error {} match above.
input.span(),
),
ShellError::UnsupportedInput { msg: "Binary data can only be encoded".to_string(), input: "value originates from here".into(), msg_span: command_span, input_span: input.span() },
command_span,
),
},
Value::String { val, .. } => {
match hex_config.action_type {
ActionType::Encode => Value::error(
ShellError::UnsupportedInput(
"String value can only be decoded".to_string(),
"value originates from here".into(),
command_span,
// This line requires the Value::Error {} match above.
input.span(),
),
ShellError::UnsupportedInput { msg: "String value can only be decoded".to_string(), input: "value originates from here".into(), msg_span: command_span, input_span: input.span() },
command_span,
),

View File

@ -2,7 +2,7 @@ use heck::ToLowerCamelCase;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use super::operate;
@ -79,13 +79,10 @@ impl Command for SubCommand {
Example {
description: "convert a column from a table to camelCase",
example: r#"[[lang, gems]; [nu_test, 100]] | str camel-case lang"#,
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["lang".to_string(), "gems".to_string()],
vals: vec![Value::test_string("nuTest"), Value::test_int(100)],
})],
Span::test_data(),
)),
result: Some(Value::test_list(vec![Value::test_record(record! {
"lang" => Value::test_string("nuTest"),
"gems" => Value::test_int(100),
})])),
},
]
}

View File

@ -2,7 +2,7 @@ use heck::ToKebabCase;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use super::operate;
@ -79,13 +79,10 @@ impl Command for SubCommand {
Example {
description: "convert a column from a table to kebab-case",
example: r#"[[lang, gems]; [nuTest, 100]] | str kebab-case lang"#,
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["lang".to_string(), "gems".to_string()],
vals: vec![Value::test_string("nu-test"), Value::test_int(100)],
})],
Span::test_data(),
)),
result: Some(Value::test_list(vec![Value::test_record(record! {
"lang" => Value::test_string("nu-test"),
"gems" => Value::test_int(100),
})])),
},
]
}

View File

@ -2,7 +2,7 @@ use heck::ToUpperCamelCase;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use super::operate;
@ -79,13 +79,10 @@ impl Command for SubCommand {
Example {
description: "convert a column from a table to PascalCase",
example: r#"[[lang, gems]; [nu_test, 100]] | str pascal-case lang"#,
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["lang".to_string(), "gems".to_string()],
vals: vec![Value::test_string("NuTest"), Value::test_int(100)],
})],
Span::test_data(),
)),
result: Some(Value::test_list(vec![Value::test_record(record! {
"lang" => Value::test_string("NuTest"),
"gems" => Value::test_int(100),
})])),
},
]
}

View File

@ -2,7 +2,7 @@ use heck::ToShoutySnakeCase;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use super::operate;
@ -79,13 +79,10 @@ impl Command for SubCommand {
Example {
description: "convert a column from a table to SCREAMING_SNAKE_CASE",
example: r#"[[lang, gems]; [nu_test, 100]] | str screaming-snake-case lang"#,
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["lang".to_string(), "gems".to_string()],
vals: vec![Value::test_string("NU_TEST"), Value::test_int(100)],
})],
Span::test_data(),
)),
result: Some(Value::test_list(vec![Value::test_record(record! {
"lang" => Value::test_string("NU_TEST"),
"gems" => Value::test_int(100),
})])),
},
]
}

View File

@ -2,7 +2,7 @@ use heck::ToSnakeCase;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use super::operate;
@ -78,13 +78,10 @@ impl Command for SubCommand {
Example {
description: "convert a column from a table to snake_case",
example: r#"[[lang, gems]; [nuTest, 100]] | str snake-case lang"#,
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["lang".to_string(), "gems".to_string()],
vals: vec![Value::test_string("nu_test"), Value::test_int(100)],
})],
Span::test_data(),
)),
result: Some(Value::test_list(vec![Value::test_record(record! {
"lang" => Value::test_string("nu_test"),
"gems" => Value::test_int(100),
})])),
},
]
}

View File

@ -2,7 +2,7 @@ use heck::ToTitleCase;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use super::operate;
@ -74,13 +74,10 @@ impl Command for SubCommand {
Example {
description: "convert a column from a table to Title Case",
example: r#"[[title, count]; ['nu test', 100]] | str title-case title"#,
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["title".to_string(), "count".to_string()],
vals: vec![Value::test_string("Nu Test"), Value::test_int(100)],
})],
Span::test_data(),
)),
result: Some(Value::test_list(vec![Value::test_record(record! {
"title" => Value::test_string("Nu Test"),
"count" => Value::test_int(100),
})])),
},
]
}

View File

@ -6,16 +6,16 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
edition = "2021"
license = "MIT"
name = "nu-cmd-lang"
version = "0.86.0"
version = "0.87.1"
[lib]
bench = false
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.86.0" }
nu-parser = { path = "../nu-parser", version = "0.86.0" }
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
nu-utils = { path = "../nu-utils", version = "0.86.0" }
nu-engine = { path = "../nu-engine", version = "0.87.1" }
nu-parser = { path = "../nu-parser", version = "0.87.1" }
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
nu-utils = { path = "../nu-utils", version = "0.87.1" }
nu-ansi-term = "0.49.0"
fancy-regex = "0.11"

View File

@ -44,7 +44,7 @@ impl Command for Collect {
let capture_block: Closure = call.req(engine_state, stack, 0)?;
let block = engine_state.get_block(capture_block.block_id).clone();
let mut stack_captures = stack.captures_to_stack(&capture_block.captures);
let mut stack_captures = stack.captures_to_stack(capture_block.captures.clone());
let metadata = input.metadata();
let input: Value = input.into_value(call.head);
@ -71,8 +71,8 @@ impl Command for Collect {
redirect_env(engine_state, stack, &stack_captures);
// for when we support `data | let x = $in;`
// remove the variables added earlier
for var_id in capture_block.captures.keys() {
stack_captures.remove_var(*var_id);
for (var_id, _) in capture_block.captures {
stack_captures.remove_var(var_id);
}
if let Some(u) = saved_positional {
stack_captures.remove_var(u);

View File

@ -37,11 +37,21 @@ impl Command for DefEnv {
fn run(
&self,
_engine_state: &EngineState,
engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
nu_protocol::report_error_new(
engine_state,
&ShellError::GenericError(
"Deprecated command".into(),
"`def-env` is deprecated and will be removed in 0.88.".into(),
Some(call.head),
Some("Use `def --env` instead".into()),
vec![],
),
);
Ok(PipelineData::empty())
}

View File

@ -1,7 +1,8 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
ast::Call,
engine::{Closure, Command, EngineState, Stack, StateWorkingSet},
record, Category, Example, IntoPipelineData, PipelineData, PipelineMetadata, Record,
ShellError, Signature, Type, Value,
};
#[derive(Clone)]
@ -18,12 +19,21 @@ impl Command for Describe {
fn signature(&self) -> Signature {
Signature::build("describe")
.input_output_types(vec![(Type::Any, Type::String)])
.input_output_types(vec![
(Type::Any, Type::String),
(Type::Any, Type::Record(vec![])),
])
.switch(
"no-collect",
"do not collect streams of structured data",
Some('n'),
)
.switch(
"detailed",
"show detailed information about the value",
Some('d'),
)
.switch("collect-lazyrecords", "collect lazy records", Some('l'))
.category(Category::Core)
}
@ -33,12 +43,12 @@ impl Command for Describe {
fn run(
&self,
_engine_state: &EngineState,
engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
run(call, input)
run(Some(engine_state), call, input)
}
fn run_const(
@ -47,7 +57,7 @@ impl Command for Describe {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
run(call, input)
run(None, call, input)
}
fn examples(&self) -> Vec<Example> {
@ -57,18 +67,79 @@ impl Command for Describe {
example: "'hello' | describe",
result: Some(Value::test_string("string")),
},
/*
Example {
description: "Describe the type of a record in a detailed way",
example:
"{shell:'true', uwu:true, features: {bugs:false, multiplatform:true, speed: 10}, fib: [1 1 2 3 5 8], on_save: {|x| print $'Saving ($x)'}, first_commit: 2019-05-10, my_duration: (4min + 20sec)} | describe -d",
result: Some(Value::test_record(record!(
"type" => Value::test_string("record"),
"lazy" => Value::test_bool(false),
"columns" => Value::test_record(record!(
"shell" => Value::test_string("string"),
"uwu" => Value::test_string("bool"),
"features" => Value::test_record(record!(
"type" => Value::test_string("record"),
"lazy" => Value::test_bool(false),
"columns" => Value::test_record(record!(
"bugs" => Value::test_string("bool"),
"multiplatform" => Value::test_string("bool"),
"speed" => Value::test_string("int"),
)),
)),
"fib" => Value::test_record(record!(
"type" => Value::test_string("list"),
"length" => Value::test_int(6),
"values" => Value::test_list(vec![
Value::test_string("int"),
Value::test_string("int"),
Value::test_string("int"),
Value::test_string("int"),
Value::test_string("int"),
Value::test_string("int"),
]),
)),
"on_save" => Value::test_record(record!(
"type" => Value::test_string("closure"),
"signature" => Value::test_record(record!(
"name" => Value::test_string(""),
"category" => Value::test_string("default"),
)),
)),
"first_commit" => Value::test_string("date"),
"my_duration" => Value::test_string("duration"),
)),
))),
},
Example {
description: "Describe the type of a stream with detailed information",
example: "[1 2 3] | each {|i| echo $i} | describe -d",
result: None // Give "Running external commands not supported" error
// result: Some(Value::test_record(record!(
// "type" => Value::test_string("stream"),
// "origin" => Value::test_string("nushell"),
// "subtype" => Value::test_record(record!(
// "type" => Value::test_string("list"),
// "length" => Value::test_int(3),
// "values" => Value::test_list(vec![
// Value::test_string("int"),
// Value::test_string("int"),
// Value::test_string("int"),
// ])
// ))
// ))),
},
Example {
description: "Describe a stream of data, collecting it first",
example: "[1 2 3] | each {|i| $i} | describe",
result: Some(Value::test_string("list<int> (stream)")),
example: "[1 2 3] | each {|i| echo $i} | describe",
result: None // Give "Running external commands not supported" error
// result: Some(Value::test_string("list<int> (stream)")),
},
Example {
description: "Describe the input but do not collect streams",
example: "[1 2 3] | each {|i| $i} | describe --no-collect",
result: Some(Value::test_string("stream")),
example: "[1 2 3] | each {|i| echo $i} | describe --no-collect",
result: None // Give "Running external commands not supported" error
// result: Some(Value::test_string("stream")),
},
*/
]
}
@ -77,16 +148,88 @@ impl Command for Describe {
}
}
fn run(call: &Call, input: PipelineData) -> Result<PipelineData, ShellError> {
fn run(
engine_state: Option<&EngineState>,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let metadata = input.metadata().clone().map(Box::new);
let head = call.head;
let no_collect: bool = call.has_flag("no-collect");
let detailed = call.has_flag("detailed");
let description = match input {
PipelineData::ExternalStream { .. } => "raw input".into(),
let description: Value = match input {
PipelineData::ExternalStream {
ref stdout,
ref stderr,
ref exit_code,
..
} => {
if detailed {
Value::record(
record!(
"type" => Value::string("stream", head),
"origin" => Value::string("external", head),
"stdout" => match stdout {
Some(_) => Value::record(
record!(
"type" => Value::string("stream", head),
"origin" => Value::string("external", head),
"subtype" => Value::string("any", head),
),
head,
),
None => Value::nothing(head),
},
"stderr" => match stderr {
Some(_) => Value::record(
record!(
"type" => Value::string("stream", head),
"origin" => Value::string("external", head),
"subtype" => Value::string("any", head),
),
head,
),
None => Value::nothing(head),
},
"exit_code" => match exit_code {
Some(_) => Value::record(
record!(
"type" => Value::string("stream", head),
"origin" => Value::string("external", head),
"subtype" => Value::string("int", head),
),
head,
),
None => Value::nothing(head),
},
"metadata" => metadata_to_value(metadata, head),
),
head,
)
} else {
Value::string("raw input", head)
}
}
PipelineData::ListStream(_, _) => {
if no_collect {
"stream".into()
if detailed {
Value::record(
record!(
"type" => Value::string("stream", head),
"origin" => Value::string("nushell", head),
"subtype" => {
if no_collect {
Value::string("any", head)
} else {
describe_value(input.into_value(head), head, engine_state, call)?
}
},
"metadata" => metadata_to_value(metadata, head),
),
head,
)
} else if no_collect {
Value::string("stream", head)
} else {
let value = input.into_value(head);
let base_description = match value {
@ -94,19 +237,195 @@ fn run(call: &Call, input: PipelineData) -> Result<PipelineData, ShellError> {
_ => value.get_type().to_string(),
};
format!("{base_description} (stream)")
Value::string(format!("{} (stream)", base_description), head)
}
}
_ => {
let value = input.into_value(head);
match value {
Value::CustomValue { val, .. } => val.value_string(),
_ => value.get_type().to_string(),
if !detailed {
match value {
Value::CustomValue { val, .. } => Value::string(val.value_string(), head),
_ => Value::string(value.get_type().to_string(), head),
}
} else {
describe_value(value, head, engine_state, call)?
}
}
};
Ok(Value::string(description, head).into_pipeline_data())
Ok(description.into_pipeline_data())
}
fn describe_value(
value: Value,
head: nu_protocol::Span,
engine_state: Option<&EngineState>,
call: &Call,
) -> Result<Value, ShellError> {
Ok(match value {
Value::CustomValue { val, internal_span } => Value::record(
record!(
"type" => Value::string("custom", head),
"subtype" => run(engine_state,call, val.to_base_value(internal_span)?.into_pipeline_data())?.into_value(head),
),
head,
),
Value::Bool { .. }
| Value::Int { .. }
| Value::Float { .. }
| Value::Filesize { .. }
| Value::Duration { .. }
| Value::Date { .. }
| Value::Range { .. }
| Value::String { .. }
| Value::MatchPattern { .. }
| Value::Nothing { .. } => Value::record(
record!(
"type" => Value::string(value.get_type().to_string(), head),
),
head,
),
Value::Record { val, .. } => {
let mut record = Record::new();
for i in 0..val.len() {
let k = val.cols[i].clone();
let v = val.vals[i].clone();
record.push(k, {
if let Value::Record { val, .. } =
describe_value(v.clone(), head, engine_state, call)?
{
if let [Value::String { val: k, .. }] = val.vals.as_slice() {
Value::string(k, head)
} else {
Value::record(val, head)
}
} else {
describe_value(v, head, engine_state, call)?
}
});
}
Value::record(
record!(
"type" => Value::string("record", head),
"lazy" => Value::bool(false, head),
"columns" => Value::record(record, head),
),
head,
)
}
Value::List { vals, .. } => Value::record(
record!(
"type" => Value::string("list", head),
"length" => Value::int(vals.len() as i64, head),
"values" => Value::list(vals.iter().map(|v|
match describe_value(v.clone(), head, engine_state, call) {
Ok(Value::Record {val, ..}) => if val.cols.as_slice() == ["type"] {Ok(val.vals[0].clone())} else {Ok(Value::record(val, head))},
x => x
}
).collect::<Result<Vec<_>, _>>()?, head),
),
head,
),
Value::Block { val, .. }
| Value::Closure {
val: Closure { block_id: val, .. },
..
} => {
let block = engine_state.map(|engine_state| engine_state.get_block(val));
if let Some(block) = block {
let mut record = Record::new();
record.push("type", Value::string(value.get_type().to_string(), head));
record.push(
"signature",
Value::record(
record!(
"name" => Value::string(block.signature.name.clone(), head),
"category" => Value::string(block.signature.category.to_string(), head),
),
head,
),
);
Value::record(record, head)
} else {
Value::record(
record!(
"type" => Value::string("closure", head),
),
head,
)
}
}
Value::Error { error, .. } => Value::record(
record!(
"type" => Value::string("error", head),
"subtype" => Value::string(error.to_string(), head),
),
head,
),
Value::Binary { val, .. } => Value::record(
record!(
"type" => Value::string("binary", head),
"length" => Value::int(val.len() as i64, head),
),
head,
),
Value::CellPath { val, .. } => Value::record(
record!(
"type" => Value::string("cellpath", head),
"length" => Value::int(val.members.len() as i64, head),
),
head,
),
Value::LazyRecord { val, .. } => {
let collect_lazyrecords: bool = call.has_flag("collect-lazyrecords");
let mut record = Record::new();
record.push("type", Value::string("record", head));
record.push("lazy", Value::bool(true, head));
if collect_lazyrecords {
let collected = val.collect()?;
if let Value::Record { val, .. } =
describe_value(collected, head, engine_state, call)?
{
let mut record_cols = Record::new();
record.push("length", Value::int(val.len() as i64, head));
for i in 0..val.len() {
record_cols.push(
val.cols[i].clone(),
describe_value(val.vals[i].clone(), head, engine_state, call)?,
);
}
record.push("columns", Value::record(record_cols, head));
} else {
let cols = val.column_names();
record.push("length", Value::int(cols.len() as i64, head));
}
} else {
let cols = val.column_names();
record.push("length", Value::int(cols.len() as i64, head));
}
Value::record(record, head)
}
})
}
fn metadata_to_value(metadata: Option<Box<PipelineMetadata>>, head: nu_protocol::Span) -> Value {
match metadata {
Some(metadata) => Value::record(
record!(
"data_source" => Value::string(format!("{:?}", metadata.data_source), head),
),
head,
),
_ => Value::nothing(head),
}
}
#[cfg(test)]

View File

@ -72,7 +72,7 @@ impl Command for Do {
let capture_errors = call.has_flag("capture-errors");
let has_env = call.has_flag("env");
let mut callee_stack = caller_stack.captures_to_stack(&block.captures);
let mut callee_stack = caller_stack.captures_to_stack(block.captures);
let block = engine_state.get_block(block.block_id);
let params: Vec<_> = block

View File

@ -2,7 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -44,20 +44,15 @@ impl Command for ErrorMake {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let span = call.head;
let arg: Value = call.req(engine_state, stack, 0)?;
let unspanned = call.has_flag("unspanned");
let throw_error = if unspanned { None } else { Some(span) };
Err(make_error(&arg, throw_error).unwrap_or_else(|| {
ShellError::GenericError(
"Creating error value not supported.".into(),
"unsupported error format".into(),
Some(span),
None,
Vec::new(),
)
}))
let throw_span = if call.has_flag("unspanned") {
None
} else {
Some(call.head)
};
Err(make_other_error(&arg, throw_span))
}
fn examples(&self) -> Vec<Example> {
@ -82,16 +77,21 @@ impl Command for ErrorMake {
msg: "my custom error message"
label: {
text: "my custom label text" # not mandatory unless $.label exists
start: 123 # not mandatory unless $.label.end is set
end: 456 # not mandatory unless $.label.start is set
# optional
span: {
# if $.label.span exists, both start and end must be present
start: 123
end: 456
}
}
help: "A help string, suggesting a fix to the user" # optional
}"#,
result: Some(Value::error(
ShellError::GenericError(
"my custom error message".to_string(),
"my custom label text".to_string(),
Some(Span::new(123, 456)),
None,
Some("A help string, suggesting a fix to the user".to_string()),
Vec::new(),
),
Span::unknown(),
@ -101,13 +101,11 @@ impl Command for ErrorMake {
description:
"Create a custom error for a custom command that shows the span of the argument",
example: r#"def foo [x] {
let span = (metadata $x).span;
error make {
msg: "this is fishy"
label: {
text: "fish right here"
start: $span.start
end: $span.end
span: (metadata $x).span
}
}
}"#,
@ -117,100 +115,166 @@ impl Command for ErrorMake {
}
}
fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
const UNABLE_TO_PARSE: &str = "Unable to parse error format.";
fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
let span = value.span();
if let Value::Record { .. } = &value {
let msg = value.get_data_by_key("msg");
let label = value.get_data_by_key("label");
match (msg, &label) {
(Some(Value::String { val: message, .. }), Some(label)) => {
let label_start = label.get_data_by_key("start");
let label_end = label.get_data_by_key("end");
let label_text = label.get_data_by_key("text");
let label_span = Some(label.span());
match (label_start, label_end, label_text) {
(
Some(Value::Int { val: start, .. }),
Some(Value::Int { val: end, .. }),
Some(Value::String {
val: label_text, ..
}),
) => {
if start > end {
Some(ShellError::GenericError(
"invalid error format.".into(),
"`$.label.start` should be smaller than `$.label.end`".into(),
label_span,
Some(format!("{} > {}", start, end)),
Vec::new(),
))
} else {
Some(ShellError::GenericError(
message,
label_text,
Some(Span::new(start as usize, end as usize)),
None,
Vec::new(),
))
}
}
(
None,
None,
Some(Value::String {
val: label_text, ..
}),
) => Some(ShellError::GenericError(
message,
label_text,
throw_span,
None,
Vec::new(),
)),
(_, _, None) => Some(ShellError::GenericError(
"Unable to parse error format.".into(),
"missing required member `$.label.text`".into(),
label_span,
None,
Vec::new(),
)),
(Some(Value::Int { .. }), None, _) => Some(ShellError::GenericError(
"Unable to parse error format.".into(),
"missing required member `$.label.end`".into(),
label_span,
Some("required because `$.label.start` is set".to_string()),
Vec::new(),
)),
(None, Some(Value::Int { .. }), _) => Some(ShellError::GenericError(
"Unable to parse error format.".into(),
"missing required member `$.label.start`".into(),
label_span,
Some("required because `$.label.end` is set".to_string()),
Vec::new(),
)),
_ => None,
}
}
(Some(Value::String { val: message, .. }), None) => Some(ShellError::GenericError(
message,
"originates from here".to_string(),
let value = match value {
Value::Record { val, .. } => val,
_ => {
return ShellError::GenericError(
"Creating error value not supported.".into(),
"unsupported error format, must be a record".into(),
throw_span,
None,
Vec::new(),
)),
(None, _) => Some(ShellError::GenericError(
"Unable to parse error format.".into(),
)
}
};
let msg = match value.get("msg") {
Some(Value::String { val, .. }) => val.clone(),
Some(_) => {
return ShellError::GenericError(
UNABLE_TO_PARSE.into(),
"`$.msg` has wrong type, must be string".into(),
Some(span),
None,
Vec::new(),
)
}
None => {
return ShellError::GenericError(
UNABLE_TO_PARSE.into(),
"missing required member `$.msg`".into(),
Some(span),
None,
Vec::new(),
)),
_ => None,
)
}
} else {
None
};
let help = match value.get("help") {
Some(Value::String { val, .. }) => Some(val.clone()),
_ => None,
};
let (label, label_span) = match value.get("label") {
Some(value @ Value::Record { val, .. }) => (val, value.span()),
Some(_) => {
return ShellError::GenericError(
UNABLE_TO_PARSE.into(),
"`$.label` has wrong type, must be a record".into(),
Some(span),
None,
Vec::new(),
)
}
// correct return: no label
None => {
return ShellError::GenericError(
msg,
"originates from here".to_string(),
throw_span,
help,
Vec::new(),
)
}
};
// remove after a few versions
if label.get("start").is_some() || label.get("end").is_some() {
return ShellError::GenericError(
UNABLE_TO_PARSE.into(),
"`start` and `end` are deprecated".into(),
Some(span),
Some("Use `$.label.span` instead".into()),
Vec::new(),
);
}
let text = match label.get("text") {
Some(Value::String { val, .. }) => val.clone(),
Some(_) => {
return ShellError::GenericError(
UNABLE_TO_PARSE.into(),
"`$.label.text` has wrong type, must be string".into(),
Some(label_span),
None,
Vec::new(),
)
}
None => {
return ShellError::GenericError(
UNABLE_TO_PARSE.into(),
"missing required member `$.label.text`".into(),
Some(label_span),
None,
Vec::new(),
)
}
};
let (span, span_span) = match label.get("span") {
Some(value @ Value::Record { val, .. }) => (val, value.span()),
Some(value) => {
return ShellError::GenericError(
UNABLE_TO_PARSE.into(),
"`$.label.span` has wrong type, must be record".into(),
Some(value.span()),
None,
Vec::new(),
)
}
// correct return: label, no span
None => return ShellError::GenericError(msg, text, throw_span, help, Vec::new()),
};
let span_start = match get_span_sides(span, span_span, "start") {
Ok(val) => val,
Err(err) => return err,
};
let span_end = match get_span_sides(span, span_span, "end") {
Ok(val) => val,
Err(err) => return err,
};
if span_start > span_end {
return ShellError::GenericError(
"invalid error format.".into(),
"`$.label.start` should be smaller than `$.label.end`".into(),
Some(label_span),
Some(format!("{} > {}", span_start, span_end)),
Vec::new(),
);
}
// correct return: everything present
ShellError::GenericError(
msg,
text,
Some(Span::new(span_start as usize, span_end as usize)),
help,
Vec::new(),
)
}
fn get_span_sides(span: &Record, span_span: Span, side: &str) -> Result<i64, ShellError> {
match span.get(side) {
Some(Value::Int { val, .. }) => Ok(*val),
Some(_) => Err(ShellError::GenericError(
UNABLE_TO_PARSE.into(),
format!("`$.span.{side}` must be int"),
Some(span_span),
None,
Vec::new(),
)),
None => Err(ShellError::GenericError(
UNABLE_TO_PARSE.into(),
format!("`$.span.{side}` must be present, if span is specified."),
Some(span_span),
None,
Vec::new(),
)),
}
}

View File

@ -62,11 +62,21 @@ export def-env cd_with_fallback [arg = ""] {
fn run(
&self,
_engine_state: &EngineState,
engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
nu_protocol::report_error_new(
engine_state,
&ShellError::GenericError(
"Deprecated command".into(),
"`export def-env` is deprecated and will be removed in 0.88.".into(),
Some(call.head),
Some("Use `export def --env` instead".into()),
vec![],
),
);
Ok(PipelineData::empty())
}

View File

@ -34,11 +34,21 @@ impl Command for ExportExternWrapped {
fn run(
&self,
_engine_state: &EngineState,
engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
nu_protocol::report_error_new(
engine_state,
&ShellError::GenericError(
"Deprecated command".into(),
"`export extern-wrapped` is deprecated and will be removed in 0.88.".into(),
Some(call.head),
Some("Use `export def --wrapped` instead".into()),
vec![],
),
);
Ok(PipelineData::empty())
}

View File

@ -37,11 +37,21 @@ impl Command for ExternWrapped {
fn run(
&self,
_engine_state: &EngineState,
engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
nu_protocol::report_error_new(
engine_state,
&ShellError::GenericError(
"Deprecated command".into(),
"`extern-wrapped` is deprecated and will be removed in 0.88.".into(),
Some(call.head),
Some("Use `def --wrapped` instead".into()),
vec![],
),
);
Ok(PipelineData::empty())
}

View File

@ -17,7 +17,7 @@ impl Command for ScopeAliases {
Signature::build("scope aliases")
.input_output_types(vec![(Type::Nothing, Type::Any)])
.allow_variants_without_examples(true)
.category(Category::Filters)
.category(Category::Core)
}
fn usage(&self) -> &str {

View File

@ -17,7 +17,7 @@ impl Command for ScopeCommands {
Signature::build("scope commands")
.input_output_types(vec![(Type::Nothing, Type::Any)])
.allow_variants_without_examples(true)
.category(Category::Filters)
.category(Category::Core)
}
fn usage(&self) -> &str {

View File

@ -15,7 +15,7 @@ impl Command for ScopeEngineStats {
Signature::build("scope engine-stats")
.input_output_types(vec![(Type::Nothing, Type::Any)])
.allow_variants_without_examples(true)
.category(Category::Filters)
.category(Category::Core)
}
fn usage(&self) -> &str {

View File

@ -17,7 +17,7 @@ impl Command for ScopeExterns {
Signature::build("scope externs")
.input_output_types(vec![(Type::Nothing, Type::Any)])
.allow_variants_without_examples(true)
.category(Category::Filters)
.category(Category::Core)
}
fn usage(&self) -> &str {

View File

@ -17,7 +17,7 @@ impl Command for ScopeModules {
Signature::build("scope modules")
.input_output_types(vec![(Type::Nothing, Type::Any)])
.allow_variants_without_examples(true)
.category(Category::Filters)
.category(Category::Core)
}
fn usage(&self) -> &str {

View File

@ -17,7 +17,7 @@ impl Command for ScopeVariables {
Signature::build("scope variables")
.input_output_types(vec![(Type::Nothing, Type::Any)])
.allow_variants_without_examples(true)
.category(Category::Filters)
.category(Category::Core)
}
fn usage(&self) -> &str {

View File

@ -1,6 +1,6 @@
use itertools::Itertools;
use nu_protocol::{
ast::Block,
ast::{Block, RangeInclusion},
engine::{EngineState, Stack, StateDelta, StateWorkingSet},
Example, PipelineData, Signature, Span, Type, Value,
};
@ -143,7 +143,8 @@ pub fn check_example_evaluates_to_expected_output(
// you need to define its equality in the Value struct
if let Some(expected) = example.result.as_ref() {
assert_eq!(
&result, expected,
DebuggableValue(&result),
DebuggableValue(expected),
"The example result differs from the expected value",
)
}
@ -184,3 +185,103 @@ fn eval(
let (block, delta) = parse(contents, engine_state);
eval_block(block, input, cwd, engine_state, delta)
}
pub struct DebuggableValue<'a>(pub &'a Value);
impl PartialEq for DebuggableValue<'_> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<'a> std::fmt::Debug for DebuggableValue<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
Value::Bool { val, .. } => {
write!(f, "{:?}", val)
}
Value::Int { val, .. } => {
write!(f, "{:?}", val)
}
Value::Float { val, .. } => {
write!(f, "{:?}f", val)
}
Value::Filesize { val, .. } => {
write!(f, "Filesize({:?})", val)
}
Value::Duration { val, .. } => {
let duration = std::time::Duration::from_nanos(*val as u64);
write!(f, "Duration({:?})", duration)
}
Value::Date { val, .. } => {
write!(f, "Date({:?})", val)
}
Value::Range { val, .. } => match val.inclusion {
RangeInclusion::Inclusive => write!(
f,
"Range({:?}..{:?}, step: {:?})",
val.from, val.to, val.incr
),
RangeInclusion::RightExclusive => write!(
f,
"Range({:?}..<{:?}, step: {:?})",
val.from, val.to, val.incr
),
},
Value::String { val, .. } => {
write!(f, "{:?}", val)
}
Value::Record { val, .. } => {
write!(f, "{{")?;
for i in 0..val.len() {
let col = &val.cols[i];
let value = &val.vals[i];
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{:?}: {:?}", col, DebuggableValue(value))?;
}
write!(f, "}}")
}
Value::List { vals, .. } => {
write!(f, "[")?;
for (i, value) in vals.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{:?}", DebuggableValue(value))?;
}
write!(f, "]")
}
Value::Block { val, .. } => {
write!(f, "Block({:?})", val)
}
Value::Closure { val, .. } => {
write!(f, "Closure({:?})", val)
}
Value::Nothing { .. } => {
write!(f, "Nothing")
}
Value::Error { error, .. } => {
write!(f, "Error({:?})", error)
}
Value::Binary { val, .. } => {
write!(f, "Binary({:?})", val)
}
Value::CellPath { val, .. } => {
write!(f, "CellPath({:?})", val.to_string())
}
Value::CustomValue { val, .. } => {
write!(f, "CustomValue({:?})", val)
}
Value::LazyRecord { val, .. } => {
let rec = val.collect().map_err(|_| std::fmt::Error)?;
write!(f, "LazyRecord({:?})", DebuggableValue(&rec))
}
Value::MatchPattern { val, .. } => {
write!(f, "MatchPattern({:?})", val)
}
}
}
}

View File

@ -5,19 +5,19 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
edition = "2021"
license = "MIT"
name = "nu-color-config"
version = "0.86.0"
version = "0.87.1"
[lib]
bench = false
[dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
nu-ansi-term = "0.49.0"
nu-utils = { path = "../nu-utils", version = "0.86.0" }
nu-engine = { path = "../nu-engine", version = "0.86.0" }
nu-json = { path = "../nu-json", version = "0.86.0" }
nu-utils = { path = "../nu-utils", version = "0.87.1" }
nu-engine = { path = "../nu-engine", version = "0.87.1" }
nu-json = { path = "../nu-json", version = "0.87.1" }
serde = { version = "1.0", features = ["derive"] }
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
nu-test-support = { path = "../nu-test-support", version = "0.87.1" }

View File

@ -578,7 +578,7 @@ fn fill_modifiers(attrs: &str, style: &mut Style) {
//
// since we can combine styles like bold-italic, iterate through the chars
// and set the bools for later use in the nu_ansi_term::Style application
for ch in attrs.to_lowercase().chars() {
for ch in attrs.chars().map(|c| c.to_ascii_lowercase()) {
match ch {
'l' => style.is_blink = true,
'b' => style.is_bold = true,

View File

@ -57,15 +57,11 @@ impl<'a> StyleComputer<'a> {
Some(ComputableStyle::Closure(v)) => {
let span = v.span();
match v {
Value::Closure {
val: block_id,
captures,
..
} => {
let block = self.engine_state.get_block(*block_id).clone();
Value::Closure { val, .. } => {
let block = self.engine_state.get_block(val.block_id).clone();
// Because captures_to_stack() clones, we don't need to use with_env() here
// (contrast with_env() usage in `each` or `do`).
let mut stack = self.stack.captures_to_stack(captures);
let mut stack = self.stack.captures_to_stack(val.captures.clone());
// Support 1-argument blocks as well as 0-argument blocks.
if let Some(var) = block.signature.get_positional(0) {

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT"
name = "nu-command"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
version = "0.86.0"
version = "0.87.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -14,19 +14,19 @@ bench = false
[dependencies]
nu-ansi-term = "0.49.0"
nu-cmd-base = { path = "../nu-cmd-base", version = "0.86.0" }
nu-color-config = { path = "../nu-color-config", version = "0.86.0" }
nu-engine = { path = "../nu-engine", version = "0.86.0" }
nu-glob = { path = "../nu-glob", version = "0.86.0" }
nu-json = { path = "../nu-json", version = "0.86.0" }
nu-parser = { path = "../nu-parser", version = "0.86.0" }
nu-path = { path = "../nu-path", version = "0.86.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.86.0" }
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
nu-system = { path = "../nu-system", version = "0.86.0" }
nu-table = { path = "../nu-table", version = "0.86.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.86.0" }
nu-utils = { path = "../nu-utils", version = "0.86.0" }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.87.1" }
nu-color-config = { path = "../nu-color-config", version = "0.87.1" }
nu-engine = { path = "../nu-engine", version = "0.87.1" }
nu-glob = { path = "../nu-glob", version = "0.87.1" }
nu-json = { path = "../nu-json", version = "0.87.1" }
nu-parser = { path = "../nu-parser", version = "0.87.1" }
nu-path = { path = "../nu-path", version = "0.87.1" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.87.1" }
nu-protocol = { path = "../nu-protocol", version = "0.87.1" }
nu-system = { path = "../nu-system", version = "0.87.1" }
nu-table = { path = "../nu-table", version = "0.87.1" }
nu-term-grid = { path = "../nu-term-grid", version = "0.87.1" }
nu-utils = { path = "../nu-utils", version = "0.87.1" }
alphanumeric-sort = "1.5"
base64 = "0.21"
@ -37,7 +37,7 @@ chrono = { version = "0.4", features = ["std", "unstable-locales"], default-feat
chrono-humanize = "0.2.3"
chrono-tz = "0.8"
crossterm = "0.27"
csv = "1.2"
csv = "1.3"
dialoguer = { default-features = false, features = ["fuzzy-select"], version = "0.11" }
digest = { default-features = false, version = "0.10" }
dtparse = "2.0"
@ -47,7 +47,7 @@ filesize = "0.2"
filetime = "0.2"
fs_extra = "1.3"
htmlescape = "0.3"
indexmap = "2.0"
indexmap = "2.1"
indicatif = "0.17"
itertools = "0.11"
log = "0.4"
@ -88,9 +88,11 @@ unicode-segmentation = "1.10"
ureq = { version = "2.8", default-features = false, features = ["charset", "gzip", "json", "native-tls"] }
url = "2.2"
uu_cp = "0.0.22"
uuid = { version = "1.3", features = ["v4"] }
uu_whoami = "0.0.22"
uu_mkdir = "0.0.22"
uuid = { version = "1.5", features = ["v4"] }
wax = { version = "0.6" }
which = { version = "4.4", optional = true }
which = { version = "5.0", optional = true }
bracoxide = "0.1.2"
chardetng = "0.1.17"
@ -110,6 +112,7 @@ version = "3.1"
features = [
"Win32_Foundation",
"Win32_Storage_FileSystem",
"Win32_System_Environment",
"Win32_System_SystemServices",
"Win32_Security",
"Win32_System_Threading",
@ -123,8 +126,8 @@ trash-support = ["trash"]
which-support = ["which"]
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.86.0" }
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.87.1" }
nu-test-support = { path = "../nu-test-support", version = "0.87.1" }
dirs-next = "2.0"
mockito = { version = "1.2", default-features = false }

View File

@ -1,11 +1,11 @@
use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_cmd_base::util;
use nu_engine::CallExt;
use nu_protocol::record;
use nu_protocol::{
ast::{Call, CellPath},
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, Range, Record, ShellError, Signature, Span, SyntaxShape, Type,
Value,
Category, Example, PipelineData, Range, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -94,44 +94,34 @@ impl Command for BytesAt {
Example {
description: "Get a subbytes `0x[10 01]` from the bytes `0x[33 44 55 10 01 13]`",
example: " 0x[33 44 55 10 01 13] | bytes at 3..<4",
result: Some(Value::binary(vec![0x10], Span::test_data())),
result: Some(Value::test_binary(vec![0x10])),
},
Example {
description: "Get a subbytes `0x[10 01 13]` from the bytes `0x[33 44 55 10 01 13]`",
example: " 0x[33 44 55 10 01 13] | bytes at 3..6",
result: Some(Value::binary(vec![0x10, 0x01, 0x13], Span::test_data())),
result: Some(Value::test_binary(vec![0x10, 0x01, 0x13])),
},
Example {
description: "Get the remaining characters from a starting index",
example: " { data: 0x[33 44 55 10 01 13] } | bytes at 3.. data",
result: Some(Value::test_record(Record {
cols: vec!["data".to_string()],
vals: vec![Value::test_binary(vec![0x10, 0x01, 0x13])],
result: Some(Value::test_record(record! {
"data" => Value::test_binary(vec![0x10, 0x01, 0x13]),
})),
},
Example {
description: "Get the characters from the beginning until ending index",
example: " 0x[33 44 55 10 01 13] | bytes at ..<4",
result: Some(Value::binary(
vec![0x33, 0x44, 0x55, 0x10],
Span::test_data(),
)),
result: Some(Value::test_binary(vec![0x33, 0x44, 0x55, 0x10])),
},
Example {
description:
"Or the characters from the beginning until ending index inside a table",
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes at 1.. ColB ColC"#,
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
vals: vec![
Value::binary(vec![0x11, 0x12, 0x13], Span::test_data()),
Value::binary(vec![0x15, 0x16], Span::test_data()),
Value::binary(vec![0x18, 0x19], Span::test_data()),
],
})],
Span::test_data(),
)),
result: Some(Value::test_list(vec![Value::test_record(record! {
"ColA" => Value::test_binary(vec![0x11, 0x12, 0x13]),
"ColB" => Value::test_binary(vec![0x15, 0x16]),
"ColC" => Value::test_binary(vec![0x18, 0x19]),
})])),
},
]
}
@ -186,13 +176,12 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
Value::Error { .. } => input.clone(),
other => Value::error(
ShellError::UnsupportedInput(
"Only binary values are supported".into(),
format!("input type: {:?}", other.get_type()),
head,
// This line requires the Value::Error match above.
other.span(),
),
ShellError::UnsupportedInput {
msg: "Only binary values are supported".into(),
input: format!("input type: {:?}", other.get_type()),
msg_span: head,
input_span: other.span(),
},
head,
),
}

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::{Call, CellPath};
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
record, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
struct Arguments {
@ -95,33 +95,29 @@ impl Command for BytesIndexOf {
Example {
description: "Returns all matched index",
example: " 0x[33 44 55 10 01 33 44 33 44] | bytes index-of --all 0x[33 44]",
result: Some(Value::list(
vec![Value::test_int(0), Value::test_int(5), Value::test_int(7)],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_int(0),
Value::test_int(5),
Value::test_int(7),
])),
},
Example {
description: "Returns all matched index, searching from end",
example: " 0x[33 44 55 10 01 33 44 33 44] | bytes index-of --all --end 0x[33 44]",
result: Some(Value::list(
vec![Value::test_int(7), Value::test_int(5), Value::test_int(0)],
Span::test_data(),
)),
result: Some(Value::test_list(vec![
Value::test_int(7),
Value::test_int(5),
Value::test_int(0),
])),
},
Example {
description: "Returns index of pattern for specific column",
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes index-of 0x[11] ColA ColC"#,
result: Some(Value::list(
vec![Value::test_record(Record {
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
vals: vec![
Value::test_int(0),
Value::binary(vec![0x14, 0x15, 0x16], Span::test_data()),
Value::test_int(-1),
],
})],
Span::test_data(),
)),
result: Some(Value::test_list(vec![Value::test_record(record! {
"ColA" => Value::test_int(0),
"ColB" => Value::binary(vec![0x14, 0x15, 0x16], Span::test_data()),
"ColC" => Value::test_int(-1),
})])),
},
]
}

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape,
record, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape,
Type, Value,
};
@ -87,49 +87,33 @@ impl Command for BytesRemove {
Example {
description: "Remove contents",
example: "0x[10 AA FF AA FF] | bytes remove 0x[10 AA]",
result: Some(Value::binary (
result: Some(Value::test_binary (
vec![0xFF, 0xAA, 0xFF],
Span::test_data(),
)),
},
Example {
description: "Remove all occurrences of find binary in record field",
example: "{ data: 0x[10 AA 10 BB 10] } | bytes remove --all 0x[10] data",
result: Some(Value::test_record(Record {
cols: vec!["data".to_string()],
vals: vec![Value::test_binary(vec![0xAA, 0xBB])]
result: Some(Value::test_record(record! {
"data" => Value::test_binary(vec![0xAA, 0xBB])
})),
},
Example {
description: "Remove occurrences of find binary from end",
example: "0x[10 AA 10 BB CC AA 10] | bytes remove --end 0x[10]",
result: Some(Value::binary (
result: Some(Value::test_binary (
vec![0x10, 0xAA, 0x10, 0xBB, 0xCC, 0xAA],
Span::test_data(),
)),
},
Example {
description: "Remove all occurrences of find binary in table",
example: "[[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes remove 0x[11] ColA ColC",
result: Some(Value::list (
vec![Value::test_record(Record {
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
vals: vec![
Value::binary (
vec![0x12, 0x13],
Span::test_data(),
),
Value::binary (
vec![0x14, 0x15, 0x16],
Span::test_data(),
),
Value::binary (
vec![0x17, 0x18, 0x19],
Span::test_data(),
),
]
result: Some(Value::test_list (
vec![Value::test_record(record! {
"ColA" => Value::test_binary ( vec![0x12, 0x13],),
"ColB" => Value::test_binary ( vec![0x14, 0x15, 0x16],),
"ColC" => Value::test_binary ( vec![0x17, 0x18, 0x19],),
})],
Span::test_data(),
)),
},
]

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