Compare commits

...

104 Commits

Author SHA1 Message Date
JT
7bf10b980c Update Cargo.lock (#3527) 2021-06-01 20:11:21 +12:00
JT
55c243a17b Update Cargo.toml (#3526) 2021-06-01 19:32:44 +12:00
JT
df526f73be Bump to 0.32 (#3521)
* Bump to 0.32

* Bump to 0.32
2021-06-01 08:14:50 +12:00
29a77fd6ae Bump rusqlite from 0.24.2 to 0.25.3 (#3523) 2021-06-01 07:34:51 +12:00
be9ebd9e18 fix: filename quoting # and update and unify surf dependency (#3524)
* fix: filenames with '#' don't get quoted #3496

* updating and unifying dependency surf

* adding hyper-client

Co-authored-by: ahkrr <alexhk@protonmail.com>
2021-05-31 10:22:46 -05:00
01f1208ad1 to sqlite: Fix panic caused by empty tables (#3522) 2021-05-31 21:46:07 +12:00
9dbb3e80fe feat: add attribute selection to nu_plugion_selector (#3519)
This allows the user to specify for example 
selector a -t href 
to downselect based on an attribute

Co-authored-by: ahkrr <alexhk@protonmail.com>
2021-05-30 11:49:43 -05:00
a5c14ba7d4 Ensure correct partial key=value flag. (#3518) 2021-05-29 23:19:58 -05:00
4b11b283ac Resolve issues with rm * globbing (#3516)
Using the `*` wildcard should not attempt to delete files with a leading dot
unless the more explicit `.*` is used. `rm *` should also not attempt to delete
the current directory or its parent directory (`.` and `..`). I have resolved
this bug as well in a less satisfactory way. I think it may be the case that we
can only disambiguate the `.` and `..` path segments by using `Path::display`.
Here is a short list of alternatives that I tried:

- `Path::ends_with()` can detect `/..` but not `/.`.
- `Path::iter()` and `Path::components()` leave out `/.`.
- `Path::file_name()` normalizes `/.` to the parent component's file name.

Fixes #3508
2021-05-30 15:36:36 +12:00
6fdfc84904 Parse key=value named flag support. (#3515) 2021-05-29 20:42:03 -05:00
JT
bff81f24aa Autogenerate missing docs (#3514)
* Autogenerate missing docs

* Update ansi.md

* Rename question mark command docs

* Delete empty?.md
2021-05-30 12:57:04 +12:00
ed515cbc0c update keybindings to support new rustyline functionality (#3511)
* update keybindings to support new rustyline functionality

* remove some keybindings comments

* fix wasm build

* fixed multiline editing binding
2021-05-28 15:10:04 -05:00
87a6d7166c remove expect so that config doesn't fail (#3510) 2021-05-29 05:23:15 +12:00
JT
55baee9a9a Cleanup let varname and rhs (#3507) 2021-05-28 19:48:54 +12:00
JT
0886afe650 Fix for in (#3506)
* Fix for..in examples

* Fix for..in examples
2021-05-28 11:20:33 +12:00
JT
872f6166e1 Add for..in command (#3504) 2021-05-28 10:32:45 +12:00
fe348e236f Convert do command to engine-p; Fix flag name (#3503)
Renamed "ignore_errors" to "ignore-errors" to be aligned with nushell's
naming conventions.
2021-05-28 10:12:52 +12:00
e0f083d117 fix polars compile warnings (#3501) 2021-05-27 12:30:46 -05:00
48171f8e24 remove str from (#3500) 2021-05-27 12:18:02 -05:00
bcdf74562b remove into int references (#3499) 2021-05-27 11:35:25 -05:00
3a5ee1aed0 Dataframe commands (#3498)
* Sample command

* Join command with checks

* More dataframes commands

* Groupby and aggregate commands

* Missing feature dataframe flag

* Renamed file
2021-05-27 17:09:48 +12:00
d8c4b9c4fb add locale for ls size (#3497)
* add locale for ls size

* updated to work when it's not an exact match like de_DE when it needs de
2021-05-27 11:02:24 +12:00
6ae7884786 Fix path dots expansion (#3491)
* Fix parser expanding dots where it shouldn't

Previously, the parser would expand "a...b" as "a../..b". Now, >2 dots
are only expanded when the whole path component consists of dots (i.e.,
"..." expands to "../.." while "a...b" stays as it is).

* Respect OS separator when expanding >2 dots

"..." now expands to either "../.." or "..\..", based on the host OS.
2021-05-26 20:17:18 +12:00
JT
41834d16d6 Allow aliases to expand and ignore painting outside of lines (#3492) 2021-05-26 17:58:32 +12:00
1ee51f2afa Add the load-env command (#3484)
* Add the load-env command

load-env can be used to add environment variables dynamically via an
InputStream. This allows developers to create tools that output environment
variables as key-value pairs, then have the user load those variables in using
load-env. This supplants most of the need for an `eval` command, which is
mostly used in POSIX envs for setting env vars.

Fixes #3481

* fixup! Add the load-env command
2021-05-26 06:18:20 +12:00
65ee7aa372 correctly escape pipe in windows/cmd.exe (#3489)
* correctly escape pipe in windows/cmd.exe

* add some comments, take out debug line
2021-05-25 09:19:45 -05:00
ac38ee82f4 error message cleanup for into string (#3488) 2021-05-25 07:49:12 -05:00
JT
5fcc7f2328 Fix bad operator (#3479) 2021-05-24 17:27:10 +12:00
3bcc2aad80 Pass command's span correctly when reporting unexpected flags. (#3478)
Not all lite command's first part denotes a real command (for cases like sub commands that it's second lite part accounts for the command name). This is important so that we can refer to it's span correctly. Here we fix to include the command's name span correctly whether it's a command or sub command when reporting missing flag errors.
2021-05-23 19:30:30 -05:00
6165b6ae77 Add params to do (#3477) 2021-05-24 09:21:41 +12:00
e335e4fddc Groupby operations on dataframes (#3473)
* Added PolarsStruct enum to implement groupby

* template groupby

* groupby operationi on dataframes
2021-05-23 19:37:04 +12:00
5ab4199d71 Add path separator to char; Update char to engine-p; List all names of all possible chars (#3470)
* Allow querying the current path separator

* Convert char command to engine-p

* Wrap char args into struct

* Add --list option to char command

This lists all the available character names, along with the character
and its unicode points.
2021-05-22 11:48:33 -05:00
f075e2459d Commands to engine (#3448)
* commands to engine

* Correction of error in parser

* Added detailed regex error to parse

* better regex error parsing

* clippy corrections

* parse example with test

* secondary error for regex

* removed clone in error parser

* Secondary error message
2021-05-22 10:52:04 -05:00
94a26abf21 Implement path relative-to subcommand (#3461)
* Register new path relative-to command

* Implement `path relative-to` subcommand
2021-05-22 09:29:40 -05:00
bcbdc33049 Use enginep style in enter command (#3469) 2021-05-22 09:27:42 -05:00
21ef3895b3 delete crates/nu-command/src/commands/date/utc.rs (#3464)
The `date utc` command was removed in this PR:

https://github.com/nushell/nushell/pull/2780

The file was left but is no longer referenced from the parent module
and was not used.

Co-authored-by: Henrik Sjööh <henrik.sjooh@configura.com>
2021-05-22 17:09:50 +12:00
JT
3e99dc01b0 let date commands pull default date (#3463) 2021-05-22 17:02:06 +12:00
3e325a1974 update min rust version (#3462)
Co-authored-by: Henrik Sjööh <henrik.sjooh@configura.com>
2021-05-22 16:06:33 +12:00
2b92e3e8a7 port group-by to engine-p (#3458)
* migrate group-by to engine p

Part of #3390.

* consume positional argument correctly
2021-05-21 21:19:43 -05:00
cb90b90cbf nothing converted to string should return nothing and not fail (#3459) 2021-05-21 11:06:53 -05:00
9776a252ee add addition characters that can be hard to work with in nushell (#3457) 2021-05-21 08:04:48 -05:00
JT
751de20f93 Do a bit more cleanup of block params (#3455)
* Do a bit more cleanup of block params

* Do a bit more cleanup of block params
2021-05-21 19:04:27 +12:00
JT
28388b4e3a Split unit into duration and filesize (#3453) 2021-05-21 13:21:46 +12:00
JT
4fdbf30308 Paren interpolation (#3452)
* Switch interp to use parens

* improve interp parsing
2021-05-21 10:55:38 +12:00
722f191e82 updated round to support i64 (#3451) 2021-05-20 13:59:19 -05:00
JT
20f6114617 Improve block params (#3450) 2021-05-20 16:26:54 +12:00
3075e2cfbf Remove rest_args() from evaluated CommandArgs (#3449)
It was too error prone when positional arguments were used with the rest
arguments. Now, you need to explicitly state from which position you
want to count the rest args (e.g., `rest(0)`).
2021-05-20 10:26:23 +12:00
JT
e2973d2176 Add explicit block params (#3444)
* Add explicit block params

* Add explicit block params
2021-05-19 20:23:45 +12:00
JT
0ff08bb63a Just treat u64 like i64 for now (#3442) 2021-05-19 09:32:37 +12:00
08c0bf52bc Fix path join argument type and a typo (#3441) 2021-05-19 09:30:37 +12:00
d0229cb96e Load parquet and json files (#3437)
* Load parquet and json files

* changed csv file error
2021-05-19 07:33:10 +12:00
0612e5ccfb updated to the latest rustyline (#3439) 2021-05-18 12:36:55 -05:00
1b4f7b34c8 don't let externals break ansi escapes (#3438) 2021-05-17 18:01:34 -05:00
86e6fcd309 Negative indexing for range (#3427)
* adds negative indexing to range

* fixes tests to reflect new parsing changes

* removes duplicate definitons

* fmt
2021-05-17 15:08:47 +12:00
dc9cd7d8b9 Add Support for Partial Completions (#3432)
This commit adds a conditional event handler that inserts the next word of the
hint text when the user presses control and right arrow.
2021-05-16 08:43:43 +12:00
c0cc9ce7cd Dataframe new commands (#3425)
* Folder for dataframe commands

* New commands for dataframe
2021-05-15 19:24:11 +12:00
be2f66397b re-enable ansi support when externals break it (#3429) 2021-05-15 16:11:21 +12:00
07760b4129 Commands to engine p (#3426)
* hash and into converted

* keep command to engine p

* Update int.rs

Co-authored-by: JT <jonathandturner@users.noreply.github.com>
2021-05-15 16:11:07 +12:00
JT
d79a3130b8 Make the default int an i64 (#3428)
* Make the default int an i64

* fmt

* Fix random integer

* Treat pids as i64 for now
2021-05-14 20:35:09 +12:00
440a589f9e changed comment slightly 2021-05-13 14:21:16 -05:00
f5fcf9d635 Dataframe feature for plugins (#3424) 2021-05-13 21:49:46 +12:00
JT
9b8b1bad57 Don't insert PATH variable on Windows (#3422)
* Don't insert PATH variable on Windows

* Simplify fix

* Just centralize the var

* Add a message about why we have to workaround the issue
2021-05-13 15:03:49 +12:00
874ecd6c88 Made completion matching case-insensitive by default for windows (#3420) 2021-05-13 11:38:43 +12:00
JT
57e2fec497 Update LICENSE 2021-05-13 07:44:36 +12:00
0905a2c3a2 commands to engine p (#3417)
* commands to engine p

* Clippy suggestion

* Clippy suggestion
2021-05-13 07:07:20 +12:00
JT
3aa00b78f9 Improve missing var in var-path error (#3415) 2021-05-12 20:53:34 +12:00
JT
6769d46dbb Do less work in sys (#3413) 2021-05-12 17:24:22 +12:00
JT
efac712f62 Fix string interp/shorthand overlap (#3412) 2021-05-12 16:20:29 +12:00
JT
2bb23c57df Bump to 0.31.1 (#3411) 2021-05-12 15:06:50 +12:00
bc699a2cc1 date commands ported (#3410) 2021-05-12 14:01:49 +12:00
758c128147 Config commands to engine p (#3408)
* config get command

* config remove command

* config set command

* config command

* config commands
2021-05-12 14:01:16 +12:00
3795c2a39d Migrate last to engine-p (#3406)
* migrates last to engine-p

* removes unused import

* linter fix

* switch to as_usize()
2021-05-12 13:59:08 +12:00
JT
311c0e3f50 Simplify string interpolation (#3401)
* [DRAFT] simplify string interpolation

* Fix test
2021-05-12 13:53:57 +12:00
JT
25a8caa9b0 Simplify expressions (#3389)
* WIP: experiment with simpler expressions

* fix simple invoke

* update tests

* fix a few tests

* Make paren parsing more robust

* fix external args

* Remove old invocation

* Update tests

* Update tests
2021-05-12 13:01:48 +12:00
c80a9585b0 Complete Dataframe MVP (#3373)
* Dataframe MVP

* Removed test csv file

* Dataframe MVP

* Removed test csv file

* New revision polars

* New revision polars

* csv file reader

* argument parser for file reader

* Parser from Row primitive

* Column conversion

* Added as f32 and f64

* Parsing row to dataframe

* Removed repeated push to vector

* Accept table values to create dataframe

* Removed default serde

* Dataframe to rows to show data

* Save name of file with dataframe

* Usage example

* Upgrade polars version

* Clippy changes

* Added print function with head and tail

* Move dataframe struct to folder

* Lock file after running tests and merge

* Optional feature for dataframe

* Removed dataframe from plugins

* Update primitive.rs

Co-authored-by: JT <jonathandturner@users.noreply.github.com>
2021-05-12 13:01:31 +12:00
e73491441a make seq more nu-like by returning numbers when possible (#3409) 2021-05-11 12:02:16 -05:00
b93b80ccaa Remove unnecessary work from ps (#3407)
The ps plugin has an unnecessary call to `std:🧵:sleep(500ms)` that
delays runtime by 500ms. Additionally, there is a call to
`sysinfo::SystemExt::refresh_process`, which is not required as the previous
call to sysinfo::SystemExt::refresh_all handles the "refresh" of all processes
without a need to do this individually. Combining both of these improvements
means that running ps runs nearly instentaneously.

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

* Change ansi strip command

* Change benchmark to engine-p

* ansi strip removed arg.process()

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

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

* Convert "random dice" to engine-p

* Convert "random uuid" to engine-p

* Covert "random chars" to engine-p

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

* Port random integer to enginep; New FromValue impl

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

* Make sure range value extraction fails properly

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

* Port random decimal to enginep & Refactor

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

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

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

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

* Fix styling issue

* Try something else by copying existing matching code

* Fix formatting

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

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

* Remove errors that are not used

* Fix formatting

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

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

* updated parameter name, updated examples

* fixed nu-pretty-hex test

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

* Fix wrong return value

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

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

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

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

* Fix external redirection
2021-04-22 08:54:34 +12:00
482 changed files with 13699 additions and 4442 deletions

1759
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ license = "MIT"
name = "nu" name = "nu"
readme = "README.md" readme = "README.md"
repository = "https://github.com/nushell/nushell" repository = "https://github.com/nushell/nushell"
version = "0.30.0" version = "0.32.0"
[workspace] [workspace]
members = ["crates/*/"] members = ["crates/*/"]
@ -18,36 +18,36 @@ members = ["crates/*/"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
nu-cli = { version = "0.30.0", path = "./crates/nu-cli", default-features = false } nu-cli = { version = "0.32.0", path = "./crates/nu-cli", default-features = false }
nu-command = { version = "0.30.0", path = "./crates/nu-command" } nu-command = { version = "0.32.0", path = "./crates/nu-command" }
nu-data = { version = "0.30.0", path = "./crates/nu-data" } nu-data = { version = "0.32.0", path = "./crates/nu-data" }
nu-engine = { version = "0.30.0", path = "./crates/nu-engine" } nu-engine = { version = "0.32.0", path = "./crates/nu-engine" }
nu-errors = { version = "0.30.0", path = "./crates/nu-errors" } nu-errors = { version = "0.32.0", path = "./crates/nu-errors" }
nu-parser = { version = "0.30.0", path = "./crates/nu-parser" } nu-parser = { version = "0.32.0", path = "./crates/nu-parser" }
nu-plugin = { version = "0.30.0", path = "./crates/nu-plugin" } nu-plugin = { version = "0.32.0", path = "./crates/nu-plugin" }
nu-protocol = { version = "0.30.0", path = "./crates/nu-protocol" } nu-protocol = { version = "0.32.0", path = "./crates/nu-protocol" }
nu-source = { version = "0.30.0", path = "./crates/nu-source" } nu-source = { version = "0.32.0", path = "./crates/nu-source" }
nu-value-ext = { version = "0.30.0", path = "./crates/nu-value-ext" } nu-value-ext = { version = "0.32.0", path = "./crates/nu-value-ext" }
nu_plugin_binaryview = { version = "0.30.0", path = "./crates/nu_plugin_binaryview", optional = true } nu_plugin_binaryview = { version = "0.32.0", path = "./crates/nu_plugin_binaryview", optional = true }
nu_plugin_chart = { version = "0.30.0", path = "./crates/nu_plugin_chart", optional = true } nu_plugin_chart = { version = "0.32.0", path = "./crates/nu_plugin_chart", optional = true }
nu_plugin_fetch = { version = "0.30.0", path = "./crates/nu_plugin_fetch", optional = true } nu_plugin_fetch = { version = "0.32.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_bson = { version = "0.32.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_from_sqlite = { version = "0.32.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_inc = { version = "0.32.0", path = "./crates/nu_plugin_inc", optional = true }
nu_plugin_match = { version = "0.30.0", path = "./crates/nu_plugin_match", optional = true } nu_plugin_match = { version = "0.32.0", path = "./crates/nu_plugin_match", optional = true }
nu_plugin_post = { version = "0.30.0", path = "./crates/nu_plugin_post", optional = true } nu_plugin_post = { version = "0.32.0", path = "./crates/nu_plugin_post", optional = true }
nu_plugin_ps = { version = "0.30.0", path = "./crates/nu_plugin_ps", optional = true } nu_plugin_ps = { version = "0.32.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_query_json = { version = "0.32.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_s3 = { version = "0.32.0", path = "./crates/nu_plugin_s3", optional = true }
nu_plugin_selector = { version = "0.30.0", path = "./crates/nu_plugin_selector", optional = true } nu_plugin_selector = { version = "0.32.0", path = "./crates/nu_plugin_selector", optional = true }
nu_plugin_start = { version = "0.30.0", path = "./crates/nu_plugin_start", optional = true } nu_plugin_start = { version = "0.32.0", path = "./crates/nu_plugin_start", optional = true }
nu_plugin_sys = { version = "0.30.0", path = "./crates/nu_plugin_sys", optional = true } nu_plugin_sys = { version = "0.32.0", path = "./crates/nu_plugin_sys", optional = true }
nu_plugin_textview = { version = "0.30.0", path = "./crates/nu_plugin_textview", optional = true } nu_plugin_textview = { version = "0.32.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_bson = { version = "0.32.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_to_sqlite = { version = "0.32.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_tree = { version = "0.32.0", path = "./crates/nu_plugin_tree", optional = true }
nu_plugin_xpath = { version = "0.30.0", path = "./crates/nu_plugin_xpath", optional = true } nu_plugin_xpath = { version = "0.32.0", path = "./crates/nu_plugin_xpath", optional = true }
# Required to bootstrap the main binary # Required to bootstrap the main binary
clap = "2.33.3" clap = "2.33.3"
@ -58,7 +58,7 @@ log = "0.4.14"
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4.0"
[dev-dependencies] [dev-dependencies]
nu-test-support = { version = "0.30.0", path = "./crates/nu-test-support" } nu-test-support = { version = "0.32.0", path = "./crates/nu-test-support" }
dunce = "1.0.1" dunce = "1.0.1"
serial_test = "0.5.1" serial_test = "0.5.1"
hamcrest2 = "0.3.0" hamcrest2 = "0.3.0"
@ -81,13 +81,7 @@ ptree-support = ["nu-cli/ptree", "nu-command/ptree"]
rustyline-support = ["nu-cli/rustyline-support", "nu-command/rustyline-support"] rustyline-support = ["nu-cli/rustyline-support", "nu-command/rustyline-support"]
term-support = ["nu-cli/term", "nu-command/term"] term-support = ["nu-cli/term", "nu-command/term"]
uuid-support = ["nu-cli/uuid_crate", "nu-command/uuid_crate"] uuid-support = ["nu-cli/uuid_crate", "nu-command/uuid_crate"]
which-support = [ which-support = ["nu-cli/which", "nu-command/which", "nu-engine/which"]
"nu-cli/ichwh",
"nu-cli/which",
"nu-command/ichwh",
"nu-command/which",
"nu-engine/which",
]
default = [ default = [
"nu-cli/shadow-rs", "nu-cli/shadow-rs",
@ -160,6 +154,17 @@ zip-support = ["nu-cli/zip", "nu-command/zip"]
#This is disabled in extra for now #This is disabled in extra for now
table-pager = ["nu-command/table-pager"] table-pager = ["nu-command/table-pager"]
#dataframe feature for nushell
dataframe = [
"nu-protocol/dataframe",
"nu-command/dataframe",
"nu-value-ext/dataframe",
"nu-data/dataframe",
"nu_plugin_post/dataframe",
"nu_plugin_to_bson/dataframe",
]
[profile.release] [profile.release]
#strip = "symbols" #Couldn't get working +nightly #strip = "symbols" #Couldn't get working +nightly
codegen-units = 1 #Reduce parallel codegen units codegen-units = 1 #Reduce parallel codegen units

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2019 - 2020 Yehuda Katz, Jonathan Turner Copyright (c) 2019 - 2021 Yehuda Katz, Jonathan Turner
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -47,7 +47,7 @@ Try it in Gitpod.
Up-to-date installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). **Windows users**: please note that Nu works on Windows 10 and does not currently have Windows 7/8.1 support. Up-to-date installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). **Windows users**: please note that Nu works on Windows 10 and does not currently have Windows 7/8.1 support.
To build Nu, you will need to use the **latest stable (1.47 or later)** version of the compiler. To build Nu, you will need to use the **latest stable (1.51 or later)** version of the compiler.
Required dependencies: Required dependencies:
@ -220,7 +220,7 @@ We can pipeline this into a command that gets the contents of one of the columns
name │ nu name │ nu
readme │ README.md readme │ README.md
repository │ https://github.com/nushell/nushell repository │ https://github.com/nushell/nushell
version │ 0.21.0 version │ 0.32.0
───────────────┴──────────────────────────────────── ───────────────┴────────────────────────────────────
``` ```
@ -228,7 +228,7 @@ Finally, we can use commands outside of Nu once we have the data we want:
```shell ```shell
> open Cargo.toml | get package.version > open Cargo.toml | get package.version
0.21.0 0.32.0
``` ```
Here we use the variable `$it` to refer to the value being piped to the external command. Here we use the variable `$it` to refer to the value being piped to the external command.

View File

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

View File

@ -5,26 +5,27 @@ description = "CLI for nushell"
edition = "2018" edition = "2018"
license = "MIT" license = "MIT"
name = "nu-cli" name = "nu-cli"
version = "0.30.0" version = "0.32.0"
[lib] [lib]
doctest = false doctest = false
[dependencies] [dependencies]
nu-command = { version = "0.30.0", path = "../nu-command" } nu-command = { version = "0.32.0", path = "../nu-command" }
nu-data = { version = "0.30.0", path = "../nu-data" } nu-data = { version = "0.32.0", path = "../nu-data" }
nu-engine = { version = "0.30.0", path = "../nu-engine" } nu-engine = { version = "0.32.0", path = "../nu-engine" }
nu-errors = { version = "0.30.0", path = "../nu-errors" } nu-errors = { version = "0.32.0", path = "../nu-errors" }
nu-json = { version = "0.30.0", path = "../nu-json" } nu-json = { version = "0.32.0", path = "../nu-json" }
nu-parser = { version = "0.30.0", path = "../nu-parser" } nu-parser = { version = "0.32.0", path = "../nu-parser" }
nu-plugin = { version = "0.30.0", path = "../nu-plugin" } nu-plugin = { version = "0.32.0", path = "../nu-plugin" }
nu-protocol = { version = "0.30.0", path = "../nu-protocol" } nu-protocol = { version = "0.32.0", path = "../nu-protocol" }
nu-source = { version = "0.30.0", path = "../nu-source" } nu-source = { version = "0.32.0", path = "../nu-source" }
nu-stream = { version = "0.30.0", path = "../nu-stream" } nu-stream = { version = "0.32.0", path = "../nu-stream" }
nu-table = { version = "0.30.0", path = "../nu-table" } nu-table = { version = "0.32.0", path = "../nu-table" }
nu-test-support = { version = "0.30.0", path = "../nu-test-support" } nu-test-support = { version = "0.32.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.30.0", path = "../nu-value-ext" } nu-value-ext = { version = "0.32.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.30.0", path = "../nu-ansi-term" } nu-ansi-term = { version = "0.32.0", path = "../nu-ansi-term" }
nu-pretty-hex = { version = "0.32.0", path = "../nu-pretty-hex" }
Inflector = "0.11" Inflector = "0.11"
arboard = { version = "1.1.0", optional = true } arboard = { version = "1.1.0", optional = true }
@ -57,7 +58,6 @@ getset = "0.1.1"
glob = "0.3.0" glob = "0.3.0"
htmlescape = "0.3.1" htmlescape = "0.3.1"
ical = "0.7.0" ical = "0.7.0"
ichwh = { version = "0.3.4", optional = true }
indexmap = { version = "1.6.1", features = ["serde-1"] } indexmap = { version = "1.6.1", features = ["serde-1"] }
itertools = "0.10.0" itertools = "0.10.0"
lazy_static = "1.*" lazy_static = "1.*"
@ -68,7 +68,6 @@ num-format = { version = "0.4.0", features = ["with-num-bigint"] }
num-traits = "0.2.14" num-traits = "0.2.14"
parking_lot = "0.11.1" parking_lot = "0.11.1"
pin-utils = "0.1.0" pin-utils = "0.1.0"
pretty-hex = "0.2.1"
ptree = { version = "0.3.1", optional = true } ptree = { version = "0.3.1", optional = true }
query_interface = "0.3.5" query_interface = "0.3.5"
quick-xml = "0.21.0" quick-xml = "0.21.0"
@ -77,7 +76,7 @@ rayon = "1.5.0"
regex = "1.4.3" regex = "1.4.3"
roxmltree = "0.14.0" roxmltree = "0.14.0"
rust-embed = "5.9.0" rust-embed = "5.9.0"
rustyline = { version = "8.0.0", optional = true } rustyline = { version = "8.1.0", optional = true }
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.123", features = ["derive"] }
serde_bytes = "0.11.5" serde_bytes = "0.11.5"
serde_ini = "0.2.0" serde_ini = "0.2.0"
@ -116,7 +115,7 @@ users = "0.11.0"
[dependencies.rusqlite] [dependencies.rusqlite]
features = ["bundled", "blob"] features = ["bundled", "blob"]
optional = true optional = true
version = "0.24.2" version = "0.25.3"
[build-dependencies] [build-dependencies]
shadow-rs = "0.5" shadow-rs = "0.5"

View File

@ -229,13 +229,7 @@ pub fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn E
let prompt_line = prompt.as_string()?; let prompt_line = prompt.as_string()?;
context.scope.enter_scope(); context.scope.enter_scope();
let (mut prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope); let (prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
if let Some(block) =
std::sync::Arc::<nu_protocol::hir::Block>::get_mut(&mut prompt_block)
{
block.set_redirect(ExternalRedirection::Stdout);
}
if err.is_some() { if err.is_some() {
context.scope.exit_scope(); context.scope.exit_scope();
@ -250,7 +244,12 @@ pub fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn E
nu_ansi_term::ansi::RESET nu_ansi_term::ansi::RESET
) )
} else { } else {
let run_result = run_block(&prompt_block, &context, InputStream::empty()); let run_result = run_block(
&prompt_block,
&context,
InputStream::empty(),
ExternalRedirection::Stdout,
);
context.scope.exit_scope(); context.scope.exit_scope();
match run_result { match run_result {
@ -302,7 +301,9 @@ pub fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn E
} }
}; };
rl.helper_mut().expect("No helper").colored_prompt = colored_prompt; if let Some(helper) = rl.helper_mut() {
helper.colored_prompt = colored_prompt;
}
let mut initial_command = Some(String::new()); let mut initial_command = Some(String::new());
let mut readline = Err(ReadlineError::Eof); let mut readline = Err(ReadlineError::Eof);
while let Some(ref cmd) = initial_command { while let Some(ref cmd) = initial_command {
@ -485,7 +486,12 @@ pub fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, She
let input_stream = InputStream::empty(); let input_stream = InputStream::empty();
let result = run_block(&classified_block, ctx, input_stream); let result = run_block(
&classified_block,
ctx,
input_stream,
ExternalRedirection::Stdout,
);
ctx.scope.exit_scope(); ctx.scope.exit_scope();
result?.collect_string(Tag::unknown()).map(|x| x.item) result?.collect_string(Tag::unknown()).map(|x| x.item)
@ -509,14 +515,14 @@ fn current_branch() -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use nu_engine::basic_evaluation_context; use nu_engine::EvaluationContext;
#[quickcheck] #[quickcheck]
fn quickcheck_parse(data: String) -> bool { fn quickcheck_parse(data: String) -> bool {
let (tokens, err) = nu_parser::lex(&data, 0); let (tokens, err) = nu_parser::lex(&data, 0);
let (lite_block, err2) = nu_parser::parse_block(tokens); let (lite_block, err2) = nu_parser::parse_block(tokens);
if err.is_none() && err2.is_none() { if err.is_none() && err2.is_none() {
let context = basic_evaluation_context().unwrap(); let context = EvaluationContext::basic();
let _ = nu_parser::classify_block(&lite_block, &context.scope); let _ = nu_parser::classify_block(&lite_block, &context.scope);
} }
true true

View File

@ -6,6 +6,7 @@ use indexmap::set::IndexSet;
use super::matchers::Matcher; use super::matchers::Matcher;
use crate::completion::{Completer, CompletionContext, Suggestion}; use crate::completion::{Completer, CompletionContext, Suggestion};
use nu_engine::EvaluationContext; use nu_engine::EvaluationContext;
use nu_test_support::NATIVE_PATH_ENV_VAR;
pub struct CommandCompleter; pub struct CommandCompleter;
@ -121,7 +122,7 @@ fn is_executable(path: &Path) -> bool {
// TODO cache these, but watch for changes to PATH // TODO cache these, but watch for changes to PATH
fn find_path_executables() -> Option<IndexSet<String>> { fn find_path_executables() -> Option<IndexSet<String>> {
let path_var = std::env::var_os("PATH")?; let path_var = std::env::var_os(NATIVE_PATH_ENV_VAR)?;
let paths: Vec<_> = std::env::split_paths(&path_var).collect(); let paths: Vec<_> = std::env::split_paths(&path_var).collect();
let mut executables: IndexSet<String> = IndexSet::new(); let mut executables: IndexSet<String> = IndexSet::new();

View File

@ -37,7 +37,7 @@ impl<'s> Flatten<'s> {
) )
.collect(), .collect(),
Expression::Command => vec![LocationType::Command.spanned(e.span)], Expression::Command => vec![LocationType::Command.spanned(e.span)],
Expression::Path(path) => self.expression(&path.head), Expression::FullColumnPath(path) => self.expression(&path.head),
Expression::Variable(_, _) => vec![LocationType::Variable.spanned(e.span)], Expression::Variable(_, _) => vec![LocationType::Variable.spanned(e.span)],
Expression::Boolean(_) Expression::Boolean(_)
@ -384,7 +384,7 @@ mod tests {
#[test] #[test]
fn completes_incomplete_nested_structure() { fn completes_incomplete_nested_structure() {
let registry: VecRegistry = vec![Signature::build("sys")].into(); let registry: VecRegistry = vec![Signature::build("sys")].into();
let line = "echo $(sy"; let line = "echo (sy";
assert_eq!( assert_eq!(
completion_location(line, &registry, 8), completion_location(line, &registry, 8),

View File

@ -1,63 +1,42 @@
use rustyline::{KeyCode, Modifiers}; use rustyline::{KeyCode as RustyKeyCode, Modifiers};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub fn convert_keyevent(key_event: KeyEvent) -> rustyline::KeyEvent { pub fn convert_keyevent(key_event: KeyCode, modifiers: Option<Modifiers>) -> rustyline::KeyEvent {
match key_event { match key_event {
KeyEvent::UnknownEscSeq => convert_to_rl_keyevent(rustyline::KeyCode::UnknownEscSeq, None), KeyCode::UnknownEscSeq => convert_to_rl_keyevent(RustyKeyCode::UnknownEscSeq, modifiers),
KeyEvent::Backspace => convert_to_rl_keyevent(rustyline::KeyCode::Backspace, None), KeyCode::Backspace => convert_to_rl_keyevent(RustyKeyCode::Backspace, modifiers),
KeyEvent::BackTab => convert_to_rl_keyevent(rustyline::KeyCode::BackTab, None), KeyCode::BackTab => convert_to_rl_keyevent(RustyKeyCode::BackTab, modifiers),
KeyEvent::BracketedPasteStart => { KeyCode::BracketedPasteStart => {
convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteStart, None) convert_to_rl_keyevent(RustyKeyCode::BracketedPasteStart, modifiers)
} }
KeyEvent::BracketedPasteEnd => { KeyCode::BracketedPasteEnd => {
convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteEnd, None) convert_to_rl_keyevent(RustyKeyCode::BracketedPasteEnd, modifiers)
} }
KeyEvent::Char(c) => convert_to_rl_keyevent(rustyline::KeyCode::Char(c), None), KeyCode::Char(c) => convert_to_rl_keyevent(RustyKeyCode::Char(c), modifiers),
KeyEvent::ControlDown => { KeyCode::Delete => convert_to_rl_keyevent(RustyKeyCode::Delete, modifiers),
convert_to_rl_keyevent(rustyline::KeyCode::Down, Some(Modifiers::CTRL)) KeyCode::Down => convert_to_rl_keyevent(RustyKeyCode::Down, modifiers),
} KeyCode::End => convert_to_rl_keyevent(RustyKeyCode::End, modifiers),
KeyEvent::ControlLeft => { KeyCode::Enter => convert_to_rl_keyevent(RustyKeyCode::Enter, modifiers),
convert_to_rl_keyevent(rustyline::KeyCode::Left, Some(Modifiers::CTRL)) KeyCode::Esc => convert_to_rl_keyevent(RustyKeyCode::Esc, modifiers),
} KeyCode::F(u) => convert_to_rl_keyevent(RustyKeyCode::F(u), modifiers),
KeyEvent::ControlRight => { KeyCode::Home => convert_to_rl_keyevent(RustyKeyCode::Home, modifiers),
convert_to_rl_keyevent(rustyline::KeyCode::Right, Some(Modifiers::CTRL)) KeyCode::Insert => convert_to_rl_keyevent(RustyKeyCode::Insert, modifiers),
} KeyCode::Left => convert_to_rl_keyevent(RustyKeyCode::Left, modifiers),
KeyEvent::ControlUp => { KeyCode::Null => convert_to_rl_keyevent(RustyKeyCode::Null, modifiers),
convert_to_rl_keyevent(rustyline::KeyCode::Up, Some(Modifiers::CTRL)) KeyCode::PageDown => convert_to_rl_keyevent(RustyKeyCode::PageDown, modifiers),
} KeyCode::PageUp => convert_to_rl_keyevent(RustyKeyCode::PageUp, modifiers),
KeyEvent::Ctrl(c) => rustyline::KeyEvent::ctrl(c), KeyCode::Right => convert_to_rl_keyevent(RustyKeyCode::Right, modifiers),
KeyEvent::Delete => convert_to_rl_keyevent(rustyline::KeyCode::Delete, None), KeyCode::Tab => convert_to_rl_keyevent(RustyKeyCode::Tab, modifiers),
KeyEvent::Down => convert_to_rl_keyevent(rustyline::KeyCode::Down, None), KeyCode::Up => convert_to_rl_keyevent(RustyKeyCode::Up, modifiers),
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 { fn convert_to_rl_keyevent(
key_code: RustyKeyCode,
modifier: Option<Modifiers>,
) -> rustyline::KeyEvent {
rustyline::KeyEvent { rustyline::KeyEvent {
0: key_event, 0: key_code,
1: modifier.unwrap_or(Modifiers::NONE), 1: modifier.unwrap_or(Modifiers::NONE),
} }
} }
@ -132,12 +111,14 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
Cmd::Complete => rustyline::Cmd::Complete, Cmd::Complete => rustyline::Cmd::Complete,
Cmd::CompleteBackward => rustyline::Cmd::CompleteBackward, Cmd::CompleteBackward => rustyline::Cmd::CompleteBackward,
Cmd::CompleteHint => rustyline::Cmd::CompleteHint, Cmd::CompleteHint => rustyline::Cmd::CompleteHint,
Cmd::Dedent(movement) => rustyline::Cmd::Dedent(convert_movement(movement)),
Cmd::DowncaseWord => rustyline::Cmd::DowncaseWord, Cmd::DowncaseWord => rustyline::Cmd::DowncaseWord,
Cmd::EndOfFile => rustyline::Cmd::EndOfFile, Cmd::EndOfFile => rustyline::Cmd::EndOfFile,
Cmd::EndOfHistory => rustyline::Cmd::EndOfHistory, Cmd::EndOfHistory => rustyline::Cmd::EndOfHistory,
Cmd::ForwardSearchHistory => rustyline::Cmd::ForwardSearchHistory, Cmd::ForwardSearchHistory => rustyline::Cmd::ForwardSearchHistory,
Cmd::HistorySearchBackward => rustyline::Cmd::HistorySearchBackward, Cmd::HistorySearchBackward => rustyline::Cmd::HistorySearchBackward,
Cmd::HistorySearchForward => rustyline::Cmd::HistorySearchForward, Cmd::HistorySearchForward => rustyline::Cmd::HistorySearchForward,
Cmd::Indent(movement) => rustyline::Cmd::Indent(convert_movement(movement)),
Cmd::Insert { repeat, string } => rustyline::Cmd::Insert(repeat, string), Cmd::Insert { repeat, string } => rustyline::Cmd::Insert(repeat, string),
Cmd::Interrupt => rustyline::Cmd::Interrupt, Cmd::Interrupt => rustyline::Cmd::Interrupt,
Cmd::Kill(movement) => rustyline::Cmd::Kill(convert_movement(movement)), Cmd::Kill(movement) => rustyline::Cmd::Kill(convert_movement(movement)),
@ -145,8 +126,11 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
Cmd::LineUpOrPreviousHistory(u) => rustyline::Cmd::LineUpOrPreviousHistory(u), Cmd::LineUpOrPreviousHistory(u) => rustyline::Cmd::LineUpOrPreviousHistory(u),
Cmd::Move(movement) => rustyline::Cmd::Move(convert_movement(movement)), Cmd::Move(movement) => rustyline::Cmd::Move(convert_movement(movement)),
Cmd::NextHistory => rustyline::Cmd::NextHistory, Cmd::NextHistory => rustyline::Cmd::NextHistory,
Cmd::Newline => rustyline::Cmd::Newline,
Cmd::Noop => rustyline::Cmd::Noop, Cmd::Noop => rustyline::Cmd::Noop,
Cmd::Overwrite(c) => rustyline::Cmd::Overwrite(c), Cmd::Overwrite(c) => rustyline::Cmd::Overwrite(c),
#[cfg(windows)]
Cmd::PasteFromClipboard => rustyline::Cmd::PasteFromClipboard,
Cmd::PreviousHistory => rustyline::Cmd::PreviousHistory, Cmd::PreviousHistory => rustyline::Cmd::PreviousHistory,
Cmd::QuotedInsert => rustyline::Cmd::QuotedInsert, Cmd::QuotedInsert => rustyline::Cmd::QuotedInsert,
Cmd::Replace { Cmd::Replace {
@ -169,14 +153,28 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
} }
fn convert_keybinding(keybinding: Keybinding) -> (rustyline::KeyEvent, rustyline::Cmd) { fn convert_keybinding(keybinding: Keybinding) -> (rustyline::KeyEvent, rustyline::Cmd) {
let rusty_modifiers = match keybinding.modifiers {
Some(mods) => match mods {
NuModifiers::CTRL => Some(Modifiers::CTRL),
NuModifiers::ALT => Some(Modifiers::ALT),
NuModifiers::SHIFT => Some(Modifiers::SHIFT),
NuModifiers::NONE => Some(Modifiers::NONE),
NuModifiers::CTRL_SHIFT => Some(Modifiers::CTRL_SHIFT),
NuModifiers::ALT_SHIFT => Some(Modifiers::ALT_SHIFT),
NuModifiers::CTRL_ALT => Some(Modifiers::CTRL_ALT),
NuModifiers::CTRL_ALT_SHIFT => Some(Modifiers::CTRL_ALT_SHIFT),
// _ => None,
},
None => None,
};
( (
convert_keyevent(keybinding.key), convert_keyevent(keybinding.key, rusty_modifiers),
convert_cmd(keybinding.binding), convert_cmd(keybinding.binding),
) )
} }
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub enum KeyEvent { pub enum KeyCode {
/// Unsupported escape sequence (on unix platform) /// Unsupported escape sequence (on unix platform)
UnknownEscSeq, UnknownEscSeq,
/// ⌫ or `KeyEvent::Ctrl('H')` /// ⌫ or `KeyEvent::Ctrl('H')`
@ -189,16 +187,6 @@ pub enum KeyEvent {
BracketedPasteEnd, BracketedPasteEnd,
/// Single char /// Single char
Char(char), Char(char),
/// Ctrl-↓
ControlDown,
/// Ctrl-←
ControlLeft,
/// Ctrl-→
ControlRight,
/// Ctrl-↑
ControlUp,
/// Ctrl-char
Ctrl(char),
/// ⌦ /// ⌦
Delete, Delete,
/// ↓ arrow key /// ↓ arrow key
@ -217,9 +205,7 @@ pub enum KeyEvent {
Insert, Insert,
/// ← arrow key /// ← arrow key
Left, Left,
/// Escape-char or Alt-char // /// `KeyEvent::Char('\0')`
Meta(char),
/// `KeyEvent::Char('\0')`
Null, Null,
/// ⇟ /// ⇟
PageDown, PageDown,
@ -227,14 +213,6 @@ pub enum KeyEvent {
PageUp, PageUp,
/// → arrow key /// → arrow key
Right, Right,
/// Shift-↓
ShiftDown,
/// Shift-←
ShiftLeft,
/// Shift-→
ShiftRight,
/// Shift-↑
ShiftUp,
/// ⇥ or `KeyEvent::Ctrl('I')` /// ⇥ or `KeyEvent::Ctrl('I')`
Tab, Tab,
/// ↑ arrow key /// ↑ arrow key
@ -259,6 +237,8 @@ pub enum Cmd {
CompleteBackward, CompleteBackward,
/// complete-hint /// complete-hint
CompleteHint, CompleteHint,
/// Dedent current line
Dedent(Movement),
/// downcase-word /// downcase-word
DowncaseWord, DowncaseWord,
/// vi-eof-maybe /// vi-eof-maybe
@ -271,6 +251,8 @@ pub enum Cmd {
HistorySearchBackward, HistorySearchBackward,
/// history-search-forward /// history-search-forward
HistorySearchForward, HistorySearchForward,
/// Indent current line
Indent(Movement),
/// Insert text /// Insert text
Insert { repeat: RepeatCount, string: String }, Insert { repeat: RepeatCount, string: String },
/// Interrupt signal (Ctrl-C) /// Interrupt signal (Ctrl-C)
@ -283,12 +265,17 @@ pub enum Cmd {
/// forward-char, forward-word, vi-char-search, vi-end-word, vi-next-word, /// forward-char, forward-word, vi-char-search, vi-end-word, vi-next-word,
/// vi-prev-word /// vi-prev-word
Move(Movement), Move(Movement),
/// Inserts a newline
Newline,
/// next-history /// next-history
NextHistory, NextHistory,
/// No action /// No action
Noop, Noop,
/// vi-replace /// vi-replace
Overwrite(char), Overwrite(char),
/// Paste from the clipboard
#[cfg(windows)]
PasteFromClipboard,
/// previous-history /// previous-history
PreviousHistory, PreviousHistory,
/// quoted-insert /// quoted-insert
@ -422,12 +409,36 @@ pub enum CharSearch {
BackwardAfter(char), BackwardAfter(char),
} }
/// The set of modifier keys that were triggered along with a key press.
#[derive(Serialize, Deserialize)]
#[allow(non_camel_case_types)]
#[allow(clippy::clippy::upper_case_acronyms)]
pub enum NuModifiers {
/// Control modifier
CTRL = 8,
/// Escape or Alt modifier
ALT = 4,
/// Shift modifier
SHIFT = 2,
/// No modifier
NONE = 0,
/// Ctrl + Shift
CTRL_SHIFT = 10,
/// Alt + Shift
ALT_SHIFT = 6,
/// Ctrl + Alt
CTRL_ALT = 12,
/// Ctrl + Alt + Shift
CTRL_ALT_SHIFT = 14,
}
/// The number of times one command should be repeated. /// The number of times one command should be repeated.
pub type RepeatCount = usize; pub type RepeatCount = usize;
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize)]
pub struct Keybinding { pub struct Keybinding {
key: KeyEvent, key: KeyCode,
modifiers: Option<NuModifiers>,
binding: Cmd, binding: Cmd,
} }
@ -442,7 +453,7 @@ pub(crate) fn load_keybindings(
// Silently fail if there is no file there // Silently fail if there is no file there
if let Ok(contents) = contents { if let Ok(contents) = contents {
let keybindings: Keybindings = serde_yaml::from_str(&contents)?; let keybindings: Keybindings = serde_yaml::from_str(&contents)?;
// eprint!("{}{}{}", keybindings.key, keybindings.mo);
for keybinding in keybindings.into_iter() { for keybinding in keybindings.into_iter() {
let (k, b) = convert_keybinding(keybinding); let (k, b) = convert_keybinding(keybinding);

View File

@ -8,7 +8,7 @@ use crate::prelude::*;
use nu_engine::script::LineResult; use nu_engine::script::LineResult;
#[cfg(feature = "rustyline-support")] #[cfg(feature = "rustyline-support")]
use crate::keybinding::{convert_keyevent, KeyEvent}; use crate::keybinding::{convert_keyevent, KeyCode};
#[cfg(feature = "rustyline-support")] #[cfg(feature = "rustyline-support")]
use crate::shell::Helper; use crate::shell::Helper;
@ -19,7 +19,8 @@ use rustyline::{
config::Configurer, config::Configurer,
config::{ColorMode, CompletionType, Config}, config::{ColorMode, CompletionType, Config},
error::ReadlineError, error::ReadlineError,
At, Cmd, Editor, Movement, Word, line_buffer::LineBuffer,
At, Cmd, ConditionalEventHandler, Editor, EventHandler, Modifiers, Movement, Word,
}; };
#[cfg(feature = "rustyline-support")] #[cfg(feature = "rustyline-support")]
@ -36,6 +37,34 @@ pub fn convert_rustyline_result_to_string(input: Result<String, ReadlineError>)
} }
} }
#[derive(Clone)]
#[cfg(feature = "rustyline-support")]
struct PartialCompleteHintHandler;
#[cfg(feature = "rustyline-support")]
impl ConditionalEventHandler for PartialCompleteHintHandler {
fn handle(
&self,
_evt: &rustyline::Event,
_n: rustyline::RepeatCount,
_positive: bool,
ctx: &rustyline::EventContext,
) -> Option<Cmd> {
Some(match ctx.hint_text() {
Some(hint_text) if ctx.pos() == ctx.line().len() => {
let mut line_buffer = LineBuffer::with_capacity(hint_text.len());
line_buffer.update(hint_text, 0);
line_buffer.move_to_next_word(At::AfterEnd, Word::Vi, 1);
let text = hint_text[0..line_buffer.pos()].to_string();
Cmd::Insert(1, text)
}
_ => Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
})
}
}
#[cfg(feature = "rustyline-support")] #[cfg(feature = "rustyline-support")]
pub fn default_rustyline_editor_configuration() -> Editor<Helper> { pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
#[cfg(windows)] #[cfg(windows)]
@ -50,18 +79,20 @@ pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
let mut rl: Editor<_> = Editor::with_config(config); let mut rl: Editor<_> = Editor::with_config(config);
// add key bindings to move over a whole word with Ctrl+ArrowLeft and Ctrl+ArrowRight // add key bindings to move over a whole word with Ctrl+ArrowLeft and Ctrl+ArrowRight
//M modifier, E KeyEvent, K KeyCode
rl.bind_sequence( rl.bind_sequence(
convert_keyevent(KeyEvent::ControlLeft), convert_keyevent(KeyCode::Left, Some(Modifiers::CTRL)),
Cmd::Move(Movement::BackwardWord(1, Word::Vi)), Cmd::Move(Movement::BackwardWord(1, Word::Vi)),
); );
rl.bind_sequence( rl.bind_sequence(
convert_keyevent(KeyEvent::ControlRight), convert_keyevent(KeyCode::Right, Some(Modifiers::CTRL)),
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)), EventHandler::Conditional(Box::new(PartialCompleteHintHandler)),
); );
// workaround for multiline-paste hang in rustyline (see https://github.com/kkawakam/rustyline/issues/202) // workaround for multiline-paste hang in rustyline (see https://github.com/kkawakam/rustyline/issues/202)
rl.bind_sequence( rl.bind_sequence(
convert_keyevent(KeyEvent::BracketedPasteStart), convert_keyevent(KeyCode::BracketedPasteStart, None),
rustyline::Cmd::Noop, rustyline::Cmd::Noop,
); );
// Let's set the defaults up front and then override them later if the user indicates // Let's set the defaults up front and then override them later if the user indicates

View File

@ -44,6 +44,10 @@ impl NuCompleter {
let matcher = matcher.as_str(); let matcher = matcher.as_str();
let matcher: &dyn Matcher = match matcher { let matcher: &dyn Matcher = match matcher {
"case-insensitive" => &matchers::case_insensitive::Matcher, "case-insensitive" => &matchers::case_insensitive::Matcher,
"case-sensitive" => &matchers::case_sensitive::Matcher,
#[cfg(target_os = "windows")]
_ => &matchers::case_insensitive::Matcher,
#[cfg(not(target_os = "windows"))]
_ => &matchers::case_sensitive::Matcher, _ => &matchers::case_sensitive::Matcher,
}; };
@ -134,7 +138,7 @@ fn requote(orig_value: String) -> String {
let mut quotes = vec!['"', '\'', '`']; let mut quotes = vec!['"', '\'', '`'];
let mut should_quote = false; let mut should_quote = false;
for c in value.chars() { for c in value.chars() {
if c.is_whitespace() { if c.is_whitespace() || c == '#' {
should_quote = true; should_quote = true;
} else if let Some(index) = quotes.iter().position(|q| *q == c) { } else if let Some(index) = quotes.iter().position(|q| *q == c) {
should_quote = true; should_quote = true;
@ -145,7 +149,7 @@ fn requote(orig_value: String) -> String {
if should_quote { if should_quote {
if quotes.is_empty() { if quotes.is_empty() {
// TODO we don't really have an escape character, so there isn't a great option right // TODO we don't really have an escape character, so there isn't a great option right
// now. One possibility is `{{$(char backtick)}}` // now. One possibility is `{{(char backtick)}}`
value.to_string() value.to_string()
} else { } else {
let quote = quotes[0]; let quote = quotes[0];

View File

@ -150,7 +150,7 @@ impl rustyline::Helper for Helper {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use nu_engine::basic_evaluation_context; use nu_engine::EvaluationContext;
use rustyline::completion::Completer; use rustyline::completion::Completer;
use rustyline::line_buffer::LineBuffer; use rustyline::line_buffer::LineBuffer;
@ -164,7 +164,7 @@ mod tests {
buffer.insert_str(0, text); buffer.insert_str(0, text);
buffer.set_pos(text.len() - 1); buffer.set_pos(text.len() - 1);
let helper = Helper::new(basic_evaluation_context().unwrap(), None); let helper = Helper::new(EvaluationContext::basic(), None);
helper.update(&mut buffer, "cd ".len(), &replacement); helper.update(&mut buffer, "cd ".len(), &replacement);
@ -184,7 +184,7 @@ mod tests {
buffer.insert_str(0, text); buffer.insert_str(0, text);
buffer.set_pos(text.len() - 30); buffer.set_pos(text.len() - 30);
let helper = Helper::new(basic_evaluation_context().unwrap(), None); let helper = Helper::new(EvaluationContext::basic(), None);
helper.update(&mut buffer, "cd ".len(), &replacement); helper.update(&mut buffer, "cd ".len(), &replacement);

View File

@ -81,29 +81,29 @@ lazy_static! {
static ref MULT_DIV_LOOKUP_TABLE: HashMap<(Operator, BinarySide, SyntaxShape), Vec<SyntaxShape>> = { static ref MULT_DIV_LOOKUP_TABLE: HashMap<(Operator, BinarySide, SyntaxShape), Vec<SyntaxShape>> = {
vec![ vec![
((Operator::Divide, BinarySide::Left, SyntaxShape::Number), // expr => possible var shapes ((Operator::Divide, BinarySide::Left, SyntaxShape::Number), // expr => possible var shapes
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //$var / number => Unit, Int, Number vec![SyntaxShape::Filesize, SyntaxShape::Duration, SyntaxShape::Number, SyntaxShape::Int]), //$var / number => Unit, Int, Number
((Operator::Divide, BinarySide::Left, SyntaxShape::Int), ((Operator::Divide, BinarySide::Left, SyntaxShape::Int),
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //$var / int => Unit, Int, Number vec![SyntaxShape::Filesize, SyntaxShape::Duration, SyntaxShape::Number, SyntaxShape::Int]), //$var / int => Unit, Int, Number
((Operator::Divide, BinarySide::Left, SyntaxShape::Unit), ((Operator::Divide, BinarySide::Left, SyntaxShape::Filesize),
vec![SyntaxShape::Unit]), //$var / unit => Unit vec![SyntaxShape::Filesize, SyntaxShape::Duration, SyntaxShape::Filesize]), //$var / unit => Unit
((Operator::Divide, BinarySide::Right, SyntaxShape::Number), ((Operator::Divide, BinarySide::Right, SyntaxShape::Number),
vec![SyntaxShape::Number, SyntaxShape::Int]), //number / $var => Int, Number vec![SyntaxShape::Number, SyntaxShape::Int]), //number / $var => Int, Number
((Operator::Divide, BinarySide::Right, SyntaxShape::Int), ((Operator::Divide, BinarySide::Right, SyntaxShape::Int),
vec![SyntaxShape::Number, SyntaxShape::Int]), //int / $var => Int, Number vec![SyntaxShape::Number, SyntaxShape::Int]), //int / $var => Int, Number
((Operator::Divide, BinarySide::Right, SyntaxShape::Unit), ((Operator::Divide, BinarySide::Right, SyntaxShape::Filesize),
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //unit / $var => unit, int, number vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //unit / $var => unit, int, number
((Operator::Multiply, BinarySide::Left, SyntaxShape::Number), ((Operator::Multiply, BinarySide::Left, SyntaxShape::Number),
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //$var * number => Unit, Int, Number vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //$var * number => Unit, Int, Number
((Operator::Multiply, BinarySide::Left, SyntaxShape::Int), ((Operator::Multiply, BinarySide::Left, SyntaxShape::Int),
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //$var * int => Unit, Int, Number vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //$var * int => Unit, Int, Number
((Operator::Multiply, BinarySide::Left, SyntaxShape::Unit), ((Operator::Multiply, BinarySide::Left, SyntaxShape::Filesize),
vec![SyntaxShape::Int, SyntaxShape::Number]), //$var * unit => int, number //TODO this changes as soon as more complex units arrive vec![SyntaxShape::Int, SyntaxShape::Number]), //$var * unit => int, number //TODO this changes as soon as more complex units arrive
((Operator::Multiply, BinarySide::Right, SyntaxShape::Number), ((Operator::Multiply, BinarySide::Right, SyntaxShape::Number),
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //number * $var => Unit, Int, Number vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //number * $var => Unit, Int, Number
((Operator::Multiply, BinarySide::Right, SyntaxShape::Int), ((Operator::Multiply, BinarySide::Right, SyntaxShape::Int),
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //int * $var => Unit, Int, Number vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //int * $var => Unit, Int, Number
((Operator::Multiply, BinarySide::Right, SyntaxShape::Unit), ((Operator::Multiply, BinarySide::Right, SyntaxShape::Filesize),
vec![SyntaxShape::Int, SyntaxShape::Number]), //unit * $var => int, number //TODO this changes as soon as more complex units arrive vec![SyntaxShape::Int, SyntaxShape::Number]), //unit * $var => int, number //TODO this changes as soon as more complex units arrive
].into_iter().collect() ].into_iter().collect()
}; };
@ -241,8 +241,8 @@ fn get_result_shape_of(
l_shape l_shape
} }
Operator::Multiply => { Operator::Multiply => {
if l_shape == SyntaxShape::Unit || r_shape == SyntaxShape::Unit { if l_shape == SyntaxShape::Duration || r_shape == SyntaxShape::Duration {
SyntaxShape::Unit SyntaxShape::Duration
} else { } else {
SyntaxShape::Number SyntaxShape::Number
} }
@ -250,7 +250,7 @@ fn get_result_shape_of(
Operator::Divide => { Operator::Divide => {
if l_shape == r_shape { if l_shape == r_shape {
SyntaxShape::Number SyntaxShape::Number
} else if l_shape == SyntaxShape::Unit { } else if l_shape == SyntaxShape::Duration {
l_shape l_shape
} else { } else {
SyntaxShape::Number SyntaxShape::Number
@ -273,10 +273,11 @@ fn get_shape_of_expr(expr: &SpannedExpression) -> Option<SyntaxShape> {
Expression::Literal(literal) => { Expression::Literal(literal) => {
match literal { match literal {
nu_protocol::hir::Literal::Number(number) => match number { nu_protocol::hir::Literal::Number(number) => match number {
nu_protocol::hir::Number::BigInt(_) => Some(SyntaxShape::Int),
nu_protocol::hir::Number::Int(_) => Some(SyntaxShape::Int), nu_protocol::hir::Number::Int(_) => Some(SyntaxShape::Int),
nu_protocol::hir::Number::Decimal(_) => Some(SyntaxShape::Number), nu_protocol::hir::Number::Decimal(_) => Some(SyntaxShape::Number),
}, },
nu_protocol::hir::Literal::Size(_, _) => Some(SyntaxShape::Unit), nu_protocol::hir::Literal::Size(_, _) => Some(SyntaxShape::Duration),
nu_protocol::hir::Literal::String(_) => Some(SyntaxShape::String), nu_protocol::hir::Literal::String(_) => Some(SyntaxShape::String),
//Rest should have failed at parsing stage? //Rest should have failed at parsing stage?
nu_protocol::hir::Literal::GlobPattern(_) => Some(SyntaxShape::String), nu_protocol::hir::Literal::GlobPattern(_) => Some(SyntaxShape::String),
@ -295,7 +296,7 @@ fn get_shape_of_expr(expr: &SpannedExpression) -> Option<SyntaxShape> {
Expression::List(_) => Some(SyntaxShape::Table), Expression::List(_) => Some(SyntaxShape::Table),
Expression::Boolean(_) => Some(SyntaxShape::String), Expression::Boolean(_) => Some(SyntaxShape::String),
Expression::Path(_) => Some(SyntaxShape::ColumnPath), Expression::FullColumnPath(_) => Some(SyntaxShape::ColumnPath),
Expression::FilePath(_) => Some(SyntaxShape::FilePath), Expression::FilePath(_) => Some(SyntaxShape::FilePath),
Expression::Block(_) => Some(SyntaxShape::Block), Expression::Block(_) => Some(SyntaxShape::Block),
Expression::ExternalCommand(_) => Some(SyntaxShape::String), Expression::ExternalCommand(_) => Some(SyntaxShape::String),
@ -377,10 +378,7 @@ impl VarSyntaxShapeDeductor {
.iter() .iter()
.map(|decl| { .map(|decl| {
let usage: VarUsage = decl.into(); let usage: VarUsage = decl.into();
let deduction = match deducer.inferences.get(&usage) { let deduction = deducer.inferences.get(&usage).cloned();
Some(vec) => Some(vec.clone()),
None => None,
};
(decl.clone(), deduction) (decl.clone(), deduction)
}) })
.collect()) .collect())
@ -541,7 +539,7 @@ impl VarSyntaxShapeDeductor {
trace!("Inferring vars in block"); trace!("Inferring vars in block");
self.infer_shape(&b, scope)?; self.infer_shape(&b, scope)?;
} }
Expression::Path(path) => { Expression::FullColumnPath(path) => {
trace!("Inferring vars in path"); trace!("Inferring vars in path");
match &path.head.expr { match &path.head.expr {
//PathMember can't be var yet (?) //PathMember can't be var yet (?)
@ -792,7 +790,7 @@ impl VarSyntaxShapeDeductor {
| Expression::Binary(_) | Expression::Binary(_)
| Expression::Range(_) | Expression::Range(_)
| Expression::Block(_) | Expression::Block(_)
| Expression::Path(_) | Expression::FullColumnPath(_)
| Expression::FilePath(_) | Expression::FilePath(_)
| Expression::ExternalCommand(_) | Expression::ExternalCommand(_)
| Expression::Command | Expression::Command
@ -845,12 +843,21 @@ impl VarSyntaxShapeDeductor {
), ),
)?; )?;
} }
SyntaxShape::Unit => { SyntaxShape::Duration => {
self.checked_insert( self.checked_insert(
var, var,
VarShapeDeduction::from_usage_with_alternatives( VarShapeDeduction::from_usage_with_alternatives(
&var.span, &var.span,
&[SyntaxShape::Unit], &[SyntaxShape::Duration],
),
)?;
}
SyntaxShape::Filesize => {
self.checked_insert(
var,
VarShapeDeduction::from_usage_with_alternatives(
&var.span,
&[SyntaxShape::Filesize],
), ),
)?; )?;
} }
@ -1023,7 +1030,7 @@ impl VarSyntaxShapeDeductor {
Some(combination) Some(combination)
} }
}) })
.filter_map(|elem| elem) .flatten()
.collect() .collect()
} }
//No any's intersection of both is result //No any's intersection of both is result
@ -1044,7 +1051,7 @@ impl VarSyntaxShapeDeductor {
Some(combination) Some(combination)
} }
}) })
.filter_map(|elem| elem) .flatten()
.collect(); .collect();
if intersection.is_empty() { if intersection.is_empty() {
//TODO pass all labels somehow //TODO pass all labels somehow

View File

@ -5,25 +5,26 @@ description = "CLI for nushell"
edition = "2018" edition = "2018"
license = "MIT" license = "MIT"
name = "nu-command" name = "nu-command"
version = "0.30.0" version = "0.32.0"
[lib] [lib]
doctest = false doctest = false
[dependencies] [dependencies]
nu-data = { version = "0.30.0", path = "../nu-data" } nu-data = { version = "0.32.0", path = "../nu-data" }
nu-engine = { version = "0.30.0", path = "../nu-engine" } nu-engine = { version = "0.32.0", path = "../nu-engine" }
nu-errors = { version = "0.30.0", path = "../nu-errors" } nu-errors = { version = "0.32.0", path = "../nu-errors" }
nu-json = { version = "0.30.0", path = "../nu-json" } nu-json = { version = "0.32.0", path = "../nu-json" }
nu-parser = { version = "0.30.0", path = "../nu-parser" } nu-parser = { version = "0.32.0", path = "../nu-parser" }
nu-plugin = { version = "0.30.0", path = "../nu-plugin" } nu-plugin = { version = "0.32.0", path = "../nu-plugin" }
nu-protocol = { version = "0.30.0", path = "../nu-protocol" } nu-protocol = { version = "0.32.0", path = "../nu-protocol" }
nu-source = { version = "0.30.0", path = "../nu-source" } nu-source = { version = "0.32.0", path = "../nu-source" }
nu-stream = { version = "0.30.0", path = "../nu-stream" } nu-stream = { version = "0.32.0", path = "../nu-stream" }
nu-table = { version = "0.30.0", path = "../nu-table" } nu-table = { version = "0.32.0", path = "../nu-table" }
nu-test-support = { version = "0.30.0", path = "../nu-test-support" } nu-test-support = { version = "0.32.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.30.0", path = "../nu-value-ext" } nu-value-ext = { version = "0.32.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.30.0", path = "../nu-ansi-term" } nu-ansi-term = { version = "0.32.0", path = "../nu-ansi-term" }
nu-pretty-hex = { version = "0.32.0", path = "../nu-pretty-hex" }
Inflector = "0.11" Inflector = "0.11"
arboard = { version = "1.1.0", optional = true } arboard = { version = "1.1.0", optional = true }
@ -53,7 +54,6 @@ getset = "0.1.1"
glob = "0.3.0" glob = "0.3.0"
htmlescape = "0.3.1" htmlescape = "0.3.1"
ical = "0.7.0" ical = "0.7.0"
ichwh = { version = "0.3.4", optional = true }
indexmap = { version = "1.6.1", features = ["serde-1"] } indexmap = { version = "1.6.1", features = ["serde-1"] }
itertools = "0.10.0" itertools = "0.10.0"
lazy_static = "1.*" lazy_static = "1.*"
@ -66,7 +66,6 @@ num-format = { version = "0.4.0", features = ["with-num-bigint"] }
num-traits = "0.2.14" num-traits = "0.2.14"
parking_lot = "0.11.1" parking_lot = "0.11.1"
pin-utils = "0.1.0" pin-utils = "0.1.0"
pretty-hex = "0.2.1"
ptree = { version = "0.3.1", optional = true } ptree = { version = "0.3.1", optional = true }
query_interface = "0.3.5" query_interface = "0.3.5"
quick-xml = "0.21.0" quick-xml = "0.21.0"
@ -75,7 +74,7 @@ rayon = "1.5.0"
regex = "1.4.3" regex = "1.4.3"
roxmltree = "0.14.0" roxmltree = "0.14.0"
rust-embed = "5.9.0" rust-embed = "5.9.0"
rustyline = { version = "8.0.0", optional = true } rustyline = { version = "8.1.0", optional = true }
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.123", features = ["derive"] }
serde_bytes = "0.11.5" serde_bytes = "0.11.5"
serde_ini = "0.2.0" serde_ini = "0.2.0"
@ -97,9 +96,14 @@ trash = { version = "1.3.0", optional = true }
unicode-segmentation = "1.7.1" unicode-segmentation = "1.7.1"
url = "2.2.0" url = "2.2.0"
uuid_crate = { package = "uuid", version = "0.8.2", features = ["v4"], optional = true } uuid_crate = { package = "uuid", version = "0.8.2", features = ["v4"], optional = true }
which = { version = "4.0.2", optional = true } which = { version = "4.1.0", optional = true }
zip = { version = "0.5.9", optional = true } zip = { version = "0.5.9", optional = true }
[dependencies.polars]
version = "0.13.4"
optional = true
features = ["parquet", "json", "random"]
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
umask = "1.0.0" umask = "1.0.0"
users = "0.11.0" users = "0.11.0"
@ -113,7 +117,7 @@ users = "0.11.0"
[dependencies.rusqlite] [dependencies.rusqlite]
features = ["bundled", "blob"] features = ["bundled", "blob"]
optional = true optional = true
version = "0.24.2" version = "0.25.3"
[build-dependencies] [build-dependencies]
shadow-rs = "0.5" shadow-rs = "0.5"
@ -131,3 +135,4 @@ trash-support = ["trash"]
directories = ["directories-next"] directories = ["directories-next"]
dirs = ["dirs-next"] dirs = ["dirs-next"]
table-pager = ["minus", "crossterm"] table-pager = ["minus", "crossterm"]
dataframe = ["nu-protocol/dataframe", "polars"]

View File

@ -26,6 +26,8 @@ pub(crate) mod compact;
pub(crate) mod config; pub(crate) mod config;
pub(crate) mod constants; pub(crate) mod constants;
pub(crate) mod cp; pub(crate) mod cp;
#[cfg(feature = "dataframe")]
pub(crate) mod dataframe;
pub(crate) mod date; pub(crate) mod date;
pub(crate) mod debug; pub(crate) mod debug;
pub(crate) mod def; pub(crate) mod def;
@ -44,6 +46,7 @@ pub(crate) mod exec;
pub(crate) mod exit; pub(crate) mod exit;
pub(crate) mod first; pub(crate) mod first;
pub(crate) mod flatten; pub(crate) mod flatten;
pub(crate) mod for_in;
pub(crate) mod format; pub(crate) mod format;
pub(crate) mod from; pub(crate) mod from;
pub(crate) mod from_csv; pub(crate) mod from_csv;
@ -77,6 +80,7 @@ pub(crate) mod length;
pub(crate) mod let_; pub(crate) mod let_;
pub(crate) mod let_env; pub(crate) mod let_env;
pub(crate) mod lines; pub(crate) mod lines;
pub(crate) mod load_env;
pub(crate) mod ls; pub(crate) mod ls;
pub(crate) mod math; pub(crate) mod math;
pub(crate) mod merge; pub(crate) mod merge;
@ -170,9 +174,12 @@ pub(crate) use each::EachGroup;
pub(crate) use each::EachWindow; pub(crate) use each::EachWindow;
pub(crate) use echo::Echo; pub(crate) use echo::Echo;
pub(crate) use empty::Command as Empty; pub(crate) use empty::Command as Empty;
pub(crate) use for_in::ForIn;
pub(crate) use if_::If; pub(crate) use if_::If;
pub(crate) use into::Into; pub(crate) use into::Into;
pub(crate) use into::IntoBinary;
pub(crate) use into::IntoInt; pub(crate) use into::IntoInt;
pub(crate) use into::IntoString;
pub(crate) use nu::NuPlugin; pub(crate) use nu::NuPlugin;
pub(crate) use update::Command as Update; pub(crate) use update::Command as Update;
pub(crate) mod kill; pub(crate) mod kill;
@ -182,6 +189,12 @@ pub(crate) use clear::Clear;
pub(crate) mod touch; pub(crate) mod touch;
pub(crate) use all::Command as All; pub(crate) use all::Command as All;
pub(crate) use any::Command as Any; pub(crate) use any::Command as Any;
#[cfg(feature = "dataframe")]
pub(crate) use dataframe::{
DataFrame, DataFrameAggregate, DataFrameConvert, DataFrameDTypes, DataFrameDrop,
DataFrameGroupBy, DataFrameJoin, DataFrameList, DataFrameLoad, DataFrameSample,
DataFrameSelect, DataFrameShow,
};
pub(crate) use enter::Enter; pub(crate) use enter::Enter;
pub(crate) use every::Every; pub(crate) use every::Every;
pub(crate) use exec::Exec; pub(crate) use exec::Exec;
@ -220,6 +233,7 @@ pub(crate) use length::Length;
pub(crate) use let_::Let; pub(crate) use let_::Let;
pub(crate) use let_env::LetEnv; pub(crate) use let_env::LetEnv;
pub(crate) use lines::Lines; pub(crate) use lines::Lines;
pub(crate) use load_env::LoadEnv;
pub(crate) use ls::Ls; pub(crate) use ls::Ls;
pub(crate) use math::{ pub(crate) use math::{
Math, MathAbs, MathAverage, MathCeil, MathEval, MathFloor, MathMaximum, MathMedian, Math, MathAbs, MathAverage, MathCeil, MathEval, MathFloor, MathMaximum, MathMedian,
@ -234,8 +248,8 @@ pub(crate) use nth::Nth;
pub(crate) use open::Open; pub(crate) use open::Open;
pub(crate) use parse::Parse; pub(crate) use parse::Parse;
pub(crate) use path::{ pub(crate) use path::{
PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathJoin, PathParse, PathSplit, PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathJoin, PathParse,
PathType, PathRelativeTo, PathSplit, PathType,
}; };
pub(crate) use pivot::Pivot; pub(crate) use pivot::Pivot;
pub(crate) use prepend::Prepend; pub(crate) use prepend::Prepend;
@ -270,7 +284,7 @@ pub(crate) use split::{Split, SplitChars, SplitColumn, SplitRow};
pub(crate) use split_by::SplitBy; pub(crate) use split_by::SplitBy;
pub(crate) use str_::{ pub(crate) use str_::{
Str, StrCamelCase, StrCapitalize, StrCollect, StrContains, StrDowncase, StrEndsWith, Str, StrCamelCase, StrCapitalize, StrCollect, StrContains, StrDowncase, StrEndsWith,
StrFindReplace, StrFrom, StrIndexOf, StrKebabCase, StrLPad, StrLength, StrPascalCase, StrRPad, StrFindReplace, StrIndexOf, StrKebabCase, StrLPad, StrLength, StrPascalCase, StrRPad,
StrReverse, StrScreamingSnakeCase, StrSnakeCase, StrStartsWith, StrSubstring, StrToDatetime, StrReverse, StrScreamingSnakeCase, StrSnakeCase, StrStartsWith, StrSubstring, StrToDatetime,
StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase, StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
}; };
@ -321,7 +335,6 @@ mod tests {
whole_stream_command(StrUpcase), whole_stream_command(StrUpcase),
whole_stream_command(StrCapitalize), whole_stream_command(StrCapitalize),
whole_stream_command(StrFindReplace), whole_stream_command(StrFindReplace),
whole_stream_command(StrFrom),
whole_stream_command(StrSubstring), whole_stream_command(StrSubstring),
whole_stream_command(StrToDatetime), whole_stream_command(StrToDatetime),
whole_stream_command(StrContains), whole_stream_command(StrContains),
@ -354,6 +367,7 @@ mod tests {
#[test] #[test]
fn examples_work_as_expected() -> Result<(), ShellError> { fn examples_work_as_expected() -> Result<(), ShellError> {
for cmd in only_examples() { for cmd in only_examples() {
println!("cmd: {}", cmd.name());
test_examples(cmd)?; test_examples(cmd)?;
} }

View File

@ -44,7 +44,7 @@ impl WholeStreamCommand for Command {
}, },
Example { Example {
description: "Check that all values are even", description: "Check that all values are even",
example: "echo [2 4 6 8] | all? $(= $it mod 2) == 0", example: "echo [2 4 6 8] | all? ($it mod 2) == 0",
result: Some(vec![Value::from(true)]), result: Some(vec![Value::from(true)]),
}, },
] ]
@ -83,13 +83,13 @@ fn all(args: CommandArgs) -> Result<OutputStream, ShellError> {
} }
}; };
let scope = args.scope.clone(); let scope = args.scope();
let init = Ok(InputStream::one( let init = Ok(InputStream::one(
UntaggedValue::boolean(true).into_value(&tag), UntaggedValue::boolean(true).into_value(&tag),
)); ));
// Variables in nu are immutable. Having the same variable accross invocations // Variables in nu are immutable. Having the same variable across invocations
// of evaluate_baseline_expr does not mutate the variables and those each // of evaluate_baseline_expr does not mutate the variables and those each
// invocations are independent of each other! // invocations are independent of each other!
scope.enter_scope(); scope.enter_scope();

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use nu_ansi_term::*; use nu_ansi_term::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged; use nu_source::Tagged;
pub struct Command; pub struct Command;
@ -69,7 +69,7 @@ following values:
https://en.wikipedia.org/wiki/ANSI_escape_code https://en.wikipedia.org/wiki/ANSI_escape_code
OSC: '\x1b]' is not required for --osc parameter OSC: '\x1b]' is not required for --osc parameter
Example: echo [$(ansi -o '0') 'some title' $(char bel)] | str collect Example: echo [(ansi -o '0') 'some title' (char bel)] | str collect
Format: # Format: #
0 Set window title and icon name 0 Set window title and icon name
1 Set icon name 1 Set icon name
@ -96,7 +96,7 @@ Format: #
Example { Example {
description: description:
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)", "Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
example: r#"echo [$(ansi rb) Hello " " $(ansi gb) Nu " " $(ansi pb) World] | str collect"#, example: r#"echo [(ansi rb) Hello " " (ansi gb) Nu " " (ansi pb) World] | str collect"#,
result: Some(vec![Value::from( result: Some(vec![Value::from(
"\u{1b}[1;31mHello \u{1b}[1;32mNu \u{1b}[1;35mWorld", "\u{1b}[1;31mHello \u{1b}[1;32mNu \u{1b}[1;35mWorld",
)]), )]),
@ -104,7 +104,7 @@ Format: #
Example { Example {
description: description:
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)", "Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
example: r#"echo [$(ansi -e '3;93;41m') Hello $(ansi reset) " " $(ansi gb) Nu " " $(ansi pb) World] | str collect"#, example: r#"echo [(ansi -e '3;93;41m') Hello (ansi reset) " " (ansi gb) Nu " " (ansi pb) World] | str collect"#,
result: Some(vec![Value::from( result: Some(vec![Value::from(
"\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld", "\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld",
)]), )]),
@ -112,7 +112,7 @@ Format: #
] ]
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?; let args = args.evaluate_once()?;
let code: Option<Tagged<String>> = args.opt(0)?; let code: Option<Tagged<String>> = args.opt(0)?;
@ -129,9 +129,9 @@ Format: #
)); ));
} }
let output = format!("\x1b[{}", e.item); let output = format!("\x1b[{}", e.item);
return Ok(ActionStream::one(ReturnSuccess::value( return Ok(OutputStream::one(
UntaggedValue::string(output).into_value(e.tag()), UntaggedValue::string(output).into_value(e.tag()),
))); ));
} }
if let Some(o) = osc { if let Some(o) = osc {
@ -147,18 +147,18 @@ Format: #
//Operating system command aka osc ESC ] <- note the right brace, not left brace for osc //Operating system command aka osc ESC ] <- note the right brace, not left brace for osc
// OCS's need to end with a bell '\x07' char // OCS's need to end with a bell '\x07' char
let output = format!("\x1b]{};", o.item); let output = format!("\x1b]{};", o.item);
return Ok(ActionStream::one(ReturnSuccess::value( return Ok(OutputStream::one(
UntaggedValue::string(output).into_value(o.tag()), UntaggedValue::string(output).into_value(o.tag()),
))); ));
} }
if let Some(code) = code { if let Some(code) = code {
let ansi_code = str_to_ansi(&code.item); let ansi_code = str_to_ansi(&code.item);
if let Some(output) = ansi_code { if let Some(output) = ansi_code {
Ok(ActionStream::one(ReturnSuccess::value( Ok(OutputStream::one(
UntaggedValue::string(output).into_value(code.tag()), UntaggedValue::string(output).into_value(code.tag()),
))) ))
} else { } else {
Err(ShellError::labeled_error( Err(ShellError::labeled_error(
"Unknown ansi code", "Unknown ansi code",
@ -299,7 +299,7 @@ pub fn str_to_ansi(s: &str) -> Option<String> {
// Reference for ansi codes https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797 // Reference for ansi codes https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
// Another good reference http://ascii-table.com/ansi-escape-sequences.php // Another good reference http://ascii-table.com/ansi-escape-sequences.php
// For setting title like `echo [$(char title) $(pwd) $(char bel)] | str collect` // For setting title like `echo [(char title) (pwd) (char bel)] | str collect`
"title" => Some("\x1b]2;".to_string()), // ESC]2; xterm sets window title using OSC syntax escapes "title" => Some("\x1b]2;".to_string()), // ESC]2; xterm sets window title using OSC syntax escapes
// Ansi Erase Sequences // Ansi Erase Sequences

View File

@ -2,19 +2,12 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::ShellTypeName; use nu_protocol::ShellTypeName;
use nu_protocol::{ use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tag; use nu_source::Tag;
use strip_ansi_escapes::strip; use strip_ansi_escapes::strip;
pub struct SubCommand; pub struct SubCommand;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
}
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"ansi strip" "ansi strip"
@ -31,27 +24,29 @@ impl WholeStreamCommand for SubCommand {
"strip ansi escape sequences from string" "strip ansi escape sequences from string"
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
operate(args) operate(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![Example {
description: "strip ansi escape sequences from string", description: "strip ansi escape sequences from string",
example: "echo [$(ansi gb) 'hello' $(ansi reset)] | str collect | ansi strip", example: "echo [(ansi gb) 'hello' (ansi reset)] | str collect | ansi strip",
result: None, result: None,
}] }]
} }
} }
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> { fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest }, input) = args.process()?; let args = args.evaluate_once()?;
let column_paths: Vec<_> = rest;
Ok(input let column_paths: Vec<_> = args.rest(0)?;
let result: Vec<Value> = args
.input
.map(move |v| { .map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?) action(&v, v.tag())
} else { } else {
let mut ret = v; let mut ret = v;
@ -62,10 +57,12 @@ fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
)?; )?;
} }
ReturnSuccess::value(ret) Ok(ret)
} }
}) })
.to_action_stream()) .collect::<Result<Vec<Value>, _>>()?;
Ok(OutputStream::from_stream(result.into_iter()))
} }
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -8,9 +8,8 @@ use nu_protocol::{
pub struct Command; pub struct Command;
#[derive(Deserialize)] struct AnyArgs {
pub struct Arguments { predicate: CapturedBlock,
block: CapturedBlock,
} }
impl WholeStreamCommand for Command { impl WholeStreamCommand for Command {
@ -30,7 +29,7 @@ impl WholeStreamCommand for Command {
"Find if the table rows matches the condition." "Find if the table rows matches the condition."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
any(args) any(args)
} }
@ -45,79 +44,85 @@ impl WholeStreamCommand for Command {
}, },
Example { Example {
description: "Check if any of the values is odd", description: "Check if any of the values is odd",
example: "echo [2 4 1 6 8] | any? $(= $it mod 2) == 1", example: "echo [2 4 1 6 8] | any? ($it mod 2) == 1",
result: Some(vec![Value::from(true)]), result: Some(vec![Value::from(true)]),
}, },
] ]
} }
} }
fn any(args: CommandArgs) -> Result<ActionStream, ShellError> { fn any(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = Arc::new(EvaluationContext::from_args(&args)); let ctx = EvaluationContext::from_args(&args);
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let (Arguments { block }, input) = args.process()?; let args = args.evaluate_once()?;
let any_args = AnyArgs {
predicate: args.req(0)?,
};
let err = Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
args.call_info.name_tag.clone(),
));
//This seems a little odd. Can't we have predicates with pipelines/multiple statements?
let condition = { let condition = {
if block.block.block.len() != 1 { if any_args.predicate.block.block.len() != 1 {
return Err(ShellError::labeled_error( return err;
"Expected a condition",
"expected a condition",
tag,
));
} }
match block.block.block[0].pipelines.get(0) { match any_args.predicate.block.block[0].pipelines.get(0) {
Some(item) => match item.list.get(0) { Some(item) => match item.list.get(0) {
Some(ClassifiedCommand::Expr(expr)) => expr.clone(), Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
_ => { _ => {
return Err(ShellError::labeled_error( return err;
"Expected a condition",
"expected a condition",
tag,
));
} }
}, },
None => { None => {
return Err(ShellError::labeled_error( return err;
"Expected a condition",
"expected a condition",
tag,
));
} }
} }
}; };
let cond = Ok(InputStream::one( let scope = args.scope();
let init = Ok(InputStream::one(
UntaggedValue::boolean(false).into_value(&tag), UntaggedValue::boolean(false).into_value(&tag),
)); ));
Ok(input // Variables in nu are immutable. Having the same variable accross invocations
.fold(cond, move |cond, row| { // of evaluate_baseline_expr does not mutate the variables and thus each
let condition = condition.clone(); // invocations are independent of each other!
let ctx = ctx.clone(); scope.enter_scope();
ctx.scope.enter_scope(); scope.add_vars(&any_args.predicate.captured.entries);
ctx.scope.add_vars(&block.captured.entries);
ctx.scope.add_var("$it", row);
let condition = evaluate_baseline_expr(&condition, &*ctx); let result = args.input.fold(init, move |acc, row| {
ctx.scope.exit_scope(); let condition = condition.clone();
let ctx = ctx.clone();
if let Some((arg, _)) = any_args.predicate.block.params.positional.first() {
ctx.scope.add_var(arg.name(), row);
}
let curr = cond?.drain_vec(); let condition = evaluate_baseline_expr(&condition, &ctx);
let curr = curr
.get(0)
.ok_or_else(|| ShellError::unexpected("No value to check with"))?;
let cond = curr.as_bool()?;
match condition { let curr = acc?.drain_vec();
Ok(condition) => match condition.as_bool() { let curr = curr
Ok(b) => Ok(InputStream::one( .get(0)
UntaggedValue::boolean(cond || b).into_value(&curr.tag), .ok_or_else(|| ShellError::unexpected("No value to check with"))?;
)), let cond = curr.as_bool()?;
Err(e) => Err(e),
}, 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),
} },
})? Err(e) => Err(e),
.to_action_stream()) }
});
scope.exit_scope();
Ok(result?.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -27,7 +27,7 @@ The .nu-env file has the same format as your $HOME/nu/config.toml file. By loadi
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(ActionStream::one(ReturnSuccess::value( Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(get_full_help(&Autoenv, &args.scope)).into_value(Tag::unknown()), UntaggedValue::string(get_full_help(&Autoenv, args.scope())).into_value(Tag::unknown()),
))) )))
} }

View File

@ -8,6 +8,9 @@ use nu_protocol::hir::{self, Expression, ExternalRedirection, Literal, SpannedEx
use nu_protocol::{Primitive, Signature, UntaggedValue, Value}; use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
use nu_table::TextStyle; use nu_table::TextStyle;
#[cfg(feature = "dataframe")]
use nu_protocol::dataframe::PolarsData;
pub struct Command; pub struct Command;
impl WholeStreamCommand for Command { impl WholeStreamCommand for Command {
@ -43,14 +46,15 @@ impl WholeStreamCommand for Command {
} }
} }
pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> { pub fn autoview(args: CommandArgs) -> Result<OutputStream, ShellError> {
let configuration = context.configs.lock().global_config(); let configuration = args.configs().lock().global_config();
let tag = args.call_info.name_tag.clone();
let binary = context.scope.get_command("binaryview"); let binary = args.scope().get_command("binaryview");
let text = context.scope.get_command("textview"); let text = args.scope().get_command("textview");
let table = context.scope.get_command("table"); let table = args.scope().get_command("table");
let context = args.context;
let (mut input_stream, context) = context.split(); let mut input_stream = args.input;
if let Some(x) = input_stream.next() { if let Some(x) = input_stream.next() {
match input_stream.next() { match input_stream.next() {
@ -62,7 +66,7 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
let stream = InputStream::from_stream(xy_stream); let stream = InputStream::from_stream(xy_stream);
if let Some(table) = table { if let Some(table) = table {
let command_args = create_default_command_args(&context).with_input(stream); let command_args = create_default_command_args(&context, stream, tag);
let result = table.run(command_args)?; let result = table.run(command_args)?;
let _ = result.collect::<Vec<_>>(); let _ = result.collect::<Vec<_>>();
} }
@ -74,12 +78,13 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
tag: Tag { anchor, span }, tag: Tag { anchor, span },
} if anchor.is_some() => { } if anchor.is_some() => {
if let Some(text) = text { if let Some(text) = text {
let mut stream = VecDeque::new(); let command_args = create_default_command_args(
stream.push_back( &context,
UntaggedValue::string(s).into_value(Tag { anchor, span }), InputStream::one(
UntaggedValue::string(s).into_value(Tag { anchor, span }),
),
tag,
); );
let command_args =
create_default_command_args(&context).with_input(stream);
let result = text.run_with_actions(command_args)?; let result = text.run_with_actions(command_args)?;
let _ = result.collect::<Vec<_>>(); let _ = result.collect::<Vec<_>>();
} else { } else {
@ -104,6 +109,12 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
} => { } => {
out!("{}", n); out!("{}", n);
} }
Value {
value: UntaggedValue::Primitive(Primitive::BigInt(n)),
..
} => {
out!("{}", n);
}
Value { Value {
value: UntaggedValue::Primitive(Primitive::Decimal(n)), value: UntaggedValue::Primitive(Primitive::Decimal(n)),
.. ..
@ -158,14 +169,12 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
.. ..
} => { } => {
if let Some(binary) = binary { if let Some(binary) = binary {
let mut stream = VecDeque::new();
stream.push_back(x);
let command_args = let command_args =
create_default_command_args(&context).with_input(stream); create_default_command_args(&context, InputStream::one(x), tag);
let result = binary.run_with_actions(command_args)?; let result = binary.run_with_actions(command_args)?;
let _ = result.collect::<Vec<_>>(); let _ = result.collect::<Vec<_>>();
} else { } else {
use pretty_hex::*; use nu_pretty_hex::*;
out!("{:?}", b.hex_dump()); out!("{:?}", b.hex_dump());
} }
} }
@ -220,16 +229,42 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
println!("{}", nu_table::draw_table(&table, term_width, &color_hm)); println!("{}", nu_table::draw_table(&table, term_width, &color_hm));
} else if let Some(table) = table { } else if let Some(table) = table {
let mut stream = VecDeque::new();
stream.push_back(x);
let command_args = let command_args =
create_default_command_args(&context).with_input(stream); create_default_command_args(&context, InputStream::one(x), tag);
let result = table.run(command_args)?; let result = table.run(command_args)?;
let _ = result.collect::<Vec<_>>(); let _ = result.collect::<Vec<_>>();
} else { } else {
out!("{:?}", row); out!("{:?}", row);
} }
} }
#[cfg(feature = "dataframe")]
Value {
value: UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)),
tag,
} => {
if let Some(table) = table {
// TODO. Configure the parameter rows from file. It can be
// adjusted to see a certain amount of values in the head
let command_args =
create_default_command_args(&context, df.print()?.into(), tag);
let result = table.run(command_args)?;
let _ = result.collect::<Vec<_>>();
}
}
#[cfg(feature = "dataframe")]
Value {
value: UntaggedValue::DataFrame(PolarsData::GroupBy(groupby)),
tag,
} => {
if let Some(table) = table {
// TODO. Configure the parameter rows from file. It can be
// adjusted to see a certain amount of values in the head
let command_args =
create_default_command_args(&context, groupby.print()?.into(), tag);
let result = table.run(command_args)?;
let _ = result.collect::<Vec<_>>();
}
}
Value { Value {
value: UntaggedValue::Primitive(Primitive::Nothing), value: UntaggedValue::Primitive(Primitive::Nothing),
.. ..
@ -240,10 +275,8 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
value: ref item, .. value: ref item, ..
} => { } => {
if let Some(table) = table { if let Some(table) = table {
let mut stream = VecDeque::new();
stream.push_back(x);
let command_args = let command_args =
create_default_command_args(&context).with_input(stream); create_default_command_args(&context, InputStream::one(x), tag);
let result = table.run(command_args)?; let result = table.run(command_args)?;
let _ = result.collect::<Vec<_>>(); let _ = result.collect::<Vec<_>>();
} else { } else {
@ -258,14 +291,14 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(InputStream::empty()) Ok(InputStream::empty())
} }
fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawCommandArgs { fn create_default_command_args(
let span = context.name.span; context: &EvaluationContext,
RawCommandArgs { input: InputStream,
host: context.host.clone(), tag: Tag,
ctrl_c: context.ctrl_c.clone(), ) -> CommandArgs {
configs: context.configs.clone(), let span = tag.span;
current_errors: context.current_errors.clone(), CommandArgs {
shell_manager: context.shell_manager.clone(), context: context.clone(),
call_info: UnevaluatedCallInfo { call_info: UnevaluatedCallInfo {
args: hir::Call { args: hir::Call {
head: Box::new(SpannedExpression::new( head: Box::new(SpannedExpression::new(
@ -277,9 +310,9 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
span, span,
external_redirection: ExternalRedirection::Stdout, external_redirection: ExternalRedirection::Stdout,
}, },
name_tag: context.name.clone(), name_tag: tag,
}, },
scope: Scope::new(), input,
} }
} }

View File

@ -5,7 +5,10 @@ use nu_engine::run_block;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{
hir::{Block, CapturedBlock, ClassifiedCommand, Group, InternalCommand, Pipeline}, hir::{
Block, CapturedBlock, ClassifiedCommand, ExternalRedirection, Group, InternalCommand,
Pipeline,
},
Dictionary, Signature, SyntaxShape, UntaggedValue, Value, Dictionary, Signature, SyntaxShape, UntaggedValue, Value,
}; };
use rand::{ use rand::{
@ -47,7 +50,7 @@ impl WholeStreamCommand for Benchmark {
"Runs a block and returns the time it took to execute it." "Runs a block and returns the time it took to execute it."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
benchmark(args) benchmark(args)
} }
@ -67,11 +70,16 @@ impl WholeStreamCommand for Benchmark {
} }
} }
fn benchmark(raw_args: CommandArgs) -> Result<ActionStream, ShellError> { fn benchmark(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = raw_args.call_info.args.span; let tag = args.call_info.args.span;
let mut context = EvaluationContext::from_args(&raw_args); let mut context = EvaluationContext::from_args(&args);
let scope = raw_args.scope.clone(); let scope = args.scope().clone();
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process()?;
let args = args.evaluate_once()?;
let cmd_args = BenchmarkArgs {
block: args.req(0)?,
passthrough: args.get_flag("passthrough")?,
};
let env = scope.get_env_vars(); let env = scope.get_env_vars();
let name = generate_free_name(&env); let name = generate_free_name(&env);
@ -84,7 +92,13 @@ fn benchmark(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
// let start = time(); // let start = time();
context.scope.enter_scope(); context.scope.enter_scope();
let result = run_block(&block.block, &context, input); let result = run_block(
&cmd_args.block.block,
&context,
args.input,
ExternalRedirection::StdoutAndStderr,
);
context.scope.exit_scope(); context.scope.exit_scope();
let output = result?.into_vec(); let output = result?.into_vec();
@ -101,7 +115,7 @@ fn benchmark(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
let real_time = into_big_int(end_time - start_time); let real_time = into_big_int(end_time - start_time);
indexmap.insert("real time".to_string(), real_time); indexmap.insert("real time".to_string(), real_time);
benchmark_output(indexmap, output, passthrough, &tag, &mut context) benchmark_output(indexmap, output, cmd_args.passthrough, &tag, &mut context)
} }
// return advanced stats // return advanced stats
// #[cfg(feature = "rich-benchmark")] // #[cfg(feature = "rich-benchmark")]
@ -134,10 +148,10 @@ fn benchmark_output<T, Output>(
passthrough: Option<CapturedBlock>, passthrough: Option<CapturedBlock>,
tag: T, tag: T,
context: &mut EvaluationContext, context: &mut EvaluationContext,
) -> Result<ActionStream, ShellError> ) -> Result<OutputStream, ShellError>
where where
T: Into<Tag> + Copy, T: Into<Tag> + Copy,
Output: Into<ActionStream>, Output: Into<OutputStream>,
{ {
let value = UntaggedValue::Row(Dictionary::from( let value = UntaggedValue::Row(Dictionary::from(
indexmap indexmap
@ -154,14 +168,19 @@ where
let time_block = add_implicit_autoview(time_block.block); let time_block = add_implicit_autoview(time_block.block);
context.scope.enter_scope(); context.scope.enter_scope();
let result = run_block(&time_block, context, benchmark_output); let result = run_block(
&time_block,
context,
benchmark_output,
ExternalRedirection::StdoutAndStderr,
);
context.scope.exit_scope(); context.scope.exit_scope();
result?; result?;
context.clear_errors(); context.clear_errors();
Ok(block_output.into()) Ok(block_output.into())
} else { } else {
let benchmark_output = ActionStream::one(value); let benchmark_output = OutputStream::one(value);
Ok(benchmark_output) Ok(benchmark_output)
} }
} }

View File

@ -3,7 +3,7 @@ use nu_errors::ShellError;
use nu_data::value::format_leaf; use nu_data::value::format_leaf;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
pub struct BuildString; pub struct BuildString;
@ -21,7 +21,7 @@ impl WholeStreamCommand for BuildString {
"Builds a string from the arguments." "Builds a string from the arguments."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let args = args.evaluate_once()?; let args = args.evaluate_once()?;
let rest: Vec<Value> = args.rest(0)?; let rest: Vec<Value> = args.rest(0)?;
@ -32,9 +32,9 @@ impl WholeStreamCommand for BuildString {
output_string.push_str(&format_leaf(&r).plain_string(100_000)) output_string.push_str(&format_leaf(&r).plain_string(100_000))
} }
Ok(ActionStream::one(ReturnSuccess::value( Ok(OutputStream::one(
UntaggedValue::string(output_string).into_value(tag), UntaggedValue::string(output_string).into_value(tag),
))) ))
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use chrono::{Datelike, Local, NaiveDate}; use chrono::{Datelike, Local, NaiveDate};
use indexmap::IndexMap; use indexmap::IndexMap;
use nu_engine::{EvaluatedWholeStreamCommandArgs, WholeStreamCommand}; use nu_engine::{EvaluatedCommandArgs, WholeStreamCommand};
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
@ -165,7 +165,7 @@ fn get_current_date() -> (i32, u32, u32) {
} }
fn add_months_of_year_to_table( fn add_months_of_year_to_table(
args: &EvaluatedWholeStreamCommandArgs, args: &EvaluatedCommandArgs,
mut calendar_vec_deque: &mut VecDeque<Value>, mut calendar_vec_deque: &mut VecDeque<Value>,
tag: &Tag, tag: &Tag,
selected_year: i32, selected_year: i32,
@ -198,7 +198,7 @@ fn add_months_of_year_to_table(
} }
fn add_month_to_table( fn add_month_to_table(
args: &EvaluatedWholeStreamCommandArgs, args: &EvaluatedCommandArgs,
calendar_vec_deque: &mut VecDeque<Value>, calendar_vec_deque: &mut VecDeque<Value>,
tag: &Tag, tag: &Tag,
selected_year: i32, selected_year: i32,

View File

@ -26,7 +26,7 @@ impl WholeStreamCommand for Cd {
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let shell_manager = args.shell_manager.clone(); let shell_manager = args.shell_manager();
let (args, _): (CdArgs, _) = args.process()?; let (args, _): (CdArgs, _) = args.process()?;
shell_manager.cd(args, name) shell_manager.cd(args, name)
} }

View File

@ -1,11 +1,108 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::{FromValue, WholeStreamCommand}; use nu_engine::{FromValue, WholeStreamCommand};
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
use nu_source::Tagged; use nu_source::Tagged;
use indexmap::indexmap;
use indexmap::map::IndexMap;
use lazy_static::lazy_static;
pub struct Char; pub struct Char;
struct CharArgs {
name: Option<Tagged<String>>,
rest: Vec<Value>,
list: bool,
unicode: bool,
}
lazy_static! {
static ref CHAR_MAP: IndexMap<&'static str, String> = indexmap! {
// These are some regular characters that either can't used or
// it's just easier to use them like this.
"newline" => '\n'.to_string(),
"enter" => '\n'.to_string(),
"nl" => '\n'.to_string(),
"tab" => '\t'.to_string(),
"sp" => ' '.to_string(),
"space" => ' '.to_string(),
"pipe" => '|'.to_string(),
"left_brace" => '{'.to_string(),
"lbrace" => '{'.to_string(),
"right_brace" => '}'.to_string(),
"rbrace" => '}'.to_string(),
"left_paren" => '('.to_string(),
"lparen" => '('.to_string(),
"right_paren" => ')'.to_string(),
"rparen" => ')'.to_string(),
"left_bracket" => '['.to_string(),
"lbracket" => '['.to_string(),
"right_bracket" => ']'.to_string(),
"rbracket" => ']'.to_string(),
"sep" => std::path::MAIN_SEPARATOR.to_string(),
"separator" => std::path::MAIN_SEPARATOR.to_string(),
// Unicode names came from https://www.compart.com/en/unicode
// Private Use Area (U+E000-U+F8FF)
// Unicode can't be mixed with Ansi or it will break width calculation
"branch" => '\u{e0a0}'.to_string(), // 
"segment" => '\u{e0b0}'.to_string(), // 
"identical_to" => '\u{2261}'.to_string(), // ≡
"hamburger" => '\u{2261}'.to_string(), // ≡
"not_identical_to" => '\u{2262}'.to_string(), // ≢
"branch_untracked" => '\u{2262}'.to_string(), // ≢
"strictly_equivalent_to" => '\u{2263}'.to_string(), // ≣
"branch_identical" => '\u{2263}'.to_string(), // ≣
"upwards_arrow" => '\u{2191}'.to_string(), // ↑
"branch_ahead" => '\u{2191}'.to_string(), // ↑
"downwards_arrow" => '\u{2193}'.to_string(), // ↓
"branch_behind" => '\u{2193}'.to_string(), // ↓
"up_down_arrow" => '\u{2195}'.to_string(), // ↕
"branch_ahead_behind" => '\u{2195}'.to_string(), // ↕
"black_right_pointing_triangle" => '\u{25b6}'.to_string(), // ▶
"prompt" => '\u{25b6}'.to_string(), // ▶
"vector_or_cross_product" => '\u{2a2f}'.to_string(), //
"failed" => '\u{2a2f}'.to_string(), //
"high_voltage_sign" => '\u{26a1}'.to_string(), // ⚡
"elevated" => '\u{26a1}'.to_string(), // ⚡
"tilde" => '~'.to_string(), // ~
"twiddle" => '~'.to_string(), // ~
"squiggly" => '~'.to_string(), // ~
"home" => '~'.to_string(), // ~
"hash" => '#'.to_string(), // #
"hashtag" => '#'.to_string(), // #
"pound_sign" => '#'.to_string(), // #
"sharp" => '#'.to_string(), // #
"root" => '#'.to_string(), // #
// Weather symbols
"sun" => "☀️".to_string(),
"sunny" => "☀️".to_string(),
"sunrise" => "☀️".to_string(),
"moon" => "🌛".to_string(),
"cloudy" => "☁️".to_string(),
"cloud" => "☁️".to_string(),
"clouds" => "☁️".to_string(),
"rainy" => "🌦️".to_string(),
"rain" => "🌦️".to_string(),
"foggy" => "🌫️".to_string(),
"fog" => "🌫️".to_string(),
"mist" => '\u{2591}'.to_string(),
"haze" => '\u{2591}'.to_string(),
"snowy" => "❄️".to_string(),
"snow" => "❄️".to_string(),
"thunderstorm" => "🌩️".to_string(),
"thunder" => "🌩️".to_string(),
"bel" => '\x07'.to_string(), // Terminal Bell
"backspace" => '\x08'.to_string(), // Backspace
};
}
impl WholeStreamCommand for Char { impl WholeStreamCommand for Char {
fn name(&self) -> &str { fn name(&self) -> &str {
"char" "char"
@ -13,17 +110,18 @@ impl WholeStreamCommand for Char {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("char") Signature::build("char")
.required( .optional(
"character", "character",
SyntaxShape::Any, SyntaxShape::Any,
"the name of the character to output", "the name of the character to output",
) )
.rest(SyntaxShape::String, "multiple Unicode bytes") .rest(SyntaxShape::String, "multiple Unicode bytes")
.switch("list", "List all supported character names", Some('l'))
.switch("unicode", "Unicode string i.e. 1f378", Some('u')) .switch("unicode", "Unicode string i.e. 1f378", Some('u'))
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Output special characters (eg. 'newline')." "Output special characters (e.g., 'newline')."
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -35,7 +133,7 @@ impl WholeStreamCommand for Char {
}, },
Example { Example {
description: "Output prompt character, newline and a hamburger character", description: "Output prompt character, newline and a hamburger character",
example: r#"echo $(char prompt) $(char newline) $(char hamburger)"#, example: r#"echo (char prompt) (char newline) (char hamburger)"#,
result: Some(vec![ result: Some(vec![
UntaggedValue::string("\u{25b6}").into(), UntaggedValue::string("\u{25b6}").into(),
UntaggedValue::string("\n").into(), UntaggedValue::string("\n").into(),
@ -57,62 +155,86 @@ impl WholeStreamCommand for Char {
] ]
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let args_tag = args.call_info.name_tag.clone();
let args = args.evaluate_once()?; let args = args.evaluate_once()?;
let args = CharArgs {
name: args.opt(0)?,
rest: args.rest(1)?,
list: args.has_flag("list"),
unicode: args.has_flag("unicode"),
};
let name: Tagged<String> = args.req(0)?; if args.list {
let rest: Vec<Value> = args.rest(1)?; Ok(CHAR_MAP
let unicode = args.has_flag("unicode"); .iter()
.map(move |(name, s)| {
if unicode { let mut dict = TaggedDictBuilder::with_capacity(&args_tag, 2);
if !rest.is_empty() { dict.insert_untagged("name", UntaggedValue::string(*name));
// Setup a new buffer to put all the Unicode bytes in dict.insert_untagged("character", UntaggedValue::string(s));
let mut multi_byte = String::new(); let unicode_parts: Vec<String> =
// Get the first byte s.chars().map(|c| format!("{:x}", c as u32)).collect();
let decoded_char = string_to_unicode_char(&name.item, &name.tag); dict.insert_untagged("unicode", UntaggedValue::string(unicode_parts.join(" ")));
match decoded_char { dict.into_value()
Ok(ch) => multi_byte.push(ch), })
Err(e) => return Err(e), .to_output_stream())
} } else if let Some(name) = args.name {
// Get the rest of the bytes if args.unicode {
for byte_part in rest { if !args.rest.is_empty() {
let byte_part: Tagged<String> = FromValue::from_value(&byte_part)?; // Setup a new buffer to put all the Unicode bytes in
let decoded_char = string_to_unicode_char(&byte_part, &byte_part.tag); let mut multi_byte = String::new();
// Get the first byte
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
match decoded_char { match decoded_char {
Ok(ch) => multi_byte.push(ch), Ok(ch) => multi_byte.push(ch),
Err(e) => return Err(e), Err(e) => return Err(e),
} }
// Get the rest of the bytes
for byte_part in args.rest {
let byte_part: Tagged<String> = FromValue::from_value(&byte_part)?;
let decoded_char = string_to_unicode_char(&byte_part, &byte_part.tag);
match decoded_char {
Ok(ch) => multi_byte.push(ch),
Err(e) => return Err(e),
}
}
Ok(OutputStream::one(
UntaggedValue::string(multi_byte).into_value(name.tag),
))
} else {
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
if let Ok(ch) = decoded_char {
Ok(OutputStream::one(
UntaggedValue::string(ch).into_value(name.tag()),
))
} else {
Err(ShellError::labeled_error(
"error decoding Unicode character",
"error decoding Unicode character",
name.tag(),
))
}
} }
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(multi_byte).into_value(name.tag),
)))
} else { } else {
let decoded_char = string_to_unicode_char(&name.item, &name.tag); let special_character = str_to_character(&name.item);
if let Ok(ch) = decoded_char { if let Some(output) = special_character {
Ok(ActionStream::one(ReturnSuccess::value( Ok(OutputStream::one(
UntaggedValue::string(ch).into_value(name.tag()), UntaggedValue::string(output).into_value(name.tag()),
))) ))
} else { } else {
Err(ShellError::labeled_error( Err(ShellError::labeled_error(
"error decoding Unicode character", "error finding named character",
"error decoding Unicode character", "error finding named character",
name.tag(), name.tag(),
)) ))
} }
} }
} else { } else {
let special_character = str_to_character(&name.item); Err(ShellError::labeled_error(
if let Some(output) = special_character { "char requires the name of the character",
Ok(ActionStream::one(ReturnSuccess::value( "missing name of the character",
UntaggedValue::string(output).into_value(name.tag()), &args_tag,
))) ))
} else {
Err(ShellError::labeled_error(
"error finding named character",
"error finding named character",
name.tag(),
))
}
} }
} }
} }
@ -134,45 +256,7 @@ fn string_to_unicode_char(s: &str, t: &Tag) -> Result<char, ShellError> {
} }
fn str_to_character(s: &str) -> Option<String> { fn str_to_character(s: &str) -> Option<String> {
match s { CHAR_MAP.get(s).map(|s| s.into())
"newline" | "enter" | "nl" => Some("\n".into()),
"tab" => Some("\t".into()),
"sp" | "space" => Some(" ".into()),
// Unicode names came from https://www.compart.com/en/unicode
// Private Use Area (U+E000-U+F8FF)
// Unicode can't be mixed with Ansi or it will break width calculation
"branch" => Some('\u{e0a0}'.to_string()), // 
"segment" => Some('\u{e0b0}'.to_string()), // 
"identical_to" | "hamburger" => Some('\u{2261}'.to_string()), // ≡
"not_identical_to" | "branch_untracked" => Some('\u{2262}'.to_string()), // ≢
"strictly_equivalent_to" | "branch_identical" => Some('\u{2263}'.to_string()), // ≣
"upwards_arrow" | "branch_ahead" => Some('\u{2191}'.to_string()), // ↑
"downwards_arrow" | "branch_behind" => Some('\u{2193}'.to_string()), // ↓
"up_down_arrow" | "branch_ahead_behind" => Some('\u{2195}'.to_string()), // ↕
"black_right_pointing_triangle" | "prompt" => Some('\u{25b6}'.to_string()), // ▶
"vector_or_cross_product" | "failed" => Some('\u{2a2f}'.to_string()), //
"high_voltage_sign" | "elevated" => Some('\u{26a1}'.to_string()), // ⚡
"tilde" | "twiddle" | "squiggly" | "home" => Some("~".into()), // ~
"hash" | "hashtag" | "pound_sign" | "sharp" | "root" => Some("#".into()), // #
// Weather symbols
"sun" | "sunny" | "sunrise" => Some("☀️".to_string()),
"moon" => Some("🌛".to_string()),
"cloudy" | "cloud" | "clouds" => Some("☁️".to_string()),
"rainy" | "rain" => Some("🌦️".to_string()),
"foggy" | "fog" => Some("🌫️".to_string()),
"mist" | "haze" => Some("\u{2591}".to_string()),
"snowy" | "snow" => Some("❄️".to_string()),
"thunderstorm" | "thunder" => Some("🌩️".to_string()),
"bel" => Some('\x07'.to_string()), // Terminal Bell
"backspace" => Some('\x08'.to_string()), // Backspace
_ => None,
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -20,14 +20,14 @@ impl WholeStreamCommand for Chart {
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
if args.scope.get_command("chart bar").is_none() { if args.scope().get_command("chart bar").is_none() {
return Err(ShellError::untagged_runtime_error( return Err(ShellError::untagged_runtime_error(
"nu_plugin_chart not installed.", "nu_plugin_chart not installed.",
)); ));
} }
Ok(ActionStream::one(Ok(ReturnSuccess::Value( Ok(ActionStream::one(Ok(ReturnSuccess::Value(
UntaggedValue::string(get_full_help(&Chart, &args.scope)).into_value(Tag::unknown()), UntaggedValue::string(get_full_help(&Chart, args.scope())).into_value(Tag::unknown()),
)))) ))))
} }
} }

View File

@ -1,6 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::{evaluate_baseline_expr, BufCodecReader}; use nu_engine::{evaluate_baseline_expr, BufCodecReader};
use nu_engine::{MaybeTextCodec, StringOrBinary}; use nu_engine::{MaybeTextCodec, StringOrBinary};
use nu_test_support::NATIVE_PATH_ENV_VAR;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::io::Write; use std::io::Write;
@ -149,7 +150,9 @@ fn spawn(
process.arg(&command.name); process.arg(&command.name);
for arg in args { for arg in args {
// Clean the args before we use them: // Clean the args before we use them:
let arg = arg.replace("|", "\\|"); // https://stackoverflow.com/questions/1200235/how-to-pass-a-quoted-pipe-character-to-cmd-exe
// cmd.exe needs to have a caret to escape a pipe
let arg = arg.replace("|", "^|");
process.arg(&arg); process.arg(&arg);
} }
process process
@ -199,247 +202,250 @@ fn spawn(
trace!(target: "nu::run::external", "built command {:?}", process); trace!(target: "nu::run::external", "built command {:?}", process);
// TODO Switch to async_std::process once it's stabilized // TODO Switch to async_std::process once it's stabilized
if let Ok(mut child) = process.spawn() { match process.spawn() {
let (tx, rx) = mpsc::sync_channel(0); Ok(mut child) => {
let (tx, rx) = mpsc::sync_channel(0);
let mut stdin = child.stdin.take(); let mut stdin = child.stdin.take();
let stdin_write_tx = tx.clone(); let stdin_write_tx = tx.clone();
let stdout_read_tx = tx; let stdout_read_tx = tx;
let stdin_name_tag = command.name_tag.clone(); let stdin_name_tag = command.name_tag.clone();
let stdout_name_tag = command.name_tag; let stdout_name_tag = command.name_tag;
std::thread::spawn(move || { std::thread::spawn(move || {
if !input.is_empty() { if !input.is_empty() {
let mut stdin_write = stdin let mut stdin_write = stdin
.take() .take()
.expect("Internal error: could not get stdin pipe for external command"); .expect("Internal error: could not get stdin pipe for external command");
for value in input { for value in input {
match &value.value { match &value.value {
UntaggedValue::Primitive(Primitive::Nothing) => continue, UntaggedValue::Primitive(Primitive::Nothing) => continue,
UntaggedValue::Primitive(Primitive::String(s)) => { UntaggedValue::Primitive(Primitive::String(s)) => {
if stdin_write.write(s.as_bytes()).is_err() { if stdin_write.write(s.as_bytes()).is_err() {
// Other side has closed, so exit // Other side has closed, so exit
return Ok(()); return Ok(());
}
}
UntaggedValue::Primitive(Primitive::Binary(b)) => {
if stdin_write.write(b).is_err() {
// Other side has closed, so exit
return Ok(());
}
}
unsupported => {
println!("Unsupported: {:?}", unsupported);
let _ = stdin_write_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
format!(
"Received unexpected type from pipeline ({})",
unsupported.type_name()
),
"expected a string",
stdin_name_tag.clone(),
)),
tag: stdin_name_tag,
}));
return Err(());
}
};
}
}
Ok(())
});
std::thread::spawn(move || {
if external_redirection == ExternalRedirection::Stdout
|| external_redirection == ExternalRedirection::StdoutAndStderr
{
let stdout = if let Some(stdout) = child.stdout.take() {
stdout
} else {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
"Can't redirect the stdout for external command",
"can't redirect stdout",
&stdout_name_tag,
)),
tag: stdout_name_tag,
}));
return Err(());
};
// let file = futures::io::AllowStdIo::new(stdout);
// let stream = FramedRead::new(file, MaybeTextCodec::default());
let buf_read = BufReader::new(stdout);
let buf_codec = BufCodecReader::new(buf_read, MaybeTextCodec::default());
for line in buf_codec {
match line {
Ok(line) => match line {
StringOrBinary::String(s) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Primitive(Primitive::String(s.clone())),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
} }
} }
StringOrBinary::Binary(b) => { UntaggedValue::Primitive(Primitive::Binary(b)) => {
let result = stdout_read_tx.send(Ok(Value { if stdin_write.write(b).is_err() {
value: UntaggedValue::Primitive(Primitive::Binary( // Other side has closed, so exit
b.into_iter().collect(), return Ok(());
)),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
} }
} }
}, unsupported => {
Err(e) => { println!("Unsupported: {:?}", unsupported);
// If there's an exit status, it makes sense that we may error when let _ = stdin_write_tx.send(Ok(Value {
// trying to read from its stdout pipe (likely been closed). In that
// case, don't emit an error.
let should_error = match child.wait() {
Ok(exit_status) => !exit_status.success(),
Err(_) => true,
};
if should_error {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error( value: UntaggedValue::Error(ShellError::labeled_error(
format!("Unable to read from stdout ({})", e), format!(
"unable to read from stdout", "Received unexpected type from pipeline ({})",
&stdout_name_tag, unsupported.type_name()
),
"expected a string",
stdin_name_tag.clone(),
)), )),
tag: stdout_name_tag.clone(), tag: stdin_name_tag,
})); }));
return Err(());
} }
};
return Ok(());
}
} }
} }
}
if external_redirection == ExternalRedirection::Stderr
|| external_redirection == ExternalRedirection::StdoutAndStderr
{
let stderr = if let Some(stderr) = child.stderr.take() {
stderr
} else {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
"Can't redirect the stderr for external command",
"can't redirect stderr",
&stdout_name_tag,
)),
tag: stdout_name_tag,
}));
return Err(());
};
// let file = futures::io::AllowStdIo::new(stderr); Ok(())
// 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 buf_codec { std::thread::spawn(move || {
match line { if external_redirection == ExternalRedirection::Stdout
Ok(line) => match line { || external_redirection == ExternalRedirection::StdoutAndStderr
StringOrBinary::String(s) => { {
let result = stdout_read_tx.send(Ok(Value { let stdout = if let Some(stdout) = child.stdout.take() {
value: UntaggedValue::Error( stdout
ShellError::untagged_runtime_error(s), } else {
),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
}
}
StringOrBinary::Binary(_) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(
ShellError::untagged_runtime_error("<binary stderr>"),
),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
}
}
},
Err(e) => {
// If there's an exit status, it makes sense that we may error when
// trying to read from its stdout pipe (likely been closed). In that
// case, don't emit an error.
let should_error = match child.wait() {
Ok(exit_status) => !exit_status.success(),
Err(_) => true,
};
if should_error {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
format!("Unable to read from stdout ({})", e),
"unable to read from stdout",
&stdout_name_tag,
)),
tag: stdout_name_tag.clone(),
}));
}
return Ok(());
}
}
}
}
// We can give an error when we see a non-zero exit code, but this is different
// than what other shells will do.
let external_failed = match child.wait() {
Err(_) => true,
Ok(exit_status) => !exit_status.success(),
};
if external_failed {
let cfg = nu_data::config::config(Tag::unknown());
if let Ok(cfg) = cfg {
if cfg.contains_key("nonzero_exit_errors") {
let _ = stdout_read_tx.send(Ok(Value { let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error( value: UntaggedValue::Error(ShellError::labeled_error(
"External command failed", "Can't redirect the stdout for external command",
"command failed", "can't redirect stdout",
&stdout_name_tag, &stdout_name_tag,
)), )),
tag: stdout_name_tag.clone(), tag: stdout_name_tag,
})); }));
return Err(());
};
// let file = futures::io::AllowStdIo::new(stdout);
// let stream = FramedRead::new(file, MaybeTextCodec::default());
let buf_read = BufReader::new(stdout);
let buf_codec = BufCodecReader::new(buf_read, MaybeTextCodec::default());
for line in buf_codec {
match line {
Ok(line) => match line {
StringOrBinary::String(s) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Primitive(Primitive::String(
s.clone(),
)),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
}
}
StringOrBinary::Binary(b) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Primitive(Primitive::Binary(
b.into_iter().collect(),
)),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
}
}
},
Err(e) => {
// If there's an exit status, it makes sense that we may error when
// trying to read from its stdout pipe (likely been closed). In that
// case, don't emit an error.
let should_error = match child.wait() {
Ok(exit_status) => !exit_status.success(),
Err(_) => true,
};
if should_error {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
format!("Unable to read from stdout ({})", e),
"unable to read from stdout",
&stdout_name_tag,
)),
tag: stdout_name_tag.clone(),
}));
}
return Ok(());
}
}
} }
} }
let _ = stdout_read_tx.send(Ok(Value { if external_redirection == ExternalRedirection::Stderr
value: UntaggedValue::Error(ShellError::external_non_zero()), || external_redirection == ExternalRedirection::StdoutAndStderr
tag: stdout_name_tag, {
})); let stderr = if let Some(stderr) = child.stderr.take() {
} stderr
} else {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
"Can't redirect the stderr for external command",
"can't redirect stderr",
&stdout_name_tag,
)),
tag: stdout_name_tag,
}));
return Err(());
};
Ok(()) // let file = futures::io::AllowStdIo::new(stderr);
}); // let stream = FramedRead::new(file, MaybeTextCodec::default());
let buf_reader = BufReader::new(stderr);
let buf_codec = BufCodecReader::new(buf_reader, MaybeTextCodec::default());
let stream = ChannelReceiver::new(rx); for line in buf_codec {
Ok(stream.to_input_stream()) match line {
} else { Ok(line) => match line {
Err(ShellError::labeled_error( StringOrBinary::String(s) => {
"Failed to spawn process", let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(
ShellError::untagged_runtime_error(s),
),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
}
}
StringOrBinary::Binary(_) => {
let result = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(
ShellError::untagged_runtime_error("<binary stderr>"),
),
tag: stdout_name_tag.clone(),
}));
if result.is_err() {
break;
}
}
},
Err(e) => {
// If there's an exit status, it makes sense that we may error when
// trying to read from its stdout pipe (likely been closed). In that
// case, don't emit an error.
let should_error = match child.wait() {
Ok(exit_status) => !exit_status.success(),
Err(_) => true,
};
if should_error {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
format!("Unable to read from stdout ({})", e),
"unable to read from stdout",
&stdout_name_tag,
)),
tag: stdout_name_tag.clone(),
}));
}
return Ok(());
}
}
}
}
// We can give an error when we see a non-zero exit code, but this is different
// than what other shells will do.
let external_failed = match child.wait() {
Err(_) => true,
Ok(exit_status) => !exit_status.success(),
};
if external_failed {
let cfg = nu_data::config::config(Tag::unknown());
if let Ok(cfg) = cfg {
if cfg.contains_key("nonzero_exit_errors") {
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
"External command failed",
"command failed",
&stdout_name_tag,
)),
tag: stdout_name_tag.clone(),
}));
}
}
let _ = stdout_read_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::external_non_zero()),
tag: stdout_name_tag,
}));
}
Ok(())
});
let stream = ChannelReceiver::new(rx);
Ok(stream.to_input_stream())
}
Err(e) => Err(ShellError::labeled_error(
format!("{}", e),
"failed to spawn", "failed to spawn",
&command.name_tag, &command.name_tag,
)) )),
} }
} }
@ -515,7 +521,7 @@ fn remove_quotes(argument: &str) -> Option<&str> {
fn shell_os_paths() -> Vec<std::path::PathBuf> { fn shell_os_paths() -> Vec<std::path::PathBuf> {
let mut original_paths = vec![]; let mut original_paths = vec![];
if let Some(paths) = std::env::var_os("PATH") { if let Some(paths) = std::env::var_os(NATIVE_PATH_ENV_VAR) {
original_paths = std::env::split_paths(&paths).collect::<Vec<_>>(); original_paths = std::env::split_paths(&paths).collect::<Vec<_>>();
} }
@ -531,7 +537,7 @@ mod tests {
use super::{run_external_command, InputStream}; use super::{run_external_command, InputStream};
#[cfg(feature = "which")] #[cfg(feature = "which")]
use nu_engine::basic_evaluation_context; use nu_engine::EvaluationContext;
#[cfg(feature = "which")] #[cfg(feature = "which")]
use nu_test_support::commands::ExternalBuilder; use nu_test_support::commands::ExternalBuilder;
@ -554,8 +560,7 @@ mod tests {
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build(); let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
let input = InputStream::empty(); let input = InputStream::empty();
let mut ctx = let mut ctx = EvaluationContext::basic();
basic_evaluation_context().expect("There was a problem creating a basic context.");
assert!(run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout).is_err()); assert!(run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout).is_err());
} }
@ -563,7 +568,7 @@ mod tests {
// fn failure_run() -> Result<(), ShellError> { // fn failure_run() -> Result<(), ShellError> {
// let cmd = ExternalBuilder::for_name("fail").build(); // let cmd = ExternalBuilder::for_name("fail").build();
// let mut ctx = crate::cli::basic_evaluation_context().expect("There was a problem creating a basic context."); // let mut ctx = crate::cli::EvaluationContext::basic().expect("There was a problem creating a basic context.");
// let stream = run_external_command(cmd, &mut ctx, None, false) // let stream = run_external_command(cmd, &mut ctx, None, false)
// ? // ?
// .expect("There was a problem running the external command."); // .expect("There was a problem running the external command.");

View File

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; use nu_protocol::{Signature, UntaggedValue};
pub struct SubCommand; pub struct SubCommand;
@ -18,7 +18,7 @@ impl WholeStreamCommand for SubCommand {
"clear the config" "clear the config"
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
clear(args) clear(args)
} }
@ -31,22 +31,22 @@ impl WholeStreamCommand for SubCommand {
} }
} }
pub fn clear(args: CommandArgs) -> Result<ActionStream, ShellError> { pub fn clear(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args); let ctx = EvaluationContext::from_args(&args);
let result = if let Some(global_cfg) = &mut args.configs.lock().global_config { let result = if let Some(global_cfg) = &mut args.configs().lock().global_config {
global_cfg.vars.clear(); global_cfg.vars.clear();
global_cfg.write()?; global_cfg.write()?;
ctx.reload_config(global_cfg)?; ctx.reload_config(global_cfg)?;
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::Row(global_cfg.vars.clone().into()).into_value(args.call_info.name_tag), let value = UntaggedValue::Row(global_cfg.vars.clone().into()).into_value(name);
))) Ok(OutputStream::one(value))
} else { } else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error( let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
crate::commands::config::err_no_global_cfg_present(), .into_value(name);
))]
.into_iter() Ok(OutputStream::one(value))
.to_action_stream())
}; };
result result

View File

@ -2,8 +2,7 @@ use crate::prelude::*;
use nu_engine::CommandArgs; use nu_engine::CommandArgs;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; use nu_protocol::{Signature, UntaggedValue};
use nu_stream::ActionStream;
pub struct Command; pub struct Command;
@ -20,22 +19,19 @@ impl WholeStreamCommand for Command {
"Configuration management." "Configuration management."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag; let name = args.call_info.name_tag.clone();
if let Some(global_cfg) = &args.configs.lock().global_config { if let Some(global_cfg) = &args.configs().lock().global_config {
let result = global_cfg.vars.clone(); let result = global_cfg.vars.clone();
Ok(vec![ReturnSuccess::value( let value = UntaggedValue::Row(result.into()).into_value(name);
UntaggedValue::Row(result.into()).into_value(name),
)] Ok(OutputStream::one(value))
.into_iter()
.to_action_stream())
} else { } else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error( let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
crate::commands::config::err_no_global_cfg_present(), .into_value(name);
))]
.into_iter() Ok(OutputStream::one(value))
.to_action_stream())
} }
} }
} }

View File

@ -1,15 +1,10 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
pub struct SubCommand; pub struct SubCommand;
#[derive(Deserialize)]
pub struct Arguments {
column_path: ColumnPath,
}
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"config get" "config get"
@ -27,7 +22,7 @@ impl WholeStreamCommand for SubCommand {
"Gets a value from the config" "Gets a value from the config"
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
get(args) get(args)
} }
@ -40,11 +35,12 @@ impl WholeStreamCommand for SubCommand {
} }
} }
pub fn get(args: CommandArgs) -> Result<ActionStream, ShellError> { pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args); let ctx = EvaluationContext::from_args(&args);
let args = args.evaluate_once()?;
let (Arguments { column_path }, _) = args.process()?; let column_path = args.req(0)?;
let result = if let Some(global_cfg) = &ctx.configs.lock().global_config { let result = if let Some(global_cfg) = &ctx.configs.lock().global_config {
let result = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name); let result = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
@ -53,15 +49,14 @@ pub fn get(args: CommandArgs) -> Result<ActionStream, ShellError> {
Value { Value {
value: UntaggedValue::Table(list), value: UntaggedValue::Table(list),
.. ..
} => list.into_iter().to_action_stream(), } => OutputStream::from_stream(list.into_iter()),
x => ActionStream::one(ReturnSuccess::value(x)), x => OutputStream::one(x),
}) })
} else { } else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error( let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
crate::commands::config::err_no_global_cfg_present(), .into_value(name);
))]
.into_iter() Ok(OutputStream::one(value))
.to_action_stream())
}; };
result result

View File

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue}; use nu_protocol::{Primitive, Signature, UntaggedValue};
pub struct SubCommand; pub struct SubCommand;
@ -18,7 +18,7 @@ impl WholeStreamCommand for SubCommand {
"return the path to the config file" "return the path to the config file"
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
path(args) path(args)
} }
@ -31,16 +31,18 @@ impl WholeStreamCommand for SubCommand {
} }
} }
pub fn path(args: CommandArgs) -> Result<ActionStream, ShellError> { pub fn path(args: CommandArgs) -> Result<OutputStream, ShellError> {
if let Some(global_cfg) = &mut args.configs.lock().global_config { let name = args.call_info.name_tag.clone();
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::Primitive(Primitive::FilePath(global_cfg.file_path.clone())), if let Some(global_cfg) = &mut args.configs().lock().global_config {
))) let value = UntaggedValue::Primitive(Primitive::FilePath(global_cfg.file_path.clone()))
.into_value(name);
Ok(OutputStream::one(value))
} else { } else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error( let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
crate::commands::config::err_no_global_cfg_present(), .into_value(name);
))]
.into_iter() Ok(OutputStream::one(value))
.to_action_stream())
} }
} }

