Compare commits

...

187 Commits

Author SHA1 Message Date
JT
62011b6bcc Bump to 0.42 (#4234) 2021-12-28 20:56:59 +11:00
1214cd57e8 bat: use regex-onig instead of regex-fancy (#4226)
Fixes #4224

Signed-off-by: nibon7 <nibon7@163.com>
2021-12-24 08:34:59 -06:00
6cd124ddb2 allow insecure server connections when using SSL (#4219)
Fixes #4211

Signed-off-by: nibon7 <nibon7@163.com>
2021-12-23 06:48:43 +11:00
d32aec5906 Don't panic if the other end of std{out,err} is closed (#4179)
* fix #4161

println! and friends will panic on BrokenPipe. The solution is to use
writeln! instead, and ignore the error (or do we want to do something else?)

* test that nu doesn't panic in case of BrokenPipe error

* fixup! test that nu doesn't panic in case of BrokenPipe error

* make do_not_panic_if_broken_pipe only run on UNIX systems
2021-12-21 10:08:41 +11:00
e919f9a73b use heck for string casing (#4081)
I removed the Inflector dependency in favor of heck for two reasons:
- to close #3674.
- heck seems simpler and actively maintained

We could probably alter the structure of the `str_` module to expose the
individual casing behaviors better.
I did not feel as confident on changing those signatures.

So I took a lazier approach of a macro in the `mod.rs` that creates the public
shimming function to heck's traits.
2021-12-14 09:43:48 -06:00
a3c349746f ci: update macOS agent (#4207)
10.14 has been deprecated: https://github.com/Azure/azure-sdk-for-cpp/issues/3168

This hopefully fixes recent CI failures!
2021-12-14 08:55:51 -06:00
b5f8f64d79 ci: fix macOS agent (#4203)
I noticed the agent documentation uses uppercase: https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=yaml
2021-12-13 13:08:03 -06:00
1576b959f9 update feat template (#4201) 2021-12-12 16:15:31 -06:00
4096f52003 update templates2 (#4200) 2021-12-12 16:11:27 -06:00
7ceb668419 Revert "try out title change (#4198)" (#4199)
This reverts commit 420aee18ca.
2021-12-12 16:06:07 -06:00
420aee18ca try out title change (#4198) 2021-12-12 16:05:24 -06:00
pin
15e9c11849 Fix build on NetBSD (#4192) 2021-12-09 14:23:40 +02:00
9fd680ae2b fix: Implicit coercion of boolean false and empty value #4094 (#4120)
Signed-off-by: closetool <c299999999@qq.com>
2021-12-09 14:19:51 +02:00
ad94ed5e13 Fix Configuration section in bug report template (#4181)
* Fix Configuration section in bug report template

Change the placeholder content to actually match the `to md` output, and add `--pretty`

* Don't omit data in placeholder configuration table

* Remove blank line in bug_report.yml
2021-12-08 13:32:28 -06:00
1bdcdcca70 fix: change into column_path to into column-path (breaking change) (#4185) (#4189) 2021-12-08 11:04:55 +02:00
JT
610e3911f6 Bump to 0.41 (#4187) 2021-12-08 06:21:00 +13:00
ee9eddd851 avoid unnecessary allocation (#4178) 2021-12-06 07:38:58 +13:00
JT
c08e145501 Fix clippy warnings (#4176) 2021-12-03 07:05:38 +13:00
c00853a473 Seems like accessing $it outside each is not possible now (#4000) 2021-12-03 06:49:24 +13:00
79c7b20cfd add login shell flag (#4175) 2021-12-02 20:05:04 +13:00
JT
89cbfd758d Remove 'arboard' (#4174) 2021-12-02 08:48:03 +13:00
e6e6b730f3 Bye bye upx sorry (#4173)
* bye bye upx, let's try stripping alone

* remove all stripping - not sure it's even working
2021-11-30 13:34:16 -06:00
0fe6a7c1b5 bye bye upx, let's try stripping alone (#4172) 2021-11-30 12:11:01 -06:00
1794ad51bd Sanitize arguments to external commands a bit better (#4157)
* fix #4140

We are passing commands into a shell underneath but we were not
escaping arguments correctly. This new version of the code also takes
into consideration the ";" and "&" characters, which have special
meaning in shells.

We would probably benefit from a more robust way to join arguments to
shell programs. Python's stdlib has shlex.join, and perhaps we can
take that implementation as a reference.

* clean up escaping of posix shell args

I believe the right place to do escaping of arguments was in the
spawn_sh_command function. Note that this change prevents things like:

^echo "$(ls)"

from executing the ls command. Instead, this will just print

$(ls)

The regex has been taken from the python stdlib implementation of shlex.quote

* fix non-literal parameters and single quotes

* address clippy's comments

* fixup! address clippy's comments

* test that subshell commands are sanitized properly
2021-11-29 09:46:42 -06:00
fb197f562a save --append: create file if it doesn't exist (#4156)
* have save --append create file if not exists

Currently, doing:

echo a | save --raw --append file.txt

will fail if file.txt does not exist. This PR changes that

* test that `save --append` will create new file
2021-11-26 12:27:41 -06:00
91c270c14a fix markup (#4155) 2021-11-26 07:37:50 -06:00
3e93ae8af4 Correct spelling (#4152) 2021-11-25 11:11:20 -06:00
e06df124ca upgrading dependencies (#4135)
* upgrade dependencies
num-bigint 0.3.1 -> 0.4.3
bigdecimal-rs 0.2.1 -> bigdecimal 0.3.0
s3hander 0.7 -> 0.7.5
bat 0.18 -> 0.18, default-features = false

* upgrade arboard 1.1.0 -> 2.0.1

* in polars use comfy-table instead of prettytable-rs
the last release of prettytable-rs was `0.8.0 Sep 27, 2018`
and it uses `term 0.5` as a dependency

* upgrade dependencies

* upgrade trash -> 2.0.1

Co-authored-by: ahkrr <alexhk@protonmail.com>
2021-11-20 07:11:11 -06:00
JT
2590fcbe5c Bump to 0.40 (#4129) 2021-11-16 21:53:03 +13:00
JT
09691ff866 Delete docker-publish.yml 2021-11-16 14:19:35 +13:00
16db368232 upgrade polars to 0.17 (#4122) 2021-11-16 12:01:02 +13:00
JT
df87d90b8c Add 'detect columns' command (#4127)
* Add 'detect columns' command

* Fix warnings
2021-11-16 11:29:54 +13:00
f2f01b8a4d missed from_mp4, added back (#4128) 2021-11-15 16:19:44 -06:00
6c0190cd38 added upx and strip to mac and windows (#4126) 2021-11-15 15:32:48 -06:00
b26246bf12 trying upx and strip (#4125) 2021-11-15 15:01:25 -06:00
36a4effbb2 tweaked strip ci (#4124) 2021-11-15 14:30:32 -06:00
9fca417f8c update release to allow running manually (#4123) 2021-11-15 14:04:00 -06:00
d09e1148b2 add the ability to strip the debug symbols for smaller binaries on mac and linux 2021-11-15 13:47:46 -06:00
493bc2b1c9 Update README (#4118)
`winget install nu` fails because there's other options for "nu" now.
Using the full `nushell` word solved it for me.

[Imgur](https://imgur.com/aqz2qNp)
2021-11-14 19:34:57 +13:00
74b812228c upgrade dependencies (#4116)
* remove unused dependencies

* upgrade dependency bytes 0.5.6 -> 1.1.0

* upgrade dependency heapless 0.6.1 -> 0.7.8

* upgrade dependency image 0.22.4 -> 0.23.14

* upgrade dependency mp4 0.8.2 -> 0.9.0

* upgrade dependency bson 0.14.1 -> 2.0.1

Bson::Undefined, Bson::MaxKey, Bson::MinKey and Bson::DbPointer
weren't present in the previous version.

Co-authored-by: ahkrr <alexhk@protonmail.com>
2021-11-14 19:32:21 +13:00
649b3804c1 fix: panic! during parsing (#4107)
Typing `selector -qa` into nu would cause a `panic!`
This was the case because the inner loop incremented the `idx`
that was only checked in the outer loop and used it to index into
`lite_cmd.parts[idx]`
With the fix we now break loop.

Co-authored-by: ahkrr <alexhk@protonmail.com>
2021-11-05 21:46:46 +13:00
JT
df6a53f52e Update stale.yml (#4106) 2021-11-04 21:25:44 +13:00
JT
c4af5df828 Update stale.yml (#4102) 2021-10-31 16:48:58 +13:00
f94a3e15f5 Get rid of header bold option (#4076)
* refactor(options): get rid of 'header_bold' option

* docs(config): remove 'header_bold' from docs

* fix(options): replicate logic to apply true/false in bold

* style(options): apply lint fixes
2021-10-31 06:59:19 +13:00
75782f0f50 Fix #4070: Inconsistent file matching rule for ls and rm (#4099) 2021-10-28 15:05:07 +03:00
JT
2b06ce27d3 Bump to 0.39 (#4097) 2021-10-27 08:36:41 +13:00
72c241348b Remove dependencies (#4087)
* fix regression

* Removed the nipper dependency

* fix linting

* fix clippy
2021-10-22 06:58:40 +13:00
JT
ab2d2db987 Fix clippy warnings (#4088)
* Fix clippy warnings

* Fix clippy warnings
2021-10-22 06:57:51 +13:00
07e05ef183 fix regression (#4086) 2021-10-19 13:39:23 -05:00
a986de8ad0 Update stale.yml (#4073)
add labels that can exempt from stale bot
2021-10-09 14:50:27 -05:00
22cfe4391e remove history file after clearing it (#4069) 2021-10-07 10:09:31 -05:00
JT
97d17311f4 Update LICENSE (#4067) 2021-10-07 08:42:07 +13:00
0f6fd30619 stale.yml: mention time to close in stale message (#4066) 2021-10-06 09:05:29 -05:00
JT
e1ebd461d2 Bump to 0.28 (#4064) 2021-10-06 06:35:25 +13:00
JT
f000d5d0a1 Remove the broken scrolling support (#4063)
* Remove the broken scrolling support

* Remove the broken scrolling support
2021-10-06 05:57:14 +13:00
574c5961c8 Add -c flag to select command (#4062)
See cc3653cfd9 for more on the `-c` flag.

Co-authored-by: Andrés N. Robalino <andres@androbtech.com>

Co-authored-by: Andrés N. Robalino <andres@androbtech.com>
2021-10-05 13:23:37 +13:00
JT
69708f7244 update wasm deps (#4061) 2021-10-03 07:19:54 +13:00
62c5df5fc6 expand tilde when reading plugin_dirs (#4052) 2021-10-02 21:38:21 +13:00
92c855a412 Fixed two typos in the tutor. (#4051) 2021-10-02 21:37:59 +13:00
d395816929 remove ansi colors if this is not a tty (#4058) 2021-10-01 09:00:08 -05:00
5e34ef6dff new command: into column_path (#4048) 2021-09-29 07:23:34 -05:00
d567c58cc1 Add -c flag to update cells subcommand (#4039)
* Add `-c` flag to `update cells` subcommand

* Fix lints
2021-09-27 21:18:50 -05:00
4e0d7bc77c Less deps (#4038)
* compiles on nightly now. (breaking change)

* less deps

* Switch over to new resolver

(it's been stable for a while.)

* let's leave num-format for another PR
2021-09-28 07:17:00 +13:00
32581497ef Fix 90 degrees tables problem (#4043)
* fix 90 degrees tables problem

* linting

* clippy

* linting
2021-09-25 14:05:45 -05:00
d6df367c6b Corrected typo (#4040)
It is not BSON but SQLite
2021-09-25 04:25:00 -05:00
4e6327de1d Added BigInt handling to the delimited file format for the 'to' command (#4034)
Co-authored-by: patrick <patrick@spol42069.hitronhub.home>
2021-09-25 09:47:16 +12:00
b3d8666db0 compiles on nightly now. (breaking change) (#4037) 2021-09-25 09:46:48 +12:00
1de7c3d033 Scraping multiple tables (#4036)
* Output error when ls into a file without permission

* math sqrt

* added test to check fails when ls into prohibited dir

* fix lint

* math sqrt with tests and doc

* trigger wasm build

* Update filesystem_shell.rs

* Fix Running echo .. starts printing integers forever

* Allow for multiple table scraping

* linting

* Fix clippy

* linting

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2021-09-24 08:08:13 -05:00
962b258cc6 merge span (#4031) 2021-09-23 07:48:05 +12:00
59697cab63 force rebuild of dev container (#4033) 2021-09-23 07:47:28 +12:00
349af05da8 Do not throw error for files not found in lib_dirs (#4029) 2021-09-20 13:44:47 -05:00
JT
b3b3cf0689 Remove the docker instructions
Docker has been out of date for a long time, go ahead and remove.
2021-09-20 19:33:49 +12:00
5d59234f8d Flexibility updating table's cells. (#4027)
Very often we need to work with tables (say extracted from unstructured data or some
kind of final report, timeseries, and the like).

It's inevitable we will be having columns that we can't know beforehand what their names
will be, or how many.

Also, we may end up with certain cells having values we may want to remove as we explore.

Here, `update cells` fundamentally goes over every cell in the table coming in and updates
the cell's contents with the output of the block passed. Basic example here:

```
> [

    [   ty1,       t2,       ty];

    [     1,        a, $nothing]
    [(wrap), (0..<10),      1Mb]
    [    1s,     ({}),  1000000]
    [ $true,   $false,   ([[]])]

] | update cells { describe }

───┬───────────────────────┬───────────────────────────┬──────────
 # │          ty1          │            t2             │    ty
───┼───────────────────────┼───────────────────────────┼──────────
 0 │ integer               │ string                    │ nothing
 1 │ row Column(table of ) │ range[[integer, integer)] │ filesize
 2 │ string                │ nothing                   │ integer
 3 │ boolean               │ boolean                   │ table of
───┴───────────────────────┴───────────────────────────┴──────────
```

and another one (in the examples) for cases, say we have a timeseries table generated and
we want to remove the zeros and have empty strings and save it out to something like CSV.

```
> [
    [2021-04-16, 2021-06-10, 2021-09-18, 2021-10-15, 2021-11-16, 2021-11-17, 2021-11-18];
    [        37,          0,          0,          0,         37,          0,          0]
] | update cells {|value| i
  if ($value | into int) == 0 {
    ""
  } {
    $value
  }
}

───┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────
 # │ 2021-04-16 │ 2021-06-10 │ 2021-09-18 │ 2021-10-15 │ 2021-11-16 │ 2021-11-17 │ 2021-11-18
───┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────
 0 │         37 │            │            │            │         37 │            │
───┴────────────┴────────────┴────────────┴────────────┴────────────┴────────────┴────────────
```
2021-09-19 15:37:54 -05:00
Tw
4f7b423f36 Support completion when cursor inside an argument (#4023)
* Support completion when cursor inside an argument

Bash supports completion even when cursor is in an argument, this is very useful for some fixup after the initial completion.
Let add this feature as well.

Signed-off-by: Tw <wei.tan@intel.com>

* Add test for when cursor inside an argument

To support test this case, let's also take the position into account.

Signed-off-by: Tw <wei.tan@intel.com>
2021-09-19 17:23:05 +12:00
f7043bf690 Fix #3090: let binding in command leaks when error occurs (#4022) 2021-09-19 14:57:20 +12:00
Tw
1297499d7a add command g to switch shell quickly (#4014)
Signed-off-by: Tw <tw19881113@gmail.com>
2021-09-17 10:39:14 +01:00
bd0baa961c add table selector for downloading web tables (#4004)
* add table selector for downloading web tables

* type-o

* updated debug mode to inspect mode
2021-09-16 09:02:30 -05:00
4ee536f044 fix: enable SIMD (#4021) 2021-09-16 20:01:42 +12:00
JT
8581bec891 bump 0.37.1 (#4019) 2021-09-16 13:32:22 +12:00
8bcbc8eeb3 Move nu-path tests to integration tests (#4015)
* Move nu-path tests to integration tests

To prevent circular dependency between nu-path and nu-test-support crates.

* Fmt
2021-09-16 07:11:28 +12:00
c164ef5489 Update to polars 0.16 (#4013)
* update to polars 0.16

* enabled features for polars
2021-09-16 07:10:12 +12:00
cc3653cfd9 Path commands: Put column path args behid flag; Allow path join appending without flag (#4008)
* Change path join signature

* Appending now works without flag
* Column path operation is behind a -c flag

* Move column path arg retrieval to a function

Also improves errors

* Fix path join tests

* Propagate column path changes to all path commands

* Update path command examples with columns paths

* Modernize path command examples by removing "echo"

* Improve structured path error message

* Fix typo
2021-09-15 21:03:51 +03:00
JT
7fc65067cf Temporarily remove the circular dep (#4009) 2021-09-15 09:17:31 +12:00
JT
f9ae882012 Update main.wxs (#4007)
Remove references to the old binaries
2021-09-15 07:45:30 +12:00
JT
1d80a68f4c bump to 0.37 (#4006) 2021-09-15 06:44:24 +12:00
fda69354db change name to command_prompt (#4003) 2021-09-14 08:02:10 +12:00
cc5c4d38bb Small fixes and refactors to paths & source command (#3998)
* Expand path when converting value -> PathBuf

Also includes Tagged<PathBuf>.

Fixes #3605

* Expand path for PATH env. variable

Fixes #1834

* Remove leftover Cows after nu-path refactor

There were some unnecessary Cow conversions leftover from the old
nu-path implementation.

* Use canonicalize in source command; Improve errors

Previously, `source` used `expand_path()` which does not follow
symlinks.

As a follow up, I improved the source error messages so they now tell
why the source file could not be canonicalized or read into string.
2021-09-12 02:36:14 +03:00
JT
0fa0c25fb3 Fix clippy warnings (#3997) 2021-09-10 13:13:11 +12:00
55eafadf02 Improve error message when bash-style alias syntax is mistakenly used (#3995) 2021-09-10 10:44:55 +12:00
51c74eebd0 Add general refactorings (#3996) 2021-09-10 10:44:22 +12:00
Tw
ae9f4135c0 support appending when saving file (#3992)
This patch implements `>>` operation in bash.

Signed-off-by: Tw <tw19881113@gmail.com>
2021-09-05 06:12:08 +12:00
4e2d3ceaaf Allow knowing the command name tag given no input. (#3988)
```
tags
```
2021-09-03 01:46:15 -05:00
c9c6bd4836 Create errors from tables. (#3986)
```
> [
  [          msg,                 labels,                      span];
  ["The message", "Helpful message here", ([[start, end]; [0, 141]])]
] | error make

error: The message
  ┌─ shell:1:1
  │
1 │ ╭ [
2 │ │   [          msg,                 labels,                      span];
3 │ │   ["The message", "Helpful message here", ([[start, end]; [0, 141]])]
  │ ╰─────────────────────────────────────────────────────────────────────^ Helpful message here
```

Adding a more flexible approach for creating error values. One use case, for instance is the
idea of a test framework. A failed assertion instead of printing to the screen it could create
tables with more details of the failed assertion and pass it to this command for making a full
fledge error that Nu can show. This can (and should) be extended for capturing error values as well
in the pipeline. One could also use it for inspection.

For example: `.... | error inspect { # inspection here }`

or "error handling" as well, like so: `.... | error capture { fix here }`

However, we start here only with `error make` that creates an error value for you with limited support for the time being.
2021-09-02 21:07:26 -05:00
d90420ac4c Add subcommand into filesize (#3987)
* Add subcommand `into filesize`

It's currently not possible to convert a number or a string containing a number
into a filesize. The only way to create an instance of filesize type today is
with a literal in nushell syntax. This commit adds the `into filesize`
subcommand so that file sizes can be created from the outputs of programs
producing numbers or strings, like standard unix tools.

There is a limitation with this - it doesn't currently parse values like `10 MB`
or `10 MiB`, it can only look at the number itself. If the desire is there, more
flexible parsing can be added.

* fixup! Add subcommand `into filesize`

* fixup! Add subcommand `into filesize`
2021-09-02 18:19:54 -05:00
260ff99710 feat: spawn the executables directly if possible (#3974)
* feat: spawn the executables directly if possible

This pull request changes nu-command so that it spawns the process directly if:
- They are a `.exe` on Windows
- They are not a `.sh` or `.bash` on not windows.

Benefits:
- As I explained in [this comment](https://github.com/nushell/nushell/issues/3898#issuecomment-894000812), this is another step towards making Nushell a standalone shell, that doesn't need to shell out unless it is running a script for a particular shell (cmd, sh, ps1, etc.).
- Fixes the bug with multiline strings
- Better performance due to direct spawning.

For example, this script shows ~20 ms less latency.
After:
```nu
C:\> benchmark { node -e 'console.log("sssss")' }
───┬──────────────────
 # │    real time
───┼──────────────────
 0 │ 63ms 921us 600ns
───┴──────────────────
```
Before
```nu
C:\> benchmark { node -e 'console.log("sssss")' }
───┬──────────────────
 # │    real time
───┼──────────────────
 0 │ 79ms 136us 800ns
───┴──────────────────
```

Fixes #3898

* fix: make which dependency optional

Also fixes clippy warnings

* refactor: refactor spawn_exe, spawn_cmd, spawn_sh, and spawn_any

* fix: use which feature instead of which-support

* fix: use which_in to use the cwd of nu

* fix: use case insensitive comparison of the extensions

Sometimes the case of the extension is uppercased by the "which_in" function

Also use unix instead of not windows. Some os might not have sh support
2021-09-01 09:38:52 -05:00
JT
08014c6a98 Move sys, ps, fetch, post to internal commands (#3983)
* Move sys, ps, fetch, post to internal commands

* Remove old plugins

* clippy

Co-authored-by: JT <jonatha.d.turner@gmail.com>
2021-09-01 14:29:09 +12:00
66cedf0b3a Update char_.rs (#3975)
added a few more chars and abbreviations
2021-08-29 08:40:28 -05:00
707a4ebc15 added more escapes to support ansi art (#3973)
* added more escapes to support ansi art

* fixed some bugs
2021-08-28 14:58:59 -05:00
d95375d494 nu-path crate refactor (#3730)
* Resolve rebase artifacts

* Remove leftover dependencies on removed feature

* Remove unnecessary 'pub'

* Start taking notes and fooling around

* Split canonicalize to two versions; Add TODOs

One that takes `relative_to` and one that doesn't.
More TODO notes.

* Merge absolutize to and rename resolve_dots

* Add custom absolutize fn and use it in path expand

* Convert a couple of dunce::canonicalize to ours

* Update nu-path description

* Replace all canonicalize with nu-path version

* Remove leftover dunce dependencies

* Fix broken autocd with trailing slash

Trailing slash is preserved *only* in paths that do not contain "." or
"..". This should be fixed in the future to cover all paths but for now
it at least covers basic cases.

* Use dunce::canonicalize for canonicalizing

* Alow cd recovery from non-existent cwd

* Disable removed canonicalize functionality tests

Remove unused import

* Break down nu-path into separate modules

* Remove unused public imports

* Remove abundant cow mapping

* Fix clippy warning

* Reformulate old canonicalize tests to expand_path

They wouldn't work with the new canonicalize.

* Canonicalize also ~ and ndots; Unify path joining

Also, add doc comments in nu_path::expansions.

* Add comment

* Avoid expanding ndots if path is not valid UTF-8

With this change, no lossy path->string conversion should happen in the
nu-path crate.

* Fmt

* Slight expand_tilde refactor; Add doc comments

* Start nu-path integration tests

* Add tests TODO

* Fix docstring typo

* Fix some doc strings

* Add README for nu-path crate

* Add a couple of canonicalize tests

* Add nu-path integration tests

* Add trim trailing slashes tests

* Update nu-path dependency

* Remove unused import

* Regenerate lockfile
2021-08-28 15:59:09 +03:00
1c1c58e802 Remove duplicate dependencies (#3961)
* chore: Replace surf with reqwest

Removes a lot of older, duplication versions of some dependencies
(roughtly 90 dependencies removed in total)

* chore: Remove syn 0.11

* chore: Remove unnecessary features from ptree

Removes some more duplicate dependencies

* cargo update

* Ensure we run the fetch and post plugins on the tokio runtime

* Fix clippy warning

* fix: Github requires a user agent on requests

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2021-08-28 15:34:11 +12:00
JT
7fe05b8296 bump to 0.36.1 (#3972) 2021-08-27 20:48:58 +12:00
17ef531905 introducing the find command (#3971)
* introducing the `find` command

* added tests

* merged main to accomodate "rest" changes

* test fix
2021-08-27 20:48:41 +12:00
b8e2bdd6b1 Allow different names for ...rest (#3954)
* Allow different names for ...rest

* Resolves #3945

* This change requires an explicit name for the rest argument in `WholeStreamCommand`,
  which is why there are so many changed files.

* Remove redundant clone

* Add tests
2021-08-27 05:58:53 +12:00
88817a8f10 Allow environment variables to be hidden (#3950)
* Allow environment variables to be hidden

This change allows environment variables in Nushell to have a value of
`Nothing`, which can be set by the user by passing `$nothing` to
`let-env` and friends.

Environment variables with a value of Nothing behave as if they are not
set at all. This allows a user to shadow the value of an environment
variable in a parent scope, effectively removing it from their current
scope. This was not possible before, because a scope can not affect its
parent scopes.

This is a workaround for issues like #3920.

Additionally, this allows a user to simultaneously set, change and
remove multiple environment variables via `load-env`. Any environment
variables set to $nothing will be hidden and thus act as if they are
removed. This simplifies working with virtual environments, which rely
on setting multiple environment variables, including PATH, to specific
values, and remove/change them on deactivation.

One surprising behavior is that an environment variable set to $nothing
will act as if it is not set when querying it (via $nu.env.X), but it is
still possible to remove it entirely via `unlet-env`. If the same
environment variable is present in the parent scope, the value in the
parent scope will be visible to the user. This might be surprising
behavior to users who are not familiar with the implementation details.

An additional corner case is the the shorthand form of `with-env` does
not work with this feature. Using `X=$nothing` will set $nu.env.X to the
string "$nothing". The long-form works as expected: `with-env [X
$nothing] {...}`.

* Remove unused import

* Allow all primitives to be convert to strings
2021-08-26 08:15:58 -05:00
3e8ce43dcb rename command and rename for melt (#3968) 2021-08-26 08:13:54 -05:00
9d8845d7ad Allow custom lib dir path for sourcing nu script libraries. (#3940)
Given we can write nu scripts. As the codebase grows, splitting into many smaller nu scripts is necessary.

In general, when we work with paths and files we seem to face quite a few difficulties. Here we just tackle one of them and it involves sourcing
files that also source other nu files and so forth. The current working directory becomes important here and being on a different directory
when sourcing scripts will not work. Mostly because we expand the path on the current working directory and parse the files when a source command
call is done.

For the moment, we introduce a `lib_dirs` configuration value and, unfortunately, introduce a new dependency in `nu-parser` (`nu-data`) to get
a handle of the configuration file to retrieve it. This should give clues and ideas as the new parser engine continues (introduce a way to also know paths)

With this PR we can do the following:

Let's assume we want to write a nu library called `my_library`. We will have the code in a directory called `project`: The file structure will looks like this:

```
project/my_library.nu
project/my_library/hello.nu
project/my_library/name.nu
```

This "pattern" works well, that is, when creating a library have a directory named `my_library` and next to it a `my_library.nu` file. Filling them like this:

```

source my_library/hello.nu
source my_library/name.nu
```

```

def hello [] {
  "hello world"
}
```

```

def name [] {
  "Nu"
end
```

Assuming this `project` directory is stored at `/path/to/lib/project`, we can do:

```
config set lib_dirs ['path/to/lib/project']
```

Given we have this `lib_dirs` configuration value, we can be anywhere while using Nu and do the following:

```
source my_library.nu

echo (hello) (name)

```
2021-08-26 02:04:04 -05:00
52578ba483 tweak the version | pivot instructions (#3964) 2021-08-24 19:03:07 -05:00
JT
991a4801b1 Bump to 0.36 (#3963) 2021-08-25 06:01:17 +12:00
02b2c55146 Rolling and cumulative commands (#3960)
* rolling and cumulative operations

* update polars to 0.15.1

* change reference in function
2021-08-24 09:10:29 -05:00
0abe753003 update the config part (#3962) 2021-08-24 08:58:17 -05:00
JT
487fafbca3 Add a 'tutor' command (#3949)
* Add a 'tutor' command

* clippy
2021-08-21 19:41:54 +12:00
188a352c6f added help --find to search usage and extra_usage text for strings (#3948)
* added help --find to search usage and extra_usage text for strings

* changed my mind
2021-08-20 17:55:56 -05:00
e11b400a75 Allow source to accept paths with emojis (#3939)
* Allow sourcing paths with emojis

* Add source command tests for emoji paths

* Fmt

* Disable source tests on Windows with illegal paths

* Test sourcing also ASCII and single-quoted paths
2021-08-19 19:06:18 +12:00
6db5692be4 Only allow unaliasing in current scope, add tests (#3936)
* unalias only removes aliases in the current scope

* Add a test and fix previous ones which did not function as expected
2021-08-19 19:05:36 +12:00
JT
ead4029d49 Bump rustyline and add unalias test (#3935) 2021-08-18 05:55:34 +12:00
9bd408449e Add the ability to remove and list aliases (#3879)
* Add the ability to remove and list aliases

* Fix failing unit tests

* Add a test to check unalias shadowing blocks
2021-08-17 08:56:35 -05:00
JT
2b7390c2a1 Switch back to building for size (#3924) 2021-08-17 08:45:39 +12:00
ab961a78cb Add trailing slash in completion of symlinked dir (#3921) 2021-08-17 07:13:59 +12:00
65c639cf13 allow fetch to follow redirects (#3923) 2021-08-16 12:31:02 -05:00
0cf5dc11e3 Fix 'Inimplemented' typo. (#3922) 2021-08-16 09:17:31 -05:00
b873fa7a5f The zip command. (#3919)
We introduce it here and allow it to work with regular lists (tables with no columns) as well as symmetric tables. Say we have two lists and wish to zip them, like so:

```
[0 2 4 6 8] | zip {
  [1 3 5 7 9]
} | flatten

───┬───
 0 │ 0
 1 │ 1
 2 │ 2
 3 │ 3
 4 │ 4
 5 │ 5
 6 │ 6
 7 │ 7
 8 │ 8
 9 │ 9
───┴───
```

In the case for two tables instead:

```
[[symbol]; ['('] ['['] ['{']] | zip {
  [[symbol]; [')'] [']'] ['}']]
} | each {
  get symbol | $'($in.0)nushell($in.1)'
}

───┬───────────
 0 │ (nushell)
 1 │ [nushell]
 2 │ {nushell}
───┴───────────
```
2021-08-14 23:36:08 -05:00
ee563ecf4e PROMPT_STRING env variable (#3918)
* prompt string env variable

* cargo clippy
2021-08-15 06:14:14 +12:00
183b35d683 Rustyline bug fixes (#3916)
* Mitigate history file bug in Rustyline

Rustyline's duplicate ignoring code has a bug that can cause data loss and
history file corruption. Testing seems to indicate that disabling this behavior
and allowing duplicates will prevent the bug from showing up. Many people have
complained about this issue, I think it is worthwhile to fix the bug at the cost
of permitting duplicate history entries.

Upstream bug: https://github.com/kkawakam/rustyline/issues/559

* Increase Rustyline historyfile limit

Rustyline will only store 100 history items by default. This is quite a small
limit for a shell that people use as a daily driver. Especially when the
deduplication code is removed, we will hit that limit quickly and start to lose
history. This commit bumps the limit up to 10k. We can discuss if this is an
inappropriate limit or if we should allow users to specify this setting in their
nushell config file instead.
2021-08-14 06:56:28 +12:00
463dd48180 Flexible dropping of rows (by desired row number) (#3917)
We very well support `nth 0 2 3 --skip 1 4` to select particular rows and skip some using a flag. However, in practice we deal with tables (whether they come from parsing or loading files and whatnot) where we don't know the size of the table up front (and everytime we have these, they may have different sizes). There are also other use cases when we use intermediate tables during processing and wish to always drop certain rows and **keep the rest**.

Usage:

```
... | drop nth 0
... | drop nth 3 8
```
2021-08-13 12:48:05 -05:00
1bd3fdd912 upgrade shadow-rs 0.6.8 (#3914) 2021-08-13 06:03:50 +12:00
de71cbdd43 document engine-p porting (#3868)
* document engine-p porting

See #3390 for all the details.

* use static numbering
2021-08-08 05:57:32 +12:00
c9b87c4c03 Count the size of the directories when calculating the size in DirInfo (#3902)
* take dir entry size into consideration when calculting the size of a directory in DirInfo

* fmt check
2021-08-08 05:50:02 +12:00
38848082ae describe command (#3907) 2021-08-08 05:48:54 +12:00
JT
b6728efcd4 in/not-in for strings (#3906) 2021-08-07 09:49:37 +12:00
cd814851da Use bigdecimal-rs patch (#3905)
* Use bigdecimal-rs patch

* fix nu-serde's bigdecimal dependency
2021-08-07 09:27:19 +12:00
6646daab45 source config from $NU_CONFIG_DIR if it exists (#3883) 2021-08-06 11:47:11 -05:00
ba483155d7 Reimplement parsers with nu-serde (#3880)
The nu-serde crate allows us to become much more generic with respect to how we
convert output to `nu-protocol::Value`s. This allows us to remove a lot of the
special-case code that we wrote for deserializing JSON values.

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2021-08-06 11:46:19 -05:00
63abe1cb3e Datetime commands (#3894)
* date and duration from nu

* date commands

* Import to feature flag

* corrected to-csv example

* corrected sample example
2021-08-05 17:18:53 -05:00
28db8022fe Update README.md
added integrations and mentions in supported section
2021-08-05 07:35:22 -05:00
7dcc08985c Implement PartialEq for ReturnSuccess (#3888)
This makes ReturnValue and ReturnSuccess usable in assert_eq!.
2021-08-05 14:55:41 +12:00
55acdaaf8c add officially supported by section (#3895) 2021-08-04 15:57:57 -05:00
575c07c9c4 Normalize capitalization in issue templates (#3891)
* Normalize capitalization in feature_request.yml

* Normalize capitalization in bug_report.yml
2021-08-03 18:49:03 -05:00
325f45fa66 Fix typo: patter → pattern (#3890) 2021-08-03 18:48:23 -05:00
JT
bc682066d8 Bump to 0.35 (#3884) 2021-08-03 20:01:09 +12:00
0a1cdc5107 Add the nu-serde crate (#3878)
* Add the nu-serde crate

nu-serde is a crate that can be used to turn a value implementing
`serde::Serialize` into a `nu-protocol::Value`. This has the potential to
significantly simplify plugin authorship.

This crate was the previously independent
[serde-nu](https://github.com/lily-mara/serde-nu) but the nushell maintainers
expressed an interest in having it added to the mainline nushell repository.

* fixup! Add the nu-serde crate
2021-07-31 22:03:13 -05:00
6984185e61 Better representation in nested dataframes (#3875)
* better dataframe representation in nested df

* Error message correction
2021-07-31 09:02:32 -05:00
5826126284 simple contains arguments (#3874) 2021-07-31 09:01:05 -05:00
762e528ec5 Support equals sign in shorthand environment variable values (#3869)
Some environment variables, such as `RUST_LOG` include equals signs. Nushell
should support this in the shorthand environment variable syntax so that
developers using these variables can control them easily. We accomplish this by
swapping `std::str::split` for `std::str::splitn`, which ensures that we only
consider the first equals sign in the string instead of all of them, which we
did previously.

Closes #3867
2021-07-31 09:10:28 +12:00
JT
c3de9848b4 Revert "Unify use of the surf crate (#3855)" (#3871)
This reverts commit 7f7af2bbaa.
2021-07-31 09:04:01 +12:00
370ae8c20c Add support for mult-doc YAML files (#3870)
Single doc YAML files are returned as before. Multi-doc YAML files are
wrapped in an outer Table. Empty YAML files return Nothing.
2021-07-30 15:45:19 -05:00
69083bfca0 Adding parse fix for power operator error on negative integers and de… (#3821)
* Adding parse fix for power operator error on negative integers and decimal

* Adding correct formatting

* Changed is negative check to follow conventions

* Adding tests

* Added fix for the negatives and added tests

* Removed comments
2021-07-30 07:08:57 -05:00
1e15f26e98 fix interpolated strings when using unicode (#3866)
* fix interpolated strings when using unicode

* added test case
2021-07-29 19:07:34 -05:00
23ba01d89c add performance metrics for measuring startup time (#3854)
* add performance metrics for measureing startup time

* removed some comments

* update so tests pass

* update default.toml for tests, merged main

* fix clippy lints

* wording changes
2021-07-29 18:52:40 -05:00
653cbe651f Going deeper (#3864)
* nuframe in its own type in UntaggedValue

* Removed eager dataframe from enum

* Dataframe created from list of values

* Corrected order in dataframe columns

* Returned tag from stream collection

* Removed series from dataframe commands

* Arithmetic operators

* forced push

* forced push

* Replace all command

* String commands

* appending operations with dfs

* Testing suite for dataframes

* Unit test for dataframe commands

* improved equality for dataframes

* moving all dataframe operations to protocol

* objects in dataframes

* Removed cloning when converting to row
2021-07-30 09:16:30 +12:00
JT
f3e487e829 Add named positionals to all (#3863) 2021-07-30 09:12:24 +12:00
9c016ad479 document compiling without openssl (#3862) 2021-07-30 08:21:20 +12:00
JT
e602647d4d Fix clippy lint and disable broken lint (#3865) 2021-07-30 08:11:47 +12:00
9696e4d315 Improve md5 and sha256 code (#3841)
* Refactor Hash code to simplify md5 and sha256 implementations

Md5 and Sha256 (and other future digests) require less boilerplate code
now. Error reporting includues the name of the hash again.

* Add missing hash sha256 test
2021-07-29 10:22:16 -05:00
7f7af2bbaa Unify use of the surf crate (#3855)
This brings the features used by the `nu_plugin_fetch` and
`nu_plugin_post` in line and drops the default-features, reducing
the number of pulled-in dependencies and avoiding a second round of
compilations.

Retry of #3777 but with different features, post and fetch plugin tested

Signed-off-by: Daniel Egger <daniel@eggers-club.de>
2021-07-29 19:26:38 +12:00
b190051e15 Fix select to insert nulls in sparse tables instead of ignoring absent values (#3857)
Select used to ignore absent values resulting in "squashing" where
columns for multiple rows could be squashed into a single row.

This fixes the problem by inserting null/nothing into absent value, thus
preserving the structure of rows. This makes sure that all selected
columns are present in each row.
2021-07-28 16:39:42 -05:00
83b28cad8d Remove dependencies (#3853)
* nuframe in its own type in UntaggedValue

* Removed eager dataframe from enum

* Dataframe created from list of values

* Corrected order in dataframe columns

* Returned tag from stream collection

* Removed series from dataframe commands

* Arithmetic operators

* forced push

* forced push

* Replace all command

* String commands

* appending operations with dfs

* Testing suite for dataframes

* Unit test for dataframe commands

* improved equality for dataframes

* moving all dataframe operations to protocol
2021-07-27 14:20:06 -05:00
ea42a84a4a Update implementing_a_command.md (#3848)
+  nu-command/src/command.rs has been modified to nu-command/src/mod.rs in nowable version
2021-07-27 09:03:52 -05:00
e4c282f0a6 added the ability to compare time units like 1hr < 2hr (#3845) 2021-07-26 15:19:32 -05:00
d54d7cc431 append dataframes (#3839) 2021-07-26 08:36:09 +12:00
111477aa74 Add sha256 to the hash command (#3836)
Hashers now uses on Rust Crypto Digest trait which makes it trivial to
implement additional hash functions.

The original `md5` crate does not implement the Digest trait and was
replaced by `md-5` crate which does. Sha256 uses already included `sha2`
crate.
2021-07-25 14:08:08 -05:00
JT
226739d13f Bump to 0.34.1 (#3835) 2021-07-25 22:58:33 +12:00
f1ee9113ac All is a DataFrame (#3812)
* nuframe in its own type in UntaggedValue

* Removed eager dataframe from enum

* Dataframe created from list of values

* Corrected order in dataframe columns

* Returned tag from stream collection

* Removed series from dataframe commands

* Arithmetic operators

* forced push

* forced push

* Replace all command

* String commands

* appending operations with dfs

* Testing suite for dataframes

* Unit test for dataframe commands

* improved equality for dataframes
2021-07-25 22:01:54 +12:00
9120a64cfb use chrono_humanize for datetime formatting (#3834)
* use chrono_humanize for datetime formatting

* fix tests
2021-07-25 20:38:45 +12:00
fcd624a722 add date humanize command (#3833)
* add `date humanize` command

* add docs
2021-07-25 17:33:31 +12:00
e6af7f75a1 Read from standard input in rm (#3763)
* Read from standard input in `rm`

With this change, rm falls back to reading from the standard input if no
arguments are supplied. This leads to more intuitive pipes as seen in
https://github.com/nushell/nushell/issues/2824

 ls | get name | sort-by | first 20 | each {rm $it} becomes
 ls | get name | sort-by | first 20 | rm

* [Fix] Run cargo fmt, make files a rest parameter, and fix cargo build warnings

* [Fix] Fix clippy suggestions
2021-07-25 15:01:53 +12:00
27f1e7b60c change describe so it doesn't output colored strings (#3832) 2021-07-25 06:34:11 +12:00
2e24de7f47 Support other variables than PATH in pathvar (2nd attempt) (#3828)
* Fix swapped PATH env var separators

* Support pathvar to manipulate other vars than PATH

* Add tests for pathvar and its subcommands

* Adjust pathvar tests to comply with env quirks

* Make pathvar tests work on non-Windows as well

* Compact the comments in pathvar tests

* Fix last failing test

Co-authored-by: Jakub Žádník <jakub.zadnik@tuni.fi>
2021-07-24 11:44:36 -05:00
e514204db0 Fix wrong path separator (#3829) 2021-07-24 08:42:50 -05:00
0f9e55dac6 Revert "Support other variables than PATH in pathvar (#3791)" (#3827)
This reverts commit f9f39c0a1c.
2021-07-23 09:03:28 -05:00
5d7677dd07 Implement into path conversion (#3811)
This allows converting strings to filepaths without having to use
`path expand` roundtrip.

Filepaths are taken as-is without any validation/conversion.
2021-07-23 19:14:02 +12:00
57073cc6cf port capitalize to engine-p (#3794)
Part of #3390.
2021-07-23 19:13:11 +12:00
f9f39c0a1c Support other variables than PATH in pathvar (#3791)
* Fix swapped PATH env var separators

* Support pathvar to manipulate other vars than PATH

* Add tests for pathvar and its subcommands

* Fix PATH env name for Windows

Seems like Windows uses PATH as well.

Co-authored-by: Jakub Žádník <jakub.zadnik@tuni.fi>
2021-07-23 19:11:56 +12:00
d88d7f26e4 fix typo in release.yml (#3824) 2021-07-22 12:42:11 -05:00
aeaedd2e5e Convert templates to github forms (#3818)
* Create feature_request.yaml

* Rename feature_request.yaml to feature_request.yml

* Update feature_request.yml

* Update feature_request.yml

* Create bug_report.yml

* Update bug_report.yml

* Update bug_report.yml

* Update feature_request.yml

* Delete bug_report.md

* Delete feature_request.md
2021-07-23 05:39:20 +12:00
1f4ef3b606 Add worflow to publish package in winget (#3819)
Remove the job in release workflow to publish to winget.
Trigger winget workflow on published release.

Co-authored-by: Alexandre Nedelec <Alexandre.Nedelec@azeo.com>
2021-07-22 11:38:03 -05:00
9b5db297a6 Replace command (#3823)
* replace command

* cargo fmt

* Signature correction
2021-07-22 08:45:46 -05:00
b7215b5dde Fix expected age of mockup files in example tests (#3808) 2021-07-20 18:05:58 -05:00
411435d68f Dataframe Shape command (#3805)
* size command to get dataframe info

* change command name to shape

* apply lint to file
2021-07-20 07:07:42 -05:00
f656f906ff Update stale.yml
update days-before-issue-stale to 90 days
2021-07-19 15:03:24 -05:00
d0a7363e64 bat theme wasn't getting set properly (#3807) 2021-07-19 14:54:36 -05:00
7401fa2fa5 Fix docs for the config variable completion_type (#3804) 2021-07-19 07:20:56 -05:00
181ee1dade Revert "Unify use of the surf crate (#3777)" (#3783)
This reverts commit 37612345f2.
2021-07-14 20:21:18 -05:00
3645a0f0e4 Updated polars version for faster CSV reader (#3781) 2021-07-14 15:33:21 -05:00
2864eaebae fixed show_hints option to allow hints to be turned off (#3780) 2021-07-14 09:47:33 -05:00
37612345f2 Unify use of the surf crate (#3777)
This brings the features used by the `nu_plugin_fetch` and
`nu_plugin_post` in line and drops the default-features, reducing
the number of pulled-in dependencies and avoiding a second round of
compilations.

Signed-off-by: Daniel Egger <daniel@eggers-club.de>
2021-07-14 08:47:49 -05:00
bb218b824e corrected position of dataframes (#3776) 2021-07-14 08:46:32 -05:00
279329bfaa Add the -s parameter to submit package to winget in pipeline (#3767)
Co-authored-by: Alexandre Nedelec <Alexandre.Nedelec@azeo.com>
2021-07-13 18:35:56 -05:00
520 changed files with 18533 additions and 8542 deletions

View File

@ -16,7 +16,7 @@ strategy:
image: ubuntu-18.04
style: 'wasm'
macos-stable:
image: macos-10.14
image: macOS-10.15
style: 'unflagged'
windows-stable:
image: windows-2019
@ -49,7 +49,7 @@ steps:
- bash: RUSTFLAGS="-D warnings" cargo test --all
condition: eq(variables['style'], 'unflagged')
displayName: Run tests
- bash: RUSTFLAGS="-D warnings" cargo clippy --all -- -D clippy::unwrap_used
- bash: RUSTFLAGS="-D warnings" cargo clippy --all -- -D clippy::unwrap_used -A clippy::needless_collect
condition: eq(variables['style'], 'unflagged')
displayName: Check clippy lints
- bash: cd samples/wasm && npm install wasm-pack && node ./node_modules/wasm-pack/run.js build

View File

@ -1,47 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1.
2.
3.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Configuration (please complete the following information):**
Run `version | pivot` and paste the output to show OS, features, etc.
```
> version | pivot
╭───┬────────────────────┬───────────────────────────────────────────────────────────────────────╮
│ # │ Column0 │ Column1 │
├───┼────────────────────┼───────────────────────────────────────────────────────────────────────┤
│ 0 │ version │ 0.24.1 │
│ 1 │ build_os │ macos-x86_64 │
│ 2 │ rust_version │ rustc 1.48.0 │
│ 3 │ cargo_version │ cargo 1.48.0 │
│ 4 │ pkg_version │ 0.24.1 │
│ 5 │ build_time │ 2020-12-18 09:54:09 │
│ 6 │ build_rust_channel │ release │
│ 7 │ features │ ctrlc, default, directories, dirs, git, ichwh, rich-benchmark, │
│ │ │ rustyline, term, uuid, which, zip │
╰───┴────────────────────┴───────────────────────────────────────────────────────────────────────╯
```
**Add any other context about the problem here.**

63
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@ -0,0 +1,63 @@
name: Bug Report
description: Create a report to help us improve
body:
- type: textarea
id: description
attributes:
label: Describe the bug
description: Thank you for your bug report. We are working diligently with our community to integrate our latest code base that we call [engine-q](https://github.com/nushell/engine-q). We would like your help with this by checking to see if this bug report is still needed in engine-q. Thank you for your patience while we ready the next version of nushell.
validations:
required: true
- type: textarea
id: repro
attributes:
label: How to reproduce
description: Steps to reproduce the behavior
placeholder: |
1.
2.
3.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
placeholder: I expected nu to...
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: Please add any relevant screenshots here, if any
validations:
required: false
- type: textarea
id: config
attributes:
label: Configuration
description: "Please run `version | pivot key value | to md --pretty` and paste the output to show OS, features, etc."
placeholder: |
> version | pivot key value | to md --pretty
| key | value |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| version | 0.40.0 |
| build_os | linux-x86_64 |
| rust_version | rustc 1.56.1 |
| cargo_version | cargo 1.56.0 |
| pkg_version | 0.40.0 |
| build_time | 1980-01-01 00:00:00 +00:00 |
| build_rust_channel | release |
| features | clipboard-cli, ctrlc, dataframe, default, rustyline, term, trash, uuid, which, zip |
| installed_plugins | binaryview, chart bar, chart line, fetch, from bson, from sqlite, inc, match, post, ps, query json, s3, selector, start, sys, textview, to bson, to sqlite, tree, xpath |
validations:
required: false
- type: textarea
id: context
attributes:
label: Additional context
description: Add any other context about the problem here.
validations:
required: false

View File

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,34 @@
name: Feature Request
description: "When you want a new feature for something that doesn't already exist"
body:
- type: textarea
id: problem
attributes:
label: Related problem
description: Thank you for your feature request. We are working diligently with our community to integrate our latest code base that we call [engine-q](https://github.com/nushell/engine-q). We would like your help with this by checking to see if this feature request is still needed in engine-q. Thank you for your patience while we ready the next version of nushell.
placeholder: |
A clear and concise description of what the problem is.
Example: I am trying to do [...] but [...]
validations:
required: false
- type: textarea
id: desired
attributes:
label: "Describe the solution you'd like"
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: "Describe alternatives you've considered"
description: "A clear and concise description of any alternative solutions or features you've considered."
validations:
required: false
- type: textarea
id: context
attributes:
label: Additional context and details
description: Add any other context or screenshots about the feature request here.
validations:
required: false

View File

@ -1,118 +0,0 @@
name: Publish consumable Docker images
on:
push:
tags: ['v?[0-9]+.[0-9]+.[0-9]+*']
jobs:
compile:
runs-on: ubuntu-latest
strategy:
matrix:
arch:
- x86_64-unknown-linux-musl
- x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v2
- name: Install rust-embedded/cross
env: { VERSION: v0.1.16 }
run: >-
wget -nv https://github.com/rust-embedded/cross/releases/download/${VERSION}/cross-${VERSION}-x86_64-unknown-linux-gnu.tar.gz
-O- | sudo tar xz -C /usr/local/bin/
- name: compile for specific target
env: { arch: '${{ matrix.arch }}' }
run: |
cross build --target ${{ matrix.arch }} --release
# leave only the executable file
rm -frd target/${{ matrix.arch }}/release/{*/*,*.d,*.rlib,.fingerprint}
find . -empty -delete
- uses: actions/upload-artifact@master
with:
name: ${{ matrix.arch }}
path: target/${{ matrix.arch }}/release
docker:
name: Build and publish docker images
needs: compile
runs-on: ubuntu-latest
env:
DOCKER_REGISTRY: quay.io/nushell
DOCKER_PASSWORD: ${{ secrets.DOCKER_REGISTRY }}
DOCKER_USER: ${{ secrets.DOCKER_USER }}
strategy:
matrix:
tag:
- alpine
- slim
- debian
- glibc-busybox
- musl-busybox
- musl-distroless
- glibc-distroless
- glibc
- musl
include:
- { tag: alpine, base-image: alpine, arch: x86_64-unknown-linux-musl, plugin: true, use-patch: false}
- { tag: slim, base-image: 'debian:stable-slim', arch: x86_64-unknown-linux-gnu, plugin: true, use-patch: false}
- { tag: debian, base-image: debian, arch: x86_64-unknown-linux-gnu, plugin: true, use-patch: false}
- { tag: glibc-busybox, base-image: 'busybox:glibc', arch: x86_64-unknown-linux-gnu, plugin: false, use-patch: true }
- { tag: musl-busybox, base-image: 'busybox:musl', arch: x86_64-unknown-linux-musl, plugin: false, use-patch: false}
- { tag: musl-distroless, base-image: 'gcr.io/distroless/static', arch: x86_64-unknown-linux-musl, plugin: false, use-patch: false}
- { tag: glibc-distroless, base-image: 'gcr.io/distroless/cc', arch: x86_64-unknown-linux-gnu, plugin: false, use-patch: true }
- { tag: glibc, base-image: scratch, arch: x86_64-unknown-linux-gnu, plugin: false, use-patch: false}
- { tag: musl, base-image: scratch, arch: x86_64-unknown-linux-musl, plugin: false, use-patch: false}
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@master
with: { name: '${{ matrix.arch }}', path: target/release }
- name: Build and publish exact version
run: |-
export DOCKER_TAG=${GITHUB_REF##*/}-${{ matrix.tag }}
export NU_BINS=target/release/$( [ ${{ matrix.plugin }} = true ] && echo nu* || echo nu )
export PATCH=$([ ${{ matrix.use-patch }} = true ] && echo .${{ matrix.tag }} || echo '')
chmod +x $NU_BINS
echo ${DOCKER_PASSWORD} | docker login ${DOCKER_REGISTRY} -u ${DOCKER_USER} --password-stdin
docker-compose --file docker/docker-compose.package.yml build
docker-compose --file docker/docker-compose.package.yml push # exact version
env:
BASE_IMAGE: ${{ matrix.base-image }}
#region semantics tagging
- name: Retag and push with suffixed version
run: |-
VERSION=${GITHUB_REF##*/}
latest_version=${VERSION%%%.*}-${{ matrix.tag }}
latest_feature=${VERSION%%.*}-${{ matrix.tag }}
latest_patch=${VERSION%.*}-${{ matrix.tag }}
exact_version=${VERSION}-${{ matrix.tag }}
tags=( ${latest_version} ${latest_feature} ${latest_patch} ${exact_version} )
for tag in ${tags[@]}; do
docker tag ${DOCKER_REGISTRY}/nu:${VERSION}-${{ matrix.tag }} ${DOCKER_REGISTRY}/nu:${tag}
docker push ${DOCKER_REGISTRY}/nu:${tag}
done
# latest version
docker tag ${DOCKER_REGISTRY}/nu:${VERSION}-${{ matrix.tag }} ${DOCKER_REGISTRY}/nu:${{ matrix.tag }}
docker push ${DOCKER_REGISTRY}/nu:${{ matrix.tag }}
- name: Retag and push debian as latest
if: matrix.tag == 'debian'
run: |-
VERSION=${GITHUB_REF##*/}
# ${latest features} ${latest patch} ${exact version}
tags=( ${VERSION%%.*} ${VERSION%.*} ${VERSION} )
for tag in ${tags[@]}; do
docker tag ${DOCKER_REGISTRY}/nu:${VERSION}-${{ matrix.tag }} ${DOCKER_REGISTRY}/nu:${tag}
docker push ${DOCKER_REGISTRY}/nu:${tag}
done
# latest version
docker tag ${DOCKER_REGISTRY}/nu:${{ matrix.tag }} ${DOCKER_REGISTRY}/nu:latest
docker push ${DOCKER_REGISTRY}/nu:latest
#endregion semantics tagging

View File

@ -1,8 +1,9 @@
name: Create Release Draft
on:
workflow_dispatch:
push:
tags: ['[0-9]+.[0-9]+.[0-9]+*']
tags: ["[0-9]+.[0-9]+.[0-9]+*"]
jobs:
linux:
@ -28,6 +29,60 @@ jobs:
command: build
args: --release --all --features=extra
# - name: Strip binaries (nu)
# run: strip target/release/nu
# - name: Strip binaries (nu_plugin_inc)
# run: strip target/release/nu_plugin_inc
# - name: Strip binaries (nu_plugin_match)
# run: strip target/release/nu_plugin_match
# - name: Strip binaries (nu_plugin_textview)
# run: strip target/release/nu_plugin_textview
# - name: Strip binaries (nu_plugin_binaryview)
# run: strip target/release/nu_plugin_binaryview
# - name: Strip binaries (nu_plugin_chart_bar)
# run: strip target/release/nu_plugin_chart_bar
# - name: Strip binaries (nu_plugin_chart_line)
# run: strip target/release/nu_plugin_chart_line
# - name: Strip binaries (nu_plugin_from_bson)
# run: strip target/release/nu_plugin_from_bson
# - name: Strip binaries (nu_plugin_from_sqlite)
# run: strip target/release/nu_plugin_from_sqlite
# - name: Strip binaries (nu_plugin_from_mp4)
# run: strip target/release/nu_plugin_from_mp4
# - name: Strip binaries (nu_plugin_query_json)
# run: strip target/release/nu_plugin_query_json
# - name: Strip binaries (nu_plugin_s3)
# run: strip target/release/nu_plugin_s3
# - name: Strip binaries (nu_plugin_selector)
# run: strip target/release/nu_plugin_selector
# - name: Strip binaries (nu_plugin_start)
# run: strip target/release/nu_plugin_start
# - name: Strip binaries (nu_plugin_to_bson)
# run: strip target/release/nu_plugin_to_bson
# - name: Strip binaries (nu_plugin_to_sqlite)
# run: strip target/release/nu_plugin_to_sqlite
# - name: Strip binaries (nu_plugin_tree)
# run: strip target/release/nu_plugin_tree
# - name: Strip binaries (nu_plugin_xpath)
# run: strip target/release/nu_plugin_xpath
- name: Create output directory
run: mkdir output
@ -70,6 +125,60 @@ jobs:
command: build
args: --release --all --features=extra
# - name: Strip binaries (nu)
# run: strip target/release/nu
# - name: Strip binaries (nu_plugin_inc)
# run: strip target/release/nu_plugin_inc
# - name: Strip binaries (nu_plugin_match)
# run: strip target/release/nu_plugin_match
# - name: Strip binaries (nu_plugin_textview)
# run: strip target/release/nu_plugin_textview
# - name: Strip binaries (nu_plugin_binaryview)
# run: strip target/release/nu_plugin_binaryview
# - name: Strip binaries (nu_plugin_chart_bar)
# run: strip target/release/nu_plugin_chart_bar
# - name: Strip binaries (nu_plugin_chart_line)
# run: strip target/release/nu_plugin_chart_line
# - name: Strip binaries (nu_plugin_from_bson)
# run: strip target/release/nu_plugin_from_bson
# - name: Strip binaries (nu_plugin_from_sqlite)
# run: strip target/release/nu_plugin_from_sqlite
# - name: Strip binaries (nu_plugin_from_mp4)
# run: strip target/release/nu_plugin_from_mp4
# - name: Strip binaries (nu_plugin_query_json)
# run: strip target/release/nu_plugin_query_json
# - name: Strip binaries (nu_plugin_s3)
# run: strip target/release/nu_plugin_s3
# - name: Strip binaries (nu_plugin_selector)
# run: strip target/release/nu_plugin_selector
# - name: Strip binaries (nu_plugin_start)
# run: strip target/release/nu_plugin_start
# - name: Strip binaries (nu_plugin_to_bson)
# run: strip target/release/nu_plugin_to_bson
# - name: Strip binaries (nu_plugin_to_sqlite)
# run: strip target/release/nu_plugin_to_sqlite
# - name: Strip binaries (nu_plugin_tree)
# run: strip target/release/nu_plugin_tree
# - name: Strip binaries (nu_plugin_xpath)
# run: strip target/release/nu_plugin_xpath
- name: Create output directory
run: mkdir output
@ -114,6 +223,60 @@ jobs:
command: build
args: --release --all --features=extra
# - name: Strip binaries (nu.exe)
# run: strip target/release/nu.exe
# - name: Strip binaries (nu_plugin_inc.exe)
# run: strip target/release/nu_plugin_inc.exe
# - name: Strip binaries (nu_plugin_match.exe)
# run: strip target/release/nu_plugin_match.exe
# - name: Strip binaries (nu_plugin_textview.exe)
# run: strip target/release/nu_plugin_textview.exe
# - name: Strip binaries (nu_plugin_binaryview.exe)
# run: strip target/release/nu_plugin_binaryview.exe
# - name: Strip binaries (nu_plugin_chart_bar.exe)
# run: strip target/release/nu_plugin_chart_bar.exe
# - name: Strip binaries (nu_plugin_chart_line.exe)
# run: strip target/release/nu_plugin_chart_line.exe
# - name: Strip binaries (nu_plugin_from_bson.exe)
# run: strip target/release/nu_plugin_from_bson.exe
# - name: Strip binaries (nu_plugin_from_sqlite.exe)
# run: strip target/release/nu_plugin_from_sqlite.exe
# - name: Strip binaries (nu_plugin_from_mp4.exe)
# run: strip target/release/nu_plugin_from_mp4.exe
# - name: Strip binaries (nu_plugin_query_json.exe)
# run: strip target/release/nu_plugin_query_json.exe
# - name: Strip binaries (nu_plugin_s3.exe)
# run: strip target/release/nu_plugin_s3.exe
# - name: Strip binaries (nu_plugin_selector.exe)
# run: strip target/release/nu_plugin_selector.exe
# - name: Strip binaries (nu_plugin_start.exe)
# run: strip target/release/nu_plugin_start.exe
# - name: Strip binaries (nu_plugin_to_bson.exe)
# run: strip target/release/nu_plugin_to_bson.exe
# - name: Strip binaries (nu_plugin_to_sqlite.exe)
# run: strip target/release/nu_plugin_to_sqlite.exe
# - name: Strip binaries (nu_plugin_tree.exe)
# run: strip target/release/nu_plugin_tree.exe
# - name: Strip binaries (nu_plugin_xpath.exe)
# run: strip target/release/nu_plugin_xpath.exe
- name: Create output directory
run: mkdir output
@ -162,8 +325,6 @@ jobs:
- linux
- macos
- windows
outputs:
version: ${{ steps.info.outputs.version }}
steps:
- name: Check out code
uses: actions/checkout@v2
@ -285,19 +446,4 @@ jobs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./nushell-windows.msi
asset_name: ${{ steps.info.outputs.windowsdir }}.msi
asset_content_type: applictaion/x-msi
winget:
name: Publish winget package
runs-on: windows-latest
needs: release
steps:
- name: Submit package to Windows Package Manager Community Repository
run: |
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
$nushellVersion="${{ needs.release.outputs.version }}"
$nushellInstallerName="nu_$($nushellVersion -replace '\.','_')_windows.msi"
$installerUrl = "https://github.com/nushell/nushell/releases/download/$nushellVersion/$nushellInstallerName"
.\wingetcreate.exe update Nushell.Nushell -v $nushellVersion -u $installerUrl -t ${{ secrets.NUSHELL_PAT }}
asset_content_type: application/x-msi

View File

@ -19,11 +19,10 @@ jobs:
operations-per-run: 520
enable-statistics: true
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is being marked stale because it has been open for 30 days without activity. If you feel that this is in error, please comment below and we will keep it marked as active.'
stale-pr-message: 'This PR is being marked stale because it has been open for 45 days without activity. If this PR is still active, please comment below and we will keep it marked as active.'
close-issue-message: 'This issue has been marked stale for more than 10 days without activity. Closing this issue, but if you find that the issue is still valid, please reopen.'
close-pr-message: 'This PR has been marked stale for more than 10 days without activity. Closing this PR, but if you are still working on it, please reopen.'
days-before-issue-stale: 30
close-issue-message: 'This issue has been marked stale for more than 100000 days without activity. Closing this issue, but if you find that the issue is still valid, please reopen.'
close-pr-message: 'This PR has been marked stale for more than 100 days without activity. Closing this PR, but if you are still working on it, please reopen.'
days-before-issue-stale: 90
days-before-pr-stale: 45
days-before-issue-close: 10
days-before-pr-close: 10
days-before-issue-close: 100000
days-before-pr-close: 100
exempt-issue-labels: 'exempt,keep'

18
.github/workflows/winget-submission.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: Submit Nushell package to Windows Package Manager Community Repository
on:
release:
types: [published]
jobs:
winget:
name: Publish winget package
runs-on: windows-latest
steps:
- name: Submit package to Windows Package Manager Community Repository
run: |
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json
$installerUrl = $github.release.assets | Where-Object -Property name -match 'windows.msi' | Select -ExpandProperty browser_download_url -First 1
.\wingetcreate.exe update Nushell.Nushell -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.NUSHELL_PAT }}

2
.gitpod.Dockerfile vendored
View File

@ -2,7 +2,7 @@ FROM gitpod/workspace-full
# Gitpod will not rebuild Nushell's dev image unless *some* change is made to this Dockerfile.
# To force a rebuild, simply increase this counter:
ENV TRIGGER_REBUILD 1
ENV TRIGGER_REBUILD 2
USER gitpod

3569
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.34.0"
version = "0.42.0"
[workspace]
members = ["crates/*/"]
@ -18,38 +18,34 @@ members = ["crates/*/"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-cli = { version = "0.34.0", path="./crates/nu-cli", default-features=false }
nu-command = { version = "0.34.0", path="./crates/nu-command" }
nu-completion = { version = "0.34.0", path="./crates/nu-completion" }
nu-data = { version = "0.34.0", path="./crates/nu-data" }
nu-engine = { version = "0.34.0", path="./crates/nu-engine" }
nu-errors = { version = "0.34.0", path="./crates/nu-errors" }
nu-parser = { version = "0.34.0", path="./crates/nu-parser" }
nu-path = { version = "0.34.0", path="./crates/nu-path" }
nu-plugin = { version = "0.34.0", path="./crates/nu-plugin" }
nu-protocol = { version = "0.34.0", path="./crates/nu-protocol" }
nu-source = { version = "0.34.0", path="./crates/nu-source" }
nu-value-ext = { version = "0.34.0", path="./crates/nu-value-ext" }
nu-cli = { version = "0.42.0", path="./crates/nu-cli", default-features=false }
nu-command = { version = "0.42.0", path="./crates/nu-command" }
nu-completion = { version = "0.42.0", path="./crates/nu-completion" }
nu-data = { version = "0.42.0", path="./crates/nu-data" }
nu-engine = { version = "0.42.0", path="./crates/nu-engine" }
nu-errors = { version = "0.42.0", path="./crates/nu-errors" }
nu-parser = { version = "0.42.0", path="./crates/nu-parser" }
nu-path = { version = "0.42.0", path="./crates/nu-path" }
nu-plugin = { version = "0.42.0", path="./crates/nu-plugin" }
nu-protocol = { version = "0.42.0", path="./crates/nu-protocol" }
nu-source = { version = "0.42.0", path="./crates/nu-source" }
nu-value-ext = { version = "0.42.0", path="./crates/nu-value-ext" }
nu_plugin_binaryview = { version = "0.34.0", path="./crates/nu_plugin_binaryview", optional=true }
nu_plugin_chart = { version = "0.34.0", path="./crates/nu_plugin_chart", optional=true }
nu_plugin_fetch = { version = "0.34.0", path="./crates/nu_plugin_fetch", optional=true }
nu_plugin_from_bson = { version = "0.34.0", path="./crates/nu_plugin_from_bson", optional=true }
nu_plugin_from_sqlite = { version = "0.34.0", path="./crates/nu_plugin_from_sqlite", optional=true }
nu_plugin_inc = { version = "0.34.0", path="./crates/nu_plugin_inc", optional=true }
nu_plugin_match = { version = "0.34.0", path="./crates/nu_plugin_match", optional=true }
nu_plugin_post = { version = "0.34.0", path="./crates/nu_plugin_post", optional=true }
nu_plugin_ps = { version = "0.34.0", path="./crates/nu_plugin_ps", optional=true }
nu_plugin_query_json = { version = "0.34.0", path="./crates/nu_plugin_query_json", optional=true }
nu_plugin_s3 = { version = "0.34.0", path="./crates/nu_plugin_s3", optional=true }
nu_plugin_selector = { version = "0.34.0", path="./crates/nu_plugin_selector", optional=true }
nu_plugin_start = { version = "0.34.0", path="./crates/nu_plugin_start", optional=true }
nu_plugin_sys = { version = "0.34.0", path="./crates/nu_plugin_sys", optional=true }
nu_plugin_textview = { version = "0.34.0", path="./crates/nu_plugin_textview", optional=true }
nu_plugin_to_bson = { version = "0.34.0", path="./crates/nu_plugin_to_bson", optional=true }
nu_plugin_to_sqlite = { version = "0.34.0", path="./crates/nu_plugin_to_sqlite", optional=true }
nu_plugin_tree = { version = "0.34.0", path="./crates/nu_plugin_tree", optional=true }
nu_plugin_xpath = { version = "0.34.0", path="./crates/nu_plugin_xpath", optional=true }
nu_plugin_binaryview = { version = "0.42.0", path="./crates/nu_plugin_binaryview", optional=true }
nu_plugin_chart = { version = "0.42.0", path="./crates/nu_plugin_chart", optional=true }
nu_plugin_from_bson = { version = "0.42.0", path="./crates/nu_plugin_from_bson", optional=true }
nu_plugin_from_sqlite = { version = "0.42.0", path="./crates/nu_plugin_from_sqlite", optional=true }
nu_plugin_inc = { version = "0.42.0", path="./crates/nu_plugin_inc", optional=true }
nu_plugin_match = { version = "0.42.0", path="./crates/nu_plugin_match", optional=true }
nu_plugin_query_json = { version = "0.42.0", path="./crates/nu_plugin_query_json", optional=true }
nu_plugin_s3 = { version = "0.42.0", path="./crates/nu_plugin_s3", optional=true }
nu_plugin_selector = { version = "0.42.0", path="./crates/nu_plugin_selector", optional=true }
nu_plugin_start = { version = "0.42.0", path="./crates/nu_plugin_start", optional=true }
nu_plugin_textview = { version = "0.42.0", path="./crates/nu_plugin_textview", optional=true }
nu_plugin_to_bson = { version = "0.42.0", path="./crates/nu_plugin_to_bson", optional=true }
nu_plugin_to_sqlite = { version = "0.42.0", path="./crates/nu_plugin_to_sqlite", optional=true }
nu_plugin_tree = { version = "0.42.0", path="./crates/nu_plugin_tree", optional=true }
nu_plugin_xpath = { version = "0.42.0", path="./crates/nu_plugin_xpath", optional=true }
# Required to bootstrap the main binary
ctrlc = { version="3.1.7", optional=true }
@ -57,8 +53,7 @@ futures = { version="0.3.12", features=["compat", "io-compat"] }
itertools = "0.10.0"
[dev-dependencies]
nu-test-support = { version = "0.34.0", path="./crates/nu-test-support" }
dunce = "1.0.1"
nu-test-support = { version = "0.42.0", path="./crates/nu-test-support" }
serial_test = "0.5.1"
hamcrest2 = "0.3.0"
rstest = "0.10.0"
@ -66,6 +61,8 @@ rstest = "0.10.0"
[build-dependencies]
[features]
fetch-support = ["nu-command/fetch", "nu-command/post"]
sys-support = ["nu-command/sys", "nu-command/ps"]
ctrlc-support = ["nu-cli/ctrlc", "nu-command/ctrlc"]
rustyline-support = ["nu-cli/rustyline-support", "nu-command/rustyline-support"]
term-support = ["nu-command/term"]
@ -74,15 +71,13 @@ which-support = ["nu-command/which", "nu-engine/which"]
default = [
"nu-cli/shadow-rs",
"sys",
"ps",
"sys-support",
"ctrlc-support",
"which-support",
"term-support",
"rustyline-support",
"match",
"post",
"fetch",
"fetch-support",
"zip-support",
"dataframe",
]
@ -94,7 +89,6 @@ extra = [
"inc",
"tree",
"textview",
"clipboard-cli",
"trash-support",
"uuid-support",
"start",
@ -110,19 +104,14 @@ extra = [
wasi = ["inc", "match", "match", "tree", "rustyline-support"]
# Stable (Default)
fetch = ["nu_plugin_fetch"]
inc = ["nu_plugin_inc"]
match = ["nu_plugin_match"]
post = ["nu_plugin_post"]
ps = ["nu_plugin_ps"]
sys = ["nu_plugin_sys"]
textview = ["nu_plugin_textview"]
# Extra
binaryview = ["nu_plugin_binaryview"]
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
chart = ["nu_plugin_chart"]
clipboard-cli = ["nu-command/clipboard-cli"]
query-json = ["nu_plugin_query_json"]
s3 = ["nu_plugin_s3"]
selector = ["nu_plugin_selector"]
@ -136,9 +125,6 @@ tree = ["nu_plugin_tree"]
xpath = ["nu_plugin_xpath"]
zip-support = ["nu-command/zip"]
#This is disabled in extra for now
table-pager = ["nu-command/table-pager"]
#dataframe feature for nushell
dataframe = [
"nu-engine/dataframe",
@ -146,10 +132,12 @@ dataframe = [
"nu-command/dataframe",
"nu-value-ext/dataframe",
"nu-data/dataframe",
"nu_plugin_post/dataframe",
"nu_plugin_to_bson/dataframe",
]
[profile.release]
opt-level = "s" # Optimize for size.
# Core plugins that ship with `cargo install nu` by default
# Currently, Cargo limits us to installing only one binary
# unless we use [[bin]], so we use this as a workaround
@ -163,31 +151,11 @@ name = "nu_plugin_core_inc"
path = "src/plugins/nu_plugin_core_inc.rs"
required-features = ["inc"]
[[bin]]
name = "nu_plugin_core_ps"
path = "src/plugins/nu_plugin_core_ps.rs"
required-features = ["ps"]
[[bin]]
name = "nu_plugin_core_sys"
path = "src/plugins/nu_plugin_core_sys.rs"
required-features = ["sys"]
[[bin]]
name = "nu_plugin_core_fetch"
path = "src/plugins/nu_plugin_core_fetch.rs"
required-features = ["fetch"]
[[bin]]
name = "nu_plugin_core_match"
path = "src/plugins/nu_plugin_core_match.rs"
required-features = ["match"]
[[bin]]
name = "nu_plugin_core_post"
path = "src/plugins/nu_plugin_core_post.rs"
required-features = ["post"]
# Extra plugins
[[bin]]

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 - 2021 Yehuda Katz, Jonathan Turner
Copyright (c) 2019 - 2021 Nushell Project
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

@ -68,7 +68,7 @@ cargo install nu
To install Nu via the [Windows Package Manager](https://aka.ms/winget-cli):
```shell
winget install nu
winget install nushell
```
You can also build Nu yourself with all the bells and whistles (be sure to have installed the [dependencies](https://www.nushell.sh/book/installation.html#dependencies) for your platform), once you have checked out this repo with git:
@ -76,53 +76,6 @@ You can also build Nu yourself with all the bells and whistles (be sure to have
```shell
cargo build --workspace --features=extra
```
### Docker
#### Quickstart
Want to try Nu right away? Execute the following to get started.
```shell
docker run -it quay.io/nushell/nu:latest
```
#### Guide
If you want to pull a pre-built container, you can browse tags for the [nushell organization](https://quay.io/organization/nushell)
on Quay.io. Pulling a container would come down to:
```shell
docker pull quay.io/nushell/nu
docker pull quay.io/nushell/nu-base
```
Both "nu-base" and "nu" provide the nu binary, however, nu-base also includes the source code at `/code`
in the container and all dependencies.
Optionally, you can also build the containers locally using the [dockerfiles provided](docker):
To build the base image:
```shell
docker build -f docker/Dockerfile.nu-base -t nushell/nu-base .
```
And then to build the smaller container (using a Multistage build):
```shell
docker build -f docker/Dockerfile -t nushell/nu .
```
Either way, you can run either container as follows:
```shell
docker run -it nushell/nu-base
docker run -it nushell/nu
/> exit
```
The second container is a bit smaller if the size is important to you.
### Packaging status
[![Packaging status](https://repology.org/badge/vertical-allrepos/nushell.svg)](https://repology.org/project/nushell/versions)
@ -310,9 +263,15 @@ Nu is in heavy development and will naturally change as it matures and people us
| Completions | | | X | | | Completions for filepaths |
| Type-checking | | | X | | | Commands check basic types, but input/output isn't checked |
## Current Roadmap
## Officially Supported By
We've added a `Roadmap Board` to help collaboratively capture the direction we're going for the current release and capture some important issues we'd like to see in Nushell. You can find the Roadmap [here](https://github.com/nushell/nushell/projects/2).
Please submit an issue or PR to be added to this list.
### Integrations
- [zoxide](https://github.com/ajeetdsouza/zoxide)
- [starship](https://github.com/starship/starship)
### Mentions
- [The Python Launcher for Unix](https://github.com/brettcannon/python-launcher#how-do-i-get-a-table-of-python-executables-in-nushell)
## Contributing

View File

@ -10,4 +10,4 @@ Foundational libraries are split into two kinds of crates:
Plugins are likewise also split into two types:
* Core plugins - plugins that provide part of the default experience of Nu, including access to the system properties, processes, and web-connectivity features.
* Extra plugins - these plugins run a wide range of differnt capabilities like working with different file types, charting, viewing binary data, and more.
* Extra plugins - these plugins run a wide range of different capabilities like working with different file types, charting, viewing binary data, and more.

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.34.0"
version = "0.42.0"
[lib]
doctest = false
@ -21,7 +21,6 @@ derive_serde_style = ["serde"]
[dependencies]
overload = "0.1.1"
serde = { version="1.0.90", features=["derive"], optional=true }
itertools = "0.10.0"
# [dependencies.serde]
# version = "1.0.90"

View File

@ -93,7 +93,7 @@ pub static RESET: &str = "\x1B[0m";
impl Color {
fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
match *self {
match self {
Color::Black => write!(f, "30"),
Color::Red => write!(f, "31"),
Color::Green => write!(f, "32"),
@ -103,8 +103,8 @@ impl Color {
Color::Magenta => write!(f, "35"),
Color::Cyan => write!(f, "36"),
Color::White => write!(f, "37"),
Color::Fixed(num) => write!(f, "38;5;{}", &num),
Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
Color::Fixed(num) => write!(f, "38;5;{}", num),
Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", r, g, b),
Color::DarkGray => write!(f, "90"),
Color::LightRed => write!(f, "91"),
Color::LightGreen => write!(f, "92"),
@ -118,7 +118,7 @@ impl Color {
}
fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
match *self {
match self {
Color::Black => write!(f, "40"),
Color::Red => write!(f, "41"),
Color::Green => write!(f, "42"),
@ -128,8 +128,8 @@ impl Color {
Color::Magenta => write!(f, "45"),
Color::Cyan => write!(f, "46"),
Color::White => write!(f, "47"),
Color::Fixed(num) => write!(f, "48;5;{}", &num),
Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
Color::Fixed(num) => write!(f, "48;5;{}", num),
Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", r, g, b),
Color::DarkGray => write!(f, "100"),
Color::LightRed => write!(f, "101"),
Color::LightGreen => write!(f, "102"),

View File

@ -297,7 +297,7 @@ mod tests {
fn no_control_codes_for_plain() {
let one = Style::default().paint("one");
let two = Style::default().paint("two");
let output = format!("{}", AnsiStrings(&[one, two]));
assert_eq!(&*output, "onetwo");
let output = AnsiStrings(&[one, two]).to_string();
assert_eq!(output, "onetwo");
}
}

View File

@ -602,18 +602,18 @@ mod serde_json_tests {
#[test]
fn color_deserialization() {
let colors = &[
let colors = [
Color::Red,
Color::Blue,
Color::Rgb(123, 123, 123),
Color::Fixed(255),
];
for color in colors.iter() {
for color in colors {
let serialized = serde_json::to_string(&color).unwrap();
let deserialized: Color = serde_json::from_str(&serialized).unwrap();
assert_eq!(color, &deserialized);
assert_eq!(color, deserialized);
}
}

View File

@ -75,6 +75,6 @@ mod test {
assert_eq!(unstyled_len(&a), 18);
let l2 = [Black.paint("st"), Red.paint("-second"), White.paint("-t")];
assert_eq!(sub_string(3, 11, &a).as_slice(), &l2);
assert_eq!(sub_string(3, 11, &a), l2);
}
}

View File

@ -4,36 +4,38 @@ description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-cli"
version = "0.34.0"
version = "0.42.0"
build = "build.rs"
[lib]
doctest = false
[dependencies]
nu-completion = { version = "0.34.0", path="../nu-completion" }
nu-command = { version = "0.34.0", path="../nu-command" }
nu-data = { version = "0.34.0", path="../nu-data" }
nu-engine = { version = "0.34.0", path="../nu-engine" }
nu-errors = { version = "0.34.0", path="../nu-errors" }
nu-parser = { version = "0.34.0", path="../nu-parser" }
nu-protocol = { version = "0.34.0", path="../nu-protocol" }
nu-source = { version = "0.34.0", path="../nu-source" }
nu-stream = { version = "0.34.0", path="../nu-stream" }
nu-ansi-term = { version = "0.34.0", path="../nu-ansi-term" }
nu-completion = { version = "0.42.0", path="../nu-completion" }
nu-command = { version = "0.42.0", path="../nu-command" }
nu-data = { version = "0.42.0", path="../nu-data" }
nu-engine = { version = "0.42.0", path="../nu-engine" }
nu-errors = { version = "0.42.0", path="../nu-errors" }
nu-parser = { version = "0.42.0", path="../nu-parser" }
nu-protocol = { version = "0.42.0", path="../nu-protocol" }
nu-source = { version = "0.42.0", path="../nu-source" }
nu-stream = { version = "0.42.0", path="../nu-stream" }
nu-ansi-term = { version = "0.42.0", path="../nu-ansi-term" }
nu-path = { version = "0.42.0", path="../nu-path" }
indexmap ="1.6.1"
log = "0.4.14"
pretty_env_logger = "0.4.0"
strip-ansi-escapes = "0.1.0"
rustyline = { version="8.1.0", optional=true }
rustyline = { version="9.0.0", optional=true }
ctrlc = { version="3.1.7", optional=true }
shadow-rs = { version="0.6", default-features=false, optional=true }
shadow-rs = { version = "0.8.1", default-features = false, optional = true }
serde = { version="1.0.123", features=["derive"] }
serde_yaml = "0.8.16"
lazy_static = "1.4.0"
[build-dependencies]
shadow-rs = "0.6"
shadow-rs = "0.8.1"
[features]
default = ["shadow-rs"]

View File

@ -1,10 +1,10 @@
mod logger;
mod options;
mod options_parser;
pub mod stopwatch;
pub use options::{CliOptions, NuScript, Options};
use options_parser::{NuParser, OptionsParser};
use self::stopwatch::Stopwatch;
use lazy_static::lazy_static;
use nu_command::{commands::NuSignature as Nu, utils::test_bins as binaries};
use nu_engine::{get_full_help, EvaluationContext};
use nu_errors::ShellError;
@ -12,6 +12,18 @@ use nu_protocol::hir::{Call, Expression, SpannedExpression, Synthetic};
use nu_protocol::{Primitive, UntaggedValue};
use nu_source::{Span, Tag};
use nu_stream::InputStream;
pub use options::{CliOptions, NuScript, Options};
use options_parser::{NuParser, OptionsParser};
use std::sync::Mutex;
lazy_static! {
pub static ref STOPWATCH: Mutex<Stopwatch> = {
let mut sw = Stopwatch::default();
sw.start();
sw.stop();
Mutex::new(sw)
};
}
pub struct App {
parser: Box<dyn OptionsParser>,
@ -32,6 +44,14 @@ impl App {
}
pub fn main(&self, argv: &[String]) -> Result<(), ShellError> {
if self.perf() {
// start the stopwatch running
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.start();
}
let argv = quote_positionals(argv).join(" ");
if let Err(cause) = self.parse(&argv) {
@ -52,6 +72,21 @@ impl App {
consume(context, stream)?;
if self.perf() {
// stop the stopwatch since we're exiting
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.stop();
eprintln!(
"help {:?}",
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.elapsed()
);
}
std::process::exit(0);
}
@ -93,6 +128,22 @@ impl App {
};
consume(context, stream)?;
if self.perf() {
// stop the stopwatch since we're exiting
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.stop();
eprintln!(
"version {:?}",
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.elapsed()
);
}
std::process::exit(0);
}
@ -113,21 +164,29 @@ impl App {
}
let mut opts = CliOptions::new();
opts.config = self.config().map(std::ffi::OsString::from);
opts.stdin = self.takes_stdin();
opts.save_history = self.save_history();
opts.perf = self.perf();
use logger::{configure, debug_filters, logger, trace_filters};
logger(|builder| {
configure(&self, builder)?;
trace_filters(&self, builder)?;
debug_filters(&self, builder)?;
configure(self, builder)?;
trace_filters(self, builder)?;
debug_filters(self, builder)?;
Ok(())
})?;
if self.perf() {
// start a new split
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.start()
}
if let Some(commands) = self.commands() {
let commands = commands?;
let script = NuScript::code(&commands)?;
@ -136,6 +195,21 @@ impl App {
return crate::run_script_file(context, opts);
}
if self.perf() {
// start a new spit
eprintln!(
"commands using -c at launch: {:?}",
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.elapsed_split()
);
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.start();
}
if let Some(scripts) = self.scripts() {
let mut source_files = vec![];
for script in scripts {
@ -162,12 +236,38 @@ impl App {
return Ok(());
}
if self.perf() {
// start a new split
eprintln!(
"script file(s) passed in: {:?}",
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.elapsed_split()
);
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.start();
}
let context = crate::create_default_context(true)?;
if !self.skip_plugins() {
let _ = crate::register_plugins(&context);
}
if self.perf() {
// start a new split
eprintln!(
"plugins registered: {:?}",
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.elapsed_split()
);
}
#[cfg(feature = "rustyline-support")]
{
crate::cli(context, opts)?;
@ -189,6 +289,13 @@ impl App {
})
}
pub fn perf(&self) -> bool {
self.options
.get("perf")
.map(|v| matches!(v.as_bool(), Ok(true)))
.unwrap_or(false)
}
pub fn help(&self) -> bool {
self.options
.get("help")
@ -298,7 +405,7 @@ impl App {
}
pub fn parse(&self, args: &str) -> Result<(), ShellError> {
self.parser.parse(&args).map(|options| {
self.parser.parse(args).map(|options| {
self.options.swap(&options);
})
}
@ -401,14 +508,9 @@ mod tests {
#[test]
fn can_use_loglevels() -> Result<(), ShellError> {
for level in &["error", "warn", "info", "debug", "trace"] {
for level in ["error", "warn", "info", "debug", "trace"] {
let ui = cli_app();
let args = format!("nu --loglevel={}", *level);
ui.parse(&args)?;
assert_eq!(ui.loglevel().unwrap(), Ok(level.to_string()));
let ui = cli_app();
let args = format!("nu -l {}", *level);
let args = format!("nu --loglevel={}", level);
ui.parse(&args)?;
assert_eq!(ui.loglevel().unwrap(), Ok(level.to_string()));
}
@ -423,6 +525,17 @@ mod tests {
Ok(())
}
#[test]
fn can_be_login() -> Result<(), ShellError> {
let ui = cli_app();
ui.parse("nu -l")?;
let ui = cli_app();
ui.parse("nu --login")?;
Ok(())
}
#[test]
fn can_be_passed_nu_scripts() -> Result<(), ShellError> {
let ui = cli_app();
@ -434,11 +547,11 @@ mod tests {
#[test]
fn can_use_test_binaries() -> Result<(), ShellError> {
for binarie_name in &[
for binarie_name in [
"echo_env", "cococo", "iecho", "fail", "nonu", "chop", "repeater", "meow",
] {
let ui = cli_app();
let args = format!("nu --testbin={}", *binarie_name);
let args = format!("nu --testbin={}", binarie_name);
ui.parse(&args)?;
assert_eq!(ui.testbin().unwrap(), Ok(binarie_name.to_string()));
}

View File

@ -10,6 +10,7 @@ pub struct CliOptions {
pub stdin: bool,
pub scripts: Vec<NuScript>,
pub save_history: bool,
pub perf: bool,
}
impl Default for CliOptions {
@ -25,6 +26,7 @@ impl CliOptions {
stdin: false,
scripts: vec![],
save_history: true,
perf: false,
}
}
}
@ -42,7 +44,7 @@ impl Options {
}
pub fn get(&self, key: &str) -> Option<Value> {
self.inner.borrow().get(key).map(Clone::clone)
self.inner.borrow().get(key).cloned()
}
pub fn put(&self, key: &str, value: Value) {

View File

@ -67,51 +67,40 @@ impl OptionsParser for NuParser {
}
};
let value =
value
.map(|v| match k.as_ref() {
let value = value.map(|v| match k.as_ref() {
"testbin" => {
if let Ok(name) = v.as_string() {
if testbins().iter().any(|n| name == *n) {
Some(v)
v
} else {
Some(
UntaggedValue::Error(
ShellError::untagged_runtime_error(
UntaggedValue::Error(ShellError::untagged_runtime_error(
format!("{} is not supported.", name),
),
)
.into_value(v.tag),
)
))
.into_value(v.tag)
}
} else {
Some(v)
v
}
}
"loglevel" => {
if let Ok(name) = v.as_string() {
if loglevels().iter().any(|n| name == *n) {
Some(v)
v
} else {
Some(
UntaggedValue::Error(
ShellError::untagged_runtime_error(
UntaggedValue::Error(ShellError::untagged_runtime_error(
format!("{} is not supported.", name),
),
)
.into_value(v.tag),
)
))
.into_value(v.tag)
}
} else {
Some(v)
v
}
}
_ => Some(v),
})
.flatten();
_ => v,
});
if let Some(value) = value {
options.put(&k, value);
options.put(k, value);
}
});
}

View File

@ -0,0 +1,118 @@
#![allow(dead_code)]
use std::default::Default;
use std::fmt;
use std::time::{Duration, Instant};
#[derive(Clone, Copy)]
pub struct Stopwatch {
/// The time the stopwatch was started last, if ever.
start_time: Option<Instant>,
/// The time the stopwatch was split last, if ever.
split_time: Option<Instant>,
/// The time elapsed while the stopwatch was running (between start() and stop()).
elapsed: Duration,
}
impl Default for Stopwatch {
fn default() -> Stopwatch {
Stopwatch {
start_time: None,
split_time: None,
elapsed: Duration::from_secs(0),
}
}
}
impl fmt::Display for Stopwatch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
return write!(f, "{}ms", self.elapsed_ms());
}
}
impl Stopwatch {
/// Returns a new stopwatch.
pub fn new() -> Stopwatch {
let sw: Stopwatch = Default::default();
sw
}
/// Returns a new stopwatch which will immediately be started.
pub fn start_new() -> Stopwatch {
let mut sw = Stopwatch::new();
sw.start();
sw
}
/// Starts the stopwatch.
pub fn start(&mut self) {
self.start_time = Some(Instant::now());
}
/// Stops the stopwatch.
pub fn stop(&mut self) {
self.elapsed = self.elapsed();
self.start_time = None;
self.split_time = None;
}
/// Resets all counters and stops the stopwatch.
pub fn reset(&mut self) {
self.elapsed = Duration::from_secs(0);
self.start_time = None;
self.split_time = None;
}
/// Resets and starts the stopwatch again.
pub fn restart(&mut self) {
self.reset();
self.start();
}
/// Returns whether the stopwatch is running.
pub fn is_running(&self) -> bool {
self.start_time.is_some()
}
/// Returns the elapsed time since the start of the stopwatch.
pub fn elapsed(&self) -> Duration {
match self.start_time {
// stopwatch is running
Some(t1) => t1.elapsed() + self.elapsed,
// stopwatch is not running
None => self.elapsed,
}
}
/// Returns the elapsed time since the start of the stopwatch in milliseconds.
pub fn elapsed_ms(&self) -> i64 {
let dur = self.elapsed();
(dur.as_secs() * 1000 + dur.subsec_millis() as u64) as i64
}
/// Returns the elapsed time since last split or start/restart.
///
/// If the stopwatch is in stopped state this will always return a zero Duration.
pub fn elapsed_split(&mut self) -> Duration {
match self.start_time {
// stopwatch is running
Some(start) => {
let res = match self.split_time {
Some(split) => split.elapsed(),
None => start.elapsed(),
};
self.split_time = Some(Instant::now());
res
}
// stopwatch is not running
None => Duration::from_secs(0),
}
}
/// Returns the elapsed time since last split or start/restart in milliseconds.
///
/// If the stopwatch is in stopped state this will always return zero.
pub fn elapsed_split_ms(&mut self) -> i64 {
let dur = self.elapsed_split();
(dur.as_secs() * 1000 + dur.subsec_millis() as u64) as i64
}
}

View File

@ -1,3 +1,4 @@
use crate::app::STOPWATCH;
use crate::line_editor::configure_ctrl_c;
use nu_ansi_term::Color;
use nu_engine::{maybe_print_errors, run_block, script::run_script_standalone, EvaluationContext};
@ -23,6 +24,7 @@ use rustyline::{self, error::ReadlineError};
use nu_errors::ShellError;
use nu_parser::ParserScope;
use nu_path::expand_tilde;
use nu_protocol::{hir::ExternalRedirection, ConfigPath, UntaggedValue, Value};
use log::trace;
@ -30,9 +32,12 @@ use std::error::Error;
use std::iter::Iterator;
use std::path::PathBuf;
// Name of environment variable where the prompt could be stored
#[cfg(feature = "rustyline-support")]
const PROMPT_COMMAND: &str = "PROMPT_COMMAND";
pub fn search_paths() -> Vec<std::path::PathBuf> {
use std::env;
let mut search_paths = Vec::new();
// Automatically add path `nu` is in as a search path
@ -50,7 +55,7 @@ pub fn search_paths() -> Vec<std::path::PathBuf> {
{
for pipeline in pipelines {
if let Ok(plugin_dir) = pipeline.as_string() {
search_paths.push(PathBuf::from(plugin_dir));
search_paths.push(expand_tilde(plugin_dir));
}
}
}
@ -82,6 +87,66 @@ pub fn run_script_file(
Ok(())
}
#[cfg(feature = "rustyline-support")]
fn default_prompt_string(cwd: &str) -> String {
format!(
"{}{}{}{}{}{}> ",
Color::Green.bold().prefix().to_string(),
cwd,
nu_ansi_term::ansi::RESET,
Color::Cyan.bold().prefix().to_string(),
current_branch(),
nu_ansi_term::ansi::RESET
)
}
#[cfg(feature = "rustyline-support")]
fn evaluate_prompt_string(prompt_line: &str, context: &EvaluationContext, cwd: &str) -> String {
context.scope.enter_scope();
let (prompt_block, err) = nu_parser::parse(prompt_line, 0, &context.scope);
if err.is_some() {
context.scope.exit_scope();
default_prompt_string(cwd)
} else {
let run_result = run_block(
&prompt_block,
context,
InputStream::empty(),
ExternalRedirection::Stdout,
);
context.scope.exit_scope();
match run_result {
Ok(result) => match result.collect_string(Tag::unknown()) {
Ok(string_result) => {
let errors = context.get_errors();
maybe_print_errors(context, Text::from(prompt_line));
context.clear_errors();
if !errors.is_empty() {
"> ".into()
} else {
string_result.item
}
}
Err(e) => {
context.host().lock().print_err(e, &Text::from(prompt_line));
context.clear_errors();
"> ".into()
}
},
Err(e) => {
context.host().lock().print_err(e, &Text::from(prompt_line));
context.clear_errors();
"> ".into()
}
}
}
}
#[cfg(feature = "rustyline-support")]
pub fn cli(
context: EvaluationContext,
@ -97,15 +162,29 @@ pub fn cli(
} else {
load_global_cfg(&context);
}
// Store cmd duration in an env var
context.scope.add_env_var(
"CMD_DURATION_MS",
format!("{}", startup_commands_start_time.elapsed().as_millis()),
startup_commands_start_time
.elapsed()
.as_millis()
.to_string(),
);
trace!(
"startup commands took {:?}",
startup_commands_start_time.elapsed()
if options.perf {
eprintln!(
"config loaded: {:?}",
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.elapsed_split()
);
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.start();
}
//Configure rustyline
let mut rl = default_rustyline_editor_configuration();
@ -118,11 +197,39 @@ pub fn cli(
nu_data::config::path::default_history_path()
};
if options.perf {
eprintln!(
"rustyline configuration: {:?}",
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.elapsed_split()
);
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.start();
}
// Don't load history if it's not necessary
if options.save_history {
let _ = rl.load_history(&history_path);
}
if options.perf {
eprintln!(
"history load: {:?}",
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.elapsed_split()
);
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.start();
}
//set vars from cfg if present
let (skip_welcome_message, prompt) = if let Some(cfg) = &context.configs().lock().global_config
{
@ -136,6 +243,20 @@ pub fn cli(
(false, None)
};
if options.perf {
eprintln!(
"load custom prompt: {:?}",
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.elapsed_split()
);
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.start();
}
//Check whether dir we start in contains local cfg file and if so load it.
load_local_cfg_if_present(&context);
@ -159,6 +280,20 @@ pub fn cli(
let mut ctrlcbreak = false;
if options.perf {
eprintln!(
"timing stopped. starting run loop: {:?}",
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.elapsed_split()
);
STOPWATCH
.lock()
.expect("unable to lock the stopwatch")
.stop();
}
loop {
if context.ctrl_c().load(Ordering::SeqCst) {
context.ctrl_c().store(false, Ordering::SeqCst);
@ -167,73 +302,18 @@ pub fn cli(
let cwd = context.shell_manager().path();
let colored_prompt = {
// Check if the PROMPT_COMMAND env variable is set. This env variable
// contains nu code that is used to overwrite the prompt
let colored_prompt = match context.scope.get_env(PROMPT_COMMAND) {
Some(env_prompt) => evaluate_prompt_string(&env_prompt, &context, &cwd),
None => {
if let Some(prompt) = &prompt {
let prompt_line = prompt.as_string()?;
context.scope.enter_scope();
let (prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
if err.is_some() {
context.scope.exit_scope();
format!(
"{}{}{}{}{}{}> ",
Color::Green.bold().prefix().to_string(),
cwd,
nu_ansi_term::ansi::RESET,
Color::Cyan.bold().prefix().to_string(),
current_branch(),
nu_ansi_term::ansi::RESET
)
evaluate_prompt_string(&prompt_line, &context, &cwd)
} else {
let run_result = run_block(
&prompt_block,
&context,
InputStream::empty(),
ExternalRedirection::Stdout,
);
context.scope.exit_scope();
match run_result {
Ok(result) => match result.collect_string(Tag::unknown()) {
Ok(string_result) => {
let errors = context.get_errors();
maybe_print_errors(&context, Text::from(prompt_line));
context.clear_errors();
if !errors.is_empty() {
"> ".to_string()
} else {
string_result.item
default_prompt_string(&cwd)
}
}
Err(e) => {
context.host().lock().print_err(e, &Text::from(prompt_line));
context.clear_errors();
"> ".to_string()
}
},
Err(e) => {
context.host().lock().print_err(e, &Text::from(prompt_line));
context.clear_errors();
"> ".to_string()
}
}
}
} else {
format!(
"{}{}{}{}{}{}> ",
Color::Green.bold().prefix().to_string(),
cwd,
nu_ansi_term::ansi::RESET,
Color::Cyan.bold().prefix().to_string(),
current_branch(),
nu_ansi_term::ansi::RESET
)
}
};
let prompt = {
@ -277,7 +357,7 @@ pub fn cli(
// Store cmd duration in an env var
context.scope.add_env_var(
"CMD_DURATION_MS",
format!("{}", cmd_start_time.elapsed().as_millis()),
cmd_start_time.elapsed().as_millis().to_string(),
);
match line {
@ -292,7 +372,7 @@ pub fn cli(
LineResult::ClearHistory => {
if options.save_history {
rl.clear_history();
let _ = rl.append_history(&history_path);
std::fs::remove_file(&history_path)?;
}
}
@ -321,8 +401,7 @@ pub fn cli(
.lock()
.global_config
.as_ref()
.map(|cfg| cfg.var("ctrlc_exit"))
.flatten()
.and_then(|cfg| cfg.var("ctrlc_exit"))
.map(|ctrl_c| ctrl_c.is_true())
.unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells
@ -364,6 +443,7 @@ pub fn cli(
Ok(())
}
#[cfg(feature = "rustyline-support")]
pub fn load_local_cfg_if_present(context: &EvaluationContext) {
trace!("Loading local cfg if present");
match config::loadable_cfg_exists_in_dir(PathBuf::from(context.shell_manager().path())) {

View File

@ -461,7 +461,7 @@ pub(crate) fn load_keybindings(
if let Ok(contents) = contents {
let keybindings: Keybindings = serde_yaml::from_str(&contents)?;
// eprintln!("{:#?}", keybindings);
for keybinding in keybindings.into_iter() {
for keybinding in keybindings {
let (k, b) = convert_keybinding(keybinding);
// eprintln!("{:?} {:?}", k, b);

View File

@ -76,6 +76,8 @@ pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
let config = Config::builder()
.check_cursor_position(true)
.color_mode(ColorMode::Forced)
.history_ignore_dups(false)
.max_history_size(10_000)
.build();
let mut rl: Editor<_> = Editor::with_config(config);
@ -247,7 +249,7 @@ pub fn rustyline_hinter(
) -> Option<rustyline::hint::HistoryHinter> {
if let Some(line_editor_vars) = config.var("line_editor") {
for (idx, value) in line_editor_vars.row_entries() {
if idx == "show_hints" && value.expect_string() == "false" {
if idx == "show_hints" && value.as_bool() == Ok(false) {
return None;
}
}

View File

@ -90,7 +90,10 @@ impl rustyline::completion::Completer for Helper {
impl rustyline::hint::Hinter for Helper {
type Hint = String;
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
self.hinter.as_ref().and_then(|h| h.hint(line, pos, ctx))
match &self.hinter {
Some(the_hinter) => the_hinter.hint(line, pos, ctx),
None => Some("".to_string()),
}
}
}

View File

@ -5,52 +5,47 @@ description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-command"
version = "0.34.0"
version = "0.42.0"
[lib]
doctest = false
[dependencies]
nu-data = { version = "0.34.0", path="../nu-data" }
nu-engine = { version = "0.34.0", path="../nu-engine" }
nu-errors = { version = "0.34.0", path="../nu-errors" }
nu-json = { version = "0.34.0", path="../nu-json" }
nu-path = { version = "0.34.0", path="../nu-path" }
nu-parser = { version = "0.34.0", path="../nu-parser" }
nu-plugin = { version = "0.34.0", path="../nu-plugin" }
nu-protocol = { version = "0.34.0", path="../nu-protocol" }
nu-source = { version = "0.34.0", path="../nu-source" }
nu-stream = { version = "0.34.0", path="../nu-stream" }
nu-table = { version = "0.34.0", path="../nu-table" }
nu-test-support = { version = "0.34.0", path="../nu-test-support" }
nu-value-ext = { version = "0.34.0", path="../nu-value-ext" }
nu-ansi-term = { version = "0.34.0", path="../nu-ansi-term" }
nu-pretty-hex = { version = "0.34.0", path="../nu-pretty-hex" }
nu-data = { version = "0.42.0", path="../nu-data" }
nu-engine = { version = "0.42.0", path="../nu-engine" }
nu-errors = { version = "0.42.0", path="../nu-errors" }
nu-json = { version = "0.42.0", path="../nu-json" }
nu-path = { version = "0.42.0", path="../nu-path" }
nu-parser = { version = "0.42.0", path="../nu-parser" }
nu-plugin = { version = "0.42.0", path="../nu-plugin" }
nu-protocol = { version = "0.42.0", path="../nu-protocol" }
nu-serde = { version = "0.42.0", path="../nu-serde" }
nu-source = { version = "0.42.0", path="../nu-source" }
nu-stream = { version = "0.42.0", path="../nu-stream" }
nu-table = { version = "0.42.0", path="../nu-table" }
nu-test-support = { version = "0.42.0", path="../nu-test-support" }
nu-value-ext = { version = "0.42.0", path="../nu-value-ext" }
nu-ansi-term = { version = "0.42.0", path="../nu-ansi-term" }
nu-pretty-hex = { version = "0.42.0", path="../nu-pretty-hex" }
Inflector = "0.11"
arboard = { version="1.1.0", optional=true }
url = "2.2.1"
mime = "0.3.16"
heck = "0.4.0"
base64 = "0.13.0"
bigdecimal = { version="0.2.0", features=["serde"] }
byte-unit = "4.0.9"
bytes = "1.0.1"
bigdecimal = { version = "0.3.0", features = ["serde"] }
calamine = "0.18.0"
chrono = { version="0.4.19", features=["serde"] }
chrono-tz = "0.5.3"
codespan-reporting = "0.11.0"
crossterm = { version="0.19.0", optional=true }
csv = "1.1.3"
ctrlc = { version="3.1.7", optional=true }
derive-new = "0.5.8"
directories-next = "2.0.0"
dirs-next = "2.0.0"
dtparse = "1.2.0"
dunce = "1.0.1"
eml-parser = "0.1.0"
encoding_rs = "0.8.28"
filesize = "0.2.0"
fs_extra = "1.2.0"
futures = { version="0.3.12", features=["compat", "io-compat"] }
getset = "0.1.1"
glob = "0.3.0"
htmlescape = "0.3.1"
ical = "0.7.0"
@ -58,49 +53,45 @@ indexmap = { version="1.7", features=["serde-1"] }
itertools = "0.10.0"
lazy_static = "1.*"
log = "0.4.14"
md5 = "0.7.0"
md-5 = "0.9.1"
meval = "0.2.0"
minus = { version="3.4.0", optional=true, features=["async_std_lib", "search"] }
num-bigint = { version="0.3.1", features=["serde"] }
num-bigint = { version="0.4.3", features=["serde"] }
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"
query_interface = "0.3.5"
quick-xml = "0.22"
rand = "0.8"
rayon = "1.5.0"
regex = "1.4.3"
reqwest = {version = "0.11", optional = true }
roxmltree = "0.14.0"
rust-embed = "5.9.0"
rustyline = { version="8.1.0", optional=true }
rustyline = { version="9.0.0", optional=true }
serde = { version="1.0.123", features=["derive"] }
serde_bytes = "0.11.5"
serde_ini = "0.2.0"
serde_json = "1.0.61"
serde_urlencoded = "0.7.0"
serde_yaml = "0.8.16"
sha2 = "0.9.3"
strip-ansi-escapes = "0.1.0"
sxd-document = "0.3.2"
sxd-xpath = "0.4.2"
tempfile = "3.2.0"
sysinfo = { version = "0.21.2", optional = true }
thiserror = "1.0.26"
term = { version="0.7.0", optional=true }
term_size = "0.3.2"
termcolor = "1.1.2"
titlecase = "1.1.0"
tokio = { version = "1", features = ["rt-multi-thread"], optional = true }
toml = "0.5.8"
trash = { version="1.3.0", optional=true }
trash = { version = "2.0.2", optional = true }
unicode-segmentation = "1.8"
url = "2.2.0"
uuid_crate = { package="uuid", version="0.8.2", features=["v4"], optional=true }
which = { version="4.1.0", optional=true }
zip = { version="0.5.9", optional=true }
digest = "0.9.0"
[dependencies.polars]
version = "0.14.5"
version = "0.17.0"
optional = true
features = ["parquet", "json", "random", "pivot", "strings", "is_in"]
default-features = false
features = ["docs", "zip_with", "csv-file", "temporal", "performant", "pretty_fmt", "dtype-slim", "parquet", "json", "random", "pivot", "strings", "is_in", "cum_agg", "rolling_window"]
[target.'cfg(unix)'.dependencies]
umask = "1.0.0"
@ -112,13 +103,8 @@ users = "0.11.0"
# [target.'cfg(not(windows))'.dependencies]
# num-format = { version = "0.4", features = ["with-system-locale"] }
[dependencies.rusqlite]
features = ["bundled", "blob"]
optional = true
version = "0.25.3"
[build-dependencies]
shadow-rs = "0.6"
shadow-rs = "0.8.1"
[dev-dependencies]
quickcheck = "1.0.3"
@ -126,9 +112,11 @@ quickcheck_macros = "1.0.0"
hamcrest2 = "0.3.0"
[features]
clipboard-cli = ["arboard"]
rustyline-support = ["rustyline"]
stable = []
trash-support = ["trash"]
table-pager = ["minus", "crossterm"]
dataframe = ["nu-protocol/dataframe", "polars"]
fetch = ["reqwest", "tokio"]
post = ["reqwest", "tokio"]
sys = ["sysinfo"]
ps = ["sysinfo"]

View File

@ -1,7 +0,0 @@
use derive_new::new;
use nu_protocol::hir;
#[derive(new, Debug)]
pub(crate) struct Command {
pub(crate) args: hir::Call,
}

View File

@ -1,10 +1,15 @@
use crate::prelude::*;
use lazy_static::lazy_static;
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 regex::Regex;
#[allow(unused)]
use std::env;
use std::io::Write;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::sync::mpsc;
use std::{borrow::Cow, io::BufReader};
@ -17,6 +22,9 @@ use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
use nu_source::Tag;
#[cfg(feature = "which")]
use which::which_in;
pub(crate) fn run_external_command(
command: ExternalCommand,
context: &mut EvaluationContext,
@ -38,20 +46,16 @@ pub(crate) fn run_external_command(
}
#[allow(unused)]
fn trim_double_quotes(input: &str) -> String {
fn trim_enclosing_quotes(input: &str) -> String {
let mut chars = input.chars();
match (chars.next(), chars.next_back()) {
(Some('"'), Some('"')) => chars.collect(),
(Some('\''), Some('\'')) => chars.collect(),
_ => input.to_string(),
}
}
#[allow(unused)]
fn escape_where_needed(input: &str) -> String {
input.split(' ').join("\\ ").split('\'').join("\\'")
}
fn run_with_stdin(
command: ExternalCommand,
context: &mut EvaluationContext,
@ -104,20 +108,14 @@ fn run_with_stdin(
let process_args = command_args
.iter()
.map(|(arg, _is_literal)| {
let arg = nu_path::expand_tilde_string(Cow::Borrowed(arg));
let arg = nu_path::expand_tilde(arg).to_string_lossy().to_string();
#[cfg(not(windows))]
{
if !_is_literal {
let escaped = escape_double_quotes(&arg);
add_double_quotes(&escaped)
arg
} else {
let trimmed = trim_double_quotes(&arg);
if trimmed != arg {
escape_where_needed(&trimmed)
} else {
trimmed
}
trim_enclosing_quotes(&arg)
}
}
#[cfg(windows)]
@ -125,7 +123,7 @@ fn run_with_stdin(
if let Some(unquoted) = remove_quotes(&arg) {
unquoted.to_string()
} else {
arg.to_string()
arg
}
}
})
@ -141,19 +139,18 @@ fn run_with_stdin(
)
}
fn spawn(
command: &ExternalCommand,
path: &str,
args: &[String],
input: InputStream,
external_redirection: ExternalRedirection,
scope: &Scope,
) -> Result<InputStream, ShellError> {
let command = command.clone();
/// Spawn a direct exe
#[allow(unused)]
fn spawn_exe(full_path: PathBuf, args: &[String]) -> Command {
let mut process = Command::new(full_path);
for arg in args {
process.arg(&arg);
}
process
}
let mut process = {
#[cfg(windows)]
{
/// Spawn a cmd command with `cmd /c args...`
fn spawn_cmd_command(command: &ExternalCommand, args: &[String]) -> Command {
let mut process = Command::new("cmd");
process.arg("/c");
process.arg(&command.name);
@ -167,15 +164,85 @@ fn spawn(
process
}
#[cfg(not(windows))]
{
let cmd_with_args = vec![command.name.clone(), args.join(" ")].join(" ");
fn has_unsafe_shell_characters(arg: &str) -> bool {
lazy_static! {
static ref RE: Regex = Regex::new(r"[^\w@%+=:,./-]").expect("regex to be valid");
}
RE.is_match(arg)
}
fn shell_arg_escape(arg: &str) -> String {
match arg {
"" => String::from("''"),
s if !has_unsafe_shell_characters(s) => String::from(s),
_ => {
let single_quotes_escaped = arg.split('\'').join("'\"'\"'");
format!("'{}'", single_quotes_escaped)
}
}
}
/// Spawn a sh command with `sh -c args...`
fn spawn_sh_command(command: &ExternalCommand, args: &[String]) -> Command {
let joined_and_escaped_arguments = args.iter().map(|arg| shell_arg_escape(arg)).join(" ");
let cmd_with_args = vec![command.name.clone(), joined_and_escaped_arguments].join(" ");
let mut process = Command::new("sh");
process.arg("-c").arg(cmd_with_args);
process
}
};
/// a function to spawn any external command
#[allow(unused)] // for minimal builds cwd is unused
fn spawn_any(command: &ExternalCommand, args: &[String], cwd: &str) -> Command {
// resolve the executable name if it is spawnable directly
#[cfg(feature = "which")]
// TODO add more available paths to `env::var_os("PATH")`?
if let Result::Ok(full_path) = which_in(&command.name, env::var_os("PATH"), cwd) {
if let Some(extension) = full_path.extension() {
#[cfg(windows)]
if extension.eq_ignore_ascii_case("exe") {
// if exe spawn it directly
return spawn_exe(full_path, args);
} else {
// TODO implement special care for various executable types such as .bat, .ps1, .cmd, etc
// https://github.com/mklement0/Native/blob/e0e0b8785cad39a73053e35084d1f60d87fbac58/Native.psm1#L749
// otherwise shell out to cmd
return spawn_cmd_command(command, args);
}
#[cfg(not(windows))]
if !["sh", "bash"]
.iter()
.any(|ext| extension.eq_ignore_ascii_case(ext))
{
// if exe spawn it directly
return spawn_exe(full_path, args);
} else {
// otherwise shell out to sh
return spawn_sh_command(command, args);
}
}
}
// in all the other cases shell out
if cfg!(windows) {
spawn_cmd_command(command, args)
} else {
// TODO what happens if that os doesn't support spawning sh?
spawn_sh_command(command, args)
}
}
fn spawn(
command: &ExternalCommand,
path: &str,
args: &[String],
input: InputStream,
external_redirection: ExternalRedirection,
scope: &Scope,
) -> Result<InputStream, ShellError> {
let command = command.clone();
let mut process = spawn_any(&command, args, path);
process.current_dir(path);
trace!(target: "nu::run::external", "cwd = {:?}", &path);
@ -454,7 +521,7 @@ fn spawn(
Ok(stream.into_input_stream())
}
Err(e) => Err(ShellError::labeled_error(
format!("{}", e),
e.to_string(),
"failed to spawn",
&command.name_tag,
)),

View File

@ -1,5 +1 @@
mod dynamic;
pub(crate) mod external;
#[allow(unused_imports)]
pub(crate) use dynamic::Command as DynamicCommand;

View File

@ -22,6 +22,7 @@ impl WholeStreamCommand for Histogram {
None,
)
.rest(
"rest",
SyntaxShape::ColumnPath,
"column name to give the histogram's frequency column",
)

View File

@ -38,7 +38,7 @@ impl WholeStreamCommand for SubCommand {
},
Example {
description: "Set coloring options",
example: "config set color_config [[header_align header_bold]; [left $true]]",
example: "config set color_config [[header_align header_color]; [left white_bold]]",
result: None,
},
Example {

View File

@ -13,6 +13,7 @@ impl WholeStreamCommand for SubCommand {
fn signature(&self) -> Signature {
Signature::build("into binary").rest(
"rest",
SyntaxShape::ColumnPath,
"column paths to convert to binary (for table input)",
)

View File

@ -0,0 +1,118 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"into column-path"
}
fn signature(&self) -> Signature {
Signature::build("into column-path").rest(
"rest",
SyntaxShape::ColumnPath,
"values to convert to column path",
)
}
fn usage(&self) -> &str {
"Convert value to column path"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
into_filepath(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Convert string to column path in table",
example: "echo [[name]; ['/dev/null'] ['C:\\Program Files'] ['../../Cargo.toml']] | into column-path name",
result: Some(vec![
UntaggedValue::row(indexmap! {
"name".to_string() => UntaggedValue::column_path("/dev/null", Span::unknown()).into(),
})
.into(),
UntaggedValue::row(indexmap! {
"name".to_string() => UntaggedValue::column_path("C:\\Program Files", Span::unknown()).into(),
})
.into(),
UntaggedValue::row(indexmap! {
"name".to_string() => UntaggedValue::column_path("../../Cargo.toml", Span::unknown()).into(),
})
.into(),
]),
},
Example {
description: "Convert string to column path",
example: "echo 'Cargo.toml' | into column-path",
result: Some(vec![UntaggedValue::column_path("Cargo.toml", Span::unknown()).into()]),
},
]
}
}
fn into_filepath(args: CommandArgs) -> Result<OutputStream, ShellError> {
let column_paths: Vec<ColumnPath> = args.rest(0)?;
Ok(args
.input
.map(move |v| {
if column_paths.is_empty() {
action(&v, v.tag())
} else {
let mut ret = v;
for path in &column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
)?;
}
Ok(ret)
}
})
.into_input_stream())
}
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
let tag = tag.into();
match &input.value {
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::column_path(
match prim {
Primitive::String(a_string) => a_string,
_ => {
return Err(ShellError::unimplemented(
"'into column-path' for non-string primitives",
))
}
},
Span::unknown(),
)
.into_value(&tag)),
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
"specify column name to use, with 'into column-path COLUMN'",
"found table",
tag,
)),
_ => Err(ShellError::unimplemented(
"'into column-path' 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

@ -0,0 +1,127 @@
use std::path::PathBuf;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"into path"
}
fn signature(&self) -> Signature {
Signature::build("into path").rest(
"rest",
SyntaxShape::ColumnPath,
"column paths to convert to filepath (for table input)",
)
}
fn usage(&self) -> &str {
"Convert value to filepath"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
into_filepath(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Convert string to filepath in table",
example: "echo [[name]; ['/dev/null'] ['C:\\Program Files'] ['../../Cargo.toml']] | into path name",
result: Some(vec![
UntaggedValue::row(indexmap! {
"name".to_string() => UntaggedValue::filepath("/dev/null").into(),
})
.into(),
UntaggedValue::row(indexmap! {
"name".to_string() => UntaggedValue::filepath("C:\\Program Files").into(),
})
.into(),
UntaggedValue::row(indexmap! {
"name".to_string() => UntaggedValue::filepath("../../Cargo.toml").into(),
})
.into(),
]),
},
Example {
description: "Convert string to filepath",
example: "echo 'Cargo.toml' | into path",
result: Some(vec![UntaggedValue::filepath("Cargo.toml").into()]),
},
]
}
}
fn into_filepath(args: CommandArgs) -> Result<OutputStream, ShellError> {
let column_paths: Vec<ColumnPath> = args.rest(0)?;
Ok(args
.input
.map(move |v| {
if column_paths.is_empty() {
action(&v, v.tag())
} else {
let mut ret = v;
for path in &column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
)?;
}
Ok(ret)
}
})
.into_input_stream())
}
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
let tag = tag.into();
match &input.value {
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::filepath(match prim {
Primitive::String(a_string) => match filepath_from_string(a_string, &tag) {
Ok(n) => n,
Err(e) => {
return Err(e);
}
},
Primitive::FilePath(a_filepath) => a_filepath.clone(),
_ => {
return Err(ShellError::unimplemented(
"'into path' for non-string primitives",
))
}
})
.into_value(&tag)),
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
"specify column name to use, with 'into path COLUMN'",
"found table",
tag,
)),
_ => Err(ShellError::unimplemented(
"'into path' for unsupported type",
)),
}
}
fn filepath_from_string(a_string: &str, _tag: &Tag) -> Result<PathBuf, ShellError> {
Ok(PathBuf::from(a_string))
}
#[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

@ -0,0 +1,182 @@
use std::convert::TryInto;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
use num_bigint::ToBigInt;
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"into filesize"
}
fn signature(&self) -> Signature {
Signature::build("into filesize").rest(
"rest",
SyntaxShape::ColumnPath,
"column paths to convert to filesize (for table input)",
)
}
fn usage(&self) -> &str {
"Convert value to filesize"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
into_filesize(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Convert string to filesize in table",
example: "echo [[bytes]; ['5'] [3.2] [4] [2kb]] | into filesize bytes",
result: Some(vec![
UntaggedValue::row(indexmap! {
"bytes".to_string() => UntaggedValue::filesize(5).into(),
})
.into(),
UntaggedValue::row(indexmap! {
"bytes".to_string() => UntaggedValue::filesize(3).into(),
})
.into(),
UntaggedValue::row(indexmap! {
"bytes".to_string() => UntaggedValue::filesize(4).into(),
})
.into(),
UntaggedValue::row(indexmap! {
"bytes".to_string() => UntaggedValue::filesize(2000).into(),
})
.into(),
]),
},
Example {
description: "Convert string to filesize",
example: "echo '2' | into filesize",
result: Some(vec![UntaggedValue::filesize(2).into()]),
},
Example {
description: "Convert decimal to filesize",
example: "echo 8.3 | into filesize",
result: Some(vec![UntaggedValue::filesize(8).into()]),
},
Example {
description: "Convert int to filesize",
example: "echo 5 | into filesize",
result: Some(vec![UntaggedValue::filesize(5).into()]),
},
Example {
description: "Convert file size to filesize",
example: "echo 4KB | into filesize",
result: Some(vec![UntaggedValue::filesize(4000).into()]),
},
]
}
}
fn into_filesize(args: CommandArgs) -> Result<OutputStream, ShellError> {
let column_paths: Vec<ColumnPath> = args.rest(0)?;
Ok(args
.input
.map(move |v| {
if column_paths.is_empty() {
action(&v, v.tag())
} else {
let mut ret = v;
for path in &column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
)?;
}
Ok(ret)
}
})
.into_input_stream())
}
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
let tag = tag.into();
match &input.value {
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::filesize(match prim {
Primitive::String(a_string) => match int_from_string(a_string.trim(), &tag) {
Ok(n) => n,
Err(e) => {
return Err(e);
}
},
Primitive::Decimal(dec) => match dec.to_bigint() {
Some(n) => match n.to_u64() {
Some(i) => i,
None => {
return Err(ShellError::unimplemented(
"failed to convert decimal to filesize",
));
}
},
None => {
return Err(ShellError::unimplemented(
"failed to convert decimal to filesize",
));
}
},
Primitive::Int(n_ref) => (*n_ref).try_into().map_err(|_| {
ShellError::unimplemented("cannot convert negative integer to filesize")
})?,
Primitive::Filesize(a_filesize) => *a_filesize,
_ => {
return Err(ShellError::unimplemented(
"'into filesize' for non-numeric primitives",
))
}
})
.into_value(&tag)),
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
"specify column name to use, with 'into filesize COLUMN'",
"found table",
tag,
)),
_ => Err(ShellError::unimplemented(
"'into filesize' for unsupported type",
)),
}
}
fn int_from_string(a_string: &str, tag: &Tag) -> Result<u64, ShellError> {
match a_string.parse::<u64>() {
Ok(n) => Ok(n),
Err(_) => match a_string.parse::<f64>() {
Ok(f) => match f.to_u64() {
Some(i) => Ok(i),
None => Err(ShellError::labeled_error(
"Could not convert string value to filesize",
"original value",
tag.clone(),
)),
},
Err(_) => Err(ShellError::labeled_error(
"Could not convert string value to filesize",
"original value",
tag.clone(),
)),
},
}
}
#[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

@ -13,6 +13,7 @@ impl WholeStreamCommand for SubCommand {
fn signature(&self) -> Signature {
Signature::build("into int").rest(
"rest",
SyntaxShape::ColumnPath,
"column paths to convert to int (for table input)",
)

View File

@ -1,9 +1,15 @@
mod binary;
mod column_path;
mod command;
mod filepath;
mod filesize;
mod int;
pub mod string;
pub use self::filesize::SubCommand as IntoFilesize;
pub use binary::SubCommand as IntoBinary;
pub use column_path::SubCommand as IntoColumnPath;
pub use command::Command as Into;
pub use filepath::SubCommand as IntoFilepath;
pub use int::SubCommand as IntoInt;
pub use string::SubCommand as IntoString;

View File

@ -20,6 +20,7 @@ impl WholeStreamCommand for SubCommand {
fn signature(&self) -> Signature {
Signature::build("into string")
.rest(
"rest",
SyntaxShape::ColumnPath,
"column paths to convert to string (for table input)",
)
@ -158,7 +159,7 @@ pub fn action(
}
fn format_int(int: i64) -> String {
format!("{}", int)
int.to_string()
// TODO once platform-specific dependencies are stable (see Cargo.toml)
// #[cfg(windows)]
@ -175,7 +176,7 @@ fn format_int(int: i64) -> String {
}
fn format_bigint(int: &BigInt) -> String {
format!("{}", int)
int.to_string()
// TODO once platform-specific dependencies are stable (see Cargo.toml)
// #[cfg(windows)]
@ -229,7 +230,7 @@ fn format_decimal(mut decimal: BigDecimal, digits: Option<u64>, group_digits: bo
let format_default_loc = |int_part: BigInt| {
let loc = Locale::en;
//TODO: when num_format is available for recent bigint, replace this with the locale-based format
let (int_str, sep) = (format!("{}", int_part), String::from(loc.decimal()));
let (int_str, sep) = (int_part.to_string(), String::from(loc.decimal()));
format!("{}{}{}", int_str, sep, dec_str)
};

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape};
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
pub struct Alias;
@ -15,7 +15,7 @@ impl WholeStreamCommand for Alias {
Signature::build("alias")
.required("name", SyntaxShape::String, "the name of the alias")
.required("equals", SyntaxShape::String, "the equals sign")
.rest(SyntaxShape::Any, "the expansion for the alias")
.rest("rest", SyntaxShape::Any, "the expansion for the alias")
}
fn usage(&self) -> &str {
@ -35,6 +35,18 @@ impl WholeStreamCommand for Alias {
}
}
pub fn alias(_: CommandArgs) -> Result<OutputStream, ShellError> {
pub fn alias(args: CommandArgs) -> Result<OutputStream, ShellError> {
// TODO: is there a better way of checking whether no arguments were passed?
if args.nth(0).is_none() {
let aliases = UntaggedValue::string(
&args
.scope()
.get_aliases()
.iter()
.map(|val| format!("{} = '{}'", val.0, val.1.iter().map(|x| &x.item).join(" ")))
.join("\n"),
);
return Ok(OutputStream::one(aliases));
}
Ok(OutputStream::empty())
}

View File

@ -31,7 +31,7 @@ pub fn describe(args: CommandArgs) -> Result<ActionStream, ShellError> {
Ok(args
.input
.map(|row| {
let name = value::format_type(&row, 100);
let name = value::plain_type(&row, 100);
ReturnSuccess::value(
UntaggedValue::string(name).into_value(Tag::unknown_anchor(row.tag.span)),
)

View File

@ -27,7 +27,7 @@ impl WholeStreamCommand for Do {
"ignore errors as the block runs",
Some('i'),
)
.rest(SyntaxShape::Any, "the parameter(s) for the block")
.rest("rest", SyntaxShape::Any, "the parameter(s) for the block")
}
fn usage(&self) -> &str {

View File

@ -12,7 +12,7 @@ impl WholeStreamCommand for Echo {
}
fn signature(&self) -> Signature {
Signature::build("echo").rest(SyntaxShape::Any, "the values to echo")
Signature::build("echo").rest("rest", SyntaxShape::Any, "the values to echo")
}
fn usage(&self) -> &str {

View File

@ -0,0 +1,109 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"error make"
}
fn signature(&self) -> Signature {
Signature::build("error make")
}
fn usage(&self) -> &str {
"Create an error."
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let input = args.input;
Ok(input
.map(|value| {
make_error(&value)
.map(|err| UntaggedValue::Error(err).into_value(value.tag()))
.unwrap_or_else(|| {
UntaggedValue::Error(ShellError::untagged_runtime_error(
"Creating error value not supported.",
))
.into_value(value.tag())
})
})
.into_output_stream())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Creates a labeled error",
example: r#"[
[ msg, labels, span];
["The message", "Helpful message here", ([[start, end]; [0, 141]])]
] | error make"#,
result: None,
}]
}
}
fn make_error(value: &Value) -> Option<ShellError> {
if let Value {
value: UntaggedValue::Row(dict),
..
} = value
{
let msg = dict.get_data_by_key("msg".spanned_unknown());
let labels =
dict.get_data_by_key("labels".spanned_unknown())
.and_then(|table| match &table.value {
UntaggedValue::Table(_) => table
.table_entries()
.map(|value| value.as_string().ok())
.collect(),
UntaggedValue::Primitive(Primitive::String(label)) => {
Some(vec![label.to_string()])
}
_ => None,
});
let _anchor = dict.get_data_by_key("tag".spanned_unknown());
let span = dict.get_data_by_key("span".spanned_unknown());
if msg.is_none() || labels.is_none() || span.is_none() {
return None;
}
let msg = msg.and_then(|msg| msg.as_string().ok());
if let Some(labels) = labels {
if labels.is_empty() {
return None;
}
return Some(ShellError::labeled_error(
msg.expect("Message will always be present."),
&labels[0],
span.map(|data| match data {
Value {
value: UntaggedValue::Row(vals),
..
} => match (vals.entries.get("start"), vals.entries.get("end")) {
(Some(start), Some(end)) => {
let start = start.as_usize().ok().unwrap_or(0);
let end = end.as_usize().ok().unwrap_or(0);
Span::new(start, end)
}
(_, _) => Span::unknown(),
},
_ => Span::unknown(),
})
.unwrap_or_else(Span::unknown),
));
}
}
None
}

View File

@ -0,0 +1,3 @@
mod make;
pub use make::SubCommand as ErrorMake;

View File

@ -0,0 +1,110 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
pub struct Find;
impl WholeStreamCommand for Find {
fn name(&self) -> &str {
"find"
}
fn signature(&self) -> Signature {
Signature::build("find").rest("rest", SyntaxShape::String, "search term")
}
fn usage(&self) -> &str {
"Find text in the output of a previous command"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
find(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Search pipeline output for multiple terms",
example: r#"ls | find toml md sh"#,
result: None,
},
Example {
description: "Search strings for term(s)",
example: r#"echo Cargo.toml | find toml"#,
result: Some(vec![Value::from("Cargo.toml")]),
},
Example {
description: "Search a number list for term(s)",
example: r#"[1 2 3 4 5] | find 5"#,
result: Some(vec![UntaggedValue::int(5).into()]),
},
Example {
description: "Search string list for term(s)",
example: r#"[moe larry curly] | find l"#,
result: Some(vec![Value::from("larry"), Value::from("curly")]),
},
]
}
}
fn row_contains(row: &Dictionary, search_terms: Vec<String>) -> bool {
for term in search_terms {
for (k, v) in &row.entries {
let key = k.to_string().trim().to_lowercase();
let value = v.convert_to_string().trim().to_lowercase();
if key.contains(&term) || value.contains(&term) {
return true;
}
}
}
false
}
fn find(args: CommandArgs) -> Result<OutputStream, ShellError> {
let rest: Vec<Value> = args.rest(0)?;
Ok(args
.input
.filter(move |row| match &row.value {
UntaggedValue::Row(row) => {
let sterms: Vec<String> = rest
.iter()
.map(|t| t.convert_to_string().trim().to_lowercase())
.collect();
row_contains(row, sterms)
}
UntaggedValue::Primitive(_p) => {
// eprint!("prim {}", p.type_name());
let sterms: Vec<String> = rest
.iter()
.map(|t| t.convert_to_string().trim().to_lowercase())
.collect();
let prim_string = &row.convert_to_string().trim().to_lowercase();
for term in sterms {
if prim_string.contains(&term) {
return true;
}
}
false
}
_ => false,
})
.into_output_stream())
}
#[cfg(test)]
mod tests {
use super::Find;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(Find {})
}
}

View File

@ -1,14 +1,12 @@
use crate::prelude::*;
use crate::TaggedListBuilder;
use nu_engine::documentation::generate_docs;
use nu_engine::{Command, WholeStreamCommand};
use nu_engine::{documentation::generate_docs, Command, WholeStreamCommand};
use nu_errors::ShellError;
use nu_protocol::{
NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder,
UntaggedValue, Value,
Dictionary, NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape,
TaggedDictBuilder, UntaggedValue, Value,
};
use nu_source::Tag;
use nu_source::{SpannedItem, Tagged};
use nu_source::{SpannedItem, Tag, Tagged};
use nu_value_ext::ValueExt;
pub struct Help;
@ -19,7 +17,18 @@ impl WholeStreamCommand for Help {
}
fn signature(&self) -> Signature {
Signature::build("help").rest(SyntaxShape::String, "the name of command to get help on")
Signature::build("help")
.rest(
"rest",
SyntaxShape::String,
"the name of command to get help on",
)
.named(
"find",
SyntaxShape::String,
"string to find in command usage",
Some('f'),
)
}
fn usage(&self) -> &str {
@ -29,14 +38,81 @@ impl WholeStreamCommand for Help {
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
help(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "show all commands and sub-commands",
example: "help commands",
result: None,
},
Example {
description: "generate documentation",
example: "help generate_docs",
result: None,
},
Example {
description: "show help for single command",
example: "help match",
result: None,
},
Example {
description: "show help for single sub-command",
example: "help str lpad",
result: None,
},
Example {
description: "search for string in command usage",
example: "help --find char",
result: None,
},
]
}
}
fn help(args: CommandArgs) -> Result<ActionStream, ShellError> {
let name = args.call_info.name_tag.clone();
let scope = args.scope().clone();
let find: Option<Tagged<String>> = args.get_flag("find")?;
let rest: Vec<Tagged<String>> = args.rest(0)?;
if let Some(f) = find {
let search_string = f.item;
let full_commands = scope.get_commands_info();
let mut found_cmds_vec = Vec::new();
for (key, cmd) in full_commands {
let mut indexmap = IndexMap::new();
let c = cmd.usage().to_string();
let e = cmd.extra_usage().to_string();
if key.to_lowercase().contains(&search_string)
|| c.to_lowercase().contains(&search_string)
|| e.to_lowercase().contains(&search_string)
{
indexmap.insert(
"name".to_string(),
UntaggedValue::string(key).into_value(&name),
);
indexmap.insert(
"usage".to_string(),
UntaggedValue::string(cmd.usage().to_string()).into_value(&name),
);
indexmap.insert(
"extra_usage".to_string(),
UntaggedValue::string(cmd.extra_usage().to_string()).into_value(&name),
);
found_cmds_vec
.push(UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&name));
}
}
return Ok(found_cmds_vec.into_iter().into_action_stream());
}
if !rest.is_empty() {
if rest[0].item == "commands" {
let mut sorted_names = scope.get_command_names();
@ -226,7 +302,7 @@ pub fn signature_dict(signature: Signature, tag: impl Into<Tag>) -> Value {
let tag = tag.into();
let mut sig = TaggedListBuilder::new(&tag);
for arg in signature.positional.iter() {
for arg in &signature.positional {
let is_required = matches!(arg.0, PositionalType::Mandatory(_, _));
sig.push_value(for_spec(arg.0.name(), "argument", is_required, &tag));
@ -237,7 +313,7 @@ pub fn signature_dict(signature: Signature, tag: impl Into<Tag>) -> Value {
sig.push_value(for_spec("rest", "argument", is_required, &tag));
}
for (name, ty) in signature.named.iter() {
for (name, ty) in &signature.named {
match ty.0 {
NamedType::Mandatory(_, _) => sig.push_value(for_spec(name, "flag", true, &tag)),
NamedType::Optional(_, _) => sig.push_value(for_spec(name, "flag", false, &tag)),

View File

@ -100,18 +100,15 @@ fn if_command(args: CommandArgs) -> Result<OutputStream, ShellError> {
context.scope.add_vars(&condition.captured.entries);
//FIXME: should we use the scope that's brought in as well?
let condition = evaluate_baseline_expr(cond, &*context);
match condition {
let condition = evaluate_baseline_expr(cond, &context);
let result = match condition {
Ok(condition) => match condition.as_bool() {
Ok(b) => {
let result = if b {
run_block(&then_case.block, &*context, input, external_redirection)
if b {
run_block(&then_case.block, &context, input, external_redirection)
} else {
run_block(&else_case.block, &*context, input, external_redirection)
};
context.scope.exit_scope();
result
run_block(&else_case.block, &context, input, external_redirection)
}
}
Err(e) => Ok(OutputStream::from_stream(
vec![UntaggedValue::Error(e).into_untagged_value()].into_iter(),
@ -120,13 +117,16 @@ fn if_command(args: CommandArgs) -> Result<OutputStream, ShellError> {
Err(e) => Ok(OutputStream::from_stream(
vec![UntaggedValue::Error(e).into_untagged_value()].into_iter(),
)),
}
};
context.scope.exit_scope();
result
}
#[cfg(test)]
mod tests {
use super::If;
use super::ShellError;
use nu_test_support::nu;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
@ -134,4 +134,21 @@ mod tests {
test_examples(If {})
}
#[test]
fn if_doesnt_leak_on_error() {
let actual = nu!(
".",
r#"
def test-leak [] {
let var = "hello"
if 0 == "" {echo ok} {echo not}
}
test-leak
echo $var
"#
);
assert!(actual.err.contains("unknown variable"));
}
}

View File

@ -63,7 +63,7 @@ pub fn letcmd(args: CommandArgs) -> Result<ActionStream, ShellError> {
.expect("Internal error: type checker should require args");
let var_name = positional[0].var_name()?;
let rhs_raw = evaluate_baseline_expr(&positional[2], &ctx)?;
let rhs_raw = evaluate_baseline_expr(&positional[2], ctx)?;
let tag: Tag = positional[2].span.into();
let rhs: CapturedBlock = FromValue::from_value(&rhs_raw)?;
@ -98,7 +98,7 @@ pub fn letcmd(args: CommandArgs) -> Result<ActionStream, ShellError> {
};
ctx.scope.enter_scope();
let value = evaluate_baseline_expr(expr, &ctx);
let value = evaluate_baseline_expr(expr, ctx);
ctx.scope.exit_scope();
let value = value?;

View File

@ -4,6 +4,8 @@ mod def;
mod describe;
mod do_;
pub(crate) mod echo;
mod error;
mod find;
mod help;
mod history;
mod if_;
@ -13,6 +15,8 @@ mod nu_plugin;
mod nu_signature;
mod source;
mod tags;
mod tutor;
mod unalias;
mod version;
pub use self::nu_plugin::SubCommand as NuPlugin;
@ -25,6 +29,8 @@ pub use def::Def;
pub use describe::Describe;
pub use do_::Do;
pub use echo::Echo;
pub use error::*;
pub use find::Find;
pub use help::Help;
pub use history::History;
pub use if_::If;
@ -32,4 +38,6 @@ pub use ignore::Ignore;
pub use let_::Let;
pub use source::Source;
pub use tags::Tags;
pub use tutor::Tutor;
pub use unalias::Unalias;
pub use version::{version, Version};

View File

@ -4,7 +4,7 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_path::canonicalize;
use nu_path::canonicalize_with;
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged;
@ -56,7 +56,7 @@ impl WholeStreamCommand for SubCommand {
tag,
}) = load_path
{
let path = canonicalize(shell_manager.path(), load_path).map_err(|_| {
let path = canonicalize_with(load_path, shell_manager.path()).map_err(|_| {
ShellError::labeled_error(
"Cannot load plugins from directory",
"directory not found",

View File

@ -14,6 +14,8 @@ impl WholeStreamCommand for Command {
.switch("stdin", "redirect stdin", None)
.switch("skip-plugins", "do not load plugins", None)
.switch("no-history", "don't save history", None)
.switch("perf", "show startup performance metrics", None)
.switch("login", "start Nu as if it was a login shell", Some('l'))
.named(
"commands",
SyntaxShape::String,
@ -32,7 +34,7 @@ impl WholeStreamCommand for Command {
"loglevel",
SyntaxShape::String,
"LEVEL: error, warn, info, debug, trace",
Some('l'),
None,
)
.named(
"config-file",
@ -40,7 +42,7 @@ impl WholeStreamCommand for Command {
"custom configuration source file",
None,
)
.rest(SyntaxShape::String, "source file(s) to run")
.rest("rest", SyntaxShape::String, "source file(s) to run")
}
fn usage(&self) -> &str {

View File

@ -2,11 +2,11 @@ use crate::prelude::*;
use nu_engine::{script, WholeStreamCommand};
use nu_errors::ShellError;
use nu_path::expand_path;
use nu_path::{canonicalize, canonicalize_with};
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
use std::{borrow::Cow, path::Path};
use std::path::Path;
pub struct Source;
@ -32,7 +32,7 @@ impl WholeStreamCommand for Source {
"Runs a script file in the current context."
}
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
source(args)
}
@ -41,31 +41,83 @@ impl WholeStreamCommand for Source {
}
}
pub fn source(args: CommandArgs) -> Result<ActionStream, ShellError> {
pub fn source(args: CommandArgs) -> Result<OutputStream, ShellError> {
let ctx = &args.context;
let filename: Tagged<String> = args.req(0)?;
let source_file = Path::new(&filename.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.
let contents = std::fs::read_to_string(&expand_path(Cow::Borrowed(Path::new(&filename.item))));
match contents {
Ok(contents) => {
let result = script::run_script_standalone(contents, true, &ctx, false);
let lib_dirs = &ctx
.configs()
.lock()
.global_config
.as_ref()
.map(|configuration| match configuration.var("lib_dirs") {
Some(paths) => paths
.table_entries()
.cloned()
.map(|path| path.as_string())
.collect(),
None => vec![],
});
if let Some(dir) = lib_dirs {
for lib_path in dir {
match lib_path {
Ok(name) => {
let path = if let Ok(p) = canonicalize_with(&source_file, name) {
p
} else {
continue;
};
if let Ok(contents) = std::fs::read_to_string(path) {
let result = script::run_script_standalone(contents, true, ctx, false);
if let Err(err) = result {
ctx.error(err);
}
Ok(ActionStream::empty())
return Ok(OutputStream::empty());
}
Err(_) => {
}
Err(reason) => {
ctx.error(reason.clone());
}
}
}
}
let path = canonicalize(source_file).map_err(|e| {
ShellError::labeled_error(
format!("Can't load source file. Reason: {}", e.to_string()),
"Can't load this file",
filename.span(),
)
})?;
let contents = std::fs::read_to_string(path);
match contents {
Ok(contents) => {
let result = script::run_script_standalone(contents, true, ctx, false);
if let Err(err) = result {
ctx.error(err);
}
Ok(OutputStream::empty())
}
Err(e) => {
ctx.error(ShellError::labeled_error(
"Can't load file to source",
"can't load file",
format!("Can't load source file. Reason: {}", e.to_string()),
"Can't load this file",
filename.span(),
));
Ok(ActionStream::empty())
Ok(OutputStream::empty())
}
}
}

View File

@ -1,7 +1,7 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
pub struct Tags;
@ -18,37 +18,64 @@ impl WholeStreamCommand for Tags {
"Read the tags (metadata) for values."
}
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(tags(args))
}
}
fn tags(args: CommandArgs) -> ActionStream {
args.input
.map(move |v| {
let mut tags = TaggedDictBuilder::new(v.tag());
{
let anchor = v.anchor();
let span = v.tag.span;
let mut dict = TaggedDictBuilder::new(v.tag());
dict.insert_untagged("start", UntaggedValue::int(span.start() as i64));
dict.insert_untagged("end", UntaggedValue::int(span.end() as i64));
tags.insert_value("span", dict.into_value());
fn build_tag_table(tag: impl Into<Tag>) -> Value {
let tag = tag.into();
let span = tag.span;
match anchor {
Some(AnchorLocation::File(source)) => {
tags.insert_untagged("anchor", UntaggedValue::string(source));
}
Some(AnchorLocation::Url(source)) => {
tags.insert_untagged("anchor", UntaggedValue::string(source));
}
_ => {}
}
TaggedDictBuilder::build(tag.clone(), |tags| {
if let Some(anchor) = anchor_as_value(&tag) {
tags.insert_value("anchor", anchor);
}
tags.into_value()
tags.insert_value(
"span",
TaggedDictBuilder::build(tag.clone(), |span_dict| {
span_dict.insert_untagged("start", UntaggedValue::int(span.start() as i64));
span_dict.insert_untagged("end", UntaggedValue::int(span.end() as i64));
}),
);
})
.into_action_stream()
}
fn tags(args: CommandArgs) -> OutputStream {
if args.input.is_empty() {
OutputStream::one(build_tag_table(&args.name_tag()))
} else {
args.input
.map(move |v| build_tag_table(v.tag()))
.into_output_stream()
}
}
fn anchor_as_value(tag: &Tag) -> Option<Value> {
let anchor = tag.anchor.as_ref();
anchor.as_ref()?;
Some(TaggedDictBuilder::build(tag, |table| {
let value = match anchor {
Some(AnchorLocation::File(path)) => {
Some(("file", UntaggedValue::from(path.to_string())))
}
Some(AnchorLocation::Url(destination)) => {
Some(("url", UntaggedValue::from(destination.to_string())))
}
Some(AnchorLocation::Source(text)) => Some((
"source",
UntaggedValue::Primitive(Primitive::String(text.to_string())),
)),
None => None,
};
if let Some((key, value)) = value {
table.insert_untagged(key, value);
}
}))
}
#[cfg(test)]

View File

@ -0,0 +1,407 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
pub struct Tutor;
impl WholeStreamCommand for Tutor {
fn name(&self) -> &str {
"tutor"
}
fn signature(&self) -> Signature {
Signature::build("tutor")
.optional(
"search",
SyntaxShape::String,
"item to search for, or 'list' to list available tutorials",
)
.named(
"find",
SyntaxShape::String,
"Search tutorial for a phrase",
Some('f'),
)
}
fn usage(&self) -> &str {
"Run the tutorial. To begin, run: tutor"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
tutor(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Begin the tutorial",
example: "tutor begin",
result: None,
},
Example {
description: "Search a tutorial by phrase",
example: "tutor -f \"$in\"",
result: None,
},
]
}
}
fn tutor(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.name_tag();
let scope = args.scope().clone();
let search: Option<String> = args.opt(0).unwrap_or(None);
let find: Option<String> = args.get_flag("find")?;
let search_space = [
(vec!["begin"], begin_tutor()),
(
vec!["table", "tables", "row", "rows", "column", "columns"],
table_tutor(),
),
(vec!["cell", "cells"], cell_tutor()),
(
vec![
"expr",
"exprs",
"expressions",
"subexpression",
"subexpressions",
"sub-expression",
"sub-expressions",
],
expression_tutor(),
),
(vec!["echo"], echo_tutor()),
(vec!["each", "iteration", "iter"], each_tutor()),
(
vec!["var", "vars", "variable", "variables"],
variable_tutor(),
),
(vec!["block", "blocks"], block_tutor()),
(vec!["shorthand", "shorthands"], shorthand_tutor()),
];
if let Some(find) = find {
let mut results = vec![];
for search_group in search_space {
if search_group.1.contains(&find) {
results.push(search_group.0[0].to_string())
}
}
let message = format!("You can find '{}' in the following topics:\n{}\n\nYou can learn about a topic using `tutor` followed by the name of the topic.\nFor example: `tutor table` to open the table topic.\n\n",
find,
results.into_iter().map(|x| format!("- {}", x)).join("\n")
);
return Ok(display(tag, &scope, &message));
} else if let Some(search) = search {
for search_group in search_space {
if search_group.0.contains(&search.as_str()) {
return Ok(display(tag, &scope, search_group.1));
}
}
}
Ok(display(tag, &scope, default_tutor()))
}
fn default_tutor() -> &'static str {
r#"
Welcome to the Nushell tutorial!
With the `tutor` command, you'll be able to learn a lot about how Nushell
works along with many fun tips and tricks to speed up everyday tasks.
To get started, you can use `tutor begin`.
"#
}
fn begin_tutor() -> &'static str {
r#"
Nushell is a structured shell and programming language. One way to begin
using it is to try a few of the commands.
The first command to try is `ls`. The `ls` command will show you a list
of the files in the current directory. Notice that these files are shown
as a table. Each column of this table not only tells us what is being
shown, but also gives us a way to work with the data.
You can combine the `ls` command with other commands using the pipeline
symbol '|'. This allows data to flow from one command to the next.
For example, if we only wanted the name column, we could do:
```
ls | select name
```
Notice that we still get a table, but this time it only has one column:
the name column.
You can continue to learn more about tables by running:
```
tutor tables
```
If at any point, you'd like to restart this tutorial, you can run:
```
tutor begin
```
"#
}
fn table_tutor() -> &'static str {
r#"
The most common form of data in Nushell is the table. Tables contain rows and
columns of data. In each cell of the table, there is data that you can access
using Nushell commands.
To get the 3rd row in the table, you can use the `nth` command:
```
ls | nth 2
```
This will get the 3rd (note that `nth` is zero-based) row in the table created
by the `ls` command. You can use `nth` on any table created by other commands
as well.
You can also access the column of data in one of two ways. If you want
to keep the column as part of a new table, you can use `select`.
```
ls | select name
```
This runs `ls` and returns only the "name" column of the table.
If, instead, you'd like to get access to the values inside of the column, you
can use the `get` command.
```
ls | get name
```
This allows us to get to the list of strings that are the filenames rather
than having a full table. In some cases, this can make the names easier to
work with.
You can continue to learn more about working with cells of the table by
running:
```
tutor cells
```
"#
}
fn cell_tutor() -> &'static str {
r#"
Working with cells of data in the table is a key part of working with data in
Nushell. Because of this, there is a rich list of commands to work with cells
as well as handy shorthands for accessing cells.
Cells can hold simple values like strings and numbers, or more complex values
like lists and tables.
To reach a cell of data from a table, you can combine a row operation and a
column operation.
```
ls | nth 4 | get name
```
You can combine these operations into one step using a shortcut.
```
(ls).4.name
```
Names/strings represent columns names and numbers represent row numbers.
The `(ls)` is a form of expression. You can continue to learn more about
expressions by running:
```
tutor expressions
```
You can also learn about these cell shorthands by running:
```
tutor shorthands
```
"#
}
fn expression_tutor() -> &'static str {
r#"
Expressions give you the power to mix calls to commands with math. The
simplest expression is a single value like a string or number.
```
3
```
Expressions can also include math operations like addition or division.
```
10 / 2
```
Normally, an expression is one type of operation: math or commands. You can
mix these types by using subexpressions. Subexpressions are just like
expressions, but they're wrapped in parentheses `()`.
```
10 * (3 + 4)
```
Here we use parentheses to create a higher math precedence in the math
expression.
```
echo (2 + 3)
```
You can continue to learn more about the `echo` command by running:
```
tutor echo
```
"#
}
fn echo_tutor() -> &'static str {
r#"
The `echo` command in Nushell is a powerful tool for not only seeing values,
but also for creating new ones.
```
echo "Hello"
```
You can echo output. This output, if it's not redirected using a "|" pipeline
will be displayed to the screen.
```
echo 1..10
```
You can also use echo to work with individual values of a range. In this
example, `echo` will create the values from 1 to 10 as a list.
```
echo 1 2 3 4 5
```
You can also create lists of values by passing `echo` multiple arguments.
This can be helpful if you want to later processes these values.
The `echo` command can pair well with the `each` command which can run
code on each row, or item, of input.
You can continue to learn more about the `each` command by running:
```
tutor each
```
"#
}
fn each_tutor() -> &'static str {
r#"
The `each` command gives us a way of working with each individual row or
element of a list one at a time. It reads these in from the pipeline and
runs a block on each element. A block is a group of pipelines.
```
echo 1 2 3 | each { $it + 10}
```
This example iterates over each element sent by `echo`, giving us three new
values that are the original value + 10. Here, the `$it` is a variable that
is the name given to the block's parameter by default.
You can learn more about blocks by running:
```
tutor blocks
```
You can also learn more about variables by running:
```
tutor variables
```
"#
}
fn variable_tutor() -> &'static str {
r#"
Variables are an important way to store values to be used later. To create a
variable, you can use the `let` keyword. The `let` command will create a
variable and then assign it a value in one step.
```
let $x = 3
```
Once created, we can refer to this variable by name.
```
$x
```
Nushell also comes with built-in variables. The `$nu` variable is a reserved
variable that contains a lot of information about the currently running
instance of Nushell. The `$it` variable is the name given to block parameters
if you don't specify one. And `$in` is the variable that allows you to work
with all of the data coming in from the pipeline in one place.
"#
}
fn block_tutor() -> &'static str {
r#"
Blocks are a special form of expression that hold code to be run at a later
time. Often, you'll see blocks as one of the arguments given to commands
like `each` and `if`.
```
ls | each {|x| $x.name}
```
The above will create a list of the filenames in the directory.
```
if $true { echo "it's true" } { echo "it's not true" }
```
This `if` call will run the first block if the expression is true, or the
second block if the expression is false.
"#
}
fn shorthand_tutor() -> &'static str {
r#"
You can access cells in a table using a shorthand notation sometimes called a
"column path" or "cell path". These paths allow you to go from a table to
rows, columns, or cells inside of the table.
Shorthand paths are made from rows numbers, column names, or both. You can use
them on any variable or subexpression.
```
$nu.cwd
```
The above accesses the built-in `$nu` variable, gets its table, and then uses
the shorthand path to retrieve only the cell data inside the "cwd" column.
```
(ls).name.4
```
This will retrieve the cell data in the "name" column on the 5th row (note:
row numbers are zero-based).
Rows and columns don't need to come in any specific order. You can get the
same value using:
```
(ls).4.name
```
"#
}
fn display(tag: Tag, scope: &Scope, help: &str) -> OutputStream {
let help = help.split('`');
let mut build = String::new();
let mut code_mode = false;
let palette = nu_engine::DefaultPalette {};
for item in help {
if code_mode {
code_mode = false;
//TODO: support no-color mode
let colored_example = nu_engine::Painter::paint_string(item, scope, &palette);
build.push_str(&colored_example);
} else {
code_mode = true;
build.push_str(item);
}
}
OutputStream::one(UntaggedValue::string(build).into_value(tag))
}
#[cfg(test)]
mod tests {
use super::ShellError;
use super::Tutor;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(Tutor {})
}
}

View File

@ -0,0 +1,37 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape};
pub struct Unalias;
impl WholeStreamCommand for Unalias {
fn name(&self) -> &str {
"unalias"
}
fn signature(&self) -> Signature {
Signature::build("unalias").required("name", SyntaxShape::String, "the name of the alias")
}
fn usage(&self) -> &str {
"Removes an alias"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
unalias(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Remove the 'v' alias",
example: "unalias v",
result: None,
}]
}
}
pub fn unalias(_: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::empty())
}

View File

@ -221,11 +221,6 @@ fn features_enabled() -> Vec<String> {
names.push("zip".to_string());
}
#[cfg(feature = "clipboard-cli")]
{
names.push("clipboard-cli".to_string());
}
#[cfg(feature = "trash-support")]
{
names.push("trash".to_string());

View File

@ -2,14 +2,11 @@ use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
dataframe::{Column, FrameStruct, NuDataFrame},
Signature, SyntaxShape, UntaggedValue,
};
use nu_source::Tagged;
use polars::{
frame::groupby::GroupBy,
prelude::{DataType, PolarsError, Series},
};
use polars::{frame::groupby::GroupBy, prelude::PolarsError};
enum Operation {
Mean,
@ -111,17 +108,40 @@ impl WholeStreamCommand for DataFrame {
description: "Aggregate sum by grouping by column a and summing on col b",
example:
"[[a b]; [one 1] [one 2]] | dataframe to-df | dataframe group-by a | dataframe aggregate sum",
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![UntaggedValue::string("one").into()]),
Column::new("b".to_string(), vec![UntaggedValue::int(3).into()]),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
Example {
description: "Aggregate sum in dataframe columns",
example: "[[a b]; [4 1] [5 2]] | dataframe to-df | dataframe aggregate sum",
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![UntaggedValue::int(9).into()]),
Column::new("b".to_string(), vec![UntaggedValue::int(3).into()]),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
Example {
description: "Aggregate sum in series",
example: "[4 1 5 6] | dataframe to-series | dataframe aggregate sum",
result: None,
example: "[4 1 5 6] | dataframe to-df | dataframe aggregate sum",
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new("0".to_string(), vec![UntaggedValue::int(16).into()]),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
]
}
@ -139,7 +159,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
})?;
match value.value {
UntaggedValue::DataFrame(PolarsData::GroupBy(nu_groupby)) => {
UntaggedValue::FrameStruct(FrameStruct::GroupBy(nu_groupby)) => {
let groupby = nu_groupby.to_groupby()?;
let res = perform_groupby_aggregation(
@ -152,18 +172,13 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)) => {
UntaggedValue::DataFrame(df) => {
let df = df.as_ref();
let res = perform_dataframe_aggregation(&df, op, &operation.tag)?;
let res = perform_dataframe_aggregation(df, op, &operation.tag)?;
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
UntaggedValue::DataFrame(PolarsData::Series(series)) => {
let value = perform_series_aggregation(series.as_ref(), op, &operation.tag)?;
Ok(OutputStream::one(value))
}
_ => Err(ShellError::labeled_error(
"No groupby, dataframe or series in stream",
"no groupby, dataframe or series found in input stream",
@ -230,7 +245,7 @@ fn perform_groupby_aggregation(
None => &col[..],
};
res.rename(col.as_str(), new_col)
res.rename(&col, new_col)
.expect("Column is always there. Looping with known names");
}
}
@ -264,162 +279,15 @@ fn perform_dataframe_aggregation(
}
}
fn perform_series_aggregation(
series: &Series,
operation: Operation,
operation_tag: &Tag,
) -> Result<Value, ShellError> {
match operation {
Operation::Mean => {
let res = match series.mean() {
Some(val) => UntaggedValue::Primitive(val.into()),
None => UntaggedValue::Primitive(0.into()),
};
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
let value = Value {
value: res,
tag: operation_tag.clone(),
};
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
let mut data = TaggedDictBuilder::new(operation_tag.clone());
data.insert_value(series.name(), value);
Ok(data.into_value())
}
Operation::Median => {
let res = match series.median() {
Some(val) => UntaggedValue::Primitive(val.into()),
None => UntaggedValue::Primitive(0.into()),
};
let value = Value {
value: res,
tag: operation_tag.clone(),
};
let mut data = TaggedDictBuilder::new(operation_tag.clone());
data.insert_value(series.name(), value);
Ok(data.into_value())
}
Operation::Sum => {
let untagged = match series.dtype() {
DataType::Int8
| DataType::Int16
| DataType::Int32
| DataType::Int64
| DataType::UInt8
| DataType::UInt16
| DataType::UInt32
| DataType::UInt64 => {
let res: i64 = series.sum().unwrap_or(0);
Ok(UntaggedValue::Primitive(res.into()))
}
DataType::Float32 | DataType::Float64 => {
let res: f64 = series.sum().unwrap_or(0.0);
Ok(UntaggedValue::Primitive(res.into()))
}
_ => Err(ShellError::labeled_error(
"Not valid type",
format!(
"this operation can not be performed with series of type {}",
series.dtype()
),
&operation_tag.span,
)),
}?;
let value = Value {
value: untagged,
tag: operation_tag.clone(),
};
let mut data = TaggedDictBuilder::new(operation_tag.clone());
data.insert_value(series.name(), value);
Ok(data.into_value())
}
Operation::Max => {
let untagged = match series.dtype() {
DataType::Int8
| DataType::Int16
| DataType::Int32
| DataType::Int64
| DataType::UInt8
| DataType::UInt16
| DataType::UInt32
| DataType::UInt64 => {
let res: i64 = series.max().unwrap_or(0);
Ok(UntaggedValue::Primitive(res.into()))
}
DataType::Float32 | DataType::Float64 => {
let res: f64 = series.max().unwrap_or(0.0);
Ok(UntaggedValue::Primitive(res.into()))
}
_ => Err(ShellError::labeled_error(
"Not valid type",
format!(
"this operation can not be performed with series of type {}",
series.dtype()
),
&operation_tag.span,
)),
}?;
let value = Value {
value: untagged,
tag: operation_tag.clone(),
};
let mut data = TaggedDictBuilder::new(operation_tag.clone());
data.insert_value(series.name(), value);
Ok(data.into_value())
}
Operation::Min => {
let untagged = match series.dtype() {
DataType::Int8
| DataType::Int16
| DataType::Int32
| DataType::Int64
| DataType::UInt8
| DataType::UInt16
| DataType::UInt32
| DataType::UInt64 => {
let res: i64 = series.min().unwrap_or(0);
Ok(UntaggedValue::Primitive(res.into()))
}
DataType::Float32 | DataType::Float64 => {
let res: f64 = series.min().unwrap_or(0.0);
Ok(UntaggedValue::Primitive(res.into()))
}
_ => Err(ShellError::labeled_error(
"Not valid type",
format!(
"this operation can not be performed with series of type {}",
series.dtype()
),
&operation_tag.span,
)),
}?;
let value = Value {
value: untagged,
tag: operation_tag.clone(),
};
let mut data = TaggedDictBuilder::new(operation_tag.clone());
data.insert_value(series.name(), value);
Ok(data.into_value())
}
_ => Err(ShellError::labeled_error_with_secondary(
"Not valid operation",
"operation not valid for series",
&operation_tag.span,
"Perhaps you want: mean, median, sum, max, min",
&operation_tag.span,
)),
test_examples(DataFrame {})
}
}

View File

@ -0,0 +1,138 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Axis, Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe append"
}
fn usage(&self) -> &str {
"[DataFrame] Appends a new dataframe"
}
fn signature(&self) -> Signature {
Signature::build("dataframe append")
.required_named(
"other",
SyntaxShape::Any,
"dataframe to be appended",
Some('o'),
)
.required_named(
"axis",
SyntaxShape::String,
"row or col axis orientation",
Some('a'),
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Appends a dataframe as new columns",
example: r#"let a = ([[a b]; [1 2] [3 4]] | dataframe to-df);
$a | dataframe append -o $a -a row"#,
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
),
Column::new(
"b".to_string(),
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
),
Column::new(
"a_x".to_string(),
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
),
Column::new(
"b_x".to_string(),
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
Example {
description: "Appends a dataframe merging at the end of columns",
example: r#"let a = ([[a b]; [1 2] [3 4]] | dataframe to-df);
$a | dataframe append -o $a -a col"#,
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![
UntaggedValue::int(1).into(),
UntaggedValue::int(3).into(),
UntaggedValue::int(1).into(),
UntaggedValue::int(3).into(),
],
),
Column::new(
"b".to_string(),
vec![
UntaggedValue::int(2).into(),
UntaggedValue::int(4).into(),
UntaggedValue::int(2).into(),
UntaggedValue::int(4).into(),
],
),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let other: Value = args.req_named("other")?;
let axis: Tagged<String> = args.req_named("axis")?;
let axis = Axis::try_from_str(&axis.item, &axis.tag.span)?;
let df_other = match other.value {
UntaggedValue::DataFrame(df) => Ok(df),
_ => Err(ShellError::labeled_error(
"Incorrect type",
"can only append a dataframe to a dataframe",
other.tag.span,
)),
}?;
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let df_new = df.append_df(&df_other, axis, &tag.span)?;
Ok(OutputStream::one(df_new.into_value(tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -2,8 +2,8 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, NuSeries},
Signature, SyntaxShape,
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue,
};
use nu_source::Tagged;
@ -32,7 +32,15 @@ impl WholeStreamCommand for DataFrame {
vec![Example {
description: "Returns the selected column as series",
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe column a",
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"a".to_string(),
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -41,15 +49,26 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let column: Tagged<String> = args.req(0)?;
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let res = df
.as_ref()
.column(column.item.as_ref())
.column(&column.item)
.map_err(|e| parse_polars_error::<&str>(&e, &column.tag.span, None))?;
Ok(OutputStream::one(NuSeries::series_to_value(
res.clone(),
tag,
)))
let df = NuDataFrame::try_from_series(vec![res.clone()], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -0,0 +1,232 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::{
chunked_array::ChunkedArray,
prelude::{
AnyValue, DataFrame as PolarsDF, DataType, Float64Type, IntoSeries, NewChunkedArray,
Series, Utf8Type,
},
};
use super::utils::parse_polars_error;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe describe"
}
fn usage(&self) -> &str {
"[DataFrame] Describes dataframes numeric columns"
}
fn signature(&self) -> Signature {
Signature::build("dataframe describe")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Describes dataframe",
example: "[[a b]; [1 1] [1 1]] | dataframe to-df | dataframe describe",
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new(
"descriptor".to_string(),
vec![
UntaggedValue::string("count").into(),
UntaggedValue::string("sum").into(),
UntaggedValue::string("mean").into(),
UntaggedValue::string("median").into(),
UntaggedValue::string("std").into(),
UntaggedValue::string("min").into(),
UntaggedValue::string("25%").into(),
UntaggedValue::string("50%").into(),
UntaggedValue::string("75%").into(),
UntaggedValue::string("max").into(),
],
),
Column::new(
"a (i64)".to_string(),
vec![
UntaggedValue::decimal_from_float(2.0, Span::default()).into(),
UntaggedValue::decimal_from_float(2.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
UntaggedValue::decimal_from_float(0.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
],
),
Column::new(
"b (i64)".to_string(),
vec![
UntaggedValue::decimal_from_float(2.0, Span::default()).into(),
UntaggedValue::decimal_from_float(2.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
UntaggedValue::decimal_from_float(0.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
],
),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let names = ChunkedArray::<Utf8Type>::new_from_opt_slice(
"descriptor",
&[
Some("count"),
Some("sum"),
Some("mean"),
Some("median"),
Some("std"),
Some("min"),
Some("25%"),
Some("50%"),
Some("75%"),
Some("max"),
],
)
.into_series();
let head = std::iter::once(names);
let tail = df.as_ref().get_columns().iter().map(|col| {
let count = col.len() as f64;
let sum = match col.sum_as_series().cast(&DataType::Float64) {
Ok(ca) => match ca.get(0) {
AnyValue::Float64(v) => Some(v),
_ => None,
},
Err(_) => None,
};
let mean = match col.mean_as_series().get(0) {
AnyValue::Float64(v) => Some(v),
_ => None,
};
let median = match col.median_as_series().get(0) {
AnyValue::Float64(v) => Some(v),
_ => None,
};
let std = match col.std_as_series().get(0) {
AnyValue::Float64(v) => Some(v),
_ => None,
};
let min = match col.min_as_series().cast(&DataType::Float64) {
Ok(ca) => match ca.get(0) {
AnyValue::Float64(v) => Some(v),
_ => None,
},
Err(_) => None,
};
let q_25 = match col.quantile_as_series(0.25) {
Ok(ca) => match ca.cast(&DataType::Float64) {
Ok(ca) => match ca.get(0) {
AnyValue::Float64(v) => Some(v),
_ => None,
},
Err(_) => None,
},
Err(_) => None,
};
let q_50 = match col.quantile_as_series(0.50) {
Ok(ca) => match ca.cast(&DataType::Float64) {
Ok(ca) => match ca.get(0) {
AnyValue::Float64(v) => Some(v),
_ => None,
},
Err(_) => None,
},
Err(_) => None,
};
let q_75 = match col.quantile_as_series(0.75) {
Ok(ca) => match ca.cast(&DataType::Float64) {
Ok(ca) => match ca.get(0) {
AnyValue::Float64(v) => Some(v),
_ => None,
},
Err(_) => None,
},
Err(_) => None,
};
let max = match col.max_as_series().cast(&DataType::Float64) {
Ok(ca) => match ca.get(0) {
AnyValue::Float64(v) => Some(v),
_ => None,
},
Err(_) => None,
};
let name = format!("{} ({})", col.name(), col.dtype());
ChunkedArray::<Float64Type>::new_from_opt_slice(
&name,
&[
Some(count),
sum,
mean,
median,
std,
min,
q_25,
q_50,
q_75,
max,
],
)
.into_series()
});
let res = head.chain(tail).collect::<Vec<Series>>();
let df = PolarsDF::new(res).map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
let df = NuDataFrame::dataframe_to_value(df, tag);
Ok(OutputStream::one(df))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape, Value};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue, Value,
};
use super::utils::{convert_columns, parse_polars_error};
@ -17,7 +20,11 @@ impl WholeStreamCommand for DataFrame {
}
fn signature(&self) -> Signature {
Signature::build("dataframe drop").rest(SyntaxShape::Any, "column names to be dropped")
Signature::build("dataframe drop").rest(
"rest",
SyntaxShape::Any,
"column names to be dropped",
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
@ -28,7 +35,15 @@ impl WholeStreamCommand for DataFrame {
vec![Example {
description: "drop column a",
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe drop a",
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"b".to_string(),
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -39,7 +54,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let columns: Vec<Value> = args.rest(0)?;
let (col_string, col_span) = convert_columns(&columns, &tag)?;
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let new_df = match col_string.get(0) {
Some(col) => df
@ -63,3 +78,16 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape, Value};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue, Value,
};
use super::utils::{convert_columns, parse_polars_error};
@ -34,7 +37,21 @@ impl WholeStreamCommand for DataFrame {
vec![Example {
description: "drop duplicates",
example: "[[a b]; [1 2] [3 4] [1 2]] | dataframe to-df | dataframe drop-duplicates",
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
),
Column::new(
"b".to_string(),
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -52,7 +69,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
None => (None, Span::unknown()),
};
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let subset_slice = subset.as_ref().map(|cols| &cols[..]);
@ -63,3 +80,16 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, NuSeries, PolarsData},
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue, Value,
};
@ -38,15 +38,45 @@ impl WholeStreamCommand for DataFrame {
example: r#"let df = ([[a b]; [1 2] [3 0] [1 2]] | dataframe to-df);
let res = ($df.b / $df.b);
let df = ($df | dataframe with-column $res --name res);
$df | dataframe drop-nulls
"#,
result: None,
$df | dataframe drop-nulls"#,
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![UntaggedValue::int(1).into(), UntaggedValue::int(1).into()],
),
Column::new(
"b".to_string(),
vec![UntaggedValue::int(2).into(), UntaggedValue::int(2).into()],
),
Column::new(
"res".to_string(),
vec![UntaggedValue::int(1).into(), UntaggedValue::int(1).into()],
),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
Example {
description: "drop null values in dataframe",
example: r#"let s = ([1 2 0 0 3 4] | dataframe to-series);
example: r#"let s = ([1 2 0 0 3 4] | dataframe to-df);
($s / $s) | dataframe drop-nulls"#,
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"div_0_0".to_string(),
vec![
UntaggedValue::int(1).into(),
UntaggedValue::int(1).into(),
UntaggedValue::int(1).into(),
UntaggedValue::int(1).into(),
],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
]
}
@ -60,7 +90,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
})?;
match value.value {
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)) => {
UntaggedValue::DataFrame(df) => {
// Extracting the selection columns of the columns to perform the aggregation
let columns: Option<Vec<Value>> = args.opt(0)?;
let (subset, col_span) = match columns {
@ -80,10 +110,6 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
UntaggedValue::DataFrame(PolarsData::Series(series)) => {
let res = series.as_ref().drop_nulls();
Ok(OutputStream::one(NuSeries::series_to_value(res, tag)))
}
_ => Err(ShellError::labeled_error(
"Incorrect type",
"drop nulls cannot be done with this value",
@ -91,3 +117,16 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
)),
}
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuDataFrame, Signature, TaggedDictBuilder};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue, Value,
};
pub struct DataFrame;
@ -26,7 +29,27 @@ impl WholeStreamCommand for DataFrame {
vec![Example {
description: "drop column a",
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe dtypes",
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new(
"column".to_string(),
vec![
UntaggedValue::string("a").into(),
UntaggedValue::string("b").into(),
],
),
Column::new(
"dtype".to_string(),
vec![
UntaggedValue::string("i64").into(),
UntaggedValue::string("i64").into(),
],
),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -35,26 +58,49 @@ impl WholeStreamCommand for DataFrame {
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let col_names = df
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let mut dtypes: Vec<Value> = Vec::new();
let names: Vec<Value> = df
.as_ref()
.get_column_names()
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>();
let values = df
.map(|v| {
let dtype = df
.as_ref()
.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));
.column(v)
.expect("using name from list of names from dataframe")
.dtype();
data.into_value()
let dtype_str = dtype.to_string();
dtypes.push(Value {
value: dtype_str.into(),
tag: Tag::default(),
});
Ok(OutputStream::from_stream(values))
Value {
value: v.to_string().into(),
tag: Tag::default(),
}
})
.collect();
let names_col = Column::new("column".to_string(), names);
let dtypes_col = Column::new("dtype".to_string(), dtypes);
let df = NuDataFrame::try_from_columns(vec![names_col, dtypes_col], &tag.span)?;
Ok(OutputStream::one(df.into_value(tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
@ -32,12 +32,70 @@ impl WholeStreamCommand for DataFrame {
Example {
description: "Create new dataframe with dummy variables from a dataframe",
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe to-dummies",
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new(
"a_1".to_string(),
vec![UntaggedValue::int(1).into(), UntaggedValue::int(0).into()],
),
Column::new(
"a_3".to_string(),
vec![UntaggedValue::int(0).into(), UntaggedValue::int(1).into()],
),
Column::new(
"b_2".to_string(),
vec![UntaggedValue::int(1).into(), UntaggedValue::int(0).into()],
),
Column::new(
"b_4".to_string(),
vec![UntaggedValue::int(0).into(), UntaggedValue::int(1).into()],
),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
Example {
description: "Create new dataframe with dummy variables from a series",
example: "[1 2 2 3 3] | dataframe to-series | dataframe to-dummies",
result: None,
example: "[1 2 2 3 3] | dataframe to-df | dataframe to-dummies",
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new(
"0_1".to_string(),
vec![
UntaggedValue::int(1).into(),
UntaggedValue::int(0).into(),
UntaggedValue::int(0).into(),
UntaggedValue::int(0).into(),
UntaggedValue::int(0).into(),
],
),
Column::new(
"0_2".to_string(),
vec![
UntaggedValue::int(0).into(),
UntaggedValue::int(1).into(),
UntaggedValue::int(1).into(),
UntaggedValue::int(0).into(),
UntaggedValue::int(0).into(),
],
),
Column::new(
"0_3".to_string(),
vec![
UntaggedValue::int(0).into(),
UntaggedValue::int(0).into(),
UntaggedValue::int(0).into(),
UntaggedValue::int(1).into(),
UntaggedValue::int(1).into(),
],
),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
]
}
@ -51,7 +109,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
})?;
match value.value {
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)) => {
UntaggedValue::DataFrame(df) => {
let res = df.as_ref().to_dummies().map_err(|e| {
parse_polars_error(
&e,
@ -62,17 +120,6 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
UntaggedValue::DataFrame(PolarsData::Series(series)) => {
let res = series.as_ref().to_dummies().map_err(|e| {
parse_polars_error(
&e,
&tag.span,
Some("The only allowed column types for dummies are String or Int"),
)
})?;
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
_ => Err(ShellError::labeled_error(
"Incorrect type",
"dummies cannot be done with this value",
@ -80,3 +127,16 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
)),
}
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue, Value,
};
@ -34,13 +34,21 @@ impl WholeStreamCommand for DataFrame {
vec![
Example {
description: "Filter dataframe using a bool mask",
example: r#"let mask = ([$true $false] | dataframe to-series);
example: r#"let mask = ([$true $false] | dataframe to-df);
[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe filter-with $mask"#,
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![UntaggedValue::int(1).into()]),
Column::new("b".to_string(), vec![UntaggedValue::int(2).into()]),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
Example {
description: "Filter dataframe by creating a mask from operation",
example: r#"let mask = (([5 6] | dataframe to-series) > 5);
example: r#"let mask = (([5 6] | dataframe to-df) > 5);
[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe filter-with $mask"#,
result: None,
},
@ -53,29 +61,42 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let value: Value = args.req(0)?;
let series_span = value.tag.span;
let series = match value.value {
UntaggedValue::DataFrame(PolarsData::Series(series)) => Ok(series),
let df = match value.value {
UntaggedValue::DataFrame(df) => Ok(df),
_ => Err(ShellError::labeled_error(
"Incorrect type",
"can only add a series to a dataframe",
value.tag.span,
)),
}?;
let casted = series.as_ref().bool().map_err(|e| {
let series = df.as_series(&series_span)?;
let casted = series.bool().map_err(|e| {
parse_polars_error(
&e,
&&series_span,
&series_span,
Some("Perhaps you want to use a series with booleans as mask"),
)
})?;
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let res = df
.as_ref()
.filter(&casted)
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
.filter(casted)
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue,
};
use nu_source::Tagged;
@ -31,8 +34,16 @@ impl WholeStreamCommand for DataFrame {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Create new dataframe with head rows",
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe first",
result: None,
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe first 1",
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![UntaggedValue::int(1).into()]),
Column::new("b".to_string(), vec![UntaggedValue::int(2).into()]),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -46,8 +57,21 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
None => 5,
};
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let res = df.as_ref().head(Some(rows));
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape, Value};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue, Value,
};
use super::utils::{convert_columns, parse_polars_error};
pub struct DataFrame;
@ -16,7 +19,11 @@ impl WholeStreamCommand for DataFrame {
}
fn signature(&self) -> Signature {
Signature::build("dataframe get").rest(SyntaxShape::Any, "column names to sort dataframe")
Signature::build("dataframe get").rest(
"rest",
SyntaxShape::Any,
"column names to sort dataframe",
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
@ -27,7 +34,15 @@ impl WholeStreamCommand for DataFrame {
vec![Example {
description: "Creates dataframe with selected columns",
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe get a",
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"a".to_string(),
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -38,7 +53,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let (col_string, col_span) = convert_columns(&columns, &tag)?;
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let res = df
.as_ref()
@ -47,3 +62,16 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -2,7 +2,7 @@ use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, NuGroupBy, PolarsData},
dataframe::{FrameStruct, NuDataFrame, NuGroupBy},
Signature, SyntaxShape, UntaggedValue, Value,
};
@ -20,7 +20,7 @@ impl WholeStreamCommand for DataFrame {
}
fn signature(&self) -> Signature {
Signature::build("dataframe group-by").rest(SyntaxShape::Any, "groupby columns")
Signature::build("dataframe group-by").rest("rest", SyntaxShape::Any, "groupby columns")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
@ -43,7 +43,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let by_columns: Vec<Value> = args.rest(0)?;
let (columns_string, col_span) = convert_columns(&by_columns, &tag)?;
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
// This is the expensive part of the groupby; to create the
// groups that will be used for grouping the data in the
@ -57,7 +57,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let groups = groupby.get_groups().to_vec();
let groupby = Value {
tag,
value: UntaggedValue::DataFrame(PolarsData::GroupBy(NuGroupBy::new(
value: UntaggedValue::FrameStruct(FrameStruct::GroupBy(NuGroupBy::new(
NuDataFrame::new(df.as_ref().clone()),
columns_string,
groups,

View File

@ -2,7 +2,7 @@ use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{NuDataFrame, PolarsData},
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue, Value,
};
@ -44,6 +44,12 @@ impl WholeStreamCommand for DataFrame {
"type of join. Inner by default",
Some('t'),
)
.named(
"suffix",
SyntaxShape::String,
"suffix for the columns of the right dataframe",
Some('s'),
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
@ -51,20 +57,50 @@ impl WholeStreamCommand for DataFrame {
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
vec![Example {
description: "inner join dataframe",
example: r#"let right = ([[a b c]; [1 2 5] [3 4 5] [5 6 6]] | dataframe to-df);
$right | dataframe join $right -l [a b] -r [a b]"#,
result: None,
},
Example {
description: "right join dataframe",
example: r#"let right = ([[a b c]; [1 2 3] [3 4 5] [5 6 7]] | dataframe to-df);
$right | dataframe join $right -l [a c] -r [a c] -t inner"#,
result: None,
},
]
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new(
"a".to_string(),
vec![
UntaggedValue::int(1).into(),
UntaggedValue::int(3).into(),
UntaggedValue::int(5).into(),
],
),
Column::new(
"b".to_string(),
vec![
UntaggedValue::int(2).into(),
UntaggedValue::int(4).into(),
UntaggedValue::int(6).into(),
],
),
Column::new(
"c".to_string(),
vec![
UntaggedValue::int(5).into(),
UntaggedValue::int(5).into(),
UntaggedValue::int(6).into(),
],
),
Column::new(
"c_right".to_string(),
vec![
UntaggedValue::int(5).into(),
UntaggedValue::int(5).into(),
UntaggedValue::int(6).into(),
],
),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -74,6 +110,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let r_df: Value = args.req(0)?;
let l_col: Vec<Value> = args.req_named("left")?;
let r_col: Vec<Value> = args.req_named("right")?;
let r_suffix: Option<Tagged<String>> = args.get_flag("suffix")?;
let join_type_op: Option<Tagged<String>> = args.get_flag("type")?;
let join_type = match join_type_op {
@ -94,16 +131,19 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
},
};
let suffix = r_suffix.map(|s| s.item);
let (l_col_string, l_col_span) = convert_columns(&l_col, &tag)?;
let (r_col_string, r_col_span) = convert_columns(&r_col, &tag)?;
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let res = match r_df.value {
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(r_df)) => {
UntaggedValue::DataFrame(r_df) => {
// Checking the column types before performing the join
check_column_datatypes(
df.as_ref(),
r_df.as_ref(),
&l_col_string,
&l_col_span,
&r_col_string,
@ -111,7 +151,13 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
)?;
df.as_ref()
.join(r_df.as_ref(), &l_col_string, &r_col_string, join_type)
.join(
r_df.as_ref(),
&l_col_string,
&r_col_string,
join_type,
suffix,
)
.map_err(|e| parse_polars_error::<&str>(&e, &l_col_span, None))
}
_ => Err(ShellError::labeled_error(
@ -125,7 +171,8 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
}
fn check_column_datatypes<T: AsRef<str>>(
df: &polars::prelude::DataFrame,
df_l: &polars::prelude::DataFrame,
df_r: &polars::prelude::DataFrame,
l_cols: &[T],
l_col_span: &Span,
r_cols: &[T],
@ -145,14 +192,14 @@ fn check_column_datatypes<T: AsRef<str>>(
));
}
for (l, r) in l_cols.iter().zip(r_cols.iter()) {
let l_series = df
for (l, r) in l_cols.iter().zip(r_cols) {
let l_series = df_l
.column(l.as_ref())
.map_err(|e| parse_polars_error::<&str>(&e, &l_col_span, None))?;
.map_err(|e| parse_polars_error::<&str>(&e, l_col_span, None))?;
let r_series = df
let r_series = df_r
.column(r.as_ref())
.map_err(|e| parse_polars_error::<&str>(&e, &r_col_span, None))?;
.map_err(|e| parse_polars_error::<&str>(&e, r_col_span, None))?;
if l_series.dtype() != r_series.dtype() {
return Err(ShellError::labeled_error_with_secondary(
@ -171,3 +218,16 @@ fn check_column_datatypes<T: AsRef<str>>(
Ok(())
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue,
};
use nu_source::Tagged;
pub struct DataFrame;
@ -30,8 +33,16 @@ impl WholeStreamCommand for DataFrame {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Create new dataframe with last rows",
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe last",
result: None,
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe last 1",
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new("a".to_string(), vec![UntaggedValue::int(3).into()]),
Column::new("b".to_string(), vec![UntaggedValue::int(4).into()]),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -45,9 +56,22 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
None => 5,
};
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let res = df.as_ref().tail(Some(rows));
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::PolarsData, Signature, TaggedDictBuilder, UntaggedValue};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue, Value,
};
pub struct DataFrame;
@ -19,46 +22,94 @@ impl WholeStreamCommand for DataFrame {
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let values = args
let data = args
.context
.scope
.get_vars()
.into_iter()
.filter_map(|(name, value)| {
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)) = &value.value {
let mut data = TaggedDictBuilder::new(value.tag.clone());
if let UntaggedValue::DataFrame(df) = &value.value {
let rows = Value {
value: (df.as_ref().height() as i64).into(),
tag: Tag::default(),
};
let rows = df.as_ref().height();
let cols = df.as_ref().width();
let cols = Value {
value: (df.as_ref().width() as i64).into(),
tag: Tag::default(),
};
data.insert_value("name", name.as_ref());
data.insert_value("rows", format!("{}", rows));
data.insert_value("columns", format!("{}", cols));
let location = match value.tag.anchor {
Some(AnchorLocation::File(name)) => name,
Some(AnchorLocation::Url(name)) => name,
Some(AnchorLocation::Source(text)) => text.slice(0..text.end).text,
None => "stream".to_string(),
};
match value.tag.anchor {
Some(AnchorLocation::File(name)) => data.insert_value("location", name),
Some(AnchorLocation::Url(name)) => data.insert_value("location", name),
Some(AnchorLocation::Source(text)) => {
let loc_name = text.slice(0..text.end);
data.insert_value("location", loc_name.text)
}
None => data.insert_value("location", "stream"),
}
let location = Value {
value: location.into(),
tag: Tag::default(),
};
Some(data.into_value())
let name = Value {
value: name.into(),
tag: Tag::default(),
};
Some((name, rows, cols, location))
} else {
None
}
});
Ok(OutputStream::from_stream(values))
let mut name = Column::new_empty("name".to_string());
let mut rows = Column::new_empty("rows".to_string());
let mut cols = Column::new_empty("columns".to_string());
let mut location = Column::new_empty("location".to_string());
for tuple in data {
name.push(tuple.0);
rows.push(tuple.1);
cols.push(tuple.2);
location.push(tuple.3);
}
let tag = args.call_info.name_tag;
let df = NuDataFrame::try_from_columns(vec![name, rows, cols, location], &tag.span)?;
Ok(OutputStream::one(df.into_value(tag)))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Lists loaded dataframes in current scope",
example: "dataframe list",
result: None,
example: "let a = ([[a b];[1 2] [3 4]] | dataframe to-df); dataframe list",
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new("name".to_string(), vec![UntaggedValue::string("$a").into()]),
Column::new("rows".to_string(), vec![UntaggedValue::int(2).into()]),
Column::new("columns".to_string(), vec![UntaggedValue::int(2).into()]),
Column::new(
"location".to_string(),
vec![UntaggedValue::string("stream").into()],
),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,11 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape, Value};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tagged;
use super::utils::convert_columns;
@ -18,8 +22,30 @@ impl WholeStreamCommand for DataFrame {
fn signature(&self) -> Signature {
Signature::build("dataframe melt")
.required("id_columns", SyntaxShape::Table, "Id columns for melting")
.rest(SyntaxShape::Any, "columns used as value columns")
.required_named(
"columns",
SyntaxShape::Table,
"column names for melting",
Some('c'),
)
.required_named(
"values",
SyntaxShape::Table,
"column names used as value columns",
Some('v'),
)
.named(
"variable_name",
SyntaxShape::String,
"optional name for variable column",
Some('r'),
)
.named(
"value_name",
SyntaxShape::String,
"optional name for value column",
Some('l'),
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
@ -29,8 +55,59 @@ impl WholeStreamCommand for DataFrame {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "melt dataframe",
example: "[[a b]; [a 2] [b 4] [a 6]] | dataframe to-df | dataframe melt a b",
result: None,
example:
"[[a b c d]; [x 1 4 a] [y 2 5 b] [z 3 6 c]] | dataframe to-df | dataframe melt -c [b c] -v [a d]",
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new(
"b".to_string(),
vec![
UntaggedValue::int(1).into(),
UntaggedValue::int(2).into(),
UntaggedValue::int(3).into(),
UntaggedValue::int(1).into(),
UntaggedValue::int(2).into(),
UntaggedValue::int(3).into(),
],
),
Column::new(
"c".to_string(),
vec![
UntaggedValue::int(4).into(),
UntaggedValue::int(5).into(),
UntaggedValue::int(6).into(),
UntaggedValue::int(4).into(),
UntaggedValue::int(5).into(),
UntaggedValue::int(6).into(),
],
),
Column::new(
"variable".to_string(),
vec![
UntaggedValue::string("a").into(),
UntaggedValue::string("a").into(),
UntaggedValue::string("a").into(),
UntaggedValue::string("d").into(),
UntaggedValue::string("d").into(),
UntaggedValue::string("d").into(),
],
),
Column::new(
"value".to_string(),
vec![
UntaggedValue::string("x").into(),
UntaggedValue::string("y").into(),
UntaggedValue::string("z").into(),
UntaggedValue::string("a").into(),
UntaggedValue::string("b").into(),
UntaggedValue::string("c").into(),
],
),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -38,22 +115,35 @@ impl WholeStreamCommand for DataFrame {
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let id_col: Vec<Value> = args.req(0)?;
let val_col: Vec<Value> = args.rest(1)?;
let id_col: Vec<Value> = args.req_named("columns")?;
let val_col: Vec<Value> = args.req_named("values")?;
let value_name: Option<Tagged<String>> = args.get_flag("value_name")?;
let variable_name: Option<Tagged<String>> = args.get_flag("variable_name")?;
let (id_col_string, id_col_span) = convert_columns(&id_col, &tag)?;
let (val_col_string, val_col_span) = convert_columns(&val_col, &tag)?;
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
check_column_datatypes(df.as_ref(), &id_col_string, &id_col_span)?;
check_column_datatypes(df.as_ref(), &val_col_string, &val_col_span)?;
let res = df
let mut res = df
.as_ref()
.melt(&id_col_string, &val_col_string)
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
if let Some(name) = &variable_name {
res.rename("variable", &name.item)
.map_err(|e| parse_polars_error::<&str>(&e, &name.tag.span, None))?;
}
if let Some(name) = &value_name {
res.rename("value", &name.item)
.map_err(|e| parse_polars_error::<&str>(&e, &name.tag.span, None))?;
}
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
@ -75,11 +165,11 @@ fn check_column_datatypes<T: AsRef<str>>(
for w in cols.windows(2) {
let l_series = df
.column(w[0].as_ref())
.map_err(|e| parse_polars_error::<&str>(&e, &col_span, None))?;
.map_err(|e| parse_polars_error::<&str>(&e, col_span, None))?;
let r_series = df
.column(w[1].as_ref())
.map_err(|e| parse_polars_error::<&str>(&e, &col_span, None))?;
.map_err(|e| parse_polars_error::<&str>(&e, col_span, None))?;
if l_series.dtype() != r_series.dtype() {
return Err(ShellError::labeled_error_with_secondary(
@ -99,3 +189,16 @@ fn check_column_datatypes<T: AsRef<str>>(
Ok(())
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,6 +1,8 @@
pub mod aggregate;
pub mod append;
pub mod column;
pub mod command;
pub mod describe;
pub mod drop;
pub mod drop_duplicates;
pub mod drop_nulls;
@ -16,8 +18,10 @@ pub mod list;
pub mod melt;
pub mod open;
pub mod pivot;
pub mod rename;
pub mod sample;
pub mod select;
pub mod shape;
pub mod show;
pub mod slice;
pub mod sort;
@ -25,14 +29,15 @@ pub mod take;
pub mod to_csv;
pub mod to_df;
pub mod to_parquet;
pub mod to_series;
pub(crate) mod utils;
pub mod where_;
pub mod with_column;
pub use aggregate::DataFrame as DataFrameAggregate;
pub use append::DataFrame as DataFrameAppend;
pub use column::DataFrame as DataFrameColumn;
pub use command::Command as DataFrame;
pub use describe::DataFrame as DataFrameDescribe;
pub use drop::DataFrame as DataFrameDrop;
pub use drop_duplicates::DataFrame as DataFrameDropDuplicates;
pub use drop_nulls::DataFrame as DataFrameDropNulls;
@ -48,8 +53,10 @@ pub use list::DataFrame as DataFrameList;
pub use melt::DataFrame as DataFrameMelt;
pub use open::DataFrame as DataFrameOpen;
pub use pivot::DataFrame as DataFramePivot;
pub use rename::DataFrame as DataFrameRename;
pub use sample::DataFrame as DataFrameSample;
pub use select::DataFrame as DataFrameSelect;
pub use shape::DataFrame as DataFrameShape;
pub use show::DataFrame as DataFrameShow;
pub use slice::DataFrame as DataFrameSlice;
pub use sort::DataFrame as DataFrameSort;
@ -57,7 +64,6 @@ pub use take::DataFrame as DataFrameTake;
pub use to_csv::DataFrame as DataFrameToCsv;
pub use to_df::DataFrame as DataFrameToDF;
pub use to_parquet::DataFrame as DataFrameToParquet;
pub use to_series::DataFrame as DataFrameToSeries;
pub use where_::DataFrame as DataFrameWhere;
pub use with_column::DataFrame as DataFrameWithColumn;
@ -69,6 +75,19 @@ pub use series::DataFrameArgMin;
pub use series::DataFrameArgSort;
pub use series::DataFrameArgTrue;
pub use series::DataFrameArgUnique;
pub use series::DataFrameConcatenate;
pub use series::DataFrameContains;
pub use series::DataFrameCumulative;
pub use series::DataFrameGetDay;
pub use series::DataFrameGetHour;
pub use series::DataFrameGetMinute;
pub use series::DataFrameGetMonth;
pub use series::DataFrameGetNanoSecond;
pub use series::DataFrameGetOrdinal;
pub use series::DataFrameGetSecond;
pub use series::DataFrameGetWeek;
pub use series::DataFrameGetWeekDay;
pub use series::DataFrameGetYear;
pub use series::DataFrameIsDuplicated;
pub use series::DataFrameIsIn;
pub use series::DataFrameIsNotNull;
@ -77,9 +96,17 @@ pub use series::DataFrameIsUnique;
pub use series::DataFrameNNull;
pub use series::DataFrameNUnique;
pub use series::DataFrameNot;
pub use series::DataFrameReplace;
pub use series::DataFrameReplaceAll;
pub use series::DataFrameRolling;
pub use series::DataFrameSeriesRename;
pub use series::DataFrameSet;
pub use series::DataFrameSetWithIdx;
pub use series::DataFrameShift;
pub use series::DataFrameStrFTime;
pub use series::DataFrameStringLengths;
pub use series::DataFrameStringSlice;
pub use series::DataFrameToLowercase;
pub use series::DataFrameToUppercase;
pub use series::DataFrameUnique;
pub use series::DataFrameValueCounts;

View File

@ -8,7 +8,7 @@ use nu_protocol::{
};
use nu_source::Tagged;
use polars::prelude::{CsvReader, JsonReader, ParquetReader, SerReader};
use polars::prelude::{CsvEncoding, CsvReader, JsonReader, ParquetReader, SerReader};
use std::fs::File;
pub struct DataFrame;
@ -151,7 +151,8 @@ fn from_csv(args: CommandArgs) -> Result<polars::prelude::DataFrame, ShellError>
let columns: Option<Vec<Value>> = args.get_flag("columns")?;
let csv_reader = CsvReader::from_path(&file.item)
.map_err(|e| parse_polars_error::<&str>(&e, &file.tag.span, None))?;
.map_err(|e| parse_polars_error::<&str>(&e, &file.tag.span, None))?
.with_encoding(CsvEncoding::LossyUtf8);
let csv_reader = match delimiter {
None => csv_reader,

View File

@ -73,7 +73,7 @@ impl WholeStreamCommand for DataFrame {
description: "Pivot a dataframe on b and aggregation on col c",
example:
"[[a b c]; [one x 1] [two y 2]] | dataframe to-df | dataframe group-by a | dataframe pivot b c sum",
result: None,
result: None, // No sample because there are nulls in the result dataframe
}]
}
}
@ -100,7 +100,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut groupby = nu_groupby.to_groupby()?;
let pivot = groupby.pivot(pivot_col.item.as_ref(), value_col.item.as_ref());
let pivot = groupby.pivot(&pivot_col.item, &value_col.item);
let res = match op {
Operation::Mean => pivot.mean(),
@ -120,7 +120,7 @@ fn check_pivot_column(
col: &Tagged<String>,
) -> Result<(), ShellError> {
let series = df
.column(col.item.as_ref())
.column(&col.item)
.map_err(|e| parse_polars_error::<&str>(&e, &col.tag.span, None))?;
match series.dtype() {
@ -146,7 +146,7 @@ fn check_value_column(
col: &Tagged<String>,
) -> Result<(), ShellError> {
let series = df
.column(col.item.as_ref())
.column(&col.item)
.map_err(|e| parse_polars_error::<&str>(&e, &col.tag.span, None))?;
match series.dtype() {

View File

@ -0,0 +1,81 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue,
};
use nu_source::Tagged;
use super::utils::parse_polars_error;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe rename-col"
}
fn usage(&self) -> &str {
"[DataFrame] rename a dataframe column"
}
fn signature(&self) -> Signature {
Signature::build("dataframe rename-col")
.required("from", SyntaxShape::String, "column name to be renamed")
.required("to", SyntaxShape::String, "new column name")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Renames a dataframe column",
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe rename-col a ab",
result: Some(vec![NuDataFrame::try_from_columns(
vec![
Column::new(
"ab".to_string(),
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
),
Column::new(
"b".to_string(),
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
),
],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let from: Tagged<String> = args.req(0)?;
let to: Tagged<String> = args.req(1)?;
let (mut df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
df.as_mut()
.rename(&from.item, &to.item)
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
Ok(OutputStream::one(df.into_value(tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -41,14 +41,14 @@ impl WholeStreamCommand for DataFrame {
vec![
Example {
description: "Sample rows from dataframe",
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe sample -r 1",
result: None,
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe sample -n 1",
result: None, // No expected value because sampling is random
},
Example {
description: "Shows sample row using fraction and replace",
example:
"[[a b]; [1 2] [3 4] [5 6]] | dataframe to-df | dataframe sample -f 0.5 -e",
result: None,
result: None, // No expected value because sampling is random
},
]
}
@ -61,7 +61,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let fraction: Option<Tagged<f64>> = args.get_flag("fraction")?;
let replace: bool = args.has_flag("replace");
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let res = match (rows, fraction) {
(Some(rows), None) => df

View File

@ -1,7 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape, Value};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue, Value,
};
use super::utils::{convert_columns, parse_polars_error};
@ -17,7 +20,7 @@ impl WholeStreamCommand for DataFrame {
}
fn signature(&self) -> Signature {
Signature::build("dataframe select").rest(SyntaxShape::Any, "selected column names")
Signature::build("dataframe select").rest("rest", SyntaxShape::Any, "selected column names")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
@ -28,7 +31,15 @@ impl WholeStreamCommand for DataFrame {
vec![Example {
description: "Create new dataframe with column a",
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe select a",
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"a".to_string(),
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -40,7 +51,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let (col_string, col_span) = convert_columns(&columns, &tag)?;
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let res = df
.as_ref()
@ -49,3 +60,16 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuSeries, Signature, TaggedDictBuilder, UntaggedValue, Value};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue, Value,
};
pub struct DataFrame;
@ -26,15 +29,31 @@ impl WholeStreamCommand for DataFrame {
vec![
Example {
description: "Returns true if all values are false",
example: "[$false $false $false] | dataframe to-series | dataframe all-false",
result: None,
example: "[$false $false $false] | dataframe to-df | dataframe all-false",
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"all_false".to_string(),
vec![UntaggedValue::boolean(true).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
Example {
description: "Checks the result from a comparison",
example: r#"let s = ([5 6 2 8] | dataframe to-series);
example: r#"let s = ([5 6 2 10] | dataframe to-df);
let res = ($s > 9);
$res | dataframe all-false"#,
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"all_false".to_string(),
vec![UntaggedValue::boolean(false).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
]
}
@ -43,9 +62,10 @@ impl WholeStreamCommand for DataFrame {
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let bool = series.as_ref().bool().map_err(|e| {
let series = df.as_series(&df_tag.span)?;
let bool = series.bool().map_err(|e| {
parse_polars_error::<&str>(
&e,
&tag.span,
@ -60,8 +80,23 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
tag: tag.clone(),
};
let mut data = TaggedDictBuilder::new(tag);
data.insert_value("all_false", value);
let df = NuDataFrame::try_from_columns(
vec![Column::new("all_false".to_string(), vec![value])],
&tag.span,
)?;
Ok(OutputStream::one(data.into_value()))
Ok(OutputStream::one(df.into_value(tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuSeries, Signature, TaggedDictBuilder, UntaggedValue, Value};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue, Value,
};
pub struct DataFrame;
@ -26,15 +29,31 @@ impl WholeStreamCommand for DataFrame {
vec![
Example {
description: "Returns true if all values are true",
example: "[$true $true $true] | dataframe to-series | dataframe all-true",
result: None,
example: "[$true $true $true] | dataframe to-df | dataframe all-true",
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"all_true".to_string(),
vec![UntaggedValue::boolean(true).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
Example {
description: "Checks the result from a comparison",
example: r#"let s = ([5 6 2 8] | dataframe to-series);
example: r#"let s = ([5 6 2 8] | dataframe to-df);
let res = ($s > 9);
$res | dataframe all-true"#,
result: None,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"all_true".to_string(),
vec![UntaggedValue::boolean(false).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
},
]
}
@ -43,9 +62,10 @@ impl WholeStreamCommand for DataFrame {
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let bool = series.as_ref().bool().map_err(|e| {
let series = df.as_series(&df_tag.span)?;
let bool = series.bool().map_err(|e| {
parse_polars_error::<&str>(
&e,
&tag.span,
@ -60,8 +80,23 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
tag: tag.clone(),
};
let mut data = TaggedDictBuilder::new(tag);
data.insert_value("all_true", value);
let df = NuDataFrame::try_from_columns(
vec![Column::new("all_true".to_string(), vec![value])],
&tag.span,
)?;
Ok(OutputStream::one(data.into_value()))
Ok(OutputStream::one(df.into_value(tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuSeries, Signature};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::prelude::{IntoSeries, NewChunkedArray, UInt32Chunked};
@ -27,8 +30,16 @@ impl WholeStreamCommand for DataFrame {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns index for max value",
example: "[1 3 2] | dataframe to-series | dataframe arg-max",
result: None,
example: "[1 3 2] | dataframe to-df | dataframe arg-max",
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"arg_max".to_string(),
vec![UntaggedValue::int(1).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -36,9 +47,10 @@ impl WholeStreamCommand for DataFrame {
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let series = df.as_series(&df_tag.span)?;
let res = series.as_ref().arg_max();
let res = series.arg_max();
let chunked = match res {
Some(index) => UInt32Chunked::new_from_slice("arg_max", &[index as u32]),
@ -46,6 +58,20 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
};
let res = chunked.into_series();
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(NuSeries::series_to_value(res, tag)))
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuSeries, Signature};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::prelude::{IntoSeries, NewChunkedArray, UInt32Chunked};
@ -27,8 +30,16 @@ impl WholeStreamCommand for DataFrame {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns index for min value",
example: "[1 3 2] | dataframe to-series | dataframe arg-min",
result: None,
example: "[1 3 2] | dataframe to-df | dataframe arg-min",
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"arg_min".to_string(),
vec![UntaggedValue::int(0).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -36,9 +47,9 @@ impl WholeStreamCommand for DataFrame {
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let res = series.as_ref().arg_min();
let res = df.as_series(&df_tag.span)?.arg_min();
let chunked = match res {
Some(index) => UInt32Chunked::new_from_slice("arg_min", &[index as u32]),
@ -46,6 +57,20 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
};
let res = chunked.into_series();
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(NuSeries::series_to_value(res, tag)))
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuSeries, Signature};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::prelude::IntoSeries;
pub struct DataFrame;
@ -26,8 +29,22 @@ impl WholeStreamCommand for DataFrame {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns indexes for a sorted series",
example: "[1 2 2 3 3] | dataframe to-series | dataframe arg-sort",
result: None,
example: "[1 2 2 3 3] | dataframe to-df | dataframe arg-sort",
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"arg_sort".to_string(),
vec![
UntaggedValue::int(0).into(),
UntaggedValue::int(1).into(),
UntaggedValue::int(2).into(),
UntaggedValue::int(3).into(),
UntaggedValue::int(4).into(),
],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -36,12 +53,24 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let reverse = args.has_flag("reverse");
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let res = series.as_ref().argsort(reverse);
let mut res = df.as_series(&df_tag.span)?.argsort(reverse).into_series();
res.rename("arg_sort");
Ok(OutputStream::one(NuSeries::series_to_value(
res.into_series(),
tag,
)))
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuSeries, Signature};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::prelude::IntoSeries;
pub struct DataFrame;
@ -26,8 +29,16 @@ impl WholeStreamCommand for DataFrame {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns indexes where values are true",
example: "[$false $true $false] | dataframe to-series | dataframe arg-true",
result: None,
example: "[$false $true $false] | dataframe to-df | dataframe arg-true",
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"arg_true".to_string(),
vec![UntaggedValue::int(1).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -35,9 +46,10 @@ impl WholeStreamCommand for DataFrame {
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let bool = series.as_ref().bool().map_err(|e| {
let series = df.as_series(&df_tag.span)?;
let bool = series.bool().map_err(|e| {
parse_polars_error::<&str>(
&e,
&tag.span,
@ -46,7 +58,21 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
})?;
let mut res = bool.arg_true().into_series();
res.rename("int");
res.rename("arg_true");
Ok(OutputStream::one(NuSeries::series_to_value(res, tag)))
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -1,7 +1,10 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{dataframe::NuSeries, Signature};
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::prelude::IntoSeries;
pub struct DataFrame;
@ -26,8 +29,20 @@ impl WholeStreamCommand for DataFrame {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns indexes for unique values",
example: "[1 2 2 3 3] | dataframe to-series | dataframe arg-unique",
result: None,
example: "[1 2 2 3 3] | dataframe to-df | dataframe arg-unique",
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"arg_unique".to_string(),
vec![
UntaggedValue::int(0).into(),
UntaggedValue::int(1).into(),
UntaggedValue::int(3).into(),
],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
@ -35,15 +50,29 @@ impl WholeStreamCommand for DataFrame {
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let res = series
.as_ref()
let mut res = df
.as_series(&df_tag.span)?
.arg_unique()
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?
.into_series();
Ok(OutputStream::one(NuSeries::series_to_value(
res.into_series(),
tag,
)))
res.rename("arg_unique");
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -0,0 +1,107 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue, Value,
};
use polars::prelude::IntoSeries;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe concatenate"
}
fn usage(&self) -> &str {
"[Series] Concatenates strings with other array"
}
fn signature(&self) -> Signature {
Signature::build("dataframe concatenate").required(
"other",
SyntaxShape::Any,
"Other array with string to be concatenated",
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Concatenate string",
example: r#"let other = ([za xs cd] | dataframe to-df);
[abc abc abc] | dataframe to-df | dataframe concatenate $other"#,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![
UntaggedValue::string("abcza").into(),
UntaggedValue::string("abcxs").into(),
UntaggedValue::string("abccd").into(),
],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let other: Value = args.req(0)?;
let other_df = match &other.value {
UntaggedValue::DataFrame(df) => Ok(df),
_ => Err(ShellError::labeled_error(
"Incorrect type",
"can only concatenate another series",
other.tag.span,
)),
}?;
let other_series = other_df.as_series(&other.tag.span)?;
let other_chunked = other_series.utf8().map_err(|e| {
parse_polars_error::<&str>(
&e,
&other.tag.span,
Some("The concatenate command can only be used with string columns"),
)
})?;
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let series = df.as_series(&df_tag.span)?;
let chunked = series.utf8().map_err(|e| {
parse_polars_error::<&str>(
&e,
&df_tag.span,
Some("The concatenate command can only be used with string columns"),
)
})?;
let mut res = chunked.concat(other_chunked);
res.rename(series.name());
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -0,0 +1,89 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue,
};
use nu_source::Tagged;
use polars::prelude::IntoSeries;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe contains"
}
fn usage(&self) -> &str {
"[Series] Checks if a pattern is contained in a string"
}
fn signature(&self) -> Signature {
Signature::build("dataframe contains").required(
"pattern",
SyntaxShape::String,
"Regex pattern to be searched",
)
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns boolean indicating if pattern was found",
example: "[abc acb acb] | dataframe to-df | dataframe contains ab",
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![
UntaggedValue::boolean(true).into(),
UntaggedValue::boolean(false).into(),
UntaggedValue::boolean(false).into(),
],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let pattern: Tagged<String> = args.req(0)?;
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let series = df.as_series(&df_tag.span)?;
let chunked = series.utf8().map_err(|e| {
parse_polars_error::<&str>(
&e,
&df_tag.span,
Some("The contains command can only be used with string columns"),
)
})?;
let res = chunked
.contains(&pattern.item)
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -0,0 +1,127 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, SyntaxShape, UntaggedValue,
};
use nu_source::Tagged;
use polars::prelude::DataType;
enum CumType {
Min,
Max,
Sum,
}
impl CumType {
fn from_str(roll_type: &str, span: &Span) -> Result<Self, ShellError> {
match roll_type {
"min" => Ok(Self::Min),
"max" => Ok(Self::Max),
"sum" => Ok(Self::Sum),
_ => Err(ShellError::labeled_error_with_secondary(
"Wrong operation",
"Operation not valid for cumulative",
span,
"Perhaps you want to use: max, min, sum",
span,
)),
}
}
fn to_str(&self) -> &'static str {
match self {
CumType::Min => "cum_min",
CumType::Max => "cum_max",
CumType::Sum => "cum_sum",
}
}
}
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe cum"
}
fn usage(&self) -> &str {
"[Series] Cumulative calculation for a series"
}
fn signature(&self) -> Signature {
Signature::build("dataframe cum")
.required("type", SyntaxShape::String, "rolling operation")
.switch("reverse", "Reverse cumulative calculation", Some('r'))
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Cumulative sum for a series",
example: "[1 2 3 4 5] | dataframe to-df | dataframe cum sum",
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"0_cum_sum".to_string(),
vec![
UntaggedValue::int(1).into(),
UntaggedValue::int(3).into(),
UntaggedValue::int(6).into(),
UntaggedValue::int(10).into(),
UntaggedValue::int(15).into(),
],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let cum_type: Tagged<String> = args.req(0)?;
let reverse = args.has_flag("reverse");
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let series = df.as_series(&df_tag.span)?;
if let DataType::Object(_) = series.dtype() {
return Err(ShellError::labeled_error(
"Found object series",
"Series of type object cannot be used for cumulative operation",
&df_tag.span,
));
}
let cum_type = CumType::from_str(&cum_type.item, &cum_type.tag.span)?;
let mut res = match cum_type {
CumType::Max => series.cummax(reverse),
CumType::Min => series.cummin(reverse),
CumType::Sum => series.cumsum(reverse),
};
let name = format!("{}_{}", series.name(), cum_type.to_str());
res.rename(&name);
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -0,0 +1,75 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::prelude::IntoSeries;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe get-day"
}
fn usage(&self) -> &str {
"[Series] Gets day from date"
}
fn signature(&self) -> Signature {
Signature::build("dataframe get-day")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns day from a date",
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
let df = ([$dt $dt] | dataframe to-df);
$df | dataframe get-day"#,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![UntaggedValue::int(4).into(), UntaggedValue::int(4).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let series = df.as_series(&df_tag.span)?;
let casted = series
.datetime()
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
let res = casted.day().into_series();
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -0,0 +1,75 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::prelude::IntoSeries;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe get-hour"
}
fn usage(&self) -> &str {
"[Series] Gets hour from date"
}
fn signature(&self) -> Signature {
Signature::build("dataframe get-hour")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns hour from a date",
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
let df = ([$dt $dt] | dataframe to-df);
$df | dataframe get-hour"#,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![UntaggedValue::int(16).into(), UntaggedValue::int(16).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let series = df.as_series(&df_tag.span)?;
let casted = series
.datetime()
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
let res = casted.hour().into_series();
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -0,0 +1,75 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::prelude::IntoSeries;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe get-minute"
}
fn usage(&self) -> &str {
"[Series] Gets minute from date"
}
fn signature(&self) -> Signature {
Signature::build("dataframe get-minute")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns minute from a date",
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
let df = ([$dt $dt] | dataframe to-df);
$df | dataframe get-minute"#,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![UntaggedValue::int(39).into(), UntaggedValue::int(39).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let series = df.as_series(&df_tag.span)?;
let casted = series
.datetime()
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
let res = casted.minute().into_series();
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -0,0 +1,75 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::prelude::IntoSeries;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe get-month"
}
fn usage(&self) -> &str {
"[Series] Gets month from date"
}
fn signature(&self) -> Signature {
Signature::build("dataframe get-month")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns month from a date",
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
let df = ([$dt $dt] | dataframe to-df);
$df | dataframe get-month"#,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![UntaggedValue::int(8).into(), UntaggedValue::int(8).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let series = df.as_series(&df_tag.span)?;
let casted = series
.datetime()
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
let res = casted.month().into_series();
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -0,0 +1,75 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::prelude::IntoSeries;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe get-nanosecond"
}
fn usage(&self) -> &str {
"[Series] Gets nanosecond from date"
}
fn signature(&self) -> Signature {
Signature::build("dataframe get-nanosecond")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns nanosecond from a date",
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
let df = ([$dt $dt] | dataframe to-df);
$df | dataframe get-nanosecond"#,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![UntaggedValue::int(0).into(), UntaggedValue::int(0).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let series = df.as_series(&df_tag.span)?;
let casted = series
.datetime()
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
let res = casted.nanosecond().into_series();
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -0,0 +1,78 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::prelude::IntoSeries;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe get-ordinal"
}
fn usage(&self) -> &str {
"[Series] Gets ordinal date from date"
}
fn signature(&self) -> Signature {
Signature::build("dataframe get-ordinal")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns ordinal from a date",
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
let df = ([$dt $dt] | dataframe to-df);
$df | dataframe get-ordinal"#,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![
UntaggedValue::int(217).into(),
UntaggedValue::int(217).into(),
],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let series = df.as_series(&df_tag.span)?;
let casted = series
.datetime()
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
let res = casted.ordinal().into_series();
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

View File

@ -0,0 +1,75 @@
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
dataframe::{Column, NuDataFrame},
Signature, UntaggedValue,
};
use polars::prelude::IntoSeries;
pub struct DataFrame;
impl WholeStreamCommand for DataFrame {
fn name(&self) -> &str {
"dataframe get-second"
}
fn usage(&self) -> &str {
"[Series] Gets second from date"
}
fn signature(&self) -> Signature {
Signature::build("dataframe get-second")
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
command(args)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns second from a date",
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
let df = ([$dt $dt] | dataframe to-df);
$df | dataframe get-second"#,
result: Some(vec![NuDataFrame::try_from_columns(
vec![Column::new(
"0".to_string(),
vec![UntaggedValue::int(18).into(), UntaggedValue::int(18).into()],
)],
&Span::default(),
)
.expect("simple df for test should not fail")
.into_value(Tag::default())]),
}]
}
}
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
let series = df.as_series(&df_tag.span)?;
let casted = series
.datetime()
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
let res = casted.second().into_series();
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
Ok(OutputStream::one(df.into_value(df_tag)))
}
#[cfg(test)]
mod tests {
use super::DataFrame;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test_dataframe as test_examples;
test_examples(DataFrame {})
}
}

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