Compare commits

...

68 Commits

Author SHA1 Message Date
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
398 changed files with 9309 additions and 7770 deletions

190
Cargo.lock generated
View File

@ -190,7 +190,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
dependencies = [ dependencies = [
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -281,7 +281,7 @@ checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -326,7 +326,7 @@ checksum = "36ea56748e10732c49404c153638a15ec3d6211ec5ff35d9bb20e13b93576adf"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -564,9 +564,9 @@ checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
[[package]] [[package]]
name = "byte-unit" name = "byte-unit"
version = "4.0.9" version = "4.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c8758c32833faaae35b24a73d332e62d0528e89076ae841c63940e37008b153" checksum = "b9520900471c3a9bbcfe0fd4c7b6bcfeff41b20a76cf91c59b7474b09be1ee27"
dependencies = [ dependencies = [
"utf8-width", "utf8-width",
] ]
@ -579,9 +579,9 @@ checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.2" version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "bytes" name = "bytes"
@ -780,6 +780,12 @@ dependencies = [
"winapi 0.3.9", "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]] [[package]]
name = "concurrent-queue" name = "concurrent-queue"
version = "1.2.2" version = "1.2.2"
@ -1104,7 +1110,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"smallvec 1.6.1", "smallvec 1.6.1",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -1114,7 +1120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e" checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e"
dependencies = [ dependencies = [
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -1146,7 +1152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19" checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19"
dependencies = [ dependencies = [
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -1254,7 +1260,7 @@ checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -1276,7 +1282,7 @@ checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -1576,7 +1582,7 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
"synstructure", "synstructure",
] ]
@ -1855,7 +1861,7 @@ dependencies = [
"proc-macro-hack", "proc-macro-hack",
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -2013,7 +2019,7 @@ dependencies = [
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -2045,6 +2051,12 @@ dependencies = [
"url", "url",
] ]
[[package]]
name = "gjson"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864178e25a00c41404f1728997c9a21a7b746be1faefe6ce4dc41eb48bb4234f"
[[package]] [[package]]
name = "glob" name = "glob"
version = "0.3.0" version = "0.3.0"
@ -2206,7 +2218,7 @@ dependencies = [
"markup5ever", "markup5ever",
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -3075,7 +3087,7 @@ dependencies = [
[[package]] [[package]]
name = "nu" name = "nu"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"clap", "clap",
"ctrlc", "ctrlc",
@ -3104,6 +3116,7 @@ dependencies = [
"nu_plugin_match", "nu_plugin_match",
"nu_plugin_post", "nu_plugin_post",
"nu_plugin_ps", "nu_plugin_ps",
"nu_plugin_query_json",
"nu_plugin_s3", "nu_plugin_s3",
"nu_plugin_selector", "nu_plugin_selector",
"nu_plugin_start", "nu_plugin_start",
@ -3119,7 +3132,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-ansi-term" name = "nu-ansi-term"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"doc-comment", "doc-comment",
"regex 1.4.3", "regex 1.4.3",
@ -3130,7 +3143,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-cli" name = "nu-cli"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"arboard", "arboard",
@ -3231,12 +3244,10 @@ dependencies = [
[[package]] [[package]]
name = "nu-command" name = "nu-command"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"arboard", "arboard",
"async-recursion",
"async-trait",
"base64 0.13.0", "base64 0.13.0",
"bigdecimal", "bigdecimal",
"byte-unit", "byte-unit",
@ -3259,8 +3270,6 @@ dependencies = [
"filesize", "filesize",
"fs_extra", "fs_extra",
"futures 0.3.13", "futures 0.3.13",
"futures-util",
"futures_codec",
"getset", "getset",
"glob", "glob",
"hamcrest2", "hamcrest2",
@ -3335,11 +3344,12 @@ dependencies = [
[[package]] [[package]]
name = "nu-data" name = "nu-data"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"bigdecimal", "bigdecimal",
"byte-unit", "byte-unit",
"chrono", "chrono",
"common-path",
"derive-new", "derive-new",
"directories-next", "directories-next",
"dirs-next", "dirs-next",
@ -3358,21 +3368,26 @@ dependencies = [
"num-traits 0.2.14", "num-traits 0.2.14",
"query_interface", "query_interface",
"serde 1.0.124", "serde 1.0.124",
"sha2 0.9.3",
"toml", "toml",
"users", "users",
] ]
[[package]] [[package]]
name = "nu-engine" name = "nu-engine"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"ansi_term 0.12.1",
"async-recursion", "async-recursion",
"async-trait", "async-trait",
"bigdecimal",
"bytes 0.5.6", "bytes 0.5.6",
"chrono",
"codespan-reporting", "codespan-reporting",
"derive-new", "derive-new",
"dirs-next", "dirs-next",
"dunce", "dunce",
"dyn-clone",
"encoding_rs", "encoding_rs",
"filesize", "filesize",
"fs_extra", "fs_extra",
@ -3395,6 +3410,9 @@ dependencies = [
"nu-stream", "nu-stream",
"nu-test-support", "nu-test-support",
"nu-value-ext", "nu-value-ext",
"num-bigint 0.3.2",
"num-format",
"num-traits 0.2.14",
"parking_lot 0.11.1", "parking_lot 0.11.1",
"rayon", "rayon",
"serde 1.0.124", "serde 1.0.124",
@ -3410,7 +3428,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-errors" name = "nu-errors"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"bigdecimal", "bigdecimal",
"codespan-reporting", "codespan-reporting",
@ -3429,7 +3447,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-json" name = "nu-json"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"dunce", "dunce",
"lazy_static 1.4.0", "lazy_static 1.4.0",
@ -3443,7 +3461,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-parser" name = "nu-parser"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"bigdecimal", "bigdecimal",
"codespan-reporting", "codespan-reporting",
@ -3466,7 +3484,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin" name = "nu-plugin"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"bigdecimal", "bigdecimal",
"indexmap", "indexmap",
@ -3482,7 +3500,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-protocol" name = "nu-protocol"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"bigdecimal", "bigdecimal",
"byte-unit", "byte-unit",
@ -3505,7 +3523,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-source" name = "nu-source"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"derive-new", "derive-new",
"getset", "getset",
@ -3516,7 +3534,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-stream" name = "nu-stream"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"futures 0.3.13", "futures 0.3.13",
"nu-errors", "nu-errors",
@ -3526,7 +3544,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-table" name = "nu-table"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"nu-ansi-term", "nu-ansi-term",
"regex 1.4.3", "regex 1.4.3",
@ -3535,7 +3553,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-test-support" name = "nu-test-support"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"bigdecimal", "bigdecimal",
"chrono", "chrono",
@ -3554,7 +3572,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-value-ext" name = "nu-value-ext"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"itertools", "itertools",
@ -3566,7 +3584,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_binaryview" name = "nu_plugin_binaryview"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"crossterm 0.19.0", "crossterm 0.19.0",
"image 0.22.5", "image 0.22.5",
@ -3582,7 +3600,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_chart" name = "nu_plugin_chart"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"crossterm 0.19.0", "crossterm 0.19.0",
"nu-cli", "nu-cli",
@ -3597,7 +3615,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_fetch" name = "nu_plugin_fetch"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"base64 0.13.0", "base64 0.13.0",
"futures 0.3.13", "futures 0.3.13",
@ -3612,7 +3630,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_from_bson" name = "nu_plugin_from_bson"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"bigdecimal", "bigdecimal",
"bson", "bson",
@ -3626,7 +3644,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_from_sqlite" name = "nu_plugin_from_sqlite"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"bigdecimal", "bigdecimal",
"nu-errors", "nu-errors",
@ -3641,7 +3659,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_inc" name = "nu_plugin_inc"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"nu-errors", "nu-errors",
"nu-plugin", "nu-plugin",
@ -3654,7 +3672,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_match" name = "nu_plugin_match"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"nu-errors", "nu-errors",
"nu-plugin", "nu-plugin",
@ -3665,7 +3683,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_post" name = "nu_plugin_post"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"base64 0.13.0", "base64 0.13.0",
"futures 0.3.13", "futures 0.3.13",
@ -3681,7 +3699,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_ps" name = "nu_plugin_ps"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"futures 0.3.13", "futures 0.3.13",
"futures-timer", "futures-timer",
@ -3693,9 +3711,21 @@ dependencies = [
"sysinfo", "sysinfo",
] ]
[[package]]
name = "nu_plugin_query_json"
version = "0.30.0"
dependencies = [
"gjson",
"nu-errors",
"nu-plugin",
"nu-protocol",
"nu-source",
"nu-test-support",
]
[[package]] [[package]]
name = "nu_plugin_s3" name = "nu_plugin_s3"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"futures 0.3.13", "futures 0.3.13",
"nu-errors", "nu-errors",
@ -3707,7 +3737,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_selector" name = "nu_plugin_selector"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"nipper", "nipper",
"nu-errors", "nu-errors",
@ -3719,7 +3749,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_start" name = "nu_plugin_start"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"glob", "glob",
"nu-errors", "nu-errors",
@ -3732,7 +3762,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_sys" name = "nu_plugin_sys"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"futures 0.3.13", "futures 0.3.13",
"futures-util", "futures-util",
@ -3746,7 +3776,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_textview" name = "nu_plugin_textview"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"bat", "bat",
"nu-ansi-term", "nu-ansi-term",
@ -3761,7 +3791,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_to_bson" name = "nu_plugin_to_bson"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"bson", "bson",
"nu-errors", "nu-errors",
@ -3774,7 +3804,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_to_sqlite" name = "nu_plugin_to_sqlite"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"hex 0.4.3", "hex 0.4.3",
"nu-errors", "nu-errors",
@ -3789,7 +3819,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_tree" name = "nu_plugin_tree"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"derive-new", "derive-new",
"nu-errors", "nu-errors",
@ -3801,7 +3831,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_xpath" name = "nu_plugin_xpath"
version = "0.29.0" version = "0.30.0"
dependencies = [ dependencies = [
"bigdecimal", "bigdecimal",
"indexmap", "indexmap",
@ -4015,9 +4045,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]] [[package]]
name = "open" name = "open"
version = "1.5.1" version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2033f93630dd4b04768ecf5e16bcd3002a89e1e1dbef375bf290dd67e2b7a4d" checksum = "a7e9f1bdf15cd1f5a00cc9002a733a6ee6d0ff562491852d59652471c4a389f7"
dependencies = [ dependencies = [
"which", "which",
"winapi 0.3.9", "winapi 0.3.9",
@ -4219,7 +4249,7 @@ dependencies = [
"proc-macro-hack", "proc-macro-hack",
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -4257,7 +4287,7 @@ checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -4268,7 +4298,7 @@ checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -4403,7 +4433,7 @@ dependencies = [
"proc-macro-error-attr", "proc-macro-error-attr",
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
"version_check", "version_check",
] ]
@ -4517,7 +4547,7 @@ checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -4939,7 +4969,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"rust-embed-utils", "rust-embed-utils",
"syn 1.0.62", "syn 1.0.63",
"walkdir", "walkdir",
] ]
@ -5235,7 +5265,7 @@ checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -5346,7 +5376,7 @@ checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -5511,7 +5541,7 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -5585,7 +5615,7 @@ dependencies = [
"quote 1.0.9", "quote 1.0.9",
"serde 1.0.124", "serde 1.0.124",
"serde_derive", "serde_derive",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -5601,7 +5631,7 @@ dependencies = [
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"sha1 0.6.0", "sha1 0.6.0",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -5740,9 +5770,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.62" version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512" checksum = "8fd9bc7ccc2688b3344c2f48b9b546648b25ce0b20fc717ee7fa7981a8ca9717"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
@ -5766,7 +5796,7 @@ checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
"unicode-xid 0.2.1", "unicode-xid 0.2.1",
] ]
@ -5795,9 +5825,9 @@ dependencies = [
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.16.4" version = "0.16.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c280c91abd1aed2e36be1bc8f56fbc7a2acbb2b58fbcac9641510179cc72dd9" checksum = "567e910ef0207be81a4e1bb0491e9a8d9866cf45b20fe1a52c03d347da9ea51b"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"core-foundation-sys", "core-foundation-sys",
@ -5913,7 +5943,7 @@ checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -5991,7 +6021,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"standback", "standback",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -6151,7 +6181,7 @@ checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -6340,13 +6370,13 @@ dependencies = [
[[package]] [[package]]
name = "tracing-attributes" name = "tracing-attributes"
version = "0.1.13" version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07" checksum = "41768be5b9f3489491825f56f01f25290aa1d3e7cc97e182d4d34360493ba6fa"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
] ]
[[package]] [[package]]
@ -6686,7 +6716,7 @@ dependencies = [
"log 0.4.14", "log 0.4.14",
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -6736,7 +6766,7 @@ checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
"syn 1.0.62", "syn 1.0.63",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]

View File

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

View File

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

View File

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

View File

@ -5,26 +5,26 @@ description = "CLI for nushell"
edition = "2018" edition = "2018"
license = "MIT" license = "MIT"
name = "nu-cli" name = "nu-cli"
version = "0.29.0" version = "0.30.0"
[lib] [lib]
doctest = false doctest = false
[dependencies] [dependencies]
nu-command = { version = "0.29.0", path = "../nu-command" } nu-command = { version = "0.30.0", path = "../nu-command" }
nu-data = { version = "0.29.0", path = "../nu-data" } nu-data = { version = "0.30.0", path = "../nu-data" }
nu-engine = { version = "0.29.0", path = "../nu-engine" } nu-engine = { version = "0.30.0", path = "../nu-engine" }
nu-errors = { version = "0.29.0", path = "../nu-errors" } nu-errors = { version = "0.30.0", path = "../nu-errors" }
nu-json = { version = "0.29.0", path = "../nu-json" } nu-json = { version = "0.30.0", path = "../nu-json" }
nu-parser = { version = "0.29.0", path = "../nu-parser" } nu-parser = { version = "0.30.0", path = "../nu-parser" }
nu-plugin = { version = "0.29.0", path = "../nu-plugin" } nu-plugin = { version = "0.30.0", path = "../nu-plugin" }
nu-protocol = { version = "0.29.0", path = "../nu-protocol" } nu-protocol = { version = "0.30.0", path = "../nu-protocol" }
nu-source = { version = "0.29.0", path = "../nu-source" } nu-source = { version = "0.30.0", path = "../nu-source" }
nu-stream = { version = "0.29.0", path = "../nu-stream" } nu-stream = { version = "0.30.0", path = "../nu-stream" }
nu-table = { version = "0.29.0", path = "../nu-table" } nu-table = { version = "0.30.0", path = "../nu-table" }
nu-test-support = { version = "0.29.0", path = "../nu-test-support" } nu-test-support = { version = "0.30.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.29.0", path = "../nu-value-ext" } nu-value-ext = { version = "0.30.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.29.0", path = "../nu-ansi-term" } nu-ansi-term = { version = "0.30.0", path = "../nu-ansi-term" }
Inflector = "0.11" Inflector = "0.11"
arboard = { version = "1.1.0", optional = true } arboard = { version = "1.1.0", optional = true }

View File

@ -1,6 +1,6 @@
use crate::line_editor::configure_ctrl_c; use crate::line_editor::configure_ctrl_c;
use nu_command::commands::default_context::create_default_context; use nu_ansi_term::Color;
use nu_engine::{evaluation_context, run_block, script::run_script_standalone, EvaluationContext}; use nu_engine::{maybe_print_errors, run_block, script::run_script_standalone, EvaluationContext};
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) use nu_engine::script::{process_script, LineResult}; pub(crate) use nu_engine::script::{process_script, LineResult};
@ -13,8 +13,7 @@ use crate::line_editor::{
#[allow(unused_imports)] #[allow(unused_imports)]
use nu_data::config; use nu_data::config;
use nu_data::config::{Conf, NuConfig}; use nu_source::{Tag, Text};
use nu_source::{AnchorLocation, Tag, Text};
use nu_stream::InputStream; use nu_stream::InputStream;
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
#[allow(unused_imports)] #[allow(unused_imports)]
@ -23,10 +22,9 @@ use std::sync::atomic::Ordering;
#[cfg(feature = "rustyline-support")] #[cfg(feature = "rustyline-support")]
use rustyline::{self, error::ReadlineError}; use rustyline::{self, error::ReadlineError};
use crate::EnvironmentSyncer;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_parser::ParserScope; use nu_parser::ParserScope;
use nu_protocol::{hir::ExternalRedirection, UntaggedValue, Value}; use nu_protocol::{hir::ExternalRedirection, ConfigPath, UntaggedValue, Value};
use log::trace; use log::trace;
use std::error::Error; use std::error::Error;
@ -35,10 +33,9 @@ use std::path::PathBuf;
pub struct Options { pub struct Options {
pub config: Option<OsString>, pub config: Option<OsString>,
pub history: Option<PathBuf>,
pub save_history: bool,
pub stdin: bool, pub stdin: bool,
pub scripts: Vec<NuScript>, pub scripts: Vec<NuScript>,
pub save_history: bool,
} }
impl Default for Options { impl Default for Options {
@ -51,20 +48,9 @@ impl Options {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
config: None, config: None,
history: None,
save_history: true,
stdin: false, stdin: false,
scripts: vec![], scripts: vec![],
} save_history: true,
}
pub fn history(&self, block: impl FnOnce(&std::path::Path)) {
if !self.save_history {
return;
}
if let Some(file) = &self.history {
block(&file)
} }
} }
} }
@ -137,106 +123,38 @@ pub fn search_paths() -> Vec<std::path::PathBuf> {
search_paths search_paths
} }
pub async fn run_script_file(mut options: Options) -> Result<(), Box<dyn Error>> { pub fn run_script_file(context: EvaluationContext, options: Options) -> Result<(), Box<dyn Error>> {
let mut context = create_default_context(false)?; if let Some(cfg) = options.config {
let mut syncer = create_environment_syncer(&context, &mut options); load_cfg_as_global_cfg(&context, PathBuf::from(cfg));
let config = syncer.get_config(); } 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 _ = register_plugins(&context);
let _ = configure_ctrl_c(ctx); let _ = configure_ctrl_c(&context);
});
let _ = run_startup_commands(&mut context, &config).await;
let script = options let script = options
.scripts .scripts
.get(0) .get(0)
.ok_or_else(|| ShellError::unexpected("Nu source code not available"))?; .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(()) 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")] #[cfg(feature = "rustyline-support")]
pub async fn cli( pub fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn Error>> {
mut context: EvaluationContext, let _ = configure_ctrl_c(&context);
mut options: Options,
) -> Result<(), Box<dyn Error>> {
let mut syncer = create_environment_syncer(&context, &mut options);
let configuration = syncer.get_config(); // start time for running startup scripts (this metric includes loading of the cfg, but w/e)
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
let startup_commands_start_time = std::time::Instant::now(); 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 // Store cmd duration in an env var
context.scope.add_env_var( context.scope.add_env_var(
"CMD_DURATION", "CMD_DURATION",
@ -247,20 +165,43 @@ pub async fn cli(
startup_commands_start_time.elapsed() 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 // Give ourselves a scope to work in
context.scope.enter_scope(); context.scope.enter_scope();
options.history(|file| {
let _ = rl.load_history(&file);
});
let mut session_text = String::new(); let mut session_text = String::new();
let mut line_start: usize = 0; 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 { if !skip_welcome_message {
println!( println!(
"Welcome to Nushell {} (type 'help' for more info)", "Welcome to Nushell {} (type 'help' for more info)",
@ -284,31 +225,39 @@ pub async fn cli(
let cwd = context.shell_manager.path(); let cwd = context.shell_manager.path();
let colored_prompt = { let colored_prompt = {
if let Some(prompt) = configuration.var("prompt") { if let Some(prompt) = &prompt {
let prompt_line = prompt.as_string()?; let prompt_line = prompt.as_string()?;
context.scope.enter_scope(); context.scope.enter_scope();
let (mut prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope); let (mut prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
prompt_block.set_redirect(ExternalRedirection::Stdout); if let Some(block) =
std::sync::Arc::<nu_protocol::hir::Block>::get_mut(&mut prompt_block)
{
block.set_redirect(ExternalRedirection::Stdout);
}
if err.is_some() { if err.is_some() {
context.scope.exit_scope(); 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 { } else {
let run_result = run_block(&prompt_block, &context, InputStream::empty()).await; let run_result = run_block(&prompt_block, &context, InputStream::empty());
context.scope.exit_scope(); context.scope.exit_scope();
match run_result { match run_result {
Ok(result) => match result.collect_string(Tag::unknown()).await { Ok(result) => match result.collect_string(Tag::unknown()) {
Ok(string_result) => { Ok(string_result) => {
let errors = context.get_errors(); let errors = context.get_errors();
evaluation_context::maybe_print_errors( maybe_print_errors(&context, Text::from(prompt_line));
&context,
Text::from(prompt_line),
);
context.clear_errors(); context.clear_errors();
if !errors.is_empty() { if !errors.is_empty() {
@ -333,7 +282,15 @@ pub async fn cli(
} }
} }
} else { } 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
)
} }
}; };
@ -363,16 +320,13 @@ pub async fn cli(
let cmd_start_time = std::time::Instant::now(); let cmd_start_time = std::time::Instant::now();
let line = match convert_rustyline_result_to_string(readline) { let line = match convert_rustyline_result_to_string(readline) {
LineResult::Success(_) => { LineResult::Success(_) => process_script(
process_script(
&session_text[line_start..], &session_text[line_start..],
&context, &context,
false, false,
line_start, line_start,
true, true,
) ),
.await
}
x => x, x => x,
}; };
@ -381,54 +335,50 @@ pub async fn cli(
.scope .scope
.add_env_var("CMD_DURATION", format!("{:?}", cmd_start_time.elapsed())); .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 { match line {
LineResult::Success(line) => { LineResult::Success(line) => {
options.history(|file| { if options.save_history && !line.trim().is_empty() {
rl.add_history_entry(&line); rl.add_history_entry(&line);
let _ = rl.save_history(&file); let _ = rl.append_history(&history_path);
}); }
maybe_print_errors(&context, Text::from(session_text.clone()));
evaluation_context::maybe_print_errors(&context, Text::from(session_text.clone()));
} }
LineResult::ClearHistory => { LineResult::ClearHistory => {
options.history(|file| { if options.save_history {
rl.clear_history(); rl.clear_history();
let _ = rl.save_history(&file); let _ = rl.append_history(&history_path);
}); }
} }
LineResult::Error(line, reason) => { LineResult::Error(line, err) => {
options.history(|file| { if options.save_history && !line.trim().is_empty() {
rl.add_history_entry(&line); 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 => { LineResult::CtrlC => {
let config_ctrlc_exit = configuration let config_ctrlc_exit = context
.var("ctrlc_exit") .configs
.map(|s| s.value.is_true()) .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 .unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells
if !config_ctrlc_exit { if !config_ctrlc_exit {
@ -436,10 +386,9 @@ pub async fn cli(
} }
if ctrlcbreak { if ctrlcbreak {
options.history(|file| { if options.save_history {
let _ = rl.save_history(&file); let _ = rl.append_history(&history_path);
}); }
std::process::exit(0); std::process::exit(0);
} else { } else {
context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)")); context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)"));
@ -463,14 +412,49 @@ pub async fn cli(
} }
// we are ok if we can not save history // we are ok if we can not save history
options.history(|file| { if options.save_history {
let _ = rl.save_history(&file); let _ = rl.append_history(&history_path);
}); }
Ok(()) 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()) { if let Ok(plugins) = nu_engine::plugin::build_plugin::scan(search_paths()) {
context.add_commands( context.add_commands(
plugins plugins
@ -483,35 +467,7 @@ pub fn register_plugins(context: &mut EvaluationContext) -> Result<(), ShellErro
Ok(()) Ok(())
} }
async fn run_startup_commands( pub fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, ShellError> {
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> {
// FIXME: do we still need this? // FIXME: do we still need this?
let line = if let Some(s) = line.strip_suffix('\n') { let line = if let Some(s) = line.strip_suffix('\n') {
s s
@ -528,13 +484,11 @@ pub async fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<Strin
} }
let input_stream = InputStream::empty(); 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);
ctx.scope.exit_scope(); 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)] #[allow(dead_code)]
@ -555,14 +509,14 @@ fn current_branch() -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use nu_engine::EvaluationContext; use nu_engine::basic_evaluation_context;
#[quickcheck] #[quickcheck]
fn quickcheck_parse(data: String) -> bool { fn quickcheck_parse(data: String) -> bool {
let (tokens, err) = nu_parser::lex(&data, 0); let (tokens, err) = nu_parser::lex(&data, 0);
let (lite_block, err2) = nu_parser::parse_block(tokens); let (lite_block, err2) = nu_parser::parse_block(tokens);
if err.is_none() && err2.is_none() { if err.is_none() && err2.is_none() {
let context = EvaluationContext::basic().unwrap(); let context = basic_evaluation_context().unwrap();
let _ = nu_parser::classify_block(&lite_block, &context.scope); let _ = nu_parser::classify_block(&lite_block, &context.scope);
} }
true true

View File

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

View File

@ -12,7 +12,7 @@ impl matchers::Matcher for Matcher {
mod tests { mod tests {
use super::*; 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 // FIXME: could work exhaustively through ['-', '--'. ''] in a loop for each test
#[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"] #![recursion_limit = "2048"]
#[cfg(test)]
#[macro_use]
extern crate indexmap;
#[macro_use] #[macro_use]
mod prelude; mod prelude;
@ -16,7 +12,6 @@ extern crate quickcheck_macros;
mod cli; mod cli;
#[cfg(feature = "rustyline-support")] #[cfg(feature = "rustyline-support")]
mod completion; mod completion;
mod env;
mod format; mod format;
#[cfg(feature = "rustyline-support")] #[cfg(feature = "rustyline-support")]
mod keybinding; 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::{parse_and_eval, register_plugins, run_script_file};
pub use crate::cli::{NuScript, Options}; 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_command::commands::default_context::create_default_context;
pub use nu_data::config; pub use nu_data::config;
pub use nu_data::dict::TaggedListBuilder; pub use nu_data::dict::TaggedListBuilder;
pub use nu_data::primitive; pub use nu_data::primitive;
pub use nu_data::value; 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 nu_value_ext::ValueExt;
pub use num_traits::cast::ToPrimitive; pub use num_traits::cast::ToPrimitive;

View File

@ -202,7 +202,7 @@ pub fn configure_rustyline_editor(
#[cfg(feature = "rustyline-support")] #[cfg(feature = "rustyline-support")]
pub fn nu_line_editor_helper( pub fn nu_line_editor_helper(
context: &mut EvaluationContext, context: &EvaluationContext,
config: &dyn nu_data::config::Conf, config: &dyn nu_data::config::Conf,
) -> crate::shell::Helper { ) -> crate::shell::Helper {
let hinter = rustyline_hinter(config); let hinter = rustyline_hinter(config);
@ -224,7 +224,7 @@ pub fn rustyline_hinter(
Some(rustyline::hint::HistoryHinter {}) 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")] #[cfg(feature = "ctrlc")]
{ {
let cc = _context.ctrl_c.clone(); 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_export]
macro_rules! trace_out_stream { macro_rules! trace_out_stream {
(target: $target:tt, $desc:tt = $expr:expr) => {{ (target: $target:tt, $desc:tt = $expr:expr) => {{
if log::log_enabled!(target: $target, log::Level::Trace) { if log::log_enabled!(target: $target, log::Level::Trace) {
use futures::stream::StreamExt;
let objects = $expr.inspect(move |o| { let objects = $expr.inspect(move |o| {
trace!( trace!(
target: $target, target: $target,
@ -46,13 +31,12 @@ macro_rules! trace_out_stream {
}}; }};
} }
pub(crate) use futures::{Stream, StreamExt};
pub(crate) use nu_engine::Host; pub(crate) use nu_engine::Host;
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) use nu_errors::ShellError; pub(crate) use nu_errors::ShellError;
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) use nu_protocol::outln; pub(crate) use nu_protocol::outln;
pub(crate) use nu_stream::OutputStream; pub(crate) use nu_stream::ActionStream;
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) use nu_value_ext::ValueExt; pub(crate) use nu_value_ext::ValueExt;
#[allow(unused_imports)] #[allow(unused_imports)]
@ -60,33 +44,16 @@ pub(crate) use std::sync::atomic::Ordering;
#[allow(clippy::clippy::wrong_self_convention)] #[allow(clippy::clippy::wrong_self_convention)]
pub trait FromInputStream { pub trait FromInputStream {
fn from_input_stream(self) -> OutputStream; fn from_input_stream(self) -> ActionStream;
} }
impl<T> FromInputStream for T impl<T> FromInputStream for T
where where
T: Stream<Item = nu_protocol::Value> + Send + 'static, T: Iterator<Item = nu_protocol::Value> + Send + Sync + 'static,
{ {
fn from_input_stream(self) -> OutputStream { fn from_input_stream(self) -> ActionStream {
OutputStream { ActionStream {
values: self.map(nu_protocol::ReturnSuccess::value).boxed(), values: Box::new(self.map(nu_protocol::ReturnSuccess::value)),
}
}
}
#[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(),
} }
} }
} }

View File

@ -1,5 +1,6 @@
use crate::completion; use crate::completion;
use crate::shell::completer::NuCompleter; use crate::shell::completer::NuCompleter;
use nu_ansi_term::Color;
use nu_engine::{DefaultPalette, EvaluationContext, Painter}; use nu_engine::{DefaultPalette, EvaluationContext, Painter};
use nu_source::{Tag, Tagged}; use nu_source::{Tag, Tagged};
use std::borrow::Cow::{self, Owned}; 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> { 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> { fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
@ -149,6 +150,7 @@ impl rustyline::Helper for Helper {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use nu_engine::basic_evaluation_context;
use rustyline::completion::Completer; use rustyline::completion::Completer;
use rustyline::line_buffer::LineBuffer; use rustyline::line_buffer::LineBuffer;
@ -162,7 +164,7 @@ mod tests {
buffer.insert_str(0, text); buffer.insert_str(0, text);
buffer.set_pos(text.len() - 1); buffer.set_pos(text.len() - 1);
let helper = Helper::new(EvaluationContext::basic().unwrap(), None); let helper = Helper::new(basic_evaluation_context().unwrap(), None);
helper.update(&mut buffer, "cd ".len(), &replacement); helper.update(&mut buffer, "cd ".len(), &replacement);
@ -182,7 +184,7 @@ mod tests {
buffer.insert_str(0, text); buffer.insert_str(0, text);
buffer.set_pos(text.len() - 30); buffer.set_pos(text.len() - 30);
let helper = Helper::new(EvaluationContext::basic().unwrap(), None); let helper = Helper::new(basic_evaluation_context().unwrap(), None);
helper.update(&mut buffer, "cd ".len(), &replacement); 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), 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 (?) //ExternalWord is anything sent to external commands (?)
Expression::ExternalWord => Some(SyntaxShape::String), Expression::ExternalWord => Some(SyntaxShape::String),
Expression::Synthetic(_) => Some(SyntaxShape::String), Expression::Synthetic(_) => Some(SyntaxShape::String),
@ -387,7 +387,7 @@ impl VarSyntaxShapeDeductor {
} }
fn infer_shape(&mut self, block: &Block, scope: &Scope) -> Result<(), ShellError> { 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 group in &block.block {
for pipeline in &group.pipelines { for pipeline in &group.pipelines {
self.infer_pipeline(pipeline, scope)?; self.infer_pipeline(pipeline, scope)?;
@ -397,7 +397,7 @@ impl VarSyntaxShapeDeductor {
} }
pub fn infer_pipeline(&mut self, pipeline: &Pipeline, scope: &Scope) -> Result<(), ShellError> { 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() { for (cmd_pipeline_idx, classified) in pipeline.list.iter().enumerate() {
match &classified { match &classified {
ClassifiedCommand::Internal(internal) => { ClassifiedCommand::Internal(internal) => {
@ -429,7 +429,7 @@ impl VarSyntaxShapeDeductor {
} }
} }
if let Some(named) = &internal.args.named { 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() { for (_name, val) in named.iter() {
if let NamedValue::Value(_, named_expr) = val { if let NamedValue::Value(_, named_expr) = val {
self.infer_shapes_in_expr( self.infer_shapes_in_expr(
@ -443,7 +443,7 @@ impl VarSyntaxShapeDeductor {
} }
ClassifiedCommand::Expr(spanned_expr) => { ClassifiedCommand::Expr(spanned_expr) => {
trace!( trace!(
"Infering shapes in ClassifiedCommand::Expr: {:?}", "Inferring shapes in ClassifiedCommand::Expr: {:?}",
spanned_expr spanned_expr
); );
self.infer_shapes_in_expr((cmd_pipeline_idx, pipeline), spanned_expr, scope)?; self.infer_shapes_in_expr((cmd_pipeline_idx, pipeline), spanned_expr, scope)?;
@ -459,7 +459,7 @@ impl VarSyntaxShapeDeductor {
positionals: &[SpannedExpression], positionals: &[SpannedExpression],
signature: &Signature, signature: &Signature,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
trace!("Infering vars in positionals"); trace!("Inferring vars in positionals");
//TODO currently correct inference for optional positionals is not implemented. //TODO currently correct inference for optional positionals is not implemented.
// See https://github.com/nushell/nushell/pull/2486 for a discussion about this // 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 // For now we assume every variable in an optional positional is used as this optional
@ -500,7 +500,7 @@ impl VarSyntaxShapeDeductor {
named: &NamedArguments, named: &NamedArguments,
signature: &Signature, signature: &Signature,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
trace!("Infering vars in named"); trace!("Inferring vars in named");
for (name, val) in named.iter() { for (name, val) in named.iter() {
if let NamedValue::Value(span, spanned_expr) = &val { if let NamedValue::Value(span, spanned_expr) = &val {
if let Expression::Variable(var_name, _) = &spanned_expr.expr { if let Expression::Variable(var_name, _) = &spanned_expr.expr {
@ -534,15 +534,15 @@ impl VarSyntaxShapeDeductor {
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
match &spanned_expr.expr { match &spanned_expr.expr {
Expression::Binary(_) => { 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)?; self.infer_shapes_in_binary_expr((pipeline_idx, pipeline), spanned_expr, scope)?;
} }
Expression::Block(b) => { Expression::Block(b) => {
trace!("Infering vars in block"); trace!("Inferring vars in block");
self.infer_shape(&b, scope)?; self.infer_shape(&b, scope)?;
} }
Expression::Path(path) => { Expression::Path(path) => {
trace!("Infering vars in path"); trace!("Inferring vars in path");
match &path.head.expr { match &path.head.expr {
//PathMember can't be var yet (?) //PathMember can't be var yet (?)
//TODO Iterate over path parts and find var when implemented //TODO Iterate over path parts and find var when implemented
@ -560,7 +560,7 @@ impl VarSyntaxShapeDeductor {
} }
} }
Expression::Range(range) => { Expression::Range(range) => {
trace!("Infering vars in range"); trace!("Inferring vars in range");
if let Some(range_left) = &range.left { if let Some(range_left) = &range.left {
if let Expression::Variable(var_name, _) = &range_left.expr { if let Expression::Variable(var_name, _) = &range_left.expr {
self.checked_insert( self.checked_insert(
@ -585,13 +585,13 @@ impl VarSyntaxShapeDeductor {
} }
} }
Expression::List(inner_exprs) => { Expression::List(inner_exprs) => {
trace!("Infering vars in list"); trace!("Inferring vars in list");
for expr in inner_exprs { for expr in inner_exprs {
self.infer_shapes_in_expr((pipeline_idx, pipeline), expr, scope)?; self.infer_shapes_in_expr((pipeline_idx, pipeline), expr, scope)?;
} }
} }
Expression::Invocation(invoc) => { Expression::Invocation(invoc) => {
trace!("Infering vars in invocation: {:?}", invoc); trace!("Inferring vars in invocation: {:?}", invoc);
self.infer_shape(invoc, scope)?; self.infer_shape(invoc, scope)?;
} }
Expression::Table(header, _rows) => { Expression::Table(header, _rows) => {
@ -738,7 +738,7 @@ impl VarSyntaxShapeDeductor {
(pipeline_idx, pipeline): (usize, &Pipeline), (pipeline_idx, pipeline): (usize, &Pipeline),
scope: &Scope, scope: &Scope,
) -> Result<(), ShellError> { ) -> 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); let bin = spanned_to_binary(bin_spanned);
if let Expression::Literal(Literal::Operator(op)) = bin.op.expr { if let Expression::Literal(Literal::Operator(op)) = bin.op.expr {
match &op { match &op {

View File

@ -5,30 +5,28 @@ description = "CLI for nushell"
edition = "2018" edition = "2018"
license = "MIT" license = "MIT"
name = "nu-command" name = "nu-command"
version = "0.29.0" version = "0.30.0"
[lib] [lib]
doctest = false doctest = false
[dependencies] [dependencies]
nu-data = { version = "0.29.0", path = "../nu-data" } nu-data = { version = "0.30.0", path = "../nu-data" }
nu-engine = { version = "0.29.0", path = "../nu-engine" } nu-engine = { version = "0.30.0", path = "../nu-engine" }
nu-errors = { version = "0.29.0", path = "../nu-errors" } nu-errors = { version = "0.30.0", path = "../nu-errors" }
nu-json = { version = "0.29.0", path = "../nu-json" } nu-json = { version = "0.30.0", path = "../nu-json" }
nu-parser = { version = "0.29.0", path = "../nu-parser" } nu-parser = { version = "0.30.0", path = "../nu-parser" }
nu-plugin = { version = "0.29.0", path = "../nu-plugin" } nu-plugin = { version = "0.30.0", path = "../nu-plugin" }
nu-protocol = { version = "0.29.0", path = "../nu-protocol" } nu-protocol = { version = "0.30.0", path = "../nu-protocol" }
nu-source = { version = "0.29.0", path = "../nu-source" } nu-source = { version = "0.30.0", path = "../nu-source" }
nu-stream = { version = "0.29.0", path = "../nu-stream" } nu-stream = { version = "0.30.0", path = "../nu-stream" }
nu-table = { version = "0.29.0", path = "../nu-table" } nu-table = { version = "0.30.0", path = "../nu-table" }
nu-test-support = { version = "0.29.0", path = "../nu-test-support" } nu-test-support = { version = "0.30.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.29.0", path = "../nu-value-ext" } nu-value-ext = { version = "0.30.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.29.0", path = "../nu-ansi-term" } nu-ansi-term = { version = "0.30.0", path = "../nu-ansi-term" }
Inflector = "0.11" Inflector = "0.11"
arboard = { version = "1.1.0", optional = true } arboard = { version = "1.1.0", optional = true }
async-recursion = "0.3.2"
async-trait = "0.1.42"
base64 = "0.13.0" base64 = "0.13.0"
bigdecimal = { version = "0.2.0", features = ["serde"] } bigdecimal = { version = "0.2.0", features = ["serde"] }
byte-unit = "4.0.9" byte-unit = "4.0.9"
@ -51,8 +49,6 @@ encoding_rs = "0.8.28"
filesize = "0.2.0" filesize = "0.2.0"
fs_extra = "1.2.0" fs_extra = "1.2.0"
futures = { version = "0.3.12", features = ["compat", "io-compat"] } futures = { version = "0.3.12", features = ["compat", "io-compat"] }
futures-util = "0.3.12"
futures_codec = "0.4.1"
getset = "0.1.1" getset = "0.1.1"
glob = "0.3.0" glob = "0.3.0"
htmlescape = "0.3.1" htmlescape = "0.3.1"

View File

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

@ -7,14 +7,6 @@ use nu_source::Tagged;
pub struct Command; pub struct Command;
#[derive(Deserialize)]
struct AnsiArgs {
code: Value,
escape: Option<Tagged<String>>,
osc: Option<Tagged<String>>,
}
#[async_trait]
impl WholeStreamCommand for Command { impl WholeStreamCommand for Command {
fn name(&self) -> &str { fn name(&self) -> &str {
"ansi" "ansi"
@ -120,8 +112,12 @@ Format: #
] ]
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let (AnsiArgs { code, escape, osc }, _) = args.process().await?; 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 { if let Some(e) = escape {
let esc_vec: Vec<char> = e.item.chars().collect(); let esc_vec: Vec<char> = e.item.chars().collect();
@ -133,7 +129,7 @@ Format: #
)); ));
} }
let output = format!("\x1b[{}", e.item); let output = format!("\x1b[{}", e.item);
return Ok(OutputStream::one(ReturnSuccess::value( return Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(e.tag()), UntaggedValue::string(output).into_value(e.tag()),
))); )));
} }
@ -151,16 +147,16 @@ Format: #
//Operating system command aka osc ESC ] <- note the right brace, not left brace for osc //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 // OCS's need to end with a bell '\x07' char
let output = format!("\x1b]{};", o.item); let output = format!("\x1b]{};", o.item);
return Ok(OutputStream::one(ReturnSuccess::value( return Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(o.tag()), UntaggedValue::string(output).into_value(o.tag()),
))); )));
} }
let code_string = code.as_string()?; if let Some(code) = code {
let ansi_code = str_to_ansi(code_string); let ansi_code = str_to_ansi(&code.item);
if let Some(output) = ansi_code { if let Some(output) = ansi_code {
Ok(OutputStream::one(ReturnSuccess::value( Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(code.tag()), UntaggedValue::string(output).into_value(code.tag()),
))) )))
} else { } else {
@ -170,11 +166,18 @@ Format: #
code.tag(), code.tag(),
)) ))
} }
} else {
Err(ShellError::labeled_error(
"Expected ansi code",
"expect ansi code",
args.call_info.name_tag.clone(),
))
}
} }
} }
pub fn str_to_ansi(s: String) -> Option<String> { pub fn str_to_ansi(s: &str) -> Option<String> {
match s.as_str() { match s {
"g" | "green" => Some(Color::Green.prefix().to_string()), "g" | "green" => Some(Color::Green.prefix().to_string()),
"gb" | "green_bold" => Some(Color::Green.bold().prefix().to_string()), "gb" | "green_bold" => Some(Color::Green.bold().prefix().to_string()),
"gu" | "green_underline" => Some(Color::Green.underline().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 // 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 // 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()), "rgb_bg" => Some("\x1b[48;2;".to_string()),
// Ansi color index - Needs 38;5;idx or 48;5;idx where idx = 0 to 255 // Ansi color index - Needs 38;5;idx or 48;5;idx where idx = 0 to 255

View File

@ -15,7 +15,6 @@ struct Arguments {
rest: Vec<ColumnPath>, rest: Vec<ColumnPath>,
} }
#[async_trait]
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"ansi strip" "ansi strip"
@ -32,8 +31,8 @@ impl WholeStreamCommand for SubCommand {
"strip ansi escape sequences from string" "strip ansi escape sequences from string"
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
operate(args).await operate(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -45,8 +44,8 @@ impl WholeStreamCommand for SubCommand {
} }
} }
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> { fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (Arguments { rest }, input) = args.process().await?; let (Arguments { rest }, input) = args.process()?;
let column_paths: Vec<_> = rest; let column_paths: Vec<_> = rest;
Ok(input Ok(input
@ -66,7 +65,7 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
ReturnSuccess::value(ret) ReturnSuccess::value(ret)
} }
}) })
.to_output_stream()) .to_action_stream())
} }
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -0,0 +1,134 @@
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;
#[derive(Deserialize)]
pub struct Arguments {
block: 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_with_actions(&self, args: CommandArgs) -> Result<ActionStream, 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<ActionStream, ShellError> {
let ctx = Arc::new(EvaluationContext::from_args(&args));
let tag = args.call_info.name_tag.clone();
let (Arguments { block }, input) = args.process()?;
let condition = {
if block.block.block.len() != 1 {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
match block.block.block[0].pipelines.get(0) {
Some(item) => match item.list.get(0) {
Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
_ => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
},
None => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
}
};
let cond = Ok(InputStream::one(
UntaggedValue::boolean(false).into_value(&tag),
));
Ok(input
.fold(cond, move |cond, row| {
let condition = condition.clone();
let ctx = ctx.clone();
ctx.scope.enter_scope();
ctx.scope.add_vars(&block.captured.entries);
ctx.scope.add_var("$it", row);
let condition = evaluate_baseline_expr(&condition, &*ctx);
ctx.scope.exit_scope();
let curr = cond?.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),
}
})?
.to_action_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 crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
#[derive(Deserialize)] #[derive(Deserialize)]
struct Arguments { struct Arguments {
@ -10,7 +10,6 @@ struct Arguments {
pub struct Command; pub struct Command;
#[async_trait]
impl WholeStreamCommand for Command { impl WholeStreamCommand for Command {
fn name(&self) -> &str { fn name(&self) -> &str {
"append" "append"
@ -28,13 +27,14 @@ impl WholeStreamCommand for Command {
"Append a row to the table." "Append a row to the table."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { mut value }, input) = args.process().await?; 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(); value.tag = first.tag();
prepend.push(first);
} }
// Checks if we are trying to append a row literal // Checks if we are trying to append a row literal
@ -48,18 +48,13 @@ impl WholeStreamCommand for Command {
} }
} }
Ok(futures::stream::iter( Ok(prepend
input
.into_iter() .into_iter()
.chain(vec![value]) .chain(input.into_iter().chain(vec![value]))
.map(ReturnSuccess::value),
)
.to_output_stream()) .to_output_stream())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
use nu_protocol::row;
vec![ vec![
Example { Example {
description: "Add values to the end of the table", description: "Add values to the end of the table",

View File

@ -2,50 +2,8 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; 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; 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 { impl WholeStreamCommand for Autoenv {
fn name(&self) -> &str { fn name(&self) -> &str {
"autoenv" "autoenv"
@ -56,18 +14,19 @@ impl WholeStreamCommand for Autoenv {
fn extra_usage(&self) -> &str { 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." // "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. r#"Create a file called .nu-env in any directory and run 'autoenv trust' to let nushell load it when entering the directory.
The file can contain several optional sections: The .nu-env file has the same format as your $HOME/nu/config.toml file. By loading a .nu-env file the following applies:
env: environment variables to set when visiting the directory. The variables are unset after leaving the directory and any overwritten values are restored. - environment variables (section \"[env]\") are loaded from the .nu-env file. Those env variables only exist in this directory (and children directories)
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. - the \"startup\" commands are run when entering the directory
scripts: scripts to run when entering the directory or leaving it."# - the \"on_exit\" commands are run when leaving the directory
"#
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("autoenv") Signature::build("autoenv")
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(OutputStream::one(ReturnSuccess::value( Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(get_full_help(&Autoenv, &args.scope)).into_value(Tag::unknown()), 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 { vec![Example {
description: "Example .nu-env file", description: "Example .nu-env file",
example: r#"cat .nu-env example: r#"cat .nu-env
startup = ["echo ...entering the directory", "echo 1 2 3"]
on_exit = ["echo ...leaving the directory"]
[env] [env]
mykey = "myvalue" mykey = "myvalue"
"#,
[scriptvars]
myscript = "echo myval"
[scripts]
entryscripts = ["touch hello.txt", "touch hello2.txt"]
exitscripts = ["touch bye.txt"]"#,
result: None, result: None,
}] }]
} }

View File

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

View File

@ -1,5 +1,5 @@
use super::autoenv::Trusted;
use crate::prelude::*; use crate::prelude::*;
use nu_data::config::Trusted;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::SyntaxShape; use nu_protocol::SyntaxShape;
@ -8,7 +8,6 @@ use std::io::Read;
use std::{fs, path::PathBuf}; use std::{fs, path::PathBuf};
pub struct AutoenvUnTrust; pub struct AutoenvUnTrust;
#[async_trait]
impl WholeStreamCommand for AutoenvUnTrust { impl WholeStreamCommand for AutoenvUnTrust {
fn name(&self) -> &str { fn name(&self) -> &str {
"autoenv untrust" "autoenv untrust"
@ -26,10 +25,10 @@ impl WholeStreamCommand for AutoenvUnTrust {
"Untrust a .nu-env file in the current or given directory" "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 tag = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args); 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 { Some(Value {
value: UntaggedValue::Primitive(Primitive::String(ref path)), value: UntaggedValue::Primitive(Primitive::String(ref path)),
tag: _, tag: _,
@ -80,7 +79,7 @@ impl WholeStreamCommand for AutoenvUnTrust {
})?; })?;
fs::write(config_path, tomlstr).expect("Couldn't write to toml file"); 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), 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::prelude::*;
use crate::primitive::get_color_config; use crate::primitive::get_color_config;
use nu_data::value::format_leaf; 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::hir::{self, Expression, ExternalRedirection, Literal, SpannedExpression};
use nu_protocol::{Primitive, Signature, UntaggedValue, Value}; use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
use nu_table::TextStyle; use nu_table::TextStyle;
use parking_lot::Mutex;
use std::sync::atomic::AtomicBool;
pub struct Command; pub struct Command;
#[async_trait]
impl WholeStreamCommand for Command { impl WholeStreamCommand for Command {
fn name(&self) -> &str { fn name(&self) -> &str {
"autoview" "autoview"
@ -26,17 +23,8 @@ impl WholeStreamCommand for Command {
"View the contents of the pipeline as a table or list." "View the contents of the pipeline as a table or list."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
autoview(RunnableContext { autoview(args)
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 examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -55,57 +43,28 @@ impl WholeStreamCommand for Command {
} }
} }
pub struct RunnableContextWithoutInput { pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
pub shell_manager: ShellManager, let configuration = context.configs.lock().global_config();
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,
}
impl RunnableContextWithoutInput { let binary = context.scope.get_command("binaryview");
pub fn convert(context: RunnableContext) -> (InputStream, RunnableContextWithoutInput) { let text = context.scope.get_command("textview");
let new_context = RunnableContextWithoutInput { let table = context.scope.get_command("table");
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)
}
}
pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellError> { let (mut input_stream, context) = context.split();
let configuration = AutoViewConfiguration::new();
let binary = context.get_command("binaryview"); if let Some(x) = input_stream.next() {
let text = context.get_command("textview"); match input_stream.next() {
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 {
Some(y) => { Some(y) => {
let ctrl_c = context.ctrl_c.clone(); let ctrl_c = context.ctrl_c.clone();
let xy = vec![x, y]; let xy = vec![x, y];
let xy_stream = futures::stream::iter(xy) let xy_stream = xy.into_iter().chain(input_stream).interruptible(ctrl_c);
.chain(input_stream)
.interruptible(ctrl_c);
let stream = InputStream::from_stream(xy_stream); let stream = InputStream::from_stream(xy_stream);
if let Some(table) = table { if let Some(table) = table {
let command_args = create_default_command_args(&context).with_input(stream); let command_args = create_default_command_args(&context).with_input(stream);
let result = table.run(command_args).await?; let result = table.run(command_args)?;
result.collect::<Vec<_>>().await; let _ = result.collect::<Vec<_>>();
} }
} }
_ => { _ => {
@ -121,8 +80,8 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
); );
let command_args = let command_args =
create_default_command_args(&context).with_input(stream); create_default_command_args(&context).with_input(stream);
let result = text.run(command_args).await?; let result = text.run_with_actions(command_args)?;
result.collect::<Vec<_>>().await; let _ = result.collect::<Vec<_>>();
} else { } else {
out!("{}", s); out!("{}", s);
} }
@ -203,8 +162,8 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
stream.push_back(x); stream.push_back(x);
let command_args = let command_args =
create_default_command_args(&context).with_input(stream); create_default_command_args(&context).with_input(stream);
let result = binary.run(command_args).await?; let result = binary.run_with_actions(command_args)?;
result.collect::<Vec<_>>().await; let _ = result.collect::<Vec<_>>();
} else { } else {
use pretty_hex::*; use pretty_hex::*;
out!("{:?}", b.hex_dump()); out!("{:?}", b.hex_dump());
@ -219,9 +178,13 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
} }
Value { Value {
value: UntaggedValue::Row(row), value: UntaggedValue::Row(ref row),
.. ..
} if pivot_mode.is_always() } => {
let pivot_mode = configuration.pivot_mode();
let term_width = context.host.lock().width();
if pivot_mode.is_always()
|| (pivot_mode.is_auto() || (pivot_mode.is_auto()
&& (row && (row
.entries .entries
@ -231,7 +194,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
.iter() .iter()
.fold(0usize, |acc, len| acc + len.len()) .fold(0usize, |acc, len| acc + len.len())
+ row.entries.iter().count() * 2) + row.entries.iter().count() * 2)
> term_width) => > term_width)
{ {
let mut entries = vec![]; let mut entries = vec![];
for (key, value) in row.entries.iter() { for (key, value) in row.entries.iter() {
@ -250,10 +213,22 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
]); ]);
} }
let color_hm = get_color_config(&configuration);
let table = let table =
nu_table::Table::new(vec![], entries, nu_table::Theme::compact()); nu_table::Table::new(vec![], entries, nu_table::Theme::compact());
println!("{}", nu_table::draw_table(&table, term_width, &color_hm)); println!("{}", nu_table::draw_table(&table, term_width, &color_hm));
} else 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)?;
let _ = result.collect::<Vec<_>>();
} else {
out!("{:?}", row);
}
} }
Value { Value {
value: UntaggedValue::Primitive(Primitive::Nothing), value: UntaggedValue::Primitive(Primitive::Nothing),
@ -269,8 +244,8 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
stream.push_back(x); stream.push_back(x);
let command_args = let command_args =
create_default_command_args(&context).with_input(stream); create_default_command_args(&context).with_input(stream);
let result = table.run(command_args).await?; let result = table.run(command_args)?;
result.collect::<Vec<_>>().await; let _ = result.collect::<Vec<_>>();
} else { } else {
out!("{:?}", item); out!("{:?}", item);
} }
@ -280,7 +255,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
} }
} }
Ok(OutputStream::empty()) Ok(InputStream::empty())
} }
fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawCommandArgs { fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawCommandArgs {
@ -288,6 +263,7 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
RawCommandArgs { RawCommandArgs {
host: context.host.clone(), host: context.host.clone(),
ctrl_c: context.ctrl_c.clone(), ctrl_c: context.ctrl_c.clone(),
configs: context.configs.clone(),
current_errors: context.current_errors.clone(), current_errors: context.current_errors.clone(),
shell_manager: context.shell_manager.clone(), shell_manager: context.shell_manager.clone(),
call_info: UnevaluatedCallInfo { call_info: UnevaluatedCallInfo {

View File

@ -23,7 +23,6 @@ struct BenchmarkArgs {
passthrough: Option<CapturedBlock>, passthrough: Option<CapturedBlock>,
} }
#[async_trait]
impl WholeStreamCommand for Benchmark { impl WholeStreamCommand for Benchmark {
fn name(&self) -> &str { fn name(&self) -> &str {
"benchmark" "benchmark"
@ -48,8 +47,8 @@ impl WholeStreamCommand for Benchmark {
"Runs a block and returns the time it took to execute it." "Runs a block and returns the time it took to execute it."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
benchmark(args).await benchmark(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -68,11 +67,11 @@ impl WholeStreamCommand for Benchmark {
} }
} }
async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> { fn benchmark(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
let tag = raw_args.call_info.args.span; let tag = raw_args.call_info.args.span;
let mut context = EvaluationContext::from_args(&raw_args); let mut context = EvaluationContext::from_args(&raw_args);
let scope = raw_args.scope.clone(); let scope = raw_args.scope.clone();
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process().await?; let (BenchmarkArgs { block, passthrough }, input) = raw_args.process()?;
let env = scope.get_env_vars(); let env = scope.get_env_vars();
let name = generate_free_name(&env); let name = generate_free_name(&env);
@ -82,15 +81,15 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let start_time = Instant::now(); let start_time = Instant::now();
// #[cfg(feature = "rich-benchmark")] // #[cfg(feature = "rich-benchmark")]
// let start = time().await; // let start = time();
context.scope.enter_scope(); context.scope.enter_scope();
let result = run_block(&block.block, &context, input).await; let result = run_block(&block.block, &context, input);
context.scope.exit_scope(); context.scope.exit_scope();
let output = result?.into_vec().await; let output = result?.into_vec();
// #[cfg(feature = "rich-benchmark")] // #[cfg(feature = "rich-benchmark")]
// let end = time().await; // let end = time();
let end_time = Instant::now(); let end_time = Instant::now();
context.clear_errors(); context.clear_errors();
@ -102,7 +101,7 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let real_time = into_big_int(end_time - start_time); let real_time = into_big_int(end_time - start_time);
indexmap.insert("real time".to_string(), real_time); indexmap.insert("real time".to_string(), real_time);
benchmark_output(indexmap, output, passthrough, &tag, &mut context).await benchmark_output(indexmap, output, passthrough, &tag, &mut context)
} }
// return advanced stats // return advanced stats
// #[cfg(feature = "rich-benchmark")] // #[cfg(feature = "rich-benchmark")]
@ -121,7 +120,7 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
// let idle_time = into_big_int(end.idle() - start.idle()); // let idle_time = into_big_int(end.idle() - start.idle());
// indexmap.insert("idle time".to_string(), idle_time); // 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 { // } else {
// Err(ShellError::untagged_runtime_error( // Err(ShellError::untagged_runtime_error(
// "Could not retrieve CPU time", // "Could not retrieve CPU time",
@ -129,16 +128,16 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
// } // }
} }
async fn benchmark_output<T, Output>( fn benchmark_output<T, Output>(
indexmap: IndexMap<String, BigInt>, indexmap: IndexMap<String, BigInt>,
block_output: Output, block_output: Output,
passthrough: Option<CapturedBlock>, passthrough: Option<CapturedBlock>,
tag: T, tag: T,
context: &mut EvaluationContext, context: &mut EvaluationContext,
) -> Result<OutputStream, ShellError> ) -> Result<ActionStream, ShellError>
where where
T: Into<Tag> + Copy, T: Into<Tag> + Copy,
Output: Into<OutputStream>, Output: Into<ActionStream>,
{ {
let value = UntaggedValue::Row(Dictionary::from( let value = UntaggedValue::Row(Dictionary::from(
indexmap indexmap
@ -155,19 +154,20 @@ where
let time_block = add_implicit_autoview(time_block.block); let time_block = add_implicit_autoview(time_block.block);
context.scope.enter_scope(); context.scope.enter_scope();
let result = run_block(&time_block, context, benchmark_output).await; let result = run_block(&time_block, context, benchmark_output);
context.scope.exit_scope(); context.scope.exit_scope();
result?; result?;
context.clear_errors(); context.clear_errors();
Ok(block_output.into()) Ok(block_output.into())
} else { } else {
let benchmark_output = OutputStream::one(value); let benchmark_output = ActionStream::one(value);
Ok(benchmark_output) Ok(benchmark_output)
} }
} }
fn add_implicit_autoview(mut block: Block) -> Block { 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() { if block.block.is_empty() {
let group = Group::new( let group = Group::new(
vec![{ vec![{
@ -183,6 +183,7 @@ fn add_implicit_autoview(mut block: Block) -> Block {
); );
block.push(group); block.push(group);
} }
}
block block
} }

View File

@ -5,14 +5,8 @@ use nu_data::value::format_leaf;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
#[derive(Deserialize)]
pub struct BuildStringArgs {
rest: Vec<Value>,
}
pub struct BuildString; pub struct BuildString;
#[async_trait]
impl WholeStreamCommand for BuildString { impl WholeStreamCommand for BuildString {
fn name(&self) -> &str { fn name(&self) -> &str {
"build-string" "build-string"
@ -27,9 +21,10 @@ impl WholeStreamCommand for BuildString {
"Builds a string from the arguments." "Builds a string from the arguments."
} }
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 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(); let mut output_string = String::new();
@ -37,7 +32,7 @@ impl WholeStreamCommand for BuildString {
output_string.push_str(&format_leaf(&r).plain_string(100_000)) output_string.push_str(&format_leaf(&r).plain_string(100_000))
} }
Ok(OutputStream::one(ReturnSuccess::value( Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(output_string).into_value(tag), UntaggedValue::string(output_string).into_value(tag),
))) )))
} }

View File

@ -7,7 +7,6 @@ use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
pub struct Cal; pub struct Cal;
#[async_trait]
impl WholeStreamCommand for Cal { impl WholeStreamCommand for Cal {
fn name(&self) -> &str { fn name(&self) -> &str {
"cal" "cal"
@ -41,8 +40,8 @@ impl WholeStreamCommand for Cal {
"Display a calendar." "Display a calendar."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
cal(args).await cal(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -66,8 +65,8 @@ impl WholeStreamCommand for Cal {
} }
} }
pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn cal(args: CommandArgs) -> Result<ActionStream, ShellError> {
let args = args.evaluate_once().await?; let args = args.evaluate_once()?;
let mut calendar_vec_deque = VecDeque::new(); let mut calendar_vec_deque = VecDeque::new();
let tag = args.call_info.name_tag.clone(); 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 selected_year: i32 = current_year;
let mut current_day_option: Option<u32> = Some(current_day); 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() { if let Ok(year_u64) = full_year_value.as_u64() {
selected_year = year_u64 as i32; selected_year = year_u64 as i32;
@ -102,7 +101,7 @@ pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
current_day_option, 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 { fn get_invalid_year_shell_error(year_tag: &Tag) -> ShellError {
@ -210,7 +209,7 @@ fn add_month_to_table(
let month_helper = match month_helper_result { let month_helper = match month_helper_result {
Ok(month_helper) => month_helper, Ok(month_helper) => month_helper,
Err(()) => match args.get("full-year") { Err(()) => match args.call_info.args.get("full-year") {
Some(full_year_value) => { Some(full_year_value) => {
return Err(get_invalid_year_shell_error(&full_year_value.tag())) 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(); 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 let Ok(day) = week_start_value.as_string() {
if days_of_the_week.contains(&day.as_str()) { if days_of_the_week.contains(&day.as_str()) {
week_start_day = day; week_start_day = day;
@ -265,10 +264,10 @@ fn add_month_to_table(
let mut day_number: u32 = 1; let mut day_number: u32 = 1;
let day_limit: u32 = total_start_offset + month_helper.number_of_days_in_month; 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_year_column = args.has_flag("year");
let should_show_quarter_column = args.has("quarter"); let should_show_quarter_column = args.has_flag("quarter");
let should_show_month_column = args.has("month"); let should_show_month_column = args.has_flag("month");
let should_show_month_names = args.has("month-names"); let should_show_month_names = args.has_flag("month-names");
while day_number <= day_limit { while day_number <= day_limit {
let mut indexmap = IndexMap::new(); let mut indexmap = IndexMap::new();

View File

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

View File

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

View File

@ -6,7 +6,6 @@ use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
#[derive(Clone)] #[derive(Clone)]
pub struct Chart; pub struct Chart;
#[async_trait]
impl WholeStreamCommand for Chart { impl WholeStreamCommand for Chart {
fn name(&self) -> &str { fn name(&self) -> &str {
"chart" "chart"
@ -20,14 +19,14 @@ impl WholeStreamCommand for Chart {
"Displays charts." "Displays charts."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
if args.scope.get_command("chart bar").is_none() { if args.scope.get_command("chart bar").is_none() {
return Err(ShellError::untagged_runtime_error( return Err(ShellError::untagged_runtime_error(
"nu_plugin_chart not installed.", "nu_plugin_chart not installed.",
)); ));
} }
Ok(OutputStream::one(Ok(ReturnSuccess::Value( Ok(ActionStream::one(Ok(ReturnSuccess::Value(
UntaggedValue::string(get_full_help(&Chart, &args.scope)).into_value(Tag::unknown()), 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 crate::prelude::*;
use nu_engine::evaluate_baseline_expr; use nu_engine::{evaluate_baseline_expr, BufCodecReader};
use nu_engine::{MaybeTextCodec, StringOrBinary}; use nu_engine::{MaybeTextCodec, StringOrBinary};
use parking_lot::Mutex;
use std::borrow::Cow;
use std::io::Write; use std::io::Write;
use std::ops::Deref; use std::ops::Deref;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::sync::mpsc; use std::sync::mpsc;
use std::{borrow::Cow, io::BufReader};
use futures::executor::block_on_stream;
use futures_codec::FramedRead;
use log::trace; use log::trace;
use nu_errors::ShellError; use nu_errors::ShellError;
@ -18,9 +16,8 @@ use nu_protocol::hir::Expression;
use nu_protocol::hir::{ExternalCommand, ExternalRedirection}; use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value}; use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
use nu_source::Tag; use nu_source::Tag;
use nu_stream::trace_stream;
pub(crate) async fn run_external_command( pub(crate) fn run_external_command(
command: ExternalCommand, command: ExternalCommand,
context: &mut EvaluationContext, context: &mut EvaluationContext,
input: InputStream, input: InputStream,
@ -28,18 +25,19 @@ pub(crate) async fn run_external_command(
) -> Result<InputStream, ShellError> { ) -> Result<InputStream, ShellError> {
trace!(target: "nu::run::external", "-> {}", command.name); trace!(target: "nu::run::external", "-> {}", command.name);
context.sync_path_to_env();
if !context.host.lock().is_external_cmd(&command.name) { if !context.host.lock().is_external_cmd(&command.name) {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Command not found", "Command not found",
"command not found", format!("command {} not found", &command.name),
&command.name_tag, &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, command: ExternalCommand,
context: &mut EvaluationContext, context: &mut EvaluationContext,
input: InputStream, input: InputStream,
@ -47,12 +45,10 @@ async fn run_with_stdin(
) -> Result<InputStream, ShellError> { ) -> Result<InputStream, ShellError> {
let path = context.shell_manager.path(); let path = context.shell_manager.path();
let input = trace_stream!(target: "nu::trace_stream::external::stdin", "input" = input);
let mut command_args = vec![]; let mut command_args = vec![];
for arg in command.args.iter() { for arg in command.args.iter() {
let is_literal = matches!(arg.expr, Expression::Literal(_)); 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 // 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 // FIXME: we may want to preserve the gap in the future, though it's hard to say
@ -219,7 +215,7 @@ fn spawn(
.take() .take()
.expect("Internal error: could not get stdin pipe for external command"); .expect("Internal error: could not get stdin pipe for external command");
for value in block_on_stream(input) { for value in input {
match &value.value { match &value.value {
UntaggedValue::Primitive(Primitive::Nothing) => continue, UntaggedValue::Primitive(Primitive::Nothing) => continue,
UntaggedValue::Primitive(Primitive::String(s)) => { UntaggedValue::Primitive(Primitive::String(s)) => {
@ -274,10 +270,12 @@ fn spawn(
return Err(()); return Err(());
}; };
let file = futures::io::AllowStdIo::new(stdout); // let file = futures::io::AllowStdIo::new(stdout);
let stream = FramedRead::new(file, MaybeTextCodec::default()); // 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 block_on_stream(stream) { for line in buf_codec {
match line { match line {
Ok(line) => match line { Ok(line) => match line {
StringOrBinary::String(s) => { StringOrBinary::String(s) => {
@ -345,10 +343,12 @@ fn spawn(
return Err(()); return Err(());
}; };
let file = futures::io::AllowStdIo::new(stderr); // let file = futures::io::AllowStdIo::new(stderr);
let stream = FramedRead::new(file, MaybeTextCodec::default()); // let stream = FramedRead::new(file, MaybeTextCodec::default());
let buf_reader = BufReader::new(stderr);
let buf_codec = BufCodecReader::new(buf_reader, MaybeTextCodec::default());
for line in block_on_stream(stream) { for line in buf_codec {
match line { match line {
Ok(line) => match line { Ok(line) => match line {
StringOrBinary::String(s) => { StringOrBinary::String(s) => {
@ -432,7 +432,7 @@ fn spawn(
Ok(()) Ok(())
}); });
let stream = ThreadedReceiver::new(rx); let stream = ChannelReceiver::new(rx);
Ok(stream.to_input_stream()) Ok(stream.to_input_stream())
} else { } else {
Err(ShellError::labeled_error( Err(ShellError::labeled_error(
@ -443,6 +443,30 @@ fn spawn(
} }
} }
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,
}
}
}
fn expand_tilde<SI: ?Sized, P, HD>(input: &SI, home_dir: HD) -> std::borrow::Cow<str> fn expand_tilde<SI: ?Sized, P, HD>(input: &SI, home_dir: HD) -> std::borrow::Cow<str>
where where
SI: AsRef<str>, SI: AsRef<str>,
@ -507,15 +531,12 @@ mod tests {
use super::{run_external_command, InputStream}; use super::{run_external_command, InputStream};
#[cfg(feature = "which")] #[cfg(feature = "which")]
use futures::executor::block_on; use nu_engine::basic_evaluation_context;
#[cfg(feature = "which")]
use nu_engine::EvaluationContext;
#[cfg(feature = "which")]
use nu_errors::ShellError;
#[cfg(feature = "which")] #[cfg(feature = "which")]
use nu_test_support::commands::ExternalBuilder; use nu_test_support::commands::ExternalBuilder;
// async fn read(mut stream: OutputStream) -> Option<Value> { // fn read(mut stream: OutputStream) -> Option<Value> {
// match stream.try_next().await { // match stream.try_next() {
// Ok(val) => { // Ok(val) => {
// if let Some(val) = val { // if let Some(val) = val {
// val.raw_value() // val.raw_value()
@ -528,32 +549,26 @@ mod tests {
// } // }
#[cfg(feature = "which")] #[cfg(feature = "which")]
async fn non_existent_run() -> Result<(), ShellError> { fn non_existent_run() {
use nu_protocol::hir::ExternalRedirection; use nu_protocol::hir::ExternalRedirection;
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build(); let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
let input = InputStream::empty(); let input = InputStream::empty();
let mut ctx = let mut ctx =
EvaluationContext::basic().expect("There was a problem creating a basic context."); basic_evaluation_context().expect("There was a problem creating a basic context.");
assert!( assert!(run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout).is_err());
run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout)
.await
.is_err()
);
Ok(())
} }
// async fn failure_run() -> Result<(), ShellError> { // fn failure_run() -> Result<(), ShellError> {
// let cmd = ExternalBuilder::for_name("fail").build(); // let cmd = ExternalBuilder::for_name("fail").build();
// let mut ctx = crate::cli::EvaluationContext::basic().expect("There was a problem creating a basic context."); // let mut ctx = crate::cli::basic_evaluation_context().expect("There was a problem creating a basic context.");
// let stream = run_external_command(cmd, &mut ctx, None, false) // let stream = run_external_command(cmd, &mut ctx, None, false)
// .await? // ?
// .expect("There was a problem running the external command."); // .expect("There was a problem running the external command.");
// match read(stream.into()).await { // match read(stream.into()) {
// Some(Value { // Some(Value {
// value: UntaggedValue::Error(_), // value: UntaggedValue::Error(_),
// .. // ..
@ -571,8 +586,8 @@ mod tests {
#[cfg(feature = "which")] #[cfg(feature = "which")]
#[test] #[test]
fn identifies_command_not_found() -> Result<(), ShellError> { fn identifies_command_not_found() {
block_on(non_existent_run()) non_existent_run()
} }
#[test] #[test]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,3 +13,9 @@ pub use path::SubCommand as ConfigPath;
pub use remove::SubCommand as ConfigRemove; pub use remove::SubCommand as ConfigRemove;
pub use set::SubCommand as ConfigSet; pub use set::SubCommand as ConfigSet;
pub use set_into::SubCommand as ConfigSetInto; 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 crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
pub struct SubCommand; pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"config path" "config path"
@ -19,8 +18,8 @@ impl WholeStreamCommand for SubCommand {
"return the path to the config file" "return the path to the config file"
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
path(args).await path(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -32,19 +31,16 @@ impl WholeStreamCommand for SubCommand {
} }
} }
pub async fn path(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn path(args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(OutputStream::one(ReturnSuccess::value( if let Some(global_cfg) = &mut args.configs.lock().global_config {
match args.scope.get_var("config-path") { Ok(ActionStream::one(ReturnSuccess::value(
Some( UntaggedValue::Primitive(Primitive::FilePath(global_cfg.file_path.clone())),
path
@
Value {
value: UntaggedValue::Primitive(Primitive::FilePath(_)),
..
},
) => path,
_ => UntaggedValue::Primitive(Primitive::FilePath(nu_data::config::default_path()?))
.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())
}
} }

View File

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged; use nu_source::Tagged;
pub struct SubCommand; pub struct SubCommand;
@ -11,7 +11,6 @@ pub struct Arguments {
remove: Tagged<String>, remove: Tagged<String>,
} }
#[async_trait]
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"config remove" "config remove"
@ -29,8 +28,8 @@ impl WholeStreamCommand for SubCommand {
"Removes a value from the config" "Removes a value from the config"
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
remove(args).await remove(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -42,30 +41,22 @@ impl WholeStreamCommand for SubCommand {
} }
} }
pub async fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn remove(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name_span = args.call_info.name_tag.clone(); let ctx = EvaluationContext::from_args(&args);
let scope = args.scope.clone(); let (Arguments { remove }, _) = args.process()?;
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)?;
let key = remove.to_string(); let key = remove.to_string();
if result.contains_key(&key) { let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
result.swap_remove(&key); if global_cfg.vars.contains_key(&key) {
config::write(&result, &path)?; global_cfg.vars.swap_remove(&key);
Ok(futures::stream::iter(vec![ReturnSuccess::value( global_cfg.write()?;
UntaggedValue::Row(result.into()).into_value(remove.tag()), ctx.reload_config(global_cfg)?;
)]) Ok(vec![ReturnSuccess::value(
.to_output_stream()) UntaggedValue::row(global_cfg.vars.clone()).into_value(remove.tag()),
)]
.into_iter()
.to_action_stream())
} else { } else {
Err(ShellError::labeled_error( Err(ShellError::labeled_error(
"Key does not exist in config", "Key does not exist in config",
@ -73,4 +64,13 @@ pub async fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> {
remove.tag(), remove.tag(),
)) ))
} }
} else {
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 crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
pub struct SubCommand; pub struct SubCommand;
@ -13,7 +11,6 @@ pub struct Arguments {
value: Value, value: Value,
} }
#[async_trait]
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"config set" "config set"
@ -29,8 +26,8 @@ impl WholeStreamCommand for SubCommand {
"Sets a value in the config" "Sets a value in the config"
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
set(args).await set(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -59,27 +56,19 @@ 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 name = args.call_info.name_tag.clone();
let scope = args.scope.clone(); let ctx = EvaluationContext::from_args(&args);
let ( let (
Arguments { Arguments {
column_path, column_path,
mut value, mut value,
}, },
_, _,
) = args.process().await?; ) = args.process()?;
let path = match scope.get_var("config-path") { let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
Some(Value { let configuration = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
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 let UntaggedValue::Table(rows) = &value.value {
if rows.len() == 1 && rows[0].is_row() { if rows.len() == 1 && rows[0].is_row() {
@ -92,13 +81,24 @@ pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
value: UntaggedValue::Row(changes), value: UntaggedValue::Row(changes),
.. ..
}) => { }) => {
config::write(&changes.entries, &path)?; global_cfg.vars = changes.entries;
global_cfg.write()?;
ctx.reload_config(global_cfg)?;
Ok(OutputStream::one(ReturnSuccess::value( Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::Row(changes).into_value(name), UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
))) )))
} }
Ok(_) => Ok(OutputStream::empty()), Ok(_) => Ok(ActionStream::empty()),
Err(reason) => Err(reason), Err(reason) => Err(reason),
} }
} else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
};
result
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,6 @@ struct DoArgs {
ignore_errors: bool, ignore_errors: bool,
} }
#[async_trait]
impl WholeStreamCommand for Do { impl WholeStreamCommand for Do {
fn name(&self) -> &str { fn name(&self) -> &str {
"do" "do"
@ -32,8 +31,8 @@ impl WholeStreamCommand for Do {
"Runs a block, optionally ignoring errors." "Runs a block, optionally ignoring errors."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
do_(args).await do_(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -52,7 +51,7 @@ 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 external_redirection = raw_args.call_info.args.external_redirection;
let context = EvaluationContext::from_args(&raw_args); let context = EvaluationContext::from_args(&raw_args);
@ -62,7 +61,7 @@ async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
mut block, mut block,
}, },
input, input,
) = raw_args.process().await?; ) = raw_args.process()?;
let block_redirection = match external_redirection { let block_redirection = match external_redirection {
ExternalRedirection::None => { ExternalRedirection::None => {
@ -82,9 +81,11 @@ async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
x => x, x => x,
}; };
block.block.set_redirect(block_redirection); if let Some(block) = std::sync::Arc::<nu_protocol::hir::Block>::get_mut(&mut block.block) {
block.set_redirect(block_redirection);
}
context.scope.enter_scope(); context.scope.enter_scope();
let result = run_block(&block.block, &context, input).await; let result = run_block(&block.block, &context, input);
context.scope.exit_scope(); context.scope.exit_scope();
if ignore_errors { if ignore_errors {
@ -93,14 +94,14 @@ async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
match result { match result {
Ok(mut stream) => { Ok(mut stream) => {
let output = stream.drain_vec().await; let output = stream.drain_vec();
context.clear_errors(); 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 { } 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>>, columns: Option<Tagged<u64>>,
} }
#[async_trait]
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"drop column" "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'." "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> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
drop(args).await drop(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
use nu_protocol::{row, Value}; use nu_protocol::Value;
vec![Example { vec![Example {
description: "Remove the last column of a table", description: "Remove the last column of a table",
@ -48,8 +47,8 @@ impl WholeStreamCommand for SubCommand {
} }
} }
async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> { fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (Arguments { columns }, input) = args.process().await?; let (Arguments { columns }, input) = args.process()?;
let to_drop = if let Some(quantity) = columns { let to_drop = if let Some(quantity) = columns {
*quantity as usize *quantity as usize
@ -70,7 +69,7 @@ async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
select_fields(&item, descs, item.tag()) select_fields(&item, descs, item.tag())
}) })
.map(ReturnSuccess::value) .map(ReturnSuccess::value)
.to_output_stream()) .to_action_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -11,7 +11,6 @@ pub struct Arguments {
rows: Option<Tagged<u64>>, rows: Option<Tagged<u64>>,
} }
#[async_trait]
impl WholeStreamCommand for Command { impl WholeStreamCommand for Command {
fn name(&self) -> &str { fn name(&self) -> &str {
"drop" "drop"
@ -29,8 +28,8 @@ impl WholeStreamCommand for Command {
"Remove the last number of rows or columns." "Remove the last number of rows or columns."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
drop(args).await drop(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -52,9 +51,9 @@ impl WholeStreamCommand for Command {
} }
} }
async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> { fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (Arguments { rows }, input) = args.process().await?; let (Arguments { rows }, input) = args.process()?;
let v: Vec<_> = input.into_vec().await; let v: Vec<_> = input.into_vec();
let rows_to_drop = if let Some(quantity) = rows { let rows_to_drop = if let Some(quantity) = rows {
*quantity as usize *quantity as usize
@ -63,7 +62,7 @@ async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
}; };
Ok(if rows_to_drop == 0 { Ok(if rows_to_drop == 0 {
futures::stream::iter(v).to_output_stream() v.into_iter().to_action_stream()
} else { } else {
let k = if v.len() < rows_to_drop { let k = if v.len() < rows_to_drop {
0 0
@ -73,6 +72,6 @@ async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
let iter = v.into_iter().take(k); 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>>, min_size: Option<Tagged<u64>>,
} }
#[async_trait]
impl WholeStreamCommand for Du { impl WholeStreamCommand for Du {
fn name(&self) -> &str { fn name(&self) -> &str {
NAME NAME
@ -71,8 +70,8 @@ impl WholeStreamCommand for Du {
"Find disk usage sizes of specified items." "Find disk usage sizes of specified items."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
du(args).await du(args)
} }
fn examples(&self) -> Vec<Example> { 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 tag = args.call_info.name_tag.clone();
let ctrl_c = args.ctrl_c.clone(); let ctrl_c = args.ctrl_c.clone();
let ctrl_c_copy = ctrl_c.clone(); 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| { let exclude = args.exclude.map_or(Ok(None), move |x| {
Pattern::new(&x.item) Pattern::new(&x.item)
.map(Option::Some) .map(Option::Some)
@ -131,7 +130,7 @@ async fn du(args: CommandArgs) -> Result<OutputStream, ShellError> {
all, all,
}; };
let inp = futures::stream::iter(paths); let inp = paths;
Ok(inp Ok(inp
.flat_map(move |path| match path { .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()))); 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) .interruptible(ctrl_c_copy)
.to_output_stream()) .to_action_stream())
} }
fn glob_err_into(e: GlobError) -> ShellError { fn glob_err_into(e: GlobError) -> ShellError {

View File

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

View File

@ -2,9 +2,7 @@ use crate::commands::each::process_row;
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{hir::CapturedBlock, Signature, SyntaxShape, UntaggedValue, Value};
hir::CapturedBlock, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged; use nu_source::Tagged;
use serde::Deserialize; use serde::Deserialize;
@ -17,7 +15,6 @@ pub struct EachGroupArgs {
//numbered: Tagged<bool>, //numbered: Tagged<bool>,
} }
#[async_trait]
impl WholeStreamCommand for EachGroup { impl WholeStreamCommand for EachGroup {
fn name(&self) -> &str { fn name(&self) -> &str {
"each group" "each group"
@ -45,16 +42,54 @@ 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 context = Arc::new(EvaluationContext::from_args(&raw_args));
let (each_args, input): (EachGroupArgs, _) = raw_args.process().await?; let (each_args, input): (EachGroupArgs, _) = raw_args.process()?;
let block = Arc::new(Box::new(each_args.block)); let block = Arc::new(Box::new(each_args.block));
Ok(input let each_group_iterator = EachGroupIterator {
.chunks(each_args.group_size.item) block,
.then(move |input| run_block_on_vec(input, block.clone(), context.clone())) context,
.flatten() group_size: each_args.group_size.item,
.to_output_stream()) input,
};
Ok(each_group_iterator.flatten().to_action_stream())
}
}
struct EachGroupIterator {
block: Arc<Box<CapturedBlock>>,
context: Arc<EvaluationContext>,
group_size: usize,
input: InputStream,
}
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(),
))
} }
} }
@ -62,43 +97,32 @@ pub(crate) fn run_block_on_vec(
input: Vec<Value>, input: Vec<Value>,
block: Arc<Box<CapturedBlock>>, block: Arc<Box<CapturedBlock>>,
context: Arc<EvaluationContext>, context: Arc<EvaluationContext>,
) -> impl Future<Output = OutputStream> { ) -> OutputStream {
let value = Value { let value = Value {
value: UntaggedValue::Table(input), value: UntaggedValue::Table(input),
tag: Tag::unknown(), tag: Tag::unknown(),
}; };
async { match process_row(block, context, value) {
match process_row(block, context, value).await {
Ok(s) => { Ok(s) => {
// We need to handle this differently depending on whether process_row // We need to handle this differently depending on whether process_row
// returned just 1 value or if it returned multiple as a stream. // returned just 1 value or if it returned multiple as a stream.
let vec = s.collect::<Vec<_>>().await; let vec = s.collect::<Vec<_>>();
// If it returned just one value, just take that value // If it returned just one value, just take that value
if vec.len() == 1 { if vec.len() == 1 {
return OutputStream::one(vec.into_iter().next().expect( return OutputStream::one(
"This should be impossible, we just checked that vec.len() == 1.", 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 // If it returned multiple values, we need to put them into a table and
// return that. // return that.
let result = vec.into_iter().collect::<Result<Vec<ReturnSuccess>, _>>(); OutputStream::one(UntaggedValue::Table(vec).into_untagged_value())
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())))
}
Err(e) => OutputStream::one(Err(e)),
} }
Err(e) => OutputStream::one(Value::error(e)),
} }
} }

View File

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

View File

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

View File

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

View File

@ -17,7 +17,6 @@ pub struct EnterArgs {
encoding: Option<Tagged<String>>, encoding: Option<Tagged<String>>,
} }
#[async_trait]
impl WholeStreamCommand for Enter { impl WholeStreamCommand for Enter {
fn name(&self) -> &str { fn name(&self) -> &str {
"enter" "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"# documentation link at https://docs.rs/encoding_rs/0.8.28/encoding_rs/#statics"#
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
enter(args).await enter(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -76,38 +75,21 @@ documentation link at https://docs.rs/encoding_rs/0.8.28/encoding_rs/#statics"#
} }
} }
async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> { fn enter(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
let scope = raw_args.scope.clone(); let scope = raw_args.scope.clone();
let shell_manager = raw_args.shell_manager.clone(); let shell_manager = raw_args.shell_manager.clone();
let head = raw_args.call_info.args.head.clone(); let head = raw_args.call_info.args.head.clone();
let ctrl_c = raw_args.ctrl_c.clone(); let ctrl_c = raw_args.ctrl_c.clone();
let configs = raw_args.configs.clone();
let current_errors = raw_args.current_errors.clone(); let current_errors = raw_args.current_errors.clone();
let host = raw_args.host.clone(); let host = raw_args.host.clone();
let tag = raw_args.call_info.name_tag.clone(); let tag = raw_args.call_info.name_tag.clone();
let (EnterArgs { location, encoding }, _) = raw_args.process().await?; let (EnterArgs { location, encoding }, _) = raw_args.process()?;
let location_string = location.display().to_string(); let location_string = location.display().to_string();
let location_clone = location_string.clone();
if location_string.starts_with("help") { if location.is_dir() {
let spec = location_string.split(':').collect::<Vec<&str>>(); Ok(ActionStream::one(ReturnSuccess::action(
CommandAction::EnterShell(location_string),
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),
))) )))
} else { } else {
// If it's a file, attempt to open the file as a value and enter it // If it's a file, attempt to open the file as a value and enter it
@ -118,11 +100,10 @@ async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let (file_extension, tagged_contents) = crate::commands::open::fetch( let (file_extension, tagged_contents) = crate::commands::open::fetch(
&full_path, &full_path,
&PathBuf::from(location_clone), &PathBuf::from(location_string),
span, span,
encoding, encoding,
) )?;
.await?;
match tagged_contents.value { match tagged_contents.value {
UntaggedValue::Primitive(Primitive::String(_)) => { UntaggedValue::Primitive(Primitive::String(_)) => {
@ -132,6 +113,7 @@ async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let new_args = RawCommandArgs { let new_args = RawCommandArgs {
host, host,
ctrl_c, ctrl_c,
configs,
current_errors, current_errors,
shell_manager, shell_manager,
call_info: UnevaluatedCallInfo { call_info: UnevaluatedCallInfo {
@ -142,40 +124,38 @@ async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
span: Span::unknown(), span: Span::unknown(),
external_redirection: ExternalRedirection::Stdout, external_redirection: ExternalRedirection::Stdout,
}, },
name_tag: tag.clone(), name_tag: tag,
}, },
scope: scope.clone(), scope,
}; };
let tag = tagged_contents.tag.clone(); let tag = tagged_contents.tag.clone();
let mut result = converter let mut result =
.run(new_args.with_input(vec![tagged_contents])) converter.run(new_args.with_input(vec![tagged_contents]))?;
.await?; let result_vec: Vec<Value> = result.drain_vec();
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = Ok(result_vec
result.drain_vec().await; .into_iter()
Ok(futures::stream::iter(result_vec.into_iter().map( .map(move |res| {
move |res| match res { let Value { value, .. } = res;
Ok(ReturnSuccess::Value(Value { value, .. })) => Ok( Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
ReturnSuccess::Action(CommandAction::EnterValueShell(Value { Value {
value, value,
tag: tag.clone(), tag: tag.clone(),
})),
),
x => x,
}, },
)) )))
.to_output_stream()) })
.to_action_stream())
} else { } else {
Ok(OutputStream::one(ReturnSuccess::action( Ok(ActionStream::one(ReturnSuccess::action(
CommandAction::EnterValueShell(tagged_contents), CommandAction::EnterValueShell(tagged_contents),
))) )))
} }
} else { } else {
Ok(OutputStream::one(ReturnSuccess::action( Ok(ActionStream::one(ReturnSuccess::action(
CommandAction::EnterValueShell(tagged_contents), CommandAction::EnterValueShell(tagged_contents),
))) )))
} }
} }
_ => Ok(OutputStream::one(ReturnSuccess::action( _ => Ok(ActionStream::one(ReturnSuccess::action(
CommandAction::EnterValueShell(tagged_contents), CommandAction::EnterValueShell(tagged_contents),
))), ))),
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,10 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; use nu_protocol::{Signature, UntaggedValue};
pub struct From; pub struct From;
#[async_trait]
impl WholeStreamCommand for From { impl WholeStreamCommand for From {
fn name(&self) -> &str { fn name(&self) -> &str {
"from" "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)." "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> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(ReturnSuccess::value( Ok(OutputStream::one(
UntaggedValue::string(get_full_help(&From, &args.scope)).into_value(Tag::unknown()), 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; pub struct FromCsv;
#[derive(Deserialize)]
pub struct FromCsvArgs {
noheaders: bool,
separator: Option<Value>,
}
#[async_trait]
impl WholeStreamCommand for FromCsv { impl WholeStreamCommand for FromCsv {
fn name(&self) -> &str { fn name(&self) -> &str {
"from csv" "from csv"
@ -37,8 +30,8 @@ impl WholeStreamCommand for FromCsv {
"Parse text as .csv and create table." "Parse text as .csv and create table."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_csv(args).await from_csv(args)
} }
fn examples(&self) -> Vec<Example> { 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 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 { let sep = match separator {
Some(Value { Some(Value {
value: UntaggedValue::Primitive(Primitive::String(s)), 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)] #[cfg(test)]

View File

@ -45,7 +45,7 @@ fn from_delimited_string_to_value(
Ok(UntaggedValue::Table(rows).into_value(&tag)) Ok(UntaggedValue::Table(rows).into_value(&tag))
} }
pub async fn from_delimited_data( pub fn from_delimited_data(
noheaders: bool, noheaders: bool,
sep: char, sep: char,
format_name: &'static str, format_name: &'static str,
@ -53,7 +53,7 @@ pub async fn from_delimited_data(
name: Tag, name: Tag,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let name_tag = name; 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"); 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()) { 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 {
value: UntaggedValue::Table(list), value: UntaggedValue::Table(list),
.. ..
} => Ok(futures::stream::iter(list).to_output_stream()), } => Ok(list.into_iter().to_output_stream()),
x => Ok(OutputStream::one(x)), x => Ok(OutputStream::one(x)),
}, },
Err(err) => { Err(err) => {
@ -80,7 +80,7 @@ pub async fn from_delimited_data(
Err(ShellError::labeled_error_with_secondary( Err(ShellError::labeled_error_with_secondary(
line_one, line_one,
line_two, line_two,
name_tag.clone(), name_tag,
"value originates from here", "value originates from here",
concat_string.tag, concat_string.tag,
)) ))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,26 +1,14 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{Primitive, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
Primitive, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
};
use nu_source::Tagged; use nu_source::Tagged;
pub struct FromSsv; 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 STRING_REPRESENTATION: &str = "from ssv";
const DEFAULT_MINIMUM_SPACES: usize = 2; const DEFAULT_MINIMUM_SPACES: usize = 2;
#[async_trait]
impl WholeStreamCommand for FromSsv { impl WholeStreamCommand for FromSsv {
fn name(&self) -> &str { fn name(&self) -> &str {
STRING_REPRESENTATION 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." "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> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_ssv(args).await from_ssv(args)
} }
} }
@ -247,17 +235,15 @@ fn from_ssv_string_to_value(
UntaggedValue::Table(rows).into_value(&tag) 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 name = args.call_info.name_tag.clone();
let ( let args = args.evaluate_once()?;
FromSsvArgs {
noheaders, let noheaders = args.has_flag("noheaders");
aligned_columns, let aligned_columns = args.has_flag("aligned-columns");
minimum_spaces, let minimum_spaces: Option<Tagged<usize>> = args.get_flag("minimum-spaces")?;
},
input, let concat_string = args.input.collect_string(name.clone())?;
) = args.process().await?;
let concat_string = input.collect_string(name.clone()).await?;
let split_at = match minimum_spaces { let split_at = match minimum_spaces {
Some(number) => number.item, Some(number) => number.item,
None => DEFAULT_MINIMUM_SPACES, None => DEFAULT_MINIMUM_SPACES,
@ -269,15 +255,13 @@ async fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
noheaders, noheaders,
aligned_columns, aligned_columns,
split_at, split_at,
name.clone(), name,
) { ) {
Value { Value {
value: UntaggedValue::Table(list), value: UntaggedValue::Table(list),
.. ..
} => { } => list.into_iter().to_output_stream(),
futures::stream::iter(list.into_iter().map(ReturnSuccess::value)).to_output_stream() x => OutputStream::one(x),
}
x => OutputStream::one(ReturnSuccess::value(x)),
}, },
) )
} }

View File

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

View File

@ -6,12 +6,6 @@ use nu_protocol::Signature;
pub struct FromTsv; pub struct FromTsv;
#[derive(Deserialize)]
pub struct FromTsvArgs {
noheaders: bool,
}
#[async_trait]
impl WholeStreamCommand for FromTsv { impl WholeStreamCommand for FromTsv {
fn name(&self) -> &str { fn name(&self) -> &str {
"from tsv" "from tsv"
@ -29,16 +23,18 @@ impl WholeStreamCommand for FromTsv {
"Parse text as .tsv and create table." "Parse text as .tsv and create table."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_tsv(args).await 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 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)] #[cfg(test)]

View File

@ -1,11 +1,10 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue}; use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
pub struct FromUrl; pub struct FromUrl;
#[async_trait]
impl WholeStreamCommand for FromUrl { impl WholeStreamCommand for FromUrl {
fn name(&self) -> &str { fn name(&self) -> &str {
"from url" "from url"
@ -19,17 +18,17 @@ impl WholeStreamCommand for FromUrl {
"Parse url-encoded string as a table." "Parse url-encoded string as a table."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_url(args).await from_url(args)
} }
} }
async fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> { fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?; let args = args.evaluate_once()?;
let tag = args.name_tag(); let tag = args.name_tag();
let input = args.input; 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); 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)); 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( _ => Err(ShellError::labeled_error_with_secondary(
"String not compatible with url-encoding", "String not compatible with url-encoding",

View File

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

View File

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

View File

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

View File

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

View File

@ -18,7 +18,6 @@ pub struct Arguments {
rest: Vec<Value>, rest: Vec<Value>,
} }
#[async_trait]
impl WholeStreamCommand for Command { impl WholeStreamCommand for Command {
fn name(&self) -> &str { fn name(&self) -> &str {
"get" "get"
@ -35,8 +34,8 @@ impl WholeStreamCommand for Command {
"Open given cells as text." "Open given cells as text."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
get(args).await get(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -55,29 +54,31 @@ impl WholeStreamCommand for Command {
} }
} }
pub async fn get(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn get(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (Arguments { mut rest }, mut input) = args.process().await?; let (Arguments { mut rest }, mut input) = args.process()?;
let (column_paths, _) = arguments(&mut rest)?; let (column_paths, _) = arguments(&mut rest)?;
if column_paths.is_empty() { if column_paths.is_empty() {
let vec = input.drain_vec().await; let vec = input.drain_vec();
let descs = nu_protocol::merge_descriptors(&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 { } else {
trace!("get {:?}", column_paths); trace!("get {:?}", column_paths);
let output_stream = input let output_stream = input
.map(move |item| { .map(move |item| {
let output = column_paths column_paths
.iter() .iter()
.map(move |path| get_output(&item, path)) .map(move |path| get_output(&item, path))
.flatten() .flatten()
.collect::<Vec<_>>(); .collect::<Vec<_>>()
futures::stream::iter(output)
}) })
.flatten() .flatten()
.to_output_stream(); .to_action_stream();
Ok(output_stream) Ok(output_stream)
} }
} }

View File

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

View File

@ -13,7 +13,6 @@ pub struct GroupByDateArgs {
format: Option<Tagged<String>>, format: Option<Tagged<String>>,
} }
#[async_trait]
impl WholeStreamCommand for GroupByDate { impl WholeStreamCommand for GroupByDate {
fn name(&self) -> &str { fn name(&self) -> &str {
"group-by date" "group-by date"
@ -38,8 +37,8 @@ impl WholeStreamCommand for GroupByDate {
"creates a table grouped by date." "creates a table grouped by date."
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
group_by_date(args).await group_by_date(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -59,7 +58,7 @@ enum GroupByColumn {
Name(Option<Tagged<String>>), 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 name = args.call_info.name_tag.clone();
let ( let (
GroupByDateArgs { GroupByDateArgs {
@ -67,8 +66,8 @@ pub async fn group_by_date(args: CommandArgs) -> Result<OutputStream, ShellError
format, format,
}, },
input, input,
) = args.process().await?; ) = args.process()?;
let values: Vec<Value> = input.collect().await; let values: Vec<Value> = input.collect();
if values.is_empty() { if values.is_empty() {
Err(ShellError::labeled_error( 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; pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"hash base64" "hash base64"
@ -65,8 +64,8 @@ impl WholeStreamCommand for SubCommand {
"base64 encode or decode a value" "base64 encode or decode a value"
} }
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
operate(args).await operate(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -96,8 +95,8 @@ impl WholeStreamCommand for SubCommand {
} }
} }
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> { fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name_tag = &args.call_info.name_tag.clone(); let name_tag = args.call_info.name_tag.clone();
let ( let (
Arguments { Arguments {
@ -107,10 +106,10 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
rest, rest,
}, },
input, input,
) = args.process().await?; ) = args.process()?;
if encode.item && decode.item { 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", "only one of --decode and --encode flags can be used",
"conflicting flags", "conflicting flags",
name_tag, name_tag,
@ -155,7 +154,7 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
ReturnSuccess::value(ret) ReturnSuccess::value(ret)
} }
}) })
.to_output_stream()) .to_action_stream())
} }
fn action( fn action(

View File

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

View File

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

View File

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

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