View File

@ -1,16 +1,11 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged; use nu_source::Tagged;
pub struct SubCommand; pub struct SubCommand;
#[derive(Deserialize)]
pub struct Arguments {
remove: Tagged<String>,
}
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"config remove" "config remove"
@ -28,7 +23,7 @@ impl WholeStreamCommand for SubCommand {
"Removes a value from the config" "Removes a value from the config"
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
remove(args) remove(args)
} }
@ -41,9 +36,11 @@ impl WholeStreamCommand for SubCommand {
} }
} }
pub fn remove(args: CommandArgs) -> Result<ActionStream, ShellError> { pub fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args); let ctx = EvaluationContext::from_args(&args);
let (Arguments { remove }, _) = args.process()?; let args = args.evaluate_once()?;
let remove: Tagged<String> = args.req(0)?;
let key = remove.to_string(); let key = remove.to_string();
@ -52,11 +49,10 @@ pub fn remove(args: CommandArgs) -> Result<ActionStream, ShellError> {
global_cfg.vars.swap_remove(&key); global_cfg.vars.swap_remove(&key);
global_cfg.write()?; global_cfg.write()?;
ctx.reload_config(global_cfg)?; ctx.reload_config(global_cfg)?;
Ok(vec![ReturnSuccess::value(
UntaggedValue::row(global_cfg.vars.clone()).into_value(remove.tag()), let value: Value = UntaggedValue::row(global_cfg.vars.clone()).into_value(remove.tag);
)]
.into_iter() Ok(OutputStream::one(value))
.to_action_stream())
} else { } else {
Err(ShellError::labeled_error( Err(ShellError::labeled_error(
"Key does not exist in config", "Key does not exist in config",
@ -65,11 +61,10 @@ pub fn remove(args: CommandArgs) -> Result<ActionStream, ShellError> {
)) ))
} }
} else { } else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error( let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
crate::commands::config::err_no_global_cfg_present(), .into_value(name);
))]
.into_iter() Ok(OutputStream::one(value))
.to_action_stream())
}; };
result result

