280 Commits

Author SHA1 Message Date
Bahex
55de232a1c
refactor Value::follow_cell_path to reduce clones and return Cow (#15640)
# Description
While working on something else, I noticed that
`Value::follow_cell_path` receives `self`.

While it would be ideal for the signature to be `(&'a self, cell_path)
-> &'a Value`, that's not possible because:
1. Selecting a row from a list and field from a record can be done with
a reference but selecting a column from a table requires creating a new
list.
2. `Value::Custom` returns new `Value`s when indexed.

So the signature becomes `(&'a self, cell_path) -> Cow<'a, Value>`.

Another complication that arises is, once a new `Value` is created, and
it is further indexed, the `current` variable
1. can't be `&'a Value`, as the lifetime requirement means it can't
refer to local variables
2. _shouldn't_ be `Cow<'a, Value>`, as once it becomes an owned value,
it can't be borrowed ever again, as `current` is derived from its
previous value in further iterations. So once it's owned, it can't be
indexed by reference, leading to more clones

We need `current` to have _two_ possible lifetimes
1. `'out`: references derived from `&self`
2. `'local`: references derived from an owned value stored in a local
variable

```rust
enum MultiLife<'out, 'local, T>
where
    'out: 'local,
    T: ?Sized,
{
    Out(&'out T),
    Local(&'local T),
}
```
With `current: MultiLife<'out, '_, Value>`, we can traverse values with
minimal clones, and we can transform it to `Cow<'out, Value>` easily
(`MultiLife::Out -> Cow::Borrowed, MultiLife::Local -> Cow::Owned`) to
return it

# User-Facing Changes

# Tests + Formatting

# After Submitting

---------

Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com>
2025-05-01 09:43:57 -05:00
pyz4
95998bdd53
fix(custom_value) + fix(polars): map // operator to FloorDivide for custom values and in polars (#15599)
<!--
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 issue where, for custom values, the `//` operator was
incorrectly mapped to `Math::Divide` instead of `Math::FloorDivide`.
This PR also fixes the same mis-mapping in the `polars` plugin.

```nushell
> [[a b c]; [x 1 1.1] [y 2 2.2] [z 3 3.3]] | polars into-df | polars select {div: ((polars col c) / (polars col b)), floor_div: ((polars col c) // (polars col b))} | polars collect
╭───┬───────┬───────────╮
│ # │  div  │ floor_div │
├───┼───────┼───────────┤
│ 0 │ 1.100 │     1.000 │
│ 1 │ 1.100 │     1.000 │
│ 2 │ 1.100 │     1.000 │
╰───┴───────┴───────────╯
```

**Note:** the number of line changes in this PR is inflated because of
auto-formatting in `nu_plugin_polars/Cargo.toml`. Substantively, I've
only added the `round_series` feature to the polars dependency list.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
Breaking change: users who expected the operator `//` to function the
same as `/` for custom values will not get the expected result.

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->
No tests were yet added, but let me know if we should put something into
one of the polars examples.

# 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.
-->
2025-04-18 13:49:33 -07:00
Loïc Riegel
c8c018452f
Bugfix chrono panic + hotifx PR15544 (#15549)
Closes  #13972

# Description
First commit: a hotfix concerning my last PR #15544! I had a
``unwrap_or_default`` that resulted in all years before ~1800 being
considered as "now", because the ``num_nanoseconds()`` overflowed.
Cc @fdncred 

Second: about #13972
Negative years are not allowed with RFC 2822 formatting, so I fallback
RTC 3339 in such cases.

If you want you might Rebase and Merge, and not squash.

# User-Facing Changes
On master 🔴 :
```nu
~> {year: 1900} | into datetime
Mon, 1 Jan 1900 00:00:00 +0200 (125 years ago)
# OK

~> {year: 1000} | into datetime
Wed, 1 Jan 1000 00:00:00 +0200 (now)
# NOT OK: now?

~> {year: -1000} | into datetime
-1000-01-01T00:00:00+02:00 (now)
# NOT OK: now?

~> {year: -1000} | into datetime | format date 
Error:   × Main thread panicked.
  ├─▶ at C:\Users\RIL1RT\.cargo\registry\src\index.crates.io-6f17d22bba15001f\chrono-0.4.39\src\datetime\mod.rs:626:14
  ╰─▶ writing rfc2822 datetime to string should never fail: Error
  help: set the `RUST_BACKTRACE=1` environment variable to display a backtrace.
# NOT OK: panics
```

On this branch 🟢 :
```nu
~> {year: 1900} | into datetime
Mon, 1 Jan 1900 00:00:00 +0200 (in 125 years)
~>  {year: 1000} | into datetime
Wed, 1 Jan 1000 00:00:00 +0200 (1025 years ago)
~> {year: -1000} | into datetime
-1000-01-01T00:00:00+02:00 (3025 years ago)
~> {year: -1000} | into datetime | format date
-1000-01-01T00:00:00+02:00
~> '3000 years ago' | date from-human | format date
-0975-04-11T18:18:24.301641100+02:00
```

# Tests + Formatting

# After Submitting
Nothing required IMO
2025-04-11 11:52:42 -05:00
Loïc Riegel
08940ba4f8
bugfix: wrong display of human readable string (#15522)
I think after that we can close  #14790

# Description
So the issue was the tiny time delta between the moment the "date
form-human" command is executed, and the moment the value gets
displayed, using chrono_humanize.

When in inputing "in 30 seconds", we currently get:
```
[crates\nu-protocol\src\value\mod.rs:950:21] HumanTime::from(*val) = HumanTime(
    TimeDelta {
        secs: 29,
        nanos: 992402700,
    },
)```
And with "now":
```
crates\nu-protocol\src\value\mod.rs:950:21] HumanTime::from(*val) =
HumanTime(
    TimeDelta {
        secs: -1,
        nanos: 993393200,
    },
)
```

My solution is to round this timedelta to seconds and pass this to chrono_humanize.
Example: instead of passing (-1s + 993393200ns), we pass 0s.
Example: instead of passing (29s + 992402700ns), we pass 30s


# User-Facing Changes
Before 🔴 
```nushell
~> "in 3 days" | date from-human
Fri, 11 Apr 2025 09:06:36 +0200 (in 2 days)
~> "in 30 seconds" | date from-human
Tue, 8 Apr 2025 09:07:09 +0200 (in 29 seconds)
```

After those changes 🟢 
```nushell
~> "in 3 days" | date from-human
Fri, 11 Apr 2025 09:03:47 +0200 (in 3 days)
~> "in 30 seconds" | date from-human
Tue, 8 Apr 2025 09:04:28 +0200 (in 30 seconds)
```

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make sure to [enable developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu  # or use an `env_change` hook to activate it automatically
> toolkit check pr
> ```
-->

# After Submitting
<!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. -->
2025-04-08 06:29:16 -05:00
Douglas
7c160725ed
Rename user-facing 'date' to 'datetime' (#15264)
We only have one valid `datetime` type, but the string representation of
that type was `date`. This PR updates the string representation of the
`datetime` type to be `datetime` and updates other affected
dependencies:

* A `describe` example that used `date`
* The style computer automatically recognized the new change, but also
changed the default `date: purple` to `datetime: purple`.
* Likewise, changed the `default_config.nu` to populate
`$env.config.color_config.datetime`
* Likewise, the dark and light themes in `std/config`
* Updates tests
* Unrelated, but changed the `into value` error messages to use
*"datetime"* if there's an issue.

Fixes #9916 and perhaps others.

## Breaking Changes:

* Code that expected `describe` to return a `date` will now return a
`datetime`
* User configs and themes that override `$env.config.color_config.date`
will need to be updated to use `datetime`
2025-03-21 13:36:21 -04:00
132ikl
69d1c8e948
Add compile-time assertion of Value's size (#15362)
# Description

Adds an assertion of `Value`'s size, similar to `Instruction` and
`Expr`.
2025-03-20 02:59:06 +00:00
Ian Manske
d97b2e3c60
Respect system locale when formatting file sizes via config (#15271)
# Description

Commands and other pieces of code using `$env.config.format.filesize` to
format filesizes now respect the system locale when formatting the
numeric portion of a file size.

# User-Facing Changes

- System locale is respected when using `$env.config.format.filesize` to
format file sizes.
- Formatting a file size with a binary unit is now exact for large file
sizes and units.
- The output of `to text` is no longer dependent on the config.
2025-03-09 15:43:02 -05:00
Marcel Mukundi
083c534948
Fix insert/upsert creation for nested lists (#15131) (#15133)
# Description
This PR fixes #15131 by allowing the `insert` and `upsert` commands to
create lists where they may be expected based on the cell path provided.
For example, the below would have previously thrown an error, but now
creates lists and list elements where necessary
<img width="173" alt="Screenshot 2025-02-17 at 2 46 12 AM"
src="https://github.com/user-attachments/assets/6d680e7e-6268-42ed-a037-a0795014a7e0"
/>
<img width="200" alt="Screenshot 2025-02-17 at 2 46 16 AM"
src="https://github.com/user-attachments/assets/50d0e8eb-aabb-49fe-b961-5f7489fdc993"
/>
<img width="284" alt="Screenshot 2025-02-17 at 2 45 43 AM"
src="https://github.com/user-attachments/assets/242a2ec6-7e8f-4a51-92ce-9d5ec10f867f"
/>

# User-Facing Changes
This change removes errors that were previously raised by
`insert_data_at_cell_path` and `upsert_data_at_cell_path`. If one of
these commands encountered an unknown cell path in cases such as these,
it would either raise a "Not a list value" as the list index is used on
a record:

<img width="326" alt="Screenshot 2025-02-17 at 2 46 43 AM"
src="https://github.com/user-attachments/assets/39b9b006-388b-49b3-82a0-8cc9b739feaa"
/>


Or a "Row number too large" when required to create a new list element
along the way:
<img width="475" alt="Screenshot 2025-02-17 at 2 46 51 AM"
src="https://github.com/user-attachments/assets/007d1268-7d26-42aa-9bf5-d54c0abf4058"
/>


But both now succeed, which seems to be the intention as it is in parity
with record behavior. Any consumers depending on this specific behavior
will see these errors subside.

This change also includes the static method
`Value::with_data_at_cell_path` that creates a value with a given nested
value at a given cell path, creating records or lists based on the path
member type. 

# Tests + Formatting
In addition to unit tests for the altered behavior, both affected
user-facing commands (`insert` and `upsert`) gained a new command
example to both explain and test this change at the user level.
<img width="382" alt="Screenshot 2025-02-17 at 2 29 26 AM"
src="https://github.com/user-attachments/assets/e6973640-3ce6-4ea7-9ba5-d256fe5cb38b"
/>

Note: A single test did fail locally, due to my config directory
differing from expected, but works where this variable is unset
(`with-env { XDG_CONFIG_HOME: null } {cargo test}`):
```
---- repl::test_config_path::test_default_config_path stdout ----
thread 'repl::test_config_path::test_default_config_path' panicked at tests/repl/test_config_path.rs:101:5:
assertion failed: `(left == right)`

Diff < left / right > :
<[home_dir]/Library/Application Support/nushell
>[home_dir]/.config/nushell
```
2025-02-22 21:53:25 -08:00
Ian Manske
62e56d3581
Rework operator type errors (#14429)
# Description

This PR adds two new `ParseError` and `ShellError` cases for type errors
relating to operators.
- `OperatorUnsupportedType` is used when a type is not supported by an
operator in any way, shape, or form. E.g., `+` does not support `bool`.
- `OperatorIncompatibleTypes` is used when a operator is used with types
it supports, but the combination of types provided cannot be used
together. E.g., `filesize + duration` is not a valid combination.

The other preexisting error cases related to operators have been removed
and replaced with the new ones above. Namely:

- `ShellError::OperatorMismatch`
- `ShellError::UnsupportedOperator`
- `ParseError::UnsupportedOperationLHS`
- `ParseError::UnsupportedOperationRHS`
- `ParseError::UnsupportedOperationTernary`

# User-Facing Changes

- `help operators` now lists the precedence of `not` as 55 instead of 0
(above the other boolean operators). Fixes #13675.
- `math median` and `math mode` now ignore NaN values so that `[NaN NaN]
| math median` and `[NaN NaN] | math mode` no longer trigger a type
error. Instead, it's now an empty input error. Fixing this in earnest
can be left for a future PR.
- Comparisons with `nan` now return false instead of causing an error.
E.g., `1 == nan` is now `false`.
- All the operator type errors have been standardized and reworked. In
particular, they can now have a help message, which is currently used
for types errors relating to `++`.

```nu
[1] ++ 2
```
```
Error: nu::parser::operator_unsupported_type

  × The '++' operator does not work on values of type 'int'.
   ╭─[entry #1:1:5]
 1 │ [1] ++ 2
   ·     ─┬ ┬
   ·      │ ╰── int
   ·      ╰── does not support 'int'
   ╰────
  help: if you meant to append a value to a list or a record to a table, use the `append` command or wrap the value in a list. For example: `$list ++ $value` should be
        `$list ++ [$value]` or `$list | append $value`.
```
2025-02-12 20:03:40 -08:00
zc he
339c5b7c83
fix: clippy warning of rust 1.8.4 (#14984)
# Description

I'm on rust toolchain 1.8.4, and I can see clippy warnings that can't be
caught by the ci workflow, primarily related to lifetime params.

I think it doesn't hurt to fix those in advance.

# User-Facing Changes

# Tests + Formatting

# After Submitting
2025-02-02 07:56:54 -06:00
Piepmatz
080b501ba8
Fix cargo doc Warnings (#14948)
<!--
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.
-->
As an avid `cargo doc` enjoyer I realized we had some doc warnings, so I
fixed them.

After this PR `cargo doc --workspace` should stop throwing warnings.
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

No code changes.

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

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

# 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.
-->

We could add a `cargo doc` CI pipeline but usually running a full `cargo
doc` takes like forever, so maybe we don't want that.
2025-01-28 18:09:53 -06:00
Ian Manske
93e121782c
Improve and fix filesize formatting/display (#14397)
# Description

This PR cleans up the code surrounding formatting and displaying file
sizes.
- The `byte_unit` crate we use for file size units displays kilobytes as
`KB`, which is not the SI or ISO/IEC standard. Rather it should be `kB`,
so this fixes #8872. On some systems, `KB` actually means `KiB`, so this
avoids any potential confusion.
- The `byte_unit` crate, when displaying file sizes, casts integers to
floats which will lose precision for large file sizes. This PR adds a
custom `Display` implementation for `Filesize` that can give an exact
string representation of a `Filesize` for metric/SI units.
- This PR also removes the dependency on the `byte_unit` crate which
brought in several other dependencies.

Additionally, this PR makes some changes to the config for filesize
formatting (`$env.config.filesize`).
- The previous filesize config had the `metric` and `format` options. If
a metric (SI) unit was set in `format`, but `metric` was set to false,
then the `metric` option would take precedence and convert `format` to
the corresponding binary unit (or vice versa). E.g., `{ format: kB,
metric: false }` => `KiB`. Instead, this PR adds the `unit` option to
replace the `format` and `metric` options. `unit` can be set to a fixed
file size unit like `kB` or `KiB`, or it can be set to one of the
special options: `binary` or `metric`. These options tells nushell to
format file sizes using an appropriately scaled metric or binary unit
(examples below).
  ```nushell
  # precision = null

  # unit = kB
  1kB  # 1 kB
  1KiB # 1.024 kB
  
  # unit = KiB
  1kB  # 0.9765625 KiB
  1KiB # 1 KiB
  
  # unit = metric
  1000B     # 1 kB
  1024B     # 1.024 kB
  10_000MB  # 10 GB
  10_240MiB # 10.73741824 GB

  # unit = binary
  1000B     # 1000 B
  1024B     # 1 KiB
  10_000MB  # 9.313225746154785 GiB
  10_240MiB # 10 GiB
  ```
- In addition, this PR also adds the `precision` option to the filesize
config. It determines how many digits to show after the decimal point.
If set to null, then everything after the decimal point is shown.
- The default filesize config is `{ unit: metric, precision: 1 }`.

# User-Facing Changes

- Commands that use the config to format file sizes will follow the
changes described above (e.g., `table`, `into string`, `to text`, etc.).
- The file size unit/format passed to `format filesize` is now case
sensitive. An error with the valid units is shown if the case does not
match.
- `$env.config.filesize.format` and `$env.config.filesize.metric` are
deprecated and replaced by `$env.config.filesize.unit`.
- A new `$env.config.filesize.precision` option was added.

# Tests + Formatting

Mostly updated test expected outputs.

# After Submitting

This PR does not change the way NUON serializes file sizes, because that
would require changing the nu parser to be able to losslessly decode the
new, exact string representation introduced in this PR.

Similarly, this PR also does not change the file size parsing in any
way. Although the file size units provided to `format filesize` or the
filesize config are now case-sensitive, the same is not yet true for
file size literals in nushell code.
2025-01-22 22:24:51 -08:00
Bahex
089c5221cc
Add new operators has and not-has (#14841)
# Description
This PR add 2 new operators, `has` and `not-has`. They are basically
`in` and `not-in` with the order of operands swapped.

Motivation for this was the awkward way of searching for rows that
contain an item using `where`

```nushell
[[name, children]; [foo, [a, b, c]], [bar [d, e, f]]]
| where ("e" in $it.children)
```
vs
```nushell
[[name, children]; [foo, [a, b, c]], [bar [d, e, f]]]
| where children has "e"
``` 

# User-Facing Changes
Added `has` and `not-has` operators, mirroring `in` and `not-in`.

# Tests + Formatting

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

# After Submitting
2025-01-17 06:20:00 -06:00
132ikl
214714e0ab
Add run-time type checking for command pipeline input (#14741)
<!--
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 adds type checking of all command input types at run-time.
Generally, these errors should be caught by the parser, but sometimes we
can't know the type of a value at parse-time. The simplest example is
using the `echo` command, which has an output type of `any`, so
prefixing a literal with `echo` will bypass parse-time type checking.

Before this PR, each command has to individually check its input types.
This can result in scenarios where the input/output types don't match
the actual command behavior. This can cause valid usage with an
non-`any` type to become a parse-time error if a command is missing that
type in its pipeline input/output (`drop nth` and `history import` do
this before this PR). Alternatively, a command may not list a type in
its input/output types, but doesn't actually reject that type in its
code, which can have unintended side effects (`get` does this on an
empty pipeline input, and `sort` used to before #13154).

After this PR, the type of the pipeline input is checked to ensure it
matches one of the input types listed in the proceeding command's
input/output types. While each of the issues in the "before this PR"
section could be addressed with each command individually, this PR
solves this issue for _all_ commands.

**This will likely cause some breakage**, as some commands have
incorrect input/output types, and should be adjusted. Also, some scripts
may have erroneous usage of commands. In writing this PR, I discovered
that `toolkit.nu` was passing `null` values to `str join`, which doesn't
accept nothing types (if folks think it should, we can adjust it in this
PR or in a different PR). I found some issues in the standard library
and its tests. I also found that carapace's vendor script had an
incorrect chaining of `get -i`:

```nushell
let expanded_alias = (scope aliases | where name == $spans.0 | get -i 0 | get -i expansion)
```

Before this PR, if the `get -i 0` ever actually did evaluate to `null`,
the second `get` invocation would error since `get` doesn't operate on
`null` values. After this PR, this is immediately a run-time error,
alerting the user to the problematic code. As a side note, we'll need to
PR this fix (`get -i 0 | get -i expansion` -> `get -i 0.expansion`) to
carapace.

A notable exception to the type checking is commands with input type of
`nothing -> <type>`. In this case, any input type is allowed. This
allows piping values into the command without an error being thrown. For
example, `123 | echo $in` would be an error without this exception.
Additionally, custom types bypass type checking (I believe this also
happens during parsing, but not certain)

I added a `is_subtype` method to `Value` and `PipelineData`. It
functions slightly differently than `get_type().is_subtype()`, as noted
in the doccomments. Notably, it respects structural typing of lists and
tables. For example, the type of a value `[{a: 123} {a: 456, b: 789}]`
is a subtype of `table<a: int>`, whereas the type returned by
`Value::get_type` is a `list<any>`. Similarly, `PipelineData` has some
special handling for `ListStream`s and `ByteStream`s. The latter was
needed for this PR to work properly with external commands.

Here's some examples.

Before:
```nu
1..2 | drop nth 1
Error: nu::parser::input_type_mismatch

  × Command does not support range input.
   ╭─[entry #9:1:8]
 1 │ 1..2 | drop nth 1
   ·        ────┬───
   ·            ╰── command doesn't support range input
   ╰────

echo 1..2 | drop nth 1
# => ╭───┬───╮
# => │ 0 │ 1 │
# => ╰───┴───╯
```

After this PR, I've adjusted `drop nth`'s input/output types to accept
range input.

Before this PR, zip accepted any value despite not being listed in its
input/output types. This caused different behavior depending on if you
triggered a parse error or not:
```nushell
1 | zip [2]
# => Error: nu::parser::input_type_mismatch
# => 
# =>   × Command does not support int input.
# =>    ╭─[entry #3:1:5]
# =>  1 │ 1 | zip [2]
# =>    ·     ─┬─
# =>    ·      ╰── command doesn't support int input
# =>    ╰────
echo 1 | zip [2]
# => ╭───┬───────────╮
# => │ 0 │ ╭───┬───╮ │
# => │   │ │ 0 │ 1 │ │
# => │   │ │ 1 │ 2 │ │
# => │   │ ╰───┴───╯ │
# => ╰───┴───────────╯
```

After this PR, it works the same in both cases. For cases like this, if
we do decide we want `zip` or other commands to accept any input value,
then we should explicitly add that to the input types.
```nushell
1 | zip [2]
# => Error: nu::parser::input_type_mismatch
# => 
# =>   × Command does not support int input.
# =>    ╭─[entry #3:1:5]
# =>  1 │ 1 | zip [2]
# =>    ·     ─┬─
# =>    ·      ╰── command doesn't support int input
# =>    ╰────
echo 1 | zip [2]
# => Error: nu:🐚:only_supports_this_input_type
# => 
# =>   × Input type not supported.
# =>    ╭─[entry #14:2:6]
# =>  2 │ echo 1 | zip [2]
# =>    ·      ┬   ─┬─
# =>    ·      │    ╰── only list<any> and range input data is supported
# =>    ·      ╰── input type: int
# =>    ╰────
```

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

**Breaking change**: The type of a command's input is now checked
against the input/output types of that command at run-time. While these
errors should mostly be caught at parse-time, in cases where they can't
be detected at parse-time they will be caught at run-time instead. This
applies to both internal commands and custom commands.

Example function and corresponding parse-time error (same before and
after PR):
```nushell
def foo []: int -> nothing {
  print $"my cool int is ($in)"
}

1 | foo
# => my cool int is 1

"evil string" | foo
# => Error: nu::parser::input_type_mismatch
# => 
# =>   × Command does not support string input.
# =>    ╭─[entry #16:1:17]
# =>  1 │ "evil string" | foo
# =>    ·                 ─┬─
# =>    ·                  ╰── command doesn't support string input
# =>    ╰────
# => 
```

Before:
```nu
echo "evil string" | foo
# => my cool int is evil string
```

After:
```nu
echo "evil string" | foo
# => Error: nu:🐚:only_supports_this_input_type
# => 
# =>   × Input type not supported.
# =>    ╭─[entry #17:1:6]
# =>  1 │ echo "evil string" | foo
# =>    ·      ──────┬──────   ─┬─
# =>    ·            │          ╰── only int input data is supported
# =>    ·            ╰── input type: string
# =>    ╰────
```

Known affected internal commands which erroneously accepted any type:
* `str join`
* `zip`
* `reduce`

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`


# 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.
-->
* Play whack-a-mole with the commands and scripts this will inevitably
break
2025-01-08 23:09:47 +01:00
Bahex
16e174be7e
fix nuon conversions of range values (#14687)
# Description
Currently the step size of range values are discarded when converting to
nuon. This PR fixes that and makes `to nuon | from nuon` round trips
work.

# User-Facing Changes
`to nuon` conversion of `range` values now include the step size

# Tests + Formatting
Added some additional tests to cover inclusive/exclusive integer/float
and step size cases.
2025-01-07 21:29:39 +01:00
Darren Schroeder
dad956b2ee
more closure serialization (#14698)
# Description

This PR introduces a switch `--serialize` that allows serializing of
types that cannot be deserialized. Right now it only serializes closures
as strings in `to toml`, `to json`, `to nuon`, `to text`, some indirect
`to html` and `to yaml`.

A lot of the changes are just weaving the engine_state through calling
functions and the rest is just repetitive way of getting the closure
block span and grabbing the span's text.

In places where it has to report `<Closure 123>` I changed it to
`closure_123`. It always seemed like the `<>` were not very nushell-y.
This is still a breaking change.

I think this could also help with systematic translation of old config
to new config file.


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

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2025-01-07 11:51:22 -06:00
Piepmatz
25d90fa603
Use Value::coerce_bool in into bool (#14731)
<!--
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.
-->
I realized that the `into bool` command somehow implements a conversion
into a boolean value which was very similar to my implementation of
~`Value::as_env_bool`~ `Value::coerce_bool`. To streamline that behavior
a bit, I replaced most of the implementation of `into bool` with my
~`Value::as_env_bool`~ `Value::coerce_bool` method.

Also I added a new flag called `--relaxed` which lets the command behave
more closely to the ~`Value::as_env_bool`~ `Value::coerce_bool` method
as it allows null values and is more loose to strings. ~Which now begs
the question, should I rename `Value::as_env_bool` just to
`Value::coerce_bool` which would fit the `Value::coerce_str` method
name?~ (Renamed that.)

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

The `into bool` command behaves the same but with `--relaxed` you can
also throw a `null` or some more strings at it which makes it more
ergonomic for env conversions.

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

I added some more tests to see that the strict handling works and added
some more examples to the command to showcase the `--relaxed` flag which
also gets tested.

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

# 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.
-->

@Bahex mentioned in #14704 that it broke the zoxide script, this PR
should help to fix the issue.
2025-01-03 08:11:34 -06:00
Piepmatz
e7877db078
Coerce boolean values into strings too (#14704)
<!--
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.
-->

The `Value::coerce_str` method weirdly doesn't allow coercing boolean
values into strings while commands like `true | into string` work
without issues. So I added that.

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

This is technically a breaking change if a nushell library user depended
on the fact that boolean values weren't coerceable to strings. But I
doubt that really.

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`
2024-12-30 21:23:40 -06:00
Stefan Holderbach
1181349c22
Promote note about internal_span to doccomment (#14703)
Following #14700 we should make sure more folks are aware that you
shouldn't use `internal_span` outside of `Value` or core protocol/engine
internals.

By making it a doccomment maybe a few folks see the text in the lsp
hover etc.
2024-12-30 23:02:57 +01:00
Piepmatz
5f3c8d45d8
Add auto option for config.use_ansi_coloring (#14647)
<!--
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.
-->

In this PR I continued the idea of #11494, it added an `auto` option to
the ansi coloring config option, I did this too but in a more simple
approach.

So I added a new enum `UseAnsiColoring` with the three values `True`,
`False` and `Auto`. When that value is set to `auto`, the default value,
it will use `std::io::stdout().is_terminal()` to decided whether to use
ansi coloring. This allows to dynamically decide whether to print ansi
color codes or not, [cargo does it the same
way](652623b779/src/bin/cargo/main.rs (L72)).
`True` and `False` act as overrides to the `is_terminal` check. So with
that PR it is possible to force ansi colors on the `table` command or
automatically remove them from the miette errors if no terminal is used.

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

Terminal users shouldn't be affected by this change as the default value
was `true` and `is_terminal` returns for terminals `true` (duh).
Non-terminal users, that use `nu` in some embedded way or the engine
implemented in some other way (like my jupyter kernel) will now have by
default no ansi coloring and need to enable it manually if their
environment allows it.

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

The test for fancy errors expected ansi codes, since tests aren't run
"in terminal", the ansi codes got stripped away.
I added a line that forced ansi colors above it. I'm not sure if that
should be the case or if we should test against no ansi colors.

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

# 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.
-->

This should resolve #11464 and partially #11847. This also closes
#11494.
2024-12-26 11:00:01 -06:00
Ian Manske
7f61cbbfd6
Add Filesize type (#14369)
# Description
Adds a new `Filesize` type so that `FromValue` can be used to convert a
`Value::Filesize` to a `Filesize`. Currently, to extract a filesize from
a `Value` using `FromValue`, you have to extract an `i64` which coerces
`Value::Int`, `Value::Duration`, and `Value::Filesize` to an `i64`.

Having a separate type also allows us to enforce checked math to catch
overflows. Similarly, it allows us to specify other trait
implementations like `Display` in a common place.

# User-Facing Changes
Multiplication with filesizes now error on overflow. Should not be a
breaking change for plugins (i.e., serialization) since `Filesize` is
marked with `serde(transparent)`.

# Tests + Formatting
Updated some tests.
2024-11-29 21:24:17 +00:00
Ian Manske
4d3283e235
Change append operator to concatenation operator (#14344)
# Description

The "append" operator currently serves as both the append operator and
the concatenation operator. This dual role creates ambiguity when
operating on nested lists.

```nu
[1 2] ++ 3     # appends a value to a list [1 2 3]
[1 2] ++ [3 4] # concatenates two lists    [1 2 3 4]

[[1 2] [3 4]] ++ [5 6]
# does this give [[1 2] [3 4] [5 6]]
# or             [[1 2] [3 4] 5 6]  
```

Another problem is that `++=` can change the type of a variable:
```nu
mut str = 'hello '
$str ++= ['world']
($str | describe) == list<string>
```

Note that appending is only relevant for lists, but concatenation is
relevant for lists, strings, and binary values. Additionally, appending
can be expressed in terms of concatenation (see example below). So, this
PR changes the `++` operator to only perform concatenation.

# User-Facing Changes

Using the `++` operator with a list and a non-list value will now be a
compile time or runtime error.
```nu
mut list = []
$list ++= 1 # error
```
Instead, concatenate a list with one element:
```nu
$list ++= [1]
```
Or use `append`:
```nu
$list = $list | append 1
```

# After Submitting

Update book and docs.

---------

Co-authored-by: Douglas <32344964+NotTheDr01ds@users.noreply.github.com>
2024-11-24 10:59:54 -08:00
Douglas
f0cb2dafbb
Allow duration to be added to date (#14295)
# Description

Fixes #14294 - Turned out to be a whole lot easier than I expected, but
please double-check me on this, since it's an area I haven't been in
before.

# User-Facing Changes

Allow date to be added to a duration type.

# Tests + Formatting

Tests added:

* Duration + Date is allowed
* Duration - Date is not allowed
2024-11-14 10:07:37 +01:00
Ian Manske
e87a35104a
Remove as_i64 and as_f64 (#14258)
# Description
Turns out there are duplicate conversion functions: `as_i64` and
`as_f64`. In most cases, these can be replaced with `as_int` and
`as_float`, respectively.
2024-11-05 09:28:56 +01:00
Ian Manske
9f09930834
Div, mod, and floor div overhaul (#14157)
# Description
Dividing two ints can currently return either an int or a float. Not
having a single return type for an operation between two types seems
problematic. Additionally, the type signature for division says that
dividing two ints returns only an int which does not match the current
implementation (it can also return a float). This PR changes division
between almost all types to return a float (except for `filesize /
number` or `duration / number`, since there are no float representations
for these types).

Currently, floor division between certain types is not implemented even
though the type signature allows it. Also, the current implementation of
floor division uses a combination of clamping and flooring rather than
simply performing floor division which this PR fixes. Additionally, the
signature was changed so that `int // float`, `float // int`, and `float
// float` now return float instead of int. This matches the automatic
float promotion in the rest of the operators (as well as how Python does
floor division which I think is the original inspiration).

Since regular division has always returned fractional values (and now
returns a float to reflect that), `mod` is now defined in terms of floor
division. That is, `D // d = q`, `D mod d = r`, and `D = d * q + r `.
This is just like the `%` operator in Python, which is also based off
floor division (at least for ints and floats). Additionally,
implementations missing from `mod`'s current type signature have been
added (`duration mod int` and `duration mod float`).

This PR also overhauls the overflow checking and errors for div, mod,
and floor div. If an operation overflows, it will now cause an error.

# User-Facing Changes
- Div now returns a float in most cases.
- Floor division now actually does floor division.
- Floor division now does automatic float promotion, returning a float
in more instances.
- Floor division now actually allows division with filesize and
durations as its type signature claimed.
- Mod is now defined and implemented in terms of floor division rather
than truncating division.
- Mod now actually allows filesize and durations as its type signature
claimed.
- Div, mod, and floor div now all have proper overflow checks.

## Examples

When the divisor and the dividend have the same sign, the quotient and
remainder will be the same as before. (Except that this PR will give
more accurate results, since it does not do an intermediate float
conversion). If the signs of the divisor and dividend are different,
then the results will be different, or rather actually correct.

Before:

```nu
let q = 8 // -3 # -3
let r = 8 mod -3 # 2
8 == $q * -3 + $r # false
```

After:

```nu
let q = 8 // -3 # -3
let r = 8 mod -3 # -1
8 == $q * -3 + $r # true
```


Before:

```nu
let q = -8 // 3 # -3
let r = -8 mod 3 # -2
-8 == $q * 3 + $r # false
```

After:

```nu
let q = -8 // 3 # -3
let r = -8 mod 3 # 1
-8 == $q * 3 + $r # true
```

# Tests + Formatting
Added a few tests.

# After Submitting
Probably update the docs.
2024-11-04 18:03:48 +01:00
132ikl
36c1073441
Rework sorting and add cell path and closure comparators to sort-by (#13154)
# Description

Closes #12535
Implements sort-by functionality of #8322
Fixes sort-by part of #8667

This PR does two main things: add a new cell path and closure parameter
to `sort-by`, and attempt to make Nushell's sorting behavior
well-defined.

## `sort-by` features

The `columns` parameter is replaced with a `comparator` parameter, which
can be a cell path or a closure. Examples are from docs PR.

1. Cell paths

The basic interactive usage of `sort-by` is the same. For example, `ls |
sort-by modified` still works the same as before. It is not quite a
drop-in replacement, see [behavior changes](#behavior-changes).
   
   Here's an example of how the cell path comparator might be useful:
   
   ```nu
   > let cities = [
{name: 'New York', info: { established: 1624, population: 18_819_000 } }
{name: 'Kyoto', info: { established: 794, population: 37_468_000 } }
{name: 'São Paulo', info: { established: 1554, population: 21_650_000 }
}
   ]
   > $cities | sort-by info.established
   ╭───┬───────────┬────────────────────────────╮
   │ # │   name    │            info            │
   ├───┼───────────┼────────────────────────────┤
   │ 0 │ Kyoto     │ ╭─────────────┬──────────╮ │
   │   │           │ │ established │ 794      │ │
   │   │           │ │ population  │ 37468000 │ │
   │   │           │ ╰─────────────┴──────────╯ │
   │ 1 │ São Paulo │ ╭─────────────┬──────────╮ │
   │   │           │ │ established │ 1554     │ │
   │   │           │ │ population  │ 21650000 │ │
   │   │           │ ╰─────────────┴──────────╯ │
   │ 2 │ New York  │ ╭─────────────┬──────────╮ │
   │   │           │ │ established │ 1624     │ │
   │   │           │ │ population  │ 18819000 │ │
   │   │           │ ╰─────────────┴──────────╯ │
   ╰───┴───────────┴────────────────────────────╯
   ```

2. Key closures

You can supply a closure which will transform each value into a sorting
key (without changing the underlying data). Here's an example of a key
closure, where we want to sort a list of assignments by their average
grade:

   ```nu
   > let assignments = [
       {name: 'Homework 1', grades: [97 89 86 92 89] }
       {name: 'Homework 2', grades: [91 100 60 82 91] }
       {name: 'Exam 1', grades: [78 88 78 53 90] }
       {name: 'Project', grades: [92 81 82 84 83] }
   ]
   > $assignments | sort-by { get grades | math avg }
   ╭───┬────────────┬───────────────────────╮
   │ # │    name    │        grades         │
   ├───┼────────────┼───────────────────────┤
   │ 0 │ Exam 1     │ [78, 88, 78, 53, 90]  │
   │ 1 │ Project    │ [92, 81, 82, 84, 83]  │
   │ 2 │ Homework 2 │ [91, 100, 60, 82, 91] │
   │ 3 │ Homework 1 │ [97, 89, 86, 92, 89]  │
   ╰───┴────────────┴───────────────────────╯
   ```

3. Custom sort closure

The `--custom`, or `-c`, flag will tell `sort-by` to interpret closures
as custom sort closures. A custom sort closure has two parameters, and
returns a boolean. The closure should return `true` if the first
parameter comes _before_ the second parameter in the sort order.
   
For a simple example, we could rewrite a cell path sort as a custom sort
(see
[here](https://github.com/nushell/nushell.github.io/pull/1568/files#diff-a7a233e66a361d8665caf3887eb71d4288000001f401670c72b95cc23a948e86R231)
for a more complex example):
   
   ```nu
   > ls | sort-by -c {|a, b| $a.size < $b.size }
   ╭───┬─────────────────────┬──────┬──────────┬────────────────╮
   │ # │        name         │ type │   size   │    modified    │
   ├───┼─────────────────────┼──────┼──────────┼────────────────┤
   │ 0 │ my-secret-plans.txt │ file │    100 B │ 10 minutes ago │
   │ 1 │ shopping_list.txt   │ file │    100 B │ 2 months ago   │
   │ 2 │ myscript.nu         │ file │  1.1 KiB │ 2 weeks ago    │
   │ 3 │ bigfile.img         │ file │ 10.0 MiB │ 3 weeks ago    │
   ╰───┴─────────────────────┴──────┴──────────┴────────────────╯
   ```
   

## Making sort more consistent

I think it's important for something as essential as `sort` to have
well-defined semantics. This PR contains some changes to try to make the
behavior of `sort` and `sort-by` consistent. In addition, after working
with the internals of sorting code, I have a much deeper understanding
of all of the edge cases. Here is my attempt to try to better define
some of the semantics of sorting (if you are just interested in changes,
skip to "User-Facing changes")

- `sort`, `sort -v`, and `sort-by` now all work the same. Each
individual sort implementation has been refactored into two functions in
`sort_utils.rs`: `sort`, and `sort_by`. These can also be used in other
parts of Nushell where values need to be sorted.
  - `sort` and `sort-by` used to handle `-i` and `-n` differently.
- `sort -n` would consider all values which can't be coerced into a
string to be equal
- `sort-by -i` and `sort-by -n` would only work if all values were
strings
- In this PR, insensitive sort only affects comparison between strings,
and natural sort only applies to numbers and strings (see below).
- (not a change) Before and after this PR, `sort` and `sort-by` support
sorting mixed types. There was a lot of discussion about potentially
making `sort` and `sort-by` only work on lists of homogeneous types, but
the general consensus was that `sort` should not error just because its
input contains incompatible types.
- In order to try to make working with data containing `null` values
easier, I changed the PartialOrd order to sort `Nothing` values to the
end of a list, regardless of what other types the list contains. Before,
`null` would be sorted before `Binary`, `CellPath`, and `Custom` values.
- (not a change) When sorted, lists of mixed types will contain sorted
values of each type in order, for the most part
- (not a change) For example, `[0x[1] (date now) "a" ("yesterday" | into
datetime) "b" 0x[0]]` will be sorted as `["a", "b", a day ago, now, [0],
[1]]`, where sorted strings appear first, then sorted datetimes, etc.
- (not a change) The exception to this is `Int`s and `Float`s, which
will intermix, `Strings` and `Glob`s, which will intermix, and `None` as
described above. Additionally, natural sort will intermix strings with
ints and floats (see below).
- Natural sort no longer coerce all inputs to strings.
- I did originally make natural only apply to strings, but @fdncred
pointed out that the previous behavior also allowed you to sort numeric
strings with numbers. This seems like a useful feature if we are trying
to support sorting with mixed types, so I settled on coercing only
numbers (int, float). This can be reverted if people don't like it.
- Here is an example of this behavior in action, which is the same
before and after this PR:
      ```nushell
      $ [1 "4" 3 "2"] | sort --natural
      ╭───┬───╮
      │ 0 │ 1 │
      │ 1 │ 2 │
      │ 2 │ 3 │
      │ 3 │ 4 │
      ╰───┴───╯
      ```



# User-Facing Changes

## New features

- Replaces the `columns` string parameter of `sort-by` with a cell path
or a closure.
  - The cell path parameter works exactly as you would expect
- By default, the `closure` parameter acts as a "key sort"; that is,
each element is transformed by the closure into a sorting key
- With the `--custom` (`-c`) parameter, you can define a comparison
function for completely custom sorting order.

## Behavior changes

<details>
<summary><code>sort -v</code> does not coerce record values to
strings</summary>

This was a bit of a surprising behavior, and is now unified with the
behavior of `sort` and `sort-by`. Here's an example where you can
observe the values being implicitly coerced into strings for sorting, as
they are sorted like strings rather than numbers:

Old behavior:

```nushell
$ {foo: 9 bar: 10} | sort -v
╭─────┬────╮
│ bar │ 10 │
│ foo │ 9  │
╰─────┴────╯
```

New behavior:

```nushell
$ {foo: 9 bar: 10} | sort -v
╭─────┬────╮
│ foo │ 9  │
│ bar │ 10 │
╰─────┴────╯
```

</details>


<details>
<summary>Changed <code>sort-by</code> parameters from
<code>string</code> to <code>cell-path</code> or <code>closure</code>.
Typical interactive usage is the same as before, but if passing a
variable to <code>sort-by</code> it must be a cell path (or closure),
not a string</summary>

Old behavior:

```nushell
$ let sort = "modified"
$ ls | sort-by $sort
╭───┬──────┬──────┬──────┬────────────────╮
│ # │ name │ type │ size │    modified    │
├───┼──────┼──────┼──────┼────────────────┤
│ 0 │ foo  │ file │  0 B │ 10 hours ago   │
│ 1 │ bar  │ file │  0 B │ 35 seconds ago │
╰───┴──────┴──────┴──────┴────────────────╯
```

New behavior:

```nushell
$ let sort = "modified"
$ ls | sort-by $sort
Error: nu:🐚:type_mismatch

  × Type mismatch.
   ╭─[entry #10:1:14]
 1 │ ls | sort-by $sort
   ·              ──┬──
   ·                ╰── Cannot sort using a value which is not a cell path or closure
   ╰────
$ let sort = $."modified"
$ ls | sort-by $sort
╭───┬──────┬──────┬──────┬───────────────╮
│ # │ name │ type │ size │   modified    │
├───┼──────┼──────┼──────┼───────────────┤
│ 0 │ foo  │ file │  0 B │ 10 hours ago  │
│ 1 │ bar  │ file │  0 B │ 2 minutes ago │
╰───┴──────┴──────┴──────┴───────────────╯
```
</details>

<details>
<summary>Insensitve and natural sorting behavior reworked</summary>

Previously, the `-i` and `-n` worked differently for `sort` and
`sort-by` (see "Making sort more consistent"). Here are examples of how
these options result in different sorts now:

1. `sort -n`
- Old behavior (types other than numbers, strings, dates, and binary
sorted incorrectly)
      ```nushell
      $ [2sec 1sec] | sort -n
      ╭───┬──────╮
      │ 0 │ 2sec │
      │ 1 │ 1sec │
      ╰───┴──────╯
      ```
    - New behavior
      ```nushell
      $ [2sec 1sec] | sort -n
      ╭───┬──────╮
      │ 0 │ 1sec │
      │ 1 │ 2sec │
      ╰───┴──────╯
      ```
    
2. `sort-by -i`
- Old behavior (uppercase words appear before lowercase words as they
would in a typical sort, indicating this is not actually an insensitive
sort)
     ```nushell
     $ ["BAR" "bar" "foo" 2 "FOO" 1] | wrap a | sort-by -i a
     ╭───┬─────╮
     │ # │  a  │
     ├───┼─────┤
     │ 0 │   1 │
     │ 1 │   2 │
     │ 2 │ BAR │
     │ 3 │ FOO │
     │ 4 │ bar │
     │ 5 │ foo │
     ╰───┴─────╯
     ```
- New behavior (strings are sorted stably, indicating this is an
insensitive sort)
     ```nushell
     $ ["BAR" "bar" "foo" 2 "FOO" 1] | wrap a | sort-by -i a
     ╭───┬─────╮
     │ # │  a  │
     ├───┼─────┤
     │ 0 │   1 │
     │ 1 │   2 │
     │ 2 │ BAR │
     │ 3 │ bar │
     │ 4 │ foo │
     │ 5 │ FOO │
     ╰───┴─────╯
     ```

3. `sort-by -n`
- Old behavior (natural sort does not work when data contains non-string
values)
     ```nushell
     $ ["10" 8 "9"] | wrap a | sort-by -n a
     ╭───┬────╮
     │ # │ a  │
     ├───┼────┤
     │ 0 │  8 │
     │ 1 │ 10 │
     │ 2 │ 9  │
     ╰───┴────╯
     ```
   - New behavior
     ```nushell
     $ ["10" 8 "9"] | wrap a | sort-by -n a
     ╭───┬────╮
     │ # │ a  │
     ├───┼────┤
     │ 0 │  8 │
     │ 1 │ 9  │
     │ 2 │ 10 │
     ╰───┴────╯
     ```

</details>

<details>
<summary>
Sorting a list of non-record values with a non-existent column/path now
errors instead of sorting the values directly (<code>sort</code> should
be used for this, not <code>sort-by</code>)
</summary>

Old behavior:

```nushell
$ [2 1] | sort-by foo
╭───┬───╮
│ 0 │ 1 │
│ 1 │ 2 │
╰───┴───╯
```

New behavior:

```nushell
$ [2 1] | sort-by foo
Error: nu:🐚:incompatible_path_access

  × Data cannot be accessed with a cell path
   ╭─[entry #29:1:17]
 1 │ [2 1] | sort-by foo
   ·                 ─┬─
   ·                  ╰── int doesn't support cell paths
   ╰────
```

</details>

<details>
<summary><code>sort</code> and <code>sort-by</code> output
<code>List</code> instead of <code>ListStream</code> </summary>

This isn't a meaningful change (unless I misunderstand the purpose of
ListStream), since `sort` and `sort-by` both need to collect in order to
do the sorting anyway, but is user observable.

Old behavior:

```nushell
$ ls | sort | describe -d
╭──────────┬───────────────────╮
│ type     │ stream            │
│ origin   │ nushell           │
│ subtype  │ {record 3 fields} │
│ metadata │ {record 1 field}  │
╰──────────┴───────────────────╯
```

```nushell
$ ls | sort-by name | describe -d
╭──────────┬───────────────────╮
│ type     │ stream            │
│ origin   │ nushell           │
│ subtype  │ {record 3 fields} │
│ metadata │ {record 1 field}  │
╰──────────┴───────────────────╯
```

New behavior:


```nushell
ls | sort | describe -d
╭────────┬─────────────────╮
│ type   │ list            │
│ length │ 22              │
│ values │ [table 22 rows] │
╰────────┴─────────────────╯
```

```nushell
$ ls | sort-by name | describe -d
╭────────┬─────────────────╮
│ type   │ list            │
│ length │ 22              │
│ values │ [table 22 rows] │
╰────────┴─────────────────╯
```

</details>

- `sort` now errors when nothing is piped in (`sort-by` already did
this)

# Tests + Formatting

I added lots of unit tests on the new sort implementation to enforce new
sort behaviors and prevent regressions.

# After Submitting

See [docs PR](https://github.com/nushell/nushell.github.io/pull/1568),
which is ~2/3 finished.

---------

Co-authored-by: NotTheDr01ds <32344964+NotTheDr01ds@users.noreply.github.com>
Co-authored-by: Ian Manske <ian.manske@pm.me>
2024-10-09 19:18:16 -07:00
Piepmatz
f0c83a4459
Replace raw usize IDs with new types (#13832)
# Description

In this PR I replaced most of the raw usize IDs with
[newtypes](https://doc.rust-lang.org/rust-by-example/generics/new_types.html).
Some other IDs already started using new types and in this PR I did not
want to touch them. To make the implementation less repetitive, I made
use of a generic `Id<T>` with marker structs. If this lands I would try
to move make other IDs also in this pattern.

Also at some places I needed to use `cast`, I'm not sure if the type was
incorrect and therefore casting not needed or if actually different ID
types intermingle sometimes.

# User-Facing Changes

Probably few, if you got a `DeclId` via a function and placed it later
again it will still work.
2024-09-30 13:20:15 +02:00
Ian Manske
abd230e12e
Use IntoValue in config code (#13751)
# Description

Cleans up and refactors the config code using the `IntoValue` macro.
Shoutout to @cptpiepmatz for making the macro!

# User-Facing Changes

Should be none.

# After Submitting

Somehow refactor the reverse transformation.
2024-09-05 09:44:23 +02:00
Stefan Holderbach
3ab9f0b90a
Fix bugs and UB in bit shifting ops (#13663)
# Description
Fixes #11267

Shifting by a `shift >= num_bits` is undefined in the underlying
operation. Previously we also had an overflow on negative shifts for the
operators `bit-shl` and `bit-shr`
Furthermore I found a severe bug in the implementation of shifting of
`binary` data with the commands `bits shl` and `bits shr`, this
categorically produced incorrect results with shifts that were not
`shift % 4 == 0`. `bits shr` also was able to produce outputs with
different size to the input if the shift was exceeding the length of the
input data by more than a byte.

# User-Facing Changes
It is now an error trying to shift by more than the available bits with:
- `bit-shl` operator
- `bit-shr` operator
- command `bits shl`
- command `bits shr`

# Tests + Formatting
Added testing for all relevant cases
2024-08-22 11:54:27 +02:00
Stefan Holderbach
63f00e78d1
Lift SharedCow::to_mut out of if let branches (#13524)
In some `if let`s we ran the `SharedCow::to_mut` for the test and to get
access to a mutable reference in the happy path. Internally
`Arc::into_mut` has to read atomics and if necessary clone.
For else branches, where we still want to modify the record we
previously called this again (not just in rust, confirmed in the asm).

This would have introduced a `call` instruction and its cost (even if it
would be guaranteed to take the short path in `Arc::into_mut`).
Lifting it get's rid of this.
2024-08-03 00:26:48 +02:00
Ian Manske
399a7c8836
Add and use new Signals struct (#13314)
# Description
This PR introduces a new `Signals` struct to replace our adhoc passing
around of `ctrlc: Option<Arc<AtomicBool>>`. Doing so has a few benefits:
- We can better enforce when/where resetting or triggering an interrupt
is allowed.
- Consolidates `nu_utils::ctrl_c::was_pressed` and other ad-hoc
re-implementations into a single place: `Signals::check`.
- This allows us to add other types of signals later if we want. E.g.,
exiting or suspension.
- Similarly, we can more easily change the underlying implementation if
we need to in the future.
- Places that used to have a `ctrlc` of `None` now use
`Signals::empty()`, so we can double check these usages for correctness
in the future.
2024-07-07 22:29:01 +00:00
Piepmatz
b79a2255d2
Add derive macros for FromValue and IntoValue to ease the use of Values in Rust code (#13031)
# Description
After discussing with @sholderbach the cumbersome usage of
`nu_protocol::Value` in Rust, I created a derive macro to simplify it.
I’ve added a new crate called `nu-derive-value`, which includes two
macros, `IntoValue` and `FromValue`. These are re-exported in
`nu-protocol` and should be encouraged to be used via that re-export.

The macros ensure that all types can easily convert from and into
`Value`. For example, as a plugin author, you can define your plugin
configuration using a Rust struct and easily convert it using
`FromValue`. This makes plugin configuration less of a hassle.

I introduced the `IntoValue` trait for a standardized approach to
converting values into `Value` (and a fallible variant `TryIntoValue`).
This trait could potentially replace existing `into_value` methods.
Along with this, I've implemented `FromValue` for several standard types
and refined other implementations to use blanket implementations where
applicable.

I made these design choices with input from @devyn.

There are more improvements possible, but this is a solid start and the
PR is already quite substantial.

# User-Facing Changes

For `nu-protocol` users, these changes simplify the handling of
`Value`s. There are no changes for end-users of nushell itself.

# Tests + Formatting
Documenting the macros itself is not really possible, as they cannot
really reference any other types since they are the root of the
dependency graph. The standard library has the same problem
([std::Debug](https://doc.rust-lang.org/stable/std/fmt/derive.Debug.html)).
However I documented the `FromValue` and `IntoValue` traits completely.

For testing, I made of use `proc-macro2` in the derive macro code. This
would allow testing the generated source code. Instead I just tested
that the derived functionality is correct. This is done in
`nu_protocol::value::test_derive`, as a consumer of `nu-derive-value`
needs to do the testing of the macro usage. I think that these tests
should provide a stable baseline so that users can be sure that the impl
works.

# After Submitting
With these macros available, we can probably use them in some examples
for plugins to showcase the use of them.
2024-06-17 16:05:11 -07:00
Stefan Holderbach
ba6f38510c
Shrink Value by boxing Range/Closure (#12784)
# Description
On 64-bit platforms the current size of `Value` is 56 bytes. The
limiting variants were `Closure` and `Range`. Boxing the two reduces the
size of Value to 48 bytes. This is the minimal size possible with our
current 16-byte `Span` and any 24-byte `Vec` container which we use in
several variants. (Note the extra full 8-bytes necessary for the
discriminant or other smaller values due to the 8-byte alignment of
`usize`)

This is leads to a size reduction of ~15% for `Value` and should overall
be beneficial as both `Range` and `Closure` are rarely used compared to
the primitive types or even our general container types.

# User-Facing Changes
Less memory used, potential runtime benefits.

(Too late in the evening to run the benchmarks myself right now)
2024-05-09 08:10:58 +08:00
Ian Manske
847646e44e
Remove lazy records (#12682)
# Description
Removes lazy records from the language, following from the reasons
outlined in #12622. Namely, this should make semantics more clear and
will eliminate concerns regarding maintainability.

# User-Facing Changes
- Breaking change: `lazy make` is removed.
- Breaking change: `describe --collect-lazyrecords` flag is removed.
- `sys` and `debug info` now return regular records.

# After Submitting
- Update nushell book if necessary.
- Explore new `sys` and `debug info` APIs to prevent them from taking
too long (e.g., subcommands or taking an optional column/cell-path
argument).
2024-05-03 08:36:10 +08:00
Stefan Holderbach
b88d8726d0
Rework for new clippy lints (#12736)
- **Clippy lint `assigning_clones`**
- **Clippy lint `legacy_numeric_constants`**
- **`clippy::float_equality_without_abs`**
- **`nu-table`: clippy::zero_repeat_side_effects**

---------

Co-authored-by: Ian Manske <ian.manske@pm.me>
2024-05-02 19:29:03 +02:00
Ian Manske
9996e4a1f8
Shrink the size of Expr (#12610)
# Description
Continuing from #12568, this PR further reduces the size of `Expr` from
64 to 40 bytes. It also reduces `Expression` from 128 to 96 bytes and
`Type` from 32 to 24 bytes.

This was accomplished by:
- for `Expr` with multiple fields (e.g., `Expr::Thing(A, B, C)`),
merging the fields into new AST struct types and then boxing this struct
(e.g. `Expr::Thing(Box<ABC>)`).
- replacing `Vec<T>` with `Box<[T]>` in multiple places. `Expr`s and
`Expression`s should rarely be mutated, if at all, so this optimization
makes sense.

By reducing the size of these types, I didn't notice a large performance
improvement (at least compared to #12568). But this PR does reduce the
memory usage of nushell. My config is somewhat light so I only noticed a
difference of 1.4MiB (38.9MiB vs 37.5MiB).

---------

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2024-04-24 15:46:35 +00:00
Ian Manske
3b1d405b96
Remove the Value::Block case (#12582)
# Description
`Value` describes the types of first-class values that users and scripts
can create, manipulate, pass around, and store. However, `Block`s are
not first-class values in the language, so this PR removes it from
`Value`. This removes some unnecessary code, and this change should be
invisible to the user except for the change to `scope modules` described
below.

# User-Facing Changes
Breaking change: the output of `scope modules` was changed so that
`env_block` is now `has_env_block` which is a boolean value instead of a
`Block`.

# After Submitting
Update the language guide possibly.
2024-04-21 07:03:33 +02:00
Devyn Cairns
2ae9ad8676
Copy-on-write for record values (#12305)
# Description
This adds a `SharedCow` type as a transparent copy-on-write pointer that
clones to unique on mutate.

As an initial test, the `Record` within `Value::Record` is shared.

There are some pretty big wins for performance. I'll post benchmark
results in a comment. The biggest winner is nested access, as that would
have cloned the records for each cell path follow before and it doesn't
have to anymore.

The reusability of the `SharedCow` type is nice and I think it could be
used to clean up the previous work I did with `Arc` in `EngineState`.
It's meant to be a mostly transparent clone-on-write that just clones on
`.to_mut()` or `.into_owned()` if there are actually multiple
references, but avoids cloning if the reference is unique.

# User-Facing Changes
- `Value::Record` field is a different type (plugin authors)

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

# After Submitting
- [ ] use for `EngineState`
- [ ] use for `Value::List`
2024-04-14 01:42:03 +00:00
Ian Manske
7a7d43344e
Range refactor (#12405)
# Description
Currently, `Range` is a struct with a `from`, `to`, and `incr` field,
which are all type `Value`. This PR changes `Range` to be an enum over
`IntRange` and `FloatRange` for better type safety / stronger compile
time guarantees.

Fixes: #11778 Fixes: #11777 Fixes: #11776 Fixes: #11775 Fixes: #11774
Fixes: #11773 Fixes: #11769.

# User-Facing Changes
Hopefully none, besides bug fixes.

Although, the `serde` representation might have changed.
2024-04-06 09:04:56 -05:00
Devyn Cairns
2562e306b6
Improve handling of custom values in plugin examples (#12409)
# Description
Requested by @ayax79. This makes the custom value behavior more correct,
by calling the methods on the plugin to handle the custom values in
examples rather than the methods on the custom values themselves. This
helps for handle-type custom values (like what he's doing with
dataframes).

- Equality checking in `PluginTest::test_examples()` changed to use
`PluginInterface::custom_value_partial_cmp()`
- Base value rendering for `PluginSignature` changed to use
`Plugin::custom_value_to_base_value()`
- Had to be moved closer to `serve_plugin` for this reason, so the test
for writing signatures containing custom values was removed
- That behavior should still be tested to some degree, since if custom
values are not handled, signatures will fail to parse, so all of the
other tests won't work.

# User-Facing Changes

- `Record::sort_cols()` method added to share functionality required by
`PartialCmp`, and it might also be slightly faster
- Otherwise, everything should mostly be the same but better. Plugins
that don't implement special handling for custom values will still work
the same way, because the default implementation is just a pass-through
to the `CustomValue` methods.

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`
2024-04-05 21:57:20 -05:00
Stefan Holderbach
ce581a80a6
Elide clone in V::follow_cell_path for record (#12325)
# Description
This clone is not necessary and tanks the performance of deep nested
access.
As soon as we found the value, we know we discard the old value, so can
`std::mem::take` the inner (`impl Default for Value` to the rescue)

We may be able to further optimize this but not having to clone the
value is vital.
2024-03-30 14:03:31 +01:00
Stefan Holderbach
b19da158d5
Rename Value::CustomValue to Value::Custom (#12309)
# Description
The second `Value` is redundant and will consume five extra bytes on
each transmission of a custom value to/from a plugin.

# User-Facing Changes
This is a breaking change to the plugin protocol.

The [example in the protocol
reference](https://www.nushell.sh/contributor-book/plugin_protocol_reference.html#value)
becomes

```json
{
  "Custom": {
    "val": {
      "type": "PluginCustomValue",
      "name": "database",
      "data": [36, 190, 127, 40, 12, 3, 46, 83],
      "notify_on_drop": true
    },
    "span": {
      "start": 320,
      "end": 340
    }
  }
}
```

instead of 

```json
{
  "CustomValue": {
    ...
  }
}
```


# After Submitting
Update plugin protocol reference
2024-03-27 22:10:56 +01:00
Ian Manske
c747ec75c9
Add command_prelude module (#12291)
# Description
When implementing a `Command`, one must also import all the types
present in the function signatures for `Command`. This makes it so that
we often import the same set of types in each command implementation
file. E.g., something like this:
```rust
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
    record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
    ShellError, Signature, Span, Type, Value,
};
```

This PR adds the `nu_engine::command_prelude` module which contains the
necessary and commonly used types to implement a `Command`:
```rust
// command_prelude.rs
pub use crate::CallExt;
pub use nu_protocol::{
    ast::{Call, CellPath},
    engine::{Command, EngineState, Stack},
    record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, IntoSpanned,
    PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
```

This should reduce the boilerplate needed to implement a command and
also gives us a place to track the breadth of the `Command` API. I tried
to be conservative with what went into the prelude modules, since it
might be hard/annoying to remove items from the prelude in the future.
Let me know if something should be included or excluded.
2024-03-26 21:17:30 +00:00
Filip Andersson
b70766e6f5
Boxes record for smaller Value enum. (#12252)
<!--
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.
-->
Boxes `Record` inside `Value` to reduce memory usage, `Value` goes from
`72` -> `56` bytes after this change.
# 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.
-->
2024-03-26 17:17:44 +02:00
Devyn Cairns
c79c43d2f8
Add test support crate for plugin developers (#12259)
# Description

Adds a `nu-plugin-test-support` crate with an interface that supports
testing plugins.

Unlike in reality, these plugins run in the same process on separate
threads. This will allow
testing aspects of the plugin internal state and handling serialized
plugin custom values easily.
We still serialize their custom values and all of the engine to plugin
logic is still in play, so
from a logical perspective this should still expose any bugs that would
have been caused by that.
The only difference is that it doesn't run in a different process, and
doesn't try to serialize
everything to the final wire format for stdin/stdout.

TODO still:

- [x] Clean up warnings about private types exposed in trait definition
- [x] Automatically deserialize plugin custom values in the result so
they can be inspected
- [x] Automatic plugin examples test function
- [x] Write a bit more documentation
- [x] More tests
- [x] Add MIT License file to new crate

# User-Facing Changes

Plugin developers get a nice way to test their plugins.

# Tests + Formatting
Run the tests with `cargo test -p nu-plugin-test-support --
--show-output` to see some examples of what the failing test output for
examples can look like. I used the `difference` crate (MIT licensed) to
make it look nice.

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

# After Submitting

- [ ] Add a section to the book about testing
- [ ] Test some of the example plugins this way
- [ ] Add example tests to nu_plugin_template so plugin developers have
something to start with
2024-03-23 13:29:54 -05:00
Devyn Cairns
6795ad7e33
Make custom value type handling more consistent (#12230)
[Context on
Discord](https://discord.com/channels/601130461678272522/855947301380947968/1219425984990806207)

# Description

- Rename `CustomValue::value_string()` to `type_name()` to reflect its
usage better.
- Change print behavior to always call `to_base_value()` first, to give
the custom value better control over the output.
- Change `describe --detailed` to show the type name as the subtype,
rather than trying to describe the base value.
- Change custom `Type` to use `type_name()` rather than `typetag_name()`
to make things like `PluginCustomValue` more transparent

One question: should `describe --detailed` still include a description
of the base value somewhere? I'm torn on it, it seems possibly useful
for some things (maybe sqlite databases?), but having `describe -d` not
include the custom type name anywhere felt weird. Another option would
be to add another method to `CustomValue` for info to be displayed in
`describe`, so that it can be more type-specific?

# User-Facing Changes
Everything above has implications for printing and `describe` on custom
values

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`
2024-03-19 11:09:59 +01:00
Devyn Cairns
1cb5221f01
Add Value::recurse_mut() to save duplicated code in PluginCustomValue (#12218)
# Description

We do a lot of visiting contained values in the serialization / validity
functions within `PluginCustomValue` utils. This adds
`Value::recurse_mut()` which wraps up most of that logic into something
that can be reused.
2024-03-16 15:54:42 +01:00
Ian Manske
c950269575
Fix $in value for insert closure (#12209)
# Description
Fixes #12193 where the `$in` value may be null for closures provided to
`insert`.

# User-Facing Changes
The `$in` value will now always be the same as the closure parameter for
`insert`.
2024-03-14 16:43:03 -05:00
Devyn Cairns
73f3c0b60b
Support for all custom value operations on plugin custom values (#12088)
# Description

Adds support for the following operations on plugin custom values, in
addition to `to_base_value` which was already present:

- `follow_path_int()`
- `follow_path_string()`
- `partial_cmp()`
- `operation()`
- `Drop` (notification, if opted into with
`CustomValue::notify_plugin_on_drop`)

There are additionally customizable methods within the `Plugin` and
`StreamingPlugin` traits for implementing these functions in a way that
requires access to the plugin state, as a registered handle model such
as might be used in a dataframes plugin would.

`Value::append` was also changed to handle custom values correctly.

# User-Facing Changes

- Signature of `CustomValue::follow_path_string` and
`CustomValue::follow_path_int` changed to give access to the span of the
custom value itself, useful for some errors.
- Plugins using custom values have to be recompiled because the engine
will try to do custom value operations that aren't supported
- Plugins can do more things 🎉 

# Tests + Formatting
Tests were added for all of the new custom values functionality.

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

# After Submitting
- [ ] Document protocol reference `CustomValueOp` variants:
  - [ ] `FollowPathInt`
  - [ ] `FollowPathString`
  - [ ] `PartialCmp`
  - [ ] `Operation`
  - [ ] `Dropped`
- [ ] Document `notify_on_drop` optional field in `PluginCustomValue`
2024-03-12 10:37:08 +01:00
Stefan Holderbach
f695ba408a
Restructure nu-protocol in more meaningful units (#11917)
This is partially "feng-shui programming" of moving things to new
separate places.

The later commits include "`git blame` tollbooths" by moving out chunks
of code into new files, which requires an extra step to track things
with `git blame`. We can negiotiate if you want to keep particular
things in their original place.

If egregious I tried to add a bit of documentation. If I see something
that is unused/unnecessarily `pub` I will try to remove that.


- Move `nu_protocol::Exportable` to `nu-parser`
- Guess doccomment for `Exportable`
- Move `Unit` enum from `value` to `AST`
- Move engine state `Variable` def into its folder
- Move error-related files in `nu-protocol` subdir
- Move `pipeline_data` module into its own folder
- Move `stream.rs` over into the `pipeline_data` mod
- Move `PipelineMetadata` into its own file
- Doccomment `PipelineMetadata`
- Remove unused `is_leap_year` in `value/mod`
- Note about criminal `type_compatible` helper
- Move duration fmting into new `value/duration.rs`
- Move filesize fmting logic to new `value/filesize`
- Split reexports from standard imports in `value/mod`
- Doccomment trait `CustomValue`
- Polish doccomments and intradoc links
2024-03-10 18:45:45 +01:00