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

View File

@ -1,6 +1,6 @@
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
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.
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:
@ -220,7 +220,7 @@ We can pipeline this into a command that gets the contents of one of the columns
name │ nu
readme │ README.md
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
> 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.

View File

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

View File

@ -5,26 +5,27 @@ description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-cli"
version = "0.30.0"
version = "0.32.0"
[lib]
doctest = false
[dependencies]
nu-command = { version = "0.30.0", path = "../nu-command" }
nu-data = { version = "0.30.0", path = "../nu-data" }
nu-engine = { version = "0.30.0", path = "../nu-engine" }
nu-errors = { version = "0.30.0", path = "../nu-errors" }
nu-json = { version = "0.30.0", path = "../nu-json" }
nu-parser = { version = "0.30.0", path = "../nu-parser" }
nu-plugin = { version = "0.30.0", path = "../nu-plugin" }
nu-protocol = { version = "0.30.0", path = "../nu-protocol" }
nu-source = { version = "0.30.0", path = "../nu-source" }
nu-stream = { version = "0.30.0", path = "../nu-stream" }
nu-table = { version = "0.30.0", path = "../nu-table" }
nu-test-support = { version = "0.30.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.30.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.30.0", path = "../nu-ansi-term" }
nu-command = { version = "0.32.0", path = "../nu-command" }
nu-data = { version = "0.32.0", path = "../nu-data" }
nu-engine = { version = "0.32.0", path = "../nu-engine" }
nu-errors = { version = "0.32.0", path = "../nu-errors" }
nu-json = { version = "0.32.0", path = "../nu-json" }
nu-parser = { version = "0.32.0", path = "../nu-parser" }
nu-plugin = { version = "0.32.0", path = "../nu-plugin" }
nu-protocol = { version = "0.32.0", path = "../nu-protocol" }
nu-source = { version = "0.32.0", path = "../nu-source" }
nu-stream = { version = "0.32.0", path = "../nu-stream" }
nu-table = { version = "0.32.0", path = "../nu-table" }
nu-test-support = { version = "0.32.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.32.0", path = "../nu-value-ext" }
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"
arboard = { version = "1.1.0", optional = true }
@ -57,7 +58,6 @@ getset = "0.1.1"
glob = "0.3.0"
htmlescape = "0.3.1"
ical = "0.7.0"
ichwh = { version = "0.3.4", optional = true }
indexmap = { version = "1.6.1", features = ["serde-1"] }
itertools = "0.10.0"
lazy_static = "1.*"
@ -68,7 +68,6 @@ num-format = { version = "0.4.0", features = ["with-num-bigint"] }
num-traits = "0.2.14"
parking_lot = "0.11.1"
pin-utils = "0.1.0"
pretty-hex = "0.2.1"
ptree = { version = "0.3.1", optional = true }
query_interface = "0.3.5"
quick-xml = "0.21.0"
@ -77,7 +76,7 @@ rayon = "1.5.0"
regex = "1.4.3"
roxmltree = "0.14.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_bytes = "0.11.5"
serde_ini = "0.2.0"
@ -116,7 +115,7 @@ users = "0.11.0"
[dependencies.rusqlite]
features = ["bundled", "blob"]
optional = true
version = "0.24.2"
version = "0.25.3"
[build-dependencies]
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()?;
context.scope.enter_scope();
let (mut 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);
}
let (prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
if err.is_some() {
context.scope.exit_scope();
@ -250,7 +244,12 @@ pub fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn E
nu_ansi_term::ansi::RESET
)
} 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();
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 readline = Err(ReadlineError::Eof);
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 result = run_block(&classified_block, ctx, input_stream);
let result = run_block(
&classified_block,
ctx,
input_stream,
ExternalRedirection::Stdout,
);
ctx.scope.exit_scope();
result?.collect_string(Tag::unknown()).map(|x| x.item)
@ -509,14 +515,14 @@ fn current_branch() -> String {
#[cfg(test)]
mod tests {
use nu_engine::basic_evaluation_context;
use nu_engine::EvaluationContext;
#[quickcheck]
fn quickcheck_parse(data: String) -> bool {
let (tokens, err) = nu_parser::lex(&data, 0);
let (lite_block, err2) = nu_parser::parse_block(tokens);
if err.is_none() && err2.is_none() {
let context = basic_evaluation_context().unwrap();
let context = EvaluationContext::basic();
let _ = nu_parser::classify_block(&lite_block, &context.scope);
}
true

View File

@ -6,6 +6,7 @@ use indexmap::set::IndexSet;
use super::matchers::Matcher;
use crate::completion::{Completer, CompletionContext, Suggestion};
use nu_engine::EvaluationContext;
use nu_test_support::NATIVE_PATH_ENV_VAR;
pub struct CommandCompleter;
@ -121,7 +122,7 @@ fn is_executable(path: &Path) -> bool {
// TODO cache these, but watch for changes to PATH
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 mut executables: IndexSet<String> = IndexSet::new();

View File

@ -37,7 +37,7 @@ impl<'s> Flatten<'s> {
)
.collect(),
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::Boolean(_)
@ -384,7 +384,7 @@ mod tests {
#[test]
fn completes_incomplete_nested_structure() {
let registry: VecRegistry = vec![Signature::build("sys")].into();
let line = "echo $(sy";
let line = "echo (sy";
assert_eq!(
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};
pub fn convert_keyevent(key_event: KeyEvent) -> rustyline::KeyEvent {
pub fn convert_keyevent(key_event: KeyCode, modifiers: Option<Modifiers>) -> rustyline::KeyEvent {
match key_event {
KeyEvent::UnknownEscSeq => convert_to_rl_keyevent(rustyline::KeyCode::UnknownEscSeq, None),
KeyEvent::Backspace => convert_to_rl_keyevent(rustyline::KeyCode::Backspace, None),
KeyEvent::BackTab => convert_to_rl_keyevent(rustyline::KeyCode::BackTab, None),
KeyEvent::BracketedPasteStart => {
convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteStart, None)
KeyCode::UnknownEscSeq => convert_to_rl_keyevent(RustyKeyCode::UnknownEscSeq, modifiers),
KeyCode::Backspace => convert_to_rl_keyevent(RustyKeyCode::Backspace, modifiers),
KeyCode::BackTab => convert_to_rl_keyevent(RustyKeyCode::BackTab, modifiers),
KeyCode::BracketedPasteStart => {
convert_to_rl_keyevent(RustyKeyCode::BracketedPasteStart, modifiers)
}
KeyEvent::BracketedPasteEnd => {
convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteEnd, None)
KeyCode::BracketedPasteEnd => {
convert_to_rl_keyevent(RustyKeyCode::BracketedPasteEnd, modifiers)
}
KeyEvent::Char(c) => convert_to_rl_keyevent(rustyline::KeyCode::Char(c), None),
KeyEvent::ControlDown => {
convert_to_rl_keyevent(rustyline::KeyCode::Down, Some(Modifiers::CTRL))
}
KeyEvent::ControlLeft => {
convert_to_rl_keyevent(rustyline::KeyCode::Left, Some(Modifiers::CTRL))
}
KeyEvent::ControlRight => {
convert_to_rl_keyevent(rustyline::KeyCode::Right, Some(Modifiers::CTRL))
}
KeyEvent::ControlUp => {
convert_to_rl_keyevent(rustyline::KeyCode::Up, Some(Modifiers::CTRL))
}
KeyEvent::Ctrl(c) => rustyline::KeyEvent::ctrl(c),
KeyEvent::Delete => convert_to_rl_keyevent(rustyline::KeyCode::Delete, None),
KeyEvent::Down => convert_to_rl_keyevent(rustyline::KeyCode::Down, None),
KeyEvent::End => convert_to_rl_keyevent(rustyline::KeyCode::End, None),
KeyEvent::Enter => convert_to_rl_keyevent(rustyline::KeyCode::Enter, None),
KeyEvent::Esc => convert_to_rl_keyevent(rustyline::KeyCode::Esc, None),
KeyEvent::F(u) => convert_to_rl_keyevent(rustyline::KeyCode::F(u), None),
KeyEvent::Home => convert_to_rl_keyevent(rustyline::KeyCode::Home, None),
KeyEvent::Insert => convert_to_rl_keyevent(rustyline::KeyCode::Insert, None),
KeyEvent::Left => convert_to_rl_keyevent(rustyline::KeyCode::Left, None),
KeyEvent::Meta(c) => rustyline::KeyEvent::new(c, Modifiers::NONE),
KeyEvent::Null => convert_to_rl_keyevent(rustyline::KeyCode::Null, None),
KeyEvent::PageDown => convert_to_rl_keyevent(rustyline::KeyCode::PageDown, None),
KeyEvent::PageUp => convert_to_rl_keyevent(rustyline::KeyCode::PageUp, None),
KeyEvent::Right => convert_to_rl_keyevent(rustyline::KeyCode::Right, None),
KeyEvent::ShiftDown => {
convert_to_rl_keyevent(rustyline::KeyCode::Down, Some(Modifiers::SHIFT))
}
KeyEvent::ShiftLeft => {
convert_to_rl_keyevent(rustyline::KeyCode::Left, Some(Modifiers::SHIFT))
}
KeyEvent::ShiftRight => {
convert_to_rl_keyevent(rustyline::KeyCode::Right, Some(Modifiers::SHIFT))
}
KeyEvent::ShiftUp => convert_to_rl_keyevent(rustyline::KeyCode::Up, Some(Modifiers::SHIFT)),
KeyEvent::Tab => convert_to_rl_keyevent(rustyline::KeyCode::Tab, None),
KeyEvent::Up => convert_to_rl_keyevent(rustyline::KeyCode::Up, None),
KeyCode::Char(c) => convert_to_rl_keyevent(RustyKeyCode::Char(c), modifiers),
KeyCode::Delete => convert_to_rl_keyevent(RustyKeyCode::Delete, modifiers),
KeyCode::Down => convert_to_rl_keyevent(RustyKeyCode::Down, modifiers),
KeyCode::End => convert_to_rl_keyevent(RustyKeyCode::End, modifiers),
KeyCode::Enter => convert_to_rl_keyevent(RustyKeyCode::Enter, modifiers),
KeyCode::Esc => convert_to_rl_keyevent(RustyKeyCode::Esc, modifiers),
KeyCode::F(u) => convert_to_rl_keyevent(RustyKeyCode::F(u), modifiers),
KeyCode::Home => convert_to_rl_keyevent(RustyKeyCode::Home, modifiers),
KeyCode::Insert => convert_to_rl_keyevent(RustyKeyCode::Insert, modifiers),
KeyCode::Left => convert_to_rl_keyevent(RustyKeyCode::Left, modifiers),
KeyCode::Null => convert_to_rl_keyevent(RustyKeyCode::Null, modifiers),
KeyCode::PageDown => convert_to_rl_keyevent(RustyKeyCode::PageDown, modifiers),
KeyCode::PageUp => convert_to_rl_keyevent(RustyKeyCode::PageUp, modifiers),
KeyCode::Right => convert_to_rl_keyevent(RustyKeyCode::Right, modifiers),
KeyCode::Tab => convert_to_rl_keyevent(RustyKeyCode::Tab, modifiers),
KeyCode::Up => convert_to_rl_keyevent(RustyKeyCode::Up, modifiers),
}
}
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 {
0: key_event,
0: key_code,
1: modifier.unwrap_or(Modifiers::NONE),
}
}
@ -132,12 +111,14 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
Cmd::Complete => rustyline::Cmd::Complete,
Cmd::CompleteBackward => rustyline::Cmd::CompleteBackward,
Cmd::CompleteHint => rustyline::Cmd::CompleteHint,
Cmd::Dedent(movement) => rustyline::Cmd::Dedent(convert_movement(movement)),
Cmd::DowncaseWord => rustyline::Cmd::DowncaseWord,
Cmd::EndOfFile => rustyline::Cmd::EndOfFile,
Cmd::EndOfHistory => rustyline::Cmd::EndOfHistory,
Cmd::ForwardSearchHistory => rustyline::Cmd::ForwardSearchHistory,
Cmd::HistorySearchBackward => rustyline::Cmd::HistorySearchBackward,
Cmd::HistorySearchForward => rustyline::Cmd::HistorySearchForward,
Cmd::Indent(movement) => rustyline::Cmd::Indent(convert_movement(movement)),
Cmd::Insert { repeat, string } => rustyline::Cmd::Insert(repeat, string),
Cmd::Interrupt => rustyline::Cmd::Interrupt,
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::Move(movement) => rustyline::Cmd::Move(convert_movement(movement)),
Cmd::NextHistory => rustyline::Cmd::NextHistory,
Cmd::Newline => rustyline::Cmd::Newline,
Cmd::Noop => rustyline::Cmd::Noop,
Cmd::Overwrite(c) => rustyline::Cmd::Overwrite(c),
#[cfg(windows)]
Cmd::PasteFromClipboard => rustyline::Cmd::PasteFromClipboard,
Cmd::PreviousHistory => rustyline::Cmd::PreviousHistory,
Cmd::QuotedInsert => rustyline::Cmd::QuotedInsert,
Cmd::Replace {
@ -169,14 +153,28 @@ fn convert_cmd(cmd: Cmd) -> 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),
)
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum KeyEvent {
pub enum KeyCode {
/// Unsupported escape sequence (on unix platform)
UnknownEscSeq,
/// ⌫ or `KeyEvent::Ctrl('H')`
@ -189,16 +187,6 @@ pub enum KeyEvent {
BracketedPasteEnd,
/// Single char
Char(char),
/// Ctrl-↓
ControlDown,
/// Ctrl-←
ControlLeft,
/// Ctrl-→
ControlRight,
/// Ctrl-↑
ControlUp,
/// Ctrl-char
Ctrl(char),
/// ⌦
Delete,
/// ↓ arrow key
@ -217,9 +205,7 @@ pub enum KeyEvent {
Insert,
/// ← arrow key
Left,
/// Escape-char or Alt-char
Meta(char),
/// `KeyEvent::Char('\0')`
// /// `KeyEvent::Char('\0')`
Null,
/// ⇟
PageDown,
@ -227,14 +213,6 @@ pub enum KeyEvent {
PageUp,
/// → arrow key
Right,
/// Shift-↓
ShiftDown,
/// Shift-←
ShiftLeft,
/// Shift-→
ShiftRight,
/// Shift-↑
ShiftUp,
/// ⇥ or `KeyEvent::Ctrl('I')`
Tab,
/// ↑ arrow key
@ -259,6 +237,8 @@ pub enum Cmd {
CompleteBackward,
/// complete-hint
CompleteHint,
/// Dedent current line
Dedent(Movement),
/// downcase-word
DowncaseWord,
/// vi-eof-maybe
@ -271,6 +251,8 @@ pub enum Cmd {
HistorySearchBackward,
/// history-search-forward
HistorySearchForward,
/// Indent current line
Indent(Movement),
/// Insert text
Insert { repeat: RepeatCount, string: String },
/// Interrupt signal (Ctrl-C)
@ -283,12 +265,17 @@ pub enum Cmd {
/// forward-char, forward-word, vi-char-search, vi-end-word, vi-next-word,
/// vi-prev-word
Move(Movement),
/// Inserts a newline
Newline,
/// next-history
NextHistory,
/// No action
Noop,
/// vi-replace
Overwrite(char),
/// Paste from the clipboard
#[cfg(windows)]
PasteFromClipboard,
/// previous-history
PreviousHistory,
/// quoted-insert
@ -422,12 +409,36 @@ pub enum CharSearch {
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.
pub type RepeatCount = usize;
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize)]
pub struct Keybinding {
key: KeyEvent,
key: KeyCode,
modifiers: Option<NuModifiers>,
binding: Cmd,
}
@ -442,7 +453,7 @@ pub(crate) fn load_keybindings(
// Silently fail if there is no file there
if let Ok(contents) = contents {
let keybindings: Keybindings = serde_yaml::from_str(&contents)?;
// eprint!("{}{}{}", keybindings.key, keybindings.mo);
for keybinding in keybindings.into_iter() {
let (k, b) = convert_keybinding(keybinding);

View File

@ -8,7 +8,7 @@ use crate::prelude::*;
use nu_engine::script::LineResult;
#[cfg(feature = "rustyline-support")]
use crate::keybinding::{convert_keyevent, KeyEvent};
use crate::keybinding::{convert_keyevent, KeyCode};
#[cfg(feature = "rustyline-support")]
use crate::shell::Helper;
@ -19,7 +19,8 @@ use rustyline::{
config::Configurer,
config::{ColorMode, CompletionType, Config},
error::ReadlineError,
At, Cmd, Editor, Movement, Word,
line_buffer::LineBuffer,
At, Cmd, ConditionalEventHandler, Editor, EventHandler, Modifiers, Movement, Word,
};
#[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")]
pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
#[cfg(windows)]
@ -50,18 +79,20 @@ pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
let mut rl: Editor<_> = Editor::with_config(config);
// add key bindings to move over a whole word with Ctrl+ArrowLeft and Ctrl+ArrowRight
//M modifier, E KeyEvent, K KeyCode
rl.bind_sequence(
convert_keyevent(KeyEvent::ControlLeft),
convert_keyevent(KeyCode::Left, Some(Modifiers::CTRL)),
Cmd::Move(Movement::BackwardWord(1, Word::Vi)),
);
rl.bind_sequence(
convert_keyevent(KeyEvent::ControlRight),
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
convert_keyevent(KeyCode::Right, Some(Modifiers::CTRL)),
EventHandler::Conditional(Box::new(PartialCompleteHintHandler)),
);
// workaround for multiline-paste hang in rustyline (see https://github.com/kkawakam/rustyline/issues/202)
rl.bind_sequence(
convert_keyevent(KeyEvent::BracketedPasteStart),
convert_keyevent(KeyCode::BracketedPasteStart, None),
rustyline::Cmd::Noop,
);
// 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: &dyn Matcher = match 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,
};
@ -134,7 +138,7 @@ fn requote(orig_value: String) -> String {
let mut quotes = vec!['"', '\'', '`'];
let mut should_quote = false;
for c in value.chars() {
if c.is_whitespace() {
if c.is_whitespace() || c == '#' {
should_quote = true;
} else if let Some(index) = quotes.iter().position(|q| *q == c) {
should_quote = true;
@ -145,7 +149,7 @@ fn requote(orig_value: String) -> String {
if should_quote {
if quotes.is_empty() {
// 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()
} else {
let quote = quotes[0];

View File

@ -150,7 +150,7 @@ impl rustyline::Helper for Helper {}
#[cfg(test)]
mod tests {
use super::*;
use nu_engine::basic_evaluation_context;
use nu_engine::EvaluationContext;
use rustyline::completion::Completer;
use rustyline::line_buffer::LineBuffer;
@ -164,7 +164,7 @@ mod tests {
buffer.insert_str(0, text);
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);
@ -184,7 +184,7 @@ mod tests {
buffer.insert_str(0, text);
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);

View File

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

View File

@ -5,25 +5,26 @@ description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-command"
version = "0.30.0"
version = "0.32.0"
[lib]
doctest = false
[dependencies]
nu-data = { version = "0.30.0", path = "../nu-data" }
nu-engine = { version = "0.30.0", path = "../nu-engine" }
nu-errors = { version = "0.30.0", path = "../nu-errors" }
nu-json = { version = "0.30.0", path = "../nu-json" }
nu-parser = { version = "0.30.0", path = "../nu-parser" }
nu-plugin = { version = "0.30.0", path = "../nu-plugin" }
nu-protocol = { version = "0.30.0", path = "../nu-protocol" }
nu-source = { version = "0.30.0", path = "../nu-source" }
nu-stream = { version = "0.30.0", path = "../nu-stream" }
nu-table = { version = "0.30.0", path = "../nu-table" }
nu-test-support = { version = "0.30.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.30.0", path = "../nu-value-ext" }
nu-ansi-term = { version = "0.30.0", path = "../nu-ansi-term" }
nu-data = { version = "0.32.0", path = "../nu-data" }
nu-engine = { version = "0.32.0", path = "../nu-engine" }
nu-errors = { version = "0.32.0", path = "../nu-errors" }
nu-json = { version = "0.32.0", path = "../nu-json" }
nu-parser = { version = "0.32.0", path = "../nu-parser" }
nu-plugin = { version = "0.32.0", path = "../nu-plugin" }
nu-protocol = { version = "0.32.0", path = "../nu-protocol" }
nu-source = { version = "0.32.0", path = "../nu-source" }
nu-stream = { version = "0.32.0", path = "../nu-stream" }
nu-table = { version = "0.32.0", path = "../nu-table" }
nu-test-support = { version = "0.32.0", path = "../nu-test-support" }
nu-value-ext = { version = "0.32.0", path = "../nu-value-ext" }
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"
arboard = { version = "1.1.0", optional = true }
@ -53,7 +54,6 @@ getset = "0.1.1"
glob = "0.3.0"
htmlescape = "0.3.1"
ical = "0.7.0"
ichwh = { version = "0.3.4", optional = true }
indexmap = { version = "1.6.1", features = ["serde-1"] }
itertools = "0.10.0"
lazy_static = "1.*"
@ -66,7 +66,6 @@ num-format = { version = "0.4.0", features = ["with-num-bigint"] }
num-traits = "0.2.14"
parking_lot = "0.11.1"
pin-utils = "0.1.0"
pretty-hex = "0.2.1"
ptree = { version = "0.3.1", optional = true }
query_interface = "0.3.5"
quick-xml = "0.21.0"
@ -75,7 +74,7 @@ rayon = "1.5.0"
regex = "1.4.3"
roxmltree = "0.14.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_bytes = "0.11.5"
serde_ini = "0.2.0"
@ -97,9 +96,14 @@ trash = { version = "1.3.0", optional = true }
unicode-segmentation = "1.7.1"
url = "2.2.0"
uuid_crate = { package = "uuid", version = "0.8.2", features = ["v4"], optional = true }
which = { version = "4.0.2", optional = true }
which = { version = "4.1.0", optional = true }
zip = { version = "0.5.9", optional = true }
[dependencies.polars]
version = "0.13.4"
optional = true
features = ["parquet", "json", "random"]
[target.'cfg(unix)'.dependencies]
umask = "1.0.0"
users = "0.11.0"
@ -113,7 +117,7 @@ users = "0.11.0"
[dependencies.rusqlite]
features = ["bundled", "blob"]
optional = true
version = "0.24.2"
version = "0.25.3"
[build-dependencies]
shadow-rs = "0.5"
@ -131,3 +135,4 @@ trash-support = ["trash"]
directories = ["directories-next"]
dirs = ["dirs-next"]
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 constants;
pub(crate) mod cp;
#[cfg(feature = "dataframe")]
pub(crate) mod dataframe;
pub(crate) mod date;
pub(crate) mod debug;
pub(crate) mod def;
@ -44,6 +46,7 @@ pub(crate) mod exec;
pub(crate) mod exit;
pub(crate) mod first;
pub(crate) mod flatten;
pub(crate) mod for_in;
pub(crate) mod format;
pub(crate) mod from;
pub(crate) mod from_csv;
@ -77,6 +80,7 @@ pub(crate) mod length;
pub(crate) mod let_;
pub(crate) mod let_env;
pub(crate) mod lines;
pub(crate) mod load_env;
pub(crate) mod ls;
pub(crate) mod math;
pub(crate) mod merge;
@ -170,9 +174,12 @@ pub(crate) use each::EachGroup;
pub(crate) use each::EachWindow;
pub(crate) use echo::Echo;
pub(crate) use empty::Command as Empty;
pub(crate) use for_in::ForIn;
pub(crate) use if_::If;
pub(crate) use into::Into;
pub(crate) use into::IntoBinary;
pub(crate) use into::IntoInt;
pub(crate) use into::IntoString;
pub(crate) use nu::NuPlugin;
pub(crate) use update::Command as Update;
pub(crate) mod kill;
@ -182,6 +189,12 @@ pub(crate) use clear::Clear;
pub(crate) mod touch;
pub(crate) use all::Command as All;
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 every::Every;
pub(crate) use exec::Exec;
@ -220,6 +233,7 @@ pub(crate) use length::Length;
pub(crate) use let_::Let;
pub(crate) use let_env::LetEnv;
pub(crate) use lines::Lines;
pub(crate) use load_env::LoadEnv;
pub(crate) use ls::Ls;
pub(crate) use math::{
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 parse::Parse;
pub(crate) use path::{
PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathJoin, PathParse, PathSplit,
PathType,
PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathJoin, PathParse,
PathRelativeTo, PathSplit, PathType,
};
pub(crate) use pivot::Pivot;
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 str_::{
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,
StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
};
@ -321,7 +335,6 @@ mod tests {
whole_stream_command(StrUpcase),
whole_stream_command(StrCapitalize),
whole_stream_command(StrFindReplace),
whole_stream_command(StrFrom),
whole_stream_command(StrSubstring),
whole_stream_command(StrToDatetime),
whole_stream_command(StrContains),
@ -354,6 +367,7 @@ mod tests {
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
for cmd in only_examples() {
println!("cmd: {}", cmd.name());
test_examples(cmd)?;
}

View File

@ -44,7 +44,7 @@ impl WholeStreamCommand for Command {
},
Example {
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)]),
},
]
@ -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(
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
// invocations are independent of each other!
scope.enter_scope();

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use nu_ansi_term::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct Command;
@ -69,7 +69,7 @@ following values:
https://en.wikipedia.org/wiki/ANSI_escape_code
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: #
0 Set window title and icon name
1 Set icon name
@ -96,7 +96,7 @@ Format: #
Example {
description:
"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(
"\u{1b}[1;31mHello \u{1b}[1;32mNu \u{1b}[1;35mWorld",
)]),
@ -104,7 +104,7 @@ Format: #
Example {
description:
"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(
"\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 code: Option<Tagged<String>> = args.opt(0)?;
@ -129,9 +129,9 @@ Format: #
));
}
let output = format!("\x1b[{}", e.item);
return Ok(ActionStream::one(ReturnSuccess::value(
return Ok(OutputStream::one(
UntaggedValue::string(output).into_value(e.tag()),
)));
));
}
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
// OCS's need to end with a bell '\x07' char
let output = format!("\x1b]{};", o.item);
return Ok(ActionStream::one(ReturnSuccess::value(
return Ok(OutputStream::one(
UntaggedValue::string(output).into_value(o.tag()),
)));
));
}
if let Some(code) = code {
let ansi_code = str_to_ansi(&code.item);
if let Some(output) = ansi_code {
Ok(ActionStream::one(ReturnSuccess::value(
Ok(OutputStream::one(
UntaggedValue::string(output).into_value(code.tag()),
)))
))
} else {
Err(ShellError::labeled_error(
"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
// 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
// Ansi Erase Sequences

View File

@ -2,19 +2,12 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::ShellTypeName;
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tag;
use strip_ansi_escapes::strip;
pub struct SubCommand;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
}
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"ansi strip"
@ -31,27 +24,29 @@ impl WholeStreamCommand for SubCommand {
"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)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
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,
}]
}
}
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (Arguments { rest }, input) = args.process()?;
let column_paths: Vec<_> = rest;
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
Ok(input
let column_paths: Vec<_> = args.rest(0)?;
let result: Vec<Value> = args
.input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?)
action(&v, v.tag())
} else {
let mut ret = v;
@ -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> {

View File

@ -8,9 +8,8 @@ use nu_protocol::{
pub struct Command;
#[derive(Deserialize)]
pub struct Arguments {
block: CapturedBlock,
struct AnyArgs {
predicate: CapturedBlock,
}
impl WholeStreamCommand for Command {
@ -30,7 +29,7 @@ impl WholeStreamCommand for Command {
"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)
}
@ -45,79 +44,85 @@ impl WholeStreamCommand for Command {
},
Example {
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)]),
},
]
}
}
fn any(args: CommandArgs) -> Result<ActionStream, ShellError> {
let ctx = Arc::new(EvaluationContext::from_args(&args));
fn any(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = EvaluationContext::from_args(&args);
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 = {
if block.block.block.len() != 1 {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
if any_args.predicate.block.block.len() != 1 {
return err;
}
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(ClassifiedCommand::Expr(expr)) => expr.clone(),
_ => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
return err;
}
},
None => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
return err;
}
}
};
let cond = Ok(InputStream::one(
let scope = args.scope();
let init = Ok(InputStream::one(
UntaggedValue::boolean(false).into_value(&tag),
));
Ok(input
.fold(cond, move |cond, row| {
let condition = condition.clone();
let ctx = ctx.clone();
ctx.scope.enter_scope();
ctx.scope.add_vars(&block.captured.entries);
ctx.scope.add_var("$it", row);
// Variables in nu are immutable. Having the same variable accross invocations
// of evaluate_baseline_expr does not mutate the variables and thus each
// invocations are independent of each other!
scope.enter_scope();
scope.add_vars(&any_args.predicate.captured.entries);
let condition = evaluate_baseline_expr(&condition, &*ctx);
ctx.scope.exit_scope();
let result = args.input.fold(init, move |acc, row| {
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 curr = curr
.get(0)
.ok_or_else(|| ShellError::unexpected("No value to check with"))?;
let cond = curr.as_bool()?;
let condition = evaluate_baseline_expr(&condition, &ctx);
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),
},
let curr = acc?.drain_vec();
let curr = curr
.get(0)
.ok_or_else(|| ShellError::unexpected("No value to check with"))?;
let cond = curr.as_bool()?;
match condition {
Ok(condition) => match condition.as_bool() {
Ok(b) => Ok(InputStream::one(
UntaggedValue::boolean(cond || b).into_value(&curr.tag),
)),
Err(e) => Err(e),
}
})?
.to_action_stream())
},
Err(e) => Err(e),
}
});
scope.exit_scope();
Ok(result?.to_output_stream())
}
#[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> {
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_table::TextStyle;
#[cfg(feature = "dataframe")]
use nu_protocol::dataframe::PolarsData;
pub struct Command;
impl WholeStreamCommand for Command {
@ -43,14 +46,15 @@ impl WholeStreamCommand for Command {
}
}
pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
let configuration = context.configs.lock().global_config();
pub fn autoview(args: CommandArgs) -> Result<OutputStream, ShellError> {
let configuration = args.configs().lock().global_config();
let tag = args.call_info.name_tag.clone();
let binary = context.scope.get_command("binaryview");
let text = context.scope.get_command("textview");
let table = context.scope.get_command("table");
let (mut input_stream, context) = context.split();
let binary = args.scope().get_command("binaryview");
let text = args.scope().get_command("textview");
let table = args.scope().get_command("table");
let context = args.context;
let mut input_stream = args.input;
if let Some(x) = 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);
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.collect::<Vec<_>>();
}
@ -74,12 +78,13 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
tag: Tag { anchor, span },
} if anchor.is_some() => {
if let Some(text) = text {
let mut stream = VecDeque::new();
stream.push_back(
UntaggedValue::string(s).into_value(Tag { anchor, span }),
let command_args = create_default_command_args(
&context,
InputStream::one(
UntaggedValue::string(s).into_value(Tag { anchor, span }),
),
tag,
);
let command_args =
create_default_command_args(&context).with_input(stream);
let result = text.run_with_actions(command_args)?;
let _ = result.collect::<Vec<_>>();
} else {
@ -104,6 +109,12 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
} => {
out!("{}", n);
}
Value {
value: UntaggedValue::Primitive(Primitive::BigInt(n)),
..
} => {
out!("{}", n);
}
Value {
value: UntaggedValue::Primitive(Primitive::Decimal(n)),
..
@ -158,14 +169,12 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
..
} => {
if let Some(binary) = binary {
let mut stream = VecDeque::new();
stream.push_back(x);
let command_args =
create_default_command_args(&context).with_input(stream);
create_default_command_args(&context, InputStream::one(x), tag);
let result = binary.run_with_actions(command_args)?;
let _ = result.collect::<Vec<_>>();
} else {
use pretty_hex::*;
use nu_pretty_hex::*;
out!("{:?}", b.hex_dump());
}
}
@ -220,16 +229,42 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
println!("{}", nu_table::draw_table(&table, term_width, &color_hm));
} else if let Some(table) = table {
let mut stream = VecDeque::new();
stream.push_back(x);
let command_args =
create_default_command_args(&context).with_input(stream);
create_default_command_args(&context, InputStream::one(x), tag);
let result = table.run(command_args)?;
let _ = result.collect::<Vec<_>>();
} else {
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: UntaggedValue::Primitive(Primitive::Nothing),
..
@ -240,10 +275,8 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
value: ref item, ..
} => {
if let Some(table) = table {
let mut stream = VecDeque::new();
stream.push_back(x);
let command_args =
create_default_command_args(&context).with_input(stream);
create_default_command_args(&context, InputStream::one(x), tag);
let result = table.run(command_args)?;
let _ = result.collect::<Vec<_>>();
} else {
@ -258,14 +291,14 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(InputStream::empty())
}
fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawCommandArgs {
let span = context.name.span;
RawCommandArgs {
host: context.host.clone(),
ctrl_c: context.ctrl_c.clone(),
configs: context.configs.clone(),
current_errors: context.current_errors.clone(),
shell_manager: context.shell_manager.clone(),
fn create_default_command_args(
context: &EvaluationContext,
input: InputStream,
tag: Tag,
) -> CommandArgs {
let span = tag.span;
CommandArgs {
context: context.clone(),
call_info: UnevaluatedCallInfo {
args: hir::Call {
head: Box::new(SpannedExpression::new(
@ -277,9 +310,9 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
span,
external_redirection: ExternalRedirection::Stdout,
},
name_tag: context.name.clone(),
name_tag: tag,
},
scope: Scope::new(),
input,
}
}

View File

@ -5,7 +5,10 @@ use nu_engine::run_block;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
hir::{Block, CapturedBlock, ClassifiedCommand, Group, InternalCommand, Pipeline},
hir::{
Block, CapturedBlock, ClassifiedCommand, ExternalRedirection, Group, InternalCommand,
Pipeline,
},
Dictionary, Signature, SyntaxShape, UntaggedValue, Value,
};
use rand::{
@ -47,7 +50,7 @@ impl WholeStreamCommand for Benchmark {
"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)
}
@ -67,11 +70,16 @@ impl WholeStreamCommand for Benchmark {
}
}
fn benchmark(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
let tag = raw_args.call_info.args.span;
let mut context = EvaluationContext::from_args(&raw_args);
let scope = raw_args.scope.clone();
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process()?;
fn benchmark(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.args.span;
let mut context = EvaluationContext::from_args(&args);
let scope = args.scope().clone();
let args = args.evaluate_once()?;
let cmd_args = BenchmarkArgs {
block: args.req(0)?,
passthrough: args.get_flag("passthrough")?,
};
let env = scope.get_env_vars();
let name = generate_free_name(&env);
@ -84,7 +92,13 @@ fn benchmark(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
// let start = time();
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();
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);
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
// #[cfg(feature = "rich-benchmark")]
@ -134,10 +148,10 @@ fn benchmark_output<T, Output>(
passthrough: Option<CapturedBlock>,
tag: T,
context: &mut EvaluationContext,
) -> Result<ActionStream, ShellError>
) -> Result<OutputStream, ShellError>
where
T: Into<Tag> + Copy,
Output: Into<ActionStream>,
Output: Into<OutputStream>,
{
let value = UntaggedValue::Row(Dictionary::from(
indexmap
@ -154,14 +168,19 @@ where
let time_block = add_implicit_autoview(time_block.block);
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();
result?;
context.clear_errors();
Ok(block_output.into())
} else {
let benchmark_output = ActionStream::one(value);
let benchmark_output = OutputStream::one(value);
Ok(benchmark_output)
}
}

View File

@ -3,7 +3,7 @@ use nu_errors::ShellError;
use nu_data::value::format_leaf;
use nu_engine::WholeStreamCommand;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
pub struct BuildString;
@ -21,7 +21,7 @@ impl WholeStreamCommand for BuildString {
"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 args = args.evaluate_once()?;
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))
}
Ok(ActionStream::one(ReturnSuccess::value(
Ok(OutputStream::one(
UntaggedValue::string(output_string).into_value(tag),
)))
))
}
fn examples(&self) -> Vec<Example> {

View File

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

View File

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

View File

@ -1,11 +1,108 @@
use crate::prelude::*;
use nu_engine::{FromValue, WholeStreamCommand};
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 indexmap::indexmap;
use indexmap::map::IndexMap;
use lazy_static::lazy_static;
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 {
fn name(&self) -> &str {
"char"
@ -13,17 +110,18 @@ impl WholeStreamCommand for Char {
fn signature(&self) -> Signature {
Signature::build("char")
.required(
.optional(
"character",
SyntaxShape::Any,
"the name of the character to output",
)
.rest(SyntaxShape::String, "multiple Unicode bytes")
.switch("list", "List all supported character names", Some('l'))
.switch("unicode", "Unicode string i.e. 1f378", Some('u'))
}
fn usage(&self) -> &str {
"Output special characters (eg. 'newline')."
"Output special characters (e.g., 'newline')."
}
fn examples(&self) -> Vec<Example> {
@ -35,7 +133,7 @@ impl WholeStreamCommand for Char {
},
Example {
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![
UntaggedValue::string("\u{25b6}").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 = 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)?;
let rest: Vec<Value> = args.rest(1)?;
let unicode = args.has_flag("unicode");
if unicode {
if !rest.is_empty() {
// Setup a new buffer to put all the Unicode bytes in
let mut multi_byte = String::new();
// Get the first byte
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
match decoded_char {
Ok(ch) => multi_byte.push(ch),
Err(e) => return Err(e),
}
// Get the rest of the bytes
for byte_part in rest {
let byte_part: Tagged<String> = FromValue::from_value(&byte_part)?;
let decoded_char = string_to_unicode_char(&byte_part, &byte_part.tag);
if args.list {
Ok(CHAR_MAP
.iter()
.map(move |(name, s)| {
let mut dict = TaggedDictBuilder::with_capacity(&args_tag, 2);
dict.insert_untagged("name", UntaggedValue::string(*name));
dict.insert_untagged("character", UntaggedValue::string(s));
let unicode_parts: Vec<String> =
s.chars().map(|c| format!("{:x}", c as u32)).collect();
dict.insert_untagged("unicode", UntaggedValue::string(unicode_parts.join(" ")));
dict.into_value()
})
.to_output_stream())
} else if let Some(name) = args.name {
if args.unicode {
if !args.rest.is_empty() {
// Setup a new buffer to put all the Unicode bytes in
let mut multi_byte = String::new();
// Get the first byte
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
match decoded_char {
Ok(ch) => multi_byte.push(ch),
Err(e) => return Err(e),
}
// Get the rest of the bytes
for byte_part in 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 {
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
if let Ok(ch) = decoded_char {
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(ch).into_value(name.tag()),
)))
let special_character = str_to_character(&name.item);
if let Some(output) = special_character {
Ok(OutputStream::one(
UntaggedValue::string(output).into_value(name.tag()),
))
} else {
Err(ShellError::labeled_error(
"error decoding Unicode character",
"error decoding Unicode character",
"error finding named character",
"error finding named character",
name.tag(),
))
}
}
} else {
let special_character = str_to_character(&name.item);
if let Some(output) = special_character {
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(name.tag()),
)))
} else {
Err(ShellError::labeled_error(
"error finding named character",
"error finding named character",
name.tag(),
))
}
Err(ShellError::labeled_error(
"char requires the name of the character",
"missing name of the character",
&args_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> {
match s {
"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,
}
CHAR_MAP.get(s).map(|s| s.into())
}
#[cfg(test)]

View File

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

View File

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

View File

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

View File

@ -1,15 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
pub struct SubCommand;
#[derive(Deserialize)]
pub struct Arguments {
column_path: ColumnPath,
}
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config get"
@ -27,7 +22,7 @@ impl WholeStreamCommand for SubCommand {
"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)
}
@ -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 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 = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
@ -53,15 +49,14 @@ pub fn get(args: CommandArgs) -> Result<ActionStream, ShellError> {
Value {
value: UntaggedValue::Table(list),
..
} => list.into_iter().to_action_stream(),
x => ActionStream::one(ReturnSuccess::value(x)),
} => OutputStream::from_stream(list.into_iter()),
x => OutputStream::one(x),
})
} else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
.into_value(name);
Ok(OutputStream::one(value))
};
result

View File

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

View File

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

View File

@ -1,16 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
pub struct SubCommand;
#[derive(Deserialize)]
pub struct Arguments {
column_path: ColumnPath,
value: Value,
}
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config set"
@ -26,7 +20,7 @@ impl WholeStreamCommand for SubCommand {
"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)
}
@ -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 ctx = EvaluationContext::from_args(&args);
let (
Arguments {
column_path,
mut value,
},
_,
) = args.process()?;
let args = args.evaluate_once()?;
let column_path = args.req(0)?;
let mut value: Value = args.req(1)?;
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
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()?;
ctx.reload_config(global_cfg)?;
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
)))
let value = 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),
}
} else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
.into_value(name);
Ok(OutputStream::one(value))
};
result

View File

@ -1,16 +1,11 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct Arguments {
set_into: Tagged<String>,
}
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config set_into"
@ -28,7 +23,7 @@ impl WholeStreamCommand for SubCommand {
"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)
}
@ -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 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 key = v.to_string();
let set_into: Tagged<String> = args.req(0)?;
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 {
if rows.is_empty() {
return Err(ShellError::labeled_error(
"No values given for set_into",
"needs value(s) from pipeline",
v.tag(),
set_into.tag(),
));
} else if rows.len() == 1 {
// A single value
@ -71,15 +68,14 @@ pub fn set_into(args: CommandArgs) -> Result<ActionStream, ShellError> {
global_cfg.write()?;
ctx.reload_config(global_cfg)?;
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
)))
let value = UntaggedValue::row(global_cfg.vars.clone()).into_value(name);
Ok(OutputStream::one(value))
} else {
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
crate::commands::config::err_no_global_cfg_present(),
))]
.into_iter()
.to_action_stream())
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
.into_value(name);
Ok(OutputStream::one(value))
};
result

View File

@ -26,7 +26,7 @@ impl WholeStreamCommand for Cpy {
}
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 (args, _) = args.process()?;
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 nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use nu_protocol::{Signature, UntaggedValue};
pub struct Command;
@ -18,10 +18,10 @@ impl WholeStreamCommand for Command {
"Apply date function."
}
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(ActionStream::one(ReturnSuccess::value(
UntaggedValue::string(get_full_help(&Command, &args.scope)).into_value(Tag::unknown()),
)))
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

@ -1,20 +1,12 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
Dictionary, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_protocol::{Dictionary, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
use std::fmt::{self, write};
pub struct Date;
#[derive(Deserialize)]
pub struct FormatArgs {
format: Tagged<String>,
table: bool,
}
impl WholeStreamCommand for Date {
fn name(&self) -> &str {
"date format"
@ -30,7 +22,7 @@ impl WholeStreamCommand for Date {
"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)
}
@ -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 (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
.map(move |value| match value {
@ -70,7 +71,7 @@ pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
&format.tag,
))
} else {
let value = if table {
let value = if table.is_some() {
let mut indexmap = IndexMap::new();
indexmap.insert(
"formatted".to_string(),
@ -82,7 +83,7 @@ pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
UntaggedValue::string(&output).into_value(&tag)
};
ReturnSuccess::value(value)
Ok(value)
}
}
_ => Err(ShellError::labeled_error(
@ -91,7 +92,7 @@ pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
&tag,
)),
})
.to_action_stream())
.to_input_stream())
}
#[cfg(test)]

View File

@ -3,7 +3,7 @@ use chrono_tz::TZ_VARIANTS;
use indexmap::IndexMap;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Dictionary, ReturnSuccess, Signature, UntaggedValue};
use nu_protocol::{Dictionary, Signature, UntaggedValue};
pub struct Date;
@ -20,7 +20,7 @@ impl WholeStreamCommand for Date {
"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)
}
@ -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 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),
);
Ok(ReturnSuccess::Value(
UntaggedValue::Row(Dictionary { entries }).into_value(&tag),
))
Ok(UntaggedValue::Row(Dictionary { entries }).into_value(&tag))
});
Ok(list.into_iter().to_action_stream())
Ok(list.into_iter().to_input_stream())
}
#[cfg(test)]

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use chrono::{DateTime, Local};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, UntaggedValue};
use nu_protocol::{Signature, UntaggedValue, Value};
pub struct Date;
@ -19,20 +19,23 @@ impl WholeStreamCommand for 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)
}
}
pub fn now(args: CommandArgs) -> Result<ActionStream, ShellError> {
let args = args.evaluate_once()?;
let tag = args.call_info.name_tag.clone();
pub fn date_now(tag: &Tag) -> Value {
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)]