View File

@ -1,16 +1,10 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
pub struct SubCommand; pub struct SubCommand;
#[derive(Deserialize)]
pub struct Arguments {
column_path: ColumnPath,
value: Value,
}
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"config set" "config set"
@ -26,7 +20,7 @@ impl WholeStreamCommand for SubCommand {
"Sets a value in the config" "Sets a value in the config"
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
set(args) set(args)
} }
@ -56,16 +50,13 @@ impl WholeStreamCommand for SubCommand {
} }
} }
pub fn set(args: CommandArgs) -> Result<ActionStream, ShellError> { pub fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args); let ctx = EvaluationContext::from_args(&args);
let ( let args = args.evaluate_once()?;
Arguments {
column_path, let column_path = args.req(0)?;
mut value, let mut value: Value = args.req(1)?;
},
_,
) = args.process()?;
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config { let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
let configuration = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name); let configuration = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
@ -85,19 +76,17 @@ pub fn set(args: CommandArgs) -> Result<ActionStream, ShellError> {
global_cfg.write()?; global_cfg.write()?;
ctx.reload_config(global_cfg)?; ctx.reload_config(global_cfg)?;
Ok(ActionStream::one(ReturnSuccess::value( let value = UntaggedValue::row(global_cfg.vars.clone()).into_value(name);
UntaggedValue::row(global_cfg.vars.clone()).into_value(name), Ok(OutputStream::one(value))
)))
} }
Ok(_) => Ok(ActionStream::empty()), Ok(_) => Ok(OutputStream::empty()),
Err(reason) => Err(reason), Err(reason) => Err(reason),
} }
} else { } else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error( let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
crate::commands::config::err_no_global_cfg_present(), .into_value(name);
))]
.into_iter() Ok(OutputStream::one(value))
.to_action_stream())
}; };
result result

