Compare commits

...

78 Commits

Author SHA1 Message Date
390d06d4e7 add bytes starts-with command (#5950)
* refactor operate, make it generic

* refactor operate, add starts with command

* add comment

* remove useless file
2022-07-05 06:42:01 -05:00
89acbda877 Pin reedline to new 0.8.0 release (#5954)
For the nushell 0.65.0 release

https://github.com/nushell/reedline/releases/tag/v0.8.0
2022-07-05 21:25:35 +12:00
JT
0d40d0438f bump to 0.65 (#5952) 2022-07-05 17:54:16 +12:00
1e8212a938 add bytes len (#5945) 2022-07-04 05:51:07 -05:00
JT
2da8310b11 Fix 'skip' support for binary streams (#5943) 2022-07-04 19:53:54 +12:00
JT
c16d8f0d5f Make take work like first (#5942) 2022-07-04 08:03:35 +12:00
JT
2ac5b0480a Binary into int (#5941)
* Add support for binary to into int

* Add test
2022-07-04 06:31:50 +12:00
4e90b478b7 Add bit operator: bit-xor (#5940) 2022-07-03 06:45:20 -05:00
3a38fb94f0 add search terms for is-admin (#5939) 2022-07-03 06:44:26 -05:00
c6f6dcb57c Change C-u and C-k to be readline compatible, move old C-u to C-s (#5938) 2022-07-03 06:43:56 -05:00
b80299eba7 change default keybinding in default config (#5925)
* change default keybinding in default config

* change from alt-o to ctrl-o

* change back to alt-o

* really changed it back to alt-o this time

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-07-02 18:12:03 -05:00
JT
a48616697a Rename bitwise operators for readability (#5937) 2022-07-02 17:05:02 -05:00
b82dccf0bd Add band and bor operator for bit operations (#5936)
* Add `band` and `bor` Operator

* Add tests
2022-07-02 13:03:36 -05:00
84caf8859f add -e flag to print, to print the value to stderr (#5935)
* Refactor: make stdout write all and flush as generic function

* support print to stderr
2022-07-02 09:54:49 -05:00
be7f35246e Fix to md --pretty when rendering a list (#5932)
Fixes #5931

Signed-off-by: nibon7 <nibon7@163.com>
2022-07-02 15:36:16 +03:00
3917fda7ed Update #4202: Add shift operator bshl and bshr for integers (#5928)
* Update #4202: Add shift operator bshl and bshr for integers

* Add more tests
2022-07-02 06:48:43 -05:00
3b357e5402 fix parse_failure_due_conflicted_flags test (#5926) 2022-07-01 21:59:51 -05:00
79da470239 simplify error make (#5883) 2022-07-01 21:06:36 -05:00
37949e70e0 Add all flag to nu-check command (#5911)
* Add all flag

* Make all and moduel flags as mutually exclusive

* Fix new test

* format code...

* tweak words

* another tweak

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-07-01 15:49:24 -05:00
5d00ecef56 Return error when external command core dumped (#5908)
* Return error when external command core dumped

Fixes #5903

Signed-off-by: nibon7 <nibon7@163.com>

* Use signal-hook to get signal name

Signed-off-by: nibon7 <nibon7@163.com>

* Fix comment

Signed-off-by: nibon7 <nibon7@163.com>
2022-07-01 08:58:21 -05:00
6dde231dde make module imports in scripts used for relative path. (#5913)
* always load env

* add interactive argument for read_config_file
2022-07-01 06:35:09 -05:00
58fa2e51a2 update crate thiserror to version 1.0.31 in crates nu-cli, nu-command, nu-parser, nu-protocol (#5919) 2022-06-30 13:55:01 -07:00
cf0877bf72 ensure required positionals don't show up as optional when help (#5916)
* ensure `required` positionals show up as `required` when `help`

* moves it to the older format

* standardises across optional and required parameters
2022-07-01 05:51:41 +12:00
a0db4ce747 Better error handling using do (#5890)
* adds `capture-errors` flag for `do`

* adds `get-type` core command to get type

* fmt

* add tests in example

* fmt

* fix tests

* manually revert previous changes related to `get-type`

* adds method to check for error name using `into string`

* fix clippy
2022-06-29 20:01:34 -05:00
6ee13126f7 Update Dockerfile (#5910)
Container now uses unpriviledged user with UID 1000 by default
Container now uses Alpine as base
Final image size dropped to just 67MB
2022-06-29 18:36:24 -05:00
1c15a4ed3a docs: clarify print and echo commands (#5909)
I thought this comment was relevant:
https://github.com/nushell/nushell/issues/5724#issuecomment-1148164153
2022-06-29 18:43:46 -04:00
7aabc381a3 fix bug where thin theme wasn't getting applied correctly (#5905) 2022-06-28 14:14:20 -05:00
8c9dced71b fix excessive ansi escape sequences (#5901) 2022-06-27 18:51:14 -05:00
06d5a31301 Make sort logic available outside sort-by (#5893) 2022-06-27 13:36:59 -04:00
ffbc0b0180 Header filtering out of for loop (#5896)
* remove extra print

* dataframe with real index

* corrected dataframe tests

* clippy error

* clippy error

* moved header filter out of loop
2022-06-27 06:33:45 -05:00
c0901ef707 Dataframe with real index (#5892)
* remove extra print

* dataframe with real index

* corrected dataframe tests

* clippy error

* clippy error
2022-06-26 17:32:18 -05:00
d3e84daa49 remove extra print (#5891) 2022-06-26 11:48:30 -05:00
228ede18cf build: update miette dependency (#5889) 2022-06-26 07:03:38 -05:00
c5a69271a2 make path exists work on expanded path (#5886)
* make path exists works with home

* fix test name
2022-06-26 06:55:55 -05:00
dc9d939c83 Introduce new command - nu check (#5864)
* nu check command - 1

* Support stream

* Polish code and fix corner case
2022-06-26 06:53:06 -05:00
32f0f94b46 feat: add --binary(-b) option to hash commands (#5885)
For instance,

```
echo 'abcdefghijklmnopqrstuvwxyz' | hash sha256 --binary
```

Will returns the hash as a binary value instead of a hexadecimaly encoded string.
2022-06-26 06:50:56 -05:00
a142d1a192 update encode decode with new signature (#5881) 2022-06-25 19:06:39 -05:00
173d60d59d Deprecate hash base64, extend decode and add encode commands (#5863)
* feat: deprecate `hash base64` command

* feat: extend `decode` and `encode` command families

This commit
- Adds `encode` command family
- Backports `hash base64` features to `encode base64` and `decode base64` subcommands.
- Refactors code a bit and extends tests for encodings
- `decode base64` returns a binary `Value` (that may be decoded into a string using `decode` command)

* feat: add `--binary(-b)` flag to `decode base64`

Default output type is now string, but binary can be requested using this new flag.
2022-06-26 00:35:23 +03:00
JT
f2989bf704 Move input/output type from Command to Signature (#5880) 2022-06-26 09:23:56 +12:00
JT
575ddbd4ef Clippy and remove unused is_binary (#5879) 2022-06-26 08:20:28 +12:00
ef9b72d360 add ability to convert timestamp_millis() (#5876)
* add ability to convert timestamp_millis()

* add example test

* add nanos too
2022-06-25 09:51:41 -05:00
25349a1eac Add an example for default command to get an env var with fallback (#5874)
* Add an example for `default` command to get an env var with fallback

* update test

* update test
2022-06-25 17:27:54 +08:00
99e4c44862 Fix less.exe downloading for windows release pkgs, close #5868 (#5873)
* Fix less.exe downloading for windows release pkgs

* Fix less.exe downloading for windows release pkgs
2022-06-25 09:09:48 +08:00
1345f97202 Errors when let in, let env and similar commands are passed. (#5866)
* throw `let nu/env/nothing/in` error in parsing

* add tests and fmt

* fix clippy

* suggestions

* fmt

* `lvalue.span` instead of `spans[1]`

* clippy

* fmt
2022-06-25 00:55:25 +03:00
f02076daa8 fix plugin path with whitespace (#5871) 2022-06-24 12:44:22 -05:00
JT
533e04a60a Bump to 0.64.1 dev version (#5865) 2022-06-24 16:47:00 +12:00
13c152b00f finish git fetch custom completions (#5859) 2022-06-23 05:19:11 -05:00
f231a6df4a Remove quotes from external args (#5846)
* remove quotes from external args

* remove internal quotes

* correct escaped quotes in string
2022-06-22 22:01:44 -05:00
3c0bccb900 Exclude ./... from expansion (#5839)
* exclude ./... from expansion

* use all instead of any

* no path expansion for external arguments

* clippy error

* expand only tilde
2022-06-22 22:00:30 -05:00
f43a65d7a7 Prevents duplicate fields in transpose -r (#5840) 2022-06-22 19:19:06 -05:00
0827ed143d cleanup $config as a built-in (#5852) 2022-06-22 13:13:03 -05:00
4b84825dbf Remove externa nu from nu config (#5847) 2022-06-22 09:42:18 +03:00
82ae06865c Port command (#5849)
* implement port command

* better comment

* fmt code

* fix example description

* fix usage

* fix tests
2022-06-21 23:27:58 -04:00
128ce6f9b7 update reedline config based on recent reedline changes (#5845) 2022-06-21 12:22:11 -05:00
44cbd88b55 allow comparison for similar types (#5844) 2022-06-21 12:15:31 -05:00
7164929c61 Db commands without DB (#5838)
* database commands without db

* database command tests
2022-06-21 12:14:29 -05:00
848ff8453b feat: Update dockerfile for latest nu release (#5843) 2022-06-21 18:28:31 +08:00
f94ca6cfde root/admin prompt is red now (#5836)
I really miss bash's visual way of signalising root, i.e. blue: user, red: root

So I brought it to nushell (since you've added `is-admin` the code is fully portable and easily-readable) and hope you'll like it
2022-06-20 15:23:55 -05:00
fab3f8fd40 fix exit code (#5835)
* fix exit code

* fix usage

* add comment
2022-06-20 09:05:11 -05:00
dbcfcdae89 calculates history duration properly (#5827) 2022-06-19 00:44:46 -04:00
08aa248c42 Add more tests for completion (#5826)
* Add more tests for completion

* Fix windows

* Cleanup
2022-06-18 19:42:00 -05:00
9f07bcc66f first stab at minimizing ansi escapes (#5822) 2022-06-17 22:07:46 -05:00
2caa44cea8 Fix parser panic (#5820) 2022-06-17 11:11:48 -07:00
28c21121cf fixes to nuon for inf, -inf, and NaN (#5818) 2022-06-17 21:01:37 +03:00
a17d46f200 add more columns to the history command when using sqlite history (#5817) 2022-06-17 09:35:34 -05:00
6cc8402127 Standardise to commands (#5800)
* standarize to commands

* move from to to into
2022-06-17 07:51:50 -05:00
5f0ad1d6ad Fix alias completion crash (#5814)
* Solve crash - commit 1

* commit 2 with issue

* Fix corner case

* Unit tests

* Fix windows tests
2022-06-17 07:50:10 -05:00
8d7bb9147e Attempts to fix file completions for open, rm and ls (and other filesystem commands) (#5805)
* fixes the issue for 'open' and other commands that explicitly use `SyntaxShape::Filepath`

* fixes for `rm` and similar commands

* fixes for `ls`: potentially breaking?

* fmt

* a curious fix to the test

* a curious fix to the test, except for Windows this time

* fixes Windows tests failing

* resolves it by putting an explicit check for `ls`

* reverts unnecessary test changes; fmt

* changes the order of completion operations
2022-06-17 07:47:43 -05:00
bc48b4553c Move the history and tutor commands out of core_commands (#5813)
* move history and tutor commands from core to misc

* add in the Misc Category for the history and tutor commands
2022-06-16 09:58:38 -07:00
28c07a5072 Add Windows Terminal profile and icon in Windows control panel (#5812)
* Show icon in Windows 'Add/Remove Programs' control panel

* Add install option for Windows Terminal profile

* Re-create icon because the icon was not shwon in Windows Terminal

Procedure: opened the original file with GIMP and simply overwrited it
2022-06-16 09:46:33 -07:00
30c8dabeb4 Add test requirements to PR template (#5809)
* Add test requirements to PR template

* tweak words

* another tweak

* add /tests folder as a suggestion

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-06-16 08:14:59 -05:00
8b368b6a4e Fix drop nth with open end range on 32-bit platforms (#5808)
Fixes #5793

Signed-off-by: nibon7 <nibon7@163.com>
2022-06-16 06:39:48 -05:00
8c0d60d0fb add notes for def_env (#5807)
* add notes for def_env

* Update crates/nu-command/src/core_commands/export_def_env.rs

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>

* Update crates/nu-command/src/core_commands/def_env.rs

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2022-06-16 06:37:44 -05:00
8b0a4ccf4c add light theme to default_config (#5804) 2022-06-16 06:19:49 -05:00
cfe4eff566 update default_context.rs to put the Du command in platform instead core (#5795) 2022-06-15 11:11:26 -07:00
38f3957edf update polars (#5791) 2022-06-15 11:45:03 -05:00
cb66d2bcad Try to fix winget package submit (#5790) 2022-06-15 07:35:28 -05:00
ff73623873 shows location of sqlite3 history file (#5784)
* shows location of sqlite3 file

* fmt
2022-06-15 10:06:49 +02:00
266 changed files with 7037 additions and 2647 deletions

View File

@ -4,6 +4,12 @@
# Tests
Make sure you've done the following:
- [ ] Add tests that cover your changes, either in the command examples, the crate/tests folder, or in the /tests folder.
- [ ] Try to think about corner cases and various ways how your changes could break. Cover them with tests.
- [ ] If adding tests is not possible, please document in the PR body a minimal example with steps on how to reproduce so one can verify your change works.
Make sure you've run and fixed any issues with these commands:
- [ ] `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)

View File

@ -102,8 +102,8 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
let releaseStem = $'($bin)-($version)-($target)'
$'(char nl)Download less related stuffs...'; hr-line
curl https://github.com/jftuga/less-Windows/releases/download/less-v590/less.exe -o $'($dist)\less.exe'
curl https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o $'($dist)\LICENSE-for-less.txt'
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v590/less.exe -o less.exe
aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
# Create Windows msi release package
if (get-env _EXTRA_) == 'msi' {

View File

@ -1,4 +1,4 @@
name: Submit Nushell package to Windows Package Manager Community Repository
name: Submit Nushell package to Windows Package Manager Community Repository
on:
workflow_dispatch:
@ -15,5 +15,5 @@ jobs:
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
$installerUrl = $github.release.assets | Where-Object -Property name -match 'windows-msvc.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 }}

136
Cargo.lock generated
View File

@ -80,9 +80,9 @@ dependencies = [
[[package]]
name = "ansi-str"
version = "0.1.1"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90cb0ceb8c444d026166795e474e9dfe54b443bdc07cc21bd6c5073f5e637482"
checksum = "e04d04a41a1463228d6eed971b9cdadd86b5577c69c09fd2379c57fe5e159071"
dependencies = [
"ansi-parser",
]
@ -131,9 +131,9 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "arrow-format"
version = "0.4.0"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2333f8ccf0d597ba779863c57a0b61f635721187fb2fdeabae92691d7d582fe5"
checksum = "216249afef413d7e9e9b4b543e73b3e371ace3a812380af98f1c871521572cdd"
dependencies = [
"planus",
"serde",
@ -141,26 +141,24 @@ dependencies = [
[[package]]
name = "arrow2"
version = "0.11.2"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b040061368d1314b0fd8b8f1fde0671eba1afc63a1c61a4dafaf2d4fc10c96f9"
checksum = "5feafd6df4e3f577529e6aa2b9b7cdb3c9fe8e8f66ebc8dc29abbe71a7e968f0"
dependencies = [
"arrow-format",
"base64",
"bytemuck",
"chrono",
"csv-core",
"either",
"fallible-streaming-iterator",
"futures",
"hash_hasher",
"indexmap",
"json-deserializer",
"lexical-core",
"multiversion",
"num-traits",
"parquet2",
"serde",
"serde_json",
"simdutf8",
"streaming-iterator",
"strength_reduce",
@ -1875,6 +1873,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "json-deserializer"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47631885425c482fcf2dc4b182fc973c3c5b81a8f43a028055559bd24cccfa6e"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@ -2199,13 +2203,13 @@ dependencies = [
[[package]]
name = "miette"
version = "4.7.1"
version = "5.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c90329e44f9208b55f45711f9558cec15d7ef8295cc65ecd6d4188ae8edc58c"
checksum = "6ec753a43fd71bb5f28751c9ec17fbe89d6d26ca8282d1e1f82f5ac3dbd5581e"
dependencies = [
"atty",
"backtrace",
"miette-derive 4.7.1",
"miette-derive 5.1.0",
"once_cell",
"owo-colors",
"supports-color",
@ -2230,9 +2234,9 @@ dependencies = [
[[package]]
name = "miette-derive"
version = "4.7.1"
version = "5.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b5bc45b761bcf1b5e6e6c4128cd93b84c218721a8d9b894aa0aff4ed180174c"
checksum = "fdfc33ea15c5446600f91d319299dd40301614afff7143cdfa9bf4c09da3ca64"
dependencies = [
"proc-macro2",
"quote",
@ -2470,7 +2474,7 @@ dependencies = [
[[package]]
name = "nu"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"assert_cmd",
"chrono",
@ -2480,7 +2484,7 @@ dependencies = [
"is_executable",
"itertools",
"log",
"miette 4.7.1",
"miette 5.1.0",
"nu-ansi-term",
"nu-cli",
"nu-color-config",
@ -2521,14 +2525,14 @@ dependencies = [
[[package]]
name = "nu-cli"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"chrono",
"crossterm",
"fuzzy-matcher",
"is_executable",
"log",
"miette 4.7.1",
"miette 5.1.0",
"nu-ansi-term",
"nu-color-config",
"nu-command",
@ -2545,7 +2549,7 @@ dependencies = [
[[package]]
name = "nu-color-config"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"nu-ansi-term",
"nu-json",
@ -2556,11 +2560,12 @@ dependencies = [
[[package]]
name = "nu-command"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"Inflector",
"alphanumeric-sort",
"base64",
"byteorder",
"bytesize",
"calamine",
"chrono",
@ -2617,6 +2622,7 @@ dependencies = [
"regex",
"reqwest",
"roxmltree",
"rstest",
"rusqlite",
"rust-embed",
"serde",
@ -2625,6 +2631,7 @@ dependencies = [
"serde_yaml",
"sha2 0.10.2",
"shadow-rs",
"signal-hook",
"sqlparser",
"strip-ansi-escapes",
"sysinfo 0.23.13",
@ -2645,7 +2652,7 @@ dependencies = [
[[package]]
name = "nu-engine"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"chrono",
"nu-glob",
@ -2657,7 +2664,7 @@ dependencies = [
[[package]]
name = "nu-glob"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"doc-comment",
"tempdir",
@ -2665,7 +2672,7 @@ dependencies = [
[[package]]
name = "nu-json"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"lazy_static",
"linked-hash-map",
@ -2678,11 +2685,12 @@ dependencies = [
[[package]]
name = "nu-parser"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"chrono",
"itertools",
"log",
"miette 4.7.1",
"miette 5.1.0",
"nu-path",
"nu-plugin",
"nu-protocol",
@ -2692,7 +2700,7 @@ dependencies = [
[[package]]
name = "nu-path"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"dirs-next",
"dunce",
@ -2701,7 +2709,7 @@ dependencies = [
[[package]]
name = "nu-plugin"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"capnp",
"nu-engine",
@ -2712,7 +2720,7 @@ dependencies = [
[[package]]
name = "nu-pretty-hex"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"heapless 0.7.13",
"nu-ansi-term",
@ -2721,13 +2729,13 @@ dependencies = [
[[package]]
name = "nu-protocol"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"byte-unit",
"chrono",
"chrono-humanize",
"indexmap",
"miette 4.7.1",
"miette 5.1.0",
"nu-json",
"nu-utils",
"num-format",
@ -2741,7 +2749,7 @@ dependencies = [
[[package]]
name = "nu-system"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"chrono",
"errno",
@ -2755,7 +2763,7 @@ dependencies = [
[[package]]
name = "nu-table"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"ansi-str",
"atty",
@ -2768,7 +2776,7 @@ dependencies = [
[[package]]
name = "nu-term-grid"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"strip-ansi-escapes",
"unicode-width",
@ -2776,7 +2784,7 @@ dependencies = [
[[package]]
name = "nu-test-support"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"getset",
"hamcrest2",
@ -2788,14 +2796,14 @@ dependencies = [
[[package]]
name = "nu-utils"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"crossterm_winapi",
]
[[package]]
name = "nu_plugin_example"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"nu-plugin",
"nu-protocol",
@ -2803,7 +2811,7 @@ dependencies = [
[[package]]
name = "nu_plugin_gstat"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"git2",
"nu-engine",
@ -2813,7 +2821,7 @@ dependencies = [
[[package]]
name = "nu_plugin_inc"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"nu-plugin",
"nu-protocol",
@ -2822,7 +2830,7 @@ dependencies = [
[[package]]
name = "nu_plugin_query"
version = "0.64.0"
version = "0.65.0"
dependencies = [
"gjson",
"nu-engine",
@ -3145,9 +3153,9 @@ dependencies = [
[[package]]
name = "parquet2"
version = "0.12.1"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbacca5619bdee7f942938890451dea1a61f082c682aac913d7b4e326e66d7b4"
checksum = "73fd2690ad041f9296876daef1f2706f6347073bdbcc719090887f1691e4a09d"
dependencies = [
"async-stream",
"bitpacking",
@ -3319,9 +3327,9 @@ dependencies = [
[[package]]
name = "polars"
version = "0.21.1"
version = "0.22.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b140da767e129c60c41c8e1968ffab5f114bcf823182edb7fa900464a31bf421"
checksum = "3d175c67e80ceaef7219258cfc3a8686531d9510875b0cefa25404e5b80a7933"
dependencies = [
"polars-core",
"polars-io",
@ -3332,9 +3340,9 @@ dependencies = [
[[package]]
name = "polars-arrow"
version = "0.21.1"
version = "0.22.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d27df11ee28956bd6f5aed54e7e05ce87b886871995e1da501134627ec89077"
checksum = "f66c7d3da2c10a09131294dbe7802fac792f570be639dc6ebf207bfc3e144287"
dependencies = [
"arrow2",
"hashbrown 0.12.1",
@ -3345,9 +3353,9 @@ dependencies = [
[[package]]
name = "polars-core"
version = "0.21.1"
version = "0.22.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdf8d12cb7ec278516228fc86469f98c62ab81ca31e4e76d2c0ccf5a09c70491"
checksum = "f7f15f443a90d5367c4fbbb151e203f03b5b96055c8b928c6bc30655a3644f13"
dependencies = [
"ahash",
"anyhow",
@ -3356,8 +3364,8 @@ dependencies = [
"comfy-table",
"hashbrown 0.12.1",
"indexmap",
"lazy_static",
"num 0.4.0",
"once_cell",
"polars-arrow",
"polars-utils",
"rand 0.8.5",
@ -3371,20 +3379,21 @@ dependencies = [
[[package]]
name = "polars-io"
version = "0.21.1"
version = "0.22.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdd4b762e5694f359ded21ca0627b5bc95b6eb49f6b330569afc1d20f0564b01"
checksum = "058d0a847ce5009b974c69ec878ed416e306436f21b626543019f738cee12315"
dependencies = [
"ahash",
"anyhow",
"arrow2",
"csv-core",
"dirs",
"lazy_static",
"lexical",
"lexical-core",
"memchr",
"memmap2",
"num 0.4.0",
"once_cell",
"polars-arrow",
"polars-core",
"polars-time",
@ -3398,9 +3407,9 @@ dependencies = [
[[package]]
name = "polars-lazy"
version = "0.21.1"
version = "0.22.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eedc21001f05611e41bb7439b38d0f4ef9406aa49c17f3b289b5f57d8fa40c59"
checksum = "dad86a4ce7e32540ff12089bce6f77270fd133a5b263328a92be61defdd6b151"
dependencies = [
"ahash",
"glob",
@ -3408,6 +3417,7 @@ dependencies = [
"polars-arrow",
"polars-core",
"polars-io",
"polars-ops",
"polars-time",
"polars-utils",
"rayon",
@ -3416,31 +3426,33 @@ dependencies = [
[[package]]
name = "polars-ops"
version = "0.21.1"
version = "0.22.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86fae68f0992955f224f09d1f15648a6fb76d8e3b962efac2f97ccc2aa58977a"
checksum = "030ecd473be113cd0264f1bc19de39a844fa12fa565db9dc52c859cbc292cf04"
dependencies = [
"polars-arrow",
"polars-core",
]
[[package]]
name = "polars-time"
version = "0.21.1"
version = "0.22.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be499f73749e820f96689c5f9ec59669b7cdd551d864358e2bdaebb5944e4bfb"
checksum = "94047b20d2da3bcc55c421be187a0c6f316cf1eea7fe7ed7347c1160a32d017c"
dependencies = [
"chrono",
"lexical",
"polars-arrow",
"polars-core",
"polars-utils",
"serde",
]
[[package]]
name = "polars-utils"
version = "0.21.1"
version = "0.22.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f4cd569d383f5f000abbd6d5146550e6cb4e43fac30d1af98699499a440d56"
checksum = "fcd3d0238462d5d9f7fbeaaea46e73ed4d58f6fae8b70d53cbe51d7538cc43f5"
dependencies = [
"parking_lot 0.12.1",
"rayon",
@ -3816,14 +3828,15 @@ dependencies = [
[[package]]
name = "reedline"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f8163ab90fabf0b8978b824743bb7a384394501b4b40c198a146c0eb9670ca4"
checksum = "d064ec92a21deb048c440b6461ccf0d0babdb5f2160ea9dfac451bc7b4b556e2"
dependencies = [
"chrono",
"crossterm",
"fd-lock",
"gethostname",
"itertools",
"nu-ansi-term",
"rusqlite",
"serde",
@ -4224,7 +4237,6 @@ version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
dependencies = [
"indexmap",
"itoa 1.0.2",
"ryu",
"serde",

View File

@ -11,7 +11,7 @@ name = "nu"
readme = "README.md"
repository = "https://github.com/nushell/nushell"
rust-version = "1.60"
version = "0.64.0"
version = "0.65.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -36,23 +36,23 @@ chrono = { version = "0.4.19", features = ["serde"] }
crossterm = "0.23.0"
ctrlc = "3.2.1"
log = "0.4"
miette = "4.5.0"
miette = "5.1.0"
nu-ansi-term = "0.46.0"
nu-cli = { path="./crates/nu-cli", version = "0.64.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.64.0" }
nu-command = { path="./crates/nu-command", version = "0.64.0" }
nu-engine = { path="./crates/nu-engine", version = "0.64.0" }
nu-json = { path="./crates/nu-json", version = "0.64.0" }
nu-parser = { path="./crates/nu-parser", version = "0.64.0" }
nu-path = { path="./crates/nu-path", version = "0.64.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.64.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.64.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.64.0" }
nu-system = { path = "./crates/nu-system", version = "0.64.0" }
nu-table = { path = "./crates/nu-table", version = "0.64.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.64.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.64.0" }
reedline = { version = "0.7.0", features = ["bashisms", "sqlite"]}
nu-cli = { path="./crates/nu-cli", version = "0.65.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.65.0" }
nu-command = { path="./crates/nu-command", version = "0.65.0" }
nu-engine = { path="./crates/nu-engine", version = "0.65.0" }
nu-json = { path="./crates/nu-json", version = "0.65.0" }
nu-parser = { path="./crates/nu-parser", version = "0.65.0" }
nu-path = { path="./crates/nu-path", version = "0.65.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.65.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.65.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.65.0" }
nu-system = { path = "./crates/nu-system", version = "0.65.0" }
nu-table = { path = "./crates/nu-table", version = "0.65.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.65.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.65.0" }
reedline = { version = "0.8.0", features = ["bashisms", "sqlite"]}
pretty_env_logger = "0.4.0"
rayon = "1.5.1"
is_executable = "1.0.1"
@ -63,7 +63,7 @@ openssl = { version = "0.10.38", features = ["vendored"], optional = true }
signal-hook = { version = "0.3.14", default-features = false }
[dev-dependencies]
nu-test-support = { path="./crates/nu-test-support", version = "0.64.0" }
nu-test-support = { path="./crates/nu-test-support", version = "0.65.0" }
tempfile = "3.2.0"
assert_cmd = "2.0.2"
pretty_assertions = "1.0.0"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -4,24 +4,24 @@ description = "CLI-related functionality for Nushell"
edition = "2021"
license = "MIT"
name = "nu-cli"
version = "0.64.0"
version = "0.65.0"
[dev-dependencies]
nu-test-support = { path="../nu-test-support", version = "0.64.0" }
nu-command = { path = "../nu-command", version = "0.64.0" }
nu-test-support = { path="../nu-test-support", version = "0.65.0" }
nu-command = { path = "../nu-command", version = "0.65.0" }
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.64.0" }
nu-path = { path = "../nu-path", version = "0.64.0" }
nu-parser = { path = "../nu-parser", version = "0.64.0" }
nu-protocol = { path = "../nu-protocol", version = "0.64.0" }
nu-utils = { path = "../nu-utils", version = "0.64.0" }
nu-engine = { path = "../nu-engine", version = "0.65.0" }
nu-path = { path = "../nu-path", version = "0.65.0" }
nu-parser = { path = "../nu-parser", version = "0.65.0" }
nu-protocol = { path = "../nu-protocol", version = "0.65.0" }
nu-utils = { path = "../nu-utils", version = "0.65.0" }
nu-ansi-term = "0.46.0"
nu-color-config = { path = "../nu-color-config", version = "0.64.0" }
reedline = { version = "0.7.0", features = ["bashisms", "sqlite"]}
nu-color-config = { path = "../nu-color-config", version = "0.65.0" }
reedline = { version = "0.8.0", features = ["bashisms", "sqlite"]}
crossterm = "0.23.0"
miette = { version = "4.5.0", features = ["fancy"] }
thiserror = "1.0.29"
miette = { version = "5.1.0", features = ["fancy"] }
thiserror = "1.0.31"
fuzzy-matcher = "0.3.7"
log = "0.4"

View File

@ -18,7 +18,7 @@ pub fn evaluate_commands(
input: PipelineData,
is_perf_true: bool,
table_mode: Option<Value>,
) -> Result<()> {
) -> Result<Option<i64>> {
// Run a command (or commands) given to us by the user
let (block, delta) = {
if let Some(ref t_mode) = table_mode {
@ -74,7 +74,7 @@ pub fn evaluate_commands(
std::process::exit(1);
}
match eval_block(engine_state, stack, &block, input, false, false) {
let exit_code = match eval_block(engine_state, stack, &block, input, false, false) {
Ok(pipeline_data) => {
crate::eval_file::print_table_or_error(engine_state, stack, pipeline_data, &mut config)
}
@ -84,11 +84,11 @@ pub fn evaluate_commands(
report_error(&working_set, &err);
std::process::exit(1);
}
}
};
if is_perf_true {
info!("evaluate {}:{}:{}", file!(), line!(), column!());
}
Ok(())
Ok(exit_code)
}

View File

@ -68,14 +68,10 @@ impl NuCompleter {
for pipeline in output.pipelines.into_iter() {
for expr in pipeline.expressions {
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
let span_offset: usize = alias_offset.iter().sum();
for (flat_idx, flat) in flattened.iter().enumerate() {
let alias = if alias_offset.is_empty() {
0
} else {
alias_offset[flat_idx]
};
if pos >= flat.0.start - alias && pos < flat.0.end - alias {
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
// Context variables
let most_left_var =
most_left_variable(flat_idx, &working_set, flattened.clone());
@ -84,42 +80,18 @@ impl NuCompleter {
let new_span = if flat_idx == 0 {
Span {
start: flat.0.start,
end: flat.0.end - 1 - alias,
end: flat.0.end - 1 - span_offset,
}
} else {
Span {
start: flat.0.start - alias,
end: flat.0.end - 1 - alias,
start: flat.0.start - span_offset,
end: flat.0.end - 1 - span_offset,
}
};
// Parses the prefix
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
prefix.remove(pos - (flat.0.start - alias));
// Completions that depends on the previous expression (e.g: use, source)
if flat_idx > 0 {
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
// Read the content for the previous expression
let prev_expr_str =
working_set.get_span_contents(previous_expr.0).to_vec();
// Completion for .nu files
if prev_expr_str == b"use" || prev_expr_str == b"source" {
let mut completer =
DotNuCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
}
}
prefix.remove(pos - (flat.0.start - span_offset));
// Variables completion
if prefix.starts_with(b"$") || most_left_var.is_some() {
@ -153,6 +125,42 @@ impl NuCompleter {
);
}
// Completions that depends on the previous expression (e.g: use, source)
if flat_idx > 0 {
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
// Read the content for the previous expression
let prev_expr_str =
working_set.get_span_contents(previous_expr.0).to_vec();
// Completion for .nu files
if prev_expr_str == b"use" || prev_expr_str == b"source" {
let mut completer =
DotNuCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
} else if prev_expr_str == b"ls" {
let mut completer =
FileCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
}
}
// Match other types
match &flat.1 {
FlatShape::Custom(decl_id) => {
@ -185,6 +193,18 @@ impl NuCompleter {
pos,
);
}
FlatShape::Filepath | FlatShape::GlobPattern => {
let mut completer = FileCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
flat_shape => {
let mut completer = CommandCompletion::new(
self.engine_state.clone(),
@ -235,7 +255,7 @@ impl ReedlineCompleter for NuCompleter {
}
}
type MatchedAlias<'a> = Vec<(&'a [u8], &'a [u8])>;
type MatchedAlias = Vec<(Vec<u8>, Vec<u8>)>;
// Handler the completion when giving lines contains at least one alias. (e.g: `g checkout`)
// that `g` is an alias of `git`
@ -254,6 +274,13 @@ fn try_find_alias(line: &[u8], working_set: &StateWorkingSet) -> (Vec<u8>, Vec<u
lens -= 1;
}
}
if !line.is_empty() {
let last = line.last().expect("input is empty");
if last == &b' ' {
output.push(b' ');
}
}
} else {
output = line.to_vec();
}
@ -261,7 +288,7 @@ fn try_find_alias(line: &[u8], working_set: &StateWorkingSet) -> (Vec<u8>, Vec<u
(output, alias_offset)
}
fn search_alias<'a>(input: &'a [u8], working_set: &'a StateWorkingSet) -> Option<MatchedAlias<'a>> {
fn search_alias(input: &[u8], working_set: &StateWorkingSet) -> Option<MatchedAlias> {
let mut vec_names = vec![];
let mut vec_alias = vec![];
let mut pos = 0;
@ -269,27 +296,31 @@ fn search_alias<'a>(input: &'a [u8], working_set: &'a StateWorkingSet) -> Option
for (index, character) in input.iter().enumerate() {
if *character == b' ' {
let range = &input[pos..index];
vec_names.push(range);
vec_names.push(range.to_owned());
pos = index + 1;
}
}
// Push the rest to names vector.
if pos < input.len() {
vec_names.push(&input[pos..]);
vec_names.push((&input[pos..]).to_owned());
}
for name in &vec_names {
if let Some(alias_id) = working_set.find_alias(name) {
if let Some(alias_id) = working_set.find_alias(&name[..]) {
let alias_span = working_set.get_alias(alias_id);
let mut span_vec = vec![];
is_alias = true;
for alias in alias_span {
let name = working_set.get_span_contents(*alias);
if !name.is_empty() {
vec_alias.push(name);
span_vec.push(name);
}
}
// Join span of vector together for complex alias, e.g: `f` is an alias for `git remote -v`
let full_aliases = span_vec.join(&[b' '][..]);
vec_alias.push(full_aliases);
} else {
vec_alias.push(name);
vec_alias.push(name.to_owned());
}
}

View File

@ -41,7 +41,7 @@ impl Completer for VariableCompletion {
options: &CompletionOptions,
) -> Vec<Suggestion> {
let mut output = vec![];
let builtins = ["$nu", "$in", "$config", "$env", "$nothing"];
let builtins = ["$nu", "$in", "$env", "$nothing"];
let var_str = std::str::from_utf8(&self.var_context.0)
.unwrap_or("")
.to_lowercase();

View File

@ -66,7 +66,7 @@ pub fn print_table_or_error(
stack: &mut Stack,
mut pipeline_data: PipelineData,
config: &mut Config,
) {
) -> Option<i64> {
let exit_code = match &mut pipeline_data {
PipelineData::ExternalStream { exit_code, .. } => exit_code.take(),
_ => None,
@ -130,6 +130,14 @@ pub fn print_table_or_error(
// Make sure everything has finished
if let Some(exit_code) = exit_code {
let _: Vec<_> = exit_code.into_iter().collect();
let mut exit_code: Vec<_> = exit_code.into_iter().collect();
exit_code
.pop()
.and_then(|last_exit_code| match last_exit_code {
Value::Int { val: code, .. } => Some(code),
_ => None,
})
} else {
None
}
}

View File

@ -21,11 +21,19 @@ impl Command for Print {
"print without inserting a newline for the line ending",
Some('n'),
)
.switch("stderr", "print to stderr instead of stdout", Some('e'))
.category(Category::Strings)
}
fn usage(&self) -> &str {
"Prints the values given"
"Print the given values to stdout"
}
fn extra_usage(&self) -> &str {
r#"Unlike `echo`, this command does not return any value (`print | describe` will return "nothing").
Since this command has no output, there is no point in piping it with other commands.
`print` may be used inside blocks of code (e.g.: hooks) to display text during execution without interfering with the pipeline."#
}
fn search_terms(&self) -> Vec<&str> {
@ -41,11 +49,12 @@ impl Command for Print {
) -> Result<PipelineData, ShellError> {
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
let no_newline = call.has_flag("no-newline");
let to_stderr = call.has_flag("stderr");
let head = call.head;
for arg in args {
arg.into_pipeline_data()
.print(engine_state, stack, no_newline)?;
.print(engine_state, stack, no_newline, to_stderr)?;
}
Ok(PipelineData::new(head))

View File

@ -875,7 +875,16 @@ fn edit_from_record(
"moveleft" => EditCommand::MoveLeft,
"moveright" => EditCommand::MoveRight,
"movewordleft" => EditCommand::MoveWordLeft,
"movebigwordleft" => EditCommand::MoveBigWordLeft,
"movewordright" => EditCommand::MoveWordRight,
"movewordrightend" => EditCommand::MoveWordRightEnd,
"movebigwordrightend" => EditCommand::MoveBigWordRightEnd,
"movewordrightstart" => EditCommand::MoveWordRightStart,
"movebigwordrightstart" => EditCommand::MoveBigWordRightStart,
"movetoposition" => {
let value = extract_value("value", cols, vals, span)?;
EditCommand::MoveToPosition(value.as_integer()? as usize)
}
"insertchar" => {
let value = extract_value("value", cols, vals, span)?;
let char = extract_char(value, config)?;
@ -888,6 +897,7 @@ fn edit_from_record(
"insertnewline" => EditCommand::InsertNewline,
"backspace" => EditCommand::Backspace,
"delete" => EditCommand::Delete,
"cutchar" => EditCommand::CutChar,
"backspaceword" => EditCommand::BackspaceWord,
"deleteword" => EditCommand::DeleteWord,
"clear" => EditCommand::Clear,
@ -898,7 +908,11 @@ fn edit_from_record(
"cuttoend" => EditCommand::CutToEnd,
"cuttolineend" => EditCommand::CutToLineEnd,
"cutwordleft" => EditCommand::CutWordLeft,
"cutbigwordleft" => EditCommand::CutBigWordLeft,
"cutwordright" => EditCommand::CutWordRight,
"cutbigwordright" => EditCommand::CutBigWordRight,
"cutwordrighttonext" => EditCommand::CutWordRightToNext,
"cutbigwordrighttonext" => EditCommand::CutBigWordRightToNext,
"pastecutbufferbefore" => EditCommand::PasteCutBufferBefore,
"pastecutbufferafter" => EditCommand::PasteCutBufferAfter,
"uppercaseword" => EditCommand::UppercaseWord,

View File

@ -247,7 +247,7 @@ pub fn eval_source(
set_last_exit_code(stack, 0);
}
if let Err(err) = pipeline_data.print(engine_state, stack, false) {
if let Err(err) = pipeline_data.print(engine_state, stack, false, false) {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &err);

View File

@ -0,0 +1,65 @@
pub mod support;
use nu_cli::NuCompleter;
use reedline::Completer;
use support::{match_suggestions, new_engine};
#[test]
fn alias_of_command_and_flags() {
let (dir, _, mut engine, mut stack) = new_engine();
// Create an alias
let alias = r#"alias ll = ls -l"#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let suggestions = completer.complete("ll t", 4);
#[cfg(windows)]
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn alias_of_basic_command() {
let (dir, _, mut engine, mut stack) = new_engine();
// Create an alias
let alias = r#"alias ll = ls "#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let suggestions = completer.complete("ll t", 4);
#[cfg(windows)]
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn alias_of_another_alias() {
let (dir, _, mut engine, mut stack) = new_engine();
// Create an alias
let alias = r#"alias ll = ls -la"#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir.clone()).is_ok());
// Create the second alias
let alias = r#"alias lf = ll -f"#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let suggestions = completer.complete("lf t", 4);
#[cfg(windows)]
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
match_suggestions(expected_paths, suggestions)
}

View File

@ -14,7 +14,7 @@ fn variables_completions() {
def my-command [animal: string@animals] { print $animal }"#;
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instatiate a new completer
// Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for $nu

View File

@ -40,3 +40,233 @@ fn file_completions() {
// Match the results
match_suggestions(expected_paths, suggestions);
}
#[test]
fn command_ls_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "ls ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_open_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "open ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_rm_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "rm ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_cp_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "cp ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_save_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "save ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_touch_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "touch ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_watch_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "watch ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}

View File

@ -4,11 +4,11 @@ description = "Color configuration code used by Nushell"
edition = "2021"
license = "MIT"
name = "nu-color-config"
version = "0.64.0"
version = "0.65.0"
[dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.64.0" }
nu-protocol = { path = "../nu-protocol", version = "0.65.0" }
nu-ansi-term = "0.46.0"
nu-json = { path = "../nu-json", version = "0.64.0" }
nu-table = { path = "../nu-table", version = "0.64.0" }
nu-json = { path = "../nu-json", version = "0.65.0" }
nu-table = { path = "../nu-table", version = "0.65.0" }
serde = { version="1.0.123", features=["derive"] }

View File

@ -4,30 +4,31 @@ description = "Nushell's built-in commands"
edition = "2021"
license = "MIT"
name = "nu-command"
version = "0.64.0"
version = "0.65.0"
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-color-config = { path = "../nu-color-config", version = "0.64.0" }
nu-engine = { path = "../nu-engine", version = "0.64.0" }
nu-glob = { path = "../nu-glob", version = "0.64.0" }
nu-json = { path = "../nu-json", version = "0.64.0" }
nu-parser = { path = "../nu-parser", version = "0.64.0" }
nu-path = { path = "../nu-path", version = "0.64.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.64.0" }
nu-protocol = { path = "../nu-protocol", version = "0.64.0" }
nu-system = { path = "../nu-system", version = "0.64.0" }
nu-table = { path = "../nu-table", version = "0.64.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.64.0" }
nu-test-support = { path = "../nu-test-support", version = "0.64.0" }
nu-utils = { path = "../nu-utils", version = "0.64.0" }
nu-color-config = { path = "../nu-color-config", version = "0.65.0" }
nu-engine = { path = "../nu-engine", version = "0.65.0" }
nu-glob = { path = "../nu-glob", version = "0.65.0" }
nu-json = { path = "../nu-json", version = "0.65.0" }
nu-parser = { path = "../nu-parser", version = "0.65.0" }
nu-path = { path = "../nu-path", version = "0.65.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.65.0" }
nu-protocol = { path = "../nu-protocol", version = "0.65.0" }
nu-system = { path = "../nu-system", version = "0.65.0" }
nu-table = { path = "../nu-table", version = "0.65.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.65.0" }
nu-test-support = { path = "../nu-test-support", version = "0.65.0" }
nu-utils = { path = "../nu-utils", version = "0.65.0" }
nu-ansi-term = "0.46.0"
# Potential dependencies for extras
alphanumeric-sort = "1.4.4"
base64 = "0.13.0"
byteorder = "1.4.3"
bytesize = "1.1.0"
calamine = "0.18.0"
chrono = { version = "0.4.19", features = ["serde"] }
@ -76,14 +77,14 @@ shadow-rs = { version = "0.11.0", default-features = false }
strip-ansi-escapes = "0.1.1"
sysinfo = "0.23.5"
terminal_size = "0.1.17"
thiserror = "1.0.29"
thiserror = "1.0.31"
titlecase = "1.1.0"
toml = "0.5.8"
unicode-segmentation = "1.8.0"
url = "2.2.1"
uuid = { version = "0.8.2", features = ["v4"] }
which = { version = "4.2.2", optional = true }
reedline = { version = "0.7.0", features = ["bashisms", "sqlite"]}
reedline = { version = "0.8.0", features = ["bashisms", "sqlite"]}
wax = { version = "0.4.0", features = ["diagnostics"] }
rusqlite = { version = "0.27.0", features = ["bundled"], optional = true }
sqlparser = { version = "0.16.0", features = ["serde"], optional = true }
@ -91,13 +92,14 @@ sqlparser = { version = "0.16.0", features = ["serde"], optional = true }
[target.'cfg(unix)'.dependencies]
umask = "2.0.0"
users = "0.11.0"
signal-hook = { version = "0.3.14", default-features = false }
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
version = "2.1.3"
optional = true
[dependencies.polars]
version = "0.21.1"
version = "0.22.8"
# path = "../../../../polars/polars"
optional = true
features = [
@ -105,7 +107,7 @@ features = [
"object", "checked_arithmetic", "strings", "cum_agg", "is_in",
"rolling_window", "strings", "rows", "random",
"dtype-datetime", "dtype-struct", "lazy", "cross_join",
"dynamic_groupby"
"dynamic_groupby", "dtype-categorical"
]
[target.'cfg(windows)'.dependencies.windows]
@ -132,3 +134,4 @@ hamcrest2 = "0.3.0"
dirs-next = "2.0.0"
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
rstest = "0.12.0"

View File

@ -0,0 +1,98 @@
use super::{operate, BytesArgument};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
#[derive(Clone)]
pub struct BytesLen;
struct Arguments {
column_paths: Option<Vec<CellPath>>,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
}
}
impl Command for BytesLen {
fn name(&self) -> &str {
"bytes length"
}
fn signature(&self) -> Signature {
Signature::build("bytes length")
.rest(
"rest",
SyntaxShape::CellPath,
"optionally find length of binary by column paths",
)
.category(Category::Bytes)
}
fn usage(&self) -> &str {
"Output the length of any bytes in the pipeline"
}
fn search_terms(&self) -> Vec<&str> {
vec!["len", "size", "count"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() {
None
} else {
Some(column_paths)
};
let arg = Arguments { column_paths };
operate(length, arg, input, call.head, engine_state.ctrlc.clone())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Return the lengths of multiple strings",
example: "0x[1F FF AA AB] | bytes length",
result: Some(Value::test_int(4)),
},
Example {
description: "Return the lengths of multiple strings",
example: "[0x[1F FF AA AB] 0x[1F]] | bytes length",
result: Some(Value::List {
vals: vec![Value::test_int(4), Value::test_int(1)],
span: Span::test_data(),
}),
},
]
}
}
fn length(input: &[u8], _arg: &Arguments, span: Span) -> Value {
Value::Int {
val: input.len() as i64,
span,
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(BytesLen {})
}
}

View File

@ -0,0 +1,77 @@
mod length;
mod starts_with;
use nu_protocol::ast::CellPath;
use nu_protocol::{PipelineData, ShellError, Span, Value};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
pub use length::BytesLen;
pub use starts_with::BytesStartsWith;
trait BytesArgument {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>>;
}
/// map input pipeline data, for each elements, if it's Binary, invoke relative `cmd` with `arg`.
fn operate<C, A>(
cmd: C,
mut arg: A,
input: PipelineData,
span: Span,
ctrlc: Option<Arc<AtomicBool>>,
) -> Result<PipelineData, ShellError>
where
A: BytesArgument + Send + Sync + 'static,
C: Fn(&[u8], &A, Span) -> Value + Send + Sync + 'static + Clone + Copy,
{
match arg.take_column_paths() {
None => input.map(
move |v| match v {
Value::Binary {
val,
span: val_span,
} => cmd(&val, &arg, val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
},
ctrlc,
),
Some(column_paths) => {
let arg = Arc::new(arg);
input.map(
move |mut v| {
for path in &column_paths {
let opt = arg.clone();
let r = v.update_cell_path(
&path.members,
Box::new(move |old| {
match old {
Value::Binary {val, span: val_span} => cmd(val, &opt, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
}}}),
);
if let Err(error) = r {
return Value::Error { error };
}
}
v
},
ctrlc,
)
}
}
}

View File

@ -0,0 +1,122 @@
use super::{operate, BytesArgument};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
struct Arguments {
pattern: Vec<u8>,
column_paths: Option<Vec<CellPath>>,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
}
}
#[derive(Clone)]
pub struct BytesStartsWith;
impl Command for BytesStartsWith {
fn name(&self) -> &str {
"bytes starts-with"
}
fn signature(&self) -> Signature {
Signature::build("bytes starts-with")
.required("pattern", SyntaxShape::Binary, "the pattern to match")
.rest(
"rest",
SyntaxShape::CellPath,
"optionally matches prefix of text by column paths",
)
.category(Category::Bytes)
}
fn usage(&self) -> &str {
"Check if bytes starts with a pattern"
}
fn search_terms(&self) -> Vec<&str> {
vec!["pattern", "match", "find", "search"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() {
None
} else {
Some(column_paths)
};
let arg = Arguments {
pattern,
column_paths,
};
operate(
starts_with,
arg,
input,
call.head,
engine_state.ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Checks if binary starts with `0x[1F FF AA]`",
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F FF AA]",
result: Some(Value::Bool {
val: true,
span: Span::test_data(),
}),
},
Example {
description: "Checks if binary starts with `0x[1F]`",
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F]",
result: Some(Value::Bool {
val: true,
span: Span::test_data(),
}),
},
Example {
description: "Checks if binary starts with `0x[1F]`",
example: "0x[1F FF AA AA] | bytes starts-with 0x[11]",
result: Some(Value::Bool {
val: false,
span: Span::test_data(),
}),
},
]
}
}
fn starts_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value {
Value::Bool {
val: input.starts_with(pattern),
span,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(BytesStartsWith {})
}
}

View File

@ -1,3 +1,4 @@
use crate::{generate_strftime_list, parse_date_from_string};
use chrono::{DateTime, FixedOffset, Local, TimeZone, Utc};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
@ -7,9 +8,6 @@ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
};
use crate::generate_strftime_list;
use crate::parse_date_from_string;
struct Arguments {
timezone: Option<Spanned<String>>,
offset: Option<Spanned<i64>>,
@ -148,6 +146,15 @@ impl Command for SubCommand {
example: "1614434140 | into datetime -o +9",
result: None,
},
Example {
description:
"Convert timestamps like the sqlite history t",
example: "1656165681720 | into datetime",
result: Some(Value::Date {
val: Utc.timestamp_millis(1656165681720).into(),
span: Span::test_data(),
}),
},
]
}
}
@ -251,10 +258,19 @@ fn action(
return match timezone {
// default to UTC
None => Value::Date {
val: Utc.timestamp(ts, 0).into(),
span: head,
},
None => {
// be able to convert chrono::Utc::now()
let dt = match ts.to_string().len() {
x if x > 13 => Utc.timestamp_nanos(ts).into(),
x if x > 10 => Utc.timestamp_millis(ts).into(),
_ => Utc.timestamp(ts, 0).into(),
};
Value::Date {
val: dt,
span: head,
}
}
Some(Spanned { item, span }) => match item {
Zone::Utc => Value::Date {
val: Utc.timestamp(ts, 0).into(),

View File

@ -8,6 +8,7 @@ use nu_protocol::{
struct Arguments {
radix: Option<Value>,
column_paths: Vec<CellPath>,
little_endian: bool,
}
#[derive(Clone)]
@ -21,6 +22,7 @@ impl Command for SubCommand {
fn signature(&self) -> Signature {
Signature::build("into int")
.named("radix", SyntaxShape::Number, "radix of integer", Some('r'))
.switch("little-endian", "use little-endian byte decoding", None)
.rest(
"rest",
SyntaxShape::CellPath,
@ -114,6 +116,7 @@ fn into_int(
let options = Arguments {
radix: call.get_flag(engine_state, stack, "radix")?,
little_endian: call.has_flag("little-endian"),
column_paths: call.rest(engine_state, stack, 0)?,
};
@ -135,13 +138,13 @@ fn into_int(
input.map(
move |v| {
if options.column_paths.is_empty() {
action(&v, head, radix)
action(&v, head, radix, options.little_endian)
} else {
let mut ret = v;
for path in &options.column_paths {
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, head, radix)),
Box::new(move |old| action(old, head, radix, options.little_endian)),
);
if let Err(error) = r {
return Value::Error { error };
@ -155,7 +158,7 @@ fn into_int(
)
}
pub fn action(input: &Value, span: Span, radix: u32) -> Value {
pub fn action(input: &Value, span: Span, radix: u32, little_endian: bool) -> Value {
match input {
Value::Int { val: _, .. } => {
if radix == 10 {
@ -190,6 +193,33 @@ pub fn action(input: &Value, span: Span, radix: u32) -> Value {
val: val.timestamp(),
span,
},
Value::Binary { val, span } => {
use byteorder::{BigEndian, ByteOrder, LittleEndian};
let mut val = val.to_vec();
if little_endian {
while val.len() < 8 {
val.push(0);
}
val.resize(8, 0);
Value::Int {
val: LittleEndian::read_i64(&val),
span: *span,
}
} else {
while val.len() < 8 {
val.insert(0, 0);
}
val.resize(8, 0);
Value::Int {
val: BigEndian::read_i64(&val),
span: *span,
}
}
}
_ => Value::Error {
error: ShellError::UnsupportedInput(
format!("'into int' for unsupported type '{}'", input.get_type()),
@ -294,21 +324,21 @@ mod test {
let word = Value::test_string("10");
let expected = Value::test_int(10);
let actual = action(&word, Span::test_data(), 10);
let actual = action(&word, Span::test_data(), 10, false);
assert_eq!(actual, expected);
}
#[test]
fn turns_binary_to_integer() {
let s = Value::test_string("0b101");
let actual = action(&s, Span::test_data(), 10);
let actual = action(&s, Span::test_data(), 10, false);
assert_eq!(actual, Value::test_int(5));
}
#[test]
fn turns_hex_to_integer() {
let s = Value::test_string("0xFF");
let actual = action(&s, Span::test_data(), 16);
let actual = action(&s, Span::test_data(), 16, false);
assert_eq!(actual, Value::test_int(255));
}
@ -316,7 +346,7 @@ mod test {
fn communicates_parsing_error_given_an_invalid_integerlike_string() {
let integer_str = Value::test_string("36anra");
let actual = action(&integer_str, Span::test_data(), 10);
let actual = action(&integer_str, Span::test_data(), 10, false);
assert_eq!(actual.get_type(), Error)
}

View File

@ -2,8 +2,8 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
engine::{Command, EngineState, Stack},
Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
SyntaxShape, Value,
into_code, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature,
Span, SyntaxShape, Value,
};
// TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml)
@ -247,6 +247,15 @@ pub fn action(
val: input.into_string(", ", config),
span,
},
Value::Error { error } => Value::String {
val: {
match into_code(error) {
Some(code) => code,
None => "".to_string(),
}
},
span,
},
Value::Nothing { .. } => Value::String {
val: "".to_string(),
span,

View File

@ -28,7 +28,33 @@ impl Command for DefEnv {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nushell.html
=== EXTRA NOTE ===
All blocks are scoped, including variable definition and environment variable changes.
Because of this, the following doesn't work:
def-env cd_with_fallback [arg = ""] {
let fall_back_path = "/tmp"
if $arg != "" {
cd $arg
} else {
cd $fall_back_path
}
}
Instead, you have to use cd in the top level scope:
def-env cd_with_fallback [arg = ""] {
let fall_back_path = "/tmp"
let path = if $arg != "" {
$arg
} else {
$fall_back_path
}
cd $path
}"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -1,7 +1,9 @@
use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Value};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, Signature, SyntaxShape, Value,
};
#[derive(Clone)]
pub struct Do;
@ -23,6 +25,11 @@ impl Command for Do {
"ignore errors as the block runs",
Some('i'),
)
.switch(
"capture-errors",
"capture errors as the block runs and return it",
Some('c'),
)
.rest("rest", SyntaxShape::Any, "the parameter(s) for the block")
.category(Category::Core)
}
@ -37,6 +44,7 @@ impl Command for Do {
let block: CaptureBlock = call.req(engine_state, stack, 0)?;
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
let ignore_errors = call.has_flag("ignore-errors");
let capture_errors = call.has_flag("capture-errors");
let mut stack = stack.captures_to_stack(&block.captures);
let block = engine_state.get_block(block.block_id);
@ -85,7 +93,7 @@ impl Command for Do {
block,
input,
call.redirect_stdout,
ignore_errors,
ignore_errors || capture_errors,
);
if ignore_errors {
@ -93,6 +101,11 @@ impl Command for Do {
Ok(x) => Ok(x),
Err(_) => Ok(PipelineData::new(call.head)),
}
} else if capture_errors {
match result {
Ok(x) => Ok(x),
Err(err) => Ok((Value::Error { error: err }).into_pipeline_data()),
}
} else {
result
}

View File

@ -23,6 +23,10 @@ impl Command for Echo {
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
"Unlike `print`, this command returns an actual value that will be passed to the next command of the pipeline."
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -2,8 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Value,
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
};
#[derive(Clone)]
@ -16,7 +15,7 @@ impl Command for ErrorMake {
fn signature(&self) -> Signature {
Signature::build("error make")
.optional("error_struct", SyntaxShape::Record, "the error to create")
.required("error_struct", SyntaxShape::Record, "the error to create")
.category(Category::Core)
}
@ -33,43 +32,20 @@ impl Command for ErrorMake {
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let span = call.head;
let ctrlc = engine_state.ctrlc.clone();
let arg: Option<Value> = call.opt(engine_state, stack, 0)?;
let arg: Value = call.req(engine_state, stack, 0)?;
if let Some(arg) = arg {
Ok(make_error(&arg, span)
.map(|err| Value::Error { error: err })
.unwrap_or_else(|| Value::Error {
error: ShellError::GenericError(
"Creating error value not supported.".into(),
"unsupported error format".into(),
Some(span),
None,
Vec::new(),
),
})
.into_pipeline_data())
} else {
input.map(
move |value| {
make_error(&value, span)
.map(|err| Value::Error { error: err })
.unwrap_or_else(|| Value::Error {
error: ShellError::GenericError(
"Creating error value not supported.".into(),
"unsupported error format".into(),
Some(span),
None,
Vec::new(),
),
})
},
ctrlc,
Err(make_error(&arg, span).unwrap_or_else(|| {
ShellError::GenericError(
"Creating error value not supported.".into(),
"unsupported error format".into(),
Some(span),
None,
Vec::new(),
)
}
}))
}
fn examples(&self) -> Vec<Example> {

View File

@ -28,7 +28,33 @@ impl Command for ExportDefEnv {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nushell.html
=== EXTRA NOTE ===
All blocks are scoped, including variable definition and environment variable changes.
Because of this, the following doesn't work:
export def-env cd_with_fallback [arg = ""] {
let fall_back_path = "/tmp"
if $arg != "" {
cd $arg
} else {
cd $fall_back_path
}
}
Instead, you have to use cd in the top level scope:
export def-env cd_with_fallback [arg = ""] {
let fall_back_path = "/tmp"
let path = if $arg != "" {
$arg
} else {
$fall_back_path
}
cd $path
}"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -1,131 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, HistoryFileFormat, IntoInterruptiblePipelineData, PipelineData, ShellError,
Signature, Value,
};
use reedline::{
FileBackedHistory, History as ReedlineHistory, SearchDirection, SearchQuery,
SqliteBackedHistory,
};
#[derive(Clone)]
pub struct History;
impl Command for History {
fn name(&self) -> &str {
"history"
}
fn usage(&self) -> &str {
"Get the command history"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("history")
.switch("clear", "Clears out the history entries", Some('c'))
.category(Category::Core)
}
fn run(
&self,
engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
// todo for sqlite history this command should be an alias to `open ~/.config/nushell/history.sqlite3 | get history`
if let Some(config_path) = nu_path::config_dir() {
let clear = call.has_flag("clear");
let ctrlc = engine_state.ctrlc.clone();
let mut history_path = config_path;
history_path.push("nushell");
match engine_state.config.history_file_format {
HistoryFileFormat::Sqlite => {
history_path.push("history.sqlite3");
}
HistoryFileFormat::PlainText => {
history_path.push("history.txt");
}
}
if clear {
let _ = std::fs::remove_file(history_path);
// TODO: FIXME also clear the auxiliary files when using sqlite
Ok(PipelineData::new(head))
} else {
let history_reader: Option<Box<dyn ReedlineHistory>> =
match engine_state.config.history_file_format {
HistoryFileFormat::Sqlite => SqliteBackedHistory::with_file(history_path)
.map(|inner| {
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
boxed
})
.ok(),
HistoryFileFormat::PlainText => FileBackedHistory::with_file(
engine_state.config.max_history_size as usize,
history_path,
)
.map(|inner| {
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
boxed
})
.ok(),
};
let data = history_reader
.and_then(|h| {
h.search(SearchQuery::everything(SearchDirection::Forward))
.ok()
})
.map(move |entries| {
entries
.into_iter()
.enumerate()
.map(move |(idx, entry)| Value::Record {
cols: vec!["command".to_string(), "index".to_string()],
vals: vec![
Value::String {
val: entry.command_line,
span: head,
},
Value::Int {
val: idx as i64,
span: head,
},
],
span: head,
})
})
.ok_or(ShellError::FileNotFound(head))?
.into_pipeline_data(ctrlc);
Ok(data)
}
} else {
Err(ShellError::FileNotFound(head))
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
example: "history | length",
description: "Get current history length",
result: None,
},
Example {
example: "history | last 5",
description: "Show last 5 commands you have ran",
result: None,
},
Example {
example: "history | wrap cmd | where cmd =~ cargo",
description: "Search all the commands from history that contains 'cargo'",
result: None,
},
]
}
}

View File

@ -16,7 +16,6 @@ mod extern_;
mod for_;
mod help;
mod hide;
mod history;
mod if_;
mod ignore;
mod let_;
@ -24,7 +23,6 @@ mod metadata;
mod module;
pub(crate) mod overlay;
mod source;
mod tutor;
mod use_;
mod version;
@ -46,7 +44,6 @@ pub use extern_::Extern;
pub use for_::For;
pub use help::Help;
pub use hide::Hide;
pub use history::History;
pub use if_::If;
pub use ignore::Ignore;
pub use let_::Let;
@ -54,7 +51,6 @@ pub use metadata::Metadata;
pub use module::Module;
pub use overlay::*;
pub use source::Source;
pub use tutor::Tutor;
pub use use_::Use;
pub use version::Version;
#[cfg(feature = "plugin")]

View File

@ -1,26 +1,26 @@
use crate::{
database::values::dsl::{ExprDb, SelectDb},
SQLiteDatabase,
};
use crate::SQLiteDatabase;
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
use sqlparser::ast::{Ident, SelectItem, SetExpr, Statement, TableAlias, TableFactor};
use sqlparser::ast::{Ident, SetExpr, Statement, TableAlias, TableFactor};
#[derive(Clone)]
pub struct AliasExpr;
pub struct AliasDb;
impl Command for AliasExpr {
impl Command for AliasDb {
fn name(&self) -> &str {
"db as"
"as"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("alias", SyntaxShape::String, "alias name")
.input_type(Type::Custom("database".into()))
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
@ -29,11 +29,59 @@ impl Command for AliasExpr {
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Creates an alias for a column selection",
example: "db col name_a | db as new_a",
result: None,
}]
vec![
Example {
description: "Creates an alias for a selected table",
example: r#"open db.mysql
| into db
| select a
| from table_1
| as t1
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT a FROM table_1 AS t1".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
Example {
description: "Creates an alias for a derived table",
example: r#"open db.mysql
| into db
| select a
| from (
open db.mysql
| into db
| select a b
| from table_a
)
| as t1
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT a FROM (SELECT a, b FROM table_a) AS t1".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
]
}
fn search_terms(&self) -> Vec<&str> {
@ -48,52 +96,12 @@ impl Command for AliasExpr {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let alias: String = call.req(engine_state, stack, 0)?;
let value = input.into_value(call.head);
if let Ok(expr) = ExprDb::try_from_value(&value) {
alias_selection(expr.into_native().into(), alias, call)
} else if let Ok(select) = SelectDb::try_from_value(&value) {
alias_selection(select, alias, call)
} else if let Ok(db) = SQLiteDatabase::try_from_value(value.clone()) {
alias_db(db, alias, call)
} else {
Err(ShellError::CantConvert(
"expression or query".into(),
value.get_type().to_string(),
value.span()?,
None,
))
}
let db = SQLiteDatabase::try_from_pipeline(input, call.head)?;
alias_db(db, alias, call)
}
}
fn alias_selection(
select: SelectDb,
alias: String,
call: &Call,
) -> Result<PipelineData, ShellError> {
let select = match select.into_native() {
SelectItem::UnnamedExpr(expr) => SelectItem::ExprWithAlias {
expr,
alias: Ident {
value: alias,
quote_style: None,
},
},
SelectItem::ExprWithAlias { expr, .. } => SelectItem::ExprWithAlias {
expr,
alias: Ident {
value: alias,
quote_style: None,
},
},
select => select,
};
let select: SelectDb = select.into();
Ok(select.into_value(call.head).into_pipeline_data())
}
fn alias_db(
mut db: SQLiteDatabase,
new_alias: String,
@ -142,7 +150,7 @@ fn alias_db(
},
s => {
return Err(ShellError::GenericError(
"Connection doesnt define a query".into(),
"Connection doesn't define a query".into(),
format!("Expected a connection with query. Got {}", s),
Some(call.head),
None,
@ -152,3 +160,19 @@ fn alias_db(
},
}
}
#[cfg(test)]
mod test {
use super::super::{FromDb, ProjectionDb};
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![
Box::new(AliasDb {}),
Box::new(ProjectionDb {}),
Box::new(FromDb {}),
])
}
}

View File

@ -6,7 +6,7 @@ use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Value,
Type, Value,
};
use sqlparser::ast::{BinaryOperator, Expr, Query, Select, SetExpr, Statement};
@ -15,16 +15,18 @@ pub struct AndDb;
impl Command for AndDb {
fn name(&self) -> &str {
"db and"
"and"
}
fn usage(&self) -> &str {
"Includes an AND clause for a query or expression"
"Includes an AND clause for a query"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("where", SyntaxShape::Any, "Where expression on the table")
.input_type(Type::Custom("database".into()))
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
@ -35,23 +37,52 @@ impl Command for AndDb {
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "selects a column from a database with a where clause",
example: r#"db open db.mysql
| db select a
| db from table_1
| db where ((db col a) > 1)
| db and ((db col b) == 1)
| db describe"#,
result: None,
description: "Selects a column from a database with an AND clause",
example: r#"open db.mysql
| into db
| select a
| from table_1
| where ((field a) > 1)
| and ((field b) == 1)
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT a FROM table_1 WHERE a > 1 AND b = 1".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
Example {
description: "Creates a nested where clause",
example: r#"db open db.mysql
| db select a
| db from table_1
| db where ((db col a) > 1 | db and ((db col a) < 10))
| db describe"#,
result: None,
description: "Creates a AND clause combined with an expression AND",
example: r#"open db.mysql
| into db
| select a
| from table_1
| where ((field a) > 1 | and ((field a) < 10))
| and ((field b) == 1)
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT a FROM table_1 WHERE (a > 1 AND a < 10) AND b = 1".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
]
}
@ -66,51 +97,32 @@ impl Command for AndDb {
let value: Value = call.req(engine_state, stack, 0)?;
let expr = ExprDb::try_from_value(&value)?.into_native();
let value = input.into_value(call.head);
if let Ok(expression) = ExprDb::try_from_value(&value) {
let expression = Expr::BinaryOp {
left: Box::new(expression.into_native()),
op: BinaryOperator::And,
right: Box::new(expr),
};
let expression: ExprDb = Expr::Nested(Box::new(expression)).into();
Ok(expression.into_value(call.head).into_pipeline_data())
} else if let Ok(mut db) = SQLiteDatabase::try_from_value(value.clone()) {
match db.statement.as_mut() {
Some(statement) => match statement {
Statement::Query(query) => modify_query(query, expr, call.head)?,
s => {
return Err(ShellError::GenericError(
"Connection doesnt define a query".into(),
format!("Expected a connection with query. Got {}", s),
Some(call.head),
None,
Vec::new(),
))
}
},
None => {
let mut db = SQLiteDatabase::try_from_pipeline(input, call.head)?;
match db.statement.as_mut() {
Some(statement) => match statement {
Statement::Query(query) => modify_query(query, expr, call.head)?,
s => {
return Err(ShellError::GenericError(
"Connection without statement".into(),
"The connection needs a statement defined".into(),
"Connection doesn't define a query".into(),
format!("Expected a connection with query. Got {}", s),
Some(call.head),
None,
Vec::new(),
))
}
};
},
None => {
return Err(ShellError::GenericError(
"Connection without statement".into(),
"The connection needs a statement defined".into(),
Some(call.head),
None,
Vec::new(),
))
}
};
Ok(db.into_value(call.head).into_pipeline_data())
} else {
Err(ShellError::CantConvert(
"expression or query".into(),
value.get_type().to_string(),
value.span()?,
None,
))
}
Ok(db.into_value(call.head).into_pipeline_data())
}
}
@ -150,3 +162,23 @@ fn modify_select(select: &mut Box<Select>, expression: Expr, span: Span) -> Resu
select.as_mut().selection = Some(new_expression);
Ok(())
}
#[cfg(test)]
mod test {
use super::super::super::expressions::{AndExpr, FieldExpr};
use super::super::{FromDb, ProjectionDb, WhereDb};
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![
Box::new(AndDb {}),
Box::new(ProjectionDb {}),
Box::new(FromDb {}),
Box::new(WhereDb {}),
Box::new(FieldExpr {}),
Box::new(AndExpr {}),
])
}
}

View File

@ -1,51 +0,0 @@
use crate::database::values::dsl::ExprDb;
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
};
#[derive(Clone)]
pub struct ColExpr;
impl Command for ColExpr {
fn name(&self) -> &str {
"db col"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("name", SyntaxShape::String, "column name")
.category(Category::Custom("database".into()))
}
fn usage(&self) -> &str {
"Creates column expression for database"
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Creates a named column expression",
example: "db col name_1",
result: None,
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "column", "expression"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let value: Value = call.req(engine_state, stack, 0)?;
let expression = ExprDb::try_from_value(&value)?;
Ok(expression.into_value(call.head).into_pipeline_data())
}
}

View File

@ -1,7 +1,7 @@
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type,
};
use super::super::SQLiteDatabase;
@ -11,21 +11,24 @@ pub struct CollectDb;
impl Command for CollectDb {
fn name(&self) -> &str {
"db collect"
"collect"
}
fn signature(&self) -> Signature {
Signature::build(self.name()).category(Category::Custom("database".into()))
Signature::build(self.name())
.input_type(Type::Custom("database".into()))
.output_type(Type::Any)
.category(Category::Custom("database".into()))
}
fn usage(&self) -> &str {
"Query a database using SQL."
"Collects a query from a database database connection"
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Collect from a select query",
example: "open foo.db | db select a | db from table_1 | db collect",
example: "open foo.db | into db | select a | from table_1 | collect",
result: None,
}]
}

View File

@ -1,42 +0,0 @@
use nu_engine::get_full_help;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, IntoPipelineData, PipelineData, ShellError, Signature, Value,
};
#[derive(Clone)]
pub struct Database;
impl Command for Database {
fn name(&self) -> &str {
"db"
}
fn usage(&self) -> &str {
"Database commands"
}
fn signature(&self) -> Signature {
Signature::build(self.name()).category(Category::Custom("database".into()))
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(Value::String {
val: get_full_help(
&Database.signature(),
&Database.examples(),
engine_state,
stack,
),
span: call.head,
}
.into_pipeline_data())
}
}

View File

@ -1,7 +1,7 @@
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
};
use super::super::SQLiteDatabase;
@ -11,11 +11,14 @@ pub struct DescribeDb;
impl Command for DescribeDb {
fn name(&self) -> &str {
"db describe"
"describe"
}
fn signature(&self) -> Signature {
Signature::build(self.name()).category(Category::Custom("database".into()))
Signature::build(self.name())
.input_type(Type::Custom("database".into()))
.output_type(Type::Any)
.category(Category::Custom("database".into()))
}
fn usage(&self) -> &str {
@ -25,13 +28,26 @@ impl Command for DescribeDb {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Describe SQLite database constructed query",
example: "db open foo.db | db select table_1 | db describe",
result: None,
example: "open foo.db | into db | select col_1 | from table_1 | describe",
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "foo.db".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT col_1 FROM table_1".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "SQLite"]
vec!["database", "SQLite", "describe"]
}
fn run(
@ -45,3 +61,19 @@ impl Command for DescribeDb {
Ok(db.describe(call.head).into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use super::super::{FromDb, ProjectionDb};
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![
Box::new(DescribeDb {}),
Box::new(ProjectionDb {}),
Box::new(FromDb {}),
])
}
}

View File

@ -5,7 +5,8 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
use sqlparser::ast::{Ident, Query, Select, SetExpr, Statement, TableAlias, TableWithJoins};
@ -14,7 +15,7 @@ pub struct FromDb;
impl Command for FromDb {
fn name(&self) -> &str {
"db from"
"from"
}
fn usage(&self) -> &str {
@ -34,6 +35,8 @@ impl Command for FromDb {
"Alias for the selected table",
Some('a'),
)
.input_type(Type::Custom("database".into()))
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
@ -43,9 +46,22 @@ impl Command for FromDb {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Selects table from database",
example: "db open db.mysql | db from table_a",
result: None,
description: "Selects a table from database",
example: "open db.mysql | into db | from table_a | describe",
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT FROM table_a".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
}]
}
@ -180,3 +196,14 @@ fn create_table(
Ok(table)
}
#[cfg(test)]
mod test {
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![Box::new(FromDb {})])
}
}

View File

@ -1,85 +0,0 @@
use crate::database::values::dsl::ExprDb;
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
};
use sqlparser::ast::{Expr, Function, FunctionArg, FunctionArgExpr, Ident, ObjectName};
#[derive(Clone)]
pub struct FunctionExpr;
impl Command for FunctionExpr {
fn name(&self) -> &str {
"db fn"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("name", SyntaxShape::String, "function name")
.switch("distinct", "distict values", Some('d'))
.rest("arguments", SyntaxShape::Any, "function arguments")
.category(Category::Custom("database".into()))
}
fn usage(&self) -> &str {
"Creates function expression for a select operation"
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Creates a function expression",
example: "db fn count name_1",
result: None,
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "function", "expression"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let name: String = call.req(engine_state, stack, 0)?;
let vals: Vec<Value> = call.rest(engine_state, stack, 1)?;
let value = Value::List {
vals,
span: call.head,
};
let expressions = ExprDb::extract_exprs(value)?;
let name: Vec<Ident> = name
.split('.')
.map(|part| Ident {
value: part.to_string(),
quote_style: None,
})
.collect();
let name = ObjectName(name);
let args: Vec<FunctionArg> = expressions
.into_iter()
.map(|expr| {
let arg = FunctionArgExpr::Expr(expr);
FunctionArg::Unnamed(arg)
})
.collect();
let expression: ExprDb = Expr::Function(Function {
name,
args,
over: None,
distinct: call.has_flag("distinct"),
})
.into();
Ok(expression.into_value(call.head).into_pipeline_data())
}
}

View File

@ -5,7 +5,8 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
use sqlparser::ast::{SetExpr, Statement};
@ -14,7 +15,7 @@ pub struct GroupByDb;
impl Command for GroupByDb {
fn name(&self) -> &str {
"db group-by"
"group-by"
}
fn usage(&self) -> &str {
@ -28,6 +29,8 @@ impl Command for GroupByDb {
SyntaxShape::Any,
"Select expression(s) on the table",
)
.input_type(Type::Custom("database".into()))
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
@ -36,15 +39,54 @@ impl Command for GroupByDb {
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "orders query by a column",
example: r#"db open db.mysql
| db from table_a
| db select a
| db group-by a
| db describe"#,
result: None,
}]
vec![
Example {
description: "groups by column a and calculates the max",
example: r#"open db.mysql
| into db
| from table_a
| select (fn max a)
| group-by a
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT max(a) FROM table_a GROUP BY a".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
Example {
description: "groups by column column a and counts records",
example: r#"open db.mysql
| into db
| from table_a
| select (fn count *)
| group-by a
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT count(*) FROM table_a GROUP BY a".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
]
}
fn run(
@ -100,3 +142,24 @@ impl Command for GroupByDb {
Ok(db.into_value(call.head).into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use super::super::super::expressions::{FieldExpr, FunctionExpr, OrExpr};
use super::super::{FromDb, ProjectionDb, WhereDb};
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![
Box::new(GroupByDb {}),
Box::new(ProjectionDb {}),
Box::new(FunctionExpr {}),
Box::new(FromDb {}),
Box::new(WhereDb {}),
Box::new(FieldExpr {}),
Box::new(OrExpr {}),
])
}
}

View File

@ -4,7 +4,8 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
use sqlparser::ast::{
Ident, Join, JoinConstraint, JoinOperator, Select, SetExpr, Statement, TableAlias,
@ -15,7 +16,7 @@ pub struct JoinDb;
impl Command for JoinDb {
fn name(&self) -> &str {
"db join"
"join"
}
fn usage(&self) -> &str {
@ -40,6 +41,8 @@ impl Command for JoinDb {
.switch("right", "right outer join", Some('r'))
.switch("outer", "full outer join", Some('o'))
.switch("cross", "cross join", Some('c'))
.input_type(Type::Custom("database".into()))
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
@ -48,11 +51,61 @@ impl Command for JoinDb {
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "",
example: "",
result: None,
}]
vec![
Example {
description: "joins two tables on col_b",
example: r#"open db.mysql
| into db
| select col_a
| from table_1 --as t1
| join table_2 col_b --as t2
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT col_a FROM table_1 AS t1 JOIN table_2 AS t2 ON col_b"
.into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
Example {
description: "joins a table with a derived table using aliases",
example: r#"open db.mysql
| into db
| select col_a
| from table_1 --as t1
| join (
open db.mysql
| into db
| select col_c
| from table_2
) ((field t1.col_a) == (field t2.col_c)) --as t2 --right
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT col_a FROM table_1 AS t1 RIGHT JOIN (SELECT col_c FROM table_2) AS t2 ON t1.col_a = t2.col_c"
.into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
]
}
fn run(
@ -176,3 +229,23 @@ fn modify_from(
)),
}
}
#[cfg(test)]
mod test {
use super::super::super::expressions::{FieldExpr, OrExpr};
use super::super::{FromDb, ProjectionDb, WhereDb};
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![
Box::new(JoinDb {}),
Box::new(ProjectionDb {}),
Box::new(FromDb {}),
Box::new(WhereDb {}),
Box::new(FieldExpr {}),
Box::new(OrExpr {}),
])
}
}

View File

@ -4,7 +4,8 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
use sqlparser::ast::Statement;
@ -13,7 +14,7 @@ pub struct LimitDb;
impl Command for LimitDb {
fn name(&self) -> &str {
"db limit"
"limit"
}
fn usage(&self) -> &str {
@ -27,6 +28,8 @@ impl Command for LimitDb {
SyntaxShape::Int,
"Number of rows to extract for query",
)
.input_type(Type::Custom("database".into()))
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
@ -37,12 +40,26 @@ impl Command for LimitDb {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Limits selection from table",
example: r#"db open db.mysql
| db from table_a
| db select a
| db limit 10
| db describe"#,
result: None,
example: r#"open db.mysql
| into db
| from table_a
| select a
| limit 10
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT a FROM table_a LIMIT 10".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
}]
}
@ -84,3 +101,23 @@ impl Command for LimitDb {
Ok(db.into_value(call.head).into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use super::super::super::expressions::{FieldExpr, OrExpr};
use super::super::{FromDb, ProjectionDb, WhereDb};
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![
Box::new(LimitDb {}),
Box::new(ProjectionDb {}),
Box::new(FromDb {}),
Box::new(WhereDb {}),
Box::new(FieldExpr {}),
Box::new(OrExpr {}),
])
}
}

View File

@ -3,22 +3,19 @@ pub mod conversions;
mod alias;
mod and;
mod col;
mod collect;
mod command;
mod describe;
mod from;
mod function;
mod group_by;
mod join;
mod limit;
mod open;
mod or;
mod order_by;
mod over;
mod query;
mod schema;
mod select;
mod to_db;
mod where_;
// Temporal module to create Query objects
@ -27,27 +24,24 @@ use testing::TestingDb;
use nu_protocol::engine::StateWorkingSet;
use alias::AliasExpr;
use alias::AliasDb;
use and::AndDb;
use col::ColExpr;
use collect::CollectDb;
use command::Database;
use describe::DescribeDb;
use from::FromDb;
use function::FunctionExpr;
pub(crate) use describe::DescribeDb;
pub(crate) use from::FromDb;
use group_by::GroupByDb;
use join::JoinDb;
use limit::LimitDb;
use open::OpenDb;
use or::OrDb;
use order_by::OrderByDb;
use over::OverExpr;
use query::QueryDb;
use schema::SchemaDb;
use select::ProjectionDb;
pub(crate) use select::ProjectionDb;
pub(crate) use to_db::ToDataBase;
use where_::WhereDb;
pub fn add_database_decls(working_set: &mut StateWorkingSet) {
pub fn add_commands_decls(working_set: &mut StateWorkingSet) {
macro_rules! bind_command {
( $command:expr ) => {
working_set.add_decl(Box::new($command));
@ -59,21 +53,18 @@ pub fn add_database_decls(working_set: &mut StateWorkingSet) {
// Series commands
bind_command!(
AliasExpr,
ToDataBase,
AliasDb,
AndDb,
ColExpr,
CollectDb,
Database,
DescribeDb,
FromDb,
FunctionExpr,
GroupByDb,
JoinDb,
LimitDb,
OpenDb,
OrderByDb,
OrDb,
OverExpr,
QueryDb,
ProjectionDb,
SchemaDb,

View File

@ -4,6 +4,7 @@ use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
Type,
};
use std::path::PathBuf;
@ -12,12 +13,14 @@ pub struct OpenDb;
impl Command for OpenDb {
fn name(&self) -> &str {
"db open"
"open-db"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("query", SyntaxShape::Filepath, "SQLite file to be opened")
.input_type(Type::Any)
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
@ -31,8 +34,8 @@ impl Command for OpenDb {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Open a sqlite file",
example: r#"db open file.sqlite"#,
description: "Creates a connection to a sqlite database based on the file name",
example: r#"open-db file.sqlite"#,
result: None,
}]
}

View File

@ -6,7 +6,7 @@ use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Value,
Type, Value,
};
use sqlparser::ast::{BinaryOperator, Expr, Query, Select, SetExpr, Statement};
@ -15,16 +15,18 @@ pub struct OrDb;
impl Command for OrDb {
fn name(&self) -> &str {
"db or"
"or"
}
fn usage(&self) -> &str {
"Includes an OR clause for a query or expression"
"Includes an OR clause for a query"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("where", SyntaxShape::Any, "Where expression on the table")
.input_type(Type::Custom("database".into()))
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
@ -35,23 +37,52 @@ impl Command for OrDb {
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "selects a column from a database with a where clause",
example: r#"db open db.mysql
| db select a
| db from table_1
| db where ((db col a) > 1)
| db or ((db col b) == 1)
| db describe"#,
result: None,
description: "selects a column from a database with an OR clause",
example: r#"open db.mysql
| into db
| select a
| from table_1
| where ((field a) > 1)
| or ((field b) == 1)
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT a FROM table_1 WHERE a > 1 OR b = 1".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
Example {
description: "Creates a nested where clause",
example: r#"db open db.mysql
| db select a
| db from table_1
| db where ((db col a) > 1 | db or ((db col a) < 10))
| db describe"#,
result: None,
description: "Creates an OR clause in the column names and a column",
example: r#"open db.mysql
| into db
| select a
| from table_1
| where ((field a) > 1 | or ((field a) < 10))
| or ((field b) == 1)
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT a FROM table_1 WHERE (a > 1 OR a < 10) OR b = 1".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
]
}
@ -66,51 +97,32 @@ impl Command for OrDb {
let value: Value = call.req(engine_state, stack, 0)?;
let expr = ExprDb::try_from_value(&value)?.into_native();
let value = input.into_value(call.head);
if let Ok(expression) = ExprDb::try_from_value(&value) {
let expression = Expr::BinaryOp {
left: Box::new(expression.into_native()),
op: BinaryOperator::Or,
right: Box::new(expr),
};
let expression: ExprDb = Expr::Nested(Box::new(expression)).into();
Ok(expression.into_value(call.head).into_pipeline_data())
} else if let Ok(mut db) = SQLiteDatabase::try_from_value(value.clone()) {
match db.statement {
Some(ref mut statement) => match statement {
Statement::Query(query) => modify_query(query, expr, call.head)?,
s => {
return Err(ShellError::GenericError(
"Connection doesnt define a query".into(),
format!("Expected a connection with query. Got {}", s),
Some(call.head),
None,
Vec::new(),
))
}
},
None => {
let mut db = SQLiteDatabase::try_from_pipeline(input, call.head)?;
match db.statement {
Some(ref mut statement) => match statement {
Statement::Query(query) => modify_query(query, expr, call.head)?,
s => {
return Err(ShellError::GenericError(
"Connection without statement".into(),
"The connection needs a statement defined".into(),
"Connection doesnt define a query".into(),
format!("Expected a connection with query. Got {}", s),
Some(call.head),
None,
Vec::new(),
))
}
};
},
None => {
return Err(ShellError::GenericError(
"Connection without statement".into(),
"The connection needs a statement defined".into(),
Some(call.head),
None,
Vec::new(),
))
}
};
Ok(db.into_value(call.head).into_pipeline_data())
} else {
Err(ShellError::CantConvert(
"expression or query".into(),
value.get_type().to_string(),
value.span()?,
None,
))
}
Ok(db.into_value(call.head).into_pipeline_data())
}
}
@ -150,3 +162,23 @@ fn modify_select(select: &mut Box<Select>, expression: Expr, span: Span) -> Resu
select.as_mut().selection = Some(new_expression);
Ok(())
}
#[cfg(test)]
mod test {
use super::super::super::expressions::{FieldExpr, OrExpr};
use super::super::{FromDb, ProjectionDb, WhereDb};
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![
Box::new(OrDb {}),
Box::new(ProjectionDb {}),
Box::new(FromDb {}),
Box::new(WhereDb {}),
Box::new(FieldExpr {}),
Box::new(OrExpr {}),
])
}
}

View File

@ -5,7 +5,8 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
use sqlparser::ast::{Expr, OrderByExpr, Statement};
@ -14,7 +15,7 @@ pub struct OrderByDb;
impl Command for OrderByDb {
fn name(&self) -> &str {
"db order-by"
"order-by"
}
fn usage(&self) -> &str {
@ -30,23 +31,65 @@ impl Command for OrderByDb {
SyntaxShape::Any,
"Select expression(s) on the table",
)
.input_type(Type::Custom("database".into()))
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "select"]
vec!["database", "order-by"]
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "orders query by a column",
example: r#"db open db.mysql
| db from table_a
| db select a
| db order-by a
| db describe"#,
result: None,
}]
vec![
Example {
description: "orders query by a column",
example: r#"open db.mysql
| into db
| from table_a
| select a
| order-by a
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT a FROM table_a ORDER BY a".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
Example {
description: "orders query by column a ascending and by column b",
example: r#"open db.mysql
| into db
| from table_a
| select a
| order-by a --ascending
| order-by b
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT a FROM table_a ORDER BY a ASC, b".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
]
}
fn run(
@ -155,3 +198,23 @@ fn update_connection(
Ok(db.into_value(call.head).into_pipeline_data())
}
#[cfg(test)]
mod test {
use super::super::super::expressions::{FieldExpr, OrExpr};
use super::super::{FromDb, ProjectionDb, WhereDb};
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![
Box::new(OrderByDb {}),
Box::new(ProjectionDb {}),
Box::new(FromDb {}),
Box::new(WhereDb {}),
Box::new(FieldExpr {}),
Box::new(OrExpr {}),
])
}
}

View File

@ -1,80 +0,0 @@
use crate::database::values::dsl::ExprDb;
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
};
use sqlparser::ast::{Expr, WindowSpec};
#[derive(Clone)]
pub struct OverExpr;
impl Command for OverExpr {
fn name(&self) -> &str {
"db over"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.rest(
"partition-by",
SyntaxShape::Any,
"columns to partition the window function",
)
.category(Category::Custom("database".into()))
}
fn usage(&self) -> &str {
"Adds a partition to an expression function"
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Adds a partition to a function expresssion",
example: "db function avg col_a | db over col_b",
result: None,
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "column", "expression"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let vals: Vec<Value> = call.rest(engine_state, stack, 0)?;
let value = Value::List {
vals,
span: call.head,
};
let partitions = ExprDb::extract_exprs(value)?;
let mut expression = ExprDb::try_from_pipeline(input, call.head)?;
match expression.as_mut() {
Expr::Function(function) => {
function.over = Some(WindowSpec {
partition_by: partitions,
order_by: Vec::new(),
window_frame: None,
});
}
s => {
return Err(ShellError::GenericError(
"Expression doesnt define a function".into(),
format!("Expected an expression with a function. Got {}", s),
Some(call.head),
None,
Vec::new(),
))
}
};
Ok(expression.into_value(call.head).into_pipeline_data())
}
}

View File

@ -3,6 +3,7 @@ use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
Type,
};
use super::super::SQLiteDatabase;
@ -12,7 +13,7 @@ pub struct QueryDb;
impl Command for QueryDb {
fn name(&self) -> &str {
"db query"
"query"
}
fn signature(&self) -> Signature {
@ -22,6 +23,8 @@ impl Command for QueryDb {
SyntaxShape::String,
"SQL to execute against the database",
)
.input_type(Type::Custom("database".into()))
.output_type(Type::Any)
.category(Category::Custom("database".into()))
}
@ -31,8 +34,8 @@ impl Command for QueryDb {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Get 1 table out of a SQLite database",
example: r#"db open foo.db | db query "SELECT * FROM Bar""#,
description: "Execute a query statement using the database connection",
example: r#"open foo.db | into db | query "SELECT * FROM Bar""#,
result: None,
}]
}

View File

@ -3,7 +3,7 @@ use crate::database::values::definitions::{db::Db, db_row::DbRow, db_table::DbTa
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, Value,
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
};
use rusqlite::Connection;
#[derive(Clone)]
@ -11,21 +11,24 @@ pub struct SchemaDb;
impl Command for SchemaDb {
fn name(&self) -> &str {
"db schema"
"schema"
}
fn signature(&self) -> Signature {
Signature::build(self.name()).category(Category::Custom("database".into()))
Signature::build(self.name())
.input_type(Type::Custom("database".into()))
.output_type(Type::Any)
.category(Category::Custom("database".into()))
}
fn usage(&self) -> &str {
"Show database information, including its schema."
"Show sqlite database information, including its schema."
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Show the schema of a SQLite database",
example: r#"open foo.db | db schema"#,
example: r#"open foo.db | into db | schema"#,
result: None,
}]
}

View File

@ -4,7 +4,7 @@ use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Value,
Type, Value,
};
use sqlparser::ast::{Query, Select, SelectItem, SetExpr, Statement};
@ -13,7 +13,7 @@ pub struct ProjectionDb;
impl Command for ProjectionDb {
fn name(&self) -> &str {
"db select"
"select"
}
fn usage(&self) -> &str {
@ -27,6 +27,8 @@ impl Command for ProjectionDb {
SyntaxShape::Any,
"Select expression(s) on the table",
)
.input_type(Type::Custom("database".into()))
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
@ -38,13 +40,43 @@ impl Command for ProjectionDb {
vec![
Example {
description: "selects a column from a database",
example: "db open db.mysql | db select a | db describe",
result: None,
example: "open db.mysql | into db | select a | describe",
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT a".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
Example {
description: "selects columns from a database",
example: "db open db.mysql | db select a b c | db describe",
result: None,
description: "selects columns from a database using alias",
example: r#"open db.mysql
| into db
| select (field a | as new_a) b c
| from table_1
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT a AS new_a, b, c FROM table_1".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
]
}
@ -104,7 +136,7 @@ fn modify_statement(
Ok(statement)
}
s => Err(ShellError::GenericError(
"Connection doesnt define a statement".into(),
"Connection doesn't define a statement".into(),
format!("Expected a connection with query. Got {}", s),
Some(span),
None,
@ -129,3 +161,21 @@ fn create_select(projection: Vec<SelectItem>) -> Select {
having: None,
}
}
#[cfg(test)]
mod test {
use super::super::super::expressions::{AliasExpr, FieldExpr};
use super::super::FromDb;
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![
Box::new(ProjectionDb {}),
Box::new(FromDb {}),
Box::new(FieldExpr {}),
Box::new(AliasExpr {}),
])
}
}

View File

@ -13,7 +13,7 @@ pub struct TestingDb;
impl Command for TestingDb {
fn name(&self) -> &str {
"db testing"
"testing-db"
}
fn signature(&self) -> Signature {
@ -27,7 +27,7 @@ impl Command for TestingDb {
}
fn usage(&self) -> &str {
"Create query object"
"Temporal Command: Create query object"
}
fn examples(&self) -> Vec<Example> {

View File

@ -0,0 +1,50 @@
use super::super::SQLiteDatabase;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type,
};
#[derive(Clone)]
pub struct ToDataBase;
impl Command for ToDataBase {
fn name(&self) -> &str {
"into db"
}
fn usage(&self) -> &str {
"Converts into an open db connection"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.input_type(Type::Any)
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "into", "db"]
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Converts an open file into a db object",
example: "open db.mysql | into db",
result: None,
}]
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let db = SQLiteDatabase::try_from_pipeline(input, call.head)?;
Ok(db.into_value(call.head).into_pipeline_data())
}
}

View File

@ -5,7 +5,8 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
use sqlparser::ast::{Expr, Query, Select, SetExpr, Statement};
@ -14,7 +15,7 @@ pub struct WhereDb;
impl Command for WhereDb {
fn name(&self) -> &str {
"db where"
"where"
}
fn usage(&self) -> &str {
@ -24,6 +25,8 @@ impl Command for WhereDb {
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("where", SyntaxShape::Any, "Where expression on the table")
.input_type(Type::Custom("database".into()))
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
@ -34,12 +37,26 @@ impl Command for WhereDb {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "selects a column from a database with a where clause",
example: r#"db open db.mysql
| db select a
| db from table_1
| db where ((db col a) > 1)
| db describe"#,
result: None,
example: r#"open db.mysql
| into db
| select a
| from table_1
| where ((field a) > 1)
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT a FROM table_1 WHERE a > 1".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
}]
}
@ -111,3 +128,23 @@ fn create_select(expression: Expr) -> Select {
having: None,
}
}
#[cfg(test)]
mod test {
use super::super::super::expressions::{FieldExpr, OrExpr};
use super::super::{FromDb, ProjectionDb};
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![
Box::new(WhereDb {}),
Box::new(ProjectionDb {}),
Box::new(FromDb {}),
Box::new(WhereDb {}),
Box::new(FieldExpr {}),
Box::new(OrExpr {}),
])
}
}

View File

@ -0,0 +1,132 @@
use crate::database::values::dsl::{ExprDb, SelectDb};
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
use sqlparser::ast::{Ident, SelectItem};
#[derive(Clone)]
pub struct AliasExpr;
impl Command for AliasExpr {
fn name(&self) -> &str {
"as"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("alias", SyntaxShape::String, "alias name")
.input_type(Type::Custom("db-expression".into()))
.output_type(Type::Custom("db-expression".into()))
.category(Category::Custom("db-expression".into()))
}
fn usage(&self) -> &str {
"Creates an alias for a column selection"
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Creates an alias for a column selection",
example: "field name_a | as new_a | into nu",
result: Some(Value::Record {
cols: vec!["expression".into(), "alias".into()],
vals: vec![
Value::Record {
cols: vec!["value".into(), "quoted_style".into()],
vals: vec![
Value::String {
val: "name_a".into(),
span: Span::test_data(),
},
Value::String {
val: "None".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
},
Value::Record {
cols: vec!["value".into(), "quoted_style".into()],
vals: vec![
Value::String {
val: "new_a".into(),
span: Span::test_data(),
},
Value::String {
val: "None".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "alias", "column"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let alias: String = call.req(engine_state, stack, 0)?;
let value = input.into_value(call.head);
if let Ok(expr) = ExprDb::try_from_value(&value) {
alias_selection(expr.into_native().into(), alias, call)
} else {
let select = SelectDb::try_from_value(&value)?;
alias_selection(select, alias, call)
}
}
}
fn alias_selection(
select: SelectDb,
alias: String,
call: &Call,
) -> Result<PipelineData, ShellError> {
let select = match select.into_native() {
SelectItem::UnnamedExpr(expr) => SelectItem::ExprWithAlias {
expr,
alias: Ident {
value: alias,
quote_style: None,
},
},
SelectItem::ExprWithAlias { expr, .. } => SelectItem::ExprWithAlias {
expr,
alias: Ident {
value: alias,
quote_style: None,
},
},
select => select,
};
let select: SelectDb = select.into();
Ok(select.into_value(call.head).into_pipeline_data())
}
#[cfg(test)]
mod test {
use super::super::FieldExpr;
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![Box::new(AliasExpr {}), Box::new(FieldExpr {})])
}
}

View File

@ -0,0 +1,141 @@
use crate::database::values::dsl::ExprDb;
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
use sqlparser::ast::{BinaryOperator, Expr};
#[derive(Clone)]
pub struct AndExpr;
impl Command for AndExpr {
fn name(&self) -> &str {
"and"
}
fn usage(&self) -> &str {
"Includes an AND clause for an expression"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("and", SyntaxShape::Any, "AND expression")
.input_type(Type::Custom("db-expression".into()))
.output_type(Type::Custom("db-expression".into()))
.category(Category::Custom("db-expression".into()))
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "and", "expression"]
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Creates an AND expression",
example: r#"(field a) > 1 | and ((field a) < 10) | into nu"#,
result: Some(Value::Record {
cols: vec!["left".into(), "op".into(), "right".into()],
vals: vec![
Value::Record {
cols: vec!["left".into(), "op".into(), "right".into()],
vals: vec![
Value::Record {
cols: vec!["value".into(), "quoted_style".into()],
vals: vec![
Value::String {
val: "a".into(),
span: Span::test_data(),
},
Value::String {
val: "None".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
},
Value::String {
val: ">".into(),
span: Span::test_data(),
},
Value::String {
val: "1".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
},
Value::String {
val: "AND".into(),
span: Span::test_data(),
},
Value::Record {
cols: vec!["left".into(), "op".into(), "right".into()],
vals: vec![
Value::Record {
cols: vec!["value".into(), "quoted_style".into()],
vals: vec![
Value::String {
val: "a".into(),
span: Span::test_data(),
},
Value::String {
val: "None".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
},
Value::String {
val: "<".into(),
span: Span::test_data(),
},
Value::String {
val: "10".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
}]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let value: Value = call.req(engine_state, stack, 0)?;
let expr = ExprDb::try_from_value(&value)?.into_native();
let expression = ExprDb::try_from_pipeline(input, call.head)?;
let expression = Expr::BinaryOp {
left: Box::new(expression.into_native()),
op: BinaryOperator::And,
right: Box::new(expr),
};
let expression: ExprDb = Expr::Nested(Box::new(expression)).into();
Ok(expression.into_value(call.head).into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use super::super::FieldExpr;
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![Box::new(AndExpr {}), Box::new(FieldExpr {})])
}
}

View File

@ -0,0 +1,76 @@
use crate::database::values::dsl::{ExprDb, SelectDb};
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
};
#[derive(Clone)]
pub struct ExprAsNu;
impl Command for ExprAsNu {
fn name(&self) -> &str {
"into nu"
}
fn usage(&self) -> &str {
"Convert a db expression into a nu value for access and exploration"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.input_type(Type::Custom("db-expression".into()))
.output_type(Type::Any)
.category(Category::Custom("db-expression".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Convert a col expression into a nushell value",
example: "field name_1 | into nu",
result: Some(Value::Record {
cols: vec!["value".into(), "quoted_style".into()],
vals: vec![
Value::String {
val: "name_1".into(),
span: Span::test_data(),
},
Value::String {
val: "None".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
}]
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let value = input.into_value(call.head);
if let Ok(expr) = ExprDb::try_from_value(&value) {
Ok(expr.to_value(call.head).into_pipeline_data())
} else {
let select = SelectDb::try_from_value(&value)?;
Ok(select.to_value(call.head).into_pipeline_data())
}
}
}
#[cfg(test)]
mod test {
use super::super::FieldExpr;
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![Box::new(ExprAsNu {}), Box::new(FieldExpr {})])
}
}

View File

@ -0,0 +1,78 @@
use crate::database::values::dsl::ExprDb;
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
#[derive(Clone)]
pub struct FieldExpr;
impl Command for FieldExpr {
fn name(&self) -> &str {
"field"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("name", SyntaxShape::String, "column name")
.input_type(Type::Any)
.output_type(Type::Custom("db-expression".into()))
.category(Category::Custom("db-expression".into()))
}
fn usage(&self) -> &str {
"Creates column expression for database"
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "column", "expression"]
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Creates a named field expression",
example: "field name_1 | into nu",
result: Some(Value::Record {
cols: vec!["value".into(), "quoted_style".into()],
vals: vec![
Value::String {
val: "name_1".into(),
span: Span::test_data(),
},
Value::String {
val: "None".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
}]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let value: Value = call.req(engine_state, stack, 0)?;
let expression = ExprDb::try_from_value(&value)?;
Ok(expression.into_value(call.head).into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![Box::new(FieldExpr {})])
}
}

View File

@ -0,0 +1,157 @@
use crate::database::values::dsl::ExprDb;
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
use sqlparser::ast::{Expr, Function, FunctionArg, FunctionArgExpr, Ident, ObjectName};
#[derive(Clone)]
pub struct FunctionExpr;
impl Command for FunctionExpr {
fn name(&self) -> &str {
"fn"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("name", SyntaxShape::String, "function name")
.switch("distinct", "distict values", Some('d'))
.rest("arguments", SyntaxShape::Any, "function arguments")
.input_type(Type::Any)
.output_type(Type::Custom("db-expression".into()))
.category(Category::Custom("db-expression".into()))
}
fn usage(&self) -> &str {
"Creates function expression for a select operation"
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Creates a function expression",
example: "fn count name_1 | into nu",
result: Some(Value::Record {
cols: vec![
"name".into(),
"args".into(),
"over".into(),
"distinct".into(),
],
vals: vec![
Value::String {
val: "count".into(),
span: Span::test_data(),
},
Value::List {
vals: vec![Value::String {
val: "name_1".into(),
span: Span::test_data(),
}],
span: Span::test_data(),
},
Value::String {
val: "None".into(),
span: Span::test_data(),
},
Value::Bool {
val: false,
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
Example {
description: "orders query by a column",
example: r#"open db.mysql
| into db
| select (fn lead col_a)
| from table_a
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT lead(col_a) FROM table_a".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
]
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "function", "expression"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let name: String = call.req(engine_state, stack, 0)?;
let vals: Vec<Value> = call.rest(engine_state, stack, 1)?;
let value = Value::List {
vals,
span: call.head,
};
let expressions = ExprDb::extract_exprs(value)?;
let name: Vec<Ident> = name
.split('.')
.map(|part| Ident {
value: part.to_string(),
quote_style: None,
})
.collect();
let name = ObjectName(name);
let args: Vec<FunctionArg> = expressions
.into_iter()
.map(|expr| {
let arg = FunctionArgExpr::Expr(expr);
FunctionArg::Unnamed(arg)
})
.collect();
let expression: ExprDb = Expr::Function(Function {
name,
args,
over: None,
distinct: call.has_flag("distinct"),
})
.into();
Ok(expression.into_value(call.head).into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use super::super::super::commands::{FromDb, ProjectionDb};
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![
Box::new(FunctionExpr {}),
Box::new(ProjectionDb {}),
Box::new(FromDb {}),
])
}
}

View File

@ -0,0 +1,40 @@
// Conversions between value and sqlparser objects
mod alias;
mod and;
mod as_nu;
mod field;
mod function;
mod or;
mod over;
use nu_protocol::engine::StateWorkingSet;
pub(crate) use alias::AliasExpr;
pub(crate) use and::AndExpr;
pub(crate) use as_nu::ExprAsNu;
pub(crate) use field::FieldExpr;
pub(crate) use function::FunctionExpr;
pub(crate) use or::OrExpr;
pub(crate) use over::OverExpr;
pub fn add_expressions_decls(working_set: &mut StateWorkingSet) {
macro_rules! bind_command {
( $command:expr ) => {
working_set.add_decl(Box::new($command));
};
( $( $command:expr ),* ) => {
$( working_set.add_decl(Box::new($command)); )*
};
}
// Series commands
bind_command!(
ExprAsNu,
AliasExpr,
AndExpr,
FieldExpr,
FunctionExpr,
OrExpr,
OverExpr
);
}

View File

@ -0,0 +1,141 @@
use crate::database::values::dsl::ExprDb;
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
use sqlparser::ast::{BinaryOperator, Expr};
#[derive(Clone)]
pub struct OrExpr;
impl Command for OrExpr {
fn name(&self) -> &str {
"or"
}
fn usage(&self) -> &str {
"Includes an OR clause for an expression"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("or", SyntaxShape::Any, "OR expression")
.input_type(Type::Custom("db-expression".into()))
.output_type(Type::Custom("db-expression".into()))
.category(Category::Custom("db-expression".into()))
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "or", "expression"]
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Creates an AND expression",
example: r#"(field a) > 1 | or ((field a) < 10) | into nu"#,
result: Some(Value::Record {
cols: vec!["left".into(), "op".into(), "right".into()],
vals: vec![
Value::Record {
cols: vec!["left".into(), "op".into(), "right".into()],
vals: vec![
Value::Record {
cols: vec!["value".into(), "quoted_style".into()],
vals: vec![
Value::String {
val: "a".into(),
span: Span::test_data(),
},
Value::String {
val: "None".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
},
Value::String {
val: ">".into(),
span: Span::test_data(),
},
Value::String {
val: "1".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
},
Value::String {
val: "OR".into(),
span: Span::test_data(),
},
Value::Record {
cols: vec!["left".into(), "op".into(), "right".into()],
vals: vec![
Value::Record {
cols: vec!["value".into(), "quoted_style".into()],
vals: vec![
Value::String {
val: "a".into(),
span: Span::test_data(),
},
Value::String {
val: "None".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
},
Value::String {
val: "<".into(),
span: Span::test_data(),
},
Value::String {
val: "10".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
}]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let value: Value = call.req(engine_state, stack, 0)?;
let expr = ExprDb::try_from_value(&value)?.into_native();
let expression = ExprDb::try_from_pipeline(input, call.head)?;
let expression = Expr::BinaryOp {
left: Box::new(expression.into_native()),
op: BinaryOperator::Or,
right: Box::new(expr),
};
let expression: ExprDb = Expr::Nested(Box::new(expression)).into();
Ok(expression.into_value(call.head).into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use super::super::FieldExpr;
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![Box::new(OrExpr {}), Box::new(FieldExpr {})])
}
}

View File

@ -0,0 +1,153 @@
use crate::database::values::dsl::ExprDb;
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
};
use sqlparser::ast::{Expr, WindowSpec};
#[derive(Clone)]
pub struct OverExpr;
impl Command for OverExpr {
fn name(&self) -> &str {
"over"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.rest(
"partition-by",
SyntaxShape::Any,
"columns to partition the window function",
)
.input_type(Type::Custom("db-expression".into()))
.output_type(Type::Custom("db-expression".into()))
.category(Category::Custom("db-expression".into()))
}
fn usage(&self) -> &str {
"Adds a partition to an expression function"
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Adds a partition to a function expression",
example: "fn avg col_a | over col_b | into nu",
result: Some(Value::Record {
cols: vec![
"name".into(),
"args".into(),
"over".into(),
"distinct".into(),
],
vals: vec![
Value::String {
val: "avg".into(),
span: Span::test_data(),
},
Value::List {
vals: vec![Value::String {
val: "col_a".into(),
span: Span::test_data(),
}],
span: Span::test_data(),
},
Value::String {
val: "Some(WindowSpec { partition_by: [Identifier(Ident { value: \"col_b\", quote_style: None })], order_by: [], window_frame: None })".into(),
span: Span::test_data(),
},
Value::Bool {
val: false,
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
Example {
description: "orders query by a column",
example: r#"open db.mysql
| into db
| select (fn lead col_a | over col_b)
| from table_a
| describe"#,
result: Some(Value::Record {
cols: vec!["connection".into(), "query".into()],
vals: vec![
Value::String {
val: "db.mysql".into(),
span: Span::test_data(),
},
Value::String {
val: "SELECT lead(col_a) OVER (PARTITION BY col_b) FROM table_a".into(),
span: Span::test_data(),
},
],
span: Span::test_data(),
}),
},
]
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "over", "expression"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let vals: Vec<Value> = call.rest(engine_state, stack, 0)?;
let value = Value::List {
vals,
span: call.head,
};
let partitions = ExprDb::extract_exprs(value)?;
let mut expression = ExprDb::try_from_pipeline(input, call.head)?;
match expression.as_mut() {
Expr::Function(function) => {
function.over = Some(WindowSpec {
partition_by: partitions,
order_by: Vec::new(),
window_frame: None,
});
}
s => {
return Err(ShellError::GenericError(
"Expression doesnt define a function".into(),
format!("Expected an expression with a function. Got {}", s),
Some(call.head),
None,
Vec::new(),
))
}
};
Ok(expression.into_value(call.head).into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use super::super::super::commands::{FromDb, ProjectionDb};
use super::super::FunctionExpr;
use super::*;
use crate::database::test_database::test_database;
#[test]
fn test_examples() {
test_database(vec![
Box::new(OverExpr {}),
Box::new(FunctionExpr {}),
Box::new(ProjectionDb {}),
Box::new(FromDb {}),
])
}
}

View File

@ -1,8 +1,21 @@
mod commands;
mod expressions;
mod values;
pub use commands::add_database_decls;
use commands::add_commands_decls;
use expressions::add_expressions_decls;
pub use values::{
convert_sqlite_row_to_nu_value, convert_sqlite_value_to_nu_value, open_connection_in_memory,
SQLiteDatabase,
};
use nu_protocol::engine::StateWorkingSet;
pub fn add_database_decls(working_set: &mut StateWorkingSet) {
add_commands_decls(working_set);
add_expressions_decls(working_set);
}
#[cfg(test)]
mod test_database;

View File

@ -0,0 +1,138 @@
use std::path::Path;
use super::commands::{DescribeDb, ToDataBase};
use super::expressions::ExprAsNu;
use crate::SQLiteDatabase;
use nu_engine::{eval_block, CallExt};
use nu_parser::parse;
use nu_protocol::{
engine::{Command, EngineState, Stack, StateWorkingSet},
Category, IntoPipelineData, PipelineData, Signature, Span, Type,
};
#[derive(Clone)]
pub struct CustomOpen;
impl Command for CustomOpen {
fn name(&self) -> &str {
"open"
}
fn usage(&self) -> &str {
"Mock open file command"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build(self.name())
.required(
"filename",
nu_protocol::SyntaxShape::String,
"the filename to use",
)
.input_type(Type::Any)
.output_type(Type::Custom("database".into()))
.category(Category::Custom("database".into()))
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &nu_protocol::ast::Call,
_input: nu_protocol::PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let path: String = call.req(engine_state, stack, 0)?;
let path = Path::new(&path);
let db = SQLiteDatabase::new(path);
Ok(db.into_value(call.head).into_pipeline_data())
}
}
pub fn test_database(cmds: Vec<Box<dyn Command + 'static>>) {
if cmds.is_empty() {
panic!("Empty commands vector")
}
// The first element in the cmds vector must be the one tested
let examples = cmds[0].examples();
let mut engine_state = Box::new(EngineState::new());
let delta = {
// Base functions that are needed for testing
// Try to keep this working set small to keep tests running as fast as possible
let mut working_set = StateWorkingSet::new(&*engine_state);
working_set.add_decl(Box::new(DescribeDb {}));
working_set.add_decl(Box::new(ToDataBase {}));
working_set.add_decl(Box::new(CustomOpen {}));
working_set.add_decl(Box::new(ExprAsNu {}));
// Adding the command that is being tested to the working set
for cmd in cmds {
working_set.add_decl(cmd);
}
working_set.render()
};
let cwd = std::env::current_dir().expect("Could not get current working directory.");
let _ = engine_state.merge_delta(delta, None, &cwd);
for example in examples {
// Skip tests that don't have results to compare to
if example.result.is_none() {
continue;
}
let start = std::time::Instant::now();
let (block, delta) = {
let mut working_set = StateWorkingSet::new(&*engine_state);
let (output, err) = parse(
&mut working_set,
None,
example.example.as_bytes(),
false,
&[],
);
if let Some(err) = err {
panic!("test parse error in `{}`: {:?}", example.example, err)
}
(output, working_set.render())
};
let _ = engine_state.merge_delta(delta, None, &cwd);
let mut stack = Stack::new();
match eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(Span::test_data()),
true,
true,
) {
Err(err) => panic!("test eval error in `{}`: {:?}", example.example, err),
Ok(result) => {
let result = result.into_value(Span::test_data());
println!("input: {}", example.example);
println!("result: {:?}", result);
println!("done: {:?}", start.elapsed());
// Note. Value implements PartialEq for Bool, Int, Float, String and Block
// If the command you are testing requires to compare another case, then
// you need to define its equality in the Value struct
if let Some(expected) = example.result {
if result != expected {
panic!(
"the example result is different to expected value: {:?} != {:?}",
result, expected
)
}
}
}
}
}
}

View File

@ -118,6 +118,11 @@ impl CustomValue for ExprDb {
Operator::In
| Operator::NotIn
| Operator::Pow
| Operator::BitOr
| Operator::BitXor
| Operator::BitAnd
| Operator::ShiftLeft
| Operator::ShiftRight
| Operator::StartsWith
| Operator::EndsWith => Err(ShellError::UnsupportedOperator(operator, op)),
}?;

View File

@ -23,6 +23,8 @@ impl Command for AppendDF {
Signature::build(self.name())
.required("other", SyntaxShape::Any, "dataframe to be appended")
.switch("col", "appends in col orientation", Some('c'))
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
@ -30,7 +32,7 @@ impl Command for AppendDF {
vec![
Example {
description: "Appends a dataframe as new columns",
example: r#"let a = ([[a b]; [1 2] [3 4]] | to-df);
example: r#"let a = ([[a b]; [1 2] [3 4]] | into df);
$a | append $a"#,
result: Some(
NuDataFrame::try_from_columns(vec![
@ -57,7 +59,7 @@ impl Command for AppendDF {
},
Example {
description: "Appends a dataframe merging at the end of columns",
example: r#"let a = ([[a b]; [1 2] [3 4]] | to-df);
example: r#"let a = ([[a b]; [1 2] [3 4]] | into df);
$a | append $a --col"#,
result: Some(
NuDataFrame::try_from_columns(vec![
@ -87,14 +89,6 @@ impl Command for AppendDF {
]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -29,6 +29,8 @@ impl Command for DescribeDF {
fn signature(&self) -> Signature {
Signature::build(self.name())
.category(Category::Custom("dataframe".into()))
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.named(
"quantiles",
SyntaxShape::Table,
@ -40,7 +42,7 @@ impl Command for DescribeDF {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "dataframe description",
example: "[[a b]; [1 1] [1 1]] | to-df | describe",
example: "[[a b]; [1 1] [1 1]] | into df | describe",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -95,14 +97,6 @@ impl Command for DescribeDF {
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -23,13 +23,15 @@ impl Command for DropDF {
fn signature(&self) -> Signature {
Signature::build(self.name())
.rest("rest", SyntaxShape::Any, "column names to be dropped")
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "drop column a",
example: "[[a b]; [1 2] [3 4]] | to-df | drop a",
example: "[[a b]; [1 2] [3 4]] | into df | drop a",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"b".to_string(),
@ -41,14 +43,6 @@ impl Command for DropDF {
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -34,13 +34,15 @@ impl Command for DropDuplicates {
"keeps last duplicate value (by default keeps first)",
Some('l'),
)
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "drop duplicates",
example: "[[a b]; [1 2] [3 4] [1 2]] | to-df | drop-duplicates",
example: "[[a b]; [1 2] [3 4] [1 2]] | into df | drop-duplicates",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -58,14 +60,6 @@ impl Command for DropDuplicates {
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -27,6 +27,8 @@ impl Command for DropNulls {
SyntaxShape::Table,
"subset of columns to drop nulls",
)
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
@ -34,7 +36,7 @@ impl Command for DropNulls {
vec![
Example {
description: "drop null values in dataframe",
example: r#"let df = ([[a b]; [1 2] [3 0] [1 2]] | to-df);
example: r#"let df = ([[a b]; [1 2] [3 0] [1 2]] | into df);
let res = ($df.b / $df.b);
let a = ($df | with-column $res --name res);
$a | drop-nulls"#,
@ -59,7 +61,7 @@ impl Command for DropNulls {
},
Example {
description: "drop null values in dataframe",
example: r#"let s = ([1 2 0 0 3 4] | to-df);
example: r#"let s = ([1 2 0 0 3 4] | into df);
($s / $s) | drop-nulls"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
@ -78,14 +80,6 @@ impl Command for DropNulls {
]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -18,13 +18,16 @@ impl Command for DataTypes {
}
fn signature(&self) -> Signature {
Signature::build(self.name()).category(Category::Custom("dataframe".into()))
Signature::build(self.name())
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Dataframe dtypes",
example: "[[a b]; [1 2] [3 4]] | to-df | dtypes",
example: "[[a b]; [1 2] [3 4]] | into df | dtypes",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -42,14 +45,6 @@ impl Command for DataTypes {
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -11,7 +11,7 @@ pub struct Dummies;
impl Command for Dummies {
fn name(&self) -> &str {
"to-dummies"
"dummies"
}
fn usage(&self) -> &str {
@ -19,14 +19,17 @@ impl Command for Dummies {
}
fn signature(&self) -> Signature {
Signature::build(self.name()).category(Category::Custom("dataframe".into()))
Signature::build(self.name())
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Create new dataframe with dummy variables from a dataframe",
example: "[[a b]; [1 2] [3 4]] | to-df | to-dummies",
example: "[[a b]; [1 2] [3 4]] | into df | dummies",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -52,7 +55,7 @@ impl Command for Dummies {
},
Example {
description: "Create new dataframe with dummy variables from a series",
example: "[1 2 2 3 3] | to-df | to-dummies",
example: "[1 2 2 3 3] | into df | dummies",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -93,14 +96,6 @@ impl Command for Dummies {
]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -29,6 +29,8 @@ impl Command for FilterWith {
SyntaxShape::Any,
"boolean mask used to filter data",
)
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe or lazyframe".into()))
}
@ -36,8 +38,8 @@ impl Command for FilterWith {
vec![
Example {
description: "Filter dataframe using a bool mask",
example: r#"let mask = ([true false] | to-df);
[[a b]; [1 2] [3 4]] | to-df | filter-with $mask"#,
example: r#"let mask = ([true false] | into df);
[[a b]; [1 2] [3 4]] | into df | filter-with $mask"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(1)]),
@ -49,7 +51,7 @@ impl Command for FilterWith {
},
Example {
description: "Filter dataframe using an expression",
example: "[[a b]; [1 2] [3 4]] | to-df | filter-with ((col a) > 1)",
example: "[[a b]; [1 2] [3 4]] | into df | filter-with ((col a) > 1)",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(3)]),
@ -62,14 +64,6 @@ impl Command for FilterWith {
]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -21,13 +21,15 @@ impl Command for FirstDF {
fn signature(&self) -> Signature {
Signature::build(self.name())
.optional("rows", SyntaxShape::Int, "Number of rows for head")
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Create new dataframe with head rows",
example: "[[a b]; [1 2] [3 4]] | to-df | first 1",
example: "[[a b]; [1 2] [3 4]] | into df | first 1",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(1)]),
@ -39,14 +41,6 @@ impl Command for FirstDF {
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -24,13 +24,15 @@ impl Command for GetDF {
fn signature(&self) -> Signature {
Signature::build(self.name())
.rest("rest", SyntaxShape::Any, "column names to sort dataframe")
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Returns the selected column",
example: "[[a b]; [1 2] [3 4]] | to-df | get a",
example: "[[a b]; [1 2] [3 4]] | into df | get a",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"a".to_string(),
@ -42,14 +44,6 @@ impl Command for GetDF {
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -21,13 +21,15 @@ impl Command for LastDF {
fn signature(&self) -> Signature {
Signature::build(self.name())
.optional("rows", SyntaxShape::Int, "Number of rows for tail")
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Create new dataframe with last rows",
example: "[[a b]; [1 2] [3 4]] | to-df | last 1",
example: "[[a b]; [1 2] [3 4]] | into df | last 1",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(3)]),
@ -39,14 +41,6 @@ impl Command for LastDF {
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -25,7 +25,7 @@ impl Command for ListDF {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Creates a new dataframe and shows it in the dataframe list",
example: r#"let test = ([[a b];[1 2] [3 4]] | to-df);
example: r#"let test = ([[a b];[1 2] [3 4]] | into df);
ls-df"#,
result: None,
}]

View File

@ -48,13 +48,16 @@ impl Command for MeltDF {
"optional name for value column",
Some('l'),
)
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "melt dataframe",
example: "[[a b c d]; [x 1 4 a] [y 2 5 b] [z 3 6 c]] | to-df | melt -c [b c] -v [a d]",
example:
"[[a b c d]; [x 1 4 a] [y 2 5 b] [z 3 6 c]] | into df | melt -c [b c] -v [a d]",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -108,14 +111,6 @@ impl Command for MeltDF {
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -58,6 +58,8 @@ impl Command for OpenDataFrame {
"Columns to be selected from csv file. CSV and Parquet file",
None,
)
.input_type(Type::Any)
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
@ -69,14 +71,6 @@ impl Command for OpenDataFrame {
}]
}
fn input_type(&self) -> Type {
Type::Any
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,
@ -155,7 +149,7 @@ fn from_json(
call: &Call,
) -> Result<polars::prelude::DataFrame, ShellError> {
let file: Spanned<PathBuf> = call.req(engine_state, stack, 0)?;
let mut file = File::open(&file.item).map_err(|e| {
let file = File::open(&file.item).map_err(|e| {
ShellError::GenericError(
"Error opening file".into(),
e.to_string(),
@ -165,7 +159,7 @@ fn from_json(
)
})?;
let buf_reader = BufReader::new(&mut file);
let buf_reader = BufReader::new(file);
let reader = JsonReader::new(buf_reader);
reader.finish().map_err(|e| {

View File

@ -33,6 +33,8 @@ impl Command for RenameDF {
SyntaxShape::Any,
"New names for the selected column(s). A string or list of strings",
)
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe or lazyframe".into()))
}
@ -40,7 +42,7 @@ impl Command for RenameDF {
vec![
Example {
description: "Renames a series",
example: "[5 6 7 8] | to-df | rename '0' new_name",
example: "[5 6 7 8] | into df | rename '0' new_name",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"new_name".to_string(),
@ -57,7 +59,7 @@ impl Command for RenameDF {
},
Example {
description: "Renames a dataframe column",
example: "[[a b]; [1 2] [3 4]] | to-df | rename a a_new",
example: "[[a b]; [1 2] [3 4]] | into df | rename a a_new",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -75,7 +77,7 @@ impl Command for RenameDF {
},
Example {
description: "Renames two dataframe columns",
example: "[[a b]; [1 2] [3 4]] | to-df | rename [a b] [a_new b_new]",
example: "[[a b]; [1 2] [3 4]] | into df | rename [a b] [a_new b_new]",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -94,14 +96,6 @@ impl Command for RenameDF {
]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -40,6 +40,9 @@ impl Command for SampleDF {
Some('s'),
)
.switch("replace", "sample with replace", Some('e'))
.switch("shuffle", "shuffle sample", Some('u'))
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
@ -47,25 +50,17 @@ impl Command for SampleDF {
vec![
Example {
description: "Sample rows from dataframe",
example: "[[a b]; [1 2] [3 4]] | to-df | sample -n 1",
example: "[[a b]; [1 2] [3 4]] | into df | 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]] | to-df | sample -f 0.5 -e",
example: "[[a b]; [1 2] [3 4] [5 6]] | into df | sample -f 0.5 -e",
result: None, // No expected value because sampling is random
},
]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,
@ -89,22 +84,26 @@ fn command(
.get_flag::<i64>(engine_state, stack, "seed")?
.map(|val| val as u64);
let replace: bool = call.has_flag("replace");
let shuffle: bool = call.has_flag("shuffle");
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
match (rows, fraction) {
(Some(rows), None) => df.as_ref().sample_n(rows.item, replace, seed).map_err(|e| {
ShellError::GenericError(
"Error creating sample".into(),
e.to_string(),
Some(rows.span),
None,
Vec::new(),
)
}),
(Some(rows), None) => df
.as_ref()
.sample_n(rows.item, replace, shuffle, seed)
.map_err(|e| {
ShellError::GenericError(
"Error creating sample".into(),
e.to_string(),
Some(rows.span),
None,
Vec::new(),
)
}),
(None, Some(frac)) => df
.as_ref()
.sample_frac(frac.item, replace, seed)
.sample_frac(frac.item, replace, shuffle, seed)
.map_err(|e| {
ShellError::GenericError(
"Error creating sample".into(),

View File

@ -21,13 +21,16 @@ impl Command for ShapeDF {
}
fn signature(&self) -> Signature {
Signature::build(self.name()).category(Category::Custom("dataframe".into()))
Signature::build(self.name())
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Shows row and column shape",
example: "[[a b]; [1 2] [3 4]] | to-df | shape",
example: "[[a b]; [1 2] [3 4]] | into df | shape",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("rows".to_string(), vec![Value::test_int(2)]),
@ -39,14 +42,6 @@ impl Command for ShapeDF {
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -25,13 +25,15 @@ impl Command for SliceDF {
Signature::build(self.name())
.required("offset", SyntaxShape::Int, "start of slice")
.required("size", SyntaxShape::Int, "size of slice")
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Create new dataframe from a slice of the rows",
example: "[[a b]; [1 2] [3 4]] | to-df | slice 0 1",
example: "[[a b]; [1 2] [3 4]] | into df | slice 0 1",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new("a".to_string(), vec![Value::test_int(1)]),
@ -43,14 +45,6 @@ impl Command for SliceDF {
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -29,6 +29,8 @@ impl Command for TakeDF {
SyntaxShape::Any,
"list of indices used to take data",
)
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
@ -36,8 +38,8 @@ impl Command for TakeDF {
vec![
Example {
description: "Takes selected rows from dataframe",
example: r#"let df = ([[a b]; [4 1] [5 2] [4 3]] | to-df);
let indices = ([0 2] | to-df);
example: r#"let df = ([[a b]; [4 1] [5 2] [4 3]] | into df);
let indices = ([0 2] | into df);
$df | take $indices"#,
result: Some(
NuDataFrame::try_from_columns(vec![
@ -56,8 +58,8 @@ impl Command for TakeDF {
},
Example {
description: "Takes selected rows from series",
example: r#"let series = ([4 1 5 2 4 3] | to-df);
let indices = ([0 2] | to-df);
example: r#"let series = ([4 1 5 2 4 3] | into df);
let indices = ([0 2] | into df);
$series | take $indices"#,
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
@ -71,14 +73,6 @@ impl Command for TakeDF {
]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -15,7 +15,7 @@ pub struct ToCSV;
impl Command for ToCSV {
fn name(&self) -> &str {
"to-csv"
"to csv"
}
fn usage(&self) -> &str {
@ -32,6 +32,8 @@ impl Command for ToCSV {
Some('d'),
)
.switch("no-header", "Indicates if file doesn't have header", None)
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Any)
.category(Category::Custom("dataframe".into()))
}
@ -39,25 +41,17 @@ impl Command for ToCSV {
vec![
Example {
description: "Saves dataframe to csv file",
example: "[[a b]; [1 2] [3 4]] | to-df | to-csv test.csv",
example: "[[a b]; [1 2] [3 4]] | into df | to csv test.csv",
result: None,
},
Example {
description: "Saves dataframe to csv file using other delimiter",
example: "[[a b]; [1 2] [3 4]] | to-df | to-csv test.csv -d '|'",
example: "[[a b]; [1 2] [3 4]] | into df | to csv test.csv -d '|'",
result: None,
},
]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Any
}
fn run(
&self,
engine_state: &EngineState,
@ -99,7 +93,7 @@ fn command(
writer.has_header(true)
};
let writer = match delimiter {
let mut writer = match delimiter {
None => writer,
Some(d) => {
if d.item.len() != 1 {

View File

@ -11,7 +11,7 @@ pub struct ToDataFrame;
impl Command for ToDataFrame {
fn name(&self) -> &str {
"to-df"
"into df"
}
fn usage(&self) -> &str {
@ -19,14 +19,17 @@ impl Command for ToDataFrame {
}
fn signature(&self) -> Signature {
Signature::build(self.name()).category(Category::Custom("dataframe".into()))
Signature::build(self.name())
.input_type(Type::Any)
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Takes a dictionary and creates a dataframe",
example: "[[a b];[1 2] [3 4]] | to-df",
example: "[[a b];[1 2] [3 4]] | into df",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -44,7 +47,7 @@ impl Command for ToDataFrame {
},
Example {
description: "Takes a list of tables and creates a dataframe",
example: "[[1 2 a] [3 4 b] [5 6 c]] | to-df",
example: "[[1 2 a] [3 4 b] [5 6 c]] | into df",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -70,7 +73,7 @@ impl Command for ToDataFrame {
},
Example {
description: "Takes a list and creates a dataframe",
example: "[a b c] | to-df",
example: "[a b c] | into df",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
@ -86,7 +89,7 @@ impl Command for ToDataFrame {
},
Example {
description: "Takes a list of booleans and creates a dataframe",
example: "[true true false] | to-df",
example: "[true true false] | into df",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
@ -103,14 +106,6 @@ impl Command for ToDataFrame {
]
}
fn input_type(&self) -> Type {
Type::Any
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
_engine_state: &EngineState,

View File

@ -2,7 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
use super::super::values::NuDataFrame;
@ -12,11 +12,11 @@ pub struct ToNu;
impl Command for ToNu {
fn name(&self) -> &str {
"to-nu"
"into nu"
}
fn usage(&self) -> &str {
"Converts a section of the dataframe to Nushell Table"
"Converts a section of the dataframe into nushell Table"
}
fn signature(&self) -> Signature {
@ -28,32 +28,49 @@ impl Command for ToNu {
Some('n'),
)
.switch("tail", "shows tail rows", Some('t'))
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Any)
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
let cols = vec!["index".into(), "a".into(), "b".into()];
let rec_1 = Value::Record {
cols: cols.clone(),
vals: vec![Value::test_int(0), Value::test_int(1), Value::test_int(2)],
span: Span::test_data(),
};
let rec_2 = Value::Record {
cols: cols.clone(),
vals: vec![Value::test_int(1), Value::test_int(3), Value::test_int(4)],
span: Span::test_data(),
};
let rec_3 = Value::Record {
cols,
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(4)],
span: Span::test_data(),
};
vec![
Example {
description: "Shows head rows from dataframe",
example: "[[a b]; [1 2] [3 4]] | to-df | to nu",
result: None,
example: "[[a b]; [1 2] [3 4]] | into df | into nu",
result: Some(Value::List {
vals: vec![rec_1, rec_2],
span: Span::test_data(),
}),
},
Example {
description: "Shows tail rows from dataframe",
example: "[[a b]; [1 2] [3 4] [5 6]] | to-df | to nu -t -n 1",
result: None,
example: "[[a b]; [1 2] [5 6] [3 4]] | into df | into nu -t -n 1",
result: Some(Value::List {
vals: vec![rec_3],
span: Span::test_data(),
}),
},
]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Any
}
fn run(
&self,
engine_state: &EngineState,
@ -71,7 +88,7 @@ fn command(
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let rows: Option<usize> = call.get_flag(engine_state, stack, "n-rows")?;
let rows: Option<usize> = call.get_flag(engine_state, stack, "rows")?;
let tail: bool = call.has_flag("tail");
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
@ -94,3 +111,14 @@ fn command(
Ok(PipelineData::Value(value, None))
}
#[cfg(test)]
mod test {
use super::super::super::test_dataframe::test_dataframe;
use super::*;
#[test]
fn test_examples() {
test_dataframe(vec![Box::new(ToNu {})])
}
}

View File

@ -15,7 +15,7 @@ pub struct ToParquet;
impl Command for ToParquet {
fn name(&self) -> &str {
"to-parquet"
"to parquet"
}
fn usage(&self) -> &str {
@ -25,25 +25,19 @@ impl Command for ToParquet {
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("file", SyntaxShape::Filepath, "file path to save dataframe")
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Any)
.category(Category::Custom("dataframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Saves dataframe to parquet file",
example: "[[a b]; [1 2] [3 4]] | to-df | to-parquet test.parquet",
example: "[[a b]; [1 2] [3 4]] | into df | to parquet test.parquet",
result: None,
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Any
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -27,6 +27,8 @@ impl Command for WithColumn {
SyntaxShape::Any,
"series to be added or expressions used to define the new columns",
)
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("dataframe or lazyframe".into()))
}
@ -35,8 +37,8 @@ impl Command for WithColumn {
Example {
description: "Adds a series to the dataframe",
example: r#"[[a b]; [1 2] [3 4]]
| to-df
| with-column ([5 6] | to-df) --name c"#,
| into df
| with-column ([5 6] | into df) --name c"#,
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -59,7 +61,7 @@ impl Command for WithColumn {
Example {
description: "Adds a series to the dataframe",
example: r#"[[a b]; [1 2] [3 4]]
| to-lazy
| into lazy
| with-column [
((col a) * 2 | as "c")
((col a) * 3 | as "d")
@ -91,14 +93,6 @@ impl Command for WithColumn {
]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -26,13 +26,15 @@ impl Command for ExprAlias {
SyntaxShape::String,
"Alias name for the expression",
)
.input_type(Type::Custom("expression".into()))
.output_type(Type::Custom("expression".into()))
.category(Category::Custom("expression".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Creates and alias expression",
example: "col a | as new_a | to-nu",
example: "col a | as new_a | into nu",
result: {
let cols = vec!["expr".into(), "value".into()];
let expr = Value::test_string("column");
@ -57,14 +59,6 @@ impl Command for ExprAlias {
}]
}
fn input_type(&self) -> Type {
Type::Custom("expression".into())
}
fn output_type(&self) -> Type {
Type::Custom("expression".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -11,21 +11,24 @@ pub struct ExprAsNu;
impl Command for ExprAsNu {
fn name(&self) -> &str {
"to-nu"
"into nu"
}
fn usage(&self) -> &str {
"Convert expression to a nu value for access and exploration"
"Convert expression into a nu value for access and exploration"
}
fn signature(&self) -> Signature {
Signature::build(self.name()).category(Category::Custom("expression".into()))
Signature::build(self.name())
.input_type(Type::Custom("expression".into()))
.output_type(Type::Any)
.category(Category::Custom("expression".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Convert a col expression into a nushell value",
example: "col a | to-nu",
example: "col a | into nu",
result: Some(Value::Record {
cols: vec!["expr".into(), "value".into()],
vals: vec![
@ -43,14 +46,6 @@ impl Command for ExprAsNu {
}]
}
fn input_type(&self) -> Type {
Type::Custom("expression".into())
}
fn output_type(&self) -> Type {
Type::Any
}
fn run(
&self,
_engine_state: &EngineState,

View File

@ -26,13 +26,15 @@ impl Command for ExprCol {
SyntaxShape::String,
"Name of column to be used",
)
.input_type(Type::Any)
.output_type(Type::Custom("expression".into()))
.category(Category::Custom("expression".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Creates a named column expression and converts it to a nu object",
example: "col a | to-nu",
example: "col a | into nu",
result: Some(Value::Record {
cols: vec!["expr".into(), "value".into()],
vals: vec![
@ -50,14 +52,6 @@ impl Command for ExprCol {
}]
}
fn input_type(&self) -> Type {
Type::Any
}
fn output_type(&self) -> Type {
Type::Custom("expression".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -25,21 +25,16 @@ macro_rules! expr_command {
}
fn signature(&self) -> Signature {
Signature::build(self.name()).category(Category::Custom("expression".into()))
Signature::build(self.name())
.input_type(Type::Custom("expression".into()))
.output_type(Type::Custom("expression".into()))
.category(Category::Custom("expression".into()))
}
fn examples(&self) -> Vec<Example> {
$examples
}
fn input_type(&self) -> Type {
Type::Custom("expression".into())
}
fn output_type(&self) -> Type {
Type::Custom("expression".into())
}
fn run(
&self,
_engine_state: &EngineState,
@ -250,7 +245,7 @@ expr_command!(
vec![Example {
description: "Max aggregation for a group by",
example: r#"[[a b]; [one 2] [one 4] [two 1]]
| to-df
| into df
| group-by a
| agg (col b | max)"#,
result: Some(
@ -281,7 +276,7 @@ expr_command!(
vec![Example {
description: "Min aggregation for a group by",
example: r#"[[a b]; [one 2] [one 4] [two 1]]
| to-df
| into df
| group-by a
| agg (col b | min)"#,
result: Some(
@ -312,7 +307,7 @@ expr_command!(
vec![Example {
description: "Sum aggregation for a group by",
example: r#"[[a b]; [one 2] [one 4] [two 1]]
| to-df
| into df
| group-by a
| agg (col b | sum)"#,
result: Some(
@ -343,7 +338,7 @@ expr_command!(
vec![Example {
description: "Mean aggregation for a group by",
example: r#"[[a b]; [one 2] [one 4] [two 1]]
| to-df
| into df
| group-by a
| agg (col b | mean)"#,
result: Some(
@ -374,7 +369,7 @@ expr_command!(
vec![Example {
description: "Median aggregation for a group by",
example: r#"[[a b]; [one 2] [one 4] [two 1]]
| to-df
| into df
| group-by a
| agg (col b | median)"#,
result: Some(
@ -405,7 +400,7 @@ expr_command!(
vec![Example {
description: "Std aggregation for a group by",
example: r#"[[a b]; [one 2] [one 2] [two 1] [two 1]]
| to-df
| into df
| group-by a
| agg (col b | std)"#,
result: Some(
@ -436,7 +431,7 @@ expr_command!(
vec![Example {
description: "Var aggregation for a group by",
example: r#"[[a b]; [one 2] [one 2] [two 1] [two 1]]
| to-df
| into df
| group-by a
| agg (col b | var)"#,
result: Some(

View File

@ -25,13 +25,15 @@ impl Command for ExprLit {
SyntaxShape::Any,
"literal to construct the expression",
)
.input_type(Type::Any)
.output_type(Type::Custom("expression".into()))
.category(Category::Custom("expression".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Created a literal expression and converts it to a nu object",
example: "lit 2 | to-nu",
example: "lit 2 | into nu",
result: Some(Value::Record {
cols: vec!["expr".into(), "value".into()],
vals: vec![
@ -49,14 +51,6 @@ impl Command for ExprLit {
}]
}
fn input_type(&self) -> Type {
Type::Any
}
fn output_type(&self) -> Type {
Type::Custom("expression".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -25,6 +25,8 @@ impl Command for ExprOtherwise {
SyntaxShape::Any,
"expressioini to apply when no when predicate matches",
)
.input_type(Type::Any)
.output_type(Type::Custom("expression".into()))
.category(Category::Custom("expression".into()))
}
@ -43,7 +45,7 @@ impl Command for ExprOtherwise {
Example {
description: "Create a new column for the dataframe",
example: r#"[[a b]; [6 2] [1 4] [4 1]]
| to-lazy
| into lazy
| with-column (
when ((col a) > 2) 4 | otherwise 5 | as c
)
@ -77,14 +79,6 @@ impl Command for ExprOtherwise {
]
}
fn input_type(&self) -> Type {
Type::Any
}
fn output_type(&self) -> Type {
Type::Custom("expression".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -26,6 +26,8 @@ impl Command for ExprQuantile {
SyntaxShape::Number,
"quantile value for quantile operation",
)
.input_type(Type::Custom("expression".into()))
.output_type(Type::Custom("expression".into()))
.category(Category::Custom("expression".into()))
}
@ -33,7 +35,7 @@ impl Command for ExprQuantile {
vec![Example {
description: "Quantile aggregation for a group by",
example: r#"[[a b]; [one 2] [one 4] [two 1]]
| to-df
| into df
| group-by a
| agg (col b | quantile 0.5)"#,
result: Some(
@ -53,14 +55,6 @@ impl Command for ExprQuantile {
}]
}
fn input_type(&self) -> Type {
Type::Custom("expression".into())
}
fn output_type(&self) -> Type {
Type::Custom("expression".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -31,6 +31,8 @@ impl Command for ExprWhen {
SyntaxShape::Any,
"expression that will be applied when predicate is true",
)
.input_type(Type::Custom("expression".into()))
.output_type(Type::Custom("expression".into()))
.category(Category::Custom("expression".into()))
}
@ -49,7 +51,7 @@ impl Command for ExprWhen {
Example {
description: "Create a new column for the dataframe",
example: r#"[[a b]; [6 2] [1 4] [4 1]]
| to-lazy
| into lazy
| with-column (
when ((col a) > 2) 4 | otherwise 5 | as c
)
@ -83,14 +85,6 @@ impl Command for ExprWhen {
]
}
fn input_type(&self) -> Type {
Type::Custom("expression".into())
}
fn output_type(&self) -> Type {
Type::Custom("expression".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -26,6 +26,8 @@ impl Command for LazyAggregate {
SyntaxShape::Any,
"Expression(s) that define the aggregations to be applied",
)
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("lazyframe".into()))
}
@ -34,7 +36,7 @@ impl Command for LazyAggregate {
Example {
description: "Group by and perform an aggregation",
example: r#"[[a b]; [1 2] [1 4] [2 6] [2 4]]
| to-df
| into df
| group-by a
| agg [
(col b | min | as "b_min")
@ -67,7 +69,7 @@ impl Command for LazyAggregate {
Example {
description: "Group by and perform an aggregation",
example: r#"[[a b]; [1 2] [1 4] [2 6] [2 4]]
| to-lazy
| into lazy
| group-by a
| agg [
(col b | min | as "b_min")
@ -101,14 +103,6 @@ impl Command for LazyAggregate {
]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -20,13 +20,16 @@ impl Command for LazyCollect {
}
fn signature(&self) -> Signature {
Signature::build(self.name()).category(Category::Custom("lazyframe".into()))
Signature::build(self.name())
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("lazyframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "drop duplicates",
example: "[[a b]; [1 2] [3 4]] | to-lazy | collect",
example: "[[a b]; [1 2] [3 4]] | into lazy | collect",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -44,14 +47,6 @@ impl Command for LazyCollect {
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
_engine_state: &EngineState,

View File

@ -26,13 +26,15 @@ impl Command for LazyFetch {
SyntaxShape::Int,
"number of rows to be fetched from lazyframe",
)
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("lazyframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Fetch a rows from the dataframe",
example: "[[a b]; [6 2] [4 2] [2 2]] | to-df | fetch 2",
example: "[[a b]; [6 2] [4 2] [2 2]] | into df | fetch 2",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
@ -50,14 +52,6 @@ impl Command for LazyFetch {
}]
}
fn input_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn output_type(&self) -> Type {
Type::Custom("dataframe".into())
}
fn run(
&self,
engine_state: &EngineState,

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