View File

@ -3,7 +3,7 @@ use chrono::{Datelike, Timelike};
use indexmap::IndexMap;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
use nu_protocol::{Dictionary, Primitive, Signature, UntaggedValue, Value};
pub struct Date;
@ -20,7 +20,7 @@ impl WholeStreamCommand for Date {
"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)
}
@ -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 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
.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);
ReturnSuccess::value(value)
Ok(value)
}
_ => Err(ShellError::labeled_error(
"Expected a date from pipeline",
@ -87,7 +91,7 @@ fn to_table(args: CommandArgs) -> Result<ActionStream, ShellError> {
&tag,
)),
})
.to_action_stream())
.to_input_stream())
}
#[cfg(test)]

View File

@ -2,16 +2,11 @@ use crate::commands::date::parser::{datetime_in_timezone, ParseErrorKind};
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
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;
pub struct Date;
#[derive(Deserialize)]
struct DateToTimeZoneArgs {
timezone: Tagged<String>,
}
impl WholeStreamCommand for Date {
fn name(&self) -> &str {
"date to-timezone"
@ -33,7 +28,7 @@ impl WholeStreamCommand for Date {
"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)
}
@ -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 (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 {
Value {
value: UntaggedValue::Primitive(Primitive::Date(dt)),
@ -71,7 +69,7 @@ fn to_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(dt) => {
let value = UntaggedValue::date(dt).into_value(&tag);
ReturnSuccess::value(value)
Ok(value)
}
Err(e) => Err(ShellError::labeled_error(
error_message(e),
@ -85,7 +83,7 @@ fn to_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
&tag,
)),
})
.to_action_stream())
.to_input_stream())
}
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 nu_engine::basic_evaluation_context;
use nu_engine::whole_stream_command;
use std::error::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::*;
@ -14,6 +13,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(NuPlugin),
whole_stream_command(Let),
whole_stream_command(LetEnv),
whole_stream_command(LoadEnv),
whole_stream_command(Def),
whole_stream_command(Source),
// System/file operations
@ -89,7 +89,6 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(StrUpcase),
whole_stream_command(StrCapitalize),
whole_stream_command(StrFindReplace),
whole_stream_command(StrFrom),
whole_stream_command(StrSubstring),
whole_stream_command(StrToDatetime),
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(Insert),
whole_stream_command(Into),
whole_stream_command(IntoBinary),
whole_stream_command(IntoInt),
whole_stream_command(IntoString),
whole_stream_command(SplitBy),
// Row manipulation
whole_stream_command(All),
@ -157,6 +158,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(EachGroup),
whole_stream_command(EachWindow),
whole_stream_command(Empty),
whole_stream_command(ForIn),
// Table manipulation
whole_stream_command(Flatten),
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(PathJoin),
whole_stream_command(PathParse),
whole_stream_command(PathRelativeTo),
whole_stream_command(PathSplit),
whole_stream_command(PathType),
// Url
@ -250,6 +253,31 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(Seq),
whole_stream_command(SeqDates),
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")]