View File

@ -1,16 +1,11 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged; use nu_source::Tagged;
pub struct SubCommand; pub struct SubCommand;
#[derive(Deserialize)]
pub struct Arguments {
set_into: Tagged<String>,
}
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"config set_into" "config set_into"
@ -28,7 +23,7 @@ impl WholeStreamCommand for SubCommand {
"Sets a value in the config" "Sets a value in the config"
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
set_into(args) set_into(args)
} }
@ -41,20 +36,22 @@ impl WholeStreamCommand for SubCommand {
} }
} }
pub fn set_into(args: CommandArgs) -> Result<ActionStream, ShellError> { pub fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args); let ctx = EvaluationContext::from_args(&args);
let (Arguments { set_into: v }, input) = args.process()?; let args = args.evaluate_once()?;
let rows: Vec<Value> = input.collect(); let set_into: Tagged<String> = args.req(0)?;
let key = v.to_string();
let rows: Vec<Value> = args.input.collect();
let key = set_into.to_string();
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config { let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
if rows.is_empty() { if rows.is_empty() {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"No values given for set_into", "No values given for set_into",
"needs value(s) from pipeline", "needs value(s) from pipeline",
v.tag(), set_into.tag(),
)); ));
} else if rows.len() == 1 { } else if rows.len() == 1 {
// A single value // A single value
@ -71,15 +68,14 @@ pub fn set_into(args: CommandArgs) -> Result<ActionStream, ShellError> {
global_cfg.write()?; global_cfg.write()?;
ctx.reload_config(global_cfg)?; ctx.reload_config(global_cfg)?;
Ok(ActionStream::one(ReturnSuccess::value( let value = UntaggedValue::row(global_cfg.vars.clone()).into_value(name);
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
))) Ok(OutputStream::one(value))
} else { } else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error( let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
crate::commands::config::err_no_global_cfg_present(), .into_value(name);
))]
.into_iter() Ok(OutputStream::one(value))
.to_action_stream())
}; };
result result

View File

@ -26,7 +26,7 @@ impl WholeStreamCommand for Cpy {
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let shell_manager = args.shell_manager.clone(); let shell_manager = args.shell_manager();
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let (args, _) = args.process()?; let (args, _) = args.process()?;
shell_manager.cp(args, name) shell_manager.cp(args, name)

View File

@ -0,0 +1,202 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged;
use polars::frame::groupby::GroupBy;
use super::utils::convert_columns;
enum Operation {
Mean,
Sum,
Min,
Max,
First,
Last,
Nunique,
Quantile(f64),
Median,
Var,
Std,
Count,
}
impl Operation {
fn from_tagged(
name: &Tagged<String>,
quantile: Option<Tagged<f64>>,
) -> Result<Operation, ShellError> {
match name.item.as_ref() {
"mean" => Ok(Operation::Mean),
"sum" => Ok(Operation::Sum),
"min" => Ok(Operation::Min),
"max" => Ok(Operation::Max),
"first" => Ok(Operation::First),
"last" => Ok(Operation::Last),
"nunique" => Ok(Operation::Nunique),
"quantile" => {
match quantile {
None => Err(ShellError::labeled_error(
"Quantile value not fount",
"Quantile operation requires quantile value",
&name.tag,
)),
Some(value ) => {
if (value.item < 0.0) | (value.item > 1.0) {
Err(ShellError::labeled_error(
"Inappropriate quantile",
"Quantile value should be between 0.0 and 1.0",
&value.tag,
))
} else {
Ok(Operation::Quantile(value.item))
}
}
}
}
"median" => Ok(Operation::Median),
"var" => Ok(Operation::Var),
"std" => Ok(Operation::Std),
"count" => Ok(Operation::Count),
_ => Err(ShellError::labeled_error_with_secondary(
"Operation not fount",
"Operation does not exist",
&name.tag,
"Perhaps you want: mean, sum, min, max, first, last, nunique, quantile, median, count",
&name.tag,
)),
}
}
}
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"pls aggregate"
}
fn usage(&self) -> &str {
"Performs an aggregation operation on a groupby object"
}
fn signature(&self) -> Signature {
Signature::build("pls aggregate")
.required("operation", SyntaxShape::String, "aggregate operation")
.optional(
"selection",
SyntaxShape::Table,
"columns to perform aggregation",
)
.named(
"quantile",
SyntaxShape::Number,
"quantile value for quantile operation",
Some('q'),
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
aggregate(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Aggregate sum by grouping by column a and summing on col b",
example:
"echo [[a b]; [one 1] [one 2]] | pls convert | pls groupby [a] | pls aggregate sum",
result: None,
}]
}
}
fn aggregate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let mut args = args.evaluate_once()?;
let quantile: Option<Tagged<f64>> = args.get_flag("quantile")?;
let operation: Tagged<String> = args.req(0)?;
let op = Operation::from_tagged(&operation, quantile)?;
// Extracting the selection columns of the columns to perform the aggregation
let agg_cols: Option<Vec<Value>> = args.opt(1)?;
let (selection, agg_span) = match agg_cols {
Some(cols) => {
let (agg_string, agg_span) = convert_columns(&cols, &tag)?;
(Some(agg_string), agg_span)
}
None => (None, Span::unknown()),
};
// The operation is only done in one dataframe. Only one input is
// expected from the InputStream
match args.input.next() {
None => Err(ShellError::labeled_error(
"No input received",
"missing dataframe input from stream",
&tag,
)),
Some(value) => {
if let UntaggedValue::DataFrame(PolarsData::GroupBy(nu_groupby)) = value.value {
let groupby = nu_groupby.to_groupby()?;
let groupby = match &selection {
Some(cols) => groupby.select(cols),
None => groupby,
};
let res = perform_aggregation(groupby, op, &operation.tag, &agg_span)?;
let final_df = Value {
tag,
value: UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame::new(
res,
))),
};
Ok(OutputStream::one(final_df))
} else {
Err(ShellError::labeled_error(
"No groupby in stream",
"no groupby found in input stream",
&tag,
))
}
}
}
}
fn perform_aggregation(
groupby: GroupBy,
operation: Operation,
operation_tag: &Tag,
agg_span: &Span,
) -> Result<polars::prelude::DataFrame, ShellError> {
match operation {
Operation::Mean => groupby.mean(),
Operation::Sum => groupby.sum(),
Operation::Min => groupby.min(),
Operation::Max => groupby.max(),
Operation::First => groupby.first(),
Operation::Last => groupby.last(),
Operation::Nunique => groupby.n_unique(),
Operation::Quantile(quantile) => groupby.quantile(quantile),
Operation::Median => groupby.median(),
Operation::Var => groupby.var(),
Operation::Std => groupby.std(),
Operation::Count => groupby.count(),
}
.map_err(|e| {
let span = if e.to_string().contains("Not found") {
agg_span
} else {
&operation_tag.span
};
ShellError::labeled_error("Aggregation error", format!("{}", e), span)
})
}

