Compare commits

...

96 Commits

Author SHA1 Message Date
b93b80ccaa Remove unnecessary work from ps (#3407)
The ps plugin has an unnecessary call to `std:🧵:sleep(500ms)` that
delays runtime by 500ms. Additionally, there is a call to
`sysinfo::SystemExt::refresh_process`, which is not required as the previous
call to sysinfo::SystemExt::refresh_all handles the "refresh" of all processes
without a need to do this individually. Combining both of these improvements
means that running ps runs nearly instentaneously.

Fixes #3402
2021-05-11 19:59:24 +12:00
JT
48128c9db6 Bump to 0.31.0 (#3405) 2021-05-11 16:44:52 +12:00
6dafaa197d Commands to engine p (#3404)
* Change ansi command

* Change ansi strip command

* Change benchmark to engine-p

* ansi strip removed arg.process()

* benchmark without process args
2021-05-11 16:03:55 +12:00
1634d8e087 add into string (#3403) 2021-05-10 12:58:51 -05:00
7a583083b8 Convert the rest of "random" subcommands to engine-p (#3399)
* Convert "random bool" to engine-p

Also implements FromValue for Tagged<BigDecimal> and Tagged<f64>.

* Convert "random dice" to engine-p

* Convert "random uuid" to engine-p

* Covert "random chars" to engine-p

* Convert "random" command to engine-p
2021-05-10 19:07:57 +12:00
75156ab0c9 engine-p: build-string remove ActionStream (#3394) 2021-05-08 16:59:12 +12:00
9fd6923821 Port random integer & decimal to engine-p + related refactoring (#3393)
* Implement minmax for Range; Simplify range command

* Port random integer to enginep; New FromValue impl

Now, FromValue is implemented for Tagged<Range> to allow extracting args
into this type.

* Make sure range value extraction fails properly

The range endpoint extraction methods now return error instead of
silently clipping the value. This now makes `random integer ..-4` fail
properly since -4 can't be cast as u64.

* Port random decimal to enginep & Refactor

This added a way to interpret Range limits as f64 and a Primitive helper
to get its value as f64.

A side effect of this commit is that it is now possible to specify the
command bounds as true decimals. E.g., `random decimal 0.0..3.14` does
not clip 3.14 to 3.
2021-05-08 07:58:12 +12:00
JT
91a929b2a9 Clippy fixes for new Rust version (#3392) 2021-05-07 07:58:21 +12:00
0f8e31af06 Juicy features cargo installer wrapper (#3388)
* Juicy features cargo installer wrapper

* Remove `--force` parameter
2021-05-06 18:55:47 +12:00
bd71c2f34d #3385: Add ignore-case and duplicated options to uniq command (#3387)
* #3385: Add ignore-case and duplicated options to uniq command

* rustfmt
2021-05-06 12:07:42 +12:00
001123dbd6 Add first prototype of functionality to parse numbers in parantheses (#3209)
* Add first prototype of functionality to parse numbers in parantheses (). Needs testing and more wide-testing+integration

* Fix styling issue

* Try something else by copying existing matching code

* Fix formatting

* Fix the parser to accept numbers in paranthesis. Not really happy with the code, but let's see

* Refactor to only use once the parsing of strings into numbers

* Remove errors that are not used

* Fix formatting

Co-authored-by: Stefan Stanciulescu <contact@stefanstanciulescu.com>
2021-05-06 09:00:55 +12:00
cfee151d4e Dont unwrap rustyline helper in cli (#3382)
If nu fails to load a user config on startup, no helper is set and the
later call to `rl.helper_mut()` will panic. There may be better ways to
handle this long-term, but printing an error about the failure to parse
the config and then starting with default values seems reasonable.
2021-05-04 15:50:38 +12:00
JT
fc59291191 Simplify down to one type of context (#3379)
* Simplify down to one type of context

* More simplification
2021-05-03 11:45:55 +12:00
4fc05cac56 Port range to engine-p (#3377)
* Removes arg serialization
* action stream -> output stream
* uses nu_protocol::Range instead of NumericRange
* random missing newline I found in the code
2021-05-03 07:47:59 +12:00
cc4616f25b added check for endian-ness, added a bytes and skip (#3375) 2021-05-01 15:48:17 -05:00
e82fbb7bcf added ability to change "#" color using header_color (#3374) 2021-05-01 12:30:50 -05:00
8cd639f6a2 add nu-pretty-hex, add into binary, update binaryview (#3370)
* add nu-pretty-hex, add into binary, update binaryview

* updated parameter name, updated examples

* fixed nu-pretty-hex test

* fixed tests again! and added a no color option to pretty-hex
2021-05-01 11:12:25 -05:00
a8f555856a tweaked the error handling to show specific errors (#3367) 2021-04-30 09:24:06 -05:00
3792562046 updated to a quicker levenshtein implementation (#3366) 2021-04-29 07:10:10 -05:00
d05c48a1d7 Fix #3231: Pick up nu-env if cd with shortcuts (#3344)
* Fix autoenv not set when using implied cd

* Fix wrong return value

* Fix windows no value returned err
2021-04-28 17:31:22 +12:00
36cc5eb933 Fix array index out of bounds error in nu_protocol::value::levenshtein_distance() (#3358)
* Fix array index out of bounds error.

Error occured when computing levenshtein_distance of two strings where
str.len() is not equal to str.chars().collect::<Vec<_>>().len()

* Add test for levenshtein_distance
2021-04-28 07:30:32 +12:00
f9f74a0f7d #3298: reduce --numbered bug (#3354) 2021-04-27 19:07:56 +12:00
77f42931ff Fix table-pager feature compilation (#3359)
It compiles and works without errors, but the pager is not asynchronous
anymore (i.e., you need to wait for `seq 1 1000000` to finish before the
pager is displayed).
2021-04-27 19:06:57 +12:00
73f62266c6 allow start to handle urls (#3351) 2021-04-24 09:33:17 -05:00
df2f3d25b0 ichwh removed (#3349)
* ichwh removed

* removed unnecessary into() on PathBuf
2021-04-23 05:14:20 +12:00
JT
599c43ce04 bump to 0.30.1 (#3348) 2021-04-22 21:07:54 +12:00
5c2199e7f4 Move any to enginep style (#3324) 2021-04-22 20:35:45 +12:00
JT
3ad4e0348f Fix external redirect (#3345)
* Fix external redirection

* Fix external redirection
2021-04-22 08:54:34 +12:00
JT
02d5729941 Properly evaluate dynamic blocks (#3339) 2021-04-21 14:31:54 +12:00
JT
ce35689d2e Make finding the path variable more robust (#3336)
* Make finding the path variable more robust

* Update reload_config also
2021-04-21 11:37:10 +12:00
JT
da81e21bf2 Add a sync from the known path to the env (#3335) 2021-04-21 08:22:53 +12:00
3b2ed7631f Path Enhancement Project #2: parse, join and split (#3256)
* Add new path parse subcommand

This includes a slight refactor to all the path subcommand `action()`
functions.

* Remove filestem and extension; Fix example

* Add additional description to path parse

* Put join arg behind flag; Fix missing import (Win)

* Fix error when column path is passed as arg

* Add structured path joining

Structured path is implicitly joined at every patch subcommand call.

* Fix existing path join tests; Fix rustfmt

* Remove redundant 'static lifetime (clippy)

* Add initial impl of path split subcommand

* Add ability to join path from parts

* Fix wrong results in path split examples

* Fix remaining asyncs after engine change

* Do not wrap split path parts into table

When the input is just a list of values, the `path split` command will
split each value directly into the output stream, similar to
`split-row`. Column path--specified values are still wrapped into a
table so they can still be used to replace table fields.

* Join list of values instead of going one-by-one

When `path join` encounters a list of values, it attempts to join them,
instead of going one-by-one like the rest of the path commands. You can
still `each { echo $it | path join }` to join them one-by-one, if the
values are, e.g., tables.

Now, the behavior of `path split` and `path join` should match the
`split-row` and `str collect` counterparts and should hopefully align
better with user's expectations.

* Make sure path join detects structured path

* Fix panic on empty input stream

Also, doesn't collect input into vector unnecessarily.

* Fix path join not appending value

* Remove argument serialization

* Make better errors; Misc refactor

* OsStr -> String encoding is now lossy, instead of throwing an error
* The consequence is action() now always returns Value instead of Result
* Removed redundant handle_value() call in `path join`
* Fix possible incorrect error detection in `path split`
* Applied rustfmt + clippy

* Add more usage, examples & test; Fix type error

The 'parent' column was required to be a path but didn't work with
string.

* Add more help & examples; Maybe fix Windows error

* Refactor operate function

Reducing code repetition

* Review usages and examples

* Add the option to manually specify the extension

* Add more tests; Fix failures on Windows

* Move path commands to engine-p

* Small refactor
2021-04-20 18:45:28 +12:00
1a46e70dfb Use new functions in which (#3310)
* Use new functions in which

* Impl rest_with_minimum and use it

* Use has_flag instead of get_switch
2021-04-20 18:38:36 +12:00
JT
0fc9b6cfa2 Bump to 0.30 (#3333)
* Bump to 0.30

* fix test
2021-04-20 18:34:10 +12:00
JT
61768aa2fd Fix parsing dot dot path (#3331) 2021-04-20 08:18:29 +12:00
ea5bf9db36 add query json plugin for experimentation (#3327)
* add query json plugin for experimentation

* add some error handling

* closer but Kind::Array is still horked

* unravel the table so the output looks right

* clippy

* added the ability to use gjson modifiers
2021-04-19 11:19:06 -05:00
JT
9d24afcfe3 Make nth more stream-able (#3330) 2021-04-19 19:45:12 +12:00
033df9457b Engine-p style in compact (#3325)
* Engine-p style in compact

* Remove unused import

* Use filter in compact, thanks to clippy for spotting it :]
2021-04-19 06:40:29 +12:00
d8e105fe34 Enginep/all (#3312)
* Add iter-extensions

* Move all to enginep style

* Remove iter extensions

* Fix clippy lints

* Add comment and make ? more visible

* Remove try_all

* Remove all because it cant return err
2021-04-19 06:39:33 +12:00
d34068da18 Implementing Nu command guide. (#3326) 2021-04-16 08:11:26 -05:00
611103d211 Fix Running echo .. starts printing integers forever (#3322) 2021-04-16 07:07:06 +12:00
528c1c5fd8 change $scope.variables output to a table (#3323) 2021-04-15 14:02:08 -05:00
JT
f73732bf1e Move to* and from* to engine-p (#3320)
* WIP

* Finish last batch
2021-04-15 19:43:33 +12:00
fd7875e572 sort scope.aliases, commands, variables (#3319) 2021-04-14 18:57:47 -05:00
e8bc319f08 Make sure that scripts can also have custom commands. (#3309)
With the current code it is possible to attach custom commands from
a custom binary, but only for interactive mode. This change makes
it possible to also customize the evaluation context for commands
and scripts.
2021-04-15 06:21:50 +12:00
a92ff57270 Use append_history instead of save_history to preserve existing history (#3314) 2021-04-15 06:20:25 +12:00
004230d02d Deserialization and outputstream math commands (#3315)
* Output error when ls into a file without permission

* math sqrt

* added test to check fails when ls into prohibited dir

* fix lint

* math sqrt with tests and doc

* trigger wasm build

* Update filesystem_shell.rs

* converted math commands to outputstream and new method for arg evaluation

* fmt

* clippy

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2021-04-15 06:18:37 +12:00
a148c640b2 add variables to $scope (#3316) 2021-04-14 09:48:14 -05:00
ea0205f2ff remote --help/-h from $scope.commands display (#3311)
* remote --help/-h from $scope.commands

* change a test
2021-04-14 07:55:58 -05:00
005649b6fc remove dupes in get_commands/get_command_names (#3308) 2021-04-13 09:21:44 -05:00
e09e3b01d6 Fix the auto-conversion regression (#3307) 2021-04-13 14:25:18 +12:00
fc15e0e27d A few optimisations (#3306)
* A few optimisations

* Fix test
2021-04-12 19:47:31 +12:00
b2fe5fabb1 Add commands to scope variable (#3272)
* Add commands to scope variable

* List commands with signature on scope variables

Usage:

```shell
echo $scope.commands | pivot
```

* Run rust formater

* Update variables.rs

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2021-04-12 14:38:47 +12:00
52d69bb021 Fix #3213 Rest arg is not optional (#3303) 2021-04-12 14:37:36 +12:00
5f550a355b Split OutputStream into ActionStream/OutputStream (#3304)
* Split OutputStream into ActionStream/OutputStream

* Fmt

* Missed update

* Cleanup helper names

* Fmt
2021-04-12 14:35:01 +12:00
dbecbdccd4 Fix type error when Value is viewed as file path (#3305)
I believe this should be "path", not "string".
2021-04-12 11:53:31 +12:00
734877338d Remove take_while from internal iterator (#3301) 2021-04-11 18:29:01 +12:00
2e439ca77f Improve range and internal iteration (#3300) 2021-04-11 13:31:08 +12:00
a853880e07 preparing for into subcommands (#3299) 2021-04-10 11:29:11 -05:00
93f3ed98e1 clean up error handling a bit (#3297)
* clean up error handling a bit

* clippy
2021-04-10 10:10:23 -05:00
a131eddf54 move out call info deserializing from str (#3294) 2021-04-09 22:58:18 -05:00
b19a7aa8a6 Disallow rm to trash if built without trash-support enabled. (#3278)
If built without `trash_support`, nu should explicitly reject attempts to use `rm` with the `--trash` option, or with a config file which includes `rm_always_trash = true`.

As of 42fac72, there doesn't seem to be any guard in the `#[cfg(not(feature = "trash-support"))]` block of `filesystem_shell::rm`, leading to the behavior described in #3116, where builds without the trash-support feature will delete things permanently regardless of flags/config options.

This should close #3116
2021-04-10 14:01:21 +12:00
f5aa53c530 Remove length collecting its input (#3292)
* Remove length collecting its input

* Update length.rs
2021-04-10 10:21:51 +12:00
80f5e14512 Fix ansi rgb fg (#3293)
* add term size command

* bug: fix ansi rgb_fg
2021-04-09 15:32:25 -05:00
41390cd963 Simplify the default feature list (#3288) 2021-04-09 13:39:44 -05:00
0b5e131410 Remove x1b, update prompt (#3291)
* add term size command

* remove \x1b and use nu_ansi_term, make prompt with no config prettier
2021-04-09 11:38:56 -05:00
556596bce8 add "into int" behavior (#3279)
* add 'int' command

* create `into int` behavior

- forcibly overwrote prior implementation

```sh
git mv -f  crates/nu-command/src/commands/int_.rs crates/nu-command/src/commands/into_int.rs
```
- picked up prior work, polished and renamed

Co-authored-by: Saeed Rasooli <saeed.gnu@gmail.com>
2021-04-09 09:17:39 -05:00
b791d1ab0d Move from using a Block to an Arc'd Block (#3289) 2021-04-09 20:12:25 +12:00
ac070ae942 Refactor/config commands (#3265)
* Use ctx.configs in all config commands

* Remove all setting/accessing of  vars.("config-path")

* Add tests

* Add comment

* Reload cfg on remove

* Hypocratic ws change

* Use history_path in hist_or_default

* Make clippy happy

* Fix rebase stuff

* Fix clippy lint
2021-04-09 18:03:12 +12:00
111ad868a7 Do not store whitespace entries into history (#3019) (#3286)
Before storing an entry into the history nushell will check if the entry
consists only of whitespaces and if so, it does not store it in the history and
this avoids  newline repetition when user is navigating in the history.
2021-04-09 12:01:31 +12:00
a7274115d0 make Table, Autoview read in memory config. (#3287) 2021-04-08 17:31:19 -05:00
81160bcefb Remove some clones and improve when autoview reads config (#3285) 2021-04-09 07:47:41 +12:00
2880109f31 Runnable contexts move. (#3283)
* Engine extract first steps.

* Don't depend on ShellManager.
2021-04-08 13:51:12 -05:00
09a1f5acb9 Begin migration away from arg serialization (#3281)
* initial implementation

* Move a few commands over to new arg system

* Fix char also
2021-04-08 20:15:36 +12:00
5fcf11fcb0 Fix externals busy waiting (#3280) 2021-04-08 07:25:15 +12:00
42fac722bb Bump to 0.29.2 (#3274)
* Bump to 0.29.2

* Fix test
2021-04-07 08:14:06 +12:00
073e5727c6 Switch to "engine-p" (#3270)
* WIP

* WIP

* first builds

* Tests pass
2021-04-06 11:19:43 -05:00
ad1c4f5e39 [fix] crashing issues when the given timestamp is out of range (#3271)
* add support for timestamp-based time conversion by specifing timezone or 'UTC/Local'

* [fix] fix the wrong test sample

* code formating

* code formating and import missing mod to test

* code formating again

* [fix] it won't crash when given timestamp is too big.

* [fix] code formatting =_=b
2021-04-06 07:22:07 -05:00
dc8a68c98f Bump sysinfo (#3267)
* add term size command

* update to the latest sysinfo
2021-04-05 14:36:19 -05:00
e5621dea58 Remove yr and mon (#3262)
* Remove `yr` and `mon`

* Remove usage of mon in test

* Fix test
2021-04-05 06:19:33 +12:00
00acf22f5f account for startup commands in the scope. (#3261)
* Revert "Impl one configurable function to run scripts (#3242)"
* pass config startup.
2021-04-04 00:14:58 -05:00
4c09716ad8 add TiB and PiB (#3257) 2021-04-04 12:08:17 +12:00
1c941557c3 Remove unused help shell. Slight cleanup and improvement. (#3258) 2021-04-03 18:56:46 -05:00
28e1a7915d Impl one configurable function to run scripts (#3242)
* Impl one func to run scripts

* Add exit_on_err

* Remove run_standalone

* Make the compiler happy :)
2021-04-04 07:31:53 +12:00
4bc9d9fd3b Fix typos and capitalization of "Unicode" (#3234)
* Capitalize "Unicode"

* Fix several typos

* Fix mixed whitespace in nu-parser's tests
2021-04-04 07:14:07 +12:00
e278ca61d1 commands: any? all? (#3252)
* commands: any? all?

We can check if `any` (or `all`) rows of tables match predicates.

Small `all?` example: Given the following table with `services` running:

```
> echo [[status]; [UP] [UP]]
───┬────────
 # │ status
───┼────────
 0 │ UP
 1 │ UP
───┴────────
```

We can ask if all services are UP, like so:

```
> echo [[status]; [UP] [UP]] | all? status == UP
true
```

* Fix any? signature.
2021-04-03 13:40:54 -05:00
2146ede15d Parse decimal units (#3243)
* parse decimal units

* linting

* stop clippy complaining

* Added tests to parsing decimals

* Fixed bug

* Fixed testing and add more
2021-04-03 21:06:13 +13:00
e737222a5d fix lack of auto-suggestion for aliases (#3249) 2021-04-03 10:39:30 +13:00
f03f1949bf Logs and tests (#3247)
* Add command name to err

* Add var name to error message

* Add test for def comment in test
2021-04-01 17:09:33 -05:00
0fe6c7c558 Mathsqrt (#3239)
* Output error when ls into a file without permission

* math sqrt

* added test to check fails when ls into prohibited dir

* fix lint

* math sqrt with tests and doc

* trigger wasm build

* Update filesystem_shell.rs

* always forgetting the linting

* fix clippy complaining

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2021-04-01 16:26:05 -05:00
b13202bbfc Fix #3244: Add tag reloaded frame (#3246) 2021-04-01 16:25:26 -05:00
90fae903ce Fixes error when trying to delete a FIFO (#3235)
* Output error when ls into a file without permission

* added test to check fails when ls into prohibited dir

* fix lint

* trigger wasm build

* be able to remove fifos

* Update filesystem_shell.rs

* I thought windows had fifos

* fixed unix and windows conditional compilation

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2021-04-01 06:10:40 +13:00
06b154f4b2 Bump to 0.29.1 (#3232)
* Bump to 0.29.1

* fix test
2021-03-31 20:13:40 +13:00
419a0665c8 Output error when ls into a file without permission (#3218)
* Output error when ls into a file without permission

* added test to check fails when ls into prohibited dir

* fix lint

* trigger wasm build

* Update filesystem_shell.rs

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2021-03-31 19:52:39 +13:00
387098fc87 Stop nu panicks in math.round on a large decimal value(Most of the time) (#3224)
* Stop crashing when dealing with large numbers in math round

* Fix formatting

* add tests

* just to trigger wasm build

* trigger wasm build
2021-03-31 19:01:39 +13:00
c42b588782 Refactor nu-cli/env* (#3041)
* Revert "History, more test coverage improvements, and refactorings. (#3217)"

This reverts commit 8fc8fc89aa.

* Add tests

* Refactor .nu-env

* Change logic of Config write to logic of read()

* Fix reload always appends to old vars

* Fix reload always takes last_modified of global config

* Add reload_config in evaluation context

* Reload config after writing to it in cfg set / cfg set_into

* Add --no-history to cli options

* Use --no-history in tests

* Add comment about maybe_print_errors

* Get ctrl_exit var from context.global_config

* Use context.global_config in command "config"

* Add Readme in engine how env vars are now handled

* Update docs from autoenv command

* Move history_path from engine to nu_data

* Move load history out of if

* No let before return

* Add import for indexmap
2021-03-31 18:52:34 +13:00
423 changed files with 12352 additions and 8871 deletions

322
Cargo.lock generated
View File

@ -184,13 +184,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "async-attributes"
version = "1.1.2"
name = "as-slice"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0"
dependencies = [
"quote 1.0.9",
"syn 1.0.62",
"generic-array 0.12.4",
"generic-array 0.13.3",
"generic-array 0.14.4",
"stable_deref_trait",
]
[[package]]
@ -281,7 +283,7 @@ checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -290,7 +292,6 @@ version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9f06685bad74e0570f5213741bea82158279a4103d988e57bfada11ad230341"
dependencies = [
"async-attributes",
"async-channel",
"async-global-executor",
"async-io",
@ -326,7 +327,7 @@ checksum = "36ea56748e10732c49404c153638a15ec3d6211ec5ff35d9bb20e13b93576adf"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -564,9 +565,9 @@ checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
[[package]]
name = "byte-unit"
version = "4.0.9"
version = "4.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c8758c32833faaae35b24a73d332e62d0528e89076ae841c63940e37008b153"
checksum = "b9520900471c3a9bbcfe0fd4c7b6bcfeff41b20a76cf91c59b7474b09be1ee27"
dependencies = [
"utf8-width",
]
@ -579,9 +580,9 @@ checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58"
[[package]]
name = "byteorder"
version = "1.4.2"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
@ -780,6 +781,12 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "common-path"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101"
[[package]]
name = "concurrent-queue"
version = "1.2.2"
@ -1104,7 +1111,7 @@ dependencies = [
"proc-macro2",
"quote 1.0.9",
"smallvec 1.6.1",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -1114,7 +1121,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e"
dependencies = [
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -1146,7 +1153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19"
dependencies = [
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -1254,7 +1261,7 @@ checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -1276,7 +1283,7 @@ checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -1576,7 +1583,7 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
"synstructure",
]
@ -1855,7 +1862,7 @@ dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -1972,6 +1979,24 @@ dependencies = [
"typenum",
]
[[package]]
name = "generic-array"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
dependencies = [
"typenum",
]
[[package]]
name = "generic-array"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309"
dependencies = [
"typenum",
]
[[package]]
name = "generic-array"
version = "0.14.4"
@ -2013,7 +2038,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -2045,6 +2070,12 @@ dependencies = [
"url",
]
[[package]]
name = "gjson"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864178e25a00c41404f1728997c9a21a7b746be1faefe6ce4dc41eb48bb4234f"
[[package]]
name = "glob"
version = "0.3.0"
@ -2107,6 +2138,15 @@ dependencies = [
"regex 1.4.3",
]
[[package]]
name = "hash32"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc"
dependencies = [
"byteorder",
]
[[package]]
name = "hashbrown"
version = "0.9.1"
@ -2125,6 +2165,18 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "heapless"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422"
dependencies = [
"as-slice",
"generic-array 0.14.4",
"hash32",
"stable_deref_trait",
]
[[package]]
name = "heck"
version = "0.3.2"
@ -2206,7 +2258,7 @@ dependencies = [
"markup5ever",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -2382,18 +2434,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "ichwh"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea685d38f1becb4f0a04e6cbff9256c6c2cd5e5905563b251401d1c13d12c654"
dependencies = [
"async-std",
"cfg-if 0.1.10",
"futures 0.3.13",
"thiserror",
]
[[package]]
name = "idna"
version = "0.2.2"
@ -3075,7 +3115,7 @@ dependencies = [
[[package]]
name = "nu"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"clap",
"ctrlc",
@ -3104,6 +3144,7 @@ dependencies = [
"nu_plugin_match",
"nu_plugin_post",
"nu_plugin_ps",
"nu_plugin_query_json",
"nu_plugin_s3",
"nu_plugin_selector",
"nu_plugin_start",
@ -3120,6 +3161,15 @@ dependencies = [
[[package]]
name = "nu-ansi-term"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bd69a141e8fdfa5ac882d8b816db2b9ad138ef7e3baa7cb753a9b3789aa8c7e"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "nu-ansi-term"
version = "0.31.0"
dependencies = [
"doc-comment",
"regex 1.4.3",
@ -3130,7 +3180,7 @@ dependencies = [
[[package]]
name = "nu-cli"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"Inflector",
"arboard",
@ -3163,13 +3213,12 @@ dependencies = [
"glob",
"htmlescape",
"ical",
"ichwh",
"indexmap",
"itertools",
"lazy_static 1.4.0",
"log 0.4.14",
"meval",
"nu-ansi-term",
"nu-ansi-term 0.31.0",
"nu-command",
"nu-data",
"nu-engine",
@ -3177,6 +3226,7 @@ dependencies = [
"nu-json",
"nu-parser",
"nu-plugin",
"nu-pretty-hex",
"nu-protocol",
"nu-source",
"nu-stream",
@ -3188,7 +3238,6 @@ dependencies = [
"num-traits 0.2.14",
"parking_lot 0.11.1",
"pin-utils",
"pretty-hex",
"ptree",
"query_interface",
"quick-xml 0.21.0",
@ -3231,12 +3280,10 @@ dependencies = [
[[package]]
name = "nu-command"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"Inflector",
"arboard",
"async-recursion",
"async-trait",
"base64 0.13.0",
"bigdecimal",
"byte-unit",
@ -3259,14 +3306,11 @@ dependencies = [
"filesize",
"fs_extra",
"futures 0.3.13",
"futures-util",
"futures_codec",
"getset",
"glob",
"hamcrest2",
"htmlescape",
"ical",
"ichwh",
"indexmap",
"itertools",
"lazy_static 1.4.0",
@ -3274,13 +3318,14 @@ dependencies = [
"md5 0.7.0",
"meval",
"minus",
"nu-ansi-term",
"nu-ansi-term 0.31.0",
"nu-data",
"nu-engine",
"nu-errors",
"nu-json",
"nu-parser",
"nu-plugin",
"nu-pretty-hex",
"nu-protocol",
"nu-source",
"nu-stream",
@ -3292,7 +3337,6 @@ dependencies = [
"num-traits 0.2.14",
"parking_lot 0.11.1",
"pin-utils",
"pretty-hex",
"ptree",
"query_interface",
"quick-xml 0.21.0",
@ -3335,18 +3379,19 @@ dependencies = [
[[package]]
name = "nu-data"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"bigdecimal",
"byte-unit",
"chrono",
"common-path",
"derive-new",
"directories-next",
"dirs-next",
"getset",
"indexmap",
"log 0.4.14",
"nu-ansi-term",
"nu-ansi-term 0.31.0",
"nu-errors",
"nu-protocol",
"nu-source",
@ -3358,21 +3403,26 @@ dependencies = [
"num-traits 0.2.14",
"query_interface",
"serde 1.0.124",
"sha2 0.9.3",
"toml",
"users",
]
[[package]]
name = "nu-engine"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"ansi_term 0.12.1",
"async-recursion",
"async-trait",
"bigdecimal",
"bytes 0.5.6",
"chrono",
"codespan-reporting",
"derive-new",
"dirs-next",
"dunce",
"dyn-clone",
"encoding_rs",
"filesize",
"fs_extra",
@ -3385,7 +3435,7 @@ dependencies = [
"indexmap",
"itertools",
"log 0.4.14",
"nu-ansi-term",
"nu-ansi-term 0.31.0",
"nu-data",
"nu-errors",
"nu-parser",
@ -3395,6 +3445,9 @@ dependencies = [
"nu-stream",
"nu-test-support",
"nu-value-ext",
"num-bigint 0.3.2",
"num-format",
"num-traits 0.2.14",
"parking_lot 0.11.1",
"rayon",
"serde 1.0.124",
@ -3410,14 +3463,14 @@ dependencies = [
[[package]]
name = "nu-errors"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"bigdecimal",
"codespan-reporting",
"derive-new",
"getset",
"glob",
"nu-ansi-term",
"nu-ansi-term 0.31.0",
"nu-source",
"num-bigint 0.3.2",
"num-traits 0.2.14",
@ -3429,7 +3482,7 @@ dependencies = [
[[package]]
name = "nu-json"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"dunce",
"lazy_static 1.4.0",
@ -3443,7 +3496,7 @@ dependencies = [
[[package]]
name = "nu-parser"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"bigdecimal",
"codespan-reporting",
@ -3466,7 +3519,7 @@ dependencies = [
[[package]]
name = "nu-plugin"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"bigdecimal",
"indexmap",
@ -3480,9 +3533,18 @@ dependencies = [
"serde_json",
]
[[package]]
name = "nu-pretty-hex"
version = "0.31.0"
dependencies = [
"heapless",
"nu-ansi-term 0.29.0",
"rand 0.8.3",
]
[[package]]
name = "nu-protocol"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"bigdecimal",
"byte-unit",
@ -3505,7 +3567,7 @@ dependencies = [
[[package]]
name = "nu-source"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"derive-new",
"getset",
@ -3516,7 +3578,7 @@ dependencies = [
[[package]]
name = "nu-stream"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"futures 0.3.13",
"nu-errors",
@ -3526,16 +3588,16 @@ dependencies = [
[[package]]
name = "nu-table"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"nu-ansi-term",
"nu-ansi-term 0.31.0",
"regex 1.4.3",
"unicode-width",
]
[[package]]
name = "nu-test-support"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"bigdecimal",
"chrono",
@ -3554,7 +3616,7 @@ dependencies = [
[[package]]
name = "nu-value-ext"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"indexmap",
"itertools",
@ -3566,23 +3628,23 @@ dependencies = [
[[package]]
name = "nu_plugin_binaryview"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"crossterm 0.19.0",
"image 0.22.5",
"neso",
"nu-ansi-term",
"nu-ansi-term 0.31.0",
"nu-errors",
"nu-plugin",
"nu-pretty-hex",
"nu-protocol",
"nu-source",
"pretty-hex",
"rawkey",
]
[[package]]
name = "nu_plugin_chart"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"crossterm 0.19.0",
"nu-cli",
@ -3597,7 +3659,7 @@ dependencies = [
[[package]]
name = "nu_plugin_fetch"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"base64 0.13.0",
"futures 0.3.13",
@ -3612,7 +3674,7 @@ dependencies = [
[[package]]
name = "nu_plugin_from_bson"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"bigdecimal",
"bson",
@ -3626,7 +3688,7 @@ dependencies = [
[[package]]
name = "nu_plugin_from_sqlite"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"bigdecimal",
"nu-errors",
@ -3641,7 +3703,7 @@ dependencies = [
[[package]]
name = "nu_plugin_inc"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"nu-errors",
"nu-plugin",
@ -3654,7 +3716,7 @@ dependencies = [
[[package]]
name = "nu_plugin_match"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"nu-errors",
"nu-plugin",
@ -3665,7 +3727,7 @@ dependencies = [
[[package]]
name = "nu_plugin_post"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"base64 0.13.0",
"futures 0.3.13",
@ -3681,7 +3743,7 @@ dependencies = [
[[package]]
name = "nu_plugin_ps"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"futures 0.3.13",
"futures-timer",
@ -3693,9 +3755,21 @@ dependencies = [
"sysinfo",
]
[[package]]
name = "nu_plugin_query_json"
version = "0.31.0"
dependencies = [
"gjson",
"nu-errors",
"nu-plugin",
"nu-protocol",
"nu-source",
"nu-test-support",
]
[[package]]
name = "nu_plugin_s3"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"futures 0.3.13",
"nu-errors",
@ -3707,7 +3781,7 @@ dependencies = [
[[package]]
name = "nu_plugin_selector"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"nipper",
"nu-errors",
@ -3719,7 +3793,7 @@ dependencies = [
[[package]]
name = "nu_plugin_start"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"glob",
"nu-errors",
@ -3728,11 +3802,12 @@ dependencies = [
"nu-source",
"open",
"url",
"webbrowser",
]
[[package]]
name = "nu_plugin_sys"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"futures 0.3.13",
"futures-util",
@ -3746,10 +3821,10 @@ dependencies = [
[[package]]
name = "nu_plugin_textview"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"bat",
"nu-ansi-term",
"nu-ansi-term 0.31.0",
"nu-data",
"nu-errors",
"nu-plugin",
@ -3761,7 +3836,7 @@ dependencies = [
[[package]]
name = "nu_plugin_to_bson"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"bson",
"nu-errors",
@ -3774,7 +3849,7 @@ dependencies = [
[[package]]
name = "nu_plugin_to_sqlite"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"hex 0.4.3",
"nu-errors",
@ -3789,7 +3864,7 @@ dependencies = [
[[package]]
name = "nu_plugin_tree"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"derive-new",
"nu-errors",
@ -3801,7 +3876,7 @@ dependencies = [
[[package]]
name = "nu_plugin_xpath"
version = "0.29.0"
version = "0.31.0"
dependencies = [
"bigdecimal",
"indexmap",
@ -4015,9 +4090,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "open"
version = "1.5.1"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2033f93630dd4b04768ecf5e16bcd3002a89e1e1dbef375bf290dd67e2b7a4d"
checksum = "a7e9f1bdf15cd1f5a00cc9002a733a6ee6d0ff562491852d59652471c4a389f7"
dependencies = [
"which",
"winapi 0.3.9",
@ -4219,7 +4294,7 @@ dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -4257,7 +4332,7 @@ checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -4268,7 +4343,7 @@ checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -4378,12 +4453,6 @@ dependencies = [
"typed-arena",
]
[[package]]
name = "pretty-hex"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131"
[[package]]
name = "pretty_env_logger"
version = "0.4.0"
@ -4403,7 +4472,7 @@ dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
"version_check",
]
@ -4517,7 +4586,7 @@ checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -4939,7 +5008,7 @@ dependencies = [
"proc-macro2",
"quote 1.0.9",
"rust-embed-utils",
"syn 1.0.62",
"syn 1.0.63",
"walkdir",
]
@ -5235,7 +5304,7 @@ checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -5346,7 +5415,7 @@ checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -5511,7 +5580,7 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -5585,7 +5654,7 @@ dependencies = [
"quote 1.0.9",
"serde 1.0.124",
"serde_derive",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -5601,7 +5670,7 @@ dependencies = [
"serde_derive",
"serde_json",
"sha1 0.6.0",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -5740,9 +5809,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.62"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512"
checksum = "8fd9bc7ccc2688b3344c2f48b9b546648b25ce0b20fc717ee7fa7981a8ca9717"
dependencies = [
"proc-macro2",
"quote 1.0.9",
@ -5766,7 +5835,7 @@ checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
"unicode-xid 0.2.1",
]
@ -5795,9 +5864,9 @@ dependencies = [
[[package]]
name = "sysinfo"
version = "0.16.4"
version = "0.16.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c280c91abd1aed2e36be1bc8f56fbc7a2acbb2b58fbcac9641510179cc72dd9"
checksum = "567e910ef0207be81a4e1bb0491e9a8d9866cf45b20fe1a52c03d347da9ea51b"
dependencies = [
"cfg-if 1.0.0",
"core-foundation-sys",
@ -5913,7 +5982,7 @@ checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -5991,7 +6060,7 @@ dependencies = [
"proc-macro2",
"quote 1.0.9",
"standback",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -6151,7 +6220,7 @@ checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -6340,13 +6409,13 @@ dependencies = [
[[package]]
name = "tracing-attributes"
version = "0.1.13"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07"
checksum = "41768be5b9f3489491825f56f01f25290aa1d3e7cc97e182d4d34360493ba6fa"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
]
[[package]]
@ -6686,7 +6755,7 @@ dependencies = [
"log 0.4.14",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
"wasm-bindgen-shared",
]
@ -6736,7 +6805,7 @@ checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.62",
"syn 1.0.63",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -6757,6 +6826,17 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webbrowser"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecad156490d6b620308ed411cfee90d280b3cbd13e189ea0d3fada8acc89158a"
dependencies = [
"web-sys",
"widestring",
"winapi 0.3.9",
]
[[package]]
name = "weezl"
version = "0.1.4"
@ -6774,14 +6854,20 @@ dependencies = [
[[package]]
name = "which"
version = "4.0.2"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87c14ef7e1b8b8ecfc75d5eca37949410046e66f15d185c01d70824f1f8111ef"
checksum = "b55551e42cbdf2ce2bedd2203d0cc08dba002c27510f86dab6d0ce304cba3dfe"
dependencies = [
"either",
"libc",
"thiserror",
]
[[package]]
name = "widestring"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
[[package]]
name = "wild"
version = "2.0.4"

View File

@ -10,7 +10,7 @@ license = "MIT"
name = "nu"
readme = "README.md"
repository = "https://github.com/nushell/nushell"
version = "0.29.0"
version = "0.31.0"
[workspace]
members = ["crates/*/"]
@ -18,35 +18,36 @@ members = ["crates/*/"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-cli = { version = "0.29.0", path = "./crates/nu-cli", default-features = false }
nu-command = { version = "0.29.0", path = "./crates/nu-command" }
nu-data = { version = "0.29.0", path = "./crates/nu-data" }
nu-engine = { version = "0.29.0", path = "./crates/nu-engine" }
nu-errors = { version = "0.29.0", path = "./crates/nu-errors" }
nu-parser = { version = "0.29.0", path = "./crates/nu-parser" }
nu-plugin = { version = "0.29.0", path = "./crates/nu-plugin" }
nu-protocol = { version = "0.29.0", path = "./crates/nu-protocol" }
nu-source = { version = "0.29.0", path = "./crates/nu-source" }
nu-value-ext = { version = "0.29.0", path = "./crates/nu-value-ext" }
nu-cli = { version = "0.31.0", path = "./crates/nu-cli", default-features = false }
nu-command = { version = "0.31.0", path = "./crates/nu-command" }
nu-data = { version = "0.31.0", path = "./crates/nu-data" }
nu-engine = { version = "0.31.0", path = "./crates/nu-engine" }
nu-errors = { version = "0.31.0", path = "./crates/nu-errors" }
nu-parser = { version = "0.31.0", path = "./crates/nu-parser" }
nu-plugin = { version = "0.31.0", path = "./crates/nu-plugin" }
nu-protocol = { version = "0.31.0", path = "./crates/nu-protocol" }
nu-source = { version = "0.31.0", path = "./crates/nu-source" }
nu-value-ext = { version = "0.31.0", path = "./crates/nu-value-ext" }
nu_plugin_binaryview = { version = "0.29.0", path = "./crates/nu_plugin_binaryview", optional = true }
nu_plugin_chart = { version = "0.29.0", path = "./crates/nu_plugin_chart", optional = true }
nu_plugin_fetch = { version = "0.29.0", path = "./crates/nu_plugin_fetch", optional = true }
nu_plugin_from_bson = { version = "0.29.0", path = "./crates/nu_plugin_from_bson", optional = true }
nu_plugin_from_sqlite = { version = "0.29.0", path = "./crates/nu_plugin_from_sqlite", optional = true }
nu_plugin_inc = { version = "0.29.0", path = "./crates/nu_plugin_inc", optional = true }
nu_plugin_match = { version = "0.29.0", path = "./crates/nu_plugin_match", optional = true }
nu_plugin_post = { version = "0.29.0", path = "./crates/nu_plugin_post", optional = true }
nu_plugin_ps = { version = "0.29.0", path = "./crates/nu_plugin_ps", optional = true }
nu_plugin_s3 = { version = "0.29.0", path = "./crates/nu_plugin_s3", optional = true }
nu_plugin_selector = { version = "0.29.0", path = "./crates/nu_plugin_selector", optional = true }
nu_plugin_start = { version = "0.29.0", path = "./crates/nu_plugin_start", optional = true }
nu_plugin_sys = { version = "0.29.0", path = "./crates/nu_plugin_sys", optional = true }
nu_plugin_textview = { version = "0.29.0", path = "./crates/nu_plugin_textview", optional = true }
nu_plugin_to_bson = { version = "0.29.0", path = "./crates/nu_plugin_to_bson", optional = true }
nu_plugin_to_sqlite = { version = "0.29.0", path = "./crates/nu_plugin_to_sqlite", optional = true }
nu_plugin_tree = { version = "0.29.0", path = "./crates/nu_plugin_tree", optional = true }
nu_plugin_xpath = { version = "0.29.0", path = "./crates/nu_plugin_xpath", optional = true }
nu_plugin_binaryview = { version = "0.31.0", path = "./crates/nu_plugin_binaryview", optional = true }
nu_plugin_chart = { version = "0.31.0", path = "./crates/nu_plugin_chart", optional = true }
nu_plugin_fetch = { version = "0.31.0", path = "./crates/nu_plugin_fetch", optional = true }
nu_plugin_from_bson = { version = "0.31.0", path = "./crates/nu_plugin_from_bson", optional = true }
nu_plugin_from_sqlite = { version = "0.31.0", path = "./crates/nu_plugin_from_sqlite", optional = true }
nu_plugin_inc = { version = "0.31.0", path = "./crates/nu_plugin_inc", optional = true }
nu_plugin_match = { version = "0.31.0", path = "./crates/nu_plugin_match", optional = true }
nu_plugin_post = { version = "0.31.0", path = "./crates/nu_plugin_post", optional = true }
nu_plugin_ps = { version = "0.31.0", path = "./crates/nu_plugin_ps", optional = true }
nu_plugin_query_json = { version = "0.31.0", path = "./crates/nu_plugin_query_json", optional = true }
nu_plugin_s3 = { version = "0.31.0", path = "./crates/nu_plugin_s3", optional = true }
nu_plugin_selector = { version = "0.31.0", path = "./crates/nu_plugin_selector", optional = true }
nu_plugin_start = { version = "0.31.0", path = "./crates/nu_plugin_start", optional = true }
nu_plugin_sys = { version = "0.31.0", path = "./crates/nu_plugin_sys", optional = true }
nu_plugin_textview = { version = "0.31.0", path = "./crates/nu_plugin_textview", optional = true }
nu_plugin_to_bson = { version = "0.31.0", path = "./crates/nu_plugin_to_bson", optional = true }
nu_plugin_to_sqlite = { version = "0.31.0", path = "./crates/nu_plugin_to_sqlite", optional = true }
nu_plugin_tree = { version = "0.31.0", path = "./crates/nu_plugin_tree", optional = true }
nu_plugin_xpath = { version = "0.31.0", path = "./crates/nu_plugin_xpath", optional = true }
# Required to bootstrap the main binary
clap = "2.33.3"
@ -57,7 +58,7 @@ log = "0.4.14"
pretty_env_logger = "0.4.0"
[dev-dependencies]
nu-test-support = { version = "0.29.0", path = "./crates/nu-test-support" }
nu-test-support = { version = "0.31.0", path = "./crates/nu-test-support" }
dunce = "1.0.1"
serial_test = "0.5.1"
hamcrest2 = "0.3.0"
@ -81,9 +82,7 @@ rustyline-support = ["nu-cli/rustyline-support", "nu-command/rustyline-support"]
term-support = ["nu-cli/term", "nu-command/term"]
uuid-support = ["nu-cli/uuid_crate", "nu-command/uuid_crate"]
which-support = [
"nu-cli/ichwh",
"nu-cli/which",
"nu-command/ichwh",
"nu-command/which",
"nu-engine/which",
]
@ -92,14 +91,10 @@ default = [
"nu-cli/shadow-rs",
"sys",
"ps",
"textview",
"inc",
"directories-support",
"ctrlc-support",
"which-support",
"ptree-support",
"term-support",
"uuid-support",
"rustyline-support",
"match",
"post",
@ -111,9 +106,13 @@ stable = ["default"]
extra = [
"default",
"binaryview",
"inc",
"tree",
"ptree-support",
"textview",
"clipboard-cli",
"trash-support",
"uuid-support",
"start",
"bson",
"sqlite",
@ -121,6 +120,7 @@ extra = [
"chart",
"xpath",
"selector",
"query-json",
]
wasi = ["inc", "match", "ptree-support", "match", "tree", "rustyline-support"]
@ -135,13 +135,13 @@ post = ["nu_plugin_post"]
ps = ["nu_plugin_ps"]
sys = ["nu_plugin_sys"]
textview = ["nu_plugin_textview"]
zip-support = ["nu-cli/zip", "nu-command/zip"]
# Extra
binaryview = ["nu_plugin_binaryview"]
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
chart = ["nu_plugin_chart"]
clipboard-cli = ["nu-cli/clipboard-cli", "nu-command/clipboard-cli"]
query-json = ["nu_plugin_query_json"]
s3 = ["nu_plugin_s3"]
selector = ["nu_plugin_selector"]
sqlite = ["nu_plugin_from_sqlite", "nu_plugin_to_sqlite"]
@ -153,6 +153,8 @@ trash-support = [
]
tree = ["nu_plugin_tree"]
xpath = ["nu_plugin_xpath"]
zip-support = ["nu-cli/zip", "nu-command/zip"]
#This is disabled in extra for now
table-pager = ["nu-command/table-pager"]
@ -160,7 +162,8 @@ table-pager = ["nu-command/table-pager"]
#strip = "symbols" #Couldn't get working +nightly
codegen-units = 1 #Reduce parallel codegen units
lto = true #Link Time Optimization
opt-level = 'z' #Optimize for size
# opt-level = 'z' #Optimize for size
# debug = true
# Core plugins that ship with `cargo install nu` by default
# Currently, Cargo limits us to installing only one binary
@ -212,6 +215,11 @@ name = "nu_plugin_extra_tree"
path = "src/plugins/nu_plugin_extra_tree.rs"
required-features = ["tree"]
[[bin]]
name = "nu_plugin_extra_query_json"
path = "src/plugins/nu_plugin_extra_query_json.rs"
required-features = ["query-json"]
[[bin]]
name = "nu_plugin_extra_start"
path = "src/plugins/nu_plugin_extra_start.rs"

View File

@ -9,7 +9,7 @@ description = "Library for ANSI terminal colors and styles (bold, underline)"
edition = "2018"
license = "MIT"
name = "nu-ansi-term"
version = "0.29.0"
version = "0.31.0"
[lib]
doctest = false

View File

@ -1,3 +1,4 @@
#![allow(missing_docs)]
use crate::style::{Color, Style};
use crate::write::AnyWrite;
use std::fmt;

View File

@ -246,7 +246,7 @@ extern crate doc_comment;
#[cfg(test)]
doctest!("../README.md");
mod ansi;
pub mod ansi;
pub use ansi::{Infix, Prefix, Suffix};
mod style;

View File

@ -5,26 +5,27 @@ description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-cli"
version = "0.29.0"
version = "0.31.0"
[lib]
doctest = false
[dependencies]
nu-command = { version = "0.29.0", path = "../nu-command" }
nu-data = { version = "0.29.0", path = "../nu-data" }
nu-engine = { version = "0.29.0", path = "../nu-engine" }
nu-errors = { version = "0.29.0", path = "../nu-errors" }
nu-json = { version = "0.29.0", path = "../nu-json" }
nu-parser = { version = "0.29.0", path = "../nu-parser" }
nu-plugin = { version = "0.29.0", path = "../nu-plugin" }
nu-protocol = { version = "0.29.0", path = "../nu-protocol" }
nu-source = { version = "0.29.0", path = "../nu-source" }
nu-stream = { version = "0.29.0", path = "../nu-stream" }
nu-table = { version = "0.29.0", path = "../nu-table" }
nu-test-support = { version = "0.29.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.29.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.29.0", path = "../nu-ansi-term" }
nu-command = { version = "0.31.0", path = "../nu-command" }
nu-data = { version = "0.31.0", path = "../nu-data" }
nu-engine = { version = "0.31.0", path = "../nu-engine" }
nu-errors = { version = "0.31.0", path = "../nu-errors" }
nu-json = { version = "0.31.0", path = "../nu-json" }
nu-parser = { version = "0.31.0", path = "../nu-parser" }
nu-plugin = { version = "0.31.0", path = "../nu-plugin" }
nu-protocol = { version = "0.31.0", path = "../nu-protocol" }
nu-source = { version = "0.31.0", path = "../nu-source" }
nu-stream = { version = "0.31.0", path = "../nu-stream" }
nu-table = { version = "0.31.0", path = "../nu-table" }
nu-test-support = { version = "0.31.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.31.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.31.0", path = "../nu-ansi-term" }
nu-pretty-hex = { version = "0.31.0", path = "../nu-pretty-hex" }
Inflector = "0.11"
arboard = { version = "1.1.0", optional = true }
@ -57,7 +58,6 @@ getset = "0.1.1"
glob = "0.3.0"
htmlescape = "0.3.1"
ical = "0.7.0"
ichwh = { version = "0.3.4", optional = true }
indexmap = { version = "1.6.1", features = ["serde-1"] }
itertools = "0.10.0"
lazy_static = "1.*"
@ -68,7 +68,6 @@ num-format = { version = "0.4.0", features = ["with-num-bigint"] }
num-traits = "0.2.14"
parking_lot = "0.11.1"
pin-utils = "0.1.0"
pretty-hex = "0.2.1"
ptree = { version = "0.3.1", optional = true }
query_interface = "0.3.5"
quick-xml = "0.21.0"

View File

@ -1,6 +1,6 @@
use crate::line_editor::configure_ctrl_c;
use nu_command::commands::default_context::create_default_context;
use nu_engine::{evaluation_context, run_block, script::run_script_standalone, EvaluationContext};
use nu_ansi_term::Color;
use nu_engine::{maybe_print_errors, run_block, script::run_script_standalone, EvaluationContext};
#[allow(unused_imports)]
pub(crate) use nu_engine::script::{process_script, LineResult};
@ -13,8 +13,7 @@ use crate::line_editor::{
#[allow(unused_imports)]
use nu_data::config;
use nu_data::config::{Conf, NuConfig};
use nu_source::{AnchorLocation, Tag, Text};
use nu_source::{Tag, Text};
use nu_stream::InputStream;
use std::ffi::{OsStr, OsString};
#[allow(unused_imports)]
@ -23,10 +22,9 @@ use std::sync::atomic::Ordering;
#[cfg(feature = "rustyline-support")]
use rustyline::{self, error::ReadlineError};
use crate::EnvironmentSyncer;
use nu_errors::ShellError;
use nu_parser::ParserScope;
use nu_protocol::{hir::ExternalRedirection, UntaggedValue, Value};
use nu_protocol::{hir::ExternalRedirection, ConfigPath, UntaggedValue, Value};
use log::trace;
use std::error::Error;
@ -35,10 +33,9 @@ use std::path::PathBuf;
pub struct Options {
pub config: Option<OsString>,
pub history: Option<PathBuf>,
pub save_history: bool,
pub stdin: bool,
pub scripts: Vec<NuScript>,
pub save_history: bool,
}
impl Default for Options {
@ -51,20 +48,9 @@ impl Options {
pub fn new() -> Self {
Self {
config: None,
history: None,
save_history: true,
stdin: false,
scripts: vec![],
}
}
pub fn history(&self, block: impl FnOnce(&std::path::Path)) {
if !self.save_history {
return;
}
if let Some(file) = &self.history {
block(&file)
save_history: true,
}
}
}
@ -137,106 +123,38 @@ pub fn search_paths() -> Vec<std::path::PathBuf> {
search_paths
}
pub async fn run_script_file(mut options: Options) -> Result<(), Box<dyn Error>> {
let mut context = create_default_context(false)?;
let mut syncer = create_environment_syncer(&context, &mut options);
let config = syncer.get_config();
pub fn run_script_file(context: EvaluationContext, options: Options) -> Result<(), Box<dyn Error>> {
if let Some(cfg) = options.config {
load_cfg_as_global_cfg(&context, PathBuf::from(cfg));
} else {
load_global_cfg(&context);
}
context.configure(&config, |_, ctx| {
syncer.load_environment();
syncer.sync_env_vars(ctx);
syncer.sync_path_vars(ctx);
if let Err(reason) = syncer.autoenv(ctx) {
ctx.with_host(|host| host.print_err(reason, &Text::from("")));
}
let _ = register_plugins(ctx);
let _ = configure_ctrl_c(ctx);
});
let _ = run_startup_commands(&mut context, &config).await;
let _ = register_plugins(&context);
let _ = configure_ctrl_c(&context);
let script = options
.scripts
.get(0)
.ok_or_else(|| ShellError::unexpected("Nu source code not available"))?;
run_script_standalone(script.get_code().to_string(), options.stdin, &context, true).await?;
run_script_standalone(script.get_code().to_string(), options.stdin, &context, true)?;
Ok(())
}
fn create_environment_syncer(
context: &EvaluationContext,
options: &mut Options,
) -> EnvironmentSyncer {
let configuration = match &options.config {
Some(config_file) => {
let location = Some(AnchorLocation::File(
config_file.to_string_lossy().to_string(),
));
let tag = Tag::unknown().anchored(location);
context.scope.add_var(
"config-path",
UntaggedValue::filepath(PathBuf::from(&config_file)).into_value(tag),
);
NuConfig::with(Some(config_file.into()))
}
None => NuConfig::new(),
};
let history_path = configuration.history_path();
options.history = Some(history_path.clone());
let location = Some(AnchorLocation::File(
history_path.to_string_lossy().to_string(),
));
let tag = Tag::unknown().anchored(location);
context.scope.add_var(
"history-path",
UntaggedValue::filepath(history_path).into_value(tag),
);
EnvironmentSyncer::with_config(Box::new(configuration))
}
#[cfg(feature = "rustyline-support")]
pub async fn cli(
mut context: EvaluationContext,
mut options: Options,
) -> Result<(), Box<dyn Error>> {
let mut syncer = create_environment_syncer(&context, &mut options);
pub fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn Error>> {
let _ = configure_ctrl_c(&context);
let configuration = syncer.get_config();
let mut rl = default_rustyline_editor_configuration();
context.configure(&configuration, |config, ctx| {
syncer.load_environment();
syncer.sync_env_vars(ctx);
syncer.sync_path_vars(ctx);
if let Err(reason) = syncer.autoenv(ctx) {
ctx.with_host(|host| host.print_err(reason, &Text::from("")));
}
let _ = configure_ctrl_c(ctx);
let _ = configure_rustyline_editor(&mut rl, config);
let helper = Some(nu_line_editor_helper(ctx, config));
rl.set_helper(helper);
});
// start time for command duration
// start time for running startup scripts (this metric includes loading of the cfg, but w/e)
let startup_commands_start_time = std::time::Instant::now();
// run the startup commands
let _ = run_startup_commands(&mut context, &configuration).await;
if let Some(cfg) = options.config {
load_cfg_as_global_cfg(&context, PathBuf::from(cfg));
} else {
load_global_cfg(&context);
}
// Store cmd duration in an env var
context.scope.add_env_var(
"CMD_DURATION",
@ -247,20 +165,43 @@ pub async fn cli(
startup_commands_start_time.elapsed()
);
//Configure rustyline
let mut rl = default_rustyline_editor_configuration();
let history_path = if let Some(cfg) = &context.configs.lock().global_config {
let _ = configure_rustyline_editor(&mut rl, cfg);
let helper = Some(nu_line_editor_helper(&context, cfg));
rl.set_helper(helper);
nu_data::config::path::history_path_or_default(cfg)
} else {
nu_data::config::path::default_history_path()
};
// Don't load history if it's not necessary
if options.save_history {
let _ = rl.load_history(&history_path);
}
//set vars from cfg if present
let (skip_welcome_message, prompt) = if let Some(cfg) = &context.configs.lock().global_config {
(
cfg.var("skip_welcome_message")
.map(|x| x.is_true())
.unwrap_or(false),
cfg.var("prompt"),
)
} else {
(false, None)
};
//Check whether dir we start in contains local cfg file and if so load it.
load_local_cfg_if_present(&context);
// Give ourselves a scope to work in
context.scope.enter_scope();
options.history(|file| {
let _ = rl.load_history(&file);
});
let mut session_text = String::new();
let mut line_start: usize = 0;
let skip_welcome_message = configuration
.var("skip_welcome_message")
.map(|x| x.is_true())
.unwrap_or(false);
if !skip_welcome_message {
println!(
"Welcome to Nushell {} (type 'help' for more info)",
@ -284,31 +225,38 @@ pub async fn cli(
let cwd = context.shell_manager.path();
let colored_prompt = {
if let Some(prompt) = configuration.var("prompt") {
if let Some(prompt) = &prompt {
let prompt_line = prompt.as_string()?;
context.scope.enter_scope();
let (mut prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
prompt_block.set_redirect(ExternalRedirection::Stdout);
let (prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
if err.is_some() {
context.scope.exit_scope();
format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch())
format!(
"{}{}{}{}{}{}> ",
Color::Green.bold().prefix().to_string(),
cwd,
nu_ansi_term::ansi::RESET,
Color::Cyan.bold().prefix().to_string(),
current_branch(),
nu_ansi_term::ansi::RESET
)
} else {
let run_result = run_block(&prompt_block, &context, InputStream::empty()).await;
let run_result = run_block(
&prompt_block,
&context,
InputStream::empty(),
ExternalRedirection::Stdout,
);
context.scope.exit_scope();
match run_result {
Ok(result) => match result.collect_string(Tag::unknown()).await {
Ok(result) => match result.collect_string(Tag::unknown()) {
Ok(string_result) => {
let errors = context.get_errors();
evaluation_context::maybe_print_errors(
&context,
Text::from(prompt_line),
);
maybe_print_errors(&context, Text::from(prompt_line));
context.clear_errors();
if !errors.is_empty() {
@ -333,7 +281,15 @@ pub async fn cli(
}
}
} else {
format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch())
format!(
"{}{}{}{}{}{}> ",
Color::Green.bold().prefix().to_string(),
cwd,
nu_ansi_term::ansi::RESET,
Color::Cyan.bold().prefix().to_string(),
current_branch(),
nu_ansi_term::ansi::RESET
)
}
};
@ -345,7 +301,9 @@ pub async fn cli(
}
};
rl.helper_mut().expect("No helper").colored_prompt = colored_prompt;
if let Some(helper) = rl.helper_mut() {
helper.colored_prompt = colored_prompt;
}
let mut initial_command = Some(String::new());
let mut readline = Err(ReadlineError::Eof);
while let Some(ref cmd) = initial_command {
@ -363,16 +321,13 @@ pub async fn cli(
let cmd_start_time = std::time::Instant::now();
let line = match convert_rustyline_result_to_string(readline) {
LineResult::Success(_) => {
process_script(
&session_text[line_start..],
&context,
false,
line_start,
true,
)
.await
}
LineResult::Success(_) => process_script(
&session_text[line_start..],
&context,
false,
line_start,
true,
),
x => x,
};
@ -381,54 +336,50 @@ pub async fn cli(
.scope
.add_env_var("CMD_DURATION", format!("{:?}", cmd_start_time.elapsed()));
// Check the config to see if we need to update the path
// TODO: make sure config is cached so we don't path this load every call
// FIXME: we probably want to be a bit more graceful if we can't set the environment
context.configure(&configuration, |config, ctx| {
if syncer.did_config_change() {
syncer.reload();
syncer.sync_env_vars(ctx);
syncer.sync_path_vars(ctx);
}
if let Err(reason) = syncer.autoenv(ctx) {
ctx.with_host(|host| host.print_err(reason, &Text::from("")));
}
let _ = configure_rustyline_editor(&mut rl, config);
});
match line {
LineResult::Success(line) => {
options.history(|file| {
if options.save_history && !line.trim().is_empty() {
rl.add_history_entry(&line);
let _ = rl.save_history(&file);
});
evaluation_context::maybe_print_errors(&context, Text::from(session_text.clone()));
let _ = rl.append_history(&history_path);
}
maybe_print_errors(&context, Text::from(session_text.clone()));
}
LineResult::ClearHistory => {
options.history(|file| {
if options.save_history {
rl.clear_history();
let _ = rl.save_history(&file);
});
let _ = rl.append_history(&history_path);
}
}
LineResult::Error(line, reason) => {
options.history(|file| {
LineResult::Error(line, err) => {
if options.save_history && !line.trim().is_empty() {
rl.add_history_entry(&line);
let _ = rl.save_history(&file);
});
let _ = rl.append_history(&history_path);
}
context.with_host(|host| host.print_err(reason, &Text::from(session_text.clone())));
context
.host
.lock()
.print_err(err, &Text::from(session_text.clone()));
// I am not so sure, we don't need maybe_print_errors here (as we printed an err
// above), because maybe_print_errors also clears the errors.
// TODO Analyze where above err comes from, and whether we need to clear
// context.errors here
// Or just be consistent and return errors always in context.errors...
maybe_print_errors(&context, Text::from(session_text.clone()));
}
LineResult::CtrlC => {
let config_ctrlc_exit = configuration
.var("ctrlc_exit")
.map(|s| s.value.is_true())
let config_ctrlc_exit = context
.configs
.lock()
.global_config
.as_ref()
.map(|cfg| cfg.var("ctrlc_exit"))
.flatten()
.map(|ctrl_c| ctrl_c.is_true())
.unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells
if !config_ctrlc_exit {
@ -436,10 +387,9 @@ pub async fn cli(
}
if ctrlcbreak {
options.history(|file| {
let _ = rl.save_history(&file);
});
if options.save_history {
let _ = rl.append_history(&history_path);
}
std::process::exit(0);
} else {
context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)"));
@ -463,14 +413,49 @@ pub async fn cli(
}
// we are ok if we can not save history
options.history(|file| {
let _ = rl.save_history(&file);
});
if options.save_history {
let _ = rl.append_history(&history_path);
}
Ok(())
}
pub fn register_plugins(context: &mut EvaluationContext) -> Result<(), ShellError> {
pub fn load_local_cfg_if_present(context: &EvaluationContext) {
trace!("Loading local cfg if present");
match config::loadable_cfg_exists_in_dir(PathBuf::from(context.shell_manager.path())) {
Ok(Some(cfg_path)) => {
if let Err(err) = context.load_config(&ConfigPath::Local(cfg_path)) {
context.host.lock().print_err(err, &Text::from(""))
}
}
Err(e) => {
//Report error while checking for local cfg file
context.host.lock().print_err(e, &Text::from(""))
}
Ok(None) => {
//No local cfg file present in start dir
}
}
}
fn load_cfg_as_global_cfg(context: &EvaluationContext, path: PathBuf) {
if let Err(err) = context.load_config(&ConfigPath::Global(path)) {
context.host.lock().print_err(err, &Text::from(""));
}
}
pub fn load_global_cfg(context: &EvaluationContext) {
match config::default_path() {
Ok(path) => {
load_cfg_as_global_cfg(context, path);
}
Err(e) => {
context.host.lock().print_err(e, &Text::from(""));
}
}
}
pub fn register_plugins(context: &EvaluationContext) -> Result<(), ShellError> {
if let Ok(plugins) = nu_engine::plugin::build_plugin::scan(search_paths()) {
context.add_commands(
plugins
@ -483,35 +468,7 @@ pub fn register_plugins(context: &mut EvaluationContext) -> Result<(), ShellErro
Ok(())
}
async fn run_startup_commands(
context: &mut EvaluationContext,
config: &dyn nu_data::config::Conf,
) -> Result<(), ShellError> {
if let Some(commands) = config.var("startup") {
match commands {
Value {
value: UntaggedValue::Table(pipelines),
..
} => {
let mut script_file = String::new();
for pipeline in pipelines {
script_file.push_str(&pipeline.as_string()?);
script_file.push('\n');
}
let _ = run_script_standalone(script_file, false, context, false).await;
}
_ => {
return Err(ShellError::untagged_runtime_error(
"expected a table of pipeline strings as startup commands",
));
}
}
}
Ok(())
}
pub async fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, ShellError> {
pub fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, ShellError> {
// FIXME: do we still need this?
let line = if let Some(s) = line.strip_suffix('\n') {
s
@ -528,13 +485,16 @@ pub async fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<Strin
}
let input_stream = InputStream::empty();
let env = ctx.get_env();
ctx.scope.add_env(env);
let result = run_block(&classified_block, ctx, input_stream).await;
let result = run_block(
&classified_block,
ctx,
input_stream,
ExternalRedirection::Stdout,
);
ctx.scope.exit_scope();
result?.collect_string(Tag::unknown()).await.map(|x| x.item)
result?.collect_string(Tag::unknown()).map(|x| x.item)
}
#[allow(dead_code)]
@ -562,7 +522,7 @@ mod tests {
let (tokens, err) = nu_parser::lex(&data, 0);
let (lite_block, err2) = nu_parser::parse_block(tokens);
if err.is_none() && err2.is_none() {
let context = EvaluationContext::basic().unwrap();
let context = EvaluationContext::basic();
let _ = nu_parser::classify_block(&lite_block, &context.scope);
}
true

View File

@ -254,6 +254,8 @@ pub fn completion_location(line: &str, block: &Block, pos: usize) -> Vec<Complet
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use nu_parser::{classify_block, lex, parse_block, ParserScope};
@ -285,9 +287,9 @@ mod tests {
todo!()
}
fn add_definition(&self, _block: Block) {}
fn add_definition(&self, _block: Arc<Block>) {}
fn get_definitions(&self) -> Vec<Block> {
fn get_definitions(&self) -> Vec<Arc<Block>> {
vec![]
}

View File

@ -12,7 +12,7 @@ impl matchers::Matcher for Matcher {
mod tests {
use super::*;
// TODO: check some unicode matches if this becomes relevant
// TODO: check some Unicode matches if this becomes relevant
// FIXME: could work exhaustively through ['-', '--'. ''] in a loop for each test
#[test]

View File

@ -1,3 +0,0 @@
pub(crate) mod directory_specific_environment;
pub(crate) mod environment;
pub(crate) mod environment_syncer;

View File

@ -1,259 +0,0 @@
use indexmap::{IndexMap, IndexSet};
use nu_command::commands::autoenv;
use nu_errors::ShellError;
use serde::Deserialize;
use std::env::*;
use std::process::Command;
use std::{
ffi::OsString,
fmt::Debug,
path::{Path, PathBuf},
};
//Tests reside in /nushell/tests/shell/pipeline/commands/internal.rs
type EnvKey = String;
type EnvVal = OsString;
#[derive(Debug, Default)]
pub struct DirectorySpecificEnvironment {
pub last_seen_directory: PathBuf,
//If an environment var has been added from a .nu in a directory, we track it here so we can remove it when the user leaves the directory.
//If setting the var overwrote some value, we save the old value in an option so we can restore it later.
added_vars: IndexMap<PathBuf, IndexMap<EnvKey, Option<EnvVal>>>,
//We track directories that we have read .nu-env from. This is different from the keys in added_vars since sometimes a file only wants to run scripts.
visited_dirs: IndexSet<PathBuf>,
exitscripts: IndexMap<PathBuf, Vec<String>>,
}
#[derive(Deserialize, Debug, Default)]
pub struct NuEnvDoc {
pub env: Option<IndexMap<String, String>>,
pub scriptvars: Option<IndexMap<String, String>>,
pub scripts: Option<IndexMap<String, Vec<String>>>,
pub entryscripts: Option<Vec<String>>,
pub exitscripts: Option<Vec<String>>,
}
impl DirectorySpecificEnvironment {
pub fn new() -> DirectorySpecificEnvironment {
let root_dir = if cfg!(target_os = "windows") {
PathBuf::from("c:\\")
} else {
PathBuf::from("/")
};
DirectorySpecificEnvironment {
last_seen_directory: root_dir,
added_vars: IndexMap::new(),
visited_dirs: IndexSet::new(),
exitscripts: IndexMap::new(),
}
}
fn toml_if_trusted(&mut self, nu_env_file: &Path) -> Result<NuEnvDoc, ShellError> {
let content = std::fs::read(&nu_env_file)?;
if autoenv::file_is_trusted(&nu_env_file, &content)? {
let mut doc: NuEnvDoc = toml::de::from_slice(&content)
.map_err(|e| ShellError::untagged_runtime_error(format!("{:?}", e)))?;
if let Some(scripts) = doc.scripts.as_ref() {
for (k, v) in scripts {
if k == "entryscripts" {
doc.entryscripts = Some(v.clone());
} else if k == "exitscripts" {
doc.exitscripts = Some(v.clone());
}
}
}
return Ok(doc);
}
Err(ShellError::untagged_runtime_error(
format!("{:?} is untrusted. Run 'autoenv trust {:?}' to trust it.\nThis needs to be done after each change to the file.", nu_env_file, nu_env_file.parent().unwrap_or_else(|| &Path::new("")))))
}
pub fn maintain_autoenv(&mut self) -> Result<(), ShellError> {
let mut dir = current_dir()?;
if self.last_seen_directory == dir {
return Ok(());
}
//We track which keys we set as we go up the directory hierarchy, so that we don't overwrite a value we set in a subdir.
let mut added_keys = IndexSet::new();
let mut new_visited_dirs = IndexSet::new();
let mut popped = true;
while popped {
let nu_env_file = dir.join(".nu-env");
if nu_env_file.exists() && !self.visited_dirs.contains(&dir) {
let nu_env_doc = self.toml_if_trusted(&nu_env_file)?;
//add regular variables from the [env section]
if let Some(env) = nu_env_doc.env {
for (env_key, env_val) in env {
self.maybe_add_key(&mut added_keys, &dir, &env_key, &env_val);
}
}
//Add variables that need to evaluate scripts to run, from [scriptvars] section
if let Some(sv) = nu_env_doc.scriptvars {
for (key, script) in sv {
self.maybe_add_key(
&mut added_keys,
&dir,
&key,
value_from_script(&script)?.as_str(),
);
}
}
if let Some(es) = nu_env_doc.entryscripts {
for s in es {
run(s.as_str(), None)?;
}
}
if let Some(es) = nu_env_doc.exitscripts {
self.exitscripts.insert(dir.clone(), es);
}
}
new_visited_dirs.insert(dir.clone());
popped = dir.pop();
}
//Time to clear out vars set by directories that we have left.
let mut new_vars = IndexMap::new();
for (dir, dirmap) in self.added_vars.drain(..) {
if new_visited_dirs.contains(&dir) {
new_vars.insert(dir, dirmap);
} else {
for (k, v) in dirmap {
if let Some(v) = v {
std::env::set_var(k, v);
} else {
std::env::remove_var(k);
}
}
}
}
//Run exitscripts, can not be done in same loop as new vars as some files can contain only exitscripts
let mut new_exitscripts = IndexMap::new();
for (dir, scripts) in self.exitscripts.drain(..) {
if new_visited_dirs.contains(&dir) {
new_exitscripts.insert(dir, scripts);
} else {
for s in scripts {
run(s.as_str(), Some(&dir))?;
}
}
}
self.visited_dirs = new_visited_dirs;
self.exitscripts = new_exitscripts;
self.added_vars = new_vars;
self.last_seen_directory = current_dir()?;
Ok(())
}
pub fn maybe_add_key(
&mut self,
seen_vars: &mut IndexSet<EnvKey>,
dir: &Path,
key: &str,
val: &str,
) {
//This condition is to make sure variables in parent directories don't overwrite variables set by subdirectories.
if !seen_vars.contains(key) {
seen_vars.insert(key.to_string());
self.added_vars
.entry(PathBuf::from(dir))
.or_insert(IndexMap::new())
.insert(key.to_string(), var_os(key));
std::env::set_var(key, val);
}
}
// If the user recently ran autoenv untrust on a file, we clear the environment variables it set and make sure to not run any possible exitscripts.
pub fn clear_recently_untrusted_file(&mut self) -> Result<(), ShellError> {
// Figure out which file was untrusted
// Remove all vars set by it
let current_trusted_files: IndexSet<PathBuf> = autoenv::read_trusted()?
.files
.iter()
.map(|(k, _)| PathBuf::from(k))
.collect();
// We figure out which file(s) the user untrusted by taking the set difference of current trusted files in .config/nu/nu-env.toml and the files tracked by self.added_env_vars
// If a file is in self.added_env_vars but not in nu-env.toml, it was just untrusted.
let untrusted_files: IndexSet<PathBuf> = self
.added_vars
.iter()
.filter_map(|(path, _)| {
if !current_trusted_files.contains(path) {
return Some(path.clone());
}
None
})
.collect();
for path in untrusted_files {
if let Some(added_keys) = self.added_vars.get(&path) {
for (key, _) in added_keys {
remove_var(key);
}
}
self.exitscripts.remove(&path);
self.added_vars.remove(&path);
}
Ok(())
}
}
fn run(cmd: &str, dir: Option<&PathBuf>) -> Result<(), ShellError> {
if cfg!(target_os = "windows") {
if let Some(dir) = dir {
let command = format!("cd {} & {}", dir.to_string_lossy(), cmd);
Command::new("cmd")
.args(&["/C", command.as_str()])
.output()?
} else {
Command::new("cmd").args(&["/C", cmd]).output()?
}
} else if let Some(dir) = dir {
// FIXME: When nu scripting is added, cding like might not be a good idea. If nu decides to execute entryscripts when entering the dir this way, it will cause troubles.
// For now only standard shell scripts are used, so this is an issue for the future.
Command::new("sh")
.arg("-c")
.arg(format!("cd {:?}; {}", dir, cmd))
.output()?
} else {
Command::new("sh").arg("-c").arg(&cmd).output()?
};
Ok(())
}
fn value_from_script(cmd: &str) -> Result<String, ShellError> {
let command = if cfg!(target_os = "windows") {
Command::new("cmd").args(&["/C", cmd]).output()?
} else {
Command::new("sh").arg("-c").arg(&cmd).output()?
};
if command.stdout.is_empty() {
return Err(ShellError::untagged_runtime_error(format!(
"{:?} did not return any output",
cmd
)));
}
let response = std::str::from_utf8(&command.stdout[..command.stdout.len()]).map_err(|e| {
ShellError::untagged_runtime_error(format!(
"Couldn't parse stdout from command {:?}: {:?}",
command, e
))
})?;
Ok(response.trim().to_string())
}

View File

@ -1,274 +0,0 @@
use crate::env::directory_specific_environment::*;
use indexmap::{indexmap, IndexSet};
use nu_data::config::Conf;
use nu_engine::Env;
use nu_errors::ShellError;
use nu_protocol::{UntaggedValue, Value};
#[derive(Debug, Default)]
pub struct Environment {
environment_vars: Option<Value>,
path_vars: Option<Value>,
pub autoenv: DirectorySpecificEnvironment,
}
impl Environment {
pub fn new() -> Environment {
Environment {
environment_vars: None,
path_vars: None,
autoenv: DirectorySpecificEnvironment::new(),
}
}
pub fn from_config<T: Conf>(configuration: &T) -> Environment {
let env = configuration.env();
let path = configuration.path();
Environment {
environment_vars: env,
path_vars: path,
autoenv: DirectorySpecificEnvironment::new(),
}
}
pub fn autoenv(&mut self, reload_trusted: bool) -> Result<(), ShellError> {
self.autoenv.maintain_autoenv()?;
if reload_trusted {
self.autoenv.clear_recently_untrusted_file()?;
}
Ok(())
}
pub fn morph<T: Conf>(&mut self, configuration: &T) {
self.environment_vars = configuration.env();
self.path_vars = configuration.path();
}
}
impl Env for Environment {
fn env(&self) -> Option<Value> {
if let Some(vars) = &self.environment_vars {
return Some(vars.clone());
}
None
}
fn path(&self) -> Option<Value> {
if let Some(vars) = &self.path_vars {
return Some(vars.clone());
}
None
}
fn add_env(&mut self, key: &str, value: &str) {
let value = UntaggedValue::string(value);
let new_envs = {
if let Some(Value {
value: UntaggedValue::Row(ref envs),
ref tag,
}) = self.environment_vars
{
let mut new_envs = envs.clone();
if !new_envs.contains_key(key) {
new_envs.insert_data_at_key(key, value.into_value(tag.clone()));
}
Value {
value: UntaggedValue::Row(new_envs),
tag: tag.clone(),
}
} else {
UntaggedValue::Row(indexmap! { key.into() => value.into_untagged_value() }.into())
.into_untagged_value()
}
};
self.environment_vars = Some(new_envs);
}
fn add_path(&mut self, paths: std::ffi::OsString) {
let new_paths = {
if let Some(Value {
value: UntaggedValue::Table(ref current_paths),
ref tag,
}) = self.path_vars
{
let mut new_paths = current_paths.clone();
let new_path_candidates = std::env::split_paths(&paths).map(|path| {
UntaggedValue::string(path.to_string_lossy()).into_value(tag.clone())
});
new_paths.extend(new_path_candidates);
let paths: IndexSet<Value> = new_paths.into_iter().collect();
Value {
value: UntaggedValue::Table(paths.into_iter().collect()),
tag: tag.clone(),
}
} else {
let p = paths.into_string().unwrap_or_else(|_| String::from(""));
let p = UntaggedValue::string(p).into_untagged_value();
UntaggedValue::Table(vec![p]).into_untagged_value()
}
};
self.path_vars = Some(new_paths);
}
}
#[cfg(test)]
mod tests {
use super::{Env, Environment};
use nu_data::config::{tests::FakeConfig, Conf};
use nu_protocol::UntaggedValue;
use nu_test_support::fs::Stub::FileWithContent;
use nu_test_support::playground::Playground;
#[test]
fn picks_up_environment_variables_from_configuration() {
Playground::setup("environment_test_1", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"configuration.toml",
r#"
[env]
mosquetero_1 = "Andrés N. Robalino"
mosquetero_2 = "Jonathan Turner"
mosquetero_3 = "Yehuda katz"
mosquetero_4 = "Jason Gedge"
"#,
)]);
let mut file = dirs.test().clone();
file.push("configuration.toml");
let fake_config = FakeConfig::new(&file);
let actual = Environment::from_config(&fake_config);
assert_eq!(actual.env(), fake_config.env());
});
}
#[test]
fn picks_up_path_variables_from_configuration() {
Playground::setup("environment_test_2", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"configuration.toml",
r#"
path = ["/Users/andresrobalino/.volta/bin", "/users/mosqueteros/bin"]
"#,
)]);
let mut file = dirs.test().clone();
file.push("configuration.toml");
let fake_config = FakeConfig::new(&file);
let actual = Environment::from_config(&fake_config);
assert_eq!(actual.path(), fake_config.path());
});
}
#[test]
fn updates_env_variable() {
Playground::setup("environment_test_3", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"configuration.toml",
r#"
[env]
SHELL = "/usr/bin/you_already_made_the_nu_choice"
"#,
)]);
let mut file = dirs.test().clone();
file.push("configuration.toml");
let fake_config = FakeConfig::new(&file);
let mut actual = Environment::from_config(&fake_config);
actual.add_env("USER", "NUNO");
assert_eq!(
actual.env(),
Some(
UntaggedValue::row(
indexmap! {
"USER".into() => UntaggedValue::string("NUNO").into_untagged_value(),
"SHELL".into() => UntaggedValue::string("/usr/bin/you_already_made_the_nu_choice").into_untagged_value(),
}
).into_untagged_value()
)
);
});
}
#[test]
fn does_not_update_env_variable_if_it_exists() {
Playground::setup("environment_test_4", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"configuration.toml",
r#"
[env]
SHELL = "/usr/bin/you_already_made_the_nu_choice"
"#,
)]);
let mut file = dirs.test().clone();
file.push("configuration.toml");
let fake_config = FakeConfig::new(&file);
let mut actual = Environment::from_config(&fake_config);
actual.add_env("SHELL", "/usr/bin/sh");
assert_eq!(
actual.env(),
Some(
UntaggedValue::row(
indexmap! {
"SHELL".into() => UntaggedValue::string("/usr/bin/you_already_made_the_nu_choice").into_untagged_value(),
}
).into_untagged_value()
)
);
});
}
#[test]
fn updates_path_variable() {
Playground::setup("environment_test_5", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"configuration.toml",
r#"
path = ["/Users/andresrobalino/.volta/bin", "/users/mosqueteros/bin"]
"#,
)]);
let mut file = dirs.test().clone();
file.push("configuration.toml");
let fake_config = FakeConfig::new(&file);
let mut actual = Environment::from_config(&fake_config);
actual.add_path(std::ffi::OsString::from("/path/to/be/added"));
assert_eq!(
actual.path(),
Some(
UntaggedValue::table(&[
UntaggedValue::string("/Users/andresrobalino/.volta/bin")
.into_untagged_value(),
UntaggedValue::string("/users/mosqueteros/bin").into_untagged_value(),
UntaggedValue::string("/path/to/be/added").into_untagged_value(),
])
.into_untagged_value()
)
);
});
}
}

View File

@ -1,617 +0,0 @@
use crate::env::environment::Environment;
use nu_data::config::{Conf, NuConfig};
use nu_engine::Env;
use nu_engine::EvaluationContext;
use nu_errors::ShellError;
use parking_lot::Mutex;
use std::sync::{atomic::Ordering, Arc};
pub struct EnvironmentSyncer {
pub env: Arc<Mutex<Box<Environment>>>,
pub config: Arc<Mutex<Box<dyn Conf>>>,
}
impl Default for EnvironmentSyncer {
fn default() -> Self {
Self::new()
}
}
impl EnvironmentSyncer {
pub fn with_config(config: Box<dyn Conf>) -> Self {
EnvironmentSyncer {
env: Arc::new(Mutex::new(Box::new(Environment::new()))),
config: Arc::new(Mutex::new(config)),
}
}
pub fn new() -> EnvironmentSyncer {
EnvironmentSyncer {
env: Arc::new(Mutex::new(Box::new(Environment::new()))),
config: Arc::new(Mutex::new(Box::new(NuConfig::new()))),
}
}
#[cfg(test)]
pub fn set_config(&mut self, config: Box<dyn Conf>) {
self.config = Arc::new(Mutex::new(config));
}
pub fn get_config(&self) -> Box<dyn Conf> {
let config = self.config.lock();
config.clone_box()
}
pub fn load_environment(&mut self) {
let config = self.config.lock();
self.env = Arc::new(Mutex::new(Box::new(Environment::from_config(&*config))));
}
pub fn did_config_change(&mut self) -> bool {
let config = self.config.lock();
config.is_modified().unwrap_or(false)
}
pub fn reload(&mut self) {
let mut config = self.config.lock();
config.reload();
let mut environment = self.env.lock();
environment.morph(&*config);
}
pub fn autoenv(&self, ctx: &mut EvaluationContext) -> Result<(), ShellError> {
let mut environment = self.env.lock();
let recently_used = ctx
.user_recently_used_autoenv_untrust
.load(Ordering::SeqCst);
let auto = environment.autoenv(recently_used);
ctx.user_recently_used_autoenv_untrust
.store(false, Ordering::SeqCst);
auto
}
pub fn sync_env_vars(&mut self, ctx: &mut EvaluationContext) {
let mut environment = self.env.lock();
if environment.env().is_some() {
for (name, value) in ctx.with_host(|host| host.vars()) {
if name != "path" && name != "PATH" {
// account for new env vars present in the current session
// that aren't loaded from config.
environment.add_env(&name, &value);
// clear the env var from the session
// we are about to replace them
ctx.with_host(|host| host.env_rm(std::ffi::OsString::from(name)));
}
}
if let Some(variables) = environment.env() {
for var in variables.row_entries() {
if let Ok(string) = var.1.as_string() {
ctx.with_host(|host| {
host.env_set(
std::ffi::OsString::from(var.0),
std::ffi::OsString::from(&string),
)
});
ctx.scope.add_env_var_to_base(var.0, string);
}
}
}
}
}
pub fn sync_path_vars(&mut self, ctx: &mut EvaluationContext) {
let mut environment = self.env.lock();
if environment.path().is_some() {
let native_paths = ctx.with_host(|host| host.env_get(std::ffi::OsString::from("PATH")));
if let Some(native_paths) = native_paths {
environment.add_path(native_paths);
ctx.with_host(|host| {
host.env_rm(std::ffi::OsString::from("PATH"));
});
}
if let Some(new_paths) = environment.path() {
let prepared = std::env::join_paths(
new_paths
.table_entries()
.map(|p| p.as_string())
.filter_map(Result::ok),
);
if let Ok(paths_ready) = prepared {
ctx.with_host(|host| {
host.env_set(
std::ffi::OsString::from("PATH"),
std::ffi::OsString::from(&paths_ready),
);
});
ctx.scope
.add_env_var_to_base("PATH", paths_ready.to_string_lossy().to_string());
}
}
}
}
#[cfg(test)]
pub fn clear_env_vars(&mut self, ctx: &mut EvaluationContext) {
for (key, _value) in ctx.with_host(|host| host.vars()) {
if key != "path" && key != "PATH" {
ctx.with_host(|host| host.env_rm(std::ffi::OsString::from(key)));
}
}
}
#[cfg(test)]
pub fn clear_path_var(&mut self, ctx: &mut EvaluationContext) {
ctx.with_host(|host| host.env_rm(std::ffi::OsString::from("PATH")));
}
}
#[cfg(test)]
mod tests {
use super::EnvironmentSyncer;
use indexmap::IndexMap;
use nu_data::config::tests::FakeConfig;
use nu_engine::Env;
use nu_engine::EvaluationContext;
use nu_errors::ShellError;
use nu_test_support::fs::Stub::FileWithContent;
use nu_test_support::playground::Playground;
use parking_lot::Mutex;
use std::path::PathBuf;
use std::sync::Arc;
// This test fails on Linux.
// It's possible it has something to do with the fake configuration
// TODO: More tests.
#[cfg(not(target_os = "linux"))]
#[test]
fn syncs_env_if_new_env_entry_is_added_to_an_existing_configuration() -> Result<(), ShellError>
{
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let mut expected = IndexMap::new();
expected.insert(
"SHELL".to_string(),
"/usr/bin/you_already_made_the_nu_choice".to_string(),
);
Playground::setup("syncs_env_from_config_updated_test_1", |dirs, sandbox| {
sandbox.with_files(vec![
FileWithContent(
"configuration.toml",
r#"
[env]
SHELL = "/usr/bin/you_already_made_the_nu_choice"
"#,
),
FileWithContent(
"updated_configuration.toml",
r#"
[env]
SHELL = "/usr/bin/you_already_made_the_nu_choice"
USER = "NUNO"
"#,
),
]);
let file = dirs.test().join("configuration.toml");
let new_file = dirs.test().join("updated_configuration.toml");
let fake_config = FakeConfig::new(&file);
let mut actual = EnvironmentSyncer::with_config(Box::new(fake_config));
// Here, the environment variables from the current session
// are cleared since we will load and set them from the
// configuration file
actual.clear_env_vars(&mut ctx);
// Nu loads the environment variables from the configuration file
actual.load_environment();
actual.sync_env_vars(&mut ctx);
{
let environment = actual.env.lock();
let mut vars = IndexMap::new();
environment
.env()
.expect("No variables in the environment.")
.row_entries()
.for_each(|(name, value)| {
vars.insert(
name.to_string(),
value.as_string().expect("Couldn't convert to string"),
);
});
for k in expected.keys() {
assert!(vars.contains_key(k));
}
}
assert!(!actual.did_config_change());
// Replacing the newer configuration file to the existing one.
let new_config_contents = std::fs::read_to_string(new_file).expect("Failed");
std::fs::write(&file, &new_config_contents).expect("Failed");
// A change has happened
assert!(actual.did_config_change());
// Syncer should reload and add new envs
actual.reload();
actual.sync_env_vars(&mut ctx);
expected.insert("USER".to_string(), "NUNO".to_string());
{
let environment = actual.env.lock();
let mut vars = IndexMap::new();
environment
.env()
.expect("No variables in the environment.")
.row_entries()
.for_each(|(name, value)| {
vars.insert(
name.to_string(),
value.as_string().expect("Couldn't convert to string"),
);
});
for k in expected.keys() {
assert!(vars.contains_key(k));
}
}
});
Ok(())
}
#[test]
fn syncs_env_if_new_env_entry_in_session_is_not_in_configuration_file() -> Result<(), ShellError>
{
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let mut expected = IndexMap::new();
expected.insert(
"SHELL".to_string(),
"/usr/bin/you_already_made_the_nu_choice".to_string(),
);
expected.insert("USER".to_string(), "NUNO".to_string());
Playground::setup("syncs_env_test_1", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"configuration.toml",
r#"
[env]
SHELL = "/usr/bin/you_already_made_the_nu_choice"
"#,
)]);
let mut file = dirs.test().clone();
file.push("configuration.toml");
let fake_config = FakeConfig::new(&file);
let mut actual = EnvironmentSyncer::new();
actual.set_config(Box::new(fake_config));
// Here, the environment variables from the current session
// are cleared since we will load and set them from the
// configuration file (if any)
actual.clear_env_vars(&mut ctx);
// We explicitly simulate and add the USER variable to the current
// session's environment variables with the value "NUNO".
ctx.with_host(|test_host| {
test_host.env_set(
std::ffi::OsString::from("USER"),
std::ffi::OsString::from("NUNO"),
)
});
// Nu loads the environment variables from the configuration file (if any)
actual.load_environment();
// By this point, Nu has already loaded the environment variables
// stored in the configuration file. Before continuing we check
// if any new environment variables have been added from the ones loaded
// in the configuration file.
//
// Nu sees the missing "USER" variable and accounts for it.
actual.sync_env_vars(&mut ctx);
// Confirms session environment variables are replaced from Nu configuration file
// including the newer one accounted for.
ctx.with_host(|test_host| {
let var_user = test_host
.env_get(std::ffi::OsString::from("USER"))
.expect("Couldn't get USER var from host.")
.into_string()
.expect("Couldn't convert to string.");
let var_shell = test_host
.env_get(std::ffi::OsString::from("SHELL"))
.expect("Couldn't get SHELL var from host.")
.into_string()
.expect("Couldn't convert to string.");
let mut found = IndexMap::new();
found.insert("SHELL".to_string(), var_shell);
found.insert("USER".to_string(), var_user);
for k in found.keys() {
assert!(expected.contains_key(k));
}
});
// Now confirm in-memory environment variables synced appropriately
// including the newer one accounted for.
let environment = actual.env.lock();
let mut vars = IndexMap::new();
environment
.env()
.expect("No variables in the environment.")
.row_entries()
.for_each(|(name, value)| {
vars.insert(
name.to_string(),
value.as_string().expect("Couldn't convert to string"),
);
});
for k in expected.keys() {
assert!(vars.contains_key(k));
}
});
Ok(())
}
#[test]
fn nu_envs_have_higher_priority_and_does_not_get_overwritten() -> Result<(), ShellError> {
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let mut expected = IndexMap::new();
expected.insert(
"SHELL".to_string(),
"/usr/bin/you_already_made_the_nu_choice".to_string(),
);
Playground::setup("syncs_env_test_2", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"configuration.toml",
r#"
[env]
SHELL = "/usr/bin/you_already_made_the_nu_choice"
"#,
)]);
let mut file = dirs.test().clone();
file.push("configuration.toml");
let fake_config = FakeConfig::new(&file);
let mut actual = EnvironmentSyncer::new();
actual.set_config(Box::new(fake_config));
actual.clear_env_vars(&mut ctx);
ctx.with_host(|test_host| {
test_host.env_set(
std::ffi::OsString::from("SHELL"),
std::ffi::OsString::from("/usr/bin/sh"),
)
});
actual.load_environment();
actual.sync_env_vars(&mut ctx);
ctx.with_host(|test_host| {
let var_shell = test_host
.env_get(std::ffi::OsString::from("SHELL"))
.expect("Couldn't get SHELL var from host.")
.into_string()
.expect("Couldn't convert to string.");
let mut found = IndexMap::new();
found.insert("SHELL".to_string(), var_shell);
for k in found.keys() {
assert!(expected.contains_key(k));
}
});
let environment = actual.env.lock();
let mut vars = IndexMap::new();
environment
.env()
.expect("No variables in the environment.")
.row_entries()
.for_each(|(name, value)| {
vars.insert(
name.to_string(),
value.as_string().expect("couldn't convert to string"),
);
});
for k in expected.keys() {
assert!(vars.contains_key(k));
}
});
Ok(())
}
#[test]
fn syncs_path_if_new_path_entry_in_session_is_not_in_configuration_file(
) -> Result<(), ShellError> {
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let expected = std::env::join_paths(vec![
PathBuf::from("/Users/andresrobalino/.volta/bin"),
PathBuf::from("/Users/mosqueteros/bin"),
PathBuf::from("/path/to/be/added"),
])
.expect("Couldn't join paths.")
.into_string()
.expect("Couldn't convert to string.");
Playground::setup("syncs_path_test_1", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"configuration.toml",
r#"
path = ["/Users/andresrobalino/.volta/bin", "/Users/mosqueteros/bin"]
"#,
)]);
let mut file = dirs.test().clone();
file.push("configuration.toml");
let fake_config = FakeConfig::new(&file);
let mut actual = EnvironmentSyncer::new();
actual.set_config(Box::new(fake_config));
// Here, the environment variables from the current session
// are cleared since we will load and set them from the
// configuration file (if any)
actual.clear_path_var(&mut ctx);
// We explicitly simulate and add the PATH variable to the current
// session with the path "/path/to/be/added".
ctx.with_host(|test_host| {
test_host.env_set(
std::ffi::OsString::from("PATH"),
std::env::join_paths(vec![PathBuf::from("/path/to/be/added")])
.expect("Couldn't join paths."),
)
});
// Nu loads the path variables from the configuration file (if any)
actual.load_environment();
// By this point, Nu has already loaded environment path variable
// stored in the configuration file. Before continuing we check
// if any new paths have been added from the ones loaded in the
// configuration file.
//
// Nu sees the missing "/path/to/be/added" and accounts for it.
actual.sync_path_vars(&mut ctx);
ctx.with_host(|test_host| {
let actual = test_host
.env_get(std::ffi::OsString::from("PATH"))
.expect("Couldn't get PATH var from host.")
.into_string()
.expect("Couldn't convert to string.");
assert_eq!(actual, expected);
});
let environment = actual.env.lock();
let paths = std::env::join_paths(
&environment
.path()
.expect("No path variable in the environment.")
.table_entries()
.map(|value| value.as_string().expect("Couldn't convert to string"))
.map(PathBuf::from)
.collect::<Vec<_>>(),
)
.expect("Couldn't join paths.")
.into_string()
.expect("Couldn't convert to string.");
assert_eq!(paths, expected);
});
Ok(())
}
#[test]
fn nu_paths_have_higher_priority_and_new_paths_get_appended_to_the_end(
) -> Result<(), ShellError> {
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let expected = std::env::join_paths(vec![
PathBuf::from("/Users/andresrobalino/.volta/bin"),
PathBuf::from("/Users/mosqueteros/bin"),
PathBuf::from("/path/to/be/added"),
])
.expect("Couldn't join paths.")
.into_string()
.expect("Couldn't convert to string.");
Playground::setup("syncs_path_test_2", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"configuration.toml",
r#"
path = ["/Users/andresrobalino/.volta/bin", "/Users/mosqueteros/bin"]
"#,
)]);
let mut file = dirs.test().clone();
file.push("configuration.toml");
let fake_config = FakeConfig::new(&file);
let mut actual = EnvironmentSyncer::new();
actual.set_config(Box::new(fake_config));
actual.clear_path_var(&mut ctx);
ctx.with_host(|test_host| {
test_host.env_set(
std::ffi::OsString::from("PATH"),
std::env::join_paths(vec![PathBuf::from("/path/to/be/added")])
.expect("Couldn't join paths."),
)
});
actual.load_environment();
actual.sync_path_vars(&mut ctx);
ctx.with_host(|test_host| {
let actual = test_host
.env_get(std::ffi::OsString::from("PATH"))
.expect("Couldn't get PATH var from host.")
.into_string()
.expect("Couldn't convert to string.");
assert_eq!(actual, expected);
});
let environment = actual.env.lock();
let paths = std::env::join_paths(
&environment
.path()
.expect("No path variable in the environment.")
.table_entries()
.map(|value| value.as_string().expect("Couldn't convert to string"))
.map(PathBuf::from)
.collect::<Vec<_>>(),
)
.expect("Couldn't join paths.")
.into_string()
.expect("Couldn't convert to string.");
assert_eq!(paths, expected);
});
Ok(())
}
}

View File

@ -1,9 +1,5 @@
#![recursion_limit = "2048"]
#[cfg(test)]
#[macro_use]
extern crate indexmap;
#[macro_use]
mod prelude;
@ -16,7 +12,6 @@ extern crate quickcheck_macros;
mod cli;
#[cfg(feature = "rustyline-support")]
mod completion;
mod env;
mod format;
#[cfg(feature = "rustyline-support")]
mod keybinding;
@ -30,13 +25,12 @@ pub use crate::cli::cli;
pub use crate::cli::{parse_and_eval, register_plugins, run_script_file};
pub use crate::cli::{NuScript, Options};
pub use crate::env::environment_syncer::EnvironmentSyncer;
pub use nu_command::commands::default_context::create_default_context;
pub use nu_data::config;
pub use nu_data::dict::TaggedListBuilder;
pub use nu_data::primitive;
pub use nu_data::value;
pub use nu_stream::{InputStream, InterruptibleStream, OutputStream};
pub use nu_stream::{ActionStream, InputStream, InterruptibleStream};
pub use nu_value_ext::ValueExt;
pub use num_traits::cast::ToPrimitive;

View File

@ -202,7 +202,7 @@ pub fn configure_rustyline_editor(
#[cfg(feature = "rustyline-support")]
pub fn nu_line_editor_helper(
context: &mut EvaluationContext,
context: &EvaluationContext,
config: &dyn nu_data::config::Conf,
) -> crate::shell::Helper {
let hinter = rustyline_hinter(config);
@ -224,7 +224,7 @@ pub fn rustyline_hinter(
Some(rustyline::hint::HistoryHinter {})
}
pub fn configure_ctrl_c(_context: &mut EvaluationContext) -> Result<(), Box<dyn Error>> {
pub fn configure_ctrl_c(_context: &EvaluationContext) -> Result<(), Box<dyn Error>> {
#[cfg(feature = "ctrlc")]
{
let cc = _context.ctrl_c.clone();

View File

@ -8,25 +8,10 @@ macro_rules! return_err {
};
}
#[macro_export]
macro_rules! stream {
($($expr:expr),*) => {{
let mut v = VecDeque::new();
$(
v.push_back($expr);
)*
v
}}
}
#[macro_export]
macro_rules! trace_out_stream {
(target: $target:tt, $desc:tt = $expr:expr) => {{
if log::log_enabled!(target: $target, log::Level::Trace) {
use futures::stream::StreamExt;
let objects = $expr.inspect(move |o| {
trace!(
target: $target,
@ -46,13 +31,12 @@ macro_rules! trace_out_stream {
}};
}
pub(crate) use futures::{Stream, StreamExt};
pub(crate) use nu_engine::Host;
#[allow(unused_imports)]
pub(crate) use nu_errors::ShellError;
#[allow(unused_imports)]
pub(crate) use nu_protocol::outln;
pub(crate) use nu_stream::OutputStream;
pub(crate) use nu_stream::ActionStream;
#[allow(unused_imports)]
pub(crate) use nu_value_ext::ValueExt;
#[allow(unused_imports)]
@ -60,33 +44,16 @@ pub(crate) use std::sync::atomic::Ordering;
#[allow(clippy::clippy::wrong_self_convention)]
pub trait FromInputStream {
fn from_input_stream(self) -> OutputStream;
fn from_input_stream(self) -> ActionStream;
}
impl<T> FromInputStream for T
where
T: Stream<Item = nu_protocol::Value> + Send + 'static,
T: Iterator<Item = nu_protocol::Value> + Send + Sync + 'static,
{
fn from_input_stream(self) -> OutputStream {
OutputStream {
values: self.map(nu_protocol::ReturnSuccess::value).boxed(),
}
}
}
#[allow(clippy::clippy::wrong_self_convention)]
pub trait ToOutputStream {
fn to_output_stream(self) -> OutputStream;
}
impl<T, U> ToOutputStream for T
where
T: Stream<Item = U> + Send + 'static,
U: Into<nu_protocol::ReturnValue>,
{
fn to_output_stream(self) -> OutputStream {
OutputStream {
values: self.map(|item| item.into()).boxed(),
fn from_input_stream(self) -> ActionStream {
ActionStream {
values: Box::new(self.map(nu_protocol::ReturnSuccess::value)),
}
}
}

View File

@ -1,5 +1,6 @@
use crate::completion;
use crate::shell::completer::NuCompleter;
use nu_ansi_term::Color;
use nu_engine::{DefaultPalette, EvaluationContext, Painter};
use nu_source::{Tag, Tagged};
use std::borrow::Cow::{self, Owned};
@ -79,7 +80,7 @@ impl rustyline::highlight::Highlighter for Helper {
}
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
Owned(Color::DarkGray.prefix().to_string() + hint + nu_ansi_term::ansi::RESET)
}
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
@ -149,6 +150,7 @@ impl rustyline::Helper for Helper {}
#[cfg(test)]
mod tests {
use super::*;
use nu_engine::EvaluationContext;
use rustyline::completion::Completer;
use rustyline::line_buffer::LineBuffer;
@ -162,7 +164,7 @@ mod tests {
buffer.insert_str(0, text);
buffer.set_pos(text.len() - 1);
let helper = Helper::new(EvaluationContext::basic().unwrap(), None);
let helper = Helper::new(EvaluationContext::basic(), None);
helper.update(&mut buffer, "cd ".len(), &replacement);
@ -182,7 +184,7 @@ mod tests {
buffer.insert_str(0, text);
buffer.set_pos(text.len() - 30);
let helper = Helper::new(EvaluationContext::basic().unwrap(), None);
let helper = Helper::new(EvaluationContext::basic(), None);
helper.update(&mut buffer, "cd ".len(), &replacement);

View File

@ -285,7 +285,7 @@ fn get_shape_of_expr(expr: &SpannedExpression) -> Option<SyntaxShape> {
nu_protocol::hir::Literal::Bare(_) => Some(SyntaxShape::String),
}
}
//Synthetic are expressions that are generated by the parser and not inputed by the user
//Synthetic are expressions that are generated by the parser and not inputted by the user
//ExternalWord is anything sent to external commands (?)
Expression::ExternalWord => Some(SyntaxShape::String),
Expression::Synthetic(_) => Some(SyntaxShape::String),
@ -377,17 +377,14 @@ impl VarSyntaxShapeDeductor {
.iter()
.map(|decl| {
let usage: VarUsage = decl.into();
let deduction = match deducer.inferences.get(&usage) {
Some(vec) => Some(vec.clone()),
None => None,
};
let deduction = deducer.inferences.get(&usage).cloned();
(decl.clone(), deduction)
})
.collect())
}
fn infer_shape(&mut self, block: &Block, scope: &Scope) -> Result<(), ShellError> {
trace!("Infering vars in shape");
trace!("Inferring vars in shape");
for group in &block.block {
for pipeline in &group.pipelines {
self.infer_pipeline(pipeline, scope)?;
@ -397,7 +394,7 @@ impl VarSyntaxShapeDeductor {
}
pub fn infer_pipeline(&mut self, pipeline: &Pipeline, scope: &Scope) -> Result<(), ShellError> {
trace!("Infering vars in pipeline");
trace!("Inferring vars in pipeline");
for (cmd_pipeline_idx, classified) in pipeline.list.iter().enumerate() {
match &classified {
ClassifiedCommand::Internal(internal) => {
@ -429,7 +426,7 @@ impl VarSyntaxShapeDeductor {
}
}
if let Some(named) = &internal.args.named {
trace!("Infering vars in named exprs");
trace!("Inferring vars in named exprs");
for (_name, val) in named.iter() {
if let NamedValue::Value(_, named_expr) = val {
self.infer_shapes_in_expr(
@ -443,7 +440,7 @@ impl VarSyntaxShapeDeductor {
}
ClassifiedCommand::Expr(spanned_expr) => {
trace!(
"Infering shapes in ClassifiedCommand::Expr: {:?}",
"Inferring shapes in ClassifiedCommand::Expr: {:?}",
spanned_expr
);
self.infer_shapes_in_expr((cmd_pipeline_idx, pipeline), spanned_expr, scope)?;
@ -459,7 +456,7 @@ impl VarSyntaxShapeDeductor {
positionals: &[SpannedExpression],
signature: &Signature,
) -> Result<(), ShellError> {
trace!("Infering vars in positionals");
trace!("Inferring vars in positionals");
//TODO currently correct inference for optional positionals is not implemented.
// See https://github.com/nushell/nushell/pull/2486 for a discussion about this
// For now we assume every variable in an optional positional is used as this optional
@ -500,7 +497,7 @@ impl VarSyntaxShapeDeductor {
named: &NamedArguments,
signature: &Signature,
) -> Result<(), ShellError> {
trace!("Infering vars in named");
trace!("Inferring vars in named");
for (name, val) in named.iter() {
if let NamedValue::Value(span, spanned_expr) = &val {
if let Expression::Variable(var_name, _) = &spanned_expr.expr {
@ -534,15 +531,15 @@ impl VarSyntaxShapeDeductor {
) -> Result<(), ShellError> {
match &spanned_expr.expr {
Expression::Binary(_) => {
trace!("Infering vars in bin expr");
trace!("Inferring vars in bin expr");
self.infer_shapes_in_binary_expr((pipeline_idx, pipeline), spanned_expr, scope)?;
}
Expression::Block(b) => {
trace!("Infering vars in block");
trace!("Inferring vars in block");
self.infer_shape(&b, scope)?;
}
Expression::Path(path) => {
trace!("Infering vars in path");
trace!("Inferring vars in path");
match &path.head.expr {
//PathMember can't be var yet (?)
//TODO Iterate over path parts and find var when implemented
@ -560,7 +557,7 @@ impl VarSyntaxShapeDeductor {
}
}
Expression::Range(range) => {
trace!("Infering vars in range");
trace!("Inferring vars in range");
if let Some(range_left) = &range.left {
if let Expression::Variable(var_name, _) = &range_left.expr {
self.checked_insert(
@ -585,13 +582,13 @@ impl VarSyntaxShapeDeductor {
}
}
Expression::List(inner_exprs) => {
trace!("Infering vars in list");
trace!("Inferring vars in list");
for expr in inner_exprs {
self.infer_shapes_in_expr((pipeline_idx, pipeline), expr, scope)?;
}
}
Expression::Invocation(invoc) => {
trace!("Infering vars in invocation: {:?}", invoc);
trace!("Inferring vars in invocation: {:?}", invoc);
self.infer_shape(invoc, scope)?;
}
Expression::Table(header, _rows) => {
@ -738,7 +735,7 @@ impl VarSyntaxShapeDeductor {
(pipeline_idx, pipeline): (usize, &Pipeline),
scope: &Scope,
) -> Result<(), ShellError> {
trace!("Infering shapes between var {:?} and expr {:?}", var, expr);
trace!("Inferring shapes between var {:?} and expr {:?}", var, expr);
let bin = spanned_to_binary(bin_spanned);
if let Expression::Literal(Literal::Operator(op)) = bin.op.expr {
match &op {
@ -1023,7 +1020,7 @@ impl VarSyntaxShapeDeductor {
Some(combination)
}
})
.filter_map(|elem| elem)
.flatten()
.collect()
}
//No any's intersection of both is result
@ -1044,7 +1041,7 @@ impl VarSyntaxShapeDeductor {
Some(combination)
}
})
.filter_map(|elem| elem)
.flatten()
.collect();
if intersection.is_empty() {
//TODO pass all labels somehow

View File

@ -5,30 +5,29 @@ description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-command"
version = "0.29.0"
version = "0.31.0"
[lib]
doctest = false
[dependencies]
nu-data = { version = "0.29.0", path = "../nu-data" }
nu-engine = { version = "0.29.0", path = "../nu-engine" }
nu-errors = { version = "0.29.0", path = "../nu-errors" }
nu-json = { version = "0.29.0", path = "../nu-json" }
nu-parser = { version = "0.29.0", path = "../nu-parser" }
nu-plugin = { version = "0.29.0", path = "../nu-plugin" }
nu-protocol = { version = "0.29.0", path = "../nu-protocol" }
nu-source = { version = "0.29.0", path = "../nu-source" }
nu-stream = { version = "0.29.0", path = "../nu-stream" }
nu-table = { version = "0.29.0", path = "../nu-table" }
nu-test-support = { version = "0.29.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.29.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.29.0", path = "../nu-ansi-term" }
nu-data = { version = "0.31.0", path = "../nu-data" }
nu-engine = { version = "0.31.0", path = "../nu-engine" }
nu-errors = { version = "0.31.0", path = "../nu-errors" }
nu-json = { version = "0.31.0", path = "../nu-json" }
nu-parser = { version = "0.31.0", path = "../nu-parser" }
nu-plugin = { version = "0.31.0", path = "../nu-plugin" }
nu-protocol = { version = "0.31.0", path = "../nu-protocol" }
nu-source = { version = "0.31.0", path = "../nu-source" }
nu-stream = { version = "0.31.0", path = "../nu-stream" }
nu-table = { version = "0.31.0", path = "../nu-table" }
nu-test-support = { version = "0.31.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.31.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.31.0", path = "../nu-ansi-term" }
nu-pretty-hex = { version = "0.31.0", path = "../nu-pretty-hex" }
Inflector = "0.11"
arboard = { version = "1.1.0", optional = true }
async-recursion = "0.3.2"
async-trait = "0.1.42"
base64 = "0.13.0"
bigdecimal = { version = "0.2.0", features = ["serde"] }
byte-unit = "4.0.9"
@ -51,13 +50,10 @@ encoding_rs = "0.8.28"
filesize = "0.2.0"
fs_extra = "1.2.0"
futures = { version = "0.3.12", features = ["compat", "io-compat"] }
futures-util = "0.3.12"
futures_codec = "0.4.1"
getset = "0.1.1"
glob = "0.3.0"
htmlescape = "0.3.1"
ical = "0.7.0"
ichwh = { version = "0.3.4", optional = true }
indexmap = { version = "1.6.1", features = ["serde-1"] }
itertools = "0.10.0"
lazy_static = "1.*"
@ -70,7 +66,6 @@ num-format = { version = "0.4.0", features = ["with-num-bigint"] }
num-traits = "0.2.14"
parking_lot = "0.11.1"
pin-utils = "0.1.0"
pretty-hex = "0.2.1"
ptree = { version = "0.3.1", optional = true }
query_interface = "0.3.5"
quick-xml = "0.21.0"
@ -101,7 +96,7 @@ trash = { version = "1.3.0", optional = true }
unicode-segmentation = "1.7.1"
url = "2.2.0"
uuid_crate = { package = "uuid", version = "0.8.2", features = ["v4"], optional = true }
which = { version = "4.0.2", optional = true }
which = { version = "4.1.0", optional = true }
zip = { version = "0.5.9", optional = true }
[target.'cfg(unix)'.dependencies]

View File

@ -4,7 +4,9 @@ pub(crate) mod macros;
mod from_delimited_data;
mod to_delimited_data;
pub(crate) mod all;
pub(crate) mod ansi;
pub(crate) mod any;
pub(crate) mod append;
pub(crate) mod args;
pub mod autoenv;
@ -68,7 +70,7 @@ pub(crate) mod histogram;
pub(crate) mod history;
pub(crate) mod if_;
pub(crate) mod insert;
pub(crate) mod into_int;
pub(crate) mod into;
pub(crate) mod keep;
pub(crate) mod last;
pub(crate) mod length;
@ -169,6 +171,10 @@ pub(crate) use each::EachWindow;
pub(crate) use echo::Echo;
pub(crate) use empty::Command as Empty;
pub(crate) use if_::If;
pub(crate) use into::Into;
pub(crate) use into::IntoBinary;
pub(crate) use into::IntoInt;
pub(crate) use into::IntoString;
pub(crate) use nu::NuPlugin;
pub(crate) use update::Command as Update;
pub(crate) mod kill;
@ -176,6 +182,8 @@ pub(crate) use kill::Kill;
pub(crate) mod clear;
pub(crate) use clear::Clear;
pub(crate) mod touch;
pub(crate) use all::Command as All;
pub(crate) use any::Command as Any;
pub(crate) use enter::Enter;
pub(crate) use every::Every;
pub(crate) use exec::Exec;
@ -208,7 +216,6 @@ pub(crate) use help::Help;
pub(crate) use histogram::Histogram;
pub(crate) use history::History;
pub(crate) use insert::Command as Insert;
pub(crate) use into_int::IntoInt;
pub(crate) use keep::{Keep, KeepUntil, KeepWhile};
pub(crate) use last::Last;
pub(crate) use length::Length;
@ -218,7 +225,8 @@ pub(crate) use lines::Lines;
pub(crate) use ls::Ls;
pub(crate) use math::{
Math, MathAbs, MathAverage, MathCeil, MathEval, MathFloor, MathMaximum, MathMedian,
MathMinimum, MathMode, MathProduct, MathRound, MathStddev, MathSummation, MathVariance,
MathMinimum, MathMode, MathProduct, MathRound, MathSqrt, MathStddev, MathSummation,
MathVariance,
};
pub(crate) use merge::Merge;
pub(crate) use mkdir::Mkdir;
@ -228,8 +236,8 @@ pub(crate) use nth::Nth;
pub(crate) use open::Open;
pub(crate) use parse::Parse;
pub(crate) use path::{
PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathExtension, PathFilestem,
PathJoin, PathType,
PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathJoin, PathParse, PathSplit,
PathType,
};
pub(crate) use pivot::Pivot;
pub(crate) use prepend::Prepend;
@ -285,7 +293,7 @@ pub(crate) use touch::Touch;
pub(crate) use uniq::Uniq;
pub(crate) use url_::{UrlCommand, UrlHost, UrlPath, UrlQuery, UrlScheme};
pub(crate) use version::Version;
pub(crate) use where_::Where;
pub(crate) use where_::Command as Where;
pub(crate) use which_::Which;
pub(crate) use with_env::WithEnv;
pub(crate) use wrap::Wrap;

View File

@ -0,0 +1,136 @@
use crate::prelude::*;
use nu_engine::evaluate_baseline_expr;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
hir::CapturedBlock, hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue,
};
pub struct Command;
struct AllArgs {
predicate: CapturedBlock,
}
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"all?"
}
fn signature(&self) -> Signature {
Signature::build("all?").required(
"condition",
SyntaxShape::RowCondition,
"the condition that must match",
)
}
fn usage(&self) -> &str {
"Find if the table rows matches the condition."
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
all(args)
}
fn examples(&self) -> Vec<Example> {
use nu_protocol::Value;
vec![
Example {
description: "Find if services are running",
example: "echo [[status]; [UP] [UP]] | all? status == UP",
result: Some(vec![Value::from(true)]),
},
Example {
description: "Check that all values are even",
example: "echo [2 4 6 8] | all? $(= $it mod 2) == 0",
result: Some(vec![Value::from(true)]),
},
]
}
}
fn all(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = EvaluationContext::from_args(&args);
let tag = args.call_info.name_tag.clone();
let args = args.evaluate_once()?;
let all_args = AllArgs {
predicate: args.req(0)?,
};
let err = Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
args.call_info.name_tag.clone(),
));
//This seems a little odd. Can't we have predicates with pipelines/multiple statements?
let condition = {
if all_args.predicate.block.block.len() != 1 {
return err;
}
match all_args.predicate.block.block[0].pipelines.get(0) {
Some(item) => match item.list.get(0) {
Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
_ => {
return err;
}
},
None => {
return err;
}
}
};
let scope = args.scope();
let init = Ok(InputStream::one(
UntaggedValue::boolean(true).into_value(&tag),
));
// Variables in nu are immutable. Having the same variable accross invocations
// of evaluate_baseline_expr does not mutate the variables and those each
// invocations are independent of each other!
scope.enter_scope();
scope.add_vars(&all_args.predicate.captured.entries);
let result = args.input.fold(init, move |acc, row| {
let condition = condition.clone();
let ctx = ctx.clone();
ctx.scope.add_var("$it", row);
let condition = evaluate_baseline_expr(&condition, &ctx);
let curr = acc?.drain_vec();
let curr = curr
.get(0)
.ok_or_else(|| ShellError::unexpected("No value to check with"))?;
let cond = curr.as_bool()?;
match condition {
Ok(condition) => match condition.as_bool() {
Ok(b) => Ok(InputStream::one(
UntaggedValue::boolean(cond && b).into_value(&curr.tag),
)),
Err(e) => Err(e),
},
Err(e) => Err(e),
}
});
scope.exit_scope();
Ok(result?.to_output_stream())
}
#[cfg(test)]
mod tests {
use super::Command;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(Command {})
}
}

View File

@ -2,19 +2,11 @@ use crate::prelude::*;
use nu_ansi_term::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct Command;
#[derive(Deserialize)]
struct AnsiArgs {
code: Value,
escape: Option<Tagged<String>>,
osc: Option<Tagged<String>>,
}
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"ansi"
@ -120,8 +112,12 @@ Format: #
]
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let (AnsiArgs { code, escape, osc }, _) = args.process().await?;
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let code: Option<Tagged<String>> = args.opt(0)?;
let escape: Option<Tagged<String>> = args.get_flag("escape")?;
let osc: Option<Tagged<String>> = args.get_flag("osc")?;
if let Some(e) = escape {
let esc_vec: Vec<char> = e.item.chars().collect();
@ -133,9 +129,9 @@ Format: #
));
}
let output = format!("\x1b[{}", e.item);
return Ok(OutputStream::one(ReturnSuccess::value(
return Ok(OutputStream::one(
UntaggedValue::string(output).into_value(e.tag()),
)));
));
}
if let Some(o) = osc {
@ -151,30 +147,37 @@ Format: #
//Operating system command aka osc ESC ] <- note the right brace, not left brace for osc
// OCS's need to end with a bell '\x07' char
let output = format!("\x1b]{};", o.item);
return Ok(OutputStream::one(ReturnSuccess::value(
return Ok(OutputStream::one(
UntaggedValue::string(output).into_value(o.tag()),
)));
));
}
let code_string = code.as_string()?;
let ansi_code = str_to_ansi(code_string);
if let Some(code) = code {
let ansi_code = str_to_ansi(&code.item);
if let Some(output) = ansi_code {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(code.tag()),
)))
if let Some(output) = ansi_code {
Ok(OutputStream::one(
UntaggedValue::string(output).into_value(code.tag()),
))
} else {
Err(ShellError::labeled_error(
"Unknown ansi code",
"unknown ansi code",
code.tag(),
))
}
} else {
Err(ShellError::labeled_error(
"Unknown ansi code",
"unknown ansi code",
code.tag(),
"Expected ansi code",
"expect ansi code",
args.call_info.name_tag.clone(),
))
}
}
}
pub fn str_to_ansi(s: String) -> Option<String> {
match s.as_str() {
pub fn str_to_ansi(s: &str) -> Option<String> {
match s {
"g" | "green" => Some(Color::Green.prefix().to_string()),
"gb" | "green_bold" => Some(Color::Green.bold().prefix().to_string()),
"gu" | "green_underline" => Some(Color::Green.underline().prefix().to_string()),
@ -330,7 +333,7 @@ pub fn str_to_ansi(s: String) -> Option<String> {
// Ansi RGB - Needs to be 32;2;r;g;b or 48;2;r;g;b
// assuming the rgb will be passed via command and no here
"rgb_fg" => Some("\x1b[32;2;".to_string()),
"rgb_fg" => Some("\x1b[38;2;".to_string()),
"rgb_bg" => Some("\x1b[48;2;".to_string()),
// Ansi color index - Needs 38;5;idx or 48;5;idx where idx = 0 to 255

View File

@ -2,20 +2,12 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::ShellTypeName;
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tag;
use strip_ansi_escapes::strip;
pub struct SubCommand;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"ansi strip"
@ -32,8 +24,8 @@ impl WholeStreamCommand for SubCommand {
"strip ansi escape sequences from string"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
operate(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
operate(args)
}
fn examples(&self) -> Vec<Example> {
@ -45,14 +37,16 @@ impl WholeStreamCommand for SubCommand {
}
}
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest }, input) = args.process().await?;
let column_paths: Vec<_> = rest;
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
Ok(input
let column_paths: Vec<_> = args.rest_args()?;
let result: Vec<Value> = args
.input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?)
action(&v, v.tag())
} else {
let mut ret = v;
@ -63,10 +57,12 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
)?;
}
ReturnSuccess::value(ret)
Ok(ret)
}
})
.to_output_stream())
.collect::<Result<Vec<Value>, _>>()?;
Ok(OutputStream::from_stream(result.into_iter()))
}
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -0,0 +1,137 @@
use crate::prelude::*;
use nu_engine::evaluate_baseline_expr;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
hir::CapturedBlock, hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue,
};
pub struct Command;
struct AnyArgs {
predicate: CapturedBlock,
}
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"any?"
}
fn signature(&self) -> Signature {
Signature::build("any?").required(
"condition",
SyntaxShape::RowCondition,
"the condition that must match",
)
}
fn usage(&self) -> &str {
"Find if the table rows matches the condition."
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
any(args)
}
fn examples(&self) -> Vec<Example> {
use nu_protocol::Value;
vec![
Example {
description: "Find if a service is not running",
example: "echo [[status]; [UP] [DOWN] [UP]] | any? status == DOWN",
result: Some(vec![Value::from(true)]),
},
Example {
description: "Check if any of the values is odd",
example: "echo [2 4 1 6 8] | any? $(= $it mod 2) == 1",
result: Some(vec![Value::from(true)]),
},
]
}
}
fn any(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = EvaluationContext::from_args(&args);
let tag = args.call_info.name_tag.clone();
let args = args.evaluate_once()?;
let any_args = AnyArgs {
predicate: args.req(0)?,
};
let err = Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
args.call_info.name_tag.clone(),
));
//This seems a little odd. Can't we have predicates with pipelines/multiple statements?
let condition = {
if any_args.predicate.block.block.len() != 1 {
return err;
}
match any_args.predicate.block.block[0].pipelines.get(0) {
Some(item) => match item.list.get(0) {
Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
_ => {
return err;
}
},
None => {
return err;
}
}
};
let scope = args.scope();
let init = Ok(InputStream::one(
UntaggedValue::boolean(false).into_value(&tag),
));
// Variables in nu are immutable. Having the same variable accross invocations
// of evaluate_baseline_expr does not mutate the variables and thus each
// invocations are independent of each other!
scope.enter_scope();
scope.add_vars(&any_args.predicate.captured.entries);
let result = args.input.fold(init, move |acc, row| {
let condition = condition.clone();
let ctx = ctx.clone();
ctx.scope.add_var("$it", row);
let condition = evaluate_baseline_expr(&condition, &ctx);
let curr = acc?.drain_vec();
let curr = curr
.get(0)
.ok_or_else(|| ShellError::unexpected("No value to check with"))?;
let cond = curr.as_bool()?;
match condition {
Ok(condition) => match condition.as_bool() {
Ok(b) => Ok(InputStream::one(
UntaggedValue::boolean(cond || b).into_value(&curr.tag),
)),
Err(e) => Err(e),
},
Err(e) => Err(e),
}
});
scope.exit_scope();
Ok(result?.to_output_stream())
}
#[cfg(test)]
mod tests {
use super::Command;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(Command {})
}
}

View File

@ -1,7 +1,7 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
#[derive(Deserialize)]
struct Arguments {
@ -10,7 +10,6 @@ struct Arguments {
pub struct Command;
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"append"
@ -28,13 +27,14 @@ impl WholeStreamCommand for Command {
"Append a row to the table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { mut value }, input) = args.process().await?;
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { mut value }, mut input) = args.process()?;
let input: Vec<Value> = input.collect().await;
let mut prepend = vec![];
if let Some(first) = input.get(0) {
if let Some(first) = input.next() {
value.tag = first.tag();
prepend.push(first);
}
// Checks if we are trying to append a row literal
@ -48,18 +48,13 @@ impl WholeStreamCommand for Command {
}
}
Ok(futures::stream::iter(
input
.into_iter()
.chain(vec![value])
.map(ReturnSuccess::value),
)
.to_output_stream())
Ok(prepend
.into_iter()
.chain(input.into_iter().chain(vec![value]))
.to_output_stream())
}
fn examples(&self) -> Vec<Example> {
use nu_protocol::row;
vec![
Example {
description: "Add values to the end of the table",

View File

@ -2,50 +2,8 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use serde::Deserialize;
use serde::Serialize;
use sha2::{Digest, Sha256};
use std::io::Read;
use std::path::{Path, PathBuf};
pub struct Autoenv;
#[derive(Deserialize, Serialize, Debug, Default)]
pub struct Trusted {
pub files: IndexMap<String, Vec<u8>>,
}
impl Trusted {
pub fn new() -> Self {
Trusted {
files: IndexMap::new(),
}
}
}
pub fn file_is_trusted(nu_env_file: &Path, content: &[u8]) -> Result<bool, ShellError> {
let contentdigest = Sha256::digest(&content).as_slice().to_vec();
let nufile = std::fs::canonicalize(nu_env_file)?;
let trusted = read_trusted()?;
Ok(trusted.files.get(&nufile.to_string_lossy().to_string()) == Some(&contentdigest))
}
pub fn read_trusted() -> Result<Trusted, ShellError> {
let config_path = config::default_path_for(&Some(PathBuf::from("nu-env.toml")))?;
let mut file = std::fs::OpenOptions::new()
.read(true)
.create(true)
.write(true)
.open(config_path)
.map_err(|_| ShellError::untagged_runtime_error("Couldn't open nu-env.toml"))?;
let mut doc = String::new();
file.read_to_string(&mut doc)?;
let allowed = toml::de::from_str(doc.as_str()).unwrap_or_else(|_| Trusted::new());
Ok(allowed)
}
#[async_trait]
impl WholeStreamCommand for Autoenv {
fn name(&self) -> &str {
"autoenv"
@ -56,19 +14,20 @@ impl WholeStreamCommand for Autoenv {
fn extra_usage(&self) -> &str {
// "Mark a .nu-env file in a directory as trusted. Needs to be re-run after each change to the file or its filepath."
r#"Create a file called .nu-env in any directory and run 'autoenv trust' to let nushell read it when entering the directory.
The file can contain several optional sections:
env: environment variables to set when visiting the directory. The variables are unset after leaving the directory and any overwritten values are restored.
scriptvars: environment variables that should be set to the return value of a script. After they have been set, they behave in the same way as variables set in the env section.
scripts: scripts to run when entering the directory or leaving it."#
r#"Create a file called .nu-env in any directory and run 'autoenv trust' to let nushell load it when entering the directory.
The .nu-env file has the same format as your $HOME/nu/config.toml file. By loading a .nu-env file the following applies:
- environment variables (section \"[env]\") are loaded from the .nu-env file. Those env variables only exist in this directory (and children directories)
- the \"startup\" commands are run when entering the directory
- the \"on_exit\" commands are run when leaving the directory
"#
}
fn signature(&self) -> Signature {
Signature::build("autoenv")
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(get_full_help(&Autoenv, &args.scope)).into_value(Tag::unknown()),
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(get_full_help(&Autoenv, args.scope())).into_value(Tag::unknown()),
)))
}
@ -76,15 +35,12 @@ The file can contain several optional sections:
vec![Example {
description: "Example .nu-env file",
example: r#"cat .nu-env
startup = ["echo ...entering the directory", "echo 1 2 3"]
on_exit = ["echo ...leaving the directory"]
[env]
mykey = "myvalue"
[scriptvars]
myscript = "echo myval"
[scripts]
entryscripts = ["touch hello.txt", "touch hello2.txt"]
exitscripts = ["touch bye.txt"]"#,
"#,
result: None,
}]
}

View File

@ -1,5 +1,5 @@
use super::autoenv::read_trusted;
use crate::prelude::*;
use nu_data::config::read_trusted;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::SyntaxShape;
@ -8,7 +8,6 @@ use sha2::{Digest, Sha256};
use std::{fs, path::PathBuf};
pub struct AutoenvTrust;
#[async_trait]
impl WholeStreamCommand for AutoenvTrust {
fn name(&self) -> &str {
"autoenv trust"
@ -22,11 +21,11 @@ impl WholeStreamCommand for AutoenvTrust {
"Trust a .nu-env file in the current or given directory"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args);
let file_to_trust = match args.call_info.evaluate(&ctx).await?.args.nth(0) {
let file_to_trust = match args.call_info.evaluate(&ctx)?.args.nth(0) {
Some(Value {
value: UntaggedValue::Primitive(Primitive::String(ref path)),
tag: _,
@ -56,7 +55,7 @@ impl WholeStreamCommand for AutoenvTrust {
})?;
fs::write(config_path, tomlstr).expect("Couldn't write to toml file");
Ok(OutputStream::one(ReturnSuccess::value(
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(".nu-env trusted!").into_value(tag),
)))
}

View File

@ -1,5 +1,5 @@
use super::autoenv::Trusted;
use crate::prelude::*;
use nu_data::config::Trusted;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::SyntaxShape;
@ -8,7 +8,6 @@ use std::io::Read;
use std::{fs, path::PathBuf};
pub struct AutoenvUnTrust;
#[async_trait]
impl WholeStreamCommand for AutoenvUnTrust {
fn name(&self) -> &str {
"autoenv untrust"
@ -26,10 +25,10 @@ impl WholeStreamCommand for AutoenvUnTrust {
"Untrust a .nu-env file in the current or given directory"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args);
let file_to_untrust = match args.call_info.evaluate(&ctx).await?.args.nth(0) {
let file_to_untrust = match args.call_info.evaluate(&ctx)?.args.nth(0) {
Some(Value {
value: UntaggedValue::Primitive(Primitive::String(ref path)),
tag: _,
@ -80,7 +79,7 @@ impl WholeStreamCommand for AutoenvUnTrust {
})?;
fs::write(config_path, tomlstr).expect("Couldn't write to toml file");
Ok(OutputStream::one(ReturnSuccess::value(
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(".nu-env untrusted!").into_value(tag),
)))
}

View File

@ -1,4 +1,4 @@
use crate::commands::autoview::options::{ConfigExtensions, NuConfig as AutoViewConfiguration};
use crate::commands::autoview::options::ConfigExtensions;
use crate::prelude::*;
use crate::primitive::get_color_config;
use nu_data::value::format_leaf;
@ -7,12 +7,9 @@ use nu_errors::ShellError;
use nu_protocol::hir::{self, Expression, ExternalRedirection, Literal, SpannedExpression};
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
use nu_table::TextStyle;
use parking_lot::Mutex;
use std::sync::atomic::AtomicBool;
pub struct Command;
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"autoview"
@ -26,17 +23,8 @@ impl WholeStreamCommand for Command {
"View the contents of the pipeline as a table or list."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
autoview(RunnableContext {
input: args.input,
scope: args.scope.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
})
.await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
autoview(args)
}
fn examples(&self) -> Vec<Example> {
@ -55,57 +43,29 @@ impl WholeStreamCommand for Command {
}
}
pub struct RunnableContextWithoutInput {
pub shell_manager: ShellManager,
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
pub ctrl_c: Arc<AtomicBool>,
pub scope: Scope,
pub name: Tag,
}
pub fn autoview(args: CommandArgs) -> Result<OutputStream, ShellError> {
let configuration = args.configs().lock().global_config();
let tag = args.call_info.name_tag.clone();
impl RunnableContextWithoutInput {
pub fn convert(context: RunnableContext) -> (InputStream, RunnableContextWithoutInput) {
let new_context = RunnableContextWithoutInput {
shell_manager: context.shell_manager,
host: context.host,
ctrl_c: context.ctrl_c,
current_errors: context.current_errors,
scope: context.scope,
name: context.name,
};
(context.input, new_context)
}
}
let binary = args.scope().get_command("binaryview");
let text = args.scope().get_command("textview");
let table = args.scope().get_command("table");
let context = args.context;
let mut input_stream = args.input;
pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellError> {
let configuration = AutoViewConfiguration::new();
let binary = context.get_command("binaryview");
let text = context.get_command("textview");
let table = context.get_command("table");
let pivot_mode = configuration.pivot_mode();
let (mut input_stream, context) = RunnableContextWithoutInput::convert(context);
let term_width = context.host.lock().width();
let color_hm = get_color_config();
if let Some(x) = input_stream.next().await {
match input_stream.next().await {
if let Some(x) = input_stream.next() {
match input_stream.next() {
Some(y) => {
let ctrl_c = context.ctrl_c.clone();
let xy = vec![x, y];
let xy_stream = futures::stream::iter(xy)
.chain(input_stream)
.interruptible(ctrl_c);
let xy_stream = xy.into_iter().chain(input_stream).interruptible(ctrl_c);
let stream = InputStream::from_stream(xy_stream);
if let Some(table) = table {
let command_args = create_default_command_args(&context).with_input(stream);
let result = table.run(command_args).await?;
result.collect::<Vec<_>>().await;
let command_args = create_default_command_args(&context, stream, tag);
let result = table.run(command_args)?;
let _ = result.collect::<Vec<_>>();
}
}
_ => {
@ -115,14 +75,15 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
tag: Tag { anchor, span },
} if anchor.is_some() => {
if let Some(text) = text {
let mut stream = VecDeque::new();
stream.push_back(
UntaggedValue::string(s).into_value(Tag { anchor, span }),
let command_args = create_default_command_args(
&context,
InputStream::one(
UntaggedValue::string(s).into_value(Tag { anchor, span }),
),
tag,
);
let command_args =
create_default_command_args(&context).with_input(stream);
let result = text.run(command_args).await?;
result.collect::<Vec<_>>().await;
let result = text.run_with_actions(command_args)?;
let _ = result.collect::<Vec<_>>();
} else {
out!("{}", s);
}
@ -199,14 +160,12 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
..
} => {
if let Some(binary) = binary {
let mut stream = VecDeque::new();
stream.push_back(x);
let command_args =
create_default_command_args(&context).with_input(stream);
let result = binary.run(command_args).await?;
result.collect::<Vec<_>>().await;
create_default_command_args(&context, InputStream::one(x), tag);
let result = binary.run_with_actions(command_args)?;
let _ = result.collect::<Vec<_>>();
} else {
use pretty_hex::*;
use nu_pretty_hex::*;
out!("{:?}", b.hex_dump());
}
}
@ -219,41 +178,55 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
}
Value {
value: UntaggedValue::Row(row),
value: UntaggedValue::Row(ref row),
..
} if pivot_mode.is_always()
|| (pivot_mode.is_auto()
&& (row
.entries
.iter()
.map(|(_, v)| v.convert_to_string())
.collect::<Vec<_>>()
.iter()
.fold(0usize, |acc, len| acc + len.len())
+ row.entries.iter().count() * 2)
> term_width) =>
{
let mut entries = vec![];
for (key, value) in row.entries.iter() {
entries.push(vec![
nu_table::StyledString::new(
key.to_string(),
TextStyle::new()
.alignment(nu_table::Alignment::Left)
.fg(nu_ansi_term::Color::Green)
.bold(Some(true)),
),
nu_table::StyledString::new(
format_leaf(value).plain_string(100_000),
nu_table::TextStyle::basic_left(),
),
]);
} => {
let pivot_mode = configuration.pivot_mode();
let term_width = context.host.lock().width();
if pivot_mode.is_always()
|| (pivot_mode.is_auto()
&& (row
.entries
.iter()
.map(|(_, v)| v.convert_to_string())
.collect::<Vec<_>>()
.iter()
.fold(0usize, |acc, len| acc + len.len())
+ row.entries.iter().count() * 2)
> term_width)
{
let mut entries = vec![];
for (key, value) in row.entries.iter() {
entries.push(vec![
nu_table::StyledString::new(
key.to_string(),
TextStyle::new()
.alignment(nu_table::Alignment::Left)
.fg(nu_ansi_term::Color::Green)
.bold(Some(true)),
),
nu_table::StyledString::new(
format_leaf(value).plain_string(100_000),
nu_table::TextStyle::basic_left(),
),
]);
}
let color_hm = get_color_config(&configuration);
let table =
nu_table::Table::new(vec![], entries, nu_table::Theme::compact());
println!("{}", nu_table::draw_table(&table, term_width, &color_hm));
} else if let Some(table) = table {
let command_args =
create_default_command_args(&context, InputStream::one(x), tag);
let result = table.run(command_args)?;
let _ = result.collect::<Vec<_>>();
} else {
out!("{:?}", row);
}
let table =
nu_table::Table::new(vec![], entries, nu_table::Theme::compact());
println!("{}", nu_table::draw_table(&table, term_width, &color_hm));
}
Value {
value: UntaggedValue::Primitive(Primitive::Nothing),
@ -265,12 +238,10 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
value: ref item, ..
} => {
if let Some(table) = table {
let mut stream = VecDeque::new();
stream.push_back(x);
let command_args =
create_default_command_args(&context).with_input(stream);
let result = table.run(command_args).await?;
result.collect::<Vec<_>>().await;
create_default_command_args(&context, InputStream::one(x), tag);
let result = table.run(command_args)?;
let _ = result.collect::<Vec<_>>();
} else {
out!("{:?}", item);
}
@ -280,16 +251,17 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
}
}
Ok(OutputStream::empty())
Ok(InputStream::empty())
}
fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawCommandArgs {
let span = context.name.span;
RawCommandArgs {
host: context.host.clone(),
ctrl_c: context.ctrl_c.clone(),
current_errors: context.current_errors.clone(),
shell_manager: context.shell_manager.clone(),
fn create_default_command_args(
context: &EvaluationContext,
input: InputStream,
tag: Tag,
) -> CommandArgs {
let span = tag.span;
CommandArgs {
context: context.clone(),
call_info: UnevaluatedCallInfo {
args: hir::Call {
head: Box::new(SpannedExpression::new(
@ -301,9 +273,9 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
span,
external_redirection: ExternalRedirection::Stdout,
},
name_tag: context.name.clone(),
name_tag: tag,
},
scope: Scope::new(),
input,
}
}

View File

@ -5,7 +5,10 @@ use nu_engine::run_block;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
hir::{Block, CapturedBlock, ClassifiedCommand, Group, InternalCommand, Pipeline},
hir::{
Block, CapturedBlock, ClassifiedCommand, ExternalRedirection, Group, InternalCommand,
Pipeline,
},
Dictionary, Signature, SyntaxShape, UntaggedValue, Value,
};
use rand::{
@ -23,7 +26,6 @@ struct BenchmarkArgs {
passthrough: Option<CapturedBlock>,
}
#[async_trait]
impl WholeStreamCommand for Benchmark {
fn name(&self) -> &str {
"benchmark"
@ -48,8 +50,8 @@ impl WholeStreamCommand for Benchmark {
"Runs a block and returns the time it took to execute it."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
benchmark(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
benchmark(args)
}
fn examples(&self) -> Vec<Example> {
@ -68,11 +70,16 @@ impl WholeStreamCommand for Benchmark {
}
}
async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = raw_args.call_info.args.span;
let mut context = EvaluationContext::from_args(&raw_args);
let scope = raw_args.scope.clone();
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process().await?;
fn benchmark(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.args.span;
let mut context = EvaluationContext::from_args(&args);
let scope = args.scope().clone();
let args = args.evaluate_once()?;
let cmd_args = BenchmarkArgs {
block: args.req(0)?,
passthrough: args.get_flag("passthrough")?,
};
let env = scope.get_env_vars();
let name = generate_free_name(&env);
@ -82,15 +89,21 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let start_time = Instant::now();
// #[cfg(feature = "rich-benchmark")]
// let start = time().await;
// let start = time();
context.scope.enter_scope();
let result = run_block(&block.block, &context, input).await;
let result = run_block(
&cmd_args.block.block,
&context,
args.input,
ExternalRedirection::StdoutAndStderr,
);
context.scope.exit_scope();
let output = result?.into_vec().await;
let output = result?.into_vec();
// #[cfg(feature = "rich-benchmark")]
// let end = time().await;
// let end = time();
let end_time = Instant::now();
context.clear_errors();
@ -102,7 +115,7 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let real_time = into_big_int(end_time - start_time);
indexmap.insert("real time".to_string(), real_time);
benchmark_output(indexmap, output, passthrough, &tag, &mut context).await
benchmark_output(indexmap, output, cmd_args.passthrough, &tag, &mut context)
}
// return advanced stats
// #[cfg(feature = "rich-benchmark")]
@ -121,7 +134,7 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
// let idle_time = into_big_int(end.idle() - start.idle());
// indexmap.insert("idle time".to_string(), idle_time);
// benchmark_output(indexmap, output, passthrough, &tag, &mut context).await
// benchmark_output(indexmap, output, passthrough, &tag, &mut context)
// } else {
// Err(ShellError::untagged_runtime_error(
// "Could not retrieve CPU time",
@ -129,7 +142,7 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
// }
}
async fn benchmark_output<T, Output>(
fn benchmark_output<T, Output>(
indexmap: IndexMap<String, BigInt>,
block_output: Output,
passthrough: Option<CapturedBlock>,
@ -155,7 +168,12 @@ where
let time_block = add_implicit_autoview(time_block.block);
context.scope.enter_scope();
let result = run_block(&time_block, context, benchmark_output).await;
let result = run_block(
&time_block,
context,
benchmark_output,
ExternalRedirection::StdoutAndStderr,
);
context.scope.exit_scope();
result?;
context.clear_errors();
@ -167,21 +185,23 @@ where
}
}
fn add_implicit_autoview(mut block: Block) -> Block {
if block.block.is_empty() {
let group = Group::new(
vec![{
let mut commands = Pipeline::new(block.span);
commands.push(ClassifiedCommand::Internal(InternalCommand::new(
"autoview".to_string(),
block.span,
block.span,
)));
commands
}],
block.span,
);
block.push(group);
fn add_implicit_autoview(mut block: Arc<Block>) -> Arc<Block> {
if let Some(block) = std::sync::Arc::<nu_protocol::hir::Block>::get_mut(&mut block) {
if block.block.is_empty() {
let group = Group::new(
vec![{
let mut commands = Pipeline::new(block.span);
commands.push(ClassifiedCommand::Internal(InternalCommand::new(
"autoview".to_string(),
block.span,
block.span,
)));
commands
}],
block.span,
);
block.push(group);
}
}
block
}

View File

@ -3,16 +3,10 @@ use nu_errors::ShellError;
use nu_data::value::format_leaf;
use nu_engine::WholeStreamCommand;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
#[derive(Deserialize)]
pub struct BuildStringArgs {
rest: Vec<Value>,
}
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
pub struct BuildString;
#[async_trait]
impl WholeStreamCommand for BuildString {
fn name(&self) -> &str {
"build-string"
@ -27,9 +21,10 @@ impl WholeStreamCommand for BuildString {
"Builds a string from the arguments."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (BuildStringArgs { rest }, _) = args.process().await?;
let args = args.evaluate_once()?;
let rest: Vec<Value> = args.rest(0)?;
let mut output_string = String::new();
@ -37,9 +32,9 @@ impl WholeStreamCommand for BuildString {
output_string.push_str(&format_leaf(&r).plain_string(100_000))
}
Ok(OutputStream::one(ReturnSuccess::value(
Ok(OutputStream::one(
UntaggedValue::string(output_string).into_value(tag),
)))
))
}
fn examples(&self) -> Vec<Example> {

View File

@ -1,13 +1,12 @@
use crate::prelude::*;
use chrono::{Datelike, Local, NaiveDate};
use indexmap::IndexMap;
use nu_engine::{EvaluatedWholeStreamCommandArgs, WholeStreamCommand};
use nu_engine::{EvaluatedCommandArgs, WholeStreamCommand};
use nu_errors::ShellError;
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
pub struct Cal;
#[async_trait]
impl WholeStreamCommand for Cal {
fn name(&self) -> &str {
"cal"
@ -41,8 +40,8 @@ impl WholeStreamCommand for Cal {
"Display a calendar."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
cal(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
cal(args)
}
fn examples(&self) -> Vec<Example> {
@ -66,8 +65,8 @@ impl WholeStreamCommand for Cal {
}
}
pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
pub fn cal(args: CommandArgs) -> Result<ActionStream, ShellError> {
let args = args.evaluate_once()?;
let mut calendar_vec_deque = VecDeque::new();
let tag = args.call_info.name_tag.clone();
@ -76,7 +75,7 @@ pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut selected_year: i32 = current_year;
let mut current_day_option: Option<u32> = Some(current_day);
let month_range = if let Some(full_year_value) = args.get("full-year") {
let month_range = if let Some(full_year_value) = args.call_info.args.get("full-year") {
if let Ok(year_u64) = full_year_value.as_u64() {
selected_year = year_u64 as i32;
@ -102,7 +101,7 @@ pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
current_day_option,
)?;
Ok(futures::stream::iter(calendar_vec_deque).to_output_stream())
Ok(calendar_vec_deque.into_iter().to_action_stream())
}
fn get_invalid_year_shell_error(year_tag: &Tag) -> ShellError {
@ -166,7 +165,7 @@ fn get_current_date() -> (i32, u32, u32) {
}
fn add_months_of_year_to_table(
args: &EvaluatedWholeStreamCommandArgs,
args: &EvaluatedCommandArgs,
mut calendar_vec_deque: &mut VecDeque<Value>,
tag: &Tag,
selected_year: i32,
@ -199,7 +198,7 @@ fn add_months_of_year_to_table(
}
fn add_month_to_table(
args: &EvaluatedWholeStreamCommandArgs,
args: &EvaluatedCommandArgs,
calendar_vec_deque: &mut VecDeque<Value>,
tag: &Tag,
selected_year: i32,
@ -210,7 +209,7 @@ fn add_month_to_table(
let month_helper = match month_helper_result {
Ok(month_helper) => month_helper,
Err(()) => match args.get("full-year") {
Err(()) => match args.call_info.args.get("full-year") {
Some(full_year_value) => {
return Err(get_invalid_year_shell_error(&full_year_value.tag()))
}
@ -236,7 +235,7 @@ fn add_month_to_table(
let mut week_start_day = days_of_the_week[0].to_string();
if let Some(week_start_value) = args.get("week-start") {
if let Some(week_start_value) = args.call_info.args.get("week-start") {
if let Ok(day) = week_start_value.as_string() {
if days_of_the_week.contains(&day.as_str()) {
week_start_day = day;
@ -265,10 +264,10 @@ fn add_month_to_table(
let mut day_number: u32 = 1;
let day_limit: u32 = total_start_offset + month_helper.number_of_days_in_month;
let should_show_year_column = args.has("year");
let should_show_quarter_column = args.has("quarter");
let should_show_month_column = args.has("month");
let should_show_month_names = args.has("month-names");
let should_show_year_column = args.has_flag("year");
let should_show_quarter_column = args.has_flag("quarter");
let should_show_month_column = args.has_flag("month");
let should_show_month_names = args.has_flag("month-names");
while day_number <= day_limit {
let mut indexmap = IndexMap::new();

View File

@ -7,7 +7,6 @@ use nu_protocol::{Signature, SyntaxShape};
pub struct Cd;
#[async_trait]
impl WholeStreamCommand for Cd {
fn name(&self) -> &str {
"cd"
@ -25,10 +24,10 @@ impl WholeStreamCommand for Cd {
"Change to a new path."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone();
let shell_manager = args.shell_manager.clone();
let (args, _): (CdArgs, _) = args.process().await?;
let shell_manager = args.shell_manager();
let (args, _): (CdArgs, _) = args.process()?;
shell_manager.cd(args, name)
}

View File

@ -1,19 +1,11 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_engine::{FromValue, WholeStreamCommand};
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct Char;
#[derive(Deserialize)]
struct CharArgs {
name: Tagged<String>,
rest: Vec<Tagged<String>>,
unicode: bool,
}
#[async_trait]
impl WholeStreamCommand for Char {
fn name(&self) -> &str {
"char"
@ -26,8 +18,8 @@ impl WholeStreamCommand for Char {
SyntaxShape::Any,
"the name of the character to output",
)
.rest(SyntaxShape::String, "multiple unicode bytes")
.switch("unicode", "unicode string i.e. 1f378", Some('u'))
.rest(SyntaxShape::String, "multiple Unicode bytes")
.switch("unicode", "Unicode string i.e. 1f378", Some('u'))
}
fn usage(&self) -> &str {
@ -51,12 +43,12 @@ impl WholeStreamCommand for Char {
]),
},
Example {
description: "Output unicode character",
description: "Output Unicode character",
example: r#"char -u 1f378"#,
result: Some(vec![Value::from("\u{1f378}")]),
},
Example {
description: "Output multi-byte unicode character",
description: "Output multi-byte Unicode character",
example: r#"char -u 1F468 200D 1F466 200D 1F466"#,
result: Some(vec![Value::from(
"\u{1F468}\u{200D}\u{1F466}\u{200D}\u{1F466}",
@ -65,19 +57,16 @@ impl WholeStreamCommand for Char {
]
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let (
CharArgs {
name,
rest,
unicode,
},
_,
) = args.process().await?;
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let args = args.evaluate_once()?;
let name: Tagged<String> = args.req(0)?;
let rest: Vec<Value> = args.rest(1)?;
let unicode = args.has_flag("unicode");
if unicode {
if !rest.is_empty() {
// Setup a new buffer to put all the unicode bytes in
// Setup a new buffer to put all the Unicode bytes in
let mut multi_byte = String::new();
// Get the first byte
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
@ -87,25 +76,26 @@ impl WholeStreamCommand for Char {
}
// Get the rest of the bytes
for byte_part in rest {
let byte_part: Tagged<String> = FromValue::from_value(&byte_part)?;
let decoded_char = string_to_unicode_char(&byte_part, &byte_part.tag);
match decoded_char {
Ok(ch) => multi_byte.push(ch),
Err(e) => return Err(e),
}
}
Ok(OutputStream::one(ReturnSuccess::value(
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(multi_byte).into_value(name.tag),
)))
} else {
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
if let Ok(ch) = decoded_char {
Ok(OutputStream::one(ReturnSuccess::value(
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(ch).into_value(name.tag()),
)))
} else {
Err(ShellError::labeled_error(
"error decoding unicode character",
"error decoding unicode character",
"error decoding Unicode character",
"error decoding Unicode character",
name.tag(),
))
}
@ -113,7 +103,7 @@ impl WholeStreamCommand for Char {
} else {
let special_character = str_to_character(&name.item);
if let Some(output) = special_character {
Ok(OutputStream::one(ReturnSuccess::value(
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(name.tag()),
)))
} else {
@ -136,8 +126,8 @@ fn string_to_unicode_char(s: &str, t: &Tag) -> Result<char, ShellError> {
Ok(ch)
} else {
Err(ShellError::labeled_error(
"error decoding unicode character",
"error decoding unicode character",
"error decoding Unicode character",
"error decoding Unicode character",
t,
))
}

View File

@ -6,7 +6,6 @@ use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
#[derive(Clone)]
pub struct Chart;
#[async_trait]
impl WholeStreamCommand for Chart {
fn name(&self) -> &str {
"chart"
@ -20,15 +19,15 @@ impl WholeStreamCommand for Chart {
"Displays charts."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.scope.get_command("chart bar").is_none() {
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
if args.scope().get_command("chart bar").is_none() {
return Err(ShellError::untagged_runtime_error(
"nu_plugin_chart not installed.",
));
}
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
UntaggedValue::string(get_full_help(&Chart, &args.scope)).into_value(Tag::unknown()),
Ok(ActionStream::one(Ok(ReturnSuccess::Value(
UntaggedValue::string(get_full_help(&Chart, args.scope())).into_value(Tag::unknown()),
))))
}
}

View File

@ -1,16 +1,14 @@
use crate::futures::ThreadedReceiver;
use crate::prelude::*;
use nu_engine::evaluate_baseline_expr;
use nu_engine::{evaluate_baseline_expr, BufCodecReader};
use nu_engine::{MaybeTextCodec, StringOrBinary};
use parking_lot::Mutex;
use std::borrow::Cow;
use std::io::Write;
use std::ops::Deref;
use std::process::{Command, Stdio};
use std::sync::mpsc;
use std::{borrow::Cow, io::BufReader};
use futures::executor::block_on_stream;
use futures_codec::FramedRead;
use log::trace;
use nu_errors::ShellError;
@ -18,9 +16,8 @@ use nu_protocol::hir::Expression;
use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
use nu_source::Tag;
use nu_stream::trace_stream;
pub(crate) async fn run_external_command(
pub(crate) fn run_external_command(
command: ExternalCommand,
context: &mut EvaluationContext,
input: InputStream,
@ -28,18 +25,19 @@ pub(crate) async fn run_external_command(
) -> Result<InputStream, ShellError> {
trace!(target: "nu::run::external", "-> {}", command.name);
context.sync_path_to_env();
if !context.host.lock().is_external_cmd(&command.name) {
return Err(ShellError::labeled_error(
"Command not found",
"command not found",
format!("command {} not found", &command.name),
&command.name_tag,
));
}
run_with_stdin(command, context, input, external_redirection).await
run_with_stdin(command, context, input, external_redirection)
}
async fn run_with_stdin(
fn run_with_stdin(
command: ExternalCommand,
context: &mut EvaluationContext,
input: InputStream,
@ -47,12 +45,10 @@ async fn run_with_stdin(
) -> Result<InputStream, ShellError> {
let path = context.shell_manager.path();
let input = trace_stream!(target: "nu::trace_stream::external::stdin", "input" = input);
let mut command_args = vec![];
for arg in command.args.iter() {
let is_literal = matches!(arg.expr, Expression::Literal(_));
let value = evaluate_baseline_expr(arg, context).await?;
let value = evaluate_baseline_expr(arg, context)?;
// Skip any arguments that don't really exist, treating them as optional
// FIXME: we may want to preserve the gap in the future, though it's hard to say
@ -203,243 +199,274 @@ fn spawn(
trace!(target: "nu::run::external", "built command {:?}", process);
// TODO Switch to async_std::process once it's stabilized
if let Ok(mut child) = process.spawn() {
let (tx, rx) = mpsc::sync_channel(0);
match process.spawn() {
Ok(mut child) => {
let (tx, rx) = mpsc::sync_channel(0);
let mut stdin = child.stdin.take();
let mut stdin = child.stdin.take();
let stdin_write_tx = tx.clone();
let stdout_read_tx = tx;
let stdin_name_tag = command.name_tag.clone();
let stdout_name_tag = command.name_tag;
let stdin_write_tx = tx.clone();
let stdout_read_tx = tx;
let stdin_name_tag = command.name_tag.clone();
let stdout_name_tag = command.name_tag;
std::thread::spawn(move || {
if !input.is_empty() {
let mut stdin_write = stdin
.take()
.expect("Internal error: could not get stdin pipe for external command");
std::thread::spawn(move || {
if !input.is_empty() {
let mut stdin_write = stdin
.take()
.expect("Internal error: could not get stdin pipe for external command");
for value in block_on_stream(input) {
match &value.value {
UntaggedValue::Primitive(Primitive::Nothing) => continue,
UntaggedValue::Primitive(Primitive::String(s)) => {
if stdin_write.write(s.as_bytes()).is_err() {
// Other side has closed, so exit
return Ok(());
}
}
UntaggedValue::Primitive(Primitive::Binary(b)) => {
if stdin_write.write(b).is_err() {
// Other side has closed, so exit
return Ok(());
}
}
unsupported => {
println!("Unsupported: {:?}", unsupported);
let _ = stdin_write_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
format!(
"Received unexpected type from pipeline ({})",
unsupported.type_name()
),
"expected a string",
stdin_name_tag.clone(),
)),
tag: stdin_name_tag,
}));
return Err(());
}
};
}
}
Ok(())
});
std::thread::spawn(move || {
if external_redirection == ExternalRedirection::Stdout
|| external_redirection == ExternalRedirection::StdoutAndStderr
{
let stdout = if let Some(stdout) = child.stdout.take() {
stdout
} else {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
"Can't redirect the stdout for external command",
"can't redirect stdout",
&stdout_name_tag,
)),
tag: stdout_name_tag,
}));
return Err(());
};
let file = futures::io::AllowStdIo::new(stdout);
let stream = FramedRead::new(file, MaybeTextCodec::default());
for line in block_on_stream(stream) {
match line {
Ok(line) => match line {
StringOrBinary::String(s) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Primitive(Primitive::String(s.clone())),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
for value in input {
match &value.value {
UntaggedValue::Primitive(Primitive::Nothing) => continue,
UntaggedValue::Primitive(Primitive::String(s)) => {
if stdin_write.write(s.as_bytes()).is_err() {
// Other side has closed, so exit
return Ok(());
}
}
StringOrBinary::Binary(b) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Primitive(Primitive::Binary(
b.into_iter().collect(),
)),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
UntaggedValue::Primitive(Primitive::Binary(b)) => {
if stdin_write.write(b).is_err() {
// Other side has closed, so exit
return Ok(());
}
}
},
Err(e) => {
// If there's an exit status, it makes sense that we may error when
// trying to read from its stdout pipe (likely been closed). In that
// case, don't emit an error.
let should_error = match child.wait() {
Ok(exit_status) => !exit_status.success(),
Err(_) => true,
};
if should_error {
let _ = stdout_read_tx.send(Ok(Value {
unsupported => {
println!("Unsupported: {:?}", unsupported);
let _ = stdin_write_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
format!("Unable to read from stdout ({})", e),
"unable to read from stdout",
&stdout_name_tag,
format!(
"Received unexpected type from pipeline ({})",
unsupported.type_name()
),
"expected a string",
stdin_name_tag.clone(),
)),
tag: stdout_name_tag.clone(),
tag: stdin_name_tag,
}));
return Err(());
}
return Ok(());
}
};
}
}
}
if external_redirection == ExternalRedirection::Stderr
|| external_redirection == ExternalRedirection::StdoutAndStderr
{
let stderr = if let Some(stderr) = child.stderr.take() {
stderr
} else {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
"Can't redirect the stderr for external command",
"can't redirect stderr",
&stdout_name_tag,
)),
tag: stdout_name_tag,
}));
return Err(());
};
let file = futures::io::AllowStdIo::new(stderr);
let stream = FramedRead::new(file, MaybeTextCodec::default());
Ok(())
});
for line in block_on_stream(stream) {
match line {
Ok(line) => match line {
StringOrBinary::String(s) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(
ShellError::untagged_runtime_error(s),
),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
}
}
StringOrBinary::Binary(_) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(
ShellError::untagged_runtime_error("<binary stderr>"),
),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
}
}
},
Err(e) => {
// If there's an exit status, it makes sense that we may error when
// trying to read from its stdout pipe (likely been closed). In that
// case, don't emit an error.
let should_error = match child.wait() {
Ok(exit_status) => !exit_status.success(),
Err(_) => true,
};
if should_error {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
format!("Unable to read from stdout ({})", e),
"unable to read from stdout",
&stdout_name_tag,
)),
tag: stdout_name_tag.clone(),
}));
}
return Ok(());
}
}
}
}
// We can give an error when we see a non-zero exit code, but this is different
// than what other shells will do.
let external_failed = match child.wait() {
Err(_) => true,
Ok(exit_status) => !exit_status.success(),
};
if external_failed {
let cfg = nu_data::config::config(Tag::unknown());
if let Ok(cfg) = cfg {
if cfg.contains_key("nonzero_exit_errors") {
std::thread::spawn(move || {
if external_redirection == ExternalRedirection::Stdout
|| external_redirection == ExternalRedirection::StdoutAndStderr
{
let stdout = if let Some(stdout) = child.stdout.take() {
stdout
} else {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
"External command failed",
"command failed",
"Can't redirect the stdout for external command",
"can't redirect stdout",
&stdout_name_tag,
)),
tag: stdout_name_tag.clone(),
tag: stdout_name_tag,
}));
return Err(());
};
// let file = futures::io::AllowStdIo::new(stdout);
// let stream = FramedRead::new(file, MaybeTextCodec::default());
let buf_read = BufReader::new(stdout);
let buf_codec = BufCodecReader::new(buf_read, MaybeTextCodec::default());
for line in buf_codec {
match line {
Ok(line) => match line {
StringOrBinary::String(s) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Primitive(Primitive::String(
s.clone(),
)),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
}
}
StringOrBinary::Binary(b) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Primitive(Primitive::Binary(
b.into_iter().collect(),
)),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
}
}
},
Err(e) => {
// If there's an exit status, it makes sense that we may error when
// trying to read from its stdout pipe (likely been closed). In that
// case, don't emit an error.
let should_error = match child.wait() {
Ok(exit_status) => !exit_status.success(),
Err(_) => true,
};
if should_error {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
format!("Unable to read from stdout ({})", e),
"unable to read from stdout",
&stdout_name_tag,
)),
tag: stdout_name_tag.clone(),
}));
}
return Ok(());
}
}
}
}
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::external_non_zero()),
tag: stdout_name_tag,
}));
}
if external_redirection == ExternalRedirection::Stderr
|| external_redirection == ExternalRedirection::StdoutAndStderr
{
let stderr = if let Some(stderr) = child.stderr.take() {
stderr
} else {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
"Can't redirect the stderr for external command",
"can't redirect stderr",
&stdout_name_tag,
)),
tag: stdout_name_tag,
}));
return Err(());
};
Ok(())
});
// let file = futures::io::AllowStdIo::new(stderr);
// let stream = FramedRead::new(file, MaybeTextCodec::default());
let buf_reader = BufReader::new(stderr);
let buf_codec = BufCodecReader::new(buf_reader, MaybeTextCodec::default());
let stream = ThreadedReceiver::new(rx);
Ok(stream.to_input_stream())
} else {
Err(ShellError::labeled_error(
"Failed to spawn process",
for line in buf_codec {
match line {
Ok(line) => match line {
StringOrBinary::String(s) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(
ShellError::untagged_runtime_error(s),
),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
}
}
StringOrBinary::Binary(_) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(
ShellError::untagged_runtime_error("<binary stderr>"),
),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
}
}
},
Err(e) => {
// If there's an exit status, it makes sense that we may error when
// trying to read from its stdout pipe (likely been closed). In that
// case, don't emit an error.
let should_error = match child.wait() {
Ok(exit_status) => !exit_status.success(),
Err(_) => true,
};
if should_error {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
format!("Unable to read from stdout ({})", e),
"unable to read from stdout",
&stdout_name_tag,
)),
tag: stdout_name_tag.clone(),
}));
}
return Ok(());
}
}
}
}
// We can give an error when we see a non-zero exit code, but this is different
// than what other shells will do.
let external_failed = match child.wait() {
Err(_) => true,
Ok(exit_status) => !exit_status.success(),
};
if external_failed {
let cfg = nu_data::config::config(Tag::unknown());
if let Ok(cfg) = cfg {
if cfg.contains_key("nonzero_exit_errors") {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
"External command failed",
"command failed",
&stdout_name_tag,
)),
tag: stdout_name_tag.clone(),
}));
}
}
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::external_non_zero()),
tag: stdout_name_tag,
}));
}
Ok(())
});
let stream = ChannelReceiver::new(rx);
Ok(stream.to_input_stream())
}
Err(e) => Err(ShellError::labeled_error(
format!("{}", e),
"failed to spawn",
&command.name_tag,
))
)),
}
}
struct ChannelReceiver {
rx: Arc<Mutex<mpsc::Receiver<Result<Value, ShellError>>>>,
}
impl ChannelReceiver {
pub fn new(rx: mpsc::Receiver<Result<Value, ShellError>>) -> Self {
Self {
rx: Arc::new(Mutex::new(rx)),
}
}
}
impl Iterator for ChannelReceiver {
type Item = Result<Value, ShellError>;
fn next(&mut self) -> Option<Self::Item> {
let rx = self.rx.lock();
match rx.recv() {
Ok(v) => Some(v),
Err(_) => None,
}
}
}
@ -506,16 +533,13 @@ mod tests {
#[cfg(feature = "which")]
use super::{run_external_command, InputStream};
#[cfg(feature = "which")]
use futures::executor::block_on;
#[cfg(feature = "which")]
use nu_engine::EvaluationContext;
#[cfg(feature = "which")]
use nu_errors::ShellError;
#[cfg(feature = "which")]
use nu_test_support::commands::ExternalBuilder;
// async fn read(mut stream: OutputStream) -> Option<Value> {
// match stream.try_next().await {
// fn read(mut stream: OutputStream) -> Option<Value> {
// match stream.try_next() {
// Ok(val) => {
// if let Some(val) = val {
// val.raw_value()
@ -528,32 +552,25 @@ mod tests {
// }
#[cfg(feature = "which")]
async fn non_existent_run() -> Result<(), ShellError> {
fn non_existent_run() {
use nu_protocol::hir::ExternalRedirection;
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
let input = InputStream::empty();
let mut ctx =
EvaluationContext::basic().expect("There was a problem creating a basic context.");
let mut ctx = EvaluationContext::basic();
assert!(
run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout)
.await
.is_err()
);
Ok(())
assert!(run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout).is_err());
}
// async fn failure_run() -> Result<(), ShellError> {
// fn failure_run() -> Result<(), ShellError> {
// let cmd = ExternalBuilder::for_name("fail").build();
// let mut ctx = crate::cli::EvaluationContext::basic().expect("There was a problem creating a basic context.");
// let stream = run_external_command(cmd, &mut ctx, None, false)
// .await?
// ?
// .expect("There was a problem running the external command.");
// match read(stream.into()).await {
// match read(stream.into()) {
// Some(Value {
// value: UntaggedValue::Error(_),
// ..
@ -571,8 +588,8 @@ mod tests {
#[cfg(feature = "which")]
#[test]
fn identifies_command_not_found() -> Result<(), ShellError> {
block_on(non_existent_run())
fn identifies_command_not_found() {
non_existent_run()
}
#[test]

View File

@ -6,7 +6,6 @@ use std::process::Command;
pub struct Clear;
#[async_trait]
impl WholeStreamCommand for Clear {
fn name(&self) -> &str {
"clear"
@ -20,7 +19,7 @@ impl WholeStreamCommand for Clear {
"Clears the terminal."
}
async fn run(&self, _: CommandArgs) -> Result<OutputStream, ShellError> {
fn run(&self, _: CommandArgs) -> Result<InputStream, ShellError> {
if cfg!(windows) {
Command::new("cmd")
.args(&["/C", "cls"])
@ -32,7 +31,7 @@ impl WholeStreamCommand for Clear {
.status()
.expect("failed to execute process");
}
Ok(OutputStream::empty())
Ok(InputStream::empty())
}
fn examples(&self) -> Vec<Example> {

View File

@ -1,5 +1,5 @@
use crate::prelude::*;
use futures::stream::StreamExt;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, Value};
@ -8,7 +8,6 @@ use arboard::Clipboard;
pub struct Clip;
#[async_trait]
impl WholeStreamCommand for Clip {
fn name(&self) -> &str {
"clip"
@ -22,8 +21,8 @@ impl WholeStreamCommand for Clip {
"Copy the contents of the pipeline to the copy/paste buffer."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
clip(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
clip(args)
}
fn examples(&self) -> Vec<Example> {
@ -42,10 +41,10 @@ impl WholeStreamCommand for Clip {
}
}
pub async fn clip(args: CommandArgs) -> Result<OutputStream, ShellError> {
pub fn clip(args: CommandArgs) -> Result<ActionStream, ShellError> {
let input = args.input;
let name = args.call_info.name_tag.clone();
let values: Vec<Value> = input.collect().await;
let name = args.call_info.name_tag;
let values: Vec<Value> = input.collect();
if let Ok(mut clip_context) = Clipboard::new() {
let mut new_copy_data = String::new();
@ -89,7 +88,7 @@ pub async fn clip(args: CommandArgs) -> Result<OutputStream, ShellError> {
name,
));
}
Ok(OutputStream::empty())
Ok(ActionStream::empty())
}
#[cfg(test)]

View File

@ -1,19 +1,16 @@
use crate::prelude::*;
use futures::future;
use futures::stream::StreamExt;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct Compact;
#[derive(Deserialize)]
pub struct CompactArgs {
rest: Vec<Tagged<String>>,
columns: Vec<Tagged<String>>,
}
#[async_trait]
impl WholeStreamCommand for Compact {
fn name(&self) -> &str {
"compact"
@ -27,8 +24,8 @@ impl WholeStreamCommand for Compact {
"Creates a table with non-empty rows."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
compact(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
compact(args)
}
fn examples(&self) -> Vec<Example> {
@ -40,34 +37,28 @@ impl WholeStreamCommand for Compact {
}
}
pub async fn compact(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (CompactArgs { rest: columns }, input) = args.process().await?;
pub fn compact(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (args, input) = args.extract(|params| {
Ok(CompactArgs {
columns: params.rest(0)?,
})
})?;
Ok(input
.filter_map(move |item| {
future::ready(if columns.is_empty() {
if !item.is_empty() {
Some(ReturnSuccess::value(item))
} else {
None
}
.filter(move |item| {
if args.columns.is_empty() {
!item.is_empty()
} else if let Value {
value: UntaggedValue::Row(ref r),
..
} = item
{
args.columns
.iter()
.all(|field| r.get_data(field).borrow().is_some())
} else {
match item {
Value {
value: UntaggedValue::Row(ref r),
..
} => {
if columns
.iter()
.all(|field| r.get_data(field).borrow().is_some())
{
Some(ReturnSuccess::value(item))
} else {
None
}
}
_ => None,
}
})
false
}
})
.to_output_stream())
}

View File

@ -1,11 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config clear"
@ -19,8 +18,8 @@ impl WholeStreamCommand for SubCommand {
"clear the config"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
clear(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
clear(args)
}
fn examples(&self) -> Vec<Example> {
@ -32,24 +31,23 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn clear(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
pub fn clear(args: CommandArgs) -> Result<ActionStream, ShellError> {
let ctx = EvaluationContext::from_args(&args);
let path = match args.scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
let result = if let Some(global_cfg) = &mut args.configs().lock().global_config {
global_cfg.vars.clear();
global_cfg.write()?;
ctx.reload_config(global_cfg)?;
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::Row(global_cfg.vars.clone().into()).into_value(args.call_info.name_tag),
)))
} else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
};
let mut result = nu_data::config::read(name_span, &path)?;
result.clear();
config::write(&result, &path)?;
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(args.call_info.name_tag),
)))
result
}

View File

@ -2,12 +2,11 @@ use crate::prelude::*;
use nu_engine::CommandArgs;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
use nu_stream::OutputStream;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use nu_stream::ActionStream;
pub struct Command;
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"config"
@ -21,21 +20,22 @@ impl WholeStreamCommand for Command {
"Configuration management."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone();
let path = match args.scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let result = nu_data::config::read(&name, &path)?;
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
)])
.to_output_stream())
if let Some(global_cfg) = &args.configs().lock().global_config {
let result = global_cfg.vars.clone();
Ok(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
)]
.into_iter()
.to_action_stream())
} else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
}
}
}

View File

@ -1,9 +1,7 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
pub struct SubCommand;
@ -12,7 +10,6 @@ pub struct Arguments {
column_path: ColumnPath,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config get"
@ -30,8 +27,8 @@ impl WholeStreamCommand for SubCommand {
"Gets a value from the config"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
get(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
get(args)
}
fn examples(&self) -> Vec<Example> {
@ -43,35 +40,29 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
pub fn get(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone();
let scope = args.scope.clone();
let (Arguments { column_path }, _) = args.process().await?;
let ctx = EvaluationContext::from_args(&args);
let path = match scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
let (Arguments { column_path }, _) = args.process()?;
let result = if let Some(global_cfg) = &ctx.configs.lock().global_config {
let result = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
let value = crate::commands::get::get_column_path(&column_path, &result)?;
Ok(match value {
Value {
value: UntaggedValue::Table(list),
..
} => list.into_iter().to_action_stream(),
x => ActionStream::one(ReturnSuccess::value(x)),
})
} else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
};
let result = UntaggedValue::row(nu_data::config::read(&name, &path)?).into_value(&name);
let value = crate::commands::get::get_column_path(&column_path, &result)?;
Ok(match value {
Value {
value: UntaggedValue::Table(list),
..
} => {
let list: Vec<_> = list
.iter()
.map(|x| ReturnSuccess::value(x.clone()))
.collect();
futures::stream::iter(list).to_output_stream()
}
x => OutputStream::one(ReturnSuccess::value(x)),
})
result
}

View File

@ -13,3 +13,9 @@ pub use path::SubCommand as ConfigPath;
pub use remove::SubCommand as ConfigRemove;
pub use set::SubCommand as ConfigSet;
pub use set_into::SubCommand as ConfigSetInto;
use nu_errors::ShellError;
pub fn err_no_global_cfg_present() -> ShellError {
ShellError::untagged_runtime_error("No global config found!")
}

View File

@ -1,11 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config path"
@ -19,8 +18,8 @@ impl WholeStreamCommand for SubCommand {
"return the path to the config file"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
path(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
path(args)
}
fn examples(&self) -> Vec<Example> {
@ -32,19 +31,16 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn path(args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(ReturnSuccess::value(
match args.scope.get_var("config-path") {
Some(
path
@
Value {
value: UntaggedValue::Primitive(Primitive::FilePath(_)),
..
},
) => path,
_ => UntaggedValue::Primitive(Primitive::FilePath(nu_data::config::default_path()?))
.into_value(args.call_info.name_tag),
},
)))
pub fn path(args: CommandArgs) -> Result<ActionStream, ShellError> {
if let Some(global_cfg) = &mut args.configs().lock().global_config {
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::Primitive(Primitive::FilePath(global_cfg.file_path.clone())),
)))
} else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
}
}

View File

@ -1,7 +1,7 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged;
pub struct SubCommand;
@ -11,7 +11,6 @@ pub struct Arguments {
remove: Tagged<String>,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config remove"
@ -29,8 +28,8 @@ impl WholeStreamCommand for SubCommand {
"Removes a value from the config"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
remove(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
remove(args)
}
fn examples(&self) -> Vec<Example> {
@ -42,35 +41,36 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
let scope = args.scope.clone();
let (Arguments { remove }, _) = args.process().await?;
let path = match scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let mut result = nu_data::config::read(name_span, &path)?;
pub fn remove(args: CommandArgs) -> Result<ActionStream, ShellError> {
let ctx = EvaluationContext::from_args(&args);
let (Arguments { remove }, _) = args.process()?;
let key = remove.to_string();
if result.contains_key(&key) {
result.swap_remove(&key);
config::write(&result, &path)?;
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(remove.tag()),
)])
.to_output_stream())
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
if global_cfg.vars.contains_key(&key) {
global_cfg.vars.swap_remove(&key);
global_cfg.write()?;
ctx.reload_config(global_cfg)?;
Ok(vec![ReturnSuccess::value(
UntaggedValue::row(global_cfg.vars.clone()).into_value(remove.tag()),
)]
.into_iter()
.to_action_stream())
} else {
Err(ShellError::labeled_error(
"Key does not exist in config",
"key",
remove.tag(),
))
}
} else {
Err(ShellError::labeled_error(
"Key does not exist in config",
"key",
remove.tag(),
))
}
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
};
result
}

View File

@ -1,9 +1,7 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
pub struct SubCommand;
@ -13,7 +11,6 @@ pub struct Arguments {
value: Value,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config set"
@ -29,8 +26,8 @@ impl WholeStreamCommand for SubCommand {
"Sets a value in the config"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
set(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
set(args)
}
fn examples(&self) -> Vec<Example> {
@ -59,46 +56,49 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
pub fn set(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone();
let scope = args.scope.clone();
let ctx = EvaluationContext::from_args(&args);
let (
Arguments {
column_path,
mut value,
},
_,
) = args.process().await?;
) = args.process()?;
let path = match scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
let configuration = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
if let UntaggedValue::Table(rows) = &value.value {
if rows.len() == 1 && rows[0].is_row() {
value = rows[0].clone();
}
}
match configuration.forgiving_insert_data_at_column_path(&column_path, value) {
Ok(Value {
value: UntaggedValue::Row(changes),
..
}) => {
global_cfg.vars = changes.entries;
global_cfg.write()?;
ctx.reload_config(global_cfg)?;
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
)))
}
Ok(_) => Ok(ActionStream::empty()),
Err(reason) => Err(reason),
}
} else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
};
let raw_entries = nu_data::config::read(&name, &path)?;
let configuration = UntaggedValue::row(raw_entries).into_value(&name);
if let UntaggedValue::Table(rows) = &value.value {
if rows.len() == 1 && rows[0].is_row() {
value = rows[0].clone();
}
}
match configuration.forgiving_insert_data_at_column_path(&column_path, value) {
Ok(Value {
value: UntaggedValue::Row(changes),
..
}) => {
config::write(&changes.entries, &path)?;
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(changes).into_value(name),
)))
}
Ok(_) => Ok(OutputStream::empty()),
Err(reason) => Err(reason),
}
result
}

View File

@ -1,7 +1,7 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct SubCommand;
@ -11,7 +11,6 @@ pub struct Arguments {
set_into: Tagged<String>,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config set_into"
@ -29,8 +28,8 @@ impl WholeStreamCommand for SubCommand {
"Sets a value in the config"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
set_into(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
set_into(args)
}
fn examples(&self) -> Vec<Example> {
@ -42,51 +41,46 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
pub fn set_into(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone();
let scope = args.scope.clone();
let (Arguments { set_into: v }, input) = args.process().await?;
let ctx = EvaluationContext::from_args(&args);
let (Arguments { set_into: v }, input) = args.process()?;
let path = match scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let mut result = nu_data::config::read(&name, &path)?;
let rows: Vec<Value> = input.collect().await;
let rows: Vec<Value> = input.collect();
let key = v.to_string();
Ok(if rows.is_empty() {
return Err(ShellError::labeled_error(
"No values given for set_into",
"needs value(s) from pipeline",
v.tag(),
));
} else if rows.len() == 1 {
// A single value
let value = &rows[0];
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
if rows.is_empty() {
return Err(ShellError::labeled_error(
"No values given for set_into",
"needs value(s) from pipeline",
v.tag(),
));
} else if rows.len() == 1 {
// A single value
let value = &rows[0];
result.insert(key, value.clone());
global_cfg.vars.insert(key, value.clone());
} else {
// Take in the pipeline as a table
let value = UntaggedValue::Table(rows).into_value(name.clone());
config::write(&result, &path)?;
global_cfg.vars.insert(key, value);
}
OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
))
global_cfg.write()?;
ctx.reload_config(global_cfg)?;
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
)))
} else {
// Take in the pipeline as a table
let value = UntaggedValue::Table(rows).into_value(name.clone());
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
};
result.insert(key, value);
config::write(&result, &path)?;
OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
))
})
result
}

View File

@ -5,7 +5,6 @@ use nu_protocol::{Signature, SyntaxShape};
pub struct Cpy;
#[async_trait]
impl WholeStreamCommand for Cpy {
fn name(&self) -> &str {
"cp"
@ -26,10 +25,10 @@ impl WholeStreamCommand for Cpy {
"Copy files."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let shell_manager = args.shell_manager.clone();
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let shell_manager = args.shell_manager();
let name = args.call_info.name_tag.clone();
let (args, _) = args.process().await?;
let (args, _) = args.process()?;
shell_manager.cp(args, name)
}

View File

@ -5,7 +5,6 @@ use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
pub struct Command;
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"date"
@ -19,9 +18,9 @@ impl WholeStreamCommand for Command {
"Apply date function."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(get_full_help(&Command, &args.scope)).into_value(Tag::unknown()),
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
)))
}
}

View File

@ -15,7 +15,6 @@ pub struct FormatArgs {
table: bool,
}
#[async_trait]
impl WholeStreamCommand for Date {
fn name(&self) -> &str {
"date format"
@ -31,8 +30,8 @@ impl WholeStreamCommand for Date {
"Format a given date using the given format string."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
format(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
format(args)
}
fn examples(&self) -> Vec<Example> {
@ -51,9 +50,9 @@ impl WholeStreamCommand for Date {
}
}
pub async fn format(args: CommandArgs) -> Result<OutputStream, ShellError> {
pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (FormatArgs { format, table }, input) = args.process().await?;
let (FormatArgs { format, table }, input) = args.process()?;
Ok(input
.map(move |value| match value {
@ -92,7 +91,7 @@ pub async fn format(args: CommandArgs) -> Result<OutputStream, ShellError> {
&tag,
)),
})
.to_output_stream())
.to_action_stream())
}
#[cfg(test)]

View File

@ -7,7 +7,6 @@ use nu_protocol::{Dictionary, ReturnSuccess, Signature, UntaggedValue};
pub struct Date;
#[async_trait]
impl WholeStreamCommand for Date {
fn name(&self) -> &str {
"date list-timezone"
@ -21,8 +20,8 @@ impl WholeStreamCommand for Date {
"List supported time zones."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
list_timezone(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
list_timezone(args)
}
fn examples(&self) -> Vec<Example> {
@ -41,8 +40,8 @@ impl WholeStreamCommand for Date {
}
}
async fn list_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
fn list_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.call_info.name_tag.clone();
let list = TZ_VARIANTS.iter().map(move |tz| {
@ -58,7 +57,7 @@ async fn list_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
))
});
Ok(futures::stream::iter(list).to_output_stream())
Ok(list.into_iter().to_action_stream())
}
#[cfg(test)]

View File

@ -6,7 +6,6 @@ use nu_protocol::{Signature, UntaggedValue};
pub struct Date;
#[async_trait]
impl WholeStreamCommand for Date {
fn name(&self) -> &str {
"date now"
@ -20,20 +19,20 @@ impl WholeStreamCommand for Date {
"Get the current date."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
now(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
now(args)
}
}
pub async fn now(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
pub fn now(args: CommandArgs) -> Result<ActionStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.call_info.name_tag.clone();
let now: DateTime<Local> = Local::now();
let value = UntaggedValue::date(now.with_timezone(now.offset())).into_value(&tag);
Ok(OutputStream::one(value))
Ok(ActionStream::one(value))
}
#[cfg(test)]

View File

@ -7,7 +7,6 @@ use nu_protocol::{Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue
pub struct Date;
#[async_trait]
impl WholeStreamCommand for Date {
fn name(&self) -> &str {
"date to-table"
@ -21,8 +20,8 @@ impl WholeStreamCommand for Date {
"Print the date in a structured table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
to_table(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
to_table(args)
}
fn examples(&self) -> Vec<Example> {
@ -34,8 +33,8 @@ impl WholeStreamCommand for Date {
}
}
async fn to_table(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
fn to_table(args: CommandArgs) -> Result<ActionStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.call_info.name_tag.clone();
let input = args.input;
@ -88,7 +87,7 @@ async fn to_table(args: CommandArgs) -> Result<OutputStream, ShellError> {
&tag,
)),
})
.to_output_stream())
.to_action_stream())
}
#[cfg(test)]

View File

@ -12,7 +12,6 @@ struct DateToTimeZoneArgs {
timezone: Tagged<String>,
}
#[async_trait]
impl WholeStreamCommand for Date {
fn name(&self) -> &str {
"date to-timezone"
@ -34,8 +33,8 @@ impl WholeStreamCommand for Date {
"Use 'date list-timezone' to list all supported time zones."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
to_timezone(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
to_timezone(args)
}
fn examples(&self) -> Vec<Example> {
@ -59,9 +58,9 @@ impl WholeStreamCommand for Date {
}
}
async fn to_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn to_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (DateToTimeZoneArgs { timezone }, input) = args.process().await?;
let (DateToTimeZoneArgs { timezone }, input) = args.process()?;
Ok(input
.map(move |value| match value {
@ -86,7 +85,7 @@ async fn to_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
&tag,
)),
})
.to_output_stream())
.to_action_stream())
}
fn error_message(err: ParseErrorKind) -> &'static str {

View File

@ -8,7 +8,6 @@ use nu_protocol::Signature;
pub struct Date;
#[async_trait]
impl WholeStreamCommand for Date {
fn name(&self) -> &str {
"date utc"
@ -22,13 +21,13 @@ impl WholeStreamCommand for Date {
"return the current date in utc."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
utc(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
utc(args)
}
}
pub async fn utc(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
pub fn utc(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.call_info.name_tag.clone();
let no_fmt = "".to_string();

View File

@ -10,7 +10,6 @@ pub struct DebugArgs {
raw: bool,
}
#[async_trait]
impl WholeStreamCommand for Debug {
fn name(&self) -> &str {
"debug"
@ -24,13 +23,13 @@ impl WholeStreamCommand for Debug {
"Print the Rust debug representation of the values."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
debug_value(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
debug_value(args)
}
}
async fn debug_value(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (DebugArgs { raw }, input) = args.process().await?;
fn debug_value(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (DebugArgs { raw }, input) = args.process()?;
Ok(input
.map(move |v| {
if raw {
@ -41,7 +40,7 @@ async fn debug_value(args: CommandArgs) -> Result<OutputStream, ShellError> {
ReturnSuccess::debug_value(v)
}
})
.to_output_stream())
.to_action_stream())
}
#[cfg(test)]

View File

@ -14,7 +14,6 @@ pub struct DefArgs {
pub block: CapturedBlock,
}
#[async_trait]
impl WholeStreamCommand for Def {
fn name(&self) -> &str {
"def"
@ -35,11 +34,11 @@ impl WholeStreamCommand for Def {
"Create a command and set it to a definition."
}
async fn run(&self, _args: CommandArgs) -> Result<OutputStream, ShellError> {
fn run_with_actions(&self, _args: CommandArgs) -> Result<ActionStream, ShellError> {
// Currently, we don't do anything here because we should have already
// installed the definition as we entered the scope
// We just create a command so that we can get proper coloring
Ok(OutputStream::empty())
Ok(ActionStream::empty())
}
fn examples(&self) -> Vec<Example> {

View File

@ -13,7 +13,6 @@ struct DefaultArgs {
pub struct Default;
#[async_trait]
impl WholeStreamCommand for Default {
fn name(&self) -> &str {
"default"
@ -33,8 +32,8 @@ impl WholeStreamCommand for Default {
"Sets a default row's column if missing."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
default(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
default(args)
}
fn examples(&self) -> Vec<Example> {
@ -46,8 +45,8 @@ impl WholeStreamCommand for Default {
}
}
async fn default(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (DefaultArgs { column, value }, input) = args.process().await?;
fn default(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (DefaultArgs { column, value }, input) = args.process()?;
Ok(input
.map(move |item| {
@ -68,7 +67,7 @@ async fn default(args: CommandArgs) -> Result<OutputStream, ShellError> {
ReturnSuccess::value(item)
}
})
.to_output_stream())
.to_action_stream())
}
#[cfg(test)]

View File

@ -1,9 +1,9 @@
use crate::prelude::*;
use nu_engine::whole_stream_command;
use nu_engine::EvaluationContext;
use std::error::Error;
pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Box<dyn Error>> {
let context = EvaluationContext::basic()?;
let context = EvaluationContext::basic();
{
use crate::commands::*;
@ -120,9 +120,14 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(Get),
whole_stream_command(Update),
whole_stream_command(Insert),
whole_stream_command(Into),
whole_stream_command(IntoBinary),
whole_stream_command(IntoInt),
whole_stream_command(IntoString),
whole_stream_command(SplitBy),
// Row manipulation
whole_stream_command(All),
whole_stream_command(Any),
whole_stream_command(Reverse),
whole_stream_command(Append),
whole_stream_command(Prepend),
@ -187,6 +192,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(MathRound),
whole_stream_command(MathFloor),
whole_stream_command(MathCeil),
whole_stream_command(MathSqrt),
// File format output
whole_stream_command(To),
whole_stream_command(ToCsv),
@ -232,9 +238,9 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(PathDirname),
whole_stream_command(PathExists),
whole_stream_command(PathExpand),
whole_stream_command(PathExtension),
whole_stream_command(PathFilestem),
whole_stream_command(PathJoin),
whole_stream_command(PathParse),
whole_stream_command(PathSplit),
whole_stream_command(PathType),
// Url
whole_stream_command(UrlCommand),

View File

@ -9,7 +9,6 @@ pub struct Describe;
#[derive(Deserialize)]
pub struct DescribeArgs {}
#[async_trait]
impl WholeStreamCommand for Describe {
fn name(&self) -> &str {
"describe"
@ -23,12 +22,12 @@ impl WholeStreamCommand for Describe {
"Describes the objects in the stream."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
describe(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
describe(args)
}
}
pub async fn describe(args: CommandArgs) -> Result<OutputStream, ShellError> {
pub fn describe(args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(args
.input
.map(|row| {
@ -37,7 +36,7 @@ pub async fn describe(args: CommandArgs) -> Result<OutputStream, ShellError> {
UntaggedValue::string(name).into_value(Tag::unknown_anchor(row.tag.span)),
)
})
.to_output_stream())
.to_action_stream())
}
#[cfg(test)]

View File

@ -12,7 +12,6 @@ struct DoArgs {
ignore_errors: bool,
}
#[async_trait]
impl WholeStreamCommand for Do {
fn name(&self) -> &str {
"do"
@ -32,8 +31,8 @@ impl WholeStreamCommand for Do {
"Runs a block, optionally ignoring errors."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
do_(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
do_(args)
}
fn examples(&self) -> Vec<Example> {
@ -52,17 +51,17 @@ impl WholeStreamCommand for Do {
}
}
async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
fn do_(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
let external_redirection = raw_args.call_info.args.external_redirection;
let context = EvaluationContext::from_args(&raw_args);
let (
DoArgs {
ignore_errors,
mut block,
block,
},
input,
) = raw_args.process().await?;
) = raw_args.process()?;
let block_redirection = match external_redirection {
ExternalRedirection::None => {
@ -82,9 +81,8 @@ async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
x => x,
};
block.block.set_redirect(block_redirection);
context.scope.enter_scope();
let result = run_block(&block.block, &context, input).await;
let result = run_block(&block.block, &context, input, block_redirection);
context.scope.exit_scope();
if ignore_errors {
@ -93,14 +91,14 @@ async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
match result {
Ok(mut stream) => {
let output = stream.drain_vec().await;
let output = stream.drain_vec();
context.clear_errors();
Ok(futures::stream::iter(output).to_output_stream())
Ok(output.into_iter().to_action_stream())
}
Err(_) => Ok(OutputStream::empty()),
Err(_) => Ok(ActionStream::empty()),
}
} else {
result.map(|x| x.to_output_stream())
result.map(|x| x.to_action_stream())
}
}

View File

@ -12,7 +12,6 @@ pub struct Arguments {
columns: Option<Tagged<u64>>,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"drop column"
@ -30,12 +29,12 @@ impl WholeStreamCommand for SubCommand {
"Remove the last number of columns. If you want to remove columns by name, try 'reject'."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
drop(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
drop(args)
}
fn examples(&self) -> Vec<Example> {
use nu_protocol::{row, Value};
use nu_protocol::Value;
vec![Example {
description: "Remove the last column of a table",
@ -48,8 +47,8 @@ impl WholeStreamCommand for SubCommand {
}
}
async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { columns }, input) = args.process().await?;
fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (Arguments { columns }, input) = args.process()?;
let to_drop = if let Some(quantity) = columns {
*quantity as usize
@ -70,7 +69,7 @@ async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
select_fields(&item, descs, item.tag())
})
.map(ReturnSuccess::value)
.to_output_stream())
.to_action_stream())
}
#[cfg(test)]

View File

@ -11,7 +11,6 @@ pub struct Arguments {
rows: Option<Tagged<u64>>,
}
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"drop"
@ -29,8 +28,8 @@ impl WholeStreamCommand for Command {
"Remove the last number of rows or columns."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
drop(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
drop(args)
}
fn examples(&self) -> Vec<Example> {
@ -52,9 +51,9 @@ impl WholeStreamCommand for Command {
}
}
async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rows }, input) = args.process().await?;
let v: Vec<_> = input.into_vec().await;
fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (Arguments { rows }, input) = args.process()?;
let v: Vec<_> = input.into_vec();
let rows_to_drop = if let Some(quantity) = rows {
*quantity as usize
@ -63,7 +62,7 @@ async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
};
Ok(if rows_to_drop == 0 {
futures::stream::iter(v).to_output_stream()
v.into_iter().to_action_stream()
} else {
let k = if v.len() < rows_to_drop {
0
@ -73,6 +72,6 @@ async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
let iter = v.into_iter().take(k);
futures::stream::iter(iter).to_output_stream()
iter.to_action_stream()
})
}

View File

@ -28,7 +28,6 @@ pub struct DuArgs {
min_size: Option<Tagged<u64>>,
}
#[async_trait]
impl WholeStreamCommand for Du {
fn name(&self) -> &str {
NAME
@ -71,8 +70,8 @@ impl WholeStreamCommand for Du {
"Find disk usage sizes of specified items."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
du(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
du(args)
}
fn examples(&self) -> Vec<Example> {
@ -84,12 +83,12 @@ impl WholeStreamCommand for Du {
}
}
async fn du(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn du(args: CommandArgs) -> Result<ActionStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let ctrl_c = args.ctrl_c.clone();
let ctrl_c = args.ctrl_c();
let ctrl_c_copy = ctrl_c.clone();
let (args, _): (DuArgs, _) = args.process().await?;
let (args, _): (DuArgs, _) = args.process()?;
let exclude = args.exclude.map_or(Ok(None), move |x| {
Pattern::new(&x.item)
.map(Option::Some)
@ -131,7 +130,7 @@ async fn du(args: CommandArgs) -> Result<OutputStream, ShellError> {
all,
};
let inp = futures::stream::iter(paths);
let inp = paths;
Ok(inp
.flat_map(move |path| match path {
@ -146,12 +145,12 @@ async fn du(args: CommandArgs) -> Result<OutputStream, ShellError> {
output.push(Ok(ReturnSuccess::Value(v.into())));
}
}
futures::stream::iter(output)
output
}
Err(e) => futures::stream::iter(vec![Err(e)]),
Err(e) => vec![Err(e)],
})
.interruptible(ctrl_c_copy)
.to_output_stream())
.to_action_stream())
}
fn glob_err_into(e: GlobError) -> ShellError {

View File

@ -2,22 +2,14 @@ use crate::prelude::*;
use nu_engine::run_block;
use nu_engine::WholeStreamCommand;
use futures::stream::once;
use nu_errors::ShellError;
use nu_protocol::{
hir::CapturedBlock, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
hir::{CapturedBlock, ExternalRedirection},
Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
};
use nu_source::Tagged;
pub struct Each;
#[derive(Deserialize)]
pub struct EachArgs {
block: CapturedBlock,
numbered: Tagged<bool>,
}
#[async_trait]
impl WholeStreamCommand for Each {
fn name(&self) -> &str {
"each"
@ -37,8 +29,8 @@ impl WholeStreamCommand for Each {
"Run a block on each row of the table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
each(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
each(args)
}
fn examples(&self) -> Vec<Example> {
@ -67,10 +59,11 @@ impl WholeStreamCommand for Each {
}
}
pub async fn process_row(
pub fn process_row(
captured_block: Arc<Box<CapturedBlock>>,
context: Arc<EvaluationContext>,
input: Value,
external_redirection: ExternalRedirection,
) -> Result<OutputStream, ShellError> {
let input_clone = input.clone();
// When we process a row, we need to know whether the block wants to have the contents of the row as
@ -80,7 +73,7 @@ pub async fn process_row(
let input_stream = if !captured_block.block.params.positional.is_empty() {
InputStream::empty()
} else {
once(async { Ok(input_clone) }).to_input_stream()
vec![Ok(input_clone)].into_iter().to_input_stream()
};
context.scope.enter_scope();
@ -95,11 +88,16 @@ pub async fn process_row(
context.scope.add_var("$it", input);
}
let result = run_block(&captured_block.block, &*context, input_stream).await;
let result = run_block(
&captured_block.block,
&*context,
input_stream,
external_redirection,
);
context.scope.exit_scope();
Ok(result?.to_output_stream())
result
}
pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
@ -110,40 +108,42 @@ pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
dict.into_value()
}
async fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let context = Arc::new(EvaluationContext::from_args(&raw_args));
let external_redirection = raw_args.call_info.args.external_redirection;
let args = raw_args.evaluate_once()?;
let (each_args, input): (EachArgs, _) = raw_args.process().await?;
let block = Arc::new(Box::new(each_args.block));
let block: CapturedBlock = args.req(0)?;
let numbered: bool = args.has_flag("numbered");
if each_args.numbered.item {
Ok(input
let block = Arc::new(Box::new(block));
if numbered {
Ok(args
.input
.enumerate()
.then(move |input| {
.map(move |input| {
let block = block.clone();
let context = context.clone();
let row = make_indexed_item(input.0, input.1);
async {
match process_row(block, context, row).await {
Ok(s) => s,
Err(e) => OutputStream::one(Err(e)),
}
match process_row(block, context, row, external_redirection) {
Ok(s) => s,
Err(e) => OutputStream::one(Value::error(e)),
}
})
.flatten()
.to_output_stream())
} else {
Ok(input
.then(move |input| {
Ok(args
.input
.map(move |input| {
let block = block.clone();
let context = context.clone();
async {
match process_row(block, context, input).await {
Ok(s) => s,
Err(e) => OutputStream::one(Err(e)),
}
match process_row(block, context, input, external_redirection) {
Ok(s) => s,
Err(e) => OutputStream::one(Value::error(e)),
}
})
.flatten()

View File

@ -3,7 +3,8 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
hir::CapturedBlock, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
hir::{CapturedBlock, ExternalRedirection},
Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged;
use serde::Deserialize;
@ -17,7 +18,6 @@ pub struct EachGroupArgs {
//numbered: Tagged<bool>,
}
#[async_trait]
impl WholeStreamCommand for EachGroup {
fn name(&self) -> &str {
"each group"
@ -45,16 +45,58 @@ impl WholeStreamCommand for EachGroup {
}]
}
async fn run(&self, raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
fn run_with_actions(&self, raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
let context = Arc::new(EvaluationContext::from_args(&raw_args));
let (each_args, input): (EachGroupArgs, _) = raw_args.process().await?;
let external_redirection = raw_args.call_info.args.external_redirection;
let (each_args, input): (EachGroupArgs, _) = raw_args.process()?;
let block = Arc::new(Box::new(each_args.block));
Ok(input
.chunks(each_args.group_size.item)
.then(move |input| run_block_on_vec(input, block.clone(), context.clone()))
.flatten()
.to_output_stream())
let each_group_iterator = EachGroupIterator {
block,
context,
group_size: each_args.group_size.item,
input,
external_redirection,
};
Ok(each_group_iterator.flatten().to_action_stream())
}
}
struct EachGroupIterator {
block: Arc<Box<CapturedBlock>>,
context: Arc<EvaluationContext>,
group_size: usize,
input: InputStream,
external_redirection: ExternalRedirection,
}
impl Iterator for EachGroupIterator {
type Item = OutputStream;
fn next(&mut self) -> Option<Self::Item> {
let mut group = vec![];
let mut current_count = 0;
while let Some(next) = self.input.next() {
group.push(next);
current_count += 1;
if current_count >= self.group_size {
break;
}
}
if group.is_empty() {
return None;
}
Some(run_block_on_vec(
group,
self.block.clone(),
self.context.clone(),
self.external_redirection,
))
}
}
@ -62,43 +104,33 @@ pub(crate) fn run_block_on_vec(
input: Vec<Value>,
block: Arc<Box<CapturedBlock>>,
context: Arc<EvaluationContext>,
) -> impl Future<Output = OutputStream> {
external_redirection: ExternalRedirection,
) -> OutputStream {
let value = Value {
value: UntaggedValue::Table(input),
tag: Tag::unknown(),
};
async {
match process_row(block, context, value).await {
Ok(s) => {
// We need to handle this differently depending on whether process_row
// returned just 1 value or if it returned multiple as a stream.
let vec = s.collect::<Vec<_>>().await;
match process_row(block, context, value, external_redirection) {
Ok(s) => {
// We need to handle this differently depending on whether process_row
// returned just 1 value or if it returned multiple as a stream.
let vec = s.collect::<Vec<_>>();
// If it returned just one value, just take that value
if vec.len() == 1 {
return OutputStream::one(vec.into_iter().next().expect(
"This should be impossible, we just checked that vec.len() == 1.",
));
}
// If it returned multiple values, we need to put them into a table and
// return that.
let result = vec.into_iter().collect::<Result<Vec<ReturnSuccess>, _>>();
let result_table = match result {
Ok(t) => t,
Err(e) => return OutputStream::one(Err(e)),
};
let table = result_table
.into_iter()
.filter_map(|x| x.raw_value())
.collect();
OutputStream::one(Ok(ReturnSuccess::Value(UntaggedValue::Table(table).into())))
// If it returned just one value, just take that value
if vec.len() == 1 {
return OutputStream::one(
vec.into_iter()
.next()
.expect("This should be impossible, we just checked that vec.len() == 1."),
);
}
Err(e) => OutputStream::one(Err(e)),
// If it returned multiple values, we need to put them into a table and
// return that.
OutputStream::one(UntaggedValue::Table(vec).into_untagged_value())
}
Err(e) => OutputStream::one(Value::error(e)),
}
}

View File

@ -16,7 +16,6 @@ pub struct EachWindowArgs {
stride: Option<Tagged<usize>>,
}
#[async_trait]
impl WholeStreamCommand for EachWindow {
fn name(&self) -> &str {
"each window"
@ -50,16 +49,16 @@ impl WholeStreamCommand for EachWindow {
}]
}
async fn run(&self, raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
fn run_with_actions(&self, raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
let context = Arc::new(EvaluationContext::from_args(&raw_args));
let (each_args, mut input): (EachWindowArgs, _) = raw_args.process().await?;
let external_redirection = raw_args.call_info.args.external_redirection;
let (each_args, mut input): (EachWindowArgs, _) = raw_args.process()?;
let block = Arc::new(Box::new(each_args.block));
let mut window: Vec<_> = input
.by_ref()
.take(*each_args.window_size - 1)
.collect::<Vec<_>>()
.await;
.collect::<Vec<_>>();
// `window` must start with dummy values, which will be removed on the first iteration
let stride = each_args.stride.map(|x| *x).unwrap_or(1);
@ -67,7 +66,7 @@ impl WholeStreamCommand for EachWindow {
Ok(input
.enumerate()
.then(move |(i, input)| {
.map(move |(i, input)| {
// This would probably be more efficient if `last` was a VecDeque
// But we can't have that because it needs to be put into a Table
window.remove(0);
@ -77,17 +76,20 @@ impl WholeStreamCommand for EachWindow {
let context = context.clone();
let local_window = window.clone();
async move {
if i % stride == 0 {
Some(run_block_on_vec(local_window, block, context).await)
} else {
None
}
if i % stride == 0 {
Some(run_block_on_vec(
local_window,
block,
context,
external_redirection,
))
} else {
None
}
})
.filter_map(|x| async { x })
.flatten()
.to_output_stream())
.flatten()
.to_action_stream())
}
}

View File

@ -1,19 +1,12 @@
use crate::prelude::*;
use bigdecimal::Zero;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::hir::Operator;
use nu_protocol::{
Primitive, Range, RangeInclusion, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_protocol::{Primitive, Range, RangeInclusion, Signature, SyntaxShape, UntaggedValue, Value};
pub struct Echo;
#[derive(Deserialize, Debug)]
pub struct EchoArgs {
pub rest: Vec<Value>,
}
#[async_trait]
impl WholeStreamCommand for Echo {
fn name(&self) -> &str {
"echo"
@ -27,8 +20,8 @@ impl WholeStreamCommand for Echo {
"Echo the arguments back to the user."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
echo(args).await
fn run(&self, args: CommandArgs) -> Result<InputStream, ShellError> {
echo(args)
}
fn examples(&self) -> Vec<Example> {
@ -47,36 +40,37 @@ impl WholeStreamCommand for Echo {
}
}
async fn echo(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (args, _): (EchoArgs, _) = args.process().await?;
fn echo(args: CommandArgs) -> Result<InputStream, ShellError> {
let args = args.evaluate_once()?;
let rest: Vec<Value> = args.rest(0)?;
let stream = args.rest.into_iter().map(|i| match i.as_string() {
Ok(s) => OutputStream::one(Ok(ReturnSuccess::Value(
UntaggedValue::string(s).into_value(i.tag.clone()),
))),
let stream = rest.into_iter().map(|i| match i.as_string() {
Ok(s) => InputStream::one(UntaggedValue::string(s).into_value(i.tag.clone())),
_ => match i {
Value {
value: UntaggedValue::Table(table),
..
} => futures::stream::iter(table.into_iter().map(ReturnSuccess::value))
.to_output_stream(),
} => InputStream::from_stream(table.into_iter()),
Value {
value: UntaggedValue::Primitive(Primitive::Range(range)),
tag,
} => futures::stream::iter(RangeIterator::new(*range, tag)).to_output_stream(),
x => OutputStream::one(Ok(ReturnSuccess::Value(x))),
} => InputStream::from_stream(RangeIterator::new(*range, tag)),
x => InputStream::one(x),
},
});
Ok(futures::stream::iter(stream).flatten().to_output_stream())
Ok(InputStream::from_stream(stream.flatten()))
}
struct RangeIterator {
curr: Primitive,
end: Primitive,
curr: UntaggedValue,
end: UntaggedValue,
tag: Tag,
is_end_inclusive: bool,
moves_up: bool,
one: UntaggedValue,
negative_one: UntaggedValue,
done: bool,
}
impl RangeIterator {
@ -93,98 +87,104 @@ impl RangeIterator {
RangeIterator {
moves_up: start <= end,
curr: start,
end,
curr: UntaggedValue::Primitive(start),
end: UntaggedValue::Primitive(end),
tag,
is_end_inclusive: matches!(range.to.1, RangeInclusion::Inclusive),
one: UntaggedValue::int(1),
negative_one: UntaggedValue::int(-1),
done: false,
}
}
}
impl Iterator for RangeIterator {
type Item = Result<ReturnSuccess, ShellError>;
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
let ordering = if self.end == Primitive::Nothing {
use std::cmp::Ordering;
if self.done {
return None;
}
let ordering = if self.end == UntaggedValue::Primitive(Primitive::Nothing) {
Ordering::Less
} else {
let result =
nu_data::base::coerce_compare_primitive(&self.curr, &self.end).map_err(|_| {
ShellError::labeled_error(
"Cannot create range",
"unsupported range",
self.tag.span,
)
});
if let Err(result) = result {
return Some(Err(result));
match (&self.curr, &self.end) {
(
UntaggedValue::Primitive(Primitive::Int(x)),
UntaggedValue::Primitive(Primitive::Int(y)),
) => x.cmp(y),
(
UntaggedValue::Primitive(Primitive::Decimal(x)),
UntaggedValue::Primitive(Primitive::Decimal(y)),
) => x.cmp(y),
(
UntaggedValue::Primitive(Primitive::Decimal(x)),
UntaggedValue::Primitive(Primitive::Int(y)),
) => x.cmp(&(BigDecimal::zero() + y)),
(
UntaggedValue::Primitive(Primitive::Int(x)),
UntaggedValue::Primitive(Primitive::Decimal(y)),
) => (BigDecimal::zero() + x).cmp(y),
_ => {
self.done = true;
return Some(
UntaggedValue::Error(ShellError::labeled_error(
"Cannot create range",
"unsupported range",
self.tag.span,
))
.into_untagged_value(),
);
}
}
let result = result
.expect("Internal error: the error case was already protected, but that failed");
result.compare()
};
use std::cmp::Ordering;
if self.moves_up
&& (ordering == Ordering::Less || self.is_end_inclusive && ordering == Ordering::Equal)
{
let output = UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone());
let next_value = nu_data::value::compute_values(Operator::Plus, &self.curr, &self.one);
let next_value = nu_data::value::compute_values(
Operator::Plus,
&UntaggedValue::Primitive(self.curr.clone()),
&UntaggedValue::int(1),
);
let mut next = match next_value {
Ok(result) => result,
self.curr = match next_value {
Ok(result) => match result {
UntaggedValue::Primitive(p) => p,
_ => {
return Some(Err(ShellError::unimplemented(
"Internal error: expected a primitive result from increment",
)));
}
},
Err((left_type, right_type)) => {
return Some(Err(ShellError::coerce_error(
left_type.spanned(self.tag.span),
right_type.spanned(self.tag.span),
)));
self.done = true;
return Some(
UntaggedValue::Error(ShellError::coerce_error(
left_type.spanned(self.tag.span),
right_type.spanned(self.tag.span),
))
.into_untagged_value(),
);
}
};
Some(ReturnSuccess::value(output))
std::mem::swap(&mut self.curr, &mut next);
Some(next.into_value(self.tag.clone()))
} else if !self.moves_up
&& (ordering == Ordering::Greater
|| self.is_end_inclusive && ordering == Ordering::Equal)
{
let output = UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone());
let next_value =
nu_data::value::compute_values(Operator::Plus, &self.curr, &self.negative_one);
let next_value = nu_data::value::compute_values(
Operator::Plus,
&UntaggedValue::Primitive(self.curr.clone()),
&UntaggedValue::int(-1),
);
self.curr = match next_value {
Ok(result) => match result {
UntaggedValue::Primitive(p) => p,
_ => {
return Some(Err(ShellError::unimplemented(
"Internal error: expected a primitive result from increment",
)));
}
},
let mut next = match next_value {
Ok(result) => result,
Err((left_type, right_type)) => {
return Some(Err(ShellError::coerce_error(
left_type.spanned(self.tag.span),
right_type.spanned(self.tag.span),
)));
self.done = true;
return Some(
UntaggedValue::Error(ShellError::coerce_error(
left_type.spanned(self.tag.span),
right_type.spanned(self.tag.span),
))
.into_untagged_value(),
);
}
};
Some(ReturnSuccess::value(output))
std::mem::swap(&mut self.curr, &mut next);
Some(next.into_value(self.tag.clone()))
} else {
None
}

View File

@ -3,12 +3,11 @@ use nu_engine::run_block;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
hir::CapturedBlock, ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape,
UntaggedValue, Value,
hir::CapturedBlock, hir::ExternalRedirection, ColumnPath, Primitive, ReturnSuccess, Signature,
SyntaxShape, UntaggedValue, Value,
};
use crate::utils::arguments::arguments;
use futures::stream::once;
use nu_value_ext::{as_string, ValueExt};
#[derive(Deserialize)]
@ -18,7 +17,6 @@ pub struct Arguments {
pub struct Command;
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"empty?"
@ -35,8 +33,8 @@ impl WholeStreamCommand for Command {
"Check for empty values."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
is_empty(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
is_empty(args)
}
fn examples(&self) -> Vec<Example> {
@ -81,81 +79,80 @@ impl WholeStreamCommand for Command {
}
}
async fn is_empty(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn is_empty(args: CommandArgs) -> Result<ActionStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let name_tag = Arc::new(args.call_info.name_tag.clone());
let context = Arc::new(EvaluationContext::from_args(&args));
let (Arguments { mut rest }, input) = args.process().await?;
let (Arguments { mut rest }, input) = args.process()?;
let (columns, default_block): (Vec<ColumnPath>, Option<Box<CapturedBlock>>) =
arguments(&mut rest)?;
let default_block = Arc::new(default_block);
if input.is_empty() {
let stream = futures::stream::iter(vec![
UntaggedValue::Primitive(Primitive::Nothing).into_value(tag)
]);
let stream = vec![UntaggedValue::Primitive(Primitive::Nothing).into_value(tag)].into_iter();
return Ok(InputStream::from_stream(stream)
.then(move |input| {
.map(move |input| {
let tag = name_tag.clone();
let context = context.clone();
let block = default_block.clone();
let columns = vec![];
async {
match process_row(context, input, block, columns, tag).await {
Ok(s) => s,
Err(e) => OutputStream::one(Err(e)),
}
match process_row(context, input, block, columns, tag) {
Ok(s) => s,
Err(e) => ActionStream::one(Err(e)),
}
})
.flatten()
.to_output_stream());
.to_action_stream());
}
Ok(input
.then(move |input| {
.map(move |input| {
let tag = name_tag.clone();
let context = context.clone();
let block = default_block.clone();
let columns = columns.clone();
async {
match process_row(context, input, block, columns, tag).await {
Ok(s) => s,
Err(e) => OutputStream::one(Err(e)),
}
match process_row(context, input, block, columns, tag) {
Ok(s) => s,
Err(e) => ActionStream::one(Err(e)),
}
})
.flatten()
.to_output_stream())
.to_action_stream())
}
async fn process_row(
fn process_row(
context: Arc<EvaluationContext>,
input: Value,
default_block: Arc<Option<Box<CapturedBlock>>>,
column_paths: Vec<ColumnPath>,
tag: Arc<Tag>,
) -> Result<OutputStream, ShellError> {
) -> Result<ActionStream, ShellError> {
let _tag = &*tag;
let mut out = Arc::new(None);
let results = Arc::make_mut(&mut out);
if let Some(default_block) = &*default_block {
let for_block = input.clone();
let input_stream = once(async { Ok(for_block) }).to_input_stream();
let input_stream = vec![Ok(for_block)].into_iter().to_input_stream();
context.scope.enter_scope();
context.scope.add_vars(&default_block.captured.entries);
context.scope.add_var("$it", input.clone());
let stream = run_block(&default_block.block, &*context, input_stream).await;
let stream = run_block(
&default_block.block,
&*context,
input_stream,
ExternalRedirection::Stdout,
);
context.scope.exit_scope();
let mut stream = stream?;
*results = Some({
let values = stream.drain_vec().await;
let values = stream.drain_vec();
let errors = context.get_errors();
@ -186,7 +183,7 @@ async fn process_row(
ref tag,
} => {
if column_paths.is_empty() {
Ok(OutputStream::one(ReturnSuccess::value({
Ok(ActionStream::one(ReturnSuccess::value({
let is_empty = input.is_empty();
if default_block.is_some() {
@ -229,10 +226,10 @@ async fn process_row(
}
}
Ok(OutputStream::one(ReturnSuccess::value(obj)))
Ok(ActionStream::one(ReturnSuccess::value(obj)))
}
}
other => Ok(OutputStream::one(ReturnSuccess::value({
other => Ok(ActionStream::one(ReturnSuccess::value({
if other.is_empty() {
results
.clone()

View File

@ -17,7 +17,6 @@ pub struct EnterArgs {
encoding: Option<Tagged<String>>,
}
#[async_trait]
impl WholeStreamCommand for Enter {
fn name(&self) -> &str {
"enter"
@ -51,8 +50,8 @@ For a more complete list of encodings please refer to the encoding_rs
documentation link at https://docs.rs/encoding_rs/0.8.28/encoding_rs/#statics"#
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
enter(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
enter(args)
}
fn examples(&self) -> Vec<Example> {
@ -76,64 +75,40 @@ documentation link at https://docs.rs/encoding_rs/0.8.28/encoding_rs/#statics"#
}
}
async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let scope = raw_args.scope.clone();
let shell_manager = raw_args.shell_manager.clone();
let head = raw_args.call_info.args.head.clone();
let ctrl_c = raw_args.ctrl_c.clone();
let current_errors = raw_args.current_errors.clone();
let host = raw_args.host.clone();
let tag = raw_args.call_info.name_tag.clone();
let (EnterArgs { location, encoding }, _) = raw_args.process().await?;
fn enter(args: CommandArgs) -> Result<ActionStream, ShellError> {
let head = args.call_info.args.head.clone();
let context = args.context.clone();
let scope = args.scope().clone();
let path = args.context.shell_manager.path();
let (EnterArgs { location, encoding }, _) = args.process()?;
let location_string = location.display().to_string();
let location_clone = location_string.clone();
if location_string.starts_with("help") {
let spec = location_string.split(':').collect::<Vec<&str>>();
if spec.len() == 2 {
let (_, command) = (spec[0], spec[1]);
if scope.has_command(command) {
return Ok(OutputStream::one(ReturnSuccess::action(
CommandAction::EnterHelpShell(
UntaggedValue::string(command).into_value(Tag::unknown()),
),
)));
}
}
Ok(OutputStream::one(ReturnSuccess::action(
CommandAction::EnterHelpShell(UntaggedValue::nothing().into_value(Tag::unknown())),
)))
} else if location.is_dir() {
Ok(OutputStream::one(ReturnSuccess::action(
CommandAction::EnterShell(location_clone),
if location.is_dir() {
Ok(ActionStream::one(ReturnSuccess::action(
CommandAction::EnterShell(location_string),
)))
} else {
// If it's a file, attempt to open the file as a value and enter it
let cwd = shell_manager.path();
let cwd = path;
let full_path = std::path::PathBuf::from(cwd);
let span = location.span();
let (file_extension, tagged_contents) = crate::commands::open::fetch(
&full_path,
&PathBuf::from(location_clone),
&PathBuf::from(location_string),
span,
encoding,
)
.await?;
)?;
match tagged_contents.value {
UntaggedValue::Primitive(Primitive::String(_)) => {
if let Some(extension) = file_extension {
let command_name = format!("from {}", extension);
if let Some(converter) = scope.get_command(&command_name) {
let new_args = RawCommandArgs {
host,
ctrl_c,
current_errors,
shell_manager,
let tag = tagged_contents.tag.clone();
let new_args = CommandArgs {
context,
call_info: UnevaluatedCallInfo {
args: nu_protocol::hir::Call {
head,
@ -144,38 +119,34 @@ async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
},
name_tag: tag.clone(),
},
scope: scope.clone(),
input: InputStream::one(tagged_contents),
};
let tag = tagged_contents.tag.clone();
let mut result = converter
.run(new_args.with_input(vec![tagged_contents]))
.await?;
let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
result.drain_vec().await;
Ok(futures::stream::iter(result_vec.into_iter().map(
move |res| match res {
Ok(ReturnSuccess::Value(Value { value, .. })) => Ok(
ReturnSuccess::Action(CommandAction::EnterValueShell(Value {
let mut result = converter.run(new_args)?;
let result_vec: Vec<Value> = result.drain_vec();
Ok(result_vec
.into_iter()
.map(move |res| {
let Value { value, .. } = res;
Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
Value {
value,
tag: tag.clone(),
})),
),
x => x,
},
))
.to_output_stream())
},
)))
})
.to_action_stream())
} else {
Ok(OutputStream::one(ReturnSuccess::action(
Ok(ActionStream::one(ReturnSuccess::action(
CommandAction::EnterValueShell(tagged_contents),
)))
}
} else {
Ok(OutputStream::one(ReturnSuccess::action(
Ok(ActionStream::one(ReturnSuccess::action(
CommandAction::EnterValueShell(tagged_contents),
)))
}
}
_ => Ok(OutputStream::one(ReturnSuccess::action(
_ => Ok(ActionStream::one(ReturnSuccess::action(
CommandAction::EnterValueShell(tagged_contents),
))),
}

View File

@ -12,7 +12,6 @@ pub struct EveryArgs {
skip: Tagged<bool>,
}
#[async_trait]
impl WholeStreamCommand for Every {
fn name(&self) -> &str {
"every"
@ -36,8 +35,8 @@ impl WholeStreamCommand for Every {
"Show (or skip) every n-th row, starting from the first one."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
every(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
every(args)
}
fn examples(&self) -> Vec<Example> {
@ -63,15 +62,15 @@ impl WholeStreamCommand for Every {
}
}
async fn every(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (EveryArgs { stride, skip }, input) = args.process().await?;
fn every(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (EveryArgs { stride, skip }, input) = args.process()?;
let stride = stride.item;
let skip = skip.item;
Ok(input
.enumerate()
.filter_map(move |(i, value)| async move {
.filter_map(move |(i, value)| {
let stride_desired = if stride < 1 { 1 } else { stride } as usize;
let should_include = skip == (i % stride_desired != 0);
@ -81,7 +80,7 @@ async fn every(args: CommandArgs) -> Result<OutputStream, ShellError> {
None
}
})
.to_output_stream())
.to_action_stream())
}
#[cfg(test)]

View File

@ -13,7 +13,6 @@ pub struct ExecArgs {
pub rest: Vec<Tagged<String>>,
}
#[async_trait]
impl WholeStreamCommand for Exec {
fn name(&self) -> &str {
"exec"
@ -32,8 +31,8 @@ impl WholeStreamCommand for Exec {
"Execute command."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
exec(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
exec(args)
}
fn examples(&self) -> Vec<Example> {
@ -53,12 +52,12 @@ impl WholeStreamCommand for Exec {
}
#[cfg(unix)]
async fn exec(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn exec(args: CommandArgs) -> Result<ActionStream, ShellError> {
use std::os::unix::process::CommandExt;
use std::process::Command;
let name = args.call_info.name_tag.clone();
let (args, _): (ExecArgs, _) = args.process().await?;
let (args, _): (ExecArgs, _) = args.process()?;
let mut command = Command::new(args.command.item);
for tagged_arg in args.rest {
@ -75,7 +74,7 @@ async fn exec(args: CommandArgs) -> Result<OutputStream, ShellError> {
}
#[cfg(not(unix))]
async fn exec(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn exec(args: CommandArgs) -> Result<ActionStream, ShellError> {
Err(ShellError::labeled_error(
"Error on exec",
"exec is not supported on your platform",

View File

@ -4,7 +4,6 @@ use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape};
pub struct Exit;
#[async_trait]
impl WholeStreamCommand for Exit {
fn name(&self) -> &str {
"exit"
@ -24,8 +23,8 @@ impl WholeStreamCommand for Exit {
"Exit the current shell (or all shells)."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
exit(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
exit(args)
}
fn examples(&self) -> Vec<Example> {
@ -44,8 +43,8 @@ impl WholeStreamCommand for Exit {
}
}
pub async fn exit(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
pub fn exit(args: CommandArgs) -> Result<ActionStream, ShellError> {
let args = args.evaluate_once()?;
let code = if let Some(value) = args.call_info.args.nth(0) {
value.as_i32()?
@ -59,7 +58,7 @@ pub async fn exit(args: CommandArgs) -> Result<OutputStream, ShellError> {
CommandAction::LeaveShell(code)
};
Ok(OutputStream::one(ReturnSuccess::action(command_action)))
Ok(ActionStream::one(ReturnSuccess::action(command_action)))
}
#[cfg(test)]

View File

@ -11,7 +11,6 @@ pub struct FirstArgs {
rows: Option<Tagged<usize>>,
}
#[async_trait]
impl WholeStreamCommand for First {
fn name(&self) -> &str {
"first"
@ -29,8 +28,8 @@ impl WholeStreamCommand for First {
"Show only the first number of rows."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
first(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
first(args)
}
fn examples(&self) -> Vec<Example> {
@ -52,15 +51,15 @@ impl WholeStreamCommand for First {
}
}
async fn first(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (FirstArgs { rows }, input) = args.process().await?;
fn first(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (FirstArgs { rows }, input) = args.process()?;
let rows_desired = if let Some(quantity) = rows {
*quantity
} else {
1
};
Ok(input.take(rows_desired).to_output_stream())
Ok(input.take(rows_desired).to_action_stream())
}
#[cfg(test)]

View File

@ -13,7 +13,6 @@ pub struct Arguments {
rest: Vec<Tagged<String>>,
}
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"flatten"
@ -27,8 +26,8 @@ impl WholeStreamCommand for Command {
"Flatten the table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
flatten(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
flatten(args)
}
fn examples(&self) -> Vec<Example> {
@ -52,14 +51,14 @@ impl WholeStreamCommand for Command {
}
}
async fn flatten(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn flatten(args: CommandArgs) -> Result<ActionStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (Arguments { rest: columns }, input) = args.process().await?;
let (Arguments { rest: columns }, input) = args.process()?;
Ok(input
.map(move |item| futures::stream::iter(flat_value(&columns, &item, &tag).into_iter()))
.map(move |item| flat_value(&columns, &item, &tag).into_iter())
.flatten()
.to_output_stream())
.to_action_stream())
}
enum TableInside<'a> {

View File

@ -13,7 +13,6 @@ pub struct FormatArgs {
pattern: Tagged<String>,
}
#[async_trait]
impl WholeStreamCommand for Format {
fn name(&self) -> &str {
"format"
@ -31,8 +30,8 @@ impl WholeStreamCommand for Format {
"Format columns into a string using a simple pattern."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
format_command(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
format_command(args)
}
fn examples(&self) -> Vec<Example> {
@ -44,51 +43,48 @@ impl WholeStreamCommand for Format {
}
}
async fn format_command(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn format_command(args: CommandArgs) -> Result<ActionStream, ShellError> {
let ctx = Arc::new(EvaluationContext::from_args(&args));
let (FormatArgs { pattern }, input) = args.process().await?;
let (FormatArgs { pattern }, input) = args.process()?;
let format_pattern = format(&pattern);
let commands = Arc::new(format_pattern);
Ok(input
.then(move |value| {
.map(move |value| {
let mut output = String::new();
let commands = commands.clone();
let ctx = ctx.clone();
async move {
for command in &*commands {
match command {
FormatCommand::Text(s) => {
output.push_str(&s);
}
FormatCommand::Column(c) => {
// FIXME: use the correct spans
let full_column_path = nu_parser::parse_full_column_path(
&(c.to_string()).spanned(Span::unknown()),
&ctx.scope,
);
for command in &*commands {
match command {
FormatCommand::Text(s) => {
output.push_str(&s);
}
FormatCommand::Column(c) => {
// FIXME: use the correct spans
let full_column_path = nu_parser::parse_full_column_path(
&(c.to_string()).spanned(Span::unknown()),
&ctx.scope,
);
ctx.scope.enter_scope();
ctx.scope.add_var("$it", value.clone());
let result = evaluate_baseline_expr(&full_column_path.0, &*ctx).await;
ctx.scope.exit_scope();
ctx.scope.enter_scope();
ctx.scope.add_var("$it", value.clone());
let result = evaluate_baseline_expr(&full_column_path.0, &*ctx);
ctx.scope.exit_scope();
if let Ok(c) = result {
output
.push_str(&value::format_leaf(c.borrow()).plain_string(100_000))
} else {
// That column doesn't match, so don't emit anything
}
if let Ok(c) = result {
output.push_str(&value::format_leaf(c.borrow()).plain_string(100_000))
} else {
// That column doesn't match, so don't emit anything
}
}
}
ReturnSuccess::value(UntaggedValue::string(output).into_untagged_value())
}
ReturnSuccess::value(UntaggedValue::string(output).into_untagged_value())
})
.to_output_stream())
.to_action_stream())
}
#[derive(Debug)]

View File

@ -16,7 +16,6 @@ pub struct Arguments {
format: Tagged<String>,
}
#[async_trait]
impl WholeStreamCommand for FileSize {
fn name(&self) -> &str {
"format filesize"
@ -40,8 +39,8 @@ impl WholeStreamCommand for FileSize {
"Converts a column of filesizes to some specified format"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
filesize(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
filesize(args)
}
fn examples(&self) -> Vec<Example> {
@ -60,11 +59,11 @@ impl WholeStreamCommand for FileSize {
}
}
async fn process_row(
fn process_row(
input: Value,
format: Tagged<String>,
field: Arc<ColumnPath>,
) -> Result<OutputStream, ShellError> {
) -> Result<ActionStream, ShellError> {
Ok({
let replace_for = get_data_by_column_path(&input, &field, move |_, _, error| error);
match replace_for {
@ -76,7 +75,7 @@ async fn process_row(
{
let byte_format = InlineShape::format_bytes(&fs, Some(&format.item));
let byte_value = Value::from(byte_format.1);
OutputStream::one(ReturnSuccess::value(
ActionStream::one(ReturnSuccess::value(
input.replace_data_at_column_path(&field, byte_value).expect("Given that the existence check was already done, this shouldn't trigger never"),
))
} else {
@ -87,29 +86,27 @@ async fn process_row(
));
}
}
Err(e) => OutputStream::one(Err(e)),
Err(e) => ActionStream::one(Err(e)),
}
})
}
async fn filesize(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { field, format }, input) = raw_args.process().await?;
fn filesize(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
let (Arguments { field, format }, input) = raw_args.process()?;
let field = Arc::new(field);
Ok(input
.then(move |input| {
.map(move |input| {
let format = format.clone();
let field = field.clone();
async {
match process_row(input, format, field).await {
Ok(s) => s,
Err(e) => OutputStream::one(Err(e)),
}
match process_row(input, format, field) {
Ok(s) => s,
Err(e) => ActionStream::one(Err(e)),
}
})
.flatten()
.to_output_stream())
.to_action_stream())
}
#[cfg(test)]

View File

@ -1,11 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use nu_protocol::{Signature, UntaggedValue};
pub struct From;
#[async_trait]
impl WholeStreamCommand for From {
fn name(&self) -> &str {
"from"
@ -19,10 +18,10 @@ impl WholeStreamCommand for From {
"Parse content (string or binary) as a table (input format based on subcommand, like csv, ini, json, toml)."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(get_full_help(&From, &args.scope)).into_value(Tag::unknown()),
)))
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(
UntaggedValue::string(get_full_help(&From, args.scope())).into_value(Tag::unknown()),
))
}
}

View File

@ -6,13 +6,6 @@ use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
pub struct FromCsv;
#[derive(Deserialize)]
pub struct FromCsvArgs {
noheaders: bool,
separator: Option<Value>,
}
#[async_trait]
impl WholeStreamCommand for FromCsv {
fn name(&self) -> &str {
"from csv"
@ -37,8 +30,8 @@ impl WholeStreamCommand for FromCsv {
"Parse text as .csv and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_csv(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_csv(args)
}
fn examples(&self) -> Vec<Example> {
@ -67,16 +60,14 @@ impl WholeStreamCommand for FromCsv {
}
}
async fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let args = args.evaluate_once()?;
let noheaders = args.has_flag("noheaders");
let separator: Option<Value> = args.get_flag("separator")?;
let input = args.input;
let (
FromCsvArgs {
noheaders,
separator,
},
input,
) = args.process().await?;
let sep = match separator {
Some(Value {
value: UntaggedValue::Primitive(Primitive::String(s)),
@ -100,7 +91,7 @@ async fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
_ => ',',
};
from_delimited_data(noheaders, sep, "CSV", input, name).await
from_delimited_data(noheaders, sep, "CSV", input, name)
}
#[cfg(test)]

View File

@ -45,7 +45,7 @@ fn from_delimited_string_to_value(
Ok(UntaggedValue::Table(rows).into_value(&tag))
}
pub async fn from_delimited_data(
pub fn from_delimited_data(
noheaders: bool,
sep: char,
format_name: &'static str,
@ -53,7 +53,7 @@ pub async fn from_delimited_data(
name: Tag,
) -> Result<OutputStream, ShellError> {
let name_tag = name;
let concat_string = input.collect_string(name_tag.clone()).await?;
let concat_string = input.collect_string(name_tag.clone())?;
let sample_lines = concat_string.item.lines().take(3).collect_vec().join("\n");
match from_delimited_string_to_value(concat_string.item, noheaders, sep, name_tag.clone()) {
@ -61,7 +61,7 @@ pub async fn from_delimited_data(
Value {
value: UntaggedValue::Table(list),
..
} => Ok(futures::stream::iter(list).to_output_stream()),
} => Ok(list.into_iter().to_output_stream()),
x => Ok(OutputStream::one(x)),
},
Err(err) => {
@ -80,7 +80,7 @@ pub async fn from_delimited_data(
Err(ShellError::labeled_error_with_secondary(
line_one,
line_two,
name_tag.clone(),
name_tag,
"value originates from here",
concat_string.tag,
))

View File

@ -3,20 +3,13 @@ use ::eml_parser::eml::*;
use ::eml_parser::EmlParser;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue};
use nu_protocol::{Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue};
use nu_source::Tagged;
pub struct FromEml;
const DEFAULT_BODY_PREVIEW: usize = 50;
#[derive(Deserialize, Clone)]
pub struct FromEmlArgs {
#[serde(rename(deserialize = "preview-body"))]
preview_body: Option<Tagged<usize>>,
}
#[async_trait]
impl WholeStreamCommand for FromEml {
fn name(&self) -> &str {
"from eml"
@ -35,8 +28,8 @@ impl WholeStreamCommand for FromEml {
"Parse text as .eml and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_eml(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_eml(args)
}
}
@ -73,15 +66,15 @@ fn headerfieldvalue_to_value(tag: &Tag, value: &HeaderFieldValue) -> UntaggedVal
}
}
async fn from_eml(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn from_eml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (eml_args, input): (FromEmlArgs, _) = args.process().await?;
let value = input.collect_string(tag.clone()).await?;
let args = args.evaluate_once()?;
let body_preview = eml_args
.preview_body
.map(|b| b.item)
.unwrap_or(DEFAULT_BODY_PREVIEW);
let preview_body: Option<Tagged<usize>> = args.get_flag("preview-body")?;
let value = args.input.collect_string(tag.clone())?;
let body_preview = preview_body.map(|b| b.item).unwrap_or(DEFAULT_BODY_PREVIEW);
let eml = EmlParser::from_string(value.item)
.with_body_preview(body_preview)
@ -116,7 +109,7 @@ async fn from_eml(args: CommandArgs) -> Result<OutputStream, ShellError> {
dict.insert_untagged("Body", UntaggedValue::string(body));
}
Ok(OutputStream::one(ReturnSuccess::value(dict.into_value())))
Ok(OutputStream::one(dict.into_value()))
}
#[cfg(test)]

View File

@ -4,12 +4,11 @@ use ical::parser::ical::component::*;
use ical::property::Property;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
use std::io::BufReader;
pub struct FromIcs;
#[async_trait]
impl WholeStreamCommand for FromIcs {
fn name(&self) -> &str {
"from ics"
@ -23,17 +22,17 @@ impl WholeStreamCommand for FromIcs {
"Parse text as .ics and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_ics(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_ics(args)
}
}
async fn from_ics(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
fn from_ics(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.name_tag();
let input = args.input;
let input_string = input.collect_string(tag.clone()).await?.item;
let input_string = input.collect_string(tag.clone())?.item;
let input_bytes = input_string.as_bytes();
let buf_reader = BufReader::new(input_bytes);
let parser = ical::IcalParser::new(buf_reader);
@ -44,8 +43,8 @@ async fn from_ics(args: CommandArgs) -> Result<OutputStream, ShellError> {
for calendar in parser {
match calendar {
Ok(c) => output.push(ReturnSuccess::value(calendar_to_value(c, tag.clone()))),
Err(_) => output.push(Err(ShellError::labeled_error(
Ok(c) => output.push(calendar_to_value(c, tag.clone())),
Err(_) => output.push(Value::error(ShellError::labeled_error(
"Could not parse as .ics",
"input cannot be parsed as .ics",
tag.clone(),
@ -53,7 +52,7 @@ async fn from_ics(args: CommandArgs) -> Result<OutputStream, ShellError> {
}
}
Ok(futures::stream::iter(output).to_output_stream())
Ok(output.into_iter().to_output_stream())
}
fn calendar_to_value(calendar: IcalCalendar, tag: Tag) -> Value {

View File

@ -6,7 +6,6 @@ use std::collections::HashMap;
pub struct FromIni;
#[async_trait]
impl WholeStreamCommand for FromIni {
fn name(&self) -> &str {
"from ini"
@ -20,8 +19,8 @@ impl WholeStreamCommand for FromIni {
"Parse text as .ini and create table"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_ini(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_ini(args)
}
}
@ -60,18 +59,18 @@ pub fn from_ini_string_to_value(
Ok(convert_ini_top_to_nu_value(&v, tag))
}
async fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.name_tag();
let input = args.input;
let concat_string = input.collect_string(tag.clone()).await?;
let concat_string = input.collect_string(tag.clone())?;
match from_ini_string_to_value(concat_string.item, tag.clone()) {
Ok(x) => match x {
Value {
value: UntaggedValue::Table(list),
..
} => Ok(futures::stream::iter(list).to_output_stream()),
} => Ok(list.into_iter().to_output_stream()),
x => Ok(OutputStream::one(x)),
},
Err(_) => Err(ShellError::labeled_error_with_secondary(

View File

@ -1,16 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
pub struct FromJson;
#[derive(Deserialize)]
pub struct FromJsonArgs {
objects: bool,
}
#[async_trait]
impl WholeStreamCommand for FromJson {
fn name(&self) -> &str {
"from json"
@ -28,8 +22,8 @@ impl WholeStreamCommand for FromJson {
"Parse text as .json and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_json(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_json(args)
}
}
@ -68,29 +62,32 @@ pub fn from_json_string_to_value(s: String, tag: impl Into<Tag>) -> nu_json::Res
Ok(convert_json_value_to_nu_value(&v, tag))
}
async fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (FromJsonArgs { objects }, input) = args.process().await?;
let concat_string = input.collect_string(name_tag.clone()).await?;
let args = args.evaluate_once()?;
let objects = args.has_flag("objects");
let concat_string = args.input.collect_string(name_tag.clone())?;
let string_clone: Vec<_> = concat_string.item.lines().map(|x| x.to_string()).collect();
if objects {
Ok(
futures::stream::iter(string_clone.into_iter().filter_map(move |json_str| {
Ok(string_clone
.into_iter()
.filter_map(move |json_str| {
if json_str.is_empty() {
return None;
}
match from_json_string_to_value(json_str, &name_tag) {
Ok(x) => Some(ReturnSuccess::value(x)),
Ok(x) => Some(x),
Err(e) => {
let mut message = "Could not parse as JSON (".to_string();
message.push_str(&e.to_string());
message.push(')');
Some(Err(ShellError::labeled_error_with_secondary(
Some(Value::error(ShellError::labeled_error_with_secondary(
message,
"input cannot be parsed as JSON",
name_tag.clone(),
@ -99,27 +96,24 @@ async fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
)))
}
}
}))
.to_output_stream(),
)
})
.to_output_stream())
} else {
match from_json_string_to_value(concat_string.item, name_tag.clone()) {
Ok(x) => match x {
Value {
value: UntaggedValue::Table(list),
..
} => Ok(
futures::stream::iter(list.into_iter().map(ReturnSuccess::value))
.to_output_stream(),
),
x => Ok(OutputStream::one(ReturnSuccess::value(x))),
} => Ok(list.into_iter().to_output_stream()),
x => Ok(OutputStream::one(x)),
},
Err(e) => {
let mut message = "Could not parse as JSON (".to_string();
message.push_str(&e.to_string());
message.push(')');
Ok(OutputStream::one(Err(
Ok(OutputStream::one(Value::error(
ShellError::labeled_error_with_secondary(
message,
"input cannot be parsed as JSON",

View File

@ -3,50 +3,34 @@ use calamine::*;
use nu_data::TaggedListBuilder;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
use std::io::Cursor;
pub struct FromOds;
#[derive(Deserialize)]
pub struct FromOdsArgs {
noheaders: bool,
}
#[async_trait]
impl WholeStreamCommand for FromOds {
fn name(&self) -> &str {
"from ods"
}
fn signature(&self) -> Signature {
Signature::build("from ods").switch(
"noheaders",
"don't treat the first row as column names",
Some('n'),
)
Signature::build("from ods")
}
fn usage(&self) -> &str {
"Parse OpenDocument Spreadsheet(.ods) data and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_ods(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_ods(args)
}
}
async fn from_ods(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn from_ods(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let span = tag.span;
let (
FromOdsArgs {
noheaders: _noheaders,
},
input,
) = args.process().await?;
let bytes = input.collect_binary(tag.clone()).await?;
let bytes = args.input.collect_binary(tag.clone())?;
let buf: Cursor<Vec<u8>> = Cursor::new(bytes.item);
let mut ods = Ods::<_>::new(buf).map_err(|_| {
ShellError::labeled_error("Could not load ods file", "could not load ods file", &tag)
@ -88,7 +72,7 @@ async fn from_ods(args: CommandArgs) -> Result<OutputStream, ShellError> {
}
}
Ok(OutputStream::one(ReturnSuccess::value(dict.into_value())))
Ok(OutputStream::one(dict.into_value()))
}
#[cfg(test)]

View File

@ -1,26 +1,14 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
Primitive, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
};
use nu_protocol::{Primitive, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
use nu_source::Tagged;
pub struct FromSsv;
#[derive(Deserialize)]
pub struct FromSsvArgs {
noheaders: bool,
#[serde(rename(deserialize = "aligned-columns"))]
aligned_columns: bool,
#[serde(rename(deserialize = "minimum-spaces"))]
minimum_spaces: Option<Tagged<usize>>,
}
const STRING_REPRESENTATION: &str = "from ssv";
const DEFAULT_MINIMUM_SPACES: usize = 2;
#[async_trait]
impl WholeStreamCommand for FromSsv {
fn name(&self) -> &str {
STRING_REPRESENTATION
@ -46,8 +34,8 @@ impl WholeStreamCommand for FromSsv {
"Parse text as space-separated values and create a table. The default minimum number of spaces counted as a separator is 2."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_ssv(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_ssv(args)
}
}
@ -247,17 +235,15 @@ fn from_ssv_string_to_value(
UntaggedValue::Table(rows).into_value(&tag)
}
async fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let (
FromSsvArgs {
noheaders,
aligned_columns,
minimum_spaces,
},
input,
) = args.process().await?;
let concat_string = input.collect_string(name.clone()).await?;
let args = args.evaluate_once()?;
let noheaders = args.has_flag("noheaders");
let aligned_columns = args.has_flag("aligned-columns");
let minimum_spaces: Option<Tagged<usize>> = args.get_flag("minimum-spaces")?;
let concat_string = args.input.collect_string(name.clone())?;
let split_at = match minimum_spaces {
Some(number) => number.item,
None => DEFAULT_MINIMUM_SPACES,
@ -269,15 +255,13 @@ async fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
noheaders,
aligned_columns,
split_at,
name.clone(),
name,
) {
Value {
value: UntaggedValue::Table(list),
..
} => {
futures::stream::iter(list.into_iter().map(ReturnSuccess::value)).to_output_stream()
}
x => OutputStream::one(ReturnSuccess::value(x)),
} => list.into_iter().to_output_stream(),
x => OutputStream::one(x),
},
)
}

View File

@ -1,11 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
pub struct FromToml;
#[async_trait]
impl WholeStreamCommand for FromToml {
fn name(&self) -> &str {
"from toml"
@ -19,8 +18,8 @@ impl WholeStreamCommand for FromToml {
"Parse text as .toml and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_toml(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_toml(args)
}
}
@ -61,21 +60,20 @@ pub fn from_toml_string_to_value(s: String, tag: impl Into<Tag>) -> Result<Value
Ok(convert_toml_value_to_nu_value(&v, tag))
}
pub async fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
pub fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.name_tag();
let input = args.input;
let concat_string = input.collect_string(tag.clone()).await?;
let concat_string = input.collect_string(tag.clone())?;
Ok(
match from_toml_string_to_value(concat_string.item, tag.clone()) {
Ok(x) => match x {
Value {
value: UntaggedValue::Table(list),
..
} => futures::stream::iter(list.into_iter().map(ReturnSuccess::value))
.to_output_stream(),
x => OutputStream::one(ReturnSuccess::value(x)),
} => list.into_iter().to_output_stream(),
x => OutputStream::one(x),
},
Err(_) => {
return Err(ShellError::labeled_error_with_secondary(

View File

@ -6,12 +6,6 @@ use nu_protocol::Signature;
pub struct FromTsv;
#[derive(Deserialize)]
pub struct FromTsvArgs {
noheaders: bool,
}
#[async_trait]
impl WholeStreamCommand for FromTsv {
fn name(&self) -> &str {
"from tsv"
@ -29,16 +23,18 @@ impl WholeStreamCommand for FromTsv {
"Parse text as .tsv and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_tsv(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_tsv(args)
}
}
async fn from_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn from_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let (FromTsvArgs { noheaders }, input) = args.process().await?;
let args = args.evaluate_once()?;
let noheaders = args.has_flag("noheaders");
let input = args.input;
from_delimited_data(noheaders, '\t', "TSV", input, name).await
from_delimited_data(noheaders, '\t', "TSV", input, name)
}
#[cfg(test)]

View File

@ -1,11 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
pub struct FromUrl;
#[async_trait]
impl WholeStreamCommand for FromUrl {
fn name(&self) -> &str {
"from url"
@ -19,17 +18,17 @@ impl WholeStreamCommand for FromUrl {
"Parse url-encoded string as a table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_url(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_url(args)
}
}
async fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.name_tag();
let input = args.input;
let concat_string = input.collect_string(tag.clone()).await?;
let concat_string = input.collect_string(tag.clone())?;
let result = serde_urlencoded::from_str::<Vec<(String, String)>>(&concat_string.item);
@ -41,7 +40,7 @@ async fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
row.insert_untagged(k, UntaggedValue::string(v));
}
Ok(OutputStream::one(ReturnSuccess::value(row.into_value())))
Ok(OutputStream::one(row.into_value()))
}
_ => Err(ShellError::labeled_error_with_secondary(
"String not compatible with url-encoding",

View File

@ -1,14 +1,12 @@
extern crate ical;
use crate::prelude::*;
use ical::parser::vcard::component::*;
use ical::property::Property;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
pub struct FromVcf;
#[async_trait]
impl WholeStreamCommand for FromVcf {
fn name(&self) -> &str {
"from vcf"
@ -22,31 +20,33 @@ impl WholeStreamCommand for FromVcf {
"Parse text as .vcf and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_vcf(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_vcf(args)
}
}
async fn from_vcf(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
fn from_vcf(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.name_tag();
let input = args.input;
let input_string = input.collect_string(tag.clone()).await?.item;
let input_string = input.collect_string(tag.clone())?.item;
let input_bytes = input_string.into_bytes();
let cursor = std::io::Cursor::new(input_bytes);
let parser = ical::VcardParser::new(cursor);
let iter = parser.map(move |contact| match contact {
Ok(c) => ReturnSuccess::value(contact_to_value(c, tag.clone())),
Err(_) => Err(ShellError::labeled_error(
Ok(c) => contact_to_value(c, tag.clone()),
Err(_) => Value::error(ShellError::labeled_error(
"Could not parse as .vcf",
"input cannot be parsed as .vcf",
tag.clone(),
)),
});
Ok(futures::stream::iter(iter).to_output_stream())
let collected: Vec<_> = iter.collect();
Ok(collected.into_iter().to_output_stream())
}
fn contact_to_value(contact: VcardContact, tag: Tag) -> Value {

View File

@ -3,17 +3,11 @@ use calamine::*;
use nu_data::TaggedListBuilder;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
use std::io::Cursor;
pub struct FromXlsx;
#[derive(Deserialize)]
pub struct FromXlsxArgs {
noheaders: bool,
}
#[async_trait]
impl WholeStreamCommand for FromXlsx {
fn name(&self) -> &str {
"from xlsx"
@ -31,21 +25,16 @@ impl WholeStreamCommand for FromXlsx {
"Parse binary Excel(.xlsx) data and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_xlsx(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_xlsx(args)
}
}
async fn from_xlsx(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn from_xlsx(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let span = tag.span;
let (
FromXlsxArgs {
noheaders: _noheaders,
},
input,
) = args.process().await?;
let value = input.collect_binary(tag.clone()).await?;
let value = args.input.collect_binary(tag.clone())?;
let buf: Cursor<Vec<u8>> = Cursor::new(value.item);
let mut xls = Xlsx::<_>::new(buf).map_err(|_| {
@ -88,7 +77,7 @@ async fn from_xlsx(args: CommandArgs) -> Result<OutputStream, ShellError> {
}
}
Ok(OutputStream::one(ReturnSuccess::value(dict.into_value())))
Ok(OutputStream::one(dict.into_value()))
}
#[cfg(test)]

View File

@ -1,11 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
pub struct FromXml;
#[async_trait]
impl WholeStreamCommand for FromXml {
fn name(&self) -> &str {
"from xml"
@ -19,8 +18,8 @@ impl WholeStreamCommand for FromXml {
"Parse text as .xml and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_xml(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_xml(args)
}
}
@ -95,12 +94,12 @@ pub fn from_xml_string_to_value(s: String, tag: impl Into<Tag>) -> Result<Value,
Ok(from_document_to_value(&parsed, tag))
}
async fn from_xml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
fn from_xml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.name_tag();
let input = args.input;
let concat_string = input.collect_string(tag.clone()).await?;
let concat_string = input.collect_string(tag.clone())?;
Ok(
match from_xml_string_to_value(concat_string.item, tag.clone()) {
@ -108,9 +107,8 @@ async fn from_xml(args: CommandArgs) -> Result<OutputStream, ShellError> {
Value {
value: UntaggedValue::Table(list),
..
} => futures::stream::iter(list.into_iter().map(ReturnSuccess::value))
.to_output_stream(),
x => OutputStream::one(ReturnSuccess::value(x)),
} => list.into_iter().to_output_stream(),
x => OutputStream::one(x),
},
Err(_) => {
return Err(ShellError::labeled_error_with_secondary(

View File

@ -5,7 +5,6 @@ use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value}
pub struct FromYaml;
#[async_trait]
impl WholeStreamCommand for FromYaml {
fn name(&self) -> &str {
"from yaml"
@ -19,14 +18,13 @@ impl WholeStreamCommand for FromYaml {
"Parse text as .yaml/.yml and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_yaml(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_yaml(args)
}
}
pub struct FromYml;
#[async_trait]
impl WholeStreamCommand for FromYml {
fn name(&self) -> &str {
"from yml"
@ -40,8 +38,8 @@ impl WholeStreamCommand for FromYml {
"Parse text as .yaml/.yml and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_yaml(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_yaml(args)
}
}
@ -133,19 +131,19 @@ pub fn from_yaml_string_to_value(s: String, tag: impl Into<Tag>) -> Result<Value
convert_yaml_value_to_nu_value(&v, tag)
}
async fn from_yaml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
fn from_yaml(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.name_tag();
let input = args.input;
let concat_string = input.collect_string(tag.clone()).await?;
let concat_string = input.collect_string(tag.clone())?;
match from_yaml_string_to_value(concat_string.item, tag.clone()) {
Ok(x) => match x {
Value {
value: UntaggedValue::Table(list),
..
} => Ok(futures::stream::iter(list).to_output_stream()),
} => Ok(list.into_iter().to_output_stream()),
x => Ok(OutputStream::one(x)),
},
Err(_) => Err(ShellError::labeled_error_with_secondary(

View File

@ -18,7 +18,6 @@ pub struct Arguments {
rest: Vec<Value>,
}
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"get"
@ -35,8 +34,8 @@ impl WholeStreamCommand for Command {
"Open given cells as text."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
get(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
get(args)
}
fn examples(&self) -> Vec<Example> {
@ -55,29 +54,31 @@ impl WholeStreamCommand for Command {
}
}
pub async fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { mut rest }, mut input) = args.process().await?;
pub fn get(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (Arguments { mut rest }, mut input) = args.process()?;
let (column_paths, _) = arguments(&mut rest)?;
if column_paths.is_empty() {
let vec = input.drain_vec().await;
let vec = input.drain_vec();
let descs = nu_protocol::merge_descriptors(&vec);
Ok(futures::stream::iter(descs.into_iter().map(ReturnSuccess::value)).to_output_stream())
Ok(descs
.into_iter()
.map(ReturnSuccess::value)
.to_action_stream())
} else {
trace!("get {:?}", column_paths);
let output_stream = input
.map(move |item| {
let output = column_paths
column_paths
.iter()
.map(move |path| get_output(&item, path))
.flatten()
.collect::<Vec<_>>();
futures::stream::iter(output)
.collect::<Vec<_>>()
})
.flatten()
.to_output_stream();
.to_action_stream();
Ok(output_stream)
}
}
@ -227,8 +228,8 @@ pub fn get_column_from_row_error(
} => {
let primary_label = format!("There isn't a column named '{}'", &column);
if let Some(suggestions) = did_you_mean(&obj_source, column_path_tried.as_string()) {
Some(ShellError::labeled_error_with_secondary(
did_you_mean(&obj_source, column_path_tried.as_string()).map(|suggestions| {
ShellError::labeled_error_with_secondary(
"Unknown column",
primary_label,
column_path_tried.span,
@ -238,10 +239,8 @@ pub fn get_column_from_row_error(
&obj_source.data_descriptors().join(", ")
),
column_path_tried.span.since(path_members_span),
))
} else {
None
}
)
})
}
PathMember {
unspanned: UnspannedPathMember::Int(idx),

View File

@ -2,7 +2,8 @@ use crate::prelude::*;
use crate::utils::suggestions::suggestions;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::hir::ExternalRedirection;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
use nu_value_ext::as_string;
@ -13,7 +14,6 @@ pub struct Arguments {
grouper: Option<Value>,
}
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"group-by"
@ -31,8 +31,8 @@ impl WholeStreamCommand for Command {
"Create a new table grouped."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
group_by(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
group_by(args)
}
#[allow(clippy::unwrap_used)]
@ -128,12 +128,12 @@ enum Grouper {
ByBlock,
}
pub async fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
pub fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let context = Arc::new(EvaluationContext::from_args(&args));
let (Arguments { grouper }, input) = args.process().await?;
let (Arguments { grouper }, input) = args.process()?;
let values: Vec<Value> = input.collect().await;
let values: Vec<Value> = input.collect();
let mut keys: Vec<Result<String, ShellError>> = vec![];
let mut group_strategy = Grouper::ByColumn(None);
@ -149,10 +149,14 @@ pub async fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
let run = block.clone();
let context = context.clone();
match crate::commands::each::process_row(run, context, value.clone()).await {
match crate::commands::each::process_row(
run,
context,
value.clone(),
ExternalRedirection::Stdout,
) {
Ok(mut s) => {
let collection: Vec<Result<ReturnSuccess, ShellError>> =
s.drain_vec().await;
let collection: Vec<Value> = s.drain_vec();
if collection.len() > 1 {
return Err(ShellError::labeled_error(
@ -163,14 +167,12 @@ pub async fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
}
let value = match collection.get(0) {
Some(Ok(return_value)) => {
return_value.raw_value().unwrap_or_else(|| {
UntaggedValue::string(error_key).into_value(&name)
})
}
Some(Err(_)) | None => {
UntaggedValue::string(error_key).into_value(&name)
}
Some(Value {
value: UntaggedValue::Error(_),
..
})
| None => UntaggedValue::string(error_key).into_value(&name),
Some(return_value) => return_value.clone(),
};
keys.push(as_string(&value));
@ -209,7 +211,7 @@ pub async fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
let group_value = match group_strategy {
Grouper::ByBlock => {
let map = keys.clone();
let map = keys;
let block = Box::new(move |idx: usize, row: &Value| match map.get(idx) {
Some(Ok(key)) => Ok(key.clone()),
@ -222,7 +224,7 @@ pub async fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
Grouper::ByColumn(column_name) => group(&column_name, &values, &name),
};
Ok(OutputStream::one(ReturnSuccess::value(group_value?)))
Ok(OutputStream::one(group_value?))
}
pub fn group(

View File

@ -13,7 +13,6 @@ pub struct GroupByDateArgs {
format: Option<Tagged<String>>,
}
#[async_trait]
impl WholeStreamCommand for GroupByDate {
fn name(&self) -> &str {
"group-by date"
@ -38,8 +37,8 @@ impl WholeStreamCommand for GroupByDate {
"creates a table grouped by date."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
group_by_date(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
group_by_date(args)
}
fn examples(&self) -> Vec<Example> {
@ -59,7 +58,7 @@ enum GroupByColumn {
Name(Option<Tagged<String>>),
}
pub async fn group_by_date(args: CommandArgs) -> Result<OutputStream, ShellError> {
pub fn group_by_date(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone();
let (
GroupByDateArgs {
@ -67,8 +66,8 @@ pub async fn group_by_date(args: CommandArgs) -> Result<OutputStream, ShellError
format,
},
input,
) = args.process().await?;
let values: Vec<Value> = input.collect().await;
) = args.process()?;
let values: Vec<Value> = input.collect();
if values.is_empty() {
Err(ShellError::labeled_error(
@ -126,7 +125,7 @@ pub async fn group_by_date(args: CommandArgs) -> Result<OutputStream, ShellError
}
};
Ok(OutputStream::one(ReturnSuccess::value(value_result?)))
Ok(ActionStream::one(ReturnSuccess::value(value_result?)))
}
}

View File

@ -30,7 +30,6 @@ pub enum ActionType {
}
pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"hash base64"
@ -65,8 +64,8 @@ impl WholeStreamCommand for SubCommand {
"base64 encode or decode a value"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
operate(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
operate(args)
}
fn examples(&self) -> Vec<Example> {
@ -96,8 +95,8 @@ impl WholeStreamCommand for SubCommand {
}
}
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = &args.call_info.name_tag.clone();
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (
Arguments {
@ -107,10 +106,10 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
rest,
},
input,
) = args.process().await?;
) = args.process()?;
if encode.item && decode.item {
return Ok(OutputStream::one(Err(ShellError::labeled_error(
return Ok(ActionStream::one(Err(ShellError::labeled_error(
"only one of --decode and --encode flags can be used",
"conflicting flags",
name_tag,
@ -155,7 +154,7 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
ReturnSuccess::value(ret)
}
})
.to_output_stream())
.to_action_stream())
}
fn action(

View File

@ -5,7 +5,6 @@ use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
pub struct Command;
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"hash"
@ -22,9 +21,9 @@ impl WholeStreamCommand for Command {
"Apply hash function."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(get_full_help(&Command, &args.scope)).into_value(Tag::unknown()),
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
)))
}
}

View File

@ -14,7 +14,6 @@ pub struct Arguments {
pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"hash md5"
@ -31,8 +30,8 @@ impl WholeStreamCommand for SubCommand {
"md5 encode a value"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
operate(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
operate(args)
}
fn examples(&self) -> Vec<Example> {
@ -57,8 +56,8 @@ impl WholeStreamCommand for SubCommand {
}
}
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest }, input) = args.process().await?;
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (Arguments { rest }, input) = args.process()?;
let column_paths: Vec<_> = rest;
@ -79,7 +78,7 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
ReturnSuccess::value(ret)
}
})
.to_output_stream())
.to_action_stream())
}
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -1,5 +1,5 @@
use crate::prelude::*;
use futures::stream::StreamExt;
use indexmap::IndexMap;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
@ -8,7 +8,6 @@ use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
pub struct Headers;
#[async_trait]
impl WholeStreamCommand for Headers {
fn name(&self) -> &str {
"headers"
@ -22,8 +21,8 @@ impl WholeStreamCommand for Headers {
"Use the first row of the table as column names."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
headers(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
headers(args)
}
fn examples(&self) -> Vec<Example> {
@ -42,9 +41,9 @@ impl WholeStreamCommand for Headers {
}
}
pub async fn headers(args: CommandArgs) -> Result<OutputStream, ShellError> {
pub fn headers(args: CommandArgs) -> Result<ActionStream, ShellError> {
let input = args.input;
let rows: Vec<Value> = input.collect().await;
let rows: Vec<Value> = input.collect();
if rows.is_empty() {
return Err(ShellError::untagged_runtime_error(
@ -77,8 +76,10 @@ pub async fn headers(args: CommandArgs) -> Result<OutputStream, ShellError> {
)),
}?;
Ok(
futures::stream::iter(rows.into_iter().skip(1).map(move |r| {
Ok(rows
.into_iter()
.skip(1)
.map(move |r| {
//Each row is a dictionary with the headers as keys
match &r.value {
UntaggedValue::Row(d) => {
@ -100,9 +101,8 @@ pub async fn headers(args: CommandArgs) -> Result<OutputStream, ShellError> {
r.tag.span,
)),
}
}))
.to_output_stream(),
)
})
.to_action_stream())
}
#[cfg(test)]

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