View File

@ -2,14 +2,16 @@ use crate::prelude::*;
use nu_engine::run_block;
use nu_engine::WholeStreamCommand;
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;
#[derive(Deserialize, Debug)]
struct DoArgs {
block: CapturedBlock,
ignore_errors: bool,
rest: Vec<Value>,
}
impl WholeStreamCommand for Do {
@ -21,17 +23,18 @@ impl WholeStreamCommand for Do {
Signature::build("do")
.required("block", SyntaxShape::Block, "the block to run ")
.switch(
"ignore_errors",
"ignore-errors",
"ignore errors as the block runs",
Some('i'),
)
.rest(SyntaxShape::Any, "the parameter(s) for the block")
}
fn usage(&self) -> &str {
"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)
}
@ -47,32 +50,36 @@ impl WholeStreamCommand for Do {
example: r#"do -i { thisisnotarealcommand }"#,
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 context = EvaluationContext::from_args(&raw_args);
let (
DoArgs {
ignore_errors,
mut block,
},
input,
) = raw_args.process()?;
let args = raw_args.evaluate_once()?;
let do_args = DoArgs {
block: args.req(0)?,
ignore_errors: args.has_flag("ignore-errors"),
rest: args.rest(1)?,
};
let block_redirection = match external_redirection {
ExternalRedirection::None => {
if ignore_errors {
if do_args.ignore_errors {
ExternalRedirection::Stderr
} else {
ExternalRedirection::None
}
}
ExternalRedirection::Stdout => {
if ignore_errors {
if do_args.ignore_errors {
ExternalRedirection::StdoutAndStderr
} else {
ExternalRedirection::Stdout
@ -81,14 +88,30 @@ fn do_(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
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();
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();
if ignore_errors {
if do_args.ignore_errors {
// To properly ignore errors we need to redirect stderr, consume it, and remove
// any errors we see in the process.
@ -96,12 +119,12 @@ fn do_(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(mut stream) => {
let output = stream.drain_vec();
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 {
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_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape};
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct Arguments {
columns: Option<Tagged<u64>>,
}
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"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'."
}
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
drop(args)
}
@ -47,8 +42,9 @@ impl WholeStreamCommand for SubCommand {
}
}
fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (Arguments { columns }, input) = args.process()?;
fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let columns: Option<Tagged<u64>> = args.opt(0)?;
let to_drop = if let Some(quantity) = columns {
*quantity as usize
@ -56,7 +52,8 @@ fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
1
};
Ok(input
Ok(args
.input
.map(move |item| {
let headers = item.data_descriptors();
@ -66,10 +63,9 @@ fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
n => &headers[..n - to_drop],
};
select_fields(&item, descs, item.tag())
Ok(select_fields(&item, descs, item.tag()))
})
.map(ReturnSuccess::value)
.to_action_stream())
.to_input_stream())
}
#[cfg(test)]

View File

@ -6,11 +6,6 @@ use nu_source::Tagged;
pub struct Command;
#[derive(Deserialize)]
pub struct Arguments {
rows: Option<Tagged<u64>>,
}
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"drop"
@ -28,7 +23,7 @@ impl WholeStreamCommand for Command {
"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)
}
@ -51,9 +46,10 @@ impl WholeStreamCommand for Command {
}
}
fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (Arguments { rows }, input) = args.process()?;
let v: Vec<_> = input.into_vec();
fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let rows: Option<Tagged<u64>> = args.opt(0)?;
let v: Vec<_> = args.input.into_vec();
let rows_to_drop = if let Some(quantity) = rows {
*quantity as usize
@ -62,7 +58,7 @@ fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
};
Ok(if rows_to_drop == 0 {
v.into_iter().to_action_stream()
v.into_iter().map(Ok).to_input_stream()
} else {
let k = if v.len() < rows_to_drop {
0
@ -70,8 +66,8 @@ fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
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> {
let tag = args.call_info.name_tag.clone();
let ctrl_c = args.ctrl_c.clone();
let ctrl_c = args.ctrl_c();
let ctrl_c_copy = ctrl_c.clone();
let (args, _): (DuArgs, _) = args.process()?;

View File

@ -4,7 +4,8 @@ use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
hir::CapturedBlock, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
hir::{CapturedBlock, ExternalRedirection},
Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
};
pub struct Each;
@ -41,7 +42,7 @@ impl WholeStreamCommand for Each {
},
Example {
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![
UntaggedValue::int(1).into(),
UntaggedValue::int(4).into(),
@ -51,9 +52,18 @@ impl WholeStreamCommand for Each {
Example {
description: "Number each item and echo a message",
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")]),
},
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>>,
context: Arc<EvaluationContext>,
input: Value,
external_redirection: ExternalRedirection,
) -> Result<OutputStream, ShellError> {
let input_clone = input.clone();
// When we process a row, we need to know whether the block wants to have the contents of the row as
@ -77,16 +88,18 @@ pub fn process_row(
context.scope.enter_scope();
context.scope.add_vars(&captured_block.captured.entries);
if !captured_block.block.params.positional.is_empty() {
// FIXME: add check for more than parameter, once that's supported
context
.scope
.add_var(captured_block.block.params.positional[0].0.name(), input);
if let Some((arg, _)) = captured_block.block.params.positional.first() {
context.scope.add_var(arg.name(), input);
} else {
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();
@ -95,7 +108,7 @@ pub fn process_row(
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));
dict.insert_untagged("index", UntaggedValue::int(index as i64));
dict.insert_value("item", item);
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> {
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 block: CapturedBlock = args.req(0)?;
@ -119,7 +133,7 @@ fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let context = context.clone();
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,
Err(e) => OutputStream::one(Value::error(e)),
}
@ -133,7 +147,7 @@ fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let block = block.clone();
let context = context.clone();
match process_row(block, context, input) {
match process_row(block, context, input, external_redirection) {
Ok(s) => s,
Err(e) => OutputStream::one(Value::error(e)),
}

View File

@ -2,19 +2,14 @@ use crate::commands::each::process_row;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
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 serde::Deserialize;
pub struct EachGroup;
#[derive(Deserialize)]
pub struct EachGroupArgs {
group_size: Tagged<usize>,
block: CapturedBlock,
//numbered: Tagged<bool>,
}
impl WholeStreamCommand for EachGroup {
fn name(&self) -> &str {
"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 (each_args, input): (EachGroupArgs, _) = raw_args.process()?;
let block = Arc::new(Box::new(each_args.block));
let external_redirection = raw_args.call_info.args.external_redirection;
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 {
block,
context,
group_size: each_args.group_size.item,
input,
group_size: group_size.item,
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>,
group_size: usize,
input: InputStream,
external_redirection: ExternalRedirection,
}
impl Iterator for EachGroupIterator {
@ -89,6 +90,7 @@ impl Iterator for EachGroupIterator {
group,
self.block.clone(),
self.context.clone(),
self.external_redirection,
))
}
}
@ -97,13 +99,14 @@ pub(crate) fn run_block_on_vec(
input: Vec<Value>,
block: Arc<Box<CapturedBlock>>,
context: Arc<EvaluationContext>,
external_redirection: ExternalRedirection,
) -> OutputStream {
let value = Value {
value: UntaggedValue::Table(input),
tag: Tag::unknown(),
};
match process_row(block, context, value) {
match process_row(block, context, value, external_redirection) {
Ok(s) => {
// We need to handle this differently depending on whether process_row
// returned just 1 value or if it returned multiple as a stream.

View File

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

View File

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

View File

@ -3,8 +3,8 @@ use nu_engine::run_block;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
hir::CapturedBlock, ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape,
UntaggedValue, Value,
hir::CapturedBlock, hir::ExternalRedirection, ColumnPath, Primitive, ReturnSuccess, Signature,
SyntaxShape, UntaggedValue, Value,
};
use crate::utils::arguments::arguments;
@ -63,7 +63,7 @@ impl WholeStreamCommand for Command {
),
},Example {
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(
vec![
UntaggedValue::row(indexmap! {
@ -140,9 +140,16 @@ fn process_row(
context.scope.enter_scope();
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();
let mut stream = stream?;

View File

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

View File

@ -39,12 +39,12 @@ impl WholeStreamCommand for Command {
},
Example {
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")]),
},
Example {
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")]),
}
]

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::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged;
use std::borrow::Borrow;
pub struct Format;
#[derive(Deserialize)]
pub struct FormatArgs {
pattern: Tagged<String>,
}
impl WholeStreamCommand for Format {
fn name(&self) -> &str {
"format"
@ -30,7 +25,7 @@ impl WholeStreamCommand for Format {
"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)
}
@ -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 (FormatArgs { pattern }, input) = args.process()?;
let args = args.evaluate_once()?;
let pattern: Tagged<String> = args.req(0)?;
let format_pattern = format(&pattern);
let commands = Arc::new(format_pattern);
Ok(input
Ok(args
.input
.map(move |value| {
let mut output = String::new();
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)]

View File

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

View File

@ -20,7 +20,7 @@ impl WholeStreamCommand for From {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
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::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::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::String(s) => {
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);
if let Some(suggestions) = did_you_mean(&obj_source, column_path_tried.as_string()) {
Some(ShellError::labeled_error_with_secondary(
did_you_mean(&obj_source, column_path_tried.as_string()).map(|suggestions| {
ShellError::labeled_error_with_secondary(
"Unknown column",
primary_label,
column_path_tried.span,
@ -239,10 +239,8 @@ pub fn get_column_from_row_error(
&obj_source.data_descriptors().join(", ")
),
column_path_tried.span.since(path_members_span),
))
} else {
None
}
)
})
}
PathMember {
unspanned: UnspannedPathMember::Int(idx),

View File

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

View File

@ -2,21 +2,11 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::ShellTypeName;
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::{Tag, Tagged};
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)]
pub struct Base64Config {
pub character_set: String,
@ -46,13 +36,13 @@ impl WholeStreamCommand for SubCommand {
Some('c'),
)
.switch(
"encode",
"encode the input as base64. This is the default behavior if not specified.",
"encode",
"encode the input as base64. This is the default behavior if not specified.",
Some('e')
)
.switch(
"decode",
"decode the input from base64",
"decode",
"decode the input from base64",
Some('d'))
.rest(
SyntaxShape::ColumnPath,
@ -64,7 +54,7 @@ impl WholeStreamCommand for SubCommand {
"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)
}
@ -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 args = args.evaluate_once()?;
let (
Arguments {
encode,
decode,
character_set,
rest,
},
input,
) = args.process()?;
let encode = args.has_flag("encode");
let decode = args.has_flag("decode");
let character_set: Option<Tagged<String>> = args.get_flag("character_set")?;
let column_paths: Vec<ColumnPath> = args.rest(0)?;
if encode.item && decode.item {
return Ok(ActionStream::one(Err(ShellError::labeled_error(
if encode && decode {
return Err(ShellError::labeled_error(
"only one of --decode and --encode flags can be used",
"conflicting flags",
name_tag,
))));
));
}
// Default the action to be encoding if no flags are specified.
let action_type = if *decode.item() {
let action_type = if decode {
ActionType::Decode
} else {
ActionType::Encode
@ -134,12 +120,11 @@ fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
action_type,
};
let column_paths: Vec<_> = rest;
Ok(input
Ok(args
.input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, &encoding_config, v.tag())?)
action(&v, &encoding_config, v.tag())
} else {
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(
@ -182,7 +167,7 @@ fn action(
return Err(ShellError::labeled_error(
"value is not an accepted character set",
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
),
tag.into().span,

View File

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

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

View File

@ -38,7 +38,7 @@ impl WholeStreamCommand for Help {
fn help(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone();
let scope = args.scope.clone();
let scope = args.scope().clone();
let (HelpArgs { rest }, ..) = args.process()?;
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() {
match columns.remove(0).split_last() {
Some((key, _)) => Some(key.as_string().tagged(&name)),
None => None,
}
columns
.remove(0)
.split_last()
.map(|(key, _)| key.as_string().tagged(&name))
} else {
None
};
@ -160,7 +160,7 @@ pub fn histogram(args: CommandArgs) -> Result<ActionStream, ShellError> {
"{}%",
// Some(2) < the number of 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()?
);
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> {
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 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(b) => {
let result = if b {
run_block(&then_case.block, &*context, input)
run_block(&then_case.block, &*context, input, external_redirection)
} else {
run_block(&else_case.block, &*context, input)
run_block(&else_case.block, &*context, input, external_redirection)
};
context.scope.exit_scope();

View File

@ -2,6 +2,7 @@ use crate::prelude::*;
use nu_engine::run_block;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::hir::ExternalRedirection;
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
@ -50,7 +51,7 @@ impl WholeStreamCommand for Command {
.into()]),
},Example {
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! {
"author".to_string() => Value::from("Yehuda"),
"lucky_number".to_string() => UntaggedValue::int(4).into(),
@ -79,9 +80,16 @@ fn process_row(
context.scope.enter_scope();
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();

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

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

View File

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

View File

@ -1,9 +1,7 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
use num_bigint::{BigInt, BigUint, ToBigInt};
// TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml)
@ -14,22 +12,16 @@ use std::iter;
pub struct SubCommand;
struct Arguments {
decimals: Option<Tagged<u64>>,
group_digits: bool,
column_paths: Vec<ColumnPath>,
}
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"str from"
"into string"
}
fn signature(&self) -> Signature {
Signature::build("str from")
Signature::build("into string")
.rest(
SyntaxShape::ColumnPath,
"optionally convert to string by column paths",
"column paths to convert to string (for table input)",
)
.named(
"decimals",
@ -40,66 +32,83 @@ impl WholeStreamCommand for SubCommand {
}
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> {
operate(args)
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
into_string(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "round to nearest integer",
example: "echo 1.7 | str from -d 0",
description: "convert decimal to string and round to nearest integer",
example: "echo 1.7 | into string -d 0",
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 {
description: "format large number with localized digit grouping",
example: "= 1000000.2 | str from -g",
result: Some(vec![
UntaggedValue::string("1,000,000.2").into_untagged_value()
]),
description: "convert decimal to string",
example: "echo 4.3 | into string",
result: Some(vec![UntaggedValue::string("4.3").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> {
let (options, input) = args.extract(|params| {
Ok(Arguments {
decimals: params.get_flag("decimals")?,
group_digits: false,
column_paths: params.rest_args()?,
})
})?;
fn into_string(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let digits = options.decimals.as_ref().map(|tagged| tagged.item);
let group_digits = options.group_digits;
let decimals: Option<Tagged<u64>> = args.get_flag("decimals")?;
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| {
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag(), digits, group_digits)?)
if column_paths.is_empty() {
action(&v, v.tag(), digits, group_digits)
} else {
let mut ret = v;
for path in &options.column_paths {
for path in &column_paths {
ret = ret.swap_data_by_column_path(
path,
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(
input: &Value,
tag: impl Into<Tag>,
@ -109,6 +118,13 @@ pub fn action(
match &input.value {
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::string(match prim {
Primitive::Int(int) => {
if group_digits {
format_int(*int) // int.to_formatted_string(*locale)
} else {
int.to_string()
}
}
Primitive::BigInt(int) => {
if group_digits {
format_bigint(int) // int.to_formatted_string(*locale)
} else {
@ -121,27 +137,45 @@ pub fn action(
Primitive::Date(a_date) => a_date.format("%c").to_string(),
Primitive::FilePath(a_filepath) => a_filepath.as_path().display().to_string(),
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
}
Primitive::Nothing => "nothing".to_string(),
_ => {
return Err(ShellError::unimplemented(
"str from for non-numeric primitives",
))
return Err(ShellError::unimplemented(&format!(
"into string for primitive: {:?}",
prim
)))
}
})
.into_value(tag)),
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
"specify column to use 'str from'",
"specify column to use 'into string'",
"found table",
input.tag.clone(),
)),
_ => Err(ShellError::unimplemented(
"str from for non-primitive, non-table types",
)),
UntaggedValue::Table(_) => Err(ShellError::unimplemented("into string for table")),
_ => 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 {
format!("{}", int)

View File

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

View File

@ -1,10 +1,12 @@
use crate::prelude::*;
use log::trace;
use nu_engine::evaluate_baseline_expr;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_parser::ParserScope;
use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{
hir::{CapturedBlock, ClassifiedCommand},
Signature, SyntaxShape,
};
pub struct SubCommand;
@ -27,54 +29,40 @@ impl WholeStreamCommand for SubCommand {
"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 tag = args.call_info.name_tag.clone();
let call_info = args.evaluate_once()?;
let block = call_info.args.expect_nth(0)?.clone();
let (condition, captured) = match block {
Value {
value: UntaggedValue::Block(captured_block),
tag,
} => {
if captured_block.block.block.len() != 1 {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
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 => {
let block: CapturedBlock = call_info.req(0)?;
let condition = {
if block.block.block.len() != 1 {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
match block.block.block[0].pipelines.get(0) {
Some(item) => match item.list.get(0) {
Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
_ => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
},
None => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
}
Value { tag, .. } => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
};
Ok(call_info
@ -83,17 +71,17 @@ impl WholeStreamCommand for SubCommand {
let condition = condition.clone();
let ctx = ctx.clone();
ctx.scope.enter_scope();
ctx.scope.add_vars(&captured.entries);
ctx.scope.add_var("$it", item.clone());
trace!("ITEM = {:?}", item);
ctx.scope.add_vars(&block.captured.entries);
if let Some((arg, _)) = block.block.params.positional.first() {
ctx.scope.add_var(arg.name(), item.clone());
}
let result = evaluate_baseline_expr(&*condition, &*ctx);
ctx.scope.exit_scope();
trace!("RESULT = {:?}", result);
!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::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{
hir::{CapturedBlock, ClassifiedCommand},
Signature, SyntaxShape,
};
pub struct SubCommand;
@ -26,53 +29,39 @@ impl WholeStreamCommand for SubCommand {
"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 tag = args.call_info.name_tag.clone();
let call_info = args.evaluate_once()?;
let block = call_info.args.expect_nth(0)?.clone();
let (condition, captured) = match block {
Value {
value: UntaggedValue::Block(captured_block),
tag,
} => {
if captured_block.block.block.len() != 1 {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
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 => {
let block: CapturedBlock = call_info.req(0)?;
let condition = {
if block.block.block.len() != 1 {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
match block.block.block[0].pipelines.get(0) {
Some(item) => match item.list.get(0) {
Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
_ => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
},
None => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
}
Value { tag, .. } => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
};
Ok(call_info
@ -82,8 +71,10 @@ impl WholeStreamCommand for SubCommand {
let ctx = ctx.clone();
ctx.scope.enter_scope();
ctx.scope.add_var("$it", item.clone());
ctx.scope.add_vars(&captured.entries);
ctx.scope.add_vars(&block.captured.entries);
if let Some((arg, _)) = block.block.params.positional.first() {
ctx.scope.add_var(arg.name(), item.clone());
}
trace!("ITEM = {:?}", item);
let result = evaluate_baseline_expr(&*condition, &*ctx);
@ -92,7 +83,7 @@ impl WholeStreamCommand for SubCommand {
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 nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
pub struct Last;
#[derive(Deserialize)]
pub struct LastArgs {
rows: Option<Tagged<u64>>,
}
impl WholeStreamCommand for Last {
fn name(&self) -> &str {
"last"
@ -28,7 +22,7 @@ impl WholeStreamCommand for Last {
"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)
}
@ -37,7 +31,7 @@ impl WholeStreamCommand for Last {
Example {
description: "Get the last row",
example: "echo [1 2 3] | last",
result: Some(vec![Value::from(UntaggedValue::from(BigInt::from(3)))]),
result: Some(vec![UntaggedValue::int(3).into()]),
},
Example {
description: "Get the last three rows",
@ -52,12 +46,13 @@ impl WholeStreamCommand for Last {
}
}
fn last(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (LastArgs { rows }, input) = args.process()?;
let v: Vec<_> = input.into_vec();
fn last(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let rows = args.nth(0).cloned();
let v: Vec<_> = args.input.into_vec();
let end_rows_desired = if let Some(quantity) = rows {
*quantity as usize
quantity.as_usize()?
} else {
1
};
@ -70,7 +65,7 @@ fn last(args: CommandArgs) -> Result<ActionStream, ShellError> {
let iter = v.into_iter().skip(beginning_rows_to_skip);
Ok((iter).to_action_stream())
Ok(OutputStream::from_stream(iter))
}
#[cfg(test)]

View File

@ -94,7 +94,7 @@ impl Iterator for CountIterator {
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 nu_engine::{evaluate_baseline_expr, WholeStreamCommand};
use nu_engine::{evaluate_baseline_expr, FromValue, WholeStreamCommand};
use nu_errors::ShellError;
use nu_protocol::{hir::CapturedBlock, hir::ClassifiedCommand, Signature, SyntaxShape};
use nu_source::Tagged;
use nu_protocol::{
hir::{CapturedBlock, ClassifiedCommand},
Signature, SyntaxShape, UntaggedValue,
};
pub struct Let;
@ -32,20 +34,41 @@ impl WholeStreamCommand for Let {
}
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> {
let tag = args.call_info.name_tag.clone();
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 name: Tagged<String> = args.req(0)?;
let rhs: CapturedBlock = args.req(2)?;
let var_name = positional[0].var_name()?;
let rhs_raw = evaluate_baseline_expr(&positional[2], &ctx)?;
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 {
return Err(ShellError::labeled_error(
"Expected a value",
@ -75,24 +98,15 @@ pub fn letcmd(args: CommandArgs) -> Result<ActionStream, ShellError> {
};
ctx.scope.enter_scope();
ctx.scope.add_vars(&captured.entries);
let value = evaluate_baseline_expr(&expr, &ctx);
ctx.scope.exit_scope();
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
// In this case, if we don't set it now, we'll lose the scope that this
// variable should be set into.
ctx.scope.add_var(name, value);
ctx.scope.add_var(var_name, value);
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> {
let name = args.call_info.name_tag.clone();
let ctrl_c = args.ctrl_c.clone();
let shell_manager = args.shell_manager.clone();
let ctrl_c = args.ctrl_c();
let shell_manager = args.shell_manager();
let (args, _) = args.process()?;
shell_manager.ls(args, name, ctrl_c)
}

View File

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

View File

@ -20,8 +20,9 @@ impl WholeStreamCommand for SubCommand {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let mapped = args.input.map(move |val| match val.value {
UntaggedValue::Primitive(Primitive::Int(val)) => {
UntaggedValue::int(val.magnitude().clone()).into()
UntaggedValue::Primitive(Primitive::Int(val)) => UntaggedValue::int(val.abs()).into(),
UntaggedValue::Primitive(Primitive::BigInt(val)) => {
UntaggedValue::big_int(val.magnitude().clone()).into()
}
UntaggedValue::Primitive(Primitive::Decimal(val)) => {
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_errors::ShellError;
use nu_protocol::{
hir::{convert_number_to_u64, Number, Operator},
Primitive, Signature, UntaggedValue, Value,
};
use nu_protocol::{hir::Operator, Primitive, Signature, UntaggedValue, Value};
use bigdecimal::FromPrimitive;
@ -47,7 +44,7 @@ impl WholeStreamCommand for SubCommand {
fn to_byte(value: &Value) -> Option<Value> {
match &value.value {
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,
}
@ -79,7 +76,7 @@ pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
Value {
value: UntaggedValue::Primitive(Primitive::Filesize(num)),
..
} => UntaggedValue::int(num.clone()).into_untagged_value(),
} => UntaggedValue::int(*num as i64).into_untagged_value(),
other => other.clone(),
})
.collect::<Vec<_>>(),
@ -100,15 +97,18 @@ pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
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);
match result {
Ok(UntaggedValue::Primitive(Primitive::Decimal(result))) => {
let number = Number::Decimal(result);
let number = convert_number_to_u64(&number);
Ok(UntaggedValue::filesize(number).into_value(name))
}
Ok(UntaggedValue::Primitive(Primitive::Decimal(result))) => match result.to_u64() {
Some(number) => Ok(UntaggedValue::filesize(number).into_value(name)),
None => Err(ShellError::labeled_error(
"could not calculate average of non-integer or unrelated types",
"source",
name,
)),
},
Ok(_) => Err(ShellError::labeled_error(
"could not calculate average of non-integer or unrelated types",
"source",

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