View File

@ -0,0 +1,26 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, UntaggedValue};
pub struct Command;
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"pls"
}
fn usage(&self) -> &str {
"Commands to work with polars dataframes"
}
fn signature(&self) -> Signature {
Signature::build("pls")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(
UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
))
}
}

View File

@ -0,0 +1,43 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
Signature, UntaggedValue,
};
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"pls convert"
}
fn usage(&self) -> &str {
"Converts a pipelined Table or List into a polars dataframe"
}
fn signature(&self) -> Signature {
Signature::build("pls convert")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let args = args.evaluate_once()?;
let df = NuDataFrame::try_from_iter(args.input, &tag)?;
let init = InputStream::one(
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)).into_value(&tag),
);
Ok(init.to_output_stream())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Takes an input stream and converts it to a polars dataframe",
example: "echo [[a b];[1 2] [3 4]] | pls convert",
result: None,
}]
}
}

View File

@ -0,0 +1,97 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
Signature, SyntaxShape, UntaggedValue, Value,
};
use super::utils::convert_columns;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"pls drop"
}
fn usage(&self) -> &str {
"Creates a new dataframe by dropping the selected columns"
}
fn signature(&self) -> Signature {
Signature::build("pls drop").required(
"columns",
SyntaxShape::Table,
"column names to be dropped",
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
drop(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "drop column a",
example: "echo [[a b]; [1 2] [3 4]] | pls convert | pls drop [a]",
result: None,
}]
}
}
fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let mut args = args.evaluate_once()?;
let columns: Vec<Value> = args.req(0)?;
let (col_string, col_span) = convert_columns(&columns, &tag)?;
match args.input.next() {
None => Err(ShellError::labeled_error(
"No input received",
"missing dataframe input from stream",
&tag,
)),
Some(value) => {
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
dataframe: Some(ref df),
..
})) = value.value
{
let new_df = match col_string.iter().next() {
Some(col) => df.drop(col).map_err(|e| {
ShellError::labeled_error("Join error", format!("{}", e), &col_span)
}),
None => Err(ShellError::labeled_error(
"Empty names list",
"No column names where found",
&col_span,
)),
}?;
let res = col_string.iter().skip(1).try_fold(new_df, |new_df, col| {
new_df.drop(col).map_err(|e| {
ShellError::labeled_error("Drop error", format!("{}", e), &col_span)
})
})?;
let value = Value {
value: UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame::new(
res,
))),
tag: tag.clone(),
};
Ok(OutputStream::one(value))
} else {
Err(ShellError::labeled_error(
"No dataframe in stream",
"no dataframe found in input stream",
&tag,
))
}
}
}
}

View File

@ -0,0 +1,81 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
Signature, TaggedDictBuilder, UntaggedValue,
};
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"pls dtypes"
}
fn usage(&self) -> &str {
"Show dataframe data types"
}
fn signature(&self) -> Signature {
Signature::build("pls dtypes")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
dtypes(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "drop column a",
example: "echo [[a b]; [1 2] [3 4]] | pls convert | pls dtypes",
result: None,
}]
}
}
fn dtypes(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let mut args = args.evaluate_once()?;
match args.input.next() {
None => Err(ShellError::labeled_error(
"No input received",
"missing dataframe input from stream",
&tag,
)),
Some(value) => {
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
dataframe: Some(df),
..
})) = value.value
{
let col_names = df
.get_column_names()
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>();
let values =
df.dtypes()
.into_iter()
.zip(col_names.into_iter())
.map(move |(dtype, name)| {
let mut data = TaggedDictBuilder::new(tag.clone());
data.insert_value("column", name.as_ref());
data.insert_value("dtype", format!("{}", dtype));
data.into_value()
});
Ok(OutputStream::from_stream(values))
} else {
Err(ShellError::labeled_error(
"No dataframe in stream",
"no dataframe found in input stream",
&tag,
))
}
}
}
}

View File

@ -0,0 +1,94 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, NuGroupBy, PolarsData},
Signature, SyntaxShape, UntaggedValue, Value,
};
use super::utils::convert_columns;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"pls groupby"
}
fn usage(&self) -> &str {
"Creates a groupby object that can be used for other aggregations"
}
fn signature(&self) -> Signature {
Signature::build("pls groupby").required(
"by columns",
SyntaxShape::Table,
"groupby columns",
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
groupby(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Grouping by column a",
example: "echo [[a b]; [one 1] [one 2]] | pls convert | pls groupby [a]",
result: None,
}]
}
}
fn groupby(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let mut args = args.evaluate_once()?;
// Extracting the names of the columns to perform the groupby
let by_columns: Vec<Value> = args.req(0)?;
let (columns_string, col_span) = convert_columns(&by_columns, &tag)?;
// The operation is only done in one dataframe. Only one input is
// expected from the InputStream
match args.input.next() {
None => Err(ShellError::labeled_error(
"No input received",
"missing dataframe input from stream",
&tag,
)),
Some(value) => {
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(nu_df)) = value.value {
let df = match nu_df.dataframe {
Some(df) => df,
None => unreachable!("No dataframe in nu_dataframe"),
};
// This is the expensive part of the groupby; to create the
// groups that will be used for grouping the data in the
// dataframe. Once it has been done these values can be stored
// in the NuGroupBy
let groupby = df.groupby(&columns_string).map_err(|e| {
ShellError::labeled_error("Groupby error", format!("{}", e), col_span)
})?;
let groups = groupby.get_groups().to_vec();
let groupby = Value {
tag: value.tag,
value: UntaggedValue::DataFrame(PolarsData::GroupBy(NuGroupBy::new(
NuDataFrame::new_with_name(df, nu_df.name),
columns_string,
groups,
))),
};
Ok(OutputStream::one(groupby))
} else {
Err(ShellError::labeled_error(
"No dataframe in stream",
"no dataframe found in input stream",
&tag,
))
}
}
}
}

View File

@ -0,0 +1,205 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
Signature, SyntaxShape, UntaggedValue, Value,
};
use super::utils::convert_columns;
use polars::prelude::JoinType;
use nu_source::Tagged;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"pls join"
}
fn usage(&self) -> &str {
"Joins a dataframe using columns as reference"
}
fn signature(&self) -> Signature {
Signature::build("pls join")
.required("dataframe", SyntaxShape::Any, "right dataframe to join")
.required(
"l_columns",
SyntaxShape::Table,
"left column names to perform join",
)
.required(
"r_columns",
SyntaxShape::Table,
"right column names to perform join",
)
.named(
"type",
SyntaxShape::String,
"type of join. Inner by default",
Some('t'),
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
join(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "inner join dataframe",
example: "echo [[a b]; [1 2] [3 4]] | pls convert | pls join $right [a] [a]",
result: None,
},
Example {
description: "right join dataframe",
example:
"echo [[a b]; [1 2] [3 4] [5 6]] | pls convert | pls join $right [b] [b] -t right",
result: None,
},
]
}
}
fn join(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let mut args = args.evaluate_once()?;
let r_df: Value = args.req(0)?;
let l_col: Vec<Value> = args.req(1)?;
let r_col: Vec<Value> = args.req(2)?;
let join_type_op: Option<Tagged<String>> = args.get_flag("type")?;
let join_type = match join_type_op {
None => JoinType::Inner,
Some(val) => match val.item.as_ref() {
"inner" => JoinType::Inner,
"outer" => JoinType::Outer,
"left" => JoinType::Left,
_ => {
return Err(ShellError::labeled_error_with_secondary(
"Incorrect join type",
"Invalid join type",
&val.tag,
"Perhaps you mean: inner, outer or left",
&val.tag,
))
}
},
};
let (l_col_string, l_col_span) = convert_columns(&l_col, &tag)?;
let (r_col_string, r_col_span) = convert_columns(&r_col, &tag)?;
match args.input.next() {
None => Err(ShellError::labeled_error(
"No input received",
"missing dataframe input from stream",
&tag,
)),
Some(value) => {
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
dataframe: Some(ref df),
..
})) = value.value
{
let res = match r_df.value {
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
dataframe: Some(r_df),
..
})) => {
// Checking the column types before performing the join
check_column_datatypes(
df,
&l_col_string,
&l_col_span,
&r_col_string,
&r_col_span,
)?;
df.join(&r_df, &l_col_string, &r_col_string, join_type)
.map_err(|e| {
ShellError::labeled_error(
"Join error",
format!("{}", e),
&l_col_span,
)
})
}
_ => Err(ShellError::labeled_error(
"Not a dataframe",
"not a dataframe type value",
&r_df.tag,
)),
}?;
let value = Value {
value: UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame::new(
res,
))),
tag: tag.clone(),
};
Ok(OutputStream::one(value))
} else {
Err(ShellError::labeled_error(
"No dataframe in stream",
"no dataframe found in input stream",
&tag,
))
}
}
}
}
fn check_column_datatypes<T: AsRef<str>>(
df: &polars::prelude::DataFrame,
l_cols: &[T],
l_col_span: &Span,
r_cols: &[T],
r_col_span: &Span,
) -> Result<(), ShellError> {
if l_cols.len() != r_cols.len() {
return Err(ShellError::labeled_error_with_secondary(
"Mismatched number of column names",
format!(
"found {} left names vs {} right names",
l_cols.len(),
r_cols.len()
),
l_col_span,
"perhaps you need to change the number of columns to join",
r_col_span,
));
}
for (l, r) in l_cols.iter().zip(r_cols.iter()) {
let l_series = df
.column(l.as_ref())
.map_err(|e| ShellError::labeled_error("Join error", format!("{}", e), l_col_span))?;
let r_series = df
.column(r.as_ref())
.map_err(|e| ShellError::labeled_error("Join error", format!("{}", e), r_col_span))?;
if l_series.dtype() != r_series.dtype() {
return Err(ShellError::labeled_error_with_secondary(
"Mismatched datatypes",
format!(
"left column type '{}' doesn't match '{}' right column match",
l_series.dtype(),
r_series.dtype()
),
l_col_span,
"perhaps you need to select other column to match",
r_col_span,
));
}
}
Ok(())
}

View File

@ -0,0 +1,64 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
Signature, TaggedDictBuilder, UntaggedValue,
};
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"pls list"
}
fn usage(&self) -> &str {
"Lists stored dataframes"
}
fn signature(&self) -> Signature {
Signature::build("pls list")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let values = args
.context
.scope
.get_vars()
.into_iter()
.filter_map(|(name, value)| {
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
dataframe: Some(df),
name: file_name,
})) = &value.value
{
let mut data = TaggedDictBuilder::new(value.tag.clone());
let rows = df.height();
let cols = df.width();
data.insert_value("name", name.as_ref());
data.insert_value("file", file_name.as_ref());
data.insert_value("rows", format!("{}", rows));
data.insert_value("columns", format!("{}", cols));
Some(data.into_value())
} else {
None
}
});
Ok(OutputStream::from_stream(values))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Lists loaded dataframes in current scope",
example: "pls list",
result: None,
}]
}
}

View File

@ -0,0 +1,221 @@
use std::path::PathBuf;
use crate::prelude::*;
use nu_engine::{EvaluatedCommandArgs, WholeStreamCommand};
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
Primitive, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged;
use polars::prelude::{CsvReader, JsonReader, ParquetReader, SerReader};
use std::fs::File;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"pls load"
}
fn usage(&self) -> &str {
"Loads dataframe form csv file"
}
fn signature(&self) -> Signature {
Signature::build("pls load")
.required(
"file",
SyntaxShape::FilePath,
"the file path to load values from",
)
.named(
"delimiter",
SyntaxShape::String,
"file delimiter character. CSV file",
Some('d'),
)
.switch(
"no_header",
"Indicates if file doesn't have header. CSV file",
None,
)
.named(
"infer_schema",
SyntaxShape::Number,
"Set number of row to infer the schema of the file. CSV file",
None,
)
.named(
"skip_rows",
SyntaxShape::Number,
"Number of rows to skip from file. CSV file",
None,
)
.named(
"columns",
SyntaxShape::Table,
"Columns to be selected from csv file. CSV file",
None,
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
create_from_file(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Takes a file name and creates a dataframe",
example: "pls load test.csv",
result: None,
}]
}
}
fn create_from_file(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let args = args.evaluate_once()?;
let file: Tagged<PathBuf> = args.req(0)?;
let df = match file.item().extension() {
Some(e) => match e.to_str() {
Some("csv") => from_csv(args),
Some("parquet") => from_parquet(args),
Some("json") => from_json(args),
_ => Err(ShellError::labeled_error(
"Error with file",
"Not a csv, parquet or json file",
&file.tag,
)),
},
None => Err(ShellError::labeled_error(
"Error with file",
"File without extension",
&file.tag,
)),
}?;
let file_name = match file.item.into_os_string().into_string() {
Ok(name) => name,
Err(e) => {
return Err(ShellError::labeled_error(
"Error with file name",
format!("{:?}", e),
&file.tag,
))
}
};
let init = InputStream::one(
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame::new_with_name(
df, file_name,
)))
.into_value(&tag),
);
Ok(init.to_output_stream())
}
fn from_parquet(args: EvaluatedCommandArgs) -> Result<polars::prelude::DataFrame, ShellError> {
let file: Tagged<PathBuf> = args.req(0)?;
let r = File::open(&file.item)
.map_err(|e| ShellError::labeled_error("Error with file", format!("{:?}", e), &file.tag))?;
let reader = ParquetReader::new(r);
reader
.finish()
.map_err(|e| ShellError::labeled_error("Error with file", format!("{:?}", e), &file.tag))
}
fn from_json(args: EvaluatedCommandArgs) -> Result<polars::prelude::DataFrame, ShellError> {
let file: Tagged<PathBuf> = args.req(0)?;
let r = File::open(&file.item)
.map_err(|e| ShellError::labeled_error("Error with file", format!("{:?}", e), &file.tag))?;
let reader = JsonReader::new(r);
reader
.finish()
.map_err(|e| ShellError::labeled_error("Error with file", format!("{:?}", e), &file.tag))
}
fn from_csv(args: EvaluatedCommandArgs) -> Result<polars::prelude::DataFrame, ShellError> {
let file: Tagged<PathBuf> = args.req(0)?;
let delimiter: Option<Tagged<String>> = args.get_flag("delimiter")?;
let no_header: bool = args.has_flag("no_header");
let infer_schema: Option<Tagged<usize>> = args.get_flag("infer_schema")?;
let skip_rows: Option<Tagged<usize>> = args.get_flag("skip_rows")?;
let columns: Option<Vec<Value>> = args.get_flag("columns")?;
let csv_reader = CsvReader::from_path(&file.item).map_err(|e| {
ShellError::labeled_error("Unable to parse file", format!("{}", e), &file.tag)
})?;
let csv_reader = match delimiter {
None => csv_reader,
Some(d) => {
if d.item.len() != 1 {
return Err(ShellError::labeled_error(
"Incorrect delimiter",
"Delimiter has to be one char",
&d.tag,
));
} else {
let delimiter = match d.item.chars().nth(0) {
Some(d) => d as u8,
None => unreachable!(),
};
csv_reader.with_delimiter(delimiter)
}
}
};
let csv_reader = if no_header {
csv_reader.has_header(false)
} else {
csv_reader.has_header(true)
};
let csv_reader = match infer_schema {
None => csv_reader.infer_schema(None),
Some(r) => csv_reader.infer_schema(Some(r.item)),
};
let csv_reader = match skip_rows {
None => csv_reader,
Some(r) => csv_reader.with_skip_rows(r.item),
};
let csv_reader = match columns {
None => csv_reader,
Some(c) => {
let columns = c
.into_iter()
.map(|value| match value.value {
UntaggedValue::Primitive(Primitive::String(s)) => Ok(s),
_ => Err(ShellError::labeled_error(
"Incorrect type for column",
"Only string as columns",
&value.tag,
)),
})
.collect::<Result<Vec<String>, ShellError>>();
csv_reader.with_columns(Some(columns?))
}
};
match csv_reader.finish() {
Ok(csv_reader) => Ok(csv_reader),
Err(e) => Err(ShellError::labeled_error(
"Error while parsing dataframe",
format!("{}", e),
&file.tag,
)),
}
}

View File

@ -0,0 +1,26 @@
pub mod aggregate;
pub mod command;
pub mod convert;
pub mod drop;
pub mod dtypes;
pub mod groupby;
pub mod join;
pub mod list;
pub mod load;
pub mod sample;
pub mod select;
pub mod show;
pub(crate) mod utils;
pub use aggregate::DataFrame as DataFrameAggregate;
pub use command::Command as DataFrame;
pub use convert::DataFrame as DataFrameConvert;
pub use drop::DataFrame as DataFrameDrop;
pub use dtypes::DataFrame as DataFrameDTypes;
pub use groupby::DataFrame as DataFrameGroupBy;
pub use join::DataFrame as DataFrameJoin;
pub use list::DataFrame as DataFrameList;
pub use load::DataFrame as DataFrameLoad;
pub use sample::DataFrame as DataFrameSample;
pub use select::DataFrame as DataFrameSelect;
pub use show::DataFrame as DataFrameShow;

View File

@ -0,0 +1,117 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"pls sample"
}
fn usage(&self) -> &str {
"Create sample dataframe"
}
fn signature(&self) -> Signature {
Signature::build("pls load")
.named(
"n_rows",
SyntaxShape::Number,
"number of rows to be taken from dataframe",
Some('n'),
)
.named(
"fraction",
SyntaxShape::Number,
"fraction of dataframe to be taken",
Some('f'),
)
.switch("replace", "sample with replace", Some('e'))
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
sample(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Sample rows from dataframe",
example: "echo [[a b]; [1 2] [3 4]] | pls load | pls sample -r 1",
result: None,
},
Example {
description: "Shows sample row using fraction and replace",
example: "echo [[a b]; [1 2] [3 4] [5 6]] | pls load | pls sample -f 0.5 -e",
result: None,
},
]
}
}
fn sample(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let mut args = args.evaluate_once()?;
let rows: Option<Tagged<usize>> = args.get_flag("n_rows")?;
let fraction: Option<Tagged<f64>> = args.get_flag("fraction")?;
let replace: bool = args.has_flag("replace");
match args.input.next() {
None => Err(ShellError::labeled_error(
"No input received",
"missing dataframe input from stream",
&tag,
)),
Some(value) => {
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
dataframe: Some(ref df),
..
})) = value.value
{
let res = match (rows, fraction) {
(Some(rows), None) => df.sample_n(rows.item, replace).map_err(|e| {
ShellError::labeled_error("Polars error", format!("{}", e), &rows.tag)
}),
(None, Some(frac)) => df.sample_frac(frac.item, replace).map_err(|e| {
ShellError::labeled_error("Polars error", format!("{}", e), &frac.tag)
}),
(Some(_), Some(_)) => Err(ShellError::labeled_error(
"Incompatible flags",
"Only one selection criterion allowed",
&tag,
)),
(None, None) => Err(ShellError::labeled_error_with_secondary(
"No selection",
"No selection criterion was found",
&tag,
"Perhaps you want to use the flag -n or -f",
&tag,
)),
}?;
let value = Value {
value: UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame::new(
res,
))),
tag: tag.clone(),
};
Ok(OutputStream::one(value))
} else {
Err(ShellError::labeled_error(
"No dataframe in stream",
"no dataframe found in input stream",
&tag,
))
}
}
}
}

View File

@ -0,0 +1,84 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
Signature, SyntaxShape, UntaggedValue, Value,
};
use super::utils::convert_columns;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"pls select"
}
fn usage(&self) -> &str {
"Creates a new dataframe with the selected columns"
}
fn signature(&self) -> Signature {
Signature::build("pls select").required(
"columns",
SyntaxShape::Table,
"selected column names",
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
select(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Create new dataframe with column a",
example: "echo [[a b]; [1 2] [3 4]] | pls convert | pls select [a]",
result: None,
}]
}
}
fn select(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let mut args = args.evaluate_once()?;
let columns: Vec<Value> = args.req(0)?;
let (col_string, col_span) = convert_columns(&columns, &tag)?;
match args.input.next() {
None => Err(ShellError::labeled_error(
"No input received",
"missing dataframe input from stream",
&tag,
)),
Some(value) => {
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
dataframe: Some(ref df),
..
})) = value.value
{
let res = df.select(&col_string).map_err(|e| {
ShellError::labeled_error("Drop error", format!("{}", e), &col_span)
})?;
let value = Value {
value: UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame::new(
res,
))),
tag: tag.clone(),
};
Ok(OutputStream::one(value))
} else {
Err(ShellError::labeled_error(
"No dataframe in stream",
"no dataframe found in input stream",
&tag,
))
}
}
}
}

View File

@ -0,0 +1,78 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::PolarsData, Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"pls show"
}
fn usage(&self) -> &str {
"Show dataframe"
}
fn signature(&self) -> Signature {
Signature::build("pls show")
.named(
"n_rows",
SyntaxShape::Number,
"number of rows to be shown",
Some('n'),
)
.switch("tail", "shows tail rows", Some('t'))
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
show(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Shows head rows from dataframe",
example: "echo [[a b]; [1 2] [3 4]] | pls convert | pls show",
result: None,
},
Example {
description: "Shows tail rows from dataframe",
example: "echo [[a b]; [1 2] [3 4] [5 6]] | pls convert | pls show -t -n 1",
result: None,
},
]
}
}
fn show(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let mut args = args.evaluate_once()?;
let rows: Option<Tagged<usize>> = args.get_flag("rows")?;
let tail: bool = args.has_flag("tail");
match args.input.next() {
None => Err(ShellError::labeled_error(
"No input received",
"missing dataframe input from stream",
&tag,
)),
Some(value) => {
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)) = value.value {
let rows = rows.map(|v| v.item);
let values = if tail { df.tail(rows)? } else { df.head(rows)? };
Ok(OutputStream::from_stream(values.into_iter()))
} else {
Err(ShellError::labeled_error(
"No dataframe in stream",
"no dataframe found in input stream",
&tag,
))
}
}
}
}

View File

@ -0,0 +1,42 @@
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{Primitive, UntaggedValue, Value};
// Converts a Vec<Value> to a Vec<String> with a Span marking the whole
// location of the columns for error referencing
pub(crate) fn convert_columns<'columns>(
columns: &'columns [Value],
tag: &Tag,
) -> Result<(Vec<String>, Span), ShellError> {
let mut col_span = match columns
.iter()
.nth(0)
.map(|v| Span::new(v.tag.span.start(), v.tag.span.end()))
{
Some(span) => span,
None => {
return Err(ShellError::labeled_error(
"Empty column list",
"Empty list found for command",
tag,
))
}
};
let res = columns
.iter()
.map(|value| match &value.value {
UntaggedValue::Primitive(Primitive::String(s)) => {
col_span = col_span.until(value.tag.span);
Ok(s.clone())
}
_ => Err(ShellError::labeled_error(
"Incorrect column format",
"Only string as column name",
&value.tag,
)),
})
.collect::<Result<Vec<String>, _>>()?;
Ok((res, col_span))
}

View File

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; use nu_protocol::{Signature, UntaggedValue};
pub struct Command; pub struct Command;
@ -18,10 +18,10 @@ impl WholeStreamCommand for Command {
"Apply date function." "Apply date function."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(ActionStream::one(ReturnSuccess::value( Ok(OutputStream::one(
UntaggedValue::string(get_full_help(&Command, &args.scope)).into_value(Tag::unknown()), UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
))) ))
} }
} }

View File

