Compare commits

...

91 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
4faaa5310e Bump to 0.29 (#3230)
* Bump to 0.29

* fix test
2021-03-30 22:35:21 +13:00
c448abd44e echo $scope.aliases | pivot to see all of your aliases (#3203)
* enable ability to see all aliases, pull in code from scope branch

* add in the alias tests

* add back in my changes to variables.rs after merging in andrasios changes from PR #3217
2021-03-29 21:27:51 +13:00
2517588d7d update date to-timezone usage (#3223) 2021-03-28 18:41:42 -05:00
8fc8fc89aa History, more test coverage improvements, and refactorings. (#3217)
Improvements overall to Nu. Also among the changes here, we can also be more confident towards incorporating `3041`. End to end tests for checking envs properly exported to externals is not added here (since it's in the other PR)

A few things added in this PR (probably forgetting some too)

* no writes happen to history during test runs.
* environment syncing end to end coverage added.
* clean up / refactorings few areas.
* testing API for finer control (can write tests passing more than one pipeline)
* can pass environment variables in tests that nu will inherit when running.

* No longer needed.

* no longer under a module. No need to use super.
2021-03-27 00:08:03 -05:00
b243b3ee1d fixed typo in help text (#3216) 2021-03-26 08:11:41 -05:00
28e08afada add ability to cd to ~/blah (#3210)
* add ability to cd to ~/blah. tested on windows.

* added dirs_next

* put change behind feature for linux-minimal/wasm

* clippy

* holy crap minimal, i'm about done with you!
2021-03-26 22:29:02 +13:00
7e184b58b2 Fix warnings for Rust 1.51 (#3214)
* Fix warnings for Rust 1.51

* More fixes

* More fixes
2021-03-26 21:26:57 +13:00
589fc0b8ad add support for timestamp-based time conversion by specifying timezone (#3207)
* 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
2021-03-24 08:08:23 -05:00
f0c7c1b500 fix: prompt does not find external commands #3134 (#3189)
the initial setup-commands of nushell were executed without loading environment variables
this resulted in the PATH not being available at this point until an external command was run once
which resulted in env_vars being added
let run_result = run_block(&prompt_block, &context, InputStream::empty()).await;

Co-authored-by: alexhk <alexhk@protonmail.com>
2021-03-23 22:38:07 +13:00
840bd98e01 support forward slash for directory completion in Windows (#3201)
While the "main" separator in Windows is the backslash, it supports the
forward slash as a separator too.
Add support for this so that the behavior is similar to the way Windows
PowerShell handles the forward slash: it is recognized as a separator,
and when using <tab> for path completion the slash is reversed.
2021-03-23 16:20:01 +13:00
a5cdd22bfe Add basic support for md5 hashing strings and binary data (#3197) 2021-03-21 07:48:53 +13:00
0c7bcae9b1 Update contributor-book url (#3198) 2021-03-20 23:09:17 +13:00
ab666c170c added the ability to create multi-byte unicode chars like emoji (#3195) 2021-03-18 13:42:39 -05:00
d2213d18fa Playground infraestructure (tests, etc) additions. (#3179)
* Playground infraestructure (tests, etc) additions.

A few things to note:

* Nu can be started with a custom configuration file (`nu --config-file /path/to/sample_config.toml`). Useful for mocking the configuration on test runs.
* When given a custom configuration file Nu will save any changes to the file supplied appropiately.
* The `$nu.config-path` variable either shows the default configuration file (or the custom one, if given)
* We can now run end to end tests with finer grained control (currently, since this is baseline work, standard out) This will allow to check things like exit status, assert the contents with a format, etc)

* Remove (for another PR)
2021-03-15 02:26:30 -05:00
82b6300dcb fix: cargo test failed with --release (#3183) (#3184)
Signed-off-by: nibon7 <nibon7@163.com>
2021-03-15 16:59:04 +13:00
b69cda9e07 Add --signal option to kill command (#3077) (#3079) 2021-03-15 13:10:52 +13:00
56adc7c3c6 imp: bump rustyline to 8.0.0 (#3167)
* imp: bump rustyline to 8.0.0

* fix: rustyline 8 keybindings

* fix: commands count/length test

Co-authored-by: alexhk <alexhk@protonmail.com>
2021-03-14 15:13:31 +13:00
2ace20fade Make opening a directory list its contents (#3118)
* Make opening a directory enter it.

Not sure if this change is wanted, but I'm not sure what else opening a directory could mean.
And I find myself accidentally using `open <dir>` to mean `enter <dir>`

* Add example to open directory

* Open dir should list it's contents

* Update example description and fix style
2021-03-14 10:47:31 +13:00
c13fe83784 Rename count to length (#3166)
* update docs to refer to length instead of count

* rename count to length

* change all occurrences of 'count' to 'length' in tests

* format length command
2021-03-14 10:46:40 +13:00
6cf8df8685 Move script to nu engine (#3092)
* Move run_script to engine

* Add which dep and feature to engine

* Change unwrap to expect

* Add wasm specification

* Remove which from default, add specification correctly

* Add nu-platform-specifics

* Move is_external_cmd to platform_specifics

* Add is_external_cmd to host and use it instead of nu_platform directly

* Clean up if else logic in is_external_cmd

* Bump nu-platform-specifics version

* Pass context to print_err

* Commit cargo.lock

* Move print functions to own module inside nu-engine

* Hypocratic change to run windows-nightly again

* Add import for Ordering

* Move printing of error to host

* Move platform specific which functionality to basic host

* Allow no use of cmd_name

* Fix windows compile issue
2021-03-12 18:20:54 +13:00
86a89404be fix: unicode byte counting error #3150 (#3159)
Co-authored-by: hk <alexhaka10@protonmail.com>
2021-03-12 07:11:07 +13:00
0d305d7c3e Lines no longer treats a text buffer as a line (#3153) 2021-03-11 11:35:15 +13:00
ee5bd2b4b3 move from h1-client-rustls to hyper-client (#3154) 2021-03-10 15:45:53 -06:00
481 changed files with 12006 additions and 9112 deletions

View File

@ -2,7 +2,7 @@
Welcome to nushell!
*Note: for a more complete guide see [The nu contributor book](https://github.com/nushell/contributor-book)*
*Note: for a more complete guide see [The nu contributor book](https://www.nushell.sh/contributor-book/)*
For speedy contributions open it in Gitpod, nu will be pre-installed with the latest build in a VSCode like editor all from your browser.

1263
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -15,7 +15,7 @@ fn main() {
let g = (col * 255 / WIDTH) as u8;
let b = 128;
print!("{}", Style::default().on(Color::RGB(r, g, b)).paint(" "));
print!("{}", Style::default().on(Color::Rgb(r, g, b)).paint(" "));
}
println!();

View File

@ -1,3 +1,4 @@
#![allow(missing_docs)]
use crate::style::{Color, Style};
use crate::write::AnyWrite;
use std::fmt;
@ -103,7 +104,7 @@ impl Color {
Color::Cyan => write!(f, "36"),
Color::White => write!(f, "37"),
Color::Fixed(num) => write!(f, "38;5;{}", &num),
Color::RGB(r, g, b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
Color::DarkGray => write!(f, "90"),
Color::LightRed => write!(f, "91"),
Color::LightGreen => write!(f, "92"),
@ -128,7 +129,7 @@ impl Color {
Color::Cyan => write!(f, "46"),
Color::White => write!(f, "47"),
Color::Fixed(num) => write!(f, "48;5;{}", &num),
Color::RGB(r, g, b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
Color::DarkGray => write!(f, "100"),
Color::LightRed => write!(f, "101"),
Color::LightGreen => write!(f, "102"),
@ -372,10 +373,10 @@ mod test {
test!(fixed: Fixed(100); "hi" => "\x1B[38;5;100mhi\x1B[0m");
test!(fixed_on_purple: Fixed(100).on(Purple); "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
test!(fixed_on_fixed: Fixed(100).on(Fixed(200)); "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
test!(rgb: RGB(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
test!(rgb_on_blue: RGB(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
test!(blue_on_rgb: Blue.on(RGB(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
test!(rgb_on_rgb: RGB(70,130,180).on(RGB(5,10,15)); "hi" => "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m");
test!(rgb: Rgb(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
test!(rgb_on_blue: Rgb(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
test!(blue_on_rgb: Blue.on(Rgb(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
test!(rgb_on_rgb: Rgb(70,130,180).on(Rgb(5,10,15)); "hi" => "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m");
test!(bold: Style::new().bold(); "hi" => "\x1B[1mhi\x1B[0m");
test!(underline: Style::new().underline(); "hi" => "\x1B[4mhi\x1B[0m");
test!(bunderline: Style::new().bold().underline(); "hi" => "\x1B[1;4mhi\x1B[0m");

View File

@ -112,7 +112,7 @@ mod test {
test!(both: style().bold().italic() => "Style { bold, italic }");
test!(red: Red.normal() => "Style { fg(Red) }");
test!(redblue: Red.normal().on(RGB(3, 2, 4)) => "Style { fg(Red), on(RGB(3, 2, 4)) }");
test!(redblue: Red.normal().on(Rgb(3, 2, 4)) => "Style { fg(Red), on(Rgb(3, 2, 4)) }");
test!(everything:
Red.on(Blue).blink().bold().dimmed().hidden().italic().reverse().strikethrough().underline() =>

View File

@ -11,7 +11,7 @@ use std::ops::Deref;
/// display that string. `ANSIString` and `ANSIByteString` are aliases for
/// this type on `str` and `\[u8]`, respectively.
#[derive(PartialEq, Debug)]
pub struct ANSIGenericString<'a, S: 'a + ToOwned + ?Sized>
pub struct AnsiGenericString<'a, S: 'a + ToOwned + ?Sized>
where
<S as ToOwned>::Owned: fmt::Debug,
{
@ -30,12 +30,12 @@ where
/// let clone_string = plain_string.clone();
/// assert_eq!(clone_string, plain_string);
/// ```
impl<'a, S: 'a + ToOwned + ?Sized> Clone for ANSIGenericString<'a, S>
impl<'a, S: 'a + ToOwned + ?Sized> Clone for AnsiGenericString<'a, S>
where
<S as ToOwned>::Owned: fmt::Debug,
{
fn clone(&self) -> ANSIGenericString<'a, S> {
ANSIGenericString {
fn clone(&self) -> AnsiGenericString<'a, S> {
AnsiGenericString {
style: self.style,
string: self.string.clone(),
}
@ -85,26 +85,26 @@ where
/// let plain_string = ANSIString::from("a plain string");
/// assert_eq!(&*plain_string, "a plain string");
/// ```
pub type ANSIString<'a> = ANSIGenericString<'a, str>;
pub type AnsiString<'a> = AnsiGenericString<'a, str>;
/// An `ANSIByteString` represents a formatted series of bytes. Use
/// `ANSIByteString` when styling text with an unknown encoding.
pub type ANSIByteString<'a> = ANSIGenericString<'a, [u8]>;
/// An `AnsiByteString` represents a formatted series of bytes. Use
/// `AnsiByteString` when styling text with an unknown encoding.
pub type AnsiByteString<'a> = AnsiGenericString<'a, [u8]>;
impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for ANSIGenericString<'a, S>
impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for AnsiGenericString<'a, S>
where
I: Into<Cow<'a, S>>,
<S as ToOwned>::Owned: fmt::Debug,
{
fn from(input: I) -> ANSIGenericString<'a, S> {
ANSIGenericString {
fn from(input: I) -> AnsiGenericString<'a, S> {
AnsiGenericString {
string: input.into(),
style: Style::default(),
}
}
}
impl<'a, S: 'a + ToOwned + ?Sized> ANSIGenericString<'a, S>
impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S>
where
<S as ToOwned>::Owned: fmt::Debug,
{
@ -119,7 +119,7 @@ where
}
}
impl<'a, S: 'a + ToOwned + ?Sized> Deref for ANSIGenericString<'a, S>
impl<'a, S: 'a + ToOwned + ?Sized> Deref for AnsiGenericString<'a, S>
where
<S as ToOwned>::Owned: fmt::Debug,
{
@ -130,32 +130,32 @@ where
}
}
/// A set of `ANSIGenericString`s collected together, in order to be
/// A set of `AnsiGenericStrings`s collected together, in order to be
/// written with a minimum of control characters.
#[derive(Debug, PartialEq)]
pub struct ANSIGenericStrings<'a, S: 'a + ToOwned + ?Sized>(pub &'a [ANSIGenericString<'a, S>])
pub struct AnsiGenericStrings<'a, S: 'a + ToOwned + ?Sized>(pub &'a [AnsiGenericString<'a, S>])
where
<S as ToOwned>::Owned: fmt::Debug,
S: PartialEq;
/// A set of `ANSIString`s collected together, in order to be written with a
/// A set of `AnsiString`s collected together, in order to be written with a
/// minimum of control characters.
pub type ANSIStrings<'a> = ANSIGenericStrings<'a, str>;
pub type AnsiStrings<'a> = AnsiGenericStrings<'a, str>;
/// A function to construct an `ANSIStrings` instance.
/// A function to construct an `AnsiStrings` instance.
#[allow(non_snake_case)]
pub fn ANSIStrings<'a>(arg: &'a [ANSIString<'a>]) -> ANSIStrings<'a> {
ANSIGenericStrings(arg)
pub fn AnsiStrings<'a>(arg: &'a [AnsiString<'a>]) -> AnsiStrings<'a> {
AnsiGenericStrings(arg)
}
/// A set of `ANSIByteString`s collected together, in order to be
/// A set of `AnsiByteString`s collected together, in order to be
/// written with a minimum of control characters.
pub type ANSIByteStrings<'a> = ANSIGenericStrings<'a, [u8]>;
pub type AnsiByteStrings<'a> = AnsiGenericStrings<'a, [u8]>;
/// A function to construct an `ANSIByteStrings` instance.
#[allow(non_snake_case)]
pub fn ANSIByteStrings<'a>(arg: &'a [ANSIByteString<'a>]) -> ANSIByteStrings<'a> {
ANSIGenericStrings(arg)
pub fn ANSIByteStrings<'a>(arg: &'a [AnsiByteString<'a>]) -> AnsiByteStrings<'a> {
AnsiGenericStrings(arg)
}
// ---- paint functions ----
@ -163,12 +163,12 @@ pub fn ANSIByteStrings<'a>(arg: &'a [ANSIByteString<'a>]) -> ANSIByteStrings<'a>
impl Style {
/// Paints the given text with this color, returning an ANSI string.
#[must_use]
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> ANSIGenericString<'a, S>
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S>
where
I: Into<Cow<'a, S>>,
<S as ToOwned>::Owned: fmt::Debug,
{
ANSIGenericString {
AnsiGenericString {
string: input.into(),
style: self,
}
@ -185,12 +185,12 @@ impl Color {
/// println!("{}", Blue.paint("da ba dee"));
/// ```
#[must_use]
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> ANSIGenericString<'a, S>
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S>
where
I: Into<Cow<'a, S>>,
<S as ToOwned>::Owned: fmt::Debug,
{
ANSIGenericString {
AnsiGenericString {
string: input.into(),
style: self.normal(),
}
@ -199,14 +199,14 @@ impl Color {
// ---- writers for individual ANSI strings ----
impl<'a> fmt::Display for ANSIString<'a> {
impl<'a> fmt::Display for AnsiString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let w: &mut dyn fmt::Write = f;
self.write_to_any(w)
}
}
impl<'a> ANSIByteString<'a> {
impl<'a> AnsiByteString<'a> {
/// Write an `ANSIByteString` to an `io::Write`. This writes the escape
/// sequences for the associated `Style` around the bytes.
pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
@ -215,7 +215,7 @@ impl<'a> ANSIByteString<'a> {
}
}
impl<'a, S: 'a + ToOwned + ?Sized> ANSIGenericString<'a, S>
impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S>
where
<S as ToOwned>::Owned: fmt::Debug,
&'a S: AsRef<[u8]>,
@ -229,14 +229,14 @@ where
// ---- writers for combined ANSI strings ----
impl<'a> fmt::Display for ANSIStrings<'a> {
impl<'a> fmt::Display for AnsiStrings<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let f: &mut dyn fmt::Write = f;
self.write_to_any(f)
}
}
impl<'a> ANSIByteStrings<'a> {
impl<'a> AnsiByteStrings<'a> {
/// Write `ANSIByteStrings` to an `io::Write`. This writes the minimal
/// escape sequences for the associated `Style`s around each set of
/// bytes.
@ -246,7 +246,7 @@ impl<'a> ANSIByteStrings<'a> {
}
}
impl<'a, S: 'a + ToOwned + ?Sized + PartialEq> ANSIGenericStrings<'a, S>
impl<'a, S: 'a + ToOwned + ?Sized + PartialEq> AnsiGenericStrings<'a, S>
where
<S as ToOwned>::Owned: fmt::Debug,
&'a S: AsRef<[u8]>,
@ -289,7 +289,7 @@ where
#[cfg(test)]
mod tests {
pub use super::super::ANSIStrings;
pub use super::super::AnsiStrings;
pub use crate::style::Color::*;
pub use crate::style::Style;
@ -297,7 +297,7 @@ mod tests {
fn no_control_codes_for_plain() {
let one = Style::default().paint("one");
let two = Style::default().paint("two");
let output = format!("{}", ANSIStrings(&[one, two]));
let output = format!("{}", AnsiStrings(&[one, two]));
assert_eq!(&*output, "onetwo");
}
}

View File

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

View File

@ -365,7 +365,7 @@ pub enum Color {
Fixed(u8),
/// A 24-bit RGB color, as specified by ISO-8613-3.
RGB(u8, u8, u8),
Rgb(u8, u8, u8),
}
impl Color {

View File

@ -1,12 +1,12 @@
use crate::display::{ANSIString, ANSIStrings};
use crate::display::{AnsiString, AnsiStrings};
use std::ops::Deref;
/// Return a substring of the given ANSIStrings sequence, while keeping the formatting.
pub fn sub_string<'a>(
start: usize,
len: usize,
strs: &ANSIStrings<'a>,
) -> Vec<ANSIString<'static>> {
strs: &AnsiStrings<'a>,
) -> Vec<AnsiString<'static>> {
let mut vec = Vec::new();
let mut pos = start;
let mut len_rem = len;
@ -39,7 +39,7 @@ pub fn sub_string<'a>(
}
/// Return a concatenated copy of `strs` without the formatting, as an allocated `String`.
pub fn unstyle(strs: &ANSIStrings) -> String {
pub fn unstyle(strs: &AnsiStrings) -> String {
let mut s = String::new();
for i in strs.0.iter() {
@ -50,7 +50,7 @@ pub fn unstyle(strs: &ANSIStrings) -> String {
}
/// Return the unstyled length of ANSIStrings. This is equaivalent to `unstyle(strs).len()`.
pub fn unstyled_len(strs: &ANSIStrings) -> usize {
pub fn unstyled_len(strs: &AnsiStrings) -> usize {
let mut l = 0;
for i in strs.0.iter() {
l += i.deref().len();
@ -70,7 +70,7 @@ mod test {
Red.paint("-second"),
White.paint("-third"),
];
let a = ANSIStrings(&l);
let a = AnsiStrings(&l);
assert_eq!(unstyle(&a), "first-second-third");
assert_eq!(unstyled_len(&a), 18);

View File

@ -5,26 +5,26 @@ description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-cli"
version = "0.28.0"
version = "0.30.0"
[lib]
doctest = false
[dependencies]
nu-command = { version = "0.28.0", path = "../nu-command" }
nu-data = { version = "0.28.0", path = "../nu-data" }
nu-engine = { version = "0.28.0", path = "../nu-engine" }
nu-errors = { version = "0.28.0", path = "../nu-errors" }
nu-json = { version = "0.28.0", path = "../nu-json" }
nu-parser = { version = "0.28.0", path = "../nu-parser" }
nu-plugin = { version = "0.28.0", path = "../nu-plugin" }
nu-protocol = { version = "0.28.0", path = "../nu-protocol" }
nu-source = { version = "0.28.0", path = "../nu-source" }
nu-stream = { version = "0.28.0", path = "../nu-stream" }
nu-table = { version = "0.28.0", path = "../nu-table" }
nu-test-support = { version = "0.28.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.28.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.28.0", path = "../nu-ansi-term" }
nu-command = { version = "0.30.0", path = "../nu-command" }
nu-data = { version = "0.30.0", path = "../nu-data" }
nu-engine = { version = "0.30.0", path = "../nu-engine" }
nu-errors = { version = "0.30.0", path = "../nu-errors" }
nu-json = { version = "0.30.0", path = "../nu-json" }
nu-parser = { version = "0.30.0", path = "../nu-parser" }
nu-plugin = { version = "0.30.0", path = "../nu-plugin" }
nu-protocol = { version = "0.30.0", path = "../nu-protocol" }
nu-source = { version = "0.30.0", path = "../nu-source" }
nu-stream = { version = "0.30.0", path = "../nu-stream" }
nu-table = { version = "0.30.0", path = "../nu-table" }
nu-test-support = { version = "0.30.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.30.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.30.0", path = "../nu-ansi-term" }
Inflector = "0.11"
arboard = { version = "1.1.0", optional = true }
@ -77,7 +77,7 @@ rayon = "1.5.0"
regex = "1.4.3"
roxmltree = "0.14.0"
rust-embed = "5.9.0"
rustyline = { version = "6.3.0", optional = true }
rustyline = { version = "8.0.0", optional = true }
serde = { version = "1.0.123", features = ["derive"] }
serde_bytes = "0.11.5"
serde_ini = "0.2.0"

View File

@ -1,12 +1,9 @@
use crate::line_editor::configure_ctrl_c;
use nu_command::commands::default_context::create_default_context;
#[allow(unused_imports)]
use nu_command::maybe_print_errors;
use nu_engine::run_block;
use nu_engine::EvaluationContext;
use nu_ansi_term::Color;
use nu_engine::{maybe_print_errors, run_block, script::run_script_standalone, EvaluationContext};
#[allow(unused_imports)]
pub(crate) use nu_command::script::{process_script, LineResult};
pub(crate) use nu_engine::script::{process_script, LineResult};
#[cfg(feature = "rustyline-support")]
use crate::line_editor::{
@ -18,24 +15,85 @@ use crate::line_editor::{
use nu_data::config;
use nu_source::{Tag, Text};
use nu_stream::InputStream;
use std::ffi::{OsStr, OsString};
#[allow(unused_imports)]
use std::sync::atomic::Ordering;
use nu_command::script::{print_err, run_script_standalone};
#[cfg(feature = "rustyline-support")]
use rustyline::{self, error::ReadlineError};
use crate::EnvironmentSyncer;
use nu_errors::ShellError;
use nu_parser::ParserScope;
use nu_protocol::{hir::ExternalRedirection, UntaggedValue, Value};
use nu_protocol::{hir::ExternalRedirection, ConfigPath, UntaggedValue, Value};
use log::trace;
use std::error::Error;
use std::iter::Iterator;
use std::path::PathBuf;
pub struct Options {
pub config: Option<OsString>,
pub stdin: bool,
pub scripts: Vec<NuScript>,
pub save_history: bool,
}
impl Default for Options {
fn default() -> Self {
Self::new()
}
}
impl Options {
pub fn new() -> Self {
Self {
config: None,
stdin: false,
scripts: vec![],
save_history: true,
}
}
}
pub struct NuScript {
pub filepath: Option<OsString>,
pub contents: String,
}
impl NuScript {
pub fn code<'a>(content: impl Iterator<Item = &'a str>) -> Result<Self, ShellError> {
let text = content
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join("\n");
Ok(Self {
filepath: None,
contents: text,
})
}
pub fn get_code(&self) -> &str {
&self.contents
}
pub fn source_file(path: &OsStr) -> Result<Self, ShellError> {
use std::fs::File;
use std::io::Read;
let path = path.to_os_string();
let mut file = File::open(&path)?;
let mut buffer = String::new();
file.read_to_string(&mut buffer)?;
Ok(Self {
filepath: Some(path),
contents: buffer,
})
}
}
pub fn search_paths() -> Vec<std::path::PathBuf> {
use std::env;
@ -65,62 +123,38 @@ pub fn search_paths() -> Vec<std::path::PathBuf> {
search_paths
}
pub async fn run_script_file(
file_contents: String,
redirect_stdin: bool,
) -> Result<(), Box<dyn Error>> {
let mut syncer = EnvironmentSyncer::new();
let mut context = create_default_context(false)?;
let config = syncer.get_config();
pub fn run_script_file(context: EvaluationContext, options: Options) -> Result<(), Box<dyn Error>> {
if let Some(cfg) = options.config {
load_cfg_as_global_cfg(&context, PathBuf::from(cfg));
} else {
load_global_cfg(&context);
}
context.configure(&config, |_, ctx| {
syncer.load_environment();
syncer.sync_env_vars(ctx);
syncer.sync_path_vars(ctx);
let _ = register_plugins(&context);
let _ = configure_ctrl_c(&context);
if let Err(reason) = syncer.autoenv(ctx) {
print_err(reason, &Text::from(""), ctx);
}
let script = options
.scripts
.get(0)
.ok_or_else(|| ShellError::unexpected("Nu source code not available"))?;
let _ = register_plugins(ctx);
let _ = configure_ctrl_c(ctx);
});
let _ = run_startup_commands(&mut context, &config).await;
run_script_standalone(file_contents, redirect_stdin, &context, true).await?;
run_script_standalone(script.get_code().to_string(), options.stdin, &context, true)?;
Ok(())
}
/// The entry point for the CLI. Will register all known internal commands, load experimental commands, load plugins, then prepare the prompt and line reader for input.
#[cfg(feature = "rustyline-support")]
pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
let mut syncer = EnvironmentSyncer::new();
let configuration = syncer.get_config();
pub fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn Error>> {
let _ = configure_ctrl_c(&context);
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) {
print_err(reason, &Text::from(""), ctx);
}
let _ = configure_ctrl_c(ctx);
let _ = configure_rustyline_editor(&mut rl, config);
let helper = Some(nu_line_editor_helper(ctx, config));
rl.set_helper(helper);
});
// start time for command duration
// start time for running startup scripts (this metric includes loading of the cfg, but w/e)
let startup_commands_start_time = std::time::Instant::now();
// run the startup commands
let _ = run_startup_commands(&mut context, &configuration).await;
if let Some(cfg) = options.config {
load_cfg_as_global_cfg(&context, PathBuf::from(cfg));
} else {
load_global_cfg(&context);
}
// Store cmd duration in an env var
context.scope.add_env_var(
"CMD_DURATION",
@ -131,19 +165,43 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
startup_commands_start_time.elapsed()
);
//Configure rustyline
let mut rl = default_rustyline_editor_configuration();
let history_path = if let Some(cfg) = &context.configs.lock().global_config {
let _ = configure_rustyline_editor(&mut rl, cfg);
let helper = Some(nu_line_editor_helper(&context, cfg));
rl.set_helper(helper);
nu_data::config::path::history_path_or_default(cfg)
} else {
nu_data::config::path::default_history_path()
};
// Don't load history if it's not necessary
if options.save_history {
let _ = rl.load_history(&history_path);
}
//set vars from cfg if present
let (skip_welcome_message, prompt) = if let Some(cfg) = &context.configs.lock().global_config {
(
cfg.var("skip_welcome_message")
.map(|x| x.is_true())
.unwrap_or(false),
cfg.var("prompt"),
)
} else {
(false, None)
};
//Check whether dir we start in contains local cfg file and if so load it.
load_local_cfg_if_present(&context);
// Give ourselves a scope to work in
context.scope.enter_scope();
let history_path = nu_engine::history_path(&configuration);
let _ = rl.load_history(&history_path);
let mut session_text = String::new();
let mut line_start: usize = 0;
let skip_welcome_message = configuration
.var("skip_welcome_message")
.map(|x| x.is_true())
.unwrap_or(false);
if !skip_welcome_message {
println!(
"Welcome to Nushell {} (type 'help' for more info)",
@ -167,26 +225,36 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
let cwd = context.shell_manager.path();
let colored_prompt = {
if let Some(prompt) = configuration.var("prompt") {
if let Some(prompt) = &prompt {
let prompt_line = prompt.as_string()?;
context.scope.enter_scope();
let (mut prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
prompt_block.set_redirect(ExternalRedirection::Stdout);
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() {
context.scope.exit_scope();
format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch())
format!(
"{}{}{}{}{}{}> ",
Color::Green.bold().prefix().to_string(),
cwd,
nu_ansi_term::ansi::RESET,
Color::Cyan.bold().prefix().to_string(),
current_branch(),
nu_ansi_term::ansi::RESET
)
} else {
// let env = context.get_env();
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();
match run_result {
Ok(result) => match result.collect_string(Tag::unknown()).await {
Ok(result) => match result.collect_string(Tag::unknown()) {
Ok(string_result) => {
let errors = context.get_errors();
maybe_print_errors(&context, Text::from(prompt_line));
@ -199,14 +267,14 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
}
}
Err(e) => {
crate::cli::print_err(e, &Text::from(prompt_line), &context);
context.host.lock().print_err(e, &Text::from(prompt_line));
context.clear_errors();
"> ".to_string()
}
},
Err(e) => {
crate::cli::print_err(e, &Text::from(prompt_line), &context);
context.host.lock().print_err(e, &Text::from(prompt_line));
context.clear_errors();
"> ".to_string()
@ -214,7 +282,15 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
}
}
} 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
)
}
};
@ -244,16 +320,13 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
let cmd_start_time = std::time::Instant::now();
let line = match convert_rustyline_result_to_string(readline) {
LineResult::Success(_) => {
process_script(
&session_text[line_start..],
&context,
false,
line_start,
true,
)
.await
}
LineResult::Success(_) => process_script(
&session_text[line_start..],
&context,
false,
line_start,
true,
),
x => x,
};
@ -262,49 +335,50 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
.scope
.add_env_var("CMD_DURATION", format!("{:?}", cmd_start_time.elapsed()));
// Check the config to see if we need to update the path
// TODO: make sure config is cached so we don't path this load every call
// FIXME: we probably want to be a bit more graceful if we can't set the environment
context.configure(&configuration, |config, ctx| {
if syncer.did_config_change() {
syncer.reload();
syncer.sync_env_vars(ctx);
syncer.sync_path_vars(ctx);
}
if let Err(reason) = syncer.autoenv(ctx) {
print_err(reason, &Text::from(""), ctx);
}
let _ = configure_rustyline_editor(&mut rl, config);
});
match line {
LineResult::Success(line) => {
rl.add_history_entry(&line);
let _ = rl.save_history(&history_path);
if options.save_history && !line.trim().is_empty() {
rl.add_history_entry(&line);
let _ = rl.append_history(&history_path);
}
maybe_print_errors(&context, Text::from(session_text.clone()));
}
LineResult::ClearHistory => {
rl.clear_history();
let _ = rl.save_history(&history_path);
if options.save_history {
rl.clear_history();
let _ = rl.append_history(&history_path);
}
}
LineResult::Error(line, err) => {
rl.add_history_entry(&line);
let _ = rl.save_history(&history_path);
if options.save_history && !line.trim().is_empty() {
rl.add_history_entry(&line);
let _ = rl.append_history(&history_path);
}
print_err(err, &Text::from(session_text.clone()), &context);
context
.host
.lock()
.print_err(err, &Text::from(session_text.clone()));
// I am not so sure, we don't need maybe_print_errors here (as we printed an err
// above), because maybe_print_errors also clears the errors.
// TODO Analyze where above err comes from, and whether we need to clear
// context.errors here
// Or just be consistent and return errors always in context.errors...
maybe_print_errors(&context, Text::from(session_text.clone()));
}
LineResult::CtrlC => {
let config_ctrlc_exit = config::config(Tag::unknown())?
.get("ctrlc_exit")
.map(|s| s.value.is_true())
let config_ctrlc_exit = context
.configs
.lock()
.global_config
.as_ref()
.map(|cfg| cfg.var("ctrlc_exit"))
.flatten()
.map(|ctrl_c| ctrl_c.is_true())
.unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells
if !config_ctrlc_exit {
@ -312,7 +386,9 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
}
if ctrlcbreak {
let _ = rl.save_history(&history_path);
if options.save_history {
let _ = rl.append_history(&history_path);
}
std::process::exit(0);
} else {
context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)"));
@ -336,12 +412,49 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
}
// we are ok if we can not save history
let _ = rl.save_history(&history_path);
if options.save_history {
let _ = rl.append_history(&history_path);
}
Ok(())
}
pub fn register_plugins(context: &mut EvaluationContext) -> Result<(), ShellError> {
pub fn load_local_cfg_if_present(context: &EvaluationContext) {
trace!("Loading local cfg if present");
match config::loadable_cfg_exists_in_dir(PathBuf::from(context.shell_manager.path())) {
Ok(Some(cfg_path)) => {
if let Err(err) = context.load_config(&ConfigPath::Local(cfg_path)) {
context.host.lock().print_err(err, &Text::from(""))
}
}
Err(e) => {
//Report error while checking for local cfg file
context.host.lock().print_err(e, &Text::from(""))
}
Ok(None) => {
//No local cfg file present in start dir
}
}
}
fn load_cfg_as_global_cfg(context: &EvaluationContext, path: PathBuf) {
if let Err(err) = context.load_config(&ConfigPath::Global(path)) {
context.host.lock().print_err(err, &Text::from(""));
}
}
pub fn load_global_cfg(context: &EvaluationContext) {
match config::default_path() {
Ok(path) => {
load_cfg_as_global_cfg(context, path);
}
Err(e) => {
context.host.lock().print_err(e, &Text::from(""));
}
}
}
pub fn register_plugins(context: &EvaluationContext) -> Result<(), ShellError> {
if let Ok(plugins) = nu_engine::plugin::build_plugin::scan(search_paths()) {
context.add_commands(
plugins
@ -354,35 +467,7 @@ pub fn register_plugins(context: &mut EvaluationContext) -> Result<(), ShellErro
Ok(())
}
async fn run_startup_commands(
context: &mut EvaluationContext,
config: &dyn nu_data::config::Conf,
) -> Result<(), ShellError> {
if let Some(commands) = config.var("startup") {
match commands {
Value {
value: UntaggedValue::Table(pipelines),
..
} => {
let mut script_file = String::new();
for pipeline in pipelines {
script_file.push_str(&pipeline.as_string()?);
script_file.push('\n');
}
let _ = run_script_standalone(script_file, false, context, false).await;
}
_ => {
return Err(ShellError::untagged_runtime_error(
"expected a table of pipeline strings as startup commands",
));
}
}
}
Ok(())
}
pub async fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, ShellError> {
pub fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, ShellError> {
// FIXME: do we still need this?
let line = if let Some(s) = line.strip_suffix('\n') {
s
@ -399,13 +484,11 @@ pub async fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<Strin
}
let input_stream = InputStream::empty();
let env = ctx.get_env();
ctx.scope.add_env(env);
let result = run_block(&classified_block, ctx, input_stream).await;
let result = run_block(&classified_block, ctx, input_stream);
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)]

View File

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

View File

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

View File

@ -15,7 +15,8 @@ pub struct PathSuggestion {
impl PathCompleter {
pub fn path_suggestions(&self, partial: &str, matcher: &dyn Matcher) -> Vec<PathSuggestion> {
let expanded = nu_parser::expand_ndots(partial);
let expanded = expanded.as_ref();
let expanded = expanded.replace(std::path::is_separator, &SEP.to_string());
let expanded: &str = expanded.as_ref();
let (base_dir_name, partial) = match expanded.rfind(SEP) {
Some(pos) => expanded.split_at(pos + SEP.len_utf8()),

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::basic_evaluation_context;
use nu_engine::Env;
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 = basic_evaluation_context()?;
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 = basic_evaluation_context()?;
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 = basic_evaluation_context()?;
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 = basic_evaluation_context()?;
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 = basic_evaluation_context()?;
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,38 +1,64 @@
use rustyline::{KeyCode, Modifiers};
use serde::{Deserialize, Serialize};
fn convert_keypress(keypress: KeyPress) -> rustyline::KeyPress {
match keypress {
KeyPress::UnknownEscSeq => rustyline::KeyPress::UnknownEscSeq,
KeyPress::Backspace => rustyline::KeyPress::Backspace,
KeyPress::BackTab => rustyline::KeyPress::BackTab,
KeyPress::BracketedPasteStart => rustyline::KeyPress::BracketedPasteStart,
KeyPress::BracketedPasteEnd => rustyline::KeyPress::BracketedPasteEnd,
KeyPress::Char(c) => rustyline::KeyPress::Char(c),
KeyPress::ControlDown => rustyline::KeyPress::ControlDown,
KeyPress::ControlLeft => rustyline::KeyPress::ControlLeft,
KeyPress::ControlRight => rustyline::KeyPress::ControlRight,
KeyPress::ControlUp => rustyline::KeyPress::ControlUp,
KeyPress::Ctrl(c) => rustyline::KeyPress::Ctrl(c),
KeyPress::Delete => rustyline::KeyPress::Delete,
KeyPress::Down => rustyline::KeyPress::Down,
KeyPress::End => rustyline::KeyPress::End,
KeyPress::Enter => rustyline::KeyPress::Enter,
KeyPress::Esc => rustyline::KeyPress::Esc,
KeyPress::F(u) => rustyline::KeyPress::F(u),
KeyPress::Home => rustyline::KeyPress::Home,
KeyPress::Insert => rustyline::KeyPress::Insert,
KeyPress::Left => rustyline::KeyPress::Left,
KeyPress::Meta(c) => rustyline::KeyPress::Meta(c),
KeyPress::Null => rustyline::KeyPress::Null,
KeyPress::PageDown => rustyline::KeyPress::PageDown,
KeyPress::PageUp => rustyline::KeyPress::PageUp,
KeyPress::Right => rustyline::KeyPress::Right,
KeyPress::ShiftDown => rustyline::KeyPress::ShiftDown,
KeyPress::ShiftLeft => rustyline::KeyPress::ShiftLeft,
KeyPress::ShiftRight => rustyline::KeyPress::ShiftRight,
KeyPress::ShiftUp => rustyline::KeyPress::ShiftUp,
KeyPress::Tab => rustyline::KeyPress::Tab,
KeyPress::Up => rustyline::KeyPress::Up,
pub fn convert_keyevent(key_event: KeyEvent) -> rustyline::KeyEvent {
match key_event {
KeyEvent::UnknownEscSeq => convert_to_rl_keyevent(rustyline::KeyCode::UnknownEscSeq, None),
KeyEvent::Backspace => convert_to_rl_keyevent(rustyline::KeyCode::Backspace, None),
KeyEvent::BackTab => convert_to_rl_keyevent(rustyline::KeyCode::BackTab, None),
KeyEvent::BracketedPasteStart => {
convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteStart, None)
}
KeyEvent::BracketedPasteEnd => {
convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteEnd, None)
}
KeyEvent::Char(c) => convert_to_rl_keyevent(rustyline::KeyCode::Char(c), None),
KeyEvent::ControlDown => {
convert_to_rl_keyevent(rustyline::KeyCode::Down, Some(Modifiers::CTRL))
}
KeyEvent::ControlLeft => {
convert_to_rl_keyevent(rustyline::KeyCode::Left, Some(Modifiers::CTRL))
}
KeyEvent::ControlRight => {
convert_to_rl_keyevent(rustyline::KeyCode::Right, Some(Modifiers::CTRL))
}
KeyEvent::ControlUp => {
convert_to_rl_keyevent(rustyline::KeyCode::Up, Some(Modifiers::CTRL))
}
KeyEvent::Ctrl(c) => rustyline::KeyEvent::ctrl(c),
KeyEvent::Delete => convert_to_rl_keyevent(rustyline::KeyCode::Delete, None),
KeyEvent::Down => convert_to_rl_keyevent(rustyline::KeyCode::Down, None),
KeyEvent::End => convert_to_rl_keyevent(rustyline::KeyCode::End, None),
KeyEvent::Enter => convert_to_rl_keyevent(rustyline::KeyCode::Enter, None),
KeyEvent::Esc => convert_to_rl_keyevent(rustyline::KeyCode::Esc, None),
KeyEvent::F(u) => convert_to_rl_keyevent(rustyline::KeyCode::F(u), None),
KeyEvent::Home => convert_to_rl_keyevent(rustyline::KeyCode::Home, None),
KeyEvent::Insert => convert_to_rl_keyevent(rustyline::KeyCode::Insert, None),
KeyEvent::Left => convert_to_rl_keyevent(rustyline::KeyCode::Left, None),
KeyEvent::Meta(c) => rustyline::KeyEvent::new(c, Modifiers::NONE),
KeyEvent::Null => convert_to_rl_keyevent(rustyline::KeyCode::Null, None),
KeyEvent::PageDown => convert_to_rl_keyevent(rustyline::KeyCode::PageDown, None),
KeyEvent::PageUp => convert_to_rl_keyevent(rustyline::KeyCode::PageUp, None),
KeyEvent::Right => convert_to_rl_keyevent(rustyline::KeyCode::Right, None),
KeyEvent::ShiftDown => {
convert_to_rl_keyevent(rustyline::KeyCode::Down, Some(Modifiers::SHIFT))
}
KeyEvent::ShiftLeft => {
convert_to_rl_keyevent(rustyline::KeyCode::Left, Some(Modifiers::SHIFT))
}
KeyEvent::ShiftRight => {
convert_to_rl_keyevent(rustyline::KeyCode::Right, Some(Modifiers::SHIFT))
}
KeyEvent::ShiftUp => convert_to_rl_keyevent(rustyline::KeyCode::Up, Some(Modifiers::SHIFT)),
KeyEvent::Tab => convert_to_rl_keyevent(rustyline::KeyCode::Tab, None),
KeyEvent::Up => convert_to_rl_keyevent(rustyline::KeyCode::Up, None),
}
}
fn convert_to_rl_keyevent(key_event: KeyCode, modifier: Option<Modifiers>) -> rustyline::KeyEvent {
rustyline::KeyEvent {
0: key_event,
1: modifier.unwrap_or(Modifiers::NONE),
}
}
@ -97,7 +123,9 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
match cmd {
Cmd::Abort => rustyline::Cmd::Abort,
Cmd::AcceptLine => rustyline::Cmd::AcceptLine,
Cmd::AcceptOrInsertLine => rustyline::Cmd::AcceptOrInsertLine,
Cmd::AcceptOrInsertLine => rustyline::Cmd::AcceptOrInsertLine {
accept_in_the_middle: false,
},
Cmd::BeginningOfHistory => rustyline::Cmd::BeginningOfHistory,
Cmd::CapitalizeWord => rustyline::Cmd::CapitalizeWord,
Cmd::ClearScreen => rustyline::Cmd::ClearScreen,
@ -140,18 +168,18 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
}
}
fn convert_keybinding(keybinding: Keybinding) -> (rustyline::KeyPress, rustyline::Cmd) {
fn convert_keybinding(keybinding: Keybinding) -> (rustyline::KeyEvent, rustyline::Cmd) {
(
convert_keypress(keybinding.key),
convert_keyevent(keybinding.key),
convert_cmd(keybinding.binding),
)
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum KeyPress {
pub enum KeyEvent {
/// Unsupported escape sequence (on unix platform)
UnknownEscSeq,
/// ⌫ or `KeyPress::Ctrl('H')`
/// ⌫ or `KeyEvent::Ctrl('H')`
Backspace,
/// ⇤ (usually Shift-Tab)
BackTab,
@ -177,9 +205,9 @@ pub enum KeyPress {
Down,
/// ⇲
End,
/// ↵ or `KeyPress::Ctrl('M')`
/// ↵ or `KeyEvent::Ctrl('M')`
Enter,
/// Escape or `KeyPress::Ctrl('[')`
/// Escape or `KeyEvent::Ctrl('[')`
Esc,
/// Function key
F(u8),
@ -191,7 +219,7 @@ pub enum KeyPress {
Left,
/// Escape-char or Alt-char
Meta(char),
/// `KeyPress::Char('\0')`
/// `KeyEvent::Char('\0')`
Null,
/// ⇟
PageDown,
@ -207,7 +235,7 @@ pub enum KeyPress {
ShiftRight,
/// Shift-↑
ShiftUp,
/// ⇥ or `KeyPress::Ctrl('I')`
/// ⇥ or `KeyEvent::Ctrl('I')`
Tab,
/// ↑ arrow key
Up,
@ -399,7 +427,7 @@ pub type RepeatCount = usize;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Keybinding {
key: KeyPress,
key: KeyEvent,
binding: Cmd,
}

View File

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

View File

@ -5,7 +5,10 @@ use std::error::Error;
use crate::prelude::*;
#[allow(unused_imports)]
use nu_command::script::LineResult;
use nu_engine::script::LineResult;
#[cfg(feature = "rustyline-support")]
use crate::keybinding::{convert_keyevent, KeyEvent};
#[cfg(feature = "rustyline-support")]
use crate::shell::Helper;
@ -16,7 +19,7 @@ use rustyline::{
config::Configurer,
config::{ColorMode, CompletionType, Config},
error::ReadlineError,
At, Cmd, Editor, KeyPress, Movement, Word,
At, Cmd, Editor, Movement, Word,
};
#[cfg(feature = "rustyline-support")]
@ -40,22 +43,27 @@ pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
#[cfg(not(windows))]
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
let config = Config::builder().color_mode(ColorMode::Forced).build();
let config = Config::builder()
.check_cursor_position(true)
.color_mode(ColorMode::Forced)
.build();
let mut rl: Editor<_> = Editor::with_config(config);
// add key bindings to move over a whole word with Ctrl+ArrowLeft and Ctrl+ArrowRight
rl.bind_sequence(
KeyPress::ControlLeft,
convert_keyevent(KeyEvent::ControlLeft),
Cmd::Move(Movement::BackwardWord(1, Word::Vi)),
);
rl.bind_sequence(
KeyPress::ControlRight,
convert_keyevent(KeyEvent::ControlRight),
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
);
// workaround for multiline-paste hang in rustyline (see https://github.com/kkawakam/rustyline/issues/202)
rl.bind_sequence(KeyPress::BracketedPasteStart, rustyline::Cmd::Noop);
rl.bind_sequence(
convert_keyevent(KeyEvent::BracketedPasteStart),
rustyline::Cmd::Noop,
);
// Let's set the defaults up front and then override them later if the user indicates
// defaults taken from here https://github.com/kkawakam/rustyline/blob/2fe886c9576c1ea13ca0e5808053ad491a6fe049/src/config.rs#L150-L167
rl.set_max_history_size(100);
@ -194,7 +202,7 @@ pub fn configure_rustyline_editor(
#[cfg(feature = "rustyline-support")]
pub fn nu_line_editor_helper(
context: &mut EvaluationContext,
context: &EvaluationContext,
config: &dyn nu_data::config::Conf,
) -> crate::shell::Helper {
let hinter = rustyline_hinter(config);
@ -216,7 +224,7 @@ pub fn rustyline_hinter(
Some(rustyline::hint::HistoryHinter {})
}
pub fn configure_ctrl_c(_context: &mut EvaluationContext) -> Result<(), Box<dyn Error>> {
pub fn configure_ctrl_c(_context: &EvaluationContext) -> Result<(), Box<dyn Error>> {
#[cfg(feature = "ctrlc")]
{
let cc = _context.ctrl_c.clone();

View File

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

View File

@ -1,5 +1,6 @@
use crate::completion;
use crate::shell::completer::NuCompleter;
use nu_ansi_term::Color;
use nu_engine::{DefaultPalette, EvaluationContext, Painter};
use nu_source::{Tag, Tagged};
use std::borrow::Cow::{self, Owned};
@ -57,6 +58,7 @@ impl rustyline::completion::Completer for Helper {
}
impl rustyline::hint::Hinter for Helper {
type Hint = String;
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
self.hinter.as_ref().and_then(|h| h.hint(line, pos, &ctx))
}
@ -78,7 +80,7 @@ impl rustyline::highlight::Highlighter for Helper {
}
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
Owned(Color::DarkGray.prefix().to_string() + hint + nu_ansi_term::ansi::RESET)
}
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {

View File

@ -285,7 +285,7 @@ fn get_shape_of_expr(expr: &SpannedExpression) -> Option<SyntaxShape> {
nu_protocol::hir::Literal::Bare(_) => Some(SyntaxShape::String),
}
}
//Synthetic are expressions that are generated by the parser and not inputed by the user
//Synthetic are expressions that are generated by the parser and not inputted by the user
//ExternalWord is anything sent to external commands (?)
Expression::ExternalWord => Some(SyntaxShape::String),
Expression::Synthetic(_) => Some(SyntaxShape::String),
@ -387,7 +387,7 @@ impl VarSyntaxShapeDeductor {
}
fn infer_shape(&mut self, block: &Block, scope: &Scope) -> Result<(), ShellError> {
trace!("Infering vars in shape");
trace!("Inferring vars in shape");
for group in &block.block {
for pipeline in &group.pipelines {
self.infer_pipeline(pipeline, scope)?;
@ -397,7 +397,7 @@ impl VarSyntaxShapeDeductor {
}
pub fn infer_pipeline(&mut self, pipeline: &Pipeline, scope: &Scope) -> Result<(), ShellError> {
trace!("Infering vars in pipeline");
trace!("Inferring vars in pipeline");
for (cmd_pipeline_idx, classified) in pipeline.list.iter().enumerate() {
match &classified {
ClassifiedCommand::Internal(internal) => {
@ -429,7 +429,7 @@ impl VarSyntaxShapeDeductor {
}
}
if let Some(named) = &internal.args.named {
trace!("Infering vars in named exprs");
trace!("Inferring vars in named exprs");
for (_name, val) in named.iter() {
if let NamedValue::Value(_, named_expr) = val {
self.infer_shapes_in_expr(
@ -443,7 +443,7 @@ impl VarSyntaxShapeDeductor {
}
ClassifiedCommand::Expr(spanned_expr) => {
trace!(
"Infering shapes in ClassifiedCommand::Expr: {:?}",
"Inferring shapes in ClassifiedCommand::Expr: {:?}",
spanned_expr
);
self.infer_shapes_in_expr((cmd_pipeline_idx, pipeline), spanned_expr, scope)?;
@ -459,7 +459,7 @@ impl VarSyntaxShapeDeductor {
positionals: &[SpannedExpression],
signature: &Signature,
) -> Result<(), ShellError> {
trace!("Infering vars in positionals");
trace!("Inferring vars in positionals");
//TODO currently correct inference for optional positionals is not implemented.
// See https://github.com/nushell/nushell/pull/2486 for a discussion about this
// For now we assume every variable in an optional positional is used as this optional
@ -500,7 +500,7 @@ impl VarSyntaxShapeDeductor {
named: &NamedArguments,
signature: &Signature,
) -> Result<(), ShellError> {
trace!("Infering vars in named");
trace!("Inferring vars in named");
for (name, val) in named.iter() {
if let NamedValue::Value(span, spanned_expr) = &val {
if let Expression::Variable(var_name, _) = &spanned_expr.expr {
@ -534,15 +534,15 @@ impl VarSyntaxShapeDeductor {
) -> Result<(), ShellError> {
match &spanned_expr.expr {
Expression::Binary(_) => {
trace!("Infering vars in bin expr");
trace!("Inferring vars in bin expr");
self.infer_shapes_in_binary_expr((pipeline_idx, pipeline), spanned_expr, scope)?;
}
Expression::Block(b) => {
trace!("Infering vars in block");
trace!("Inferring vars in block");
self.infer_shape(&b, scope)?;
}
Expression::Path(path) => {
trace!("Infering vars in path");
trace!("Inferring vars in path");
match &path.head.expr {
//PathMember can't be var yet (?)
//TODO Iterate over path parts and find var when implemented
@ -560,7 +560,7 @@ impl VarSyntaxShapeDeductor {
}
}
Expression::Range(range) => {
trace!("Infering vars in range");
trace!("Inferring vars in range");
if let Some(range_left) = &range.left {
if let Expression::Variable(var_name, _) = &range_left.expr {
self.checked_insert(
@ -585,13 +585,13 @@ impl VarSyntaxShapeDeductor {
}
}
Expression::List(inner_exprs) => {
trace!("Infering vars in list");
trace!("Inferring vars in list");
for expr in inner_exprs {
self.infer_shapes_in_expr((pipeline_idx, pipeline), expr, scope)?;
}
}
Expression::Invocation(invoc) => {
trace!("Infering vars in invocation: {:?}", invoc);
trace!("Inferring vars in invocation: {:?}", invoc);
self.infer_shape(invoc, scope)?;
}
Expression::Table(header, _rows) => {
@ -738,7 +738,7 @@ impl VarSyntaxShapeDeductor {
(pipeline_idx, pipeline): (usize, &Pipeline),
scope: &Scope,
) -> Result<(), ShellError> {
trace!("Infering shapes between var {:?} and expr {:?}", var, expr);
trace!("Inferring shapes between var {:?} and expr {:?}", var, expr);
let bin = spanned_to_binary(bin_spanned);
if let Expression::Literal(Literal::Operator(op)) = bin.op.expr {
match &op {

View File

@ -5,30 +5,28 @@ description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-command"
version = "0.28.0"
version = "0.30.0"
[lib]
doctest = false
[dependencies]
nu-data = { version = "0.28.0", path = "../nu-data" }
nu-engine = { version = "0.28.0", path = "../nu-engine" }
nu-errors = { version = "0.28.0", path = "../nu-errors" }
nu-json = { version = "0.28.0", path = "../nu-json" }
nu-parser = { version = "0.28.0", path = "../nu-parser" }
nu-plugin = { version = "0.28.0", path = "../nu-plugin" }
nu-protocol = { version = "0.28.0", path = "../nu-protocol" }
nu-source = { version = "0.28.0", path = "../nu-source" }
nu-stream = { version = "0.28.0", path = "../nu-stream" }
nu-table = { version = "0.28.0", path = "../nu-table" }
nu-test-support = { version = "0.28.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.28.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.28.0", path = "../nu-ansi-term" }
nu-data = { version = "0.30.0", path = "../nu-data" }
nu-engine = { version = "0.30.0", path = "../nu-engine" }
nu-errors = { version = "0.30.0", path = "../nu-errors" }
nu-json = { version = "0.30.0", path = "../nu-json" }
nu-parser = { version = "0.30.0", path = "../nu-parser" }
nu-plugin = { version = "0.30.0", path = "../nu-plugin" }
nu-protocol = { version = "0.30.0", path = "../nu-protocol" }
nu-source = { version = "0.30.0", path = "../nu-source" }
nu-stream = { version = "0.30.0", path = "../nu-stream" }
nu-table = { version = "0.30.0", path = "../nu-table" }
nu-test-support = { version = "0.30.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.30.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.30.0", path = "../nu-ansi-term" }
Inflector = "0.11"
arboard = { version = "1.1.0", optional = true }
async-recursion = "0.3.2"
async-trait = "0.1.42"
base64 = "0.13.0"
bigdecimal = { version = "0.2.0", features = ["serde"] }
byte-unit = "4.0.9"
@ -51,8 +49,6 @@ encoding_rs = "0.8.28"
filesize = "0.2.0"
fs_extra = "1.2.0"
futures = { version = "0.3.12", features = ["compat", "io-compat"] }
futures-util = "0.3.12"
futures_codec = "0.4.1"
getset = "0.1.1"
glob = "0.3.0"
htmlescape = "0.3.1"
@ -62,6 +58,7 @@ indexmap = { version = "1.6.1", features = ["serde-1"] }
itertools = "0.10.0"
lazy_static = "1.*"
log = "0.4.14"
md5 = "0.7.0"
meval = "0.2.0"
minus = { version = "3.3.0", optional = true, features = ["async_std_lib", "search"] }
num-bigint = { version = "0.3.1", features = ["serde"] }
@ -78,7 +75,7 @@ rayon = "1.5.0"
regex = "1.4.3"
roxmltree = "0.14.0"
rust-embed = "5.9.0"
rustyline = { version = "7.1.0", optional = true }
rustyline = { version = "8.0.0", optional = true }
serde = { version = "1.0.123", features = ["derive"] }
serde_bytes = "0.11.5"
serde_ini = "0.2.0"
@ -124,6 +121,7 @@ shadow-rs = "0.5"
[dev-dependencies]
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
hamcrest2 = "0.3.0"
[features]
clipboard-cli = ["arboard"]

View File

@ -4,7 +4,9 @@ pub(crate) mod macros;
mod from_delimited_data;
mod to_delimited_data;
pub(crate) mod all;
pub(crate) mod ansi;
pub(crate) mod any;
pub(crate) mod append;
pub(crate) mod args;
pub mod autoenv;
@ -20,11 +22,9 @@ pub(crate) mod chart;
pub(crate) mod classified;
#[cfg(feature = "clipboard-cli")]
pub(crate) mod clip;
pub mod command;
pub(crate) mod compact;
pub(crate) mod config;
pub(crate) mod constants;
pub(crate) mod count;
pub(crate) mod cp;
pub(crate) mod date;
pub(crate) mod debug;
@ -70,9 +70,10 @@ pub(crate) mod histogram;
pub(crate) mod history;
pub(crate) mod if_;
pub(crate) mod insert;
pub(crate) mod into_int;
pub(crate) mod into;
pub(crate) mod keep;
pub(crate) mod last;
pub(crate) mod length;
pub(crate) mod let_;
pub(crate) mod let_env;
pub(crate) mod lines;
@ -153,9 +154,8 @@ pub(crate) use char_::Char;
pub(crate) use chart::Chart;
pub(crate) use compact::Compact;
pub(crate) use config::{
Config, ConfigClear, ConfigGet, ConfigLoad, ConfigPath, ConfigRemove, ConfigSet, ConfigSetInto,
Config, ConfigClear, ConfigGet, ConfigPath, ConfigRemove, ConfigSet, ConfigSetInto,
};
pub(crate) use count::Count;
pub(crate) use cp::Cpy;
pub(crate) use date::{Date, DateFormat, DateListTimeZone, DateNow, DateToTable, DateToTimeZone};
pub(crate) use debug::Debug;
@ -171,6 +171,8 @@ pub(crate) use each::EachWindow;
pub(crate) use echo::Echo;
pub(crate) use empty::Command as Empty;
pub(crate) use if_::If;
pub(crate) use into::Into;
pub(crate) use into::IntoInt;
pub(crate) use nu::NuPlugin;
pub(crate) use update::Command as Update;
pub(crate) mod kill;
@ -178,6 +180,8 @@ pub(crate) use kill::Kill;
pub(crate) mod clear;
pub(crate) use clear::Clear;
pub(crate) mod touch;
pub(crate) use all::Command as All;
pub(crate) use any::Command as Any;
pub(crate) use enter::Enter;
pub(crate) use every::Every;
pub(crate) use exec::Exec;
@ -186,40 +190,41 @@ pub(crate) use first::First;
pub(crate) use flatten::Command as Flatten;
pub(crate) use format::{FileSize, Format};
pub(crate) use from::From;
pub(crate) use from_csv::FromCSV;
pub(crate) use from_eml::FromEML;
pub(crate) use from_csv::FromCsv;
pub(crate) use from_eml::FromEml;
pub(crate) use from_ics::FromIcs;
pub(crate) use from_ini::FromINI;
pub(crate) use from_json::FromJSON;
pub(crate) use from_ods::FromODS;
pub(crate) use from_ssv::FromSSV;
pub(crate) use from_toml::FromTOML;
pub(crate) use from_tsv::FromTSV;
pub(crate) use from_url::FromURL;
pub(crate) use from_ini::FromIni;
pub(crate) use from_json::FromJson;
pub(crate) use from_ods::FromOds;
pub(crate) use from_ssv::FromSsv;
pub(crate) use from_toml::FromToml;
pub(crate) use from_tsv::FromTsv;
pub(crate) use from_url::FromUrl;
pub(crate) use from_vcf::FromVcf;
pub(crate) use from_xlsx::FromXLSX;
pub(crate) use from_xml::FromXML;
pub(crate) use from_yaml::FromYAML;
pub(crate) use from_yaml::FromYML;
pub(crate) use from_xlsx::FromXlsx;
pub(crate) use from_xml::FromXml;
pub(crate) use from_yaml::FromYaml;
pub(crate) use from_yaml::FromYml;
pub(crate) use get::Command as Get;
pub(crate) use group_by::Command as GroupBy;
pub(crate) use group_by_date::GroupByDate;
pub(crate) use hash_::{Hash, HashBase64};
pub(crate) use hash_::{Hash, HashBase64, HashMd5};
pub(crate) use headers::Headers;
pub(crate) use help::Help;
pub(crate) use histogram::Histogram;
pub(crate) use history::History;
pub(crate) use insert::Command as Insert;
pub(crate) use into_int::IntoInt;
pub(crate) use keep::{Keep, KeepUntil, KeepWhile};
pub(crate) use last::Last;
pub(crate) use length::Length;
pub(crate) use let_::Let;
pub(crate) use let_env::LetEnv;
pub(crate) use lines::Lines;
pub(crate) use ls::Ls;
pub(crate) use math::{
Math, MathAbs, MathAverage, MathCeil, MathEval, MathFloor, MathMaximum, MathMedian,
MathMinimum, MathMode, MathProduct, MathRound, MathStddev, MathSummation, MathVariance,
MathMinimum, MathMode, MathProduct, MathRound, MathSqrt, MathStddev, MathSummation,
MathVariance,
};
pub(crate) use merge::Merge;
pub(crate) use mkdir::Mkdir;
@ -229,8 +234,8 @@ pub(crate) use nth::Nth;
pub(crate) use open::Open;
pub(crate) use parse::Parse;
pub(crate) use path::{
PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathExtension, PathFilestem,
PathJoin, PathType,
PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathJoin, PathParse, PathSplit,
PathType,
};
pub(crate) use pivot::Pivot;
pub(crate) use prepend::Prepend;
@ -273,20 +278,20 @@ pub(crate) use table::Table;
pub(crate) use tags::Tags;
pub(crate) use termsize::TermSize;
pub(crate) use to::To;
pub(crate) use to_csv::ToCSV;
pub(crate) use to_html::ToHTML;
pub(crate) use to_json::ToJSON;
pub(crate) use to_csv::ToCsv;
pub(crate) use to_html::ToHtml;
pub(crate) use to_json::ToJson;
pub(crate) use to_md::Command as ToMarkdown;
pub(crate) use to_toml::ToTOML;
pub(crate) use to_tsv::ToTSV;
pub(crate) use to_url::ToURL;
pub(crate) use to_xml::ToXML;
pub(crate) use to_yaml::ToYAML;
pub(crate) use to_toml::ToToml;
pub(crate) use to_tsv::ToTsv;
pub(crate) use to_url::ToUrl;
pub(crate) use to_xml::ToXml;
pub(crate) use to_yaml::ToYaml;
pub(crate) use touch::Touch;
pub(crate) use uniq::Uniq;
pub(crate) use url_::{UrlCommand, UrlHost, UrlPath, UrlQuery, UrlScheme};
pub(crate) use version::Version;
pub(crate) use where_::Where;
pub(crate) use where_::Command as Where;
pub(crate) use which_::Which;
pub(crate) use with_env::WithEnv;
pub(crate) use wrap::Wrap;

View File

@ -0,0 +1,136 @@
use crate::prelude::*;
use nu_engine::evaluate_baseline_expr;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
hir::CapturedBlock, hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue,
};
pub struct Command;
struct AllArgs {
predicate: CapturedBlock,
}
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"all?"
}
fn signature(&self) -> Signature {
Signature::build("all?").required(
"condition",
SyntaxShape::RowCondition,
"the condition that must match",
)
}
fn usage(&self) -> &str {
"Find if the table rows matches the condition."
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
all(args)
}
fn examples(&self) -> Vec<Example> {
use nu_protocol::Value;
vec![
Example {
description: "Find if services are running",
example: "echo [[status]; [UP] [UP]] | all? status == UP",
result: Some(vec![Value::from(true)]),
},
Example {
description: "Check that all values are even",
example: "echo [2 4 6 8] | all? $(= $it mod 2) == 0",
result: Some(vec![Value::from(true)]),
},
]
}
}
fn all(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = EvaluationContext::from_args(&args);
let tag = args.call_info.name_tag.clone();
let args = args.evaluate_once()?;
let all_args = AllArgs {
predicate: args.req(0)?,
};
let err = Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
args.call_info.name_tag.clone(),
));
//This seems a little odd. Can't we have predicates with pipelines/multiple statements?
let condition = {
if all_args.predicate.block.block.len() != 1 {
return err;
}
match all_args.predicate.block.block[0].pipelines.get(0) {
Some(item) => match item.list.get(0) {
Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
_ => {
return err;
}
},
None => {
return err;
}
}
};
let scope = args.scope.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;
#[derive(Deserialize)]
struct AnsiArgs {
code: Value,
escape: Option<Tagged<String>>,
osc: Option<Tagged<String>>,
}
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"ansi"
@ -120,8 +112,12 @@ Format: #
]
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let (AnsiArgs { code, escape, osc }, _) = args.process().await?;
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let args = args.evaluate_once()?;
let code: Option<Tagged<String>> = args.opt(0)?;
let escape: Option<Tagged<String>> = args.get_flag("escape")?;
let osc: Option<Tagged<String>> = args.get_flag("osc")?;
if let Some(e) = escape {
let esc_vec: Vec<char> = e.item.chars().collect();
@ -133,7 +129,7 @@ Format: #
));
}
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()),
)));
}
@ -151,30 +147,37 @@ Format: #
//Operating system command aka osc ESC ] <- note the right brace, not left brace for osc
// OCS's need to end with a bell '\x07' char
let output = format!("\x1b]{};", o.item);
return Ok(OutputStream::one(ReturnSuccess::value(
return Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(o.tag()),
)));
}
let code_string = code.as_string()?;
let ansi_code = str_to_ansi(code_string);
if let Some(code) = code {
let ansi_code = str_to_ansi(&code.item);
if let Some(output) = ansi_code {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(code.tag()),
)))
if let Some(output) = ansi_code {
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(code.tag()),
)))
} else {
Err(ShellError::labeled_error(
"Unknown ansi code",
"unknown ansi code",
code.tag(),
))
}
} else {
Err(ShellError::labeled_error(
"Unknown ansi code",
"unknown ansi code",
code.tag(),
"Expected ansi code",
"expect ansi code",
args.call_info.name_tag.clone(),
))
}
}
}
pub fn str_to_ansi(s: String) -> Option<String> {
match s.as_str() {
pub fn str_to_ansi(s: &str) -> Option<String> {
match s {
"g" | "green" => Some(Color::Green.prefix().to_string()),
"gb" | "green_bold" => Some(Color::Green.bold().prefix().to_string()),
"gu" | "green_underline" => Some(Color::Green.underline().prefix().to_string()),
@ -330,7 +333,7 @@ pub fn str_to_ansi(s: String) -> Option<String> {
// Ansi RGB - Needs to be 32;2;r;g;b or 48;2;r;g;b
// assuming the rgb will be passed via command and no here
"rgb_fg" => Some("\x1b[32;2;".to_string()),
"rgb_fg" => Some("\x1b[38;2;".to_string()),
"rgb_bg" => Some("\x1b[48;2;".to_string()),
// Ansi color index - Needs 38;5;idx or 48;5;idx where idx = 0 to 255

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,14 +5,8 @@ use nu_data::value::format_leaf;
use nu_engine::WholeStreamCommand;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
#[derive(Deserialize)]
pub struct BuildStringArgs {
rest: Vec<Value>,
}
pub struct BuildString;
#[async_trait]
impl WholeStreamCommand for BuildString {
fn name(&self) -> &str {
"build-string"
@ -27,9 +21,10 @@ impl WholeStreamCommand for BuildString {
"Builds a string from the arguments."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (BuildStringArgs { rest }, _) = args.process().await?;
let args = args.evaluate_once()?;
let rest: Vec<Value> = args.rest(0)?;
let mut output_string = String::new();
@ -37,7 +32,7 @@ impl WholeStreamCommand for BuildString {
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),
)))
}

View File

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

View File

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

View File

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

View File

@ -6,7 +6,6 @@ use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
#[derive(Clone)]
pub struct Chart;
#[async_trait]
impl WholeStreamCommand for Chart {
fn name(&self) -> &str {
"chart"
@ -20,14 +19,14 @@ impl WholeStreamCommand for Chart {
"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() {
return Err(ShellError::untagged_runtime_error(
"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()),
))))
}

View File

@ -1,16 +1,14 @@
use crate::futures::ThreadedReceiver;
use crate::prelude::*;
use nu_engine::evaluate_baseline_expr;
use nu_engine::{evaluate_baseline_expr, BufCodecReader};
use nu_engine::{MaybeTextCodec, StringOrBinary};
use parking_lot::Mutex;
use std::borrow::Cow;
use std::io::Write;
use std::ops::Deref;
use std::process::{Command, Stdio};
use std::sync::mpsc;
use std::{borrow::Cow, io::BufReader};
use futures::executor::block_on_stream;
use futures_codec::FramedRead;
use log::trace;
use nu_errors::ShellError;
@ -18,9 +16,8 @@ use nu_protocol::hir::Expression;
use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
use nu_source::Tag;
use nu_stream::trace_stream;
pub(crate) async fn run_external_command(
pub(crate) fn run_external_command(
command: ExternalCommand,
context: &mut EvaluationContext,
input: InputStream,
@ -28,18 +25,19 @@ pub(crate) async fn run_external_command(
) -> Result<InputStream, ShellError> {
trace!(target: "nu::run::external", "-> {}", command.name);
if !did_find_command(&command.name) {
context.sync_path_to_env();
if !context.host.lock().is_external_cmd(&command.name) {
return Err(ShellError::labeled_error(
"Command not found",
"command not found",
format!("command {} not found", &command.name),
&command.name_tag,
));
}
run_with_stdin(command, context, input, external_redirection).await
run_with_stdin(command, context, input, external_redirection)
}
async fn run_with_stdin(
fn run_with_stdin(
command: ExternalCommand,
context: &mut EvaluationContext,
input: InputStream,
@ -47,12 +45,10 @@ async fn run_with_stdin(
) -> Result<InputStream, ShellError> {
let path = context.shell_manager.path();
let input = trace_stream!(target: "nu::trace_stream::external::stdin", "input" = input);
let mut command_args = vec![];
for arg in command.args.iter() {
let is_literal = matches!(arg.expr, Expression::Literal(_));
let value = evaluate_baseline_expr(arg, context).await?;
let value = evaluate_baseline_expr(arg, context)?;
// Skip any arguments that don't really exist, treating them as optional
// FIXME: we may want to preserve the gap in the future, though it's hard to say
@ -219,7 +215,7 @@ fn spawn(
.take()
.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 {
UntaggedValue::Primitive(Primitive::Nothing) => continue,
UntaggedValue::Primitive(Primitive::String(s)) => {
@ -274,10 +270,12 @@ fn spawn(
return Err(());
};
let file = futures::io::AllowStdIo::new(stdout);
let stream = FramedRead::new(file, MaybeTextCodec::default());
// let file = futures::io::AllowStdIo::new(stdout);
// let stream = FramedRead::new(file, MaybeTextCodec::default());
let buf_read = BufReader::new(stdout);
let buf_codec = BufCodecReader::new(buf_read, MaybeTextCodec::default());
for line in block_on_stream(stream) {
for line in buf_codec {
match line {
Ok(line) => match line {
StringOrBinary::String(s) => {
@ -345,10 +343,12 @@ fn spawn(
return Err(());
};
let file = futures::io::AllowStdIo::new(stderr);
let stream = FramedRead::new(file, MaybeTextCodec::default());
// let file = futures::io::AllowStdIo::new(stderr);
// let stream = FramedRead::new(file, MaybeTextCodec::default());
let buf_reader = BufReader::new(stderr);
let buf_codec = BufCodecReader::new(buf_reader, MaybeTextCodec::default());
for line in block_on_stream(stream) {
for line in buf_codec {
match line {
Ok(line) => match line {
StringOrBinary::String(s) => {
@ -432,7 +432,7 @@ fn spawn(
Ok(())
});
let stream = ThreadedReceiver::new(rx);
let stream = ChannelReceiver::new(rx);
Ok(stream.to_input_stream())
} else {
Err(ShellError::labeled_error(
@ -443,31 +443,26 @@ fn spawn(
}
}
pub fn did_find_command(#[allow(unused)] name: &str) -> bool {
#[cfg(not(feature = "which"))]
{
// we can't perform this check, so just assume it can be found
true
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)),
}
}
}
#[cfg(all(feature = "which", unix))]
{
which::which(name).is_ok()
}
impl Iterator for ChannelReceiver {
type Item = Result<Value, ShellError>;
#[cfg(all(feature = "which", windows))]
{
if which::which(name).is_ok() {
true
} else {
// Reference: https://ss64.com/nt/syntax-internal.html
let cmd_builtins = [
"assoc", "break", "color", "copy", "date", "del", "dir", "dpath", "echo", "erase",
"for", "ftype", "md", "mkdir", "mklink", "move", "path", "ren", "rename", "rd",
"rmdir", "start", "time", "title", "type", "ver", "verify", "vol",
];
cmd_builtins.contains(&name)
fn next(&mut self) -> Option<Self::Item> {
let rx = self.rx.lock();
match rx.recv() {
Ok(v) => Some(v),
Err(_) => None,
}
}
}
@ -535,16 +530,13 @@ mod tests {
#[cfg(feature = "which")]
use super::{run_external_command, InputStream};
#[cfg(feature = "which")]
use futures::executor::block_on;
#[cfg(feature = "which")]
use nu_engine::basic_evaluation_context;
#[cfg(feature = "which")]
use nu_errors::ShellError;
#[cfg(feature = "which")]
use nu_test_support::commands::ExternalBuilder;
// async fn read(mut stream: OutputStream) -> Option<Value> {
// match stream.try_next().await {
// fn read(mut stream: OutputStream) -> Option<Value> {
// match stream.try_next() {
// Ok(val) => {
// if let Some(val) = val {
// val.raw_value()
@ -557,7 +549,7 @@ mod tests {
// }
#[cfg(feature = "which")]
async fn non_existent_run() -> Result<(), ShellError> {
fn non_existent_run() {
use nu_protocol::hir::ExternalRedirection;
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
@ -565,24 +557,18 @@ mod tests {
let mut ctx =
basic_evaluation_context().expect("There was a problem creating a basic context.");
assert!(
run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout)
.await
.is_err()
);
Ok(())
assert!(run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout).is_err());
}
// async fn failure_run() -> Result<(), ShellError> {
// fn failure_run() -> Result<(), ShellError> {
// let cmd = ExternalBuilder::for_name("fail").build();
// let mut ctx = crate::cli::basic_evaluation_context().expect("There was a problem creating a basic context.");
// let stream = run_external_command(cmd, &mut ctx, None, false)
// .await?
// ?
// .expect("There was a problem running the external command.");
// match read(stream.into()).await {
// match read(stream.into()) {
// Some(Value {
// value: UntaggedValue::Error(_),
// ..
@ -600,8 +586,8 @@ mod tests {
#[cfg(feature = "which")]
#[test]
fn identifies_command_not_found() -> Result<(), ShellError> {
block_on(non_existent_run())
fn identifies_command_not_found() {
non_existent_run()
}
#[test]

View File

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

View File

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

View File

@ -1,21 +0,0 @@
use crate::prelude::*;
use nu_engine::Command;
use nu_errors::ShellError;
use parking_lot::Mutex;
use std::sync::atomic::AtomicBool;
pub struct RunnableContext {
pub input: InputStream,
pub shell_manager: ShellManager,
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
pub ctrl_c: Arc<AtomicBool>,
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
pub scope: Scope,
pub name: Tag,
}
impl RunnableContext {
pub fn get_command(&self, name: &str) -> Option<Command> {
self.scope.get_command(name)
}
}

View File

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

View File

@ -5,7 +5,6 @@ use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config clear"
@ -19,8 +18,8 @@ impl WholeStreamCommand for SubCommand {
"clear the config"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
clear(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
clear(args)
}
fn examples(&self) -> Vec<Example> {
@ -32,18 +31,23 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn clear(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
pub fn clear(args: CommandArgs) -> Result<ActionStream, ShellError> {
let ctx = EvaluationContext::from_args(&args);
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let mut result = nu_data::config::read(name_span, &None)?;
let result = if let Some(global_cfg) = &mut args.configs.lock().global_config {
global_cfg.vars.clear();
global_cfg.write()?;
ctx.reload_config(global_cfg)?;
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::Row(global_cfg.vars.clone().into()).into_value(args.call_info.name_tag),
)))
} else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
};
result.clear();
config::write(&result, &None)?;
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(args.call_info.name_tag),
)))
result
}

View File

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

View File

@ -6,11 +6,10 @@ use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedVal
pub struct SubCommand;
#[derive(Deserialize)]
pub struct GetArgs {
path: ColumnPath,
pub struct Arguments {
column_path: ColumnPath,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config get"
@ -28,8 +27,8 @@ impl WholeStreamCommand for SubCommand {
"Gets a value from the config"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
get(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
get(args)
}
fn examples(&self) -> Vec<Example> {
@ -41,28 +40,29 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (GetArgs { path }, _) = args.process().await?;
pub fn get(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args);
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let result = UntaggedValue::row(nu_data::config::read(&name_tag, &None)?).into_value(&name_tag);
let (Arguments { column_path }, _) = args.process()?;
let value = crate::commands::get::get_column_path(&path, &result)?;
let result = if let Some(global_cfg) = &ctx.configs.lock().global_config {
let result = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
let value = crate::commands::get::get_column_path(&column_path, &result)?;
Ok(match value {
Value {
value: UntaggedValue::Table(list),
..
} => list.into_iter().to_action_stream(),
x => ActionStream::one(ReturnSuccess::value(x)),
})
} else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
};
Ok(match value {
Value {
value: UntaggedValue::Table(list),
..
} => {
let list: Vec<_> = list
.iter()
.map(|x| ReturnSuccess::value(x.clone()))
.collect();
futures::stream::iter(list).to_output_stream()
}
x => OutputStream::one(ReturnSuccess::value(x)),
})
result
}

View File

@ -1,51 +0,0 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged;
use std::path::PathBuf;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct LoadArgs {
load: Tagged<PathBuf>,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config load"
}
fn signature(&self) -> Signature {
Signature::build("config load").required(
"load",
SyntaxShape::FilePath,
"Path to load the config from",
)
}
fn usage(&self) -> &str {
"Loads the config from the path given"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
set(args).await
}
}
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let name_span = args.call_info.name_tag.clone();
let (LoadArgs { load }, _) = args.process().await?;
let configuration = load.item().clone();
let result = nu_data::config::read(name_span, &Some(configuration))?;
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
)])
.to_output_stream())
}

View File

@ -1,7 +1,6 @@
pub mod clear;
pub mod command;
pub mod get;
pub mod load;
pub mod path;
pub mod remove;
pub mod set;
@ -10,8 +9,13 @@ pub mod set_into;
pub use clear::SubCommand as ConfigClear;
pub use command::Command as Config;
pub use get::SubCommand as ConfigGet;
pub use load::SubCommand as ConfigLoad;
pub use path::SubCommand as ConfigPath;
pub use remove::SubCommand as ConfigRemove;
pub use set::SubCommand as ConfigSet;
pub use set_into::SubCommand as ConfigSetInto;
use nu_errors::ShellError;
pub fn err_no_global_cfg_present() -> ShellError {
ShellError::untagged_runtime_error("No global config found!")
}

View File

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

View File

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

View File

@ -6,12 +6,11 @@ use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedVal
pub struct SubCommand;
#[derive(Deserialize)]
pub struct SetArgs {
path: ColumnPath,
pub struct Arguments {
column_path: ColumnPath,
value: Value,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config set"
@ -27,8 +26,8 @@ impl WholeStreamCommand for SubCommand {
"Sets a value in the config"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
set(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
set(args)
}
fn examples(&self) -> Vec<Example> {
@ -57,33 +56,49 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (SetArgs { path, mut value }, _) = args.process().await?;
pub fn set(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args);
let (
Arguments {
column_path,
mut value,
},
_,
) = args.process()?;
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let raw_entries = nu_data::config::read(&name_tag, &None)?;
let configuration = UntaggedValue::row(raw_entries).into_value(&name_tag);
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
let configuration = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
if let UntaggedValue::Table(rows) = &value.value {
if rows.len() == 1 && rows[0].is_row() {
value = rows[0].clone();
if let UntaggedValue::Table(rows) = &value.value {
if rows.len() == 1 && rows[0].is_row() {
value = rows[0].clone();
}
}
}
match configuration.forgiving_insert_data_at_column_path(&path, value) {
Ok(Value {
value: UntaggedValue::Row(changes),
..
}) => {
config::write(&changes.entries, &None)?;
match configuration.forgiving_insert_data_at_column_path(&column_path, value) {
Ok(Value {
value: UntaggedValue::Row(changes),
..
}) => {
global_cfg.vars = changes.entries;
global_cfg.write()?;
ctx.reload_config(global_cfg)?;
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(changes).into_value(name_tag),
)))
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
)))
}
Ok(_) => Ok(ActionStream::empty()),
Err(reason) => Err(reason),
}
Ok(_) => Ok(OutputStream::empty()),
Err(reason) => Err(reason),
}
} else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
};
result
}

View File

@ -7,11 +7,10 @@ use nu_source::Tagged;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct SetIntoArgs {
pub struct Arguments {
set_into: Tagged<String>,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config set_into"
@ -29,8 +28,8 @@ impl WholeStreamCommand for SubCommand {
"Sets a value in the config"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
set_into(args).await
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
set_into(args)
}
fn examples(&self) -> Vec<Example> {
@ -42,49 +41,46 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
pub fn set_into(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args);
let (Arguments { set_into: v }, input) = args.process()?;
let (SetIntoArgs { set_into: v }, input) = args.process().await?;
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let mut result = nu_data::config::read(name_span, &None)?;
// In the original code, this is set to `Some` if the `load flag is set`
let configuration = None;
let rows: Vec<Value> = input.collect().await;
let rows: Vec<Value> = input.collect();
let key = v.to_string();
Ok(if rows.is_empty() {
return Err(ShellError::labeled_error(
"No values given for set_into",
"needs value(s) from pipeline",
v.tag(),
));
} else if rows.len() == 1 {
// A single value
let value = &rows[0];
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
if rows.is_empty() {
return Err(ShellError::labeled_error(
"No values given for set_into",
"needs value(s) from pipeline",
v.tag(),
));
} else if rows.len() == 1 {
// A single value
let value = &rows[0];
result.insert(key, value.clone());
global_cfg.vars.insert(key, value.clone());
} else {
// Take in the pipeline as a table
let value = UntaggedValue::Table(rows).into_value(name.clone());
config::write(&result, &configuration)?;
global_cfg.vars.insert(key, value);
}
OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
))
global_cfg.write()?;
ctx.reload_config(global_cfg)?;
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
)))
} else {
// Take in the pipeline as a table
let value = UntaggedValue::Table(rows).into_value(name.clone());
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
};
result.insert(key, value);
config::write(&result, &configuration)?;
OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
))
})
result
}

View File

@ -1,86 +0,0 @@
use crate::prelude::*;
use futures::stream::StreamExt;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, UntaggedValue, Value};
pub struct Count;
#[derive(Deserialize)]
pub struct CountArgs {
column: bool,
}
#[async_trait]
impl WholeStreamCommand for Count {
fn name(&self) -> &str {
"count"
}
fn signature(&self) -> Signature {
Signature::build("count").switch(
"column",
"Calculate number of columns in table",
Some('c'),
)
}
fn usage(&self) -> &str {
"Show the total number of rows or items."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (CountArgs { column }, input) = args.process().await?;
let rows: Vec<Value> = input.collect().await;
let count = if column {
if rows.is_empty() {
0
} else {
match &rows[0].value {
UntaggedValue::Row(dictionary) => dictionary.length(),
_ => {
return Err(ShellError::labeled_error(
"Cannot obtain column count",
"cannot obtain column count",
tag,
));
}
}
}
} else {
rows.len()
};
Ok(OutputStream::one(UntaggedValue::int(count).into_value(tag)))
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Count the number of entries in a list",
example: "echo [1 2 3 4 5] | count",
result: Some(vec![UntaggedValue::int(5).into()]),
},
Example {
description: "Count the number of columns in the calendar table",
example: "cal | count -c",
result: None,
},
]
}
}
#[cfg(test)]
mod tests {
use super::Count;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(Count {})
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,7 +29,6 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(ConfigSet),
whole_stream_command(ConfigSetInto),
whole_stream_command(ConfigClear),
whole_stream_command(ConfigLoad),
whole_stream_command(ConfigRemove),
whole_stream_command(ConfigPath),
whole_stream_command(Help),
@ -57,7 +56,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(Sleep),
// Statistics
whole_stream_command(Size),
whole_stream_command(Count),
whole_stream_command(Length),
whole_stream_command(Benchmark),
// Metadata
whole_stream_command(Tags),
@ -75,6 +74,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
// Text manipulation
whole_stream_command(Hash),
whole_stream_command(HashBase64),
whole_stream_command(HashMd5),
whole_stream_command(Split),
whole_stream_command(SplitColumn),
whole_stream_command(SplitRow),
@ -121,9 +121,12 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(Get),
whole_stream_command(Update),
whole_stream_command(Insert),
whole_stream_command(Into),
whole_stream_command(IntoInt),
whole_stream_command(SplitBy),
// Row manipulation
whole_stream_command(All),
whole_stream_command(Any),
whole_stream_command(Reverse),
whole_stream_command(Append),
whole_stream_command(Prepend),
@ -188,32 +191,33 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(MathRound),
whole_stream_command(MathFloor),
whole_stream_command(MathCeil),
whole_stream_command(MathSqrt),
// File format output
whole_stream_command(To),
whole_stream_command(ToCSV),
whole_stream_command(ToHTML),
whole_stream_command(ToJSON),
whole_stream_command(ToCsv),
whole_stream_command(ToHtml),
whole_stream_command(ToJson),
whole_stream_command(ToMarkdown),
whole_stream_command(ToTOML),
whole_stream_command(ToTSV),
whole_stream_command(ToURL),
whole_stream_command(ToYAML),
whole_stream_command(ToXML),
whole_stream_command(ToToml),
whole_stream_command(ToTsv),
whole_stream_command(ToUrl),
whole_stream_command(ToYaml),
whole_stream_command(ToXml),
// File format input
whole_stream_command(From),
whole_stream_command(FromCSV),
whole_stream_command(FromEML),
whole_stream_command(FromTSV),
whole_stream_command(FromSSV),
whole_stream_command(FromINI),
whole_stream_command(FromJSON),
whole_stream_command(FromODS),
whole_stream_command(FromTOML),
whole_stream_command(FromURL),
whole_stream_command(FromXLSX),
whole_stream_command(FromXML),
whole_stream_command(FromYAML),
whole_stream_command(FromYML),
whole_stream_command(FromCsv),
whole_stream_command(FromEml),
whole_stream_command(FromTsv),
whole_stream_command(FromSsv),
whole_stream_command(FromIni),
whole_stream_command(FromJson),
whole_stream_command(FromOds),
whole_stream_command(FromToml),
whole_stream_command(FromUrl),
whole_stream_command(FromXlsx),
whole_stream_command(FromXml),
whole_stream_command(FromYaml),
whole_stream_command(FromYml),
whole_stream_command(FromIcs),
whole_stream_command(FromVcf),
// "Private" commands (not intended to be accessed directly)
@ -233,9 +237,9 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(PathDirname),
whole_stream_command(PathExists),
whole_stream_command(PathExpand),
whole_stream_command(PathExtension),
whole_stream_command(PathFilestem),
whole_stream_command(PathJoin),
whole_stream_command(PathParse),
whole_stream_command(PathSplit),
whole_stream_command(PathType),
// Url
whole_stream_command(UrlCommand),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,16 +4,9 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
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 {
"from csv"
}
@ -37,8 +30,8 @@ impl WholeStreamCommand for FromCSV {
"Parse text as .csv and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_csv(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_csv(args)
}
fn examples(&self) -> Vec<Example> {
@ -67,16 +60,14 @@ impl WholeStreamCommand for FromCSV {
}
}
async fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let args = args.evaluate_once()?;
let noheaders = args.has_flag("noheaders");
let separator: Option<Value> = args.get_flag("separator")?;
let input = args.input;
let (
FromCSVArgs {
noheaders,
separator,
},
input,
) = args.process().await?;
let sep = match separator {
Some(Value {
value: UntaggedValue::Primitive(Primitive::String(s)),
@ -100,18 +91,18 @@ 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)]
mod tests {
use super::FromCSV;
use super::FromCsv;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromCSV {})
test_examples(FromCsv {})
}
}

View File

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

View File

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

View File

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

View File

@ -4,10 +4,9 @@ use nu_errors::ShellError;
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
use std::collections::HashMap;
pub struct FromINI;
pub struct FromIni;
#[async_trait]
impl WholeStreamCommand for FromINI {
impl WholeStreamCommand for FromIni {
fn name(&self) -> &str {
"from ini"
}
@ -20,8 +19,8 @@ impl WholeStreamCommand for FromINI {
"Parse text as .ini and create table"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_ini(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_ini(args)
}
}
@ -60,18 +59,18 @@ pub fn from_ini_string_to_value(
Ok(convert_ini_top_to_nu_value(&v, tag))
}
async fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.name_tag();
let input = args.input;
let concat_string = input.collect_string(tag.clone()).await?;
let concat_string = input.collect_string(tag.clone())?;
match from_ini_string_to_value(concat_string.item, tag.clone()) {
Ok(x) => match x {
Value {
value: UntaggedValue::Table(list),
..
} => Ok(futures::stream::iter(list).to_output_stream()),
} => Ok(list.into_iter().to_output_stream()),
x => Ok(OutputStream::one(x)),
},
Err(_) => Err(ShellError::labeled_error_with_secondary(
@ -86,13 +85,13 @@ async fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
#[cfg(test)]
mod tests {
use super::FromINI;
use super::FromIni;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromINI {})
test_examples(FromIni {})
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -4,15 +4,9 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
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 {
"from tsv"
}
@ -29,27 +23,29 @@ impl WholeStreamCommand for FromTSV {
"Parse text as .tsv and create table."
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_tsv(args).await
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
from_tsv(args)
}
}
async fn from_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn from_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let (FromTSVArgs { noheaders }, input) = args.process().await?;
let args = args.evaluate_once()?;
let noheaders = args.has_flag("noheaders");
let input = args.input;
from_delimited_data(noheaders, '\t', "TSV", input, name).await
from_delimited_data(noheaders, '\t', "TSV", input, name)
}
#[cfg(test)]
mod tests {
use super::FromTSV;
use super::FromTsv;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(FromTSV {})
test_examples(FromTsv {})
}
}

View File

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

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