@ -1,20 +1,12 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{Dictionary, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
Dictionary, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged; use nu_source::Tagged;
use std::fmt::{self, write}; use std::fmt::{self, write};
pub struct Date; pub struct Date;
#[derive(Deserialize)]
pub struct FormatArgs {
format: Tagged<String>,
table: bool,
}
impl WholeStreamCommand for Date { impl WholeStreamCommand for Date {
fn name(&self) -> &str { fn name(&self) -> &str {
"date format" "date format"
@ -30,7 +22,7 @@ impl WholeStreamCommand for Date {
"Format a given date using the given format string." "Format a given date using the given format string."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
format(args) format(args)
} }
@ -50,9 +42,18 @@ impl WholeStreamCommand for Date {
} }
} }
pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> { pub fn format(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let (FormatArgs { format, table }, input) = args.process()?; let args = args.evaluate_once()?;
let format: Tagged<String> = args.req(0)?;
let table: Option<bool> = args.get_flag("table")?;
let input = if args.input.is_empty() {
InputStream::one(crate::commands::date::now::date_now(&tag))
} else {
args.input
};
Ok(input Ok(input
.map(move |value| match value { .map(move |value| match value {
@ -70,7 +71,7 @@ pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
&format.tag, &format.tag,
)) ))
} else { } else {
let value = if table { let value = if table.is_some() {
let mut indexmap = IndexMap::new(); let mut indexmap = IndexMap::new();
indexmap.insert( indexmap.insert(
"formatted".to_string(), "formatted".to_string(),
@ -82,7 +83,7 @@ pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
UntaggedValue::string(&output).into_value(&tag) UntaggedValue::string(&output).into_value(&tag)
}; };
ReturnSuccess::value(value) Ok(value)
} }
} }
_ => Err(ShellError::labeled_error( _ => Err(ShellError::labeled_error(
@ -91,7 +92,7 @@ pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
&tag, &tag,
)), )),
}) })
.to_action_stream()) .to_input_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -3,7 +3,7 @@ use chrono_tz::TZ_VARIANTS;
use indexmap::IndexMap; use indexmap::IndexMap;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Dictionary, ReturnSuccess, Signature, UntaggedValue}; use nu_protocol::{Dictionary, Signature, UntaggedValue};
pub struct Date; pub struct Date;
@ -20,7 +20,7 @@ impl WholeStreamCommand for Date {
"List supported time zones." "List supported time zones."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
list_timezone(args) list_timezone(args)
} }
@ -40,7 +40,7 @@ impl WholeStreamCommand for Date {
} }
} }
fn list_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> { fn list_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?; let args = args.evaluate_once()?;
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
@ -52,12 +52,10 @@ fn list_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
UntaggedValue::string(tz.name()).into_value(&tag), UntaggedValue::string(tz.name()).into_value(&tag),
); );
Ok(ReturnSuccess::Value( Ok(UntaggedValue::Row(Dictionary { entries }).into_value(&tag))
UntaggedValue::Row(Dictionary { entries }).into_value(&tag),
))
}); });
Ok(list.into_iter().to_action_stream()) Ok(list.into_iter().to_input_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use chrono::{DateTime, Local}; use chrono::{DateTime, Local};
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Signature, UntaggedValue}; use nu_protocol::{Signature, UntaggedValue, Value};
pub struct Date; pub struct Date;
@ -19,20 +19,23 @@ impl WholeStreamCommand for Date {
"Get the current date." "Get the current date."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
now(args) now(args)
} }
} }
pub fn now(args: CommandArgs) -> Result<ActionStream, ShellError> { pub fn date_now(tag: &Tag) -> Value {
let args = args.evaluate_once()?;
let tag = args.call_info.name_tag.clone();
let now: DateTime<Local> = Local::now(); let now: DateTime<Local> = Local::now();
let value = UntaggedValue::date(now.with_timezone(now.offset())).into_value(&tag); UntaggedValue::date(now.with_timezone(now.offset())).into_value(tag)
}
Ok(ActionStream::one(value)) pub fn now(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let value = date_now(&args.call_info.name_tag);
Ok(OutputStream::one(value))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -3,7 +3,7 @@ use chrono::{Datelike, Timelike};
use indexmap::IndexMap; use indexmap::IndexMap;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; use nu_protocol::{Dictionary, Primitive, Signature, UntaggedValue, Value};
pub struct Date; pub struct Date;
@ -20,7 +20,7 @@ impl WholeStreamCommand for Date {
"Print the date in a structured table." "Print the date in a structured table."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
to_table(args) to_table(args)
} }
@ -33,10 +33,14 @@ impl WholeStreamCommand for Date {
} }
} }
fn to_table(args: CommandArgs) -> Result<ActionStream, ShellError> { fn to_table(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?; let args = args.evaluate_once()?;
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let input = args.input; let input = if args.input.is_empty() {
InputStream::one(crate::commands::date::now::date_now(&tag))
} else {
args.input
};
Ok(input Ok(input
.map(move |value| match value { .map(move |value| match value {
@ -79,7 +83,7 @@ fn to_table(args: CommandArgs) -> Result<ActionStream, ShellError> {
let value = UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag); let value = UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag);
ReturnSuccess::value(value) Ok(value)
} }
_ => Err(ShellError::labeled_error( _ => Err(ShellError::labeled_error(
"Expected a date from pipeline", "Expected a date from pipeline",
@ -87,7 +91,7 @@ fn to_table(args: CommandArgs) -> Result<ActionStream, ShellError> {
&tag, &tag,
)), )),
}) })
.to_action_stream()) .to_input_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -2,16 +2,11 @@ use crate::commands::date::parser::{datetime_in_timezone, ParseErrorKind};
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged; use nu_source::Tagged;
pub struct Date; pub struct Date;
#[derive(Deserialize)]
struct DateToTimeZoneArgs {
timezone: Tagged<String>,
}
impl WholeStreamCommand for Date { impl WholeStreamCommand for Date {
fn name(&self) -> &str { fn name(&self) -> &str {
"date to-timezone" "date to-timezone"
@ -33,7 +28,7 @@ impl WholeStreamCommand for Date {
"Use 'date list-timezone' to list all supported time zones." "Use 'date list-timezone' to list all supported time zones."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
to_timezone(args) to_timezone(args)
} }
@ -58,11 +53,14 @@ impl WholeStreamCommand for Date {
} }
} }
fn to_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> { fn to_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let (DateToTimeZoneArgs { timezone }, input) = args.process()?; let args = args.evaluate_once()?;
Ok(input let timezone: Tagged<String> = args.req(0)?;
Ok(args
.input
.map(move |value| match value { .map(move |value| match value {
Value { Value {
value: UntaggedValue::Primitive(Primitive::Date(dt)), value: UntaggedValue::Primitive(Primitive::Date(dt)),
@ -71,7 +69,7 @@ fn to_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(dt) => { Ok(dt) => {
let value = UntaggedValue::date(dt).into_value(&tag); let value = UntaggedValue::date(dt).into_value(&tag);
ReturnSuccess::value(value) Ok(value)
} }
Err(e) => Err(ShellError::labeled_error( Err(e) => Err(ShellError::labeled_error(
error_message(e), error_message(e),
@ -85,7 +83,7 @@ fn to_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
&tag, &tag,
)), )),
}) })
.to_action_stream()) .to_input_stream())
} }
fn error_message(err: ParseErrorKind) -> &'static str { fn error_message(err: ParseErrorKind) -> &'static str {

View File

@ -1,54 +0,0 @@
use crate::prelude::*;
use chrono::{DateTime, Utc};
use nu_errors::ShellError;
use crate::commands::date::utils::date_to_value;
use nu_engine::WholeStreamCommand;
use nu_protocol::Signature;
pub struct Date;
impl WholeStreamCommand for Date {
fn name(&self) -> &str {
"date utc"
}
fn signature(&self) -> Signature {
Signature::build("date utc")
}
fn usage(&self) -> &str {
"return the current date in utc."
}
fn run_with_actions(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
utc(args)
}
}
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();
let value = {
let local: DateTime<Utc> = Utc::now();
date_to_value(local, tag, no_fmt)
};
Ok(OutputStream::one(value))
}
#[cfg(test)]
mod tests {
use super::Date;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(Date {})
}
}

View File

@ -1,10 +1,9 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::basic_evaluation_context;
use nu_engine::whole_stream_command; use nu_engine::whole_stream_command;
use std::error::Error; use std::error::Error;
pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Box<dyn Error>> { pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Box<dyn Error>> {
let context = basic_evaluation_context()?; let context = EvaluationContext::basic();
{ {
use crate::commands::*; use crate::commands::*;
@ -14,6 +13,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(NuPlugin), whole_stream_command(NuPlugin),
whole_stream_command(Let), whole_stream_command(Let),
whole_stream_command(LetEnv), whole_stream_command(LetEnv),
whole_stream_command(LoadEnv),
whole_stream_command(Def), whole_stream_command(Def),
whole_stream_command(Source), whole_stream_command(Source),
// System/file operations // System/file operations
@ -89,7 +89,6 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(StrUpcase), whole_stream_command(StrUpcase),
whole_stream_command(StrCapitalize), whole_stream_command(StrCapitalize),
whole_stream_command(StrFindReplace), whole_stream_command(StrFindReplace),
whole_stream_command(StrFrom),
whole_stream_command(StrSubstring), whole_stream_command(StrSubstring),
whole_stream_command(StrToDatetime), whole_stream_command(StrToDatetime),
whole_stream_command(StrContains), whole_stream_command(StrContains),
@ -122,7 +121,9 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(Update), whole_stream_command(Update),
whole_stream_command(Insert), whole_stream_command(Insert),
whole_stream_command(Into), whole_stream_command(Into),
whole_stream_command(IntoBinary),
whole_stream_command(IntoInt), whole_stream_command(IntoInt),
whole_stream_command(IntoString),
whole_stream_command(SplitBy), whole_stream_command(SplitBy),
// Row manipulation // Row manipulation
whole_stream_command(All), whole_stream_command(All),
@ -157,6 +158,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(EachGroup), whole_stream_command(EachGroup),
whole_stream_command(EachWindow), whole_stream_command(EachWindow),
whole_stream_command(Empty), whole_stream_command(Empty),
whole_stream_command(ForIn),
// Table manipulation // Table manipulation
whole_stream_command(Flatten), whole_stream_command(Flatten),
whole_stream_command(Move), whole_stream_command(Move),
@ -239,6 +241,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(PathExpand), whole_stream_command(PathExpand),
whole_stream_command(PathJoin), whole_stream_command(PathJoin),
whole_stream_command(PathParse), whole_stream_command(PathParse),
whole_stream_command(PathRelativeTo),
whole_stream_command(PathSplit), whole_stream_command(PathSplit),
whole_stream_command(PathType), whole_stream_command(PathType),
// Url // Url
@ -250,6 +253,31 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(Seq), whole_stream_command(Seq),
whole_stream_command(SeqDates), whole_stream_command(SeqDates),
whole_stream_command(TermSize), whole_stream_command(TermSize),
//Dataframe commands
#[cfg(feature = "dataframe")]
whole_stream_command(DataFrame),
#[cfg(feature = "dataframe")]
whole_stream_command(DataFrameConvert),
#[cfg(feature = "dataframe")]
whole_stream_command(DataFrameLoad),
#[cfg(feature = "dataframe")]
whole_stream_command(DataFrameList),
#[cfg(feature = "dataframe")]
whole_stream_command(DataFrameGroupBy),
#[cfg(feature = "dataframe")]
whole_stream_command(DataFrameAggregate),
#[cfg(feature = "dataframe")]
whole_stream_command(DataFrameShow),
#[cfg(feature = "dataframe")]
whole_stream_command(DataFrameSample),
#[cfg(feature = "dataframe")]
whole_stream_command(DataFrameJoin),
#[cfg(feature = "dataframe")]
whole_stream_command(DataFrameDrop),
#[cfg(feature = "dataframe")]
whole_stream_command(DataFrameSelect),
#[cfg(feature = "dataframe")]
whole_stream_command(DataFrameDTypes),
]); ]);
#[cfg(feature = "clipboard-cli")] #[cfg(feature = "clipboard-cli")]

View File

@ -2,14 +2,16 @@ use crate::prelude::*;
use nu_engine::run_block; use nu_engine::run_block;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{hir::CapturedBlock, hir::ExternalRedirection, Signature, SyntaxShape, Value}; use nu_protocol::{
hir::CapturedBlock, hir::ExternalRedirection, Signature, SyntaxShape, UntaggedValue, Value,
};
pub struct Do; pub struct Do;
#[derive(Deserialize, Debug)]
struct DoArgs { struct DoArgs {
block: CapturedBlock, block: CapturedBlock,
ignore_errors: bool, ignore_errors: bool,
rest: Vec<Value>,
} }
impl WholeStreamCommand for Do { impl WholeStreamCommand for Do {
@ -21,17 +23,18 @@ impl WholeStreamCommand for Do {
Signature::build("do") Signature::build("do")
.required("block", SyntaxShape::Block, "the block to run ") .required("block", SyntaxShape::Block, "the block to run ")
.switch( .switch(
"ignore_errors", "ignore-errors",
"ignore errors as the block runs", "ignore errors as the block runs",
Some('i'), Some('i'),
) )
.rest(SyntaxShape::Any, "the parameter(s) for the block")
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Runs a block, optionally ignoring errors." "Runs a block, optionally ignoring errors."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
do_(args) do_(args)
} }
@ -47,32 +50,36 @@ impl WholeStreamCommand for Do {
example: r#"do -i { thisisnotarealcommand }"#, example: r#"do -i { thisisnotarealcommand }"#,
result: Some(vec![]), result: Some(vec![]),
}, },
Example {
description: "Run the block with a parameter",
example: r#"do { |x| $x + 100 } 55"#,
result: Some(vec![UntaggedValue::int(155).into()]),
},
] ]
} }
} }
fn do_(raw_args: CommandArgs) -> Result<ActionStream, ShellError> { fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let external_redirection = raw_args.call_info.args.external_redirection; let external_redirection = raw_args.call_info.args.external_redirection;
let context = EvaluationContext::from_args(&raw_args); let context = EvaluationContext::from_args(&raw_args);
let ( let args = raw_args.evaluate_once()?;
DoArgs { let do_args = DoArgs {
ignore_errors, block: args.req(0)?,
mut block, ignore_errors: args.has_flag("ignore-errors"),
}, rest: args.rest(1)?,
input, };
) = raw_args.process()?;
let block_redirection = match external_redirection { let block_redirection = match external_redirection {
ExternalRedirection::None => { ExternalRedirection::None => {
if ignore_errors { if do_args.ignore_errors {
ExternalRedirection::Stderr ExternalRedirection::Stderr
} else { } else {
ExternalRedirection::None ExternalRedirection::None
} }
} }
ExternalRedirection::Stdout => { ExternalRedirection::Stdout => {
if ignore_errors { if do_args.ignore_errors {
ExternalRedirection::StdoutAndStderr ExternalRedirection::StdoutAndStderr
} else { } else {
ExternalRedirection::Stdout ExternalRedirection::Stdout
@ -81,14 +88,30 @@ fn do_(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
x => x, x => x,
}; };
if let Some(block) = std::sync::Arc::<nu_protocol::hir::Block>::get_mut(&mut block.block) {
block.set_redirect(block_redirection);
}
context.scope.enter_scope(); context.scope.enter_scope();
let result = run_block(&block.block, &context, input);
context.scope.add_vars(&do_args.block.captured.entries);
for (param, value) in do_args
.block
.block
.params
.positional
.iter()
.zip(do_args.rest)
{
context.scope.add_var(param.0.name(), value.clone());
}
let result = run_block(
&do_args.block.block,
&context,
args.input,
block_redirection,
);
context.scope.exit_scope(); context.scope.exit_scope();
if ignore_errors { if do_args.ignore_errors {
// To properly ignore errors we need to redirect stderr, consume it, and remove // To properly ignore errors we need to redirect stderr, consume it, and remove
// any errors we see in the process. // any errors we see in the process.
@ -96,12 +119,12 @@ fn do_(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(mut stream) => { Ok(mut stream) => {
let output = stream.drain_vec(); let output = stream.drain_vec();
context.clear_errors(); context.clear_errors();
Ok(output.into_iter().to_action_stream()) Ok(output.into_iter().to_output_stream())
} }
Err(_) => Ok(ActionStream::empty()), Err(_) => Ok(OutputStream::empty()),
} }
} else { } else {
result.map(|x| x.to_action_stream()) result.map(|x| x.to_output_stream())
} }
} }

View File

@ -2,16 +2,11 @@ use crate::prelude::*;
use nu_data::base::select_fields; use nu_data::base::select_fields;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape}; use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged; use nu_source::Tagged;
pub struct SubCommand; pub struct SubCommand;
#[derive(Deserialize)]
pub struct Arguments {
columns: Option<Tagged<u64>>,
}
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"drop column" "drop column"
@ -29,7 +24,7 @@ impl WholeStreamCommand for SubCommand {
"Remove the last number of columns. If you want to remove columns by name, try 'reject'." "Remove the last number of columns. If you want to remove columns by name, try 'reject'."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
drop(args) drop(args)
} }
@ -47,8 +42,9 @@ impl WholeStreamCommand for SubCommand {
} }
} }
fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> { fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { columns }, input) = args.process()?; let args = args.evaluate_once()?;
let columns: Option<Tagged<u64>> = args.opt(0)?;
let to_drop = if let Some(quantity) = columns { let to_drop = if let Some(quantity) = columns {
*quantity as usize *quantity as usize
@ -56,7 +52,8 @@ fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
1 1
}; };
Ok(input Ok(args
.input
.map(move |item| { .map(move |item| {
let headers = item.data_descriptors(); let headers = item.data_descriptors();
@ -66,10 +63,9 @@ fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
n => &headers[..n - to_drop], n => &headers[..n - to_drop],
}; };
select_fields(&item, descs, item.tag()) Ok(select_fields(&item, descs, item.tag()))
}) })
.map(ReturnSuccess::value) .to_input_stream())
.to_action_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -6,11 +6,6 @@ use nu_source::Tagged;
pub struct Command; pub struct Command;
#[derive(Deserialize)]
pub struct Arguments {
rows: Option<Tagged<u64>>,
}
impl WholeStreamCommand for Command { impl WholeStreamCommand for Command {
fn name(&self) -> &str { fn name(&self) -> &str {
"drop" "drop"
@ -28,7 +23,7 @@ impl WholeStreamCommand for Command {
"Remove the last number of rows or columns." "Remove the last number of rows or columns."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
drop(args) drop(args)
} }
@ -51,9 +46,10 @@ impl WholeStreamCommand for Command {
} }
} }
fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> { fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rows }, input) = args.process()?; let args = args.evaluate_once()?;
let v: Vec<_> = input.into_vec(); let rows: Option<Tagged<u64>> = args.opt(0)?;
let v: Vec<_> = args.input.into_vec();
let rows_to_drop = if let Some(quantity) = rows { let rows_to_drop = if let Some(quantity) = rows {
*quantity as usize *quantity as usize
@ -62,7 +58,7 @@ fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
}; };
Ok(if rows_to_drop == 0 { Ok(if rows_to_drop == 0 {
v.into_iter().to_action_stream() v.into_iter().map(Ok).to_input_stream()
} else { } else {
let k = if v.len() < rows_to_drop { let k = if v.len() < rows_to_drop {
0 0
@ -70,8 +66,8 @@ fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
v.len() - rows_to_drop v.len() - rows_to_drop
}; };
let iter = v.into_iter().take(k); let iter = v.into_iter().map(Ok).take(k);
iter.to_action_stream() iter.to_input_stream()
}) })
} }

View File

@ -85,7 +85,7 @@ impl WholeStreamCommand for Du {
fn du(args: CommandArgs) -> Result<ActionStream, ShellError> { fn du(args: CommandArgs) -> Result<ActionStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let ctrl_c = args.ctrl_c.clone(); let ctrl_c = args.ctrl_c();
let ctrl_c_copy = ctrl_c.clone(); let ctrl_c_copy = ctrl_c.clone();
let (args, _): (DuArgs, _) = args.process()?; let (args, _): (DuArgs, _) = args.process()?;

View File

@ -4,7 +4,8 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{
hir::CapturedBlock, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value, hir::{CapturedBlock, ExternalRedirection},
Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
}; };
pub struct Each; pub struct Each;
@ -41,7 +42,7 @@ impl WholeStreamCommand for Each {
}, },
Example { Example {
description: "Echo the square of each integer", description: "Echo the square of each integer",
example: "echo [1 2 3] | each { echo $(= $it * $it) }", example: "echo [1 2 3] | each { echo ($it * $it) }",
result: Some(vec![ result: Some(vec![
UntaggedValue::int(1).into(), UntaggedValue::int(1).into(),
UntaggedValue::int(4).into(), UntaggedValue::int(4).into(),
@ -51,9 +52,18 @@ impl WholeStreamCommand for Each {
Example { Example {
description: "Number each item and echo a message", description: "Number each item and echo a message",
example: example:
"echo ['bob' 'fred'] | each --numbered { echo `{{$it.index}} is {{$it.item}}` }", "echo ['bob' 'fred'] | each --numbered { echo $\"($it.index) is ($it.item)\" }",
result: Some(vec![Value::from("0 is bob"), Value::from("1 is fred")]), result: Some(vec![Value::from("0 is bob"), Value::from("1 is fred")]),
}, },
Example {
description: "Name the block variable that each uses",
example: "[1, 2, 3] | each {|x| $x + 100}",
result: Some(vec![
UntaggedValue::int(101).into(),
UntaggedValue::int(102).into(),
UntaggedValue::int(103).into(),
]),
},
] ]
} }
} }
@ -62,6 +72,7 @@ pub fn process_row(
captured_block: Arc<Box<CapturedBlock>>, captured_block: Arc<Box<CapturedBlock>>,
context: Arc<EvaluationContext>, context: Arc<EvaluationContext>,
input: Value, input: Value,
external_redirection: ExternalRedirection,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let input_clone = input.clone(); let input_clone = input.clone();
// When we process a row, we need to know whether the block wants to have the contents of the row as // When we process a row, we need to know whether the block wants to have the contents of the row as
@ -77,16 +88,18 @@ pub fn process_row(
context.scope.enter_scope(); context.scope.enter_scope();
context.scope.add_vars(&captured_block.captured.entries); context.scope.add_vars(&captured_block.captured.entries);
if !captured_block.block.params.positional.is_empty() { if let Some((arg, _)) = captured_block.block.params.positional.first() {
// FIXME: add check for more than parameter, once that's supported context.scope.add_var(arg.name(), input);
context
.scope
.add_var(captured_block.block.params.positional[0].0.name(), input);
} else { } else {
context.scope.add_var("$it", input); context.scope.add_var("$it", input);
} }
let result = run_block(&captured_block.block, &*context, input_stream); let result = run_block(
&captured_block.block,
&*context,
input_stream,
external_redirection,
);
context.scope.exit_scope(); context.scope.exit_scope();
@ -95,7 +108,7 @@ pub fn process_row(
pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value { pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
let mut dict = TaggedDictBuilder::new(item.tag()); let mut dict = TaggedDictBuilder::new(item.tag());
dict.insert_untagged("index", UntaggedValue::int(index)); dict.insert_untagged("index", UntaggedValue::int(index as i64));
dict.insert_value("item", item); dict.insert_value("item", item);
dict.into_value() dict.into_value()
@ -103,6 +116,7 @@ pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> { fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let context = Arc::new(EvaluationContext::from_args(&raw_args)); let context = Arc::new(EvaluationContext::from_args(&raw_args));
let external_redirection = raw_args.call_info.args.external_redirection;
let args = raw_args.evaluate_once()?; let args = raw_args.evaluate_once()?;
let block: CapturedBlock = args.req(0)?; let block: CapturedBlock = args.req(0)?;
@ -119,7 +133,7 @@ fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let context = context.clone(); let context = context.clone();
let row = make_indexed_item(input.0, input.1); let row = make_indexed_item(input.0, input.1);
match process_row(block, context, row) { match process_row(block, context, row, external_redirection) {
Ok(s) => s, Ok(s) => s,
Err(e) => OutputStream::one(Value::error(e)), Err(e) => OutputStream::one(Value::error(e)),
} }
@ -133,7 +147,7 @@ fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let block = block.clone(); let block = block.clone();
let context = context.clone(); let context = context.clone();
match process_row(block, context, input) { match process_row(block, context, input, external_redirection) {
Ok(s) => s, Ok(s) => s,
Err(e) => OutputStream::one(Value::error(e)), Err(e) => OutputStream::one(Value::error(e)),
} }

View File

@ -2,19 +2,14 @@ use crate::commands::each::process_row;
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{hir::CapturedBlock, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{
hir::{CapturedBlock, ExternalRedirection},
Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged; use nu_source::Tagged;
use serde::Deserialize;
pub struct EachGroup; pub struct EachGroup;
#[derive(Deserialize)]
pub struct EachGroupArgs {
group_size: Tagged<usize>,
block: CapturedBlock,
//numbered: Tagged<bool>,
}
impl WholeStreamCommand for EachGroup { impl WholeStreamCommand for EachGroup {
fn name(&self) -> &str { fn name(&self) -> &str {
"each group" "each group"
@ -42,19 +37,24 @@ impl WholeStreamCommand for EachGroup {
}] }]
} }
fn run_with_actions(&self, raw_args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let context = Arc::new(EvaluationContext::from_args(&raw_args)); let context = Arc::new(EvaluationContext::from_args(&raw_args));
let (each_args, input): (EachGroupArgs, _) = raw_args.process()?; let external_redirection = raw_args.call_info.args.external_redirection;
let block = Arc::new(Box::new(each_args.block)); let args = raw_args.evaluate_once()?;
let group_size: Tagged<usize> = args.req(0)?;
let block: CapturedBlock = args.req(1)?;
let block = Arc::new(Box::new(block));
let each_group_iterator = EachGroupIterator { let each_group_iterator = EachGroupIterator {
block, block,
context, context,
group_size: each_args.group_size.item, group_size: group_size.item,
input, input: args.input,
external_redirection,
}; };
Ok(each_group_iterator.flatten().to_action_stream()) Ok(each_group_iterator.flatten().map(Ok).to_input_stream())
} }
} }
@ -63,6 +63,7 @@ struct EachGroupIterator {
context: Arc<EvaluationContext>, context: Arc<EvaluationContext>,
group_size: usize, group_size: usize,
input: InputStream, input: InputStream,
external_redirection: ExternalRedirection,
} }
impl Iterator for EachGroupIterator { impl Iterator for EachGroupIterator {
@ -89,6 +90,7 @@ impl Iterator for EachGroupIterator {
group, group,
self.block.clone(), self.block.clone(),
self.context.clone(), self.context.clone(),
self.external_redirection,
)) ))
} }
} }
@ -97,13 +99,14 @@ pub(crate) fn run_block_on_vec(
input: Vec<Value>, input: Vec<Value>,
block: Arc<Box<CapturedBlock>>, block: Arc<Box<CapturedBlock>>,
context: Arc<EvaluationContext>, context: Arc<EvaluationContext>,
external_redirection: ExternalRedirection,
) -> OutputStream { ) -> OutputStream {
let value = Value { let value = Value {
value: UntaggedValue::Table(input), value: UntaggedValue::Table(input),
tag: Tag::unknown(), tag: Tag::unknown(),
}; };
match process_row(block, context, value) { match process_row(block, context, value, external_redirection) {
Ok(s) => { Ok(s) => {
// We need to handle this differently depending on whether process_row // We need to handle this differently depending on whether process_row
// returned just 1 value or if it returned multiple as a stream. // returned just 1 value or if it returned multiple as a stream.

View File

@ -5,17 +5,9 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{hir::CapturedBlock, Primitive, Signature, SyntaxShape, UntaggedValue}; use nu_protocol::{hir::CapturedBlock, Primitive, Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged; use nu_source::Tagged;
use serde::Deserialize;
pub struct EachWindow; pub struct EachWindow;
#[derive(Deserialize)]
pub struct EachWindowArgs {
window_size: Tagged<usize>,
block: CapturedBlock,
stride: Option<Tagged<usize>>,
}
impl WholeStreamCommand for EachWindow { impl WholeStreamCommand for EachWindow {
fn name(&self) -> &str { fn name(&self) -> &str {
"each window" "each window"
@ -49,21 +41,29 @@ impl WholeStreamCommand for EachWindow {
}] }]
} }
fn run_with_actions(&self, raw_args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let context = Arc::new(EvaluationContext::from_args(&raw_args)); let context = Arc::new(EvaluationContext::from_args(&raw_args));
let (each_args, mut input): (EachWindowArgs, _) = raw_args.process()?; let external_redirection = raw_args.call_info.args.external_redirection;
let block = Arc::new(Box::new(each_args.block));
let mut window: Vec<_> = input let mut args = raw_args.evaluate_once()?;
let window_size: Tagged<usize> = args.req(0)?;
let block: CapturedBlock = args.req(1)?;
let stride: Option<Tagged<usize>> = args.get_flag("stride")?;
let block = Arc::new(Box::new(block));
let mut window: Vec<_> = args
.input
.by_ref() .by_ref()
.take(*each_args.window_size - 1) .take(*window_size - 1)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// `window` must start with dummy values, which will be removed on the first iteration // `window` must start with dummy values, which will be removed on the first iteration
let stride = each_args.stride.map(|x| *x).unwrap_or(1); let stride = stride.map(|x| *x).unwrap_or(1);
window.insert(0, UntaggedValue::Primitive(Primitive::Nothing).into()); window.insert(0, UntaggedValue::Primitive(Primitive::Nothing).into());
Ok(input Ok(args
.input
.enumerate() .enumerate()
.map(move |(i, input)| { .map(move |(i, input)| {
// This would probably be more efficient if `last` was a VecDeque // This would probably be more efficient if `last` was a VecDeque
@ -76,14 +76,20 @@ impl WholeStreamCommand for EachWindow {
let local_window = window.clone(); let local_window = window.clone();
if i % stride == 0 { if i % stride == 0 {
Some(run_block_on_vec(local_window, block, context)) Some(run_block_on_vec(
local_window,
block,
context,
external_redirection,
))
} else { } else {
None None
} }
}) })
.filter_map(|x| x)
.flatten() .flatten()
.to_action_stream()) .flatten()
.map(Ok)
.to_input_stream())
} }
} }

View File

@ -1,5 +1,4 @@
use crate::prelude::*; use crate::prelude::*;
use bigdecimal::Zero;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::hir::Operator; use nu_protocol::hir::Operator;
@ -40,23 +39,27 @@ impl WholeStreamCommand for Echo {
} }
} }
pub fn expand_value_to_stream(v: Value) -> InputStream {
match v {
Value {
value: UntaggedValue::Table(table),
..
} => InputStream::from_stream(table.into_iter()),
Value {
value: UntaggedValue::Primitive(Primitive::Range(range)),
tag,
} => InputStream::from_stream(RangeIterator::new(*range, tag)),
x => InputStream::one(x),
}
}
fn echo(args: CommandArgs) -> Result<InputStream, ShellError> { fn echo(args: CommandArgs) -> Result<InputStream, ShellError> {
let args = args.evaluate_once()?; let args = args.evaluate_once()?;
let rest: Vec<Value> = args.rest(0)?; let rest: Vec<Value> = args.rest(0)?;
let stream = rest.into_iter().map(|i| match i.as_string() { let stream = rest.into_iter().map(|i| match i.as_string() {
Ok(s) => InputStream::one(UntaggedValue::string(s).into_value(i.tag.clone())), Ok(s) => InputStream::one(UntaggedValue::string(s).into_value(i.tag)),
_ => match i { _ => expand_value_to_stream(i),
Value {
value: UntaggedValue::Table(table),
..
} => InputStream::from_stream(table.into_iter()),
Value {
value: UntaggedValue::Primitive(Primitive::Range(range)),
tag,
} => InputStream::from_stream(RangeIterator::new(*range, tag)),
x => InputStream::one(x),
},
}); });
Ok(InputStream::from_stream(stream.flatten())) Ok(InputStream::from_stream(stream.flatten()))
@ -81,7 +84,7 @@ impl RangeIterator {
}; };
let end = match range.to.0.item { let end = match range.to.0.item {
Primitive::Nothing => Primitive::Int(u64::MAX.into()), Primitive::Nothing => Primitive::Int(i64::MAX),
x => x, x => x,
}; };
@ -121,11 +124,11 @@ impl Iterator for RangeIterator {
( (
UntaggedValue::Primitive(Primitive::Decimal(x)), UntaggedValue::Primitive(Primitive::Decimal(x)),
UntaggedValue::Primitive(Primitive::Int(y)), UntaggedValue::Primitive(Primitive::Int(y)),
) => x.cmp(&(BigDecimal::zero() + y)), ) => x.cmp(&(BigDecimal::from(*y))),
( (
UntaggedValue::Primitive(Primitive::Int(x)), UntaggedValue::Primitive(Primitive::Int(x)),
UntaggedValue::Primitive(Primitive::Decimal(y)), UntaggedValue::Primitive(Primitive::Decimal(y)),
) => (BigDecimal::zero() + x).cmp(y), ) => (BigDecimal::from(*x)).cmp(y),
_ => { _ => {
self.done = true; self.done = true;
return Some( return Some(

View File

@ -3,8 +3,8 @@ use nu_engine::run_block;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{
hir::CapturedBlock, ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, hir::CapturedBlock, hir::ExternalRedirection, ColumnPath, Primitive, ReturnSuccess, Signature,
UntaggedValue, Value, SyntaxShape, UntaggedValue, Value,
}; };
use crate::utils::arguments::arguments; use crate::utils::arguments::arguments;
@ -63,7 +63,7 @@ impl WholeStreamCommand for Command {
), ),
},Example { },Example {
description: "use a block if setting the empty cell contents is wanted", description: "use a block if setting the empty cell contents is wanted",
example: "echo [[2020/04/16 2020/07/10 2020/11/16]; ['' [27] [37]]] | empty? 2020/04/16 { = [33 37] }", example: "echo [[2020/04/16 2020/07/10 2020/11/16]; ['' [27] [37]]] | empty? 2020/04/16 { [33 37] }",
result: Some( result: Some(
vec![ vec![
UntaggedValue::row(indexmap! { UntaggedValue::row(indexmap! {
@ -140,9 +140,16 @@ fn process_row(
context.scope.enter_scope(); context.scope.enter_scope();
context.scope.add_vars(&default_block.captured.entries); context.scope.add_vars(&default_block.captured.entries);
context.scope.add_var("$it", input.clone()); if let Some((arg, _)) = default_block.block.params.positional.first() {
context.scope.add_var(arg.name(), input.clone());
}
let stream = run_block(&default_block.block, &*context, input_stream); let stream = run_block(
&default_block.block,
&*context,
input_stream,
ExternalRedirection::Stdout,
);
context.scope.exit_scope(); context.scope.exit_scope();
let mut stream = stream?; let mut stream = stream?;

View File

@ -11,12 +11,6 @@ use std::path::PathBuf;
pub struct Enter; pub struct Enter;
#[derive(Deserialize)]
pub struct EnterArgs {
location: Tagged<PathBuf>,
encoding: Option<Tagged<String>>,
}
impl WholeStreamCommand for Enter { impl WholeStreamCommand for Enter {
fn name(&self) -> &str { fn name(&self) -> &str {
"enter" "enter"
@ -75,16 +69,15 @@ documentation link at https://docs.rs/encoding_rs/0.8.28/encoding_rs/#statics"#
} }
} }
fn enter(raw_args: CommandArgs) -> Result<ActionStream, ShellError> { fn enter(args: CommandArgs) -> Result<ActionStream, ShellError> {
let scope = raw_args.scope.clone(); let head = args.call_info.args.head.clone();
let shell_manager = raw_args.shell_manager.clone(); let context = args.context.clone();
let head = raw_args.call_info.args.head.clone(); let scope = args.scope().clone();
let ctrl_c = raw_args.ctrl_c.clone(); let path = args.context.shell_manager.path();
let configs = raw_args.configs.clone(); let args = args.evaluate_once()?;
let current_errors = raw_args.current_errors.clone();
let host = raw_args.host.clone(); let location: Tagged<PathBuf> = args.req(0)?;
let tag = raw_args.call_info.name_tag.clone(); let encoding: Option<Tagged<String>> = args.get_flag("encoding")?;
let (EnterArgs { location, encoding }, _) = raw_args.process()?;
let location_string = location.display().to_string(); let location_string = location.display().to_string();
if location.is_dir() { if location.is_dir() {
@ -93,7 +86,7 @@ fn enter(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
))) )))
} else { } else {
// If it's a file, attempt to open the file as a value and enter it // If it's a file, attempt to open the file as a value and enter it
let cwd = shell_manager.path(); let cwd = path;
let full_path = std::path::PathBuf::from(cwd); let full_path = std::path::PathBuf::from(cwd);
let span = location.span(); let span = location.span();
@ -110,12 +103,9 @@ fn enter(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
if let Some(extension) = file_extension { if let Some(extension) = file_extension {
let command_name = format!("from {}", extension); let command_name = format!("from {}", extension);
if let Some(converter) = scope.get_command(&command_name) { if let Some(converter) = scope.get_command(&command_name) {
let new_args = RawCommandArgs { let tag = tagged_contents.tag.clone();
host, let new_args = CommandArgs {
ctrl_c, context,
configs,
current_errors,
shell_manager,
call_info: UnevaluatedCallInfo { call_info: UnevaluatedCallInfo {
args: nu_protocol::hir::Call { args: nu_protocol::hir::Call {
head, head,
@ -124,13 +114,11 @@ fn enter(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
span: Span::unknown(), span: Span::unknown(),
external_redirection: ExternalRedirection::Stdout, external_redirection: ExternalRedirection::Stdout,
}, },
name_tag: tag, name_tag: tag.clone(),
}, },
scope, input: InputStream::one(tagged_contents),
}; };
let tag = tagged_contents.tag.clone(); let mut result = converter.run(new_args)?;
let mut result =
converter.run(new_args.with_input(vec![tagged_contents]))?;
let result_vec: Vec<Value> = result.drain_vec(); let result_vec: Vec<Value> = result.drain_vec();
Ok(result_vec Ok(result_vec
.into_iter() .into_iter()

View File

@ -39,12 +39,12 @@ impl WholeStreamCommand for Command {
}, },
Example { Example {
description: "flatten a column having a nested table", description: "flatten a column having a nested table",
example: "echo [[origin, people]; [Ecuador, $(echo [[name, meal]; ['Andres', 'arepa']])]] | flatten | get meal", example: "echo [[origin, people]; [Ecuador, (echo [[name, meal]; ['Andres', 'arepa']])]] | flatten | get meal",
result: Some(vec![Value::from("arepa")]), result: Some(vec![Value::from("arepa")]),
}, },
Example { Example {
description: "restrict the flattening by passing column names", description: "restrict the flattening by passing column names",
example: "echo [[origin, crate, versions]; [World, $(echo [[name]; ['nu-cli']]), ['0.21', '0.22']]] | flatten versions | last | get versions", example: "echo [[origin, crate, versions]; [World, (echo [[name]; ['nu-cli']]), ['0.21', '0.22']]] | flatten versions | last | get versions",
result: Some(vec![Value::from("0.22")]), result: Some(vec![Value::from("0.22")]),
} }
] ]

View File

@ -0,0 +1,174 @@
use crate::prelude::*;
use nu_engine::{evaluate_baseline_expr, run_block};
use nu_engine::{FromValue, WholeStreamCommand};
use nu_errors::ShellError;
use nu_protocol::{
hir::{CapturedBlock, ExternalRedirection},
Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
};
pub struct ForIn;
impl WholeStreamCommand for ForIn {
fn name(&self) -> &str {
"for"
}
fn signature(&self) -> Signature {
Signature::build("for")
.required("var", SyntaxShape::String, "the name of the variable")
.required("in", SyntaxShape::String, "the word 'in'")
.required("value", SyntaxShape::Any, "the value we want to iterate")
.required("block", SyntaxShape::Block, "the block to run on each item")
.switch(
"numbered",
"returned a numbered item ($it.index and $it.item)",
Some('n'),
)
}
fn usage(&self) -> &str {
"Run a block on each row of the table."
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
for_in(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Echo the square of each integer",
example: "for x in [1 2 3] { $x * $x }",
result: Some(vec![
UntaggedValue::int(1).into(),
UntaggedValue::int(4).into(),
UntaggedValue::int(9).into(),
]),
},
Example {
description: "Work with elements of a range",
example: "for $x in 1..3 { $x }",
result: Some(vec![
UntaggedValue::int(1).into(),
UntaggedValue::int(2).into(),
UntaggedValue::int(3).into(),
]),
},
Example {
description: "Number each item and echo a message",
example: "for $it in ['bob' 'fred'] --numbered { $\"($it.index) is ($it.item)\" }",
result: Some(vec![Value::from("0 is bob"), Value::from("1 is fred")]),
},
]
}
}
pub fn process_row(
captured_block: Arc<Box<CapturedBlock>>,
context: Arc<EvaluationContext>,
input: Value,
var_name: &str,
external_redirection: ExternalRedirection,
) -> Result<OutputStream, ShellError> {
let input_clone = input.clone();
// When we process a row, we need to know whether the block wants to have the contents of the row as
// a parameter to the block (so it gets assigned to a variable that can be used inside the block) or
// if it wants the contents as as an input stream
let input_stream = if !captured_block.block.params.positional.is_empty() {
InputStream::empty()
} else {
vec![Ok(input_clone)].into_iter().to_input_stream()
};
context.scope.enter_scope();
context.scope.add_vars(&captured_block.captured.entries);
context.scope.add_var(var_name, input);
let result = run_block(
&captured_block.block,
&*context,
input_stream,
external_redirection,
);
context.scope.exit_scope();
result
}
pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
let mut dict = TaggedDictBuilder::new(item.tag());
dict.insert_untagged("index", UntaggedValue::int(index as i64));
dict.insert_value("item", item);
dict.into_value()
}
fn for_in(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let context = Arc::new(EvaluationContext::from_args(&raw_args));
let external_redirection = raw_args.call_info.args.external_redirection;
//let args = raw_args.evaluate_once()?;
let numbered: bool = raw_args.call_info.switch_present("numbered");
let positional = raw_args
.call_info
.args
.positional
.expect("Internal error: type checker should require args");
let var_name = positional[0].var_name()?;
let rhs = evaluate_baseline_expr(&positional[2], &context)?;
let block: CapturedBlock =
FromValue::from_value(&evaluate_baseline_expr(&positional[3], &context)?)?;
let input = crate::commands::echo::expand_value_to_stream(rhs);
let block = Arc::new(Box::new(block));
if numbered {
Ok(input
.enumerate()
.map(move |input| {
let block = block.clone();
let context = context.clone();
let row = make_indexed_item(input.0, input.1);
match process_row(block, context, row, &var_name, external_redirection) {
Ok(s) => s,
Err(e) => OutputStream::one(Value::error(e)),
}
})
.flatten()
.to_output_stream())
} else {
Ok(input
.map(move |input| {
let block = block.clone();
let context = context.clone();
match process_row(block, context, input, &var_name, external_redirection) {
Ok(s) => s,
Err(e) => OutputStream::one(Value::error(e)),
}
})
.flatten()
.to_output_stream())
}
}
#[cfg(test)]
mod tests {
use super::ForIn;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(ForIn {})
}
}

View File

@ -2,17 +2,12 @@ use crate::prelude::*;
use nu_engine::evaluate_baseline_expr; use nu_engine::evaluate_baseline_expr;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged; use nu_source::Tagged;
use std::borrow::Borrow; use std::borrow::Borrow;
pub struct Format; pub struct Format;
#[derive(Deserialize)]
pub struct FormatArgs {
pattern: Tagged<String>,
}
impl WholeStreamCommand for Format { impl WholeStreamCommand for Format {
fn name(&self) -> &str { fn name(&self) -> &str {
"format" "format"
@ -30,7 +25,7 @@ impl WholeStreamCommand for Format {
"Format columns into a string using a simple pattern." "Format columns into a string using a simple pattern."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
format_command(args) format_command(args)
} }
@ -43,14 +38,16 @@ impl WholeStreamCommand for Format {
} }
} }
fn format_command(args: CommandArgs) -> Result<ActionStream, ShellError> { fn format_command(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = Arc::new(EvaluationContext::from_args(&args)); let ctx = Arc::new(EvaluationContext::from_args(&args));
let (FormatArgs { pattern }, input) = args.process()?; let args = args.evaluate_once()?;
let pattern: Tagged<String> = args.req(0)?;
let format_pattern = format(&pattern); let format_pattern = format(&pattern);
let commands = Arc::new(format_pattern); let commands = Arc::new(format_pattern);
Ok(input Ok(args
.input
.map(move |value| { .map(move |value| {
let mut output = String::new(); let mut output = String::new();
let commands = commands.clone(); let commands = commands.clone();
@ -82,9 +79,9 @@ fn format_command(args: CommandArgs) -> Result<ActionStream, ShellError> {
} }
} }
ReturnSuccess::value(UntaggedValue::string(output).into_untagged_value()) Ok(UntaggedValue::string(output).into_untagged_value())
}) })
.to_action_stream()) .to_input_stream())
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -2,20 +2,12 @@ use crate::prelude::*;
use nu_data::base::shape::InlineShape; use nu_data::base::shape::InlineShape;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{ColumnPath, Primitive::Filesize, Signature, SyntaxShape, UntaggedValue, Value};
ColumnPath, Primitive::Filesize, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged; use nu_source::Tagged;
use nu_value_ext::get_data_by_column_path; use nu_value_ext::get_data_by_column_path;
pub struct FileSize; pub struct FileSize;
#[derive(Deserialize)]
pub struct Arguments {
field: ColumnPath,
format: Tagged<String>,
}
impl WholeStreamCommand for FileSize { impl WholeStreamCommand for FileSize {
fn name(&self) -> &str { fn name(&self) -> &str {
"format filesize" "format filesize"
@ -39,7 +31,7 @@ impl WholeStreamCommand for FileSize {
"Converts a column of filesizes to some specified format" "Converts a column of filesizes to some specified format"
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
filesize(args) filesize(args)
} }
@ -63,50 +55,55 @@ fn process_row(
input: Value, input: Value,
format: Tagged<String>, format: Tagged<String>,
field: Arc<ColumnPath>, field: Arc<ColumnPath>,
) -> Result<ActionStream, ShellError> { ) -> Result<Value, ShellError> {
Ok({ let replace_for = get_data_by_column_path(&input, &field, move |_, _, error| error);
let replace_for = get_data_by_column_path(&input, &field, move |_, _, error| error); match replace_for {
match replace_for { Ok(s) => {
Ok(s) => { if let Value {
if let Value { value: UntaggedValue::Primitive(Filesize(fs)),
value: UntaggedValue::Primitive(Filesize(fs)), ..
.. } = s
} = s {
{ let byte_format = InlineShape::format_bytes(fs, Some(&format.item));
let byte_format = InlineShape::format_bytes(&fs, Some(&format.item)); let byte_value = Value::from(byte_format.1);
let byte_value = Value::from(byte_format.1); Ok(input
ActionStream::one(ReturnSuccess::value( .replace_data_at_column_path(&field, byte_value)
input.replace_data_at_column_path(&field, byte_value).expect("Given that the existence check was already done, this shouldn't trigger never"), .expect(
)) "Given that the existence check was already done, this shouldn't trigger never",
} else { ))
return Err(ShellError::labeled_error( } else {
"the data in this row is not of the type filesize", Err(ShellError::labeled_error(
"invalid datatype in row", "the data in this row is not of the type filesize",
input.tag(), "invalid datatype in row",
)); input.tag(),
} ))
} }
Err(e) => ActionStream::one(Err(e)),
} }
}) Err(e) => Err(e),
}
} }
fn filesize(raw_args: CommandArgs) -> Result<ActionStream, ShellError> { fn filesize(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { field, format }, input) = raw_args.process()?; let args = raw_args.evaluate_once()?;
let field: ColumnPath = args.req(0)?;
let format: Tagged<String> = args.req(1)?;
let field = Arc::new(field); let field = Arc::new(field);
Ok(input Ok(args
.input
.map(move |input| { .map(move |input| {
let format = format.clone(); let format = format.clone();
let field = field.clone(); let field = field.clone();
match process_row(input, format, field) { match process_row(input, format, field) {
Ok(s) => s, Ok(s) => Ok(s),
Err(e) => ActionStream::one(Err(e)), Err(e) => Err(e),
} }
}) })
.flatten() .flatten()
.to_action_stream()) .map(Ok)
.to_input_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -20,7 +20,7 @@ impl WholeStreamCommand for From {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one( Ok(OutputStream::one(
UntaggedValue::string(get_full_help(&From, &args.scope)).into_value(Tag::unknown()), UntaggedValue::string(get_full_help(&From, args.scope())).into_value(Tag::unknown()),
)) ))
} }
} }

View File

@ -35,7 +35,7 @@ fn convert_json_value_to_nu_value(v: &nu_json::Value, tag: impl Into<Tag>) -> Va
nu_json::Value::Null => UntaggedValue::Primitive(Primitive::Nothing).into_value(&tag), nu_json::Value::Null => UntaggedValue::Primitive(Primitive::Nothing).into_value(&tag),
nu_json::Value::Bool(b) => UntaggedValue::boolean(*b).into_value(&tag), nu_json::Value::Bool(b) => UntaggedValue::boolean(*b).into_value(&tag),
nu_json::Value::F64(n) => UntaggedValue::decimal_from_float(*n, span).into_value(&tag), nu_json::Value::F64(n) => UntaggedValue::decimal_from_float(*n, span).into_value(&tag),
nu_json::Value::U64(n) => UntaggedValue::int(*n).into_value(&tag), nu_json::Value::U64(n) => UntaggedValue::big_int(*n).into_value(&tag),
nu_json::Value::I64(n) => UntaggedValue::int(*n).into_value(&tag), nu_json::Value::I64(n) => UntaggedValue::int(*n).into_value(&tag),
nu_json::Value::String(s) => { nu_json::Value::String(s) => {
UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(&tag) UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(&tag)

View File

@ -228,8 +228,8 @@ pub fn get_column_from_row_error(
} => { } => {
let primary_label = format!("There isn't a column named '{}'", &column); let primary_label = format!("There isn't a column named '{}'", &column);
if let Some(suggestions) = did_you_mean(&obj_source, column_path_tried.as_string()) { did_you_mean(&obj_source, column_path_tried.as_string()).map(|suggestions| {
Some(ShellError::labeled_error_with_secondary( ShellError::labeled_error_with_secondary(
"Unknown column", "Unknown column",
primary_label, primary_label,
column_path_tried.span, column_path_tried.span,
@ -239,10 +239,8 @@ pub fn get_column_from_row_error(
&obj_source.data_descriptors().join(", ") &obj_source.data_descriptors().join(", ")
), ),
column_path_tried.span.since(path_members_span), column_path_tried.span.since(path_members_span),
)) )
} else { })
None
}
} }
PathMember { PathMember {
unspanned: UnspannedPathMember::Int(idx), unspanned: UnspannedPathMember::Int(idx),

View File

@ -2,17 +2,13 @@ use crate::prelude::*;
use crate::utils::suggestions::suggestions; use crate::utils::suggestions::suggestions;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::hir::ExternalRedirection;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged; use nu_source::Tagged;
use nu_value_ext::as_string; use nu_value_ext::as_string;
pub struct Command; pub struct Command;
#[derive(Deserialize)]
pub struct Arguments {
grouper: Option<Value>,
}
impl WholeStreamCommand for Command { impl WholeStreamCommand for Command {
fn name(&self) -> &str { fn name(&self) -> &str {
"group-by" "group-by"
@ -99,7 +95,7 @@ impl WholeStreamCommand for Command {
Example { Example {
description: description:
"use the block form to generate a grouping key when each row gets processed", "use the block form to generate a grouping key when each row gets processed",
example: "echo [1 3 1 3 2 1 1] | group-by { = ($it - 1) mod 3 }", example: "echo [1 3 1 3 2 1 1] | group-by { ($it - 1) mod 3 }",
result: Some(vec![UntaggedValue::row(indexmap! { result: Some(vec![UntaggedValue::row(indexmap! {
"0".to_string() => UntaggedValue::Table(vec![ "0".to_string() => UntaggedValue::Table(vec![
UntaggedValue::int(1).into(), UntaggedValue::int(1).into(),
@ -130,9 +126,10 @@ enum Grouper {
pub fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let context = Arc::new(EvaluationContext::from_args(&args)); let context = Arc::new(EvaluationContext::from_args(&args));
let (Arguments { grouper }, input) = args.process()?; let args = args.evaluate_once()?;
let values: Vec<Value> = input.collect(); let grouper = args.opt(0)?;
let values: Vec<Value> = args.input.collect();
let mut keys: Vec<Result<String, ShellError>> = vec![]; let mut keys: Vec<Result<String, ShellError>> = vec![];
let mut group_strategy = Grouper::ByColumn(None); let mut group_strategy = Grouper::ByColumn(None);
@ -148,7 +145,12 @@ pub fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
let run = block.clone(); let run = block.clone();
let context = context.clone(); let context = context.clone();
match crate::commands::each::process_row(run, context, value.clone()) { match crate::commands::each::process_row(
run,
context,
value.clone(),
ExternalRedirection::Stdout,
) {
Ok(mut s) => { Ok(mut s) => {
let collection: Vec<Value> = s.drain_vec(); let collection: Vec<Value> = s.drain_vec();

View File

@ -2,21 +2,11 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::ShellTypeName; use nu_protocol::ShellTypeName;
use nu_protocol::{ use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::{Tag, Tagged}; use nu_source::{Tag, Tagged};
use base64::{decode_config, encode_config}; use base64::{decode_config, encode_config};
#[derive(Deserialize)]
pub struct Arguments {
pub rest: Vec<ColumnPath>,
pub character_set: Option<Tagged<String>>,
pub encode: Tagged<bool>,
pub decode: Tagged<bool>,
}
#[derive(Clone)] #[derive(Clone)]
pub struct Base64Config { pub struct Base64Config {
pub character_set: String, pub character_set: String,
@ -46,13 +36,13 @@ impl WholeStreamCommand for SubCommand {
Some('c'), Some('c'),
) )
.switch( .switch(
"encode", "encode",
"encode the input as base64. This is the default behavior if not specified.", "encode the input as base64. This is the default behavior if not specified.",
Some('e') Some('e')
) )
.switch( .switch(
"decode", "decode",
"decode the input from base64", "decode the input from base64",
Some('d')) Some('d'))
.rest( .rest(
SyntaxShape::ColumnPath, SyntaxShape::ColumnPath,
@ -64,7 +54,7 @@ impl WholeStreamCommand for SubCommand {
"base64 encode or decode a value" "base64 encode or decode a value"
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
operate(args) operate(args)
} }
@ -95,29 +85,25 @@ impl WholeStreamCommand for SubCommand {
} }
} }
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> { fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone(); let name_tag = args.call_info.name_tag.clone();
let args = args.evaluate_once()?;
let ( let encode = args.has_flag("encode");
Arguments { let decode = args.has_flag("decode");
encode, let character_set: Option<Tagged<String>> = args.get_flag("character_set")?;
decode, let column_paths: Vec<ColumnPath> = args.rest(0)?;
character_set,
rest,
},
input,
) = args.process()?;
if encode.item && decode.item { if encode && decode {
return Ok(ActionStream::one(Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"only one of --decode and --encode flags can be used", "only one of --decode and --encode flags can be used",
"conflicting flags", "conflicting flags",
name_tag, name_tag,
)))); ));
} }
// Default the action to be encoding if no flags are specified. // Default the action to be encoding if no flags are specified.
let action_type = if *decode.item() { let action_type = if decode {
ActionType::Decode ActionType::Decode
} else { } else {
ActionType::Encode ActionType::Encode
@ -134,12 +120,11 @@ fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
action_type, action_type,
}; };
let column_paths: Vec<_> = rest; Ok(args
.input
Ok(input
.map(move |v| { .map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
ReturnSuccess::value(action(&v, &encoding_config, v.tag())?) action(&v, &encoding_config, v.tag())
} else { } else {
let mut ret = v; let mut ret = v;
@ -151,10 +136,10 @@ fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
)?; )?;
} }
ReturnSuccess::value(ret) Ok(ret)
} }
}) })
.to_action_stream()) .to_input_stream())
} }
fn action( fn action(
@ -182,7 +167,7 @@ fn action(
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"value is not an accepted character set", "value is not an accepted character set",
format!( format!(
"{} is not a valid character-set.\nPlease use `help hash base64` to see a list of valid character sets.", "{} is not a valid character-set.\nPlease use `help hash base64` to see a list of valid character sets.",
&base64_config.character_set &base64_config.character_set
), ),
tag.into().span, tag.into().span,

View File

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
pub struct Command; pub struct Command;
@ -21,10 +21,10 @@ impl WholeStreamCommand for Command {
"Apply hash function." "Apply hash function."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(ActionStream::one(ReturnSuccess::value( Ok(OutputStream::one(
UntaggedValue::string(get_full_help(&Command, &args.scope)).into_value(Tag::unknown()), UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
))) ))
} }
} }

View File

@ -2,16 +2,9 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::ShellTypeName; use nu_protocol::ShellTypeName;
use nu_protocol::{ use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tag; use nu_source::Tag;
#[derive(Deserialize)]
pub struct Arguments {
pub rest: Vec<ColumnPath>,
}
pub struct SubCommand; pub struct SubCommand;
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
@ -30,7 +23,7 @@ impl WholeStreamCommand for SubCommand {
"md5 encode a value" "md5 encode a value"
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
operate(args) operate(args)
} }
@ -56,15 +49,15 @@ impl WholeStreamCommand for SubCommand {
} }
} }
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> { fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest }, input) = args.process()?; let args = args.evaluate_once()?;
let column_paths: Vec<ColumnPath> = args.rest(0)?;
let column_paths: Vec<_> = rest; Ok(args
.input
Ok(input
.map(move |v| { .map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?) action(&v, v.tag())
} else { } else {
let mut ret = v; let mut ret = v;
@ -75,10 +68,10 @@ fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
)?; )?;
} }
ReturnSuccess::value(ret) Ok(ret)
} }
}) })
.to_action_stream()) .to_input_stream())
} }
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -38,7 +38,7 @@ impl WholeStreamCommand for Help {
fn help(args: CommandArgs) -> Result<ActionStream, ShellError> { fn help(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let scope = args.scope.clone(); let scope = args.scope().clone();
let (HelpArgs { rest }, ..) = args.process()?; let (HelpArgs { rest }, ..) = args.process()?;
if !rest.is_empty() { if !rest.is_empty() {

View File

@ -76,10 +76,10 @@ pub fn histogram(args: CommandArgs) -> Result<ActionStream, ShellError> {
}; };
let column_grouper = if !columns.is_empty() { let column_grouper = if !columns.is_empty() {
match columns.remove(0).split_last() { columns
Some((key, _)) => Some(key.as_string().tagged(&name)), .remove(0)
None => None, .split_last()
} .map(|(key, _)| key.as_string().tagged(&name))
} else { } else {
None None
}; };
@ -160,7 +160,7 @@ pub fn histogram(args: CommandArgs) -> Result<ActionStream, ShellError> {
"{}%", "{}%",
// Some(2) < the number of digits // Some(2) < the number of digits
// true < group the digits // true < group the digits
crate::commands::str_::from::action(&percentage, &name, Some(2), true)? crate::commands::into::string::action(&percentage, &name, Some(2), true)?
.as_string()? .as_string()?
); );
fact.insert_untagged("percentage", UntaggedValue::string(fmt_percentage)); fact.insert_untagged("percentage", UntaggedValue::string(fmt_percentage));

View File

@ -59,6 +59,7 @@ impl WholeStreamCommand for If {
} }
fn if_command(raw_args: CommandArgs) -> Result<OutputStream, ShellError> { fn if_command(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = raw_args.call_info.name_tag.clone(); let tag = raw_args.call_info.name_tag.clone();
let external_redirection = raw_args.call_info.args.external_redirection;
let context = Arc::new(EvaluationContext::from_args(&raw_args)); let context = Arc::new(EvaluationContext::from_args(&raw_args));
let args = raw_args.evaluate_once()?; let args = raw_args.evaluate_once()?;
@ -105,9 +106,9 @@ fn if_command(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(condition) => match condition.as_bool() { Ok(condition) => match condition.as_bool() {
Ok(b) => { Ok(b) => {
let result = if b { let result = if b {
run_block(&then_case.block, &*context, input) run_block(&then_case.block, &*context, input, external_redirection)
} else { } else {
run_block(&else_case.block, &*context, input) run_block(&else_case.block, &*context, input, external_redirection)
}; };
context.scope.exit_scope(); context.scope.exit_scope();

View File

@ -2,6 +2,7 @@ use crate::prelude::*;
use nu_engine::run_block; use nu_engine::run_block;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::hir::ExternalRedirection;
use nu_protocol::{ use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
}; };
@ -50,7 +51,7 @@ impl WholeStreamCommand for Command {
.into()]), .into()]),
},Example { },Example {
description: "Use in block form for more involved insertion logic", description: "Use in block form for more involved insertion logic",
example: "echo [[author, lucky_number]; ['Yehuda', 4]] | insert success { = $it.lucky_number * 10 }", example: "echo [[author, lucky_number]; ['Yehuda', 4]] | insert success { $it.lucky_number * 10 }",
result: Some(vec![UntaggedValue::row(indexmap! { result: Some(vec![UntaggedValue::row(indexmap! {
"author".to_string() => Value::from("Yehuda"), "author".to_string() => Value::from("Yehuda"),
"lucky_number".to_string() => UntaggedValue::int(4).into(), "lucky_number".to_string() => UntaggedValue::int(4).into(),
@ -79,9 +80,16 @@ fn process_row(
context.scope.enter_scope(); context.scope.enter_scope();
context.scope.add_vars(&block.captured.entries); context.scope.add_vars(&block.captured.entries);
context.scope.add_var("$it", input.clone()); if let Some((arg, _)) = block.block.params.positional.first() {
context.scope.add_var(arg.name(), input.clone());
}
let result = run_block(&block.block, &*context, input_stream); let result = run_block(
&block.block,
&*context,
input_stream,
ExternalRedirection::Stdout,
);
context.scope.exit_scope(); context.scope.exit_scope();

View File

@ -0,0 +1,270 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
use num_bigint::{BigInt, ToBigInt};
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"into binary"
}
fn signature(&self) -> Signature {
Signature::build("into binary")
.rest(
SyntaxShape::ColumnPath,
"column paths to convert to binary (for table input)",
)
.named(
"skip",
SyntaxShape::Int,
"skip x number of bytes",
Some('s'),
)
.named(
"bytes",
SyntaxShape::Int,
"show y number of bytes",
Some('b'),
)
}
fn usage(&self) -> &str {
"Convert value to a binary primitive"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
into_binary(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "convert string to a nushell binary primitive",
example:
"echo 'This is a string that is exactly 52 characters long.' | into binary",
result: Some(vec![UntaggedValue::binary(
"This is a string that is exactly 52 characters long."
.to_string()
.as_bytes()
.to_vec(),
)
.into()]),
},
Example {
description: "convert string to a nushell binary primitive",
example:
"echo 'This is a string that is exactly 52 characters long.' | into binary --skip 10",
result: Some(vec![UntaggedValue::binary(
"string that is exactly 52 characters long."
.to_string()
.as_bytes()
.to_vec(),
)
.into()]),
},
Example {
description: "convert string to a nushell binary primitive",
example:
"echo 'This is a string that is exactly 52 characters long.' | into binary --skip 10 --bytes 10",
result: Some(vec![UntaggedValue::binary(
"string tha"
.to_string()
.as_bytes()
.to_vec(),
)
.into()]),
},
Example {
description: "convert a number to a nushell binary primitive",
example: "echo 1 | into binary",
result: Some(vec![
UntaggedValue::binary(i64::from(1).to_le_bytes().to_vec()).into()
]),
},
Example {
description: "convert a boolean to a nushell binary primitive",
example: "echo $true | into binary",
result: Some(vec![
UntaggedValue::binary(i64::from(1).to_le_bytes().to_vec()).into()
]),
},
Example {
description: "convert a filesize to a nushell binary primitive",
example: "ls | where name == LICENSE | get size | into binary",
result: None,
},
Example {
description: "convert a filepath to a nushell binary primitive",
example: "ls | where name == LICENSE | get name | path expand | into binary",
result: None,
},
Example {
description: "convert a decimal to a nushell binary primitive",
example: "echo 1.234 | into binary",
result: Some(vec![
UntaggedValue::binary(BigInt::from(1).to_bytes_le().1).into()
]),
},
]
}
}
fn into_binary(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let skip: Option<Value> = args.get_flag("skip")?;
let bytes: Option<Value> = args.get_flag("bytes")?;
let column_paths: Vec<ColumnPath> = args.rest(0)?;
Ok(args
.input
.map(move |v| {
if column_paths.is_empty() {
action(&v, v.tag(), &skip, &bytes)
} else {
let mut ret = v;
for path in &column_paths {
let skip_clone = skip.clone();
let bytes_clone = bytes.clone();
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag(), &skip_clone, &bytes_clone)),
)?;
}
Ok(ret)
}
})
.to_input_stream())
}
fn int_to_endian(n: i64) -> Vec<u8> {
if cfg!(target_endian = "little") {
// eprintln!("Little Endian");
n.to_le_bytes().to_vec()
} else {
// eprintln!("Big Endian");
n.to_be_bytes().to_vec()
}
}
fn bigint_to_endian(n: &BigInt) -> Vec<u8> {
if cfg!(target_endian = "little") {
// eprintln!("Little Endian");
n.to_bytes_le().1
} else {
// eprintln!("Big Endian");
n.to_bytes_be().1
}
}
pub fn action(
input: &Value,
tag: impl Into<Tag>,
skip: &Option<Value>,
bytes: &Option<Value>,
) -> Result<Value, ShellError> {
let tag = tag.into();
let skip_bytes = match skip {
Some(s) => s.as_usize().unwrap_or(0),
None => 0usize,
};
let num_bytes = match bytes {
Some(b) => b.as_usize().unwrap_or(0),
None => 0usize,
};
match &input.value {
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::binary(match prim {
Primitive::Binary(b) => {
if num_bytes == 0usize {
b.to_vec().into_iter().skip(skip_bytes).collect()
} else {
b.to_vec()
.into_iter()
.skip(skip_bytes)
.take(num_bytes)
.collect()
}
}
Primitive::Int(n_ref) => int_to_endian(*n_ref),
Primitive::BigInt(n_ref) => bigint_to_endian(n_ref),
Primitive::Decimal(dec) => match dec.to_bigint() {
Some(n) => bigint_to_endian(&n),
None => {
return Err(ShellError::unimplemented(
"failed to convert decimal to int",
));
}
},
Primitive::Filesize(a_filesize) => match a_filesize.to_bigint() {
Some(n) => bigint_to_endian(&n),
None => {
return Err(ShellError::unimplemented(
"failed to convert filesize to bigint",
));
}
},
Primitive::String(a_string) => {
// a_string.as_bytes().to_vec()
if num_bytes == 0usize {
a_string
.as_bytes()
.to_vec()
.into_iter()
.skip(skip_bytes)
.collect()
} else {
a_string
.as_bytes()
.to_vec()
.into_iter()
.skip(skip_bytes)
.take(num_bytes)
.collect()
}
}
Primitive::Boolean(a_bool) => match a_bool {
false => int_to_endian(0),
true => int_to_endian(1),
},
Primitive::Date(a_date) => a_date.format("%c").to_string().as_bytes().to_vec(),
Primitive::FilePath(a_filepath) => a_filepath
.as_path()
.display()
.to_string()
.as_bytes()
.to_vec(),
_ => {
return Err(ShellError::unimplemented(
"'into binary' for non-numeric primitives",
))
}
})
.into_value(&tag)),
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
"specify column name to use, with 'into binary COLUMN'",
"found table",
tag,
)),
_ => Err(ShellError::unimplemented(
"'into binary' for unsupported type",
)),
}
}
#[cfg(test)]
mod tests {
use super::ShellError;
use super::SubCommand;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(SubCommand {})
}
}

View File

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; use nu_protocol::{Signature, UntaggedValue};
pub struct Command; pub struct Command;
@ -18,10 +18,10 @@ impl WholeStreamCommand for Command {
"Apply into function." "Apply into function."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(ActionStream::one(ReturnSuccess::value( Ok(OutputStream::one(
UntaggedValue::string(get_full_help(&Command, &args.scope)).into_value(Tag::unknown()), UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
))) ))
} }
} }

View File

@ -1,18 +1,11 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, use num_bigint::ToBigInt;
};
use num_bigint::{BigInt, ToBigInt};
pub struct SubCommand; pub struct SubCommand;
#[derive(Deserialize)]
pub struct Arguments {
pub rest: Vec<ColumnPath>,
}
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"into int" "into int"
@ -29,7 +22,7 @@ impl WholeStreamCommand for SubCommand {
"Convert value to integer" "Convert value to integer"
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
into_int(args) into_int(args)
} }
@ -85,13 +78,15 @@ impl WholeStreamCommand for SubCommand {
} }
} }
fn into_int(args: CommandArgs) -> Result<ActionStream, ShellError> { fn into_int(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest: column_paths }, input) = args.process()?; let args = args.evaluate_once()?;
let column_paths: Vec<ColumnPath> = args.rest(0)?;
Ok(input Ok(args
.input
.map(move |v| { .map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?) action(&v, v.tag())
} else { } else {
let mut ret = v; let mut ret = v;
for path in &column_paths { for path in &column_paths {
@ -101,10 +96,10 @@ fn into_int(args: CommandArgs) -> Result<ActionStream, ShellError> {
)?; )?;
} }
ReturnSuccess::value(ret) Ok(ret)
} }
}) })
.to_action_stream()) .to_input_stream())
} }
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> { pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
@ -118,20 +113,34 @@ pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
} }
}, },
Primitive::Decimal(dec) => match dec.to_bigint() { Primitive::Decimal(dec) => match dec.to_bigint() {
Some(n) => n, Some(n) => match n.to_i64() {
Some(i) => i,
None => {
return Err(ShellError::unimplemented(
"failed to convert decimal to int",
));
}
},
None => { None => {
return Err(ShellError::unimplemented( return Err(ShellError::unimplemented(
"failed to convert decimal to int", "failed to convert decimal to int",
)); ));
} }
}, },
Primitive::Int(n_ref) => n_ref.to_owned(), Primitive::Int(n_ref) => *n_ref,
Primitive::Boolean(a_bool) => match a_bool { Primitive::Boolean(a_bool) => match a_bool {
false => BigInt::from(0), false => 0,
true => BigInt::from(1), true => 1,
}, },
Primitive::Filesize(a_filesize) => match a_filesize.to_bigint() { Primitive::Filesize(a_filesize) => match a_filesize.to_bigint() {
Some(n) => n, Some(n) => match n.to_i64() {
Some(i) => i,
None => {
return Err(ShellError::unimplemented(
"failed to convert filesize to bigint",
));
}
},
None => { None => {
return Err(ShellError::unimplemented( return Err(ShellError::unimplemented(
"failed to convert filesize to bigint", "failed to convert filesize to bigint",
@ -154,13 +163,17 @@ pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
} }
} }
fn int_from_string(a_string: &str, tag: &Tag) -> Result<BigInt, ShellError> { fn int_from_string(a_string: &str, tag: &Tag) -> Result<i64, ShellError> {
match a_string.parse::<BigInt>() { match a_string.parse::<i64>() {
Ok(n) => Ok(n), Ok(n) => Ok(n),
Err(_) => match a_string.parse::<f64>() { Err(_) => match a_string.parse::<f64>() {
Ok(res_float) => match res_float.to_bigint() { Ok(f) => match f.to_i64() {
Some(n) => Ok(n), Some(i) => Ok(i),
None => Err(ShellError::unimplemented("failed to convert f64 to int")), None => Err(ShellError::labeled_error(
"Could not convert string value to int",
"original value",
tag.clone(),
)),
}, },
Err(_) => Err(ShellError::labeled_error( Err(_) => Err(ShellError::labeled_error(
"Could not convert string value to int", "Could not convert string value to int",

View File

@ -1,5 +1,9 @@
mod binary;
mod command; mod command;
mod int; mod int;
pub mod string;
pub use binary::SubCommand as IntoBinary;
pub use command::Command as Into; pub use command::Command as Into;
pub use int::SubCommand as IntoInt; pub use int::SubCommand as IntoInt;
pub use string::SubCommand as IntoString;

View File

@ -1,9 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged; use nu_source::Tagged;
use num_bigint::{BigInt, BigUint, ToBigInt}; use num_bigint::{BigInt, BigUint, ToBigInt};
// TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml) // TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml)
@ -14,22 +12,16 @@ use std::iter;
pub struct SubCommand; pub struct SubCommand;
struct Arguments {
decimals: Option<Tagged<u64>>,
group_digits: bool,
column_paths: Vec<ColumnPath>,
}
impl WholeStreamCommand for SubCommand { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"str from" "into string"
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("str from") Signature::build("into string")
.rest( .rest(
SyntaxShape::ColumnPath, SyntaxShape::ColumnPath,
"optionally convert to string by column paths", "column paths to convert to string (for table input)",
) )
.named( .named(
"decimals", "decimals",
@ -40,66 +32,83 @@ impl WholeStreamCommand for SubCommand {
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Converts numeric types to strings. Trims trailing zeros unless decimals parameter is specified." "Convert value to string"
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
operate(args) into_string(args)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
description: "round to nearest integer", description: "convert decimal to string and round to nearest integer",
example: "echo 1.7 | str from -d 0", example: "echo 1.7 | into string -d 0",
result: Some(vec![UntaggedValue::string("2").into_untagged_value()]), result: Some(vec![UntaggedValue::string("2").into_untagged_value()]),
}, },
/*
FIXME: this isn't currently supported because of num_format being out of date. Once it's updated, re-enable this
Example { Example {
description: "format large number with localized digit grouping", description: "convert decimal to string",
example: "= 1000000.2 | str from -g", example: "echo 4.3 | into string",
result: Some(vec![ result: Some(vec![UntaggedValue::string("4.3").into_untagged_value()]),
UntaggedValue::string("1,000,000.2").into_untagged_value() },
]), Example {
description: "convert string to string",
example: "echo '1234' | into string",
result: Some(vec![UntaggedValue::string("1234").into_untagged_value()]),
},
Example {
description: "convert boolean to string",
example: "echo $true | into string",
result: Some(vec![UntaggedValue::string("true").into_untagged_value()]),
},
Example {
description: "convert date to string",
example: "date now | into string",
result: None,
},
Example {
description: "convert filepath to string",
example: "ls Cargo.toml | get name | into string",
result: None,
},
Example {
description: "convert filesize to string",
example: "ls Cargo.toml | get size | into string",
result: None,
}, },
*/
] ]
} }
} }
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> { fn into_string(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (options, input) = args.extract(|params| { let args = args.evaluate_once()?;
Ok(Arguments {
decimals: params.get_flag("decimals")?,
group_digits: false,
column_paths: params.rest_args()?,
})
})?;
let digits = options.decimals.as_ref().map(|tagged| tagged.item); let decimals: Option<Tagged<u64>> = args.get_flag("decimals")?;
let group_digits = options.group_digits; let column_paths: Vec<ColumnPath> = args.rest(0)?;
Ok(input let digits = decimals.as_ref().map(|tagged| tagged.item);
let group_digits = false;
Ok(args
.input
.map(move |v| { .map(move |v| {
if options.column_paths.is_empty() { if column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag(), digits, group_digits)?) action(&v, v.tag(), digits, group_digits)
} else { } else {
let mut ret = v; let mut ret = v;
for path in &options.column_paths { for path in &column_paths {
ret = ret.swap_data_by_column_path( ret = ret.swap_data_by_column_path(
path, path,
Box::new(move |old| action(old, old.tag(), digits, group_digits)), Box::new(move |old| action(old, old.tag(), digits, group_digits)),
)?; )?;
} }
ReturnSuccess::value(ret) Ok(ret)
} }
}) })
.to_action_stream()) .to_input_stream())
} }
// TODO If you're using the with-system-locale feature and you're on Windows, Clang 3.9 or higher is also required.
pub fn action( pub fn action(
input: &Value, input: &Value,
tag: impl Into<Tag>, tag: impl Into<Tag>,
@ -109,6 +118,13 @@ pub fn action(
match &input.value { match &input.value {
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::string(match prim { UntaggedValue::Primitive(prim) => Ok(UntaggedValue::string(match prim {
Primitive::Int(int) => { Primitive::Int(int) => {
if group_digits {
format_int(*int) // int.to_formatted_string(*locale)
} else {
int.to_string()
}
}
Primitive::BigInt(int) => {
if group_digits { if group_digits {
format_bigint(int) // int.to_formatted_string(*locale) format_bigint(int) // int.to_formatted_string(*locale)
} else { } else {
@ -121,27 +137,45 @@ pub fn action(
Primitive::Date(a_date) => a_date.format("%c").to_string(), Primitive::Date(a_date) => a_date.format("%c").to_string(),
Primitive::FilePath(a_filepath) => a_filepath.as_path().display().to_string(), Primitive::FilePath(a_filepath) => a_filepath.as_path().display().to_string(),
Primitive::Filesize(a_filesize) => { Primitive::Filesize(a_filesize) => {
let byte_string = InlineShape::format_bytes(a_filesize, None); let byte_string = InlineShape::format_bytes(*a_filesize, None);
byte_string.1 byte_string.1
} }
Primitive::Nothing => "nothing".to_string(),
_ => { _ => {
return Err(ShellError::unimplemented( return Err(ShellError::unimplemented(&format!(
"str from for non-numeric primitives", "into string for primitive: {:?}",
)) prim
)))
} }
}) })
.into_value(tag)), .into_value(tag)),
UntaggedValue::Row(_) => Err(ShellError::labeled_error( UntaggedValue::Row(_) => Err(ShellError::labeled_error(
"specify column to use 'str from'", "specify column to use 'into string'",
"found table", "found table",
input.tag.clone(), input.tag.clone(),
)), )),
_ => Err(ShellError::unimplemented( UntaggedValue::Table(_) => Err(ShellError::unimplemented("into string for table")),
"str from for non-primitive, non-table types", _ => Err(ShellError::unimplemented("into string for non-primitive")),
)),
} }
} }
fn format_int(int: i64) -> String {
format!("{}", int)
// TODO once platform-specific dependencies are stable (see Cargo.toml)
// #[cfg(windows)]
// {
// int.to_formatted_string(&Locale::en)
// }
// #[cfg(not(windows))]
// {
// match SystemLocale::default() {
// Ok(locale) => int.to_formatted_string(&locale),
// Err(_) => int.to_formatted_string(&Locale::en),
// }
// }
}
fn format_bigint(int: &BigInt) -> String { fn format_bigint(int: &BigInt) -> String {
format!("{}", int) format!("{}", int)

View File

@ -6,11 +6,6 @@ use nu_source::Tagged;
pub struct Command; pub struct Command;
#[derive(Deserialize)]
pub struct Arguments {
rows: Option<Tagged<usize>>,
}
impl WholeStreamCommand for Command { impl WholeStreamCommand for Command {
fn name(&self) -> &str { fn name(&self) -> &str {
"keep" "keep"
@ -28,7 +23,7 @@ impl WholeStreamCommand for Command {
"Keep the number of rows only." "Keep the number of rows only."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
keep(args) keep(args)
} }
@ -53,15 +48,17 @@ impl WholeStreamCommand for Command {
} }
} }
fn keep(args: CommandArgs) -> Result<ActionStream, ShellError> { fn keep(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rows }, input) = args.process()?; let args = args.evaluate_once()?;
let rows: Option<Tagged<usize>> = args.opt(0)?;
let rows_desired = if let Some(quantity) = rows { let rows_desired = if let Some(quantity) = rows {
*quantity *quantity
} else { } else {
1 1
}; };
Ok(input.take(rows_desired).to_action_stream()) Ok(args.input.take(rows_desired).to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,10 +1,12 @@
use crate::prelude::*; use crate::prelude::*;
use log::trace;
use nu_engine::evaluate_baseline_expr; use nu_engine::evaluate_baseline_expr;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_parser::ParserScope; use nu_parser::ParserScope;
use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{
hir::{CapturedBlock, ClassifiedCommand},
Signature, SyntaxShape,
};
pub struct SubCommand; pub struct SubCommand;
@ -27,54 +29,40 @@ impl WholeStreamCommand for SubCommand {
"Keeps rows until the condition matches." "Keeps rows until the condition matches."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = Arc::new(EvaluationContext::from_args(&args)); let ctx = Arc::new(EvaluationContext::from_args(&args));
let tag = args.call_info.name_tag.clone();
let call_info = args.evaluate_once()?; let call_info = args.evaluate_once()?;
let block = call_info.args.expect_nth(0)?.clone(); let block: CapturedBlock = call_info.req(0)?;
let condition = {
let (condition, captured) = match block { if block.block.block.len() != 1 {
Value { return Err(ShellError::labeled_error(
value: UntaggedValue::Block(captured_block), "Expected a condition",
tag, "expected a condition",
} => { tag,
if captured_block.block.block.len() != 1 { ));
return Err(ShellError::labeled_error( }
"Expected a condition", match block.block.block[0].pipelines.get(0) {
"expected a condition", Some(item) => match item.list.get(0) {
tag, Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
)); _ => {
}
match captured_block.block.block[0].pipelines.get(0) {
Some(item) => match item.list.get(0) {
Some(ClassifiedCommand::Expr(expr)) => {
(Arc::new(expr.clone()), captured_block.captured.clone())
}
_ => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
},
None => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a condition", "Expected a condition",
"expected a condition", "expected a condition",
tag, tag,
)); ));
} }
},
None => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
} }
} }
Value { tag, .. } => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
}; };
Ok(call_info Ok(call_info
@ -83,17 +71,17 @@ impl WholeStreamCommand for SubCommand {
let condition = condition.clone(); let condition = condition.clone();
let ctx = ctx.clone(); let ctx = ctx.clone();
ctx.scope.enter_scope(); ctx.scope.enter_scope();
ctx.scope.add_vars(&captured.entries); ctx.scope.add_vars(&block.captured.entries);
ctx.scope.add_var("$it", item.clone()); if let Some((arg, _)) = block.block.params.positional.first() {
trace!("ITEM = {:?}", item); ctx.scope.add_var(arg.name(), item.clone());
}
let result = evaluate_baseline_expr(&*condition, &*ctx); let result = evaluate_baseline_expr(&*condition, &*ctx);
ctx.scope.exit_scope(); ctx.scope.exit_scope();
trace!("RESULT = {:?}", result);
!matches!(result, Ok(ref v) if v.is_true()) !matches!(result, Ok(ref v) if v.is_true())
}) })
.to_action_stream()) .to_output_stream())
} }
} }

View File

@ -3,7 +3,10 @@ use log::trace;
use nu_engine::evaluate_baseline_expr; use nu_engine::evaluate_baseline_expr;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{
hir::{CapturedBlock, ClassifiedCommand},
Signature, SyntaxShape,
};
pub struct SubCommand; pub struct SubCommand;
@ -26,53 +29,39 @@ impl WholeStreamCommand for SubCommand {
"Keeps rows while the condition matches." "Keeps rows while the condition matches."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = Arc::new(EvaluationContext::from_args(&args)); let ctx = Arc::new(EvaluationContext::from_args(&args));
let tag = args.call_info.name_tag.clone();
let call_info = args.evaluate_once()?; let call_info = args.evaluate_once()?;
let block = call_info.args.expect_nth(0)?.clone(); let block: CapturedBlock = call_info.req(0)?;
let condition = {
let (condition, captured) = match block { if block.block.block.len() != 1 {
Value { return Err(ShellError::labeled_error(
value: UntaggedValue::Block(captured_block), "Expected a condition",
tag, "expected a condition",
} => { tag,
if captured_block.block.block.len() != 1 { ));
return Err(ShellError::labeled_error( }
"Expected a condition", match block.block.block[0].pipelines.get(0) {
"expected a condition", Some(item) => match item.list.get(0) {
tag, Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
)); _ => {
}
match captured_block.block.block[0].pipelines.get(0) {
Some(item) => match item.list.get(0) {
Some(ClassifiedCommand::Expr(expr)) => {
(Arc::new(expr.clone()), captured_block.captured.clone())
}
_ => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
},
None => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a condition", "Expected a condition",
"expected a condition", "expected a condition",
tag, tag,
)); ));
} }
},
None => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
} }
} }
Value { tag, .. } => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
}; };
Ok(call_info Ok(call_info
@ -82,8 +71,10 @@ impl WholeStreamCommand for SubCommand {
let ctx = ctx.clone(); let ctx = ctx.clone();
ctx.scope.enter_scope(); ctx.scope.enter_scope();
ctx.scope.add_var("$it", item.clone()); ctx.scope.add_vars(&block.captured.entries);
ctx.scope.add_vars(&captured.entries); if let Some((arg, _)) = block.block.params.positional.first() {
ctx.scope.add_var(arg.name(), item.clone());
}
trace!("ITEM = {:?}", item); trace!("ITEM = {:?}", item);
let result = evaluate_baseline_expr(&*condition, &*ctx); let result = evaluate_baseline_expr(&*condition, &*ctx);
@ -92,7 +83,7 @@ impl WholeStreamCommand for SubCommand {
matches!(result, Ok(ref v) if v.is_true()) matches!(result, Ok(ref v) if v.is_true())
}) })
.to_action_stream()) .to_output_stream())
} }
} }

View File

@ -1,16 +1,10 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged;
pub struct Last; pub struct Last;
#[derive(Deserialize)]
pub struct LastArgs {
rows: Option<Tagged<u64>>,
}
impl WholeStreamCommand for Last { impl WholeStreamCommand for Last {
fn name(&self) -> &str { fn name(&self) -> &str {
"last" "last"
@ -28,7 +22,7 @@ impl WholeStreamCommand for Last {
"Show only the last number of rows." "Show only the last number of rows."
} }
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
last(args) last(args)
} }
@ -37,7 +31,7 @@ impl WholeStreamCommand for Last {
Example { Example {
description: "Get the last row", description: "Get the last row",
example: "echo [1 2 3] | last", example: "echo [1 2 3] | last",
result: Some(vec![Value::from(UntaggedValue::from(BigInt::from(3)))]), result: Some(vec![UntaggedValue::int(3).into()]),
}, },
Example { Example {
description: "Get the last three rows", description: "Get the last three rows",
@ -52,12 +46,13 @@ impl WholeStreamCommand for Last {
} }
} }
fn last(args: CommandArgs) -> Result<ActionStream, ShellError> { fn last(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (LastArgs { rows }, input) = args.process()?; let args = args.evaluate_once()?;
let v: Vec<_> = input.into_vec(); let rows = args.nth(0).cloned();
let v: Vec<_> = args.input.into_vec();
let end_rows_desired = if let Some(quantity) = rows { let end_rows_desired = if let Some(quantity) = rows {
*quantity as usize quantity.as_usize()?
} else { } else {
1 1
}; };
@ -70,7 +65,7 @@ fn last(args: CommandArgs) -> Result<ActionStream, ShellError> {
let iter = v.into_iter().skip(beginning_rows_to_skip); let iter = v.into_iter().skip(beginning_rows_to_skip);
Ok((iter).to_action_stream()) Ok(OutputStream::from_stream(iter))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -94,7 +94,7 @@ impl Iterator for CountIterator {
input.count() input.count()
}; };
Some(UntaggedValue::int(length).into_value(self.tag.clone())) Some(UntaggedValue::int(length as i64).into_value(self.tag.clone()))
} }
} }

View File

@ -1,9 +1,11 @@
use crate::prelude::*; use crate::prelude::*;
use nu_engine::{evaluate_baseline_expr, WholeStreamCommand}; use nu_engine::{evaluate_baseline_expr, FromValue, WholeStreamCommand};
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{hir::CapturedBlock, hir::ClassifiedCommand, Signature, SyntaxShape}; use nu_protocol::{
use nu_source::Tagged; hir::{CapturedBlock, ClassifiedCommand},
Signature, SyntaxShape, UntaggedValue,
};
pub struct Let; pub struct Let;
@ -32,20 +34,41 @@ impl WholeStreamCommand for Let {
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![] vec![
Example {
description: "Assign a simple value to a variable",
example: "let x = 3",
result: Some(vec![]),
},
Example {
description: "Assign the result of an expression to a variable",
example: "let result = (3 + 7); echo $result",
result: Some(vec![UntaggedValue::int(1).into()]),
},
Example {
description: "Create a variable using the full name",
example: "let $three = 3",
result: Some(vec![]),
},
]
} }
} }
pub fn letcmd(args: CommandArgs) -> Result<ActionStream, ShellError> { pub fn letcmd(args: CommandArgs) -> Result<ActionStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args); let ctx = EvaluationContext::from_args(&args);
let args = args.evaluate_once()?; let positional = args
.call_info
.args
.positional
.expect("Internal error: type checker should require args");
//let (LetArgs { name, rhs, .. }, _) = args.process()?; let var_name = positional[0].var_name()?;
let name: Tagged<String> = args.req(0)?; let rhs_raw = evaluate_baseline_expr(&positional[2], &ctx)?;
let rhs: CapturedBlock = args.req(2)?; let tag: Tag = positional[2].span.into();
let (expr, captured) = { let rhs: CapturedBlock = FromValue::from_value(&rhs_raw)?;
let (expr, _) = {
if rhs.block.block.len() != 1 { if rhs.block.block.len() != 1 {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a value", "Expected a value",
@ -75,24 +98,15 @@ pub fn letcmd(args: CommandArgs) -> Result<ActionStream, ShellError> {
}; };
ctx.scope.enter_scope(); ctx.scope.enter_scope();
ctx.scope.add_vars(&captured.entries);
let value = evaluate_baseline_expr(&expr, &ctx); let value = evaluate_baseline_expr(&expr, &ctx);
ctx.scope.exit_scope(); ctx.scope.exit_scope();
let value = value?; let value = value?;
let name = if name.item.starts_with('$') {
name.item
} else {
format!("${}", name.item)
};
// Note: this is a special case for setting the context from a command // Note: this is a special case for setting the context from a command
// In this case, if we don't set it now, we'll lose the scope that this // In this case, if we don't set it now, we'll lose the scope that this
// variable should be set into. // variable should be set into.
ctx.scope.add_var(name, value); ctx.scope.add_var(var_name, value);
Ok(ActionStream::empty()) Ok(ActionStream::empty())
} }

View File

@ -0,0 +1,95 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape, Value};
pub struct LoadEnv;
impl WholeStreamCommand for LoadEnv {
fn name(&self) -> &str {
"load-env"
}
fn signature(&self) -> Signature {
Signature::build("load-env").optional(
"environ",
SyntaxShape::Any,
"Optional environment table to load in. If not provided, will use the table provided on the input stream",
)
}
fn usage(&self) -> &str {
"Set environment variables using a table stream"
}
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
load_env(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Load variables from an input stream",
example: r#"echo [[name, value]; ["NAME", "JT"] ["AGE", "UNKNOWN"]] | load-env; echo $nu.env.NAME"#,
result: Some(vec![Value::from("JT")]),
},
Example {
description: "Load variables from an argument",
example: r#"load-env [[name, value]; ["NAME", "JT"] ["AGE", "UNKNOWN"]]; echo $nu.env.NAME"#,
result: Some(vec![Value::from("JT")]),
},
Example {
description: "Load variables from an argument and an input stream",
example: r#"echo [[name, value]; ["NAME", "JT"]] | load-env [[name, value]; ["VALUE", "FOO"]]; echo $nu.env.NAME $nu.env.VALUE"#,
result: Some(vec![Value::from("JT"), Value::from("UNKNOWN")]),
},
]
}
}
fn load_env_from_table(
values: impl IntoIterator<Item = Value>,
ctx: &EvaluationContext,
) -> Result<(), ShellError> {
for value in values {
let mut var_name = None;
let mut var_value = None;
let tag = value.tag();
for (key, value) in value.row_entries() {
if key == "name" {
var_name = Some(value.as_string()?);
} else if key == "value" {
var_value = Some(value.as_string()?);
}
}
match (var_name, var_value) {
(Some(name), Some(value)) => ctx.scope.add_env_var(name, value),
_ => {
return Err(ShellError::labeled_error(
r#"Expected each row in the table to have a "name" and "value" field."#,
r#"expected a "name" and "value" field"#,
tag,
))
}
}
}
Ok(())
}
pub fn load_env(args: CommandArgs) -> Result<ActionStream, ShellError> {
let ctx = EvaluationContext::from_args(&args);
let args = args.evaluate_once()?;
if let Some(values) = args.opt::<Vec<Value>>(0)? {
load_env_from_table(values, &ctx)?;
}
load_env_from_table(args.input, &ctx)?;
Ok(ActionStream::empty())
}

View File

@ -41,8 +41,8 @@ impl WholeStreamCommand for Ls {
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> { fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let ctrl_c = args.ctrl_c.clone(); let ctrl_c = args.ctrl_c();
let shell_manager = args.shell_manager.clone(); let shell_manager = args.shell_manager();
let (args, _) = args.process()?; let (args, _) = args.process()?;
shell_manager.ls(args, name, ctrl_c) shell_manager.ls(args, name, ctrl_c)
} }

View File

@ -1,352 +1,351 @@
#[doc(hidden)] // #[doc(hidden)]
#[macro_export] // #[macro_export]
macro_rules! command { // macro_rules! command {
( // (
Named { $export:tt $args:ident $body:block } // Named { $export:tt $args:ident $body:block }
Positional { $($number:tt)* } // Positional { $($number:tt)* }
Rest {} // Rest {}
Signature { // Signature {
name: $config_name:tt, // name: $config_name:tt,
mandatory_positional: vec![ $($mandatory_positional:tt)* ], // mandatory_positional: vec![ $($mandatory_positional:tt)* ],
optional_positional: vec![ $($optional_positional:tt)* ], // optional_positional: vec![ $($optional_positional:tt)* ],
rest_positional: $rest_positional:tt, // rest_positional: $rest_positional:tt,
named: { // named: {
$( // $(
($named_param:tt : $named_type:ty : $named_kind:tt) // ($named_param:tt : $named_type:ty : $named_kind:tt)
)* // )*
} // }
} // }
Function { // Function {
$( ( $param_name:tt : $param_type:tt ) )* // $( ( $param_name:tt : $param_type:tt ) )*
} // }
Extract { // Extract {
$($extract:tt)* // $($extract:tt)*
} // }
) => { // ) => {
#[allow(non_camel_case_types)] // #[allow(non_camel_case_types)]
pub struct $export; // pub struct $export;
impl Command for $export { // impl Command for $export {
fn run(&self, $args: CommandArgs) -> Result<OutputStream, ShellError> { // fn run(&self, $args: CommandArgs) -> Result<OutputStream, ShellError> {
fn command($args: EvaluatedCommandArgs, ( $($param_name),*, ): ( $($param_type),*, )) -> Result<OutputStream, ShellError> { // fn command($args: EvaluatedCommandArgs, ( $($param_name),*, ): ( $($param_type),*, )) -> Result<OutputStream, ShellError> {
let output = $body; // let output = $body;
Ok(output.to_action_stream()) // Ok(output.to_action_stream())
} // }
let $args = $args.evaluate_once(registry)?; // let $args = $args.evaluate_once(registry)?;
let tuple = ( $($extract ,)* ); // let tuple = ( $($extract ,)* );
command( $args, tuple ) // command( $args, tuple )
} // }
fn name(&self) -> &str { // fn name(&self) -> &str {
stringify!($config_name) // stringify!($config_name)
} // }
fn config(&self) -> $nu_parser::registry::Signature { // fn config(&self) -> $nu_parser::registry::Signature {
$nu_parser::registry::Signature { // $nu_parser::registry::Signature {
name: self.name().to_string(), // name: self.name().to_string(),
positional: vec![$($mandatory_positional)*], // positional: vec![$($mandatory_positional)*],
rest_positional: false, // rest_positional: false,
is_filter: false, // is_filter: false,
is_sink: false, // is_sink: false,
named: { // named: {
use $nu_parser::registry::NamedType; // use $nu_parser::registry::NamedType;
#[allow(unused_mut)] // #[allow(unused_mut)]
let mut named: indexmap::IndexMap<String, NamedType> = indexmap::IndexMap::new(); // let mut named: indexmap::IndexMap<String, NamedType> = indexmap::IndexMap::new();
$( // $(
named.insert(stringify!($named_param).to_string(), $nu_parser::registry::NamedType::$named_kind); // named.insert(stringify!($named_param).to_string(), $nu_parser::registry::NamedType::$named_kind);
)* // )*
named // named
} // }
} // }
} // }
} // }
}; // };
// switch // // switch
( // (
Named { $export:tt $args:ident $body:block } // Named { $export:tt $args:ident $body:block }
Positional { $($positional_count:tt)* } // Positional { $($positional_count:tt)* }
Rest { -- $param_name:ident : Switch , $($rest:tt)* } // Rest { -- $param_name:ident : Switch , $($rest:tt)* }
Signature { // Signature {
name: $config_name:tt, // name: $config_name:tt,
mandatory_positional: vec![ $($mandatory_positional:tt)* ], // mandatory_positional: vec![ $($mandatory_positional:tt)* ],
optional_positional: vec![ $($optional_positional:tt)* ], // optional_positional: vec![ $($optional_positional:tt)* ],
rest_positional: $rest_positional:tt, // rest_positional: $rest_positional:tt,
named: { // named: {
$($config_named:tt)* // $($config_named:tt)*
} // }
} // }
Function { // Function {
$($function:tt)* // $($function:tt)*
} // }
Extract { // Extract {
$($extract:tt)* // $($extract:tt)*
} // }
) => { // ) => {
command!( // command!(
Named { $export $args $body } // Named { $export $args $body }
Positional { $($positional_count)* + 1 } // Positional { $($positional_count)* + 1 }
Rest { $($rest)* } // Rest { $($rest)* }
Signature { // Signature {
name: $config_name, // name: $config_name,
mandatory_positional: vec![ $($mandatory_positional)* ], // mandatory_positional: vec![ $($mandatory_positional)* ],
optional_positional: vec![ $($optional_positional)* ], // optional_positional: vec![ $($optional_positional)* ],
rest_positional: $rest_positional, // rest_positional: $rest_positional,
named: { // named: {
$($config_named)* // $($config_named)*
($param_name : Switch : Switch) // ($param_name : Switch : Switch)
} // }
} // }
Function { // Function {
$($function)* ($param_name : Switch) // $($function)* ($param_name : Switch)
} // }
Extract { // Extract {
$($extract)* { // $($extract)* {
use std::convert::TryInto; // use std::convert::TryInto;
$args.get(stringify!($param_name)).try_into()? // $args.get(stringify!($param_name)).try_into()?
} // }
} // }
); // );
}; // };
// mandatory named arguments // // mandatory named arguments
( // (
Named { $export:tt $args:ident $body:block } // Named { $export:tt $args:ident $body:block }
Positional { $($positional_count:tt)* } // Positional { $($positional_count:tt)* }
Rest { -- $param_name:ident : $param_kind:ty , $($rest:tt)* } // Rest { -- $param_name:ident : $param_kind:ty , $($rest:tt)* }
Signature { // Signature {
name: $config_name:tt, // name: $config_name:tt,
mandatory_positional: vec![ $($mandatory_positional:tt)* ], // mandatory_positional: vec![ $($mandatory_positional:tt)* ],
optional_positional: vec![ $($optional_positional:tt)* ], // optional_positional: vec![ $($optional_positional:tt)* ],
rest_positional: $rest_positional:tt, // rest_positional: $rest_positional:tt,
named: { // named: {
$($config_named:tt)* // $($config_named:tt)*
} // }
} // }
Function { // Function {
$($function:tt)* // $($function:tt)*
} // }
Extract { // Extract {
$($extract:tt)* // $($extract:tt)*
} // }
) => { // ) => {
command!( // command!(
Named { $export $args $body } // Named { $export $args $body }
Positional { $($positional_count)* + 1 } // Positional { $($positional_count)* + 1 }
Rest { $($rest)* } // Rest { $($rest)* }
Signature { // Signature {
name: $config_name, // name: $config_name,
mandatory_positional: vec![ $($mandatory_positional)* ], // mandatory_positional: vec![ $($mandatory_positional)* ],
optional_positional: vec![ $($optional_positional)* ], // optional_positional: vec![ $($optional_positional)* ],
rest_positional: $rest_positional, // rest_positional: $rest_positional,
named: { // named: {
$($config_named)* // $($config_named)*
($param_name : Mandatory(NamedValue::Single)) // ($param_name : Mandatory(NamedValue::Single))
} // }
} // }
Function { // Function {
$($function)* ($param_name : $param_kind) // $($function)* ($param_name : $param_kind)
} // }
Extract { // Extract {
$($extract)* { // $($extract)* {
use std::convert::TryInto; // use std::convert::TryInto;
$args.get(stringify!($param_name)).try_into()? // $args.get(stringify!($param_name)).try_into()?
} // }
} // }
); // );
}; // };
// optional named arguments // // optional named arguments
( // (
Named { $export:tt $args:ident $body:block } // Named { $export:tt $args:ident $body:block }
Positional { $($positional_count:tt)* } // Positional { $($positional_count:tt)* }
Rest { -- $param_name:ident ? : $param_kind:ty , $($rest:tt)* } // Rest { -- $param_name:ident ? : $param_kind:ty , $($rest:tt)* }
Signature { // Signature {
name: $config_name:tt, // name: $config_name:tt,
mandatory_positional: vec![ $($mandatory_positional:tt)* ], // mandatory_positional: vec![ $($mandatory_positional:tt)* ],
optional_positional: vec![ $($optional_positional:tt)* ], // optional_positional: vec![ $($optional_positional:tt)* ],
rest_positional: $rest_positional:tt, // rest_positional: $rest_positional:tt,
named: { // named: {
$($config_named:tt)* // $($config_named:tt)*
} // }
} // }
Function { // Function {
$($function:tt)* // $($function:tt)*
} // }
Extract { // Extract {
$($extract:tt)* // $($extract:tt)*
} // }
) => { // ) => {
command!( // command!(
Named { $export $args $body } // Named { $export $args $body }
Positional { $($positional_count)* + 1 } // Positional { $($positional_count)* + 1 }
Rest { $($rest)* } // Rest { $($rest)* }
Signature { // Signature {
name: $config_name, // name: $config_name,
mandatory_positional: vec![ $($mandatory_positional)* ], // mandatory_positional: vec![ $($mandatory_positional)* ],
optional_positional: vec![ $($optional_positional)* ], // optional_positional: vec![ $($optional_positional)* ],
rest_positional: $rest_positional, // rest_positional: $rest_positional,
named: { // named: {
$($config_named)* // $($config_named)*
($param_name : Optional(NamedValue::Single)) // ($param_name : Optional(NamedValue::Single))
} // }
} // }
Function { // Function {
$($function)* ($param_name : $param_kind) // $($function)* ($param_name : $param_kind)
} // }
Extract { // Extract {
$($extract)* { // $($extract)* {
use std::convert::TryInto; // use std::convert::TryInto;
$args.get(stringify!($param_name)).try_into()? // $args.get(stringify!($param_name)).try_into()?
} // }
} // }
); // );
}; // };
// mandatory positional block // // mandatory positional block
( // (
Named { $export:ident $args:ident $body:block } // Named { $export:ident $args:ident $body:block }
Positional { $($positional_count:tt)* } // Positional { $($positional_count:tt)* }
Rest { $param_name:ident : Block , $($rest:tt)* } // Rest { $param_name:ident : Block , $($rest:tt)* }
Signature { // Signature {
name: $config_name:tt, // name: $config_name:tt,
mandatory_positional: vec![ $($mandatory_positional:tt)* ], // mandatory_positional: vec![ $($mandatory_positional:tt)* ],
optional_positional: vec![ $($optional_positional:tt)* ], // optional_positional: vec![ $($optional_positional:tt)* ],
rest_positional: $rest_positional:tt, // rest_positional: $rest_positional:tt,
named: { // named: {
$($config_named:tt)* // $($config_named:tt)*
} // }
} // }
Function { // Function {
$($function:tt)* // $($function:tt)*
} // }
Extract { // Extract {
$($extract:tt)* // $($extract:tt)*
} // }
) => { // ) => {
command!( // command!(
Named { $export $args $body } // Named { $export $args $body }
Positional { $($positional_count)* + 1 } // Positional { $($positional_count)* + 1 }
Rest { $($rest)* } // Rest { $($rest)* }
Signature { // Signature {
name: $config_name, // name: $config_name,
mandatory_positional: vec![ $($mandatory_positional)* $nu_parser::registry::PositionalType::mandatory_block( // mandatory_positional: vec![ $($mandatory_positional)* $nu_parser::registry::PositionalType::mandatory_block(
stringify!($param_name) // stringify!($param_name)
), ], // ), ],
optional_positional: vec![ $($optional_positional)* ], // optional_positional: vec![ $($optional_positional)* ],
rest_positional: $rest_positional, // rest_positional: $rest_positional,
named: { // named: {
$($config_named)* // $($config_named)*
} // }
} // }
Function { // Function {
$($function)* ($param_name : Block) // $($function)* ($param_name : Block)
} // }
Extract { // Extract {
$($extract:tt)* { // $($extract:tt)* {
use $nu_data::types::ExtractType; // use $nu_data::types::ExtractType;
let value = $args.expect_nth($($positional_count)*)?; // let value = $args.expect_nth($($positional_count)*)?;
Block::extract(value)? // Block::extract(value)?
} // }
} // }
); // );
}; // };
// // mandatory positional argument
// (
// Named { $export:ident $args:ident $body:block }
// Positional { $($positional_count:tt)* }
// Rest { $param_name:ident : $param_kind:ty , $($rest:tt)* }
// Signature {
// name: $config_name:tt,
// mandatory_positional: vec![ $($mandatory_positional:tt)* ],
// optional_positional: vec![ $($optional_positional:tt)* ],
// rest_positional: $rest_positional:tt,
// named: {
// $($config_named:tt)*
// }
// }
// mandatory positional argument // Function {
( // $($function:tt)*
Named { $export:ident $args:ident $body:block } // }
Positional { $($positional_count:tt)* }
Rest { $param_name:ident : $param_kind:ty , $($rest:tt)* }
Signature {
name: $config_name:tt,
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
optional_positional: vec![ $($optional_positional:tt)* ],
rest_positional: $rest_positional:tt,
named: {
$($config_named:tt)*
}
}
Function { // Extract {
$($function:tt)* // $($extract:tt)*
} // }
Extract { // ) => {
$($extract:tt)* // command!(
} // Named { $export $args $body }
// Positional { $($positional_count)* + 1 }
// Rest { $($rest)* }
// Signature {
// name: $config_name,
// mandatory_positional: vec![ $($mandatory_positional)* $nu_parser::registry::PositionalType::mandatory(
// stringify!($param_name), <$param_kind>::syntax_type()
// ), ],
// optional_positional: vec![ $($optional_positional)* ],
// rest_positional: $rest_positional,
// named: {
// $($config_named)*
// }
// }
) => { // Function {
command!( // $($function)* ($param_name : $param_kind)
Named { $export $args $body } // }
Positional { $($positional_count)* + 1 }
Rest { $($rest)* }
Signature {
name: $config_name,
mandatory_positional: vec![ $($mandatory_positional)* $nu_parser::registry::PositionalType::mandatory(
stringify!($param_name), <$param_kind>::syntax_type()
), ],
optional_positional: vec![ $($optional_positional)* ],
rest_positional: $rest_positional,
named: {
$($config_named)*
}
}
Function { // Extract {
$($function)* ($param_name : $param_kind) // $($extract:tt)* {
} // use $nu_data::types::ExtractType;
// let value = $args.expect_nth($($positional_count)*)?;
// <$param_kind>::extract(&value)?
// }
// }
// );
// };
Extract { // ($export:ident as $config_name:tt ( $args:ident , $($command_rest:tt)* ) $body:block) => {
$($extract:tt)* { // command!(
use $nu_data::types::ExtractType; // Named { $export $args $body }
let value = $args.expect_nth($($positional_count)*)?; // Positional { 0 }
<$param_kind>::extract(&value)? // Rest { $($command_rest)* }
} // Signature {
} // name: $config_name,
); // mandatory_positional: vec![],
}; // optional_positional: vec![],
// rest_positional: false,
// named: {}
// }
($export:ident as $config_name:tt ( $args:ident , $($command_rest:tt)* ) $body:block) => { // Function {
command!( // }
Named { $export $args $body }
Positional { 0 }
Rest { $($command_rest)* }
Signature {
name: $config_name,
mandatory_positional: vec![],
optional_positional: vec![],
rest_positional: false,
named: {}
}
Function { // Extract {
} // }
// );
Extract { // };
} // }
);
};
}

View File

@ -20,8 +20,9 @@ impl WholeStreamCommand for SubCommand {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let mapped = args.input.map(move |val| match val.value { let mapped = args.input.map(move |val| match val.value {
UntaggedValue::Primitive(Primitive::Int(val)) => { UntaggedValue::Primitive(Primitive::Int(val)) => UntaggedValue::int(val.abs()).into(),
UntaggedValue::int(val.magnitude().clone()).into() UntaggedValue::Primitive(Primitive::BigInt(val)) => {
UntaggedValue::big_int(val.magnitude().clone()).into()
} }
UntaggedValue::Primitive(Primitive::Decimal(val)) => { UntaggedValue::Primitive(Primitive::Decimal(val)) => {
UntaggedValue::decimal(val.abs()).into() UntaggedValue::decimal(val.abs()).into()

View File

@ -5,10 +5,7 @@ use crate::commands::math::utils::run_with_function;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{hir::Operator, Primitive, Signature, UntaggedValue, Value};
hir::{convert_number_to_u64, Number, Operator},
Primitive, Signature, UntaggedValue, Value,
};
use bigdecimal::FromPrimitive; use bigdecimal::FromPrimitive;
@ -47,7 +44,7 @@ impl WholeStreamCommand for SubCommand {
fn to_byte(value: &Value) -> Option<Value> { fn to_byte(value: &Value) -> Option<Value> {
match &value.value { match &value.value {
UntaggedValue::Primitive(Primitive::Int(num)) => { UntaggedValue::Primitive(Primitive::Int(num)) => {
Some(UntaggedValue::Primitive(Primitive::Filesize(num.clone())).into_untagged_value()) Some(UntaggedValue::Primitive(Primitive::Filesize(*num as u64)).into_untagged_value())
} }
_ => None, _ => None,
} }
@ -79,7 +76,7 @@ pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
Value { Value {
value: UntaggedValue::Primitive(Primitive::Filesize(num)), value: UntaggedValue::Primitive(Primitive::Filesize(num)),
.. ..
} => UntaggedValue::int(num.clone()).into_untagged_value(), } => UntaggedValue::int(*num as i64).into_untagged_value(),
other => other.clone(), other => other.clone(),
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
@ -100,15 +97,18 @@ pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
value: UntaggedValue::Primitive(Primitive::Filesize(num)), value: UntaggedValue::Primitive(Primitive::Filesize(num)),
.. ..
} => { } => {
let left = UntaggedValue::from(Primitive::Int(num)); let left = UntaggedValue::from(Primitive::Int(num as i64));
let result = nu_data::value::compute_values(Operator::Divide, &left, &total_rows); let result = nu_data::value::compute_values(Operator::Divide, &left, &total_rows);
match result { match result {
Ok(UntaggedValue::Primitive(Primitive::Decimal(result))) => { Ok(UntaggedValue::Primitive(Primitive::Decimal(result))) => match result.to_u64() {
let number = Number::Decimal(result); Some(number) => Ok(UntaggedValue::filesize(number).into_value(name)),
let number = convert_number_to_u64(&number); None => Err(ShellError::labeled_error(
Ok(UntaggedValue::filesize(number).into_value(name)) "could not calculate average of non-integer or unrelated types",
} "source",
name,
)),
},
Ok(_) => Err(ShellError::labeled_error( Ok(_) => Err(ShellError::labeled_error(
"could not calculate average of non-integer or unrelated types", "could not calculate average of non-integer or unrelated types",
"source", "source",

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