forked from extern/nushell
Compare commits
134 Commits
Author | SHA1 | Date | |
---|---|---|---|
e1ebd461d2 | |||
f000d5d0a1 | |||
574c5961c8 | |||
69708f7244 | |||
62c5df5fc6 | |||
92c855a412 | |||
d395816929 | |||
5e34ef6dff | |||
d567c58cc1 | |||
4e0d7bc77c | |||
32581497ef | |||
d6df367c6b | |||
4e6327de1d | |||
b3d8666db0 | |||
1de7c3d033 | |||
962b258cc6 | |||
59697cab63 | |||
349af05da8 | |||
b3b3cf0689 | |||
5d59234f8d | |||
4f7b423f36 | |||
f7043bf690 | |||
1297499d7a | |||
bd0baa961c | |||
4ee536f044 | |||
8581bec891 | |||
8bcbc8eeb3 | |||
c164ef5489 | |||
cc3653cfd9 | |||
7fc65067cf | |||
f9ae882012 | |||
1d80a68f4c | |||
fda69354db | |||
cc5c4d38bb | |||
0fa0c25fb3 | |||
55eafadf02 | |||
51c74eebd0 | |||
ae9f4135c0 | |||
4e2d3ceaaf | |||
c9c6bd4836 | |||
d90420ac4c | |||
260ff99710 | |||
08014c6a98 | |||
66cedf0b3a | |||
707a4ebc15 | |||
d95375d494 | |||
1c1c58e802 | |||
7fe05b8296 | |||
17ef531905 | |||
b8e2bdd6b1 | |||
88817a8f10 | |||
3e8ce43dcb | |||
9d8845d7ad | |||
52578ba483 | |||
991a4801b1 | |||
02b2c55146 | |||
0abe753003 | |||
487fafbca3 | |||
188a352c6f | |||
e11b400a75 | |||
6db5692be4 | |||
ead4029d49 | |||
9bd408449e | |||
2b7390c2a1 | |||
ab961a78cb | |||
65c639cf13 | |||
0cf5dc11e3 | |||
b873fa7a5f | |||
ee563ecf4e | |||
183b35d683 | |||
463dd48180 | |||
1bd3fdd912 | |||
de71cbdd43 | |||
c9b87c4c03 | |||
38848082ae | |||
b6728efcd4 | |||
cd814851da | |||
6646daab45 | |||
ba483155d7 | |||
63abe1cb3e | |||
28db8022fe | |||
7dcc08985c | |||
55acdaaf8c | |||
575c07c9c4 | |||
325f45fa66 | |||
bc682066d8 | |||
0a1cdc5107 | |||
6984185e61 | |||
5826126284 | |||
762e528ec5 | |||
c3de9848b4 | |||
370ae8c20c | |||
69083bfca0 | |||
1e15f26e98 | |||
23ba01d89c | |||
653cbe651f | |||
f3e487e829 | |||
9c016ad479 | |||
e602647d4d | |||
9696e4d315 | |||
7f7af2bbaa | |||
b190051e15 | |||
83b28cad8d | |||
ea42a84a4a | |||
e4c282f0a6 | |||
d54d7cc431 | |||
111477aa74 | |||
226739d13f | |||
f1ee9113ac | |||
9120a64cfb | |||
fcd624a722 | |||
e6af7f75a1 | |||
27f1e7b60c | |||
2e24de7f47 | |||
e514204db0 | |||
0f9e55dac6 | |||
5d7677dd07 | |||
57073cc6cf | |||
f9f39c0a1c | |||
d88d7f26e4 | |||
aeaedd2e5e | |||
1f4ef3b606 | |||
9b5db297a6 | |||
b7215b5dde | |||
411435d68f | |||
f656f906ff | |||
d0a7363e64 | |||
7401fa2fa5 | |||
181ee1dade | |||
3645a0f0e4 | |||
2864eaebae | |||
37612345f2 | |||
bb218b824e | |||
279329bfaa |
@ -49,7 +49,7 @@ steps:
|
||||
- bash: RUSTFLAGS="-D warnings" cargo test --all
|
||||
condition: eq(variables['style'], 'unflagged')
|
||||
displayName: Run tests
|
||||
- bash: RUSTFLAGS="-D warnings" cargo clippy --all -- -D clippy::unwrap_used
|
||||
- bash: RUSTFLAGS="-D warnings" cargo clippy --all -- -D clippy::unwrap_used -A clippy::needless_collect
|
||||
condition: eq(variables['style'], 'unflagged')
|
||||
displayName: Check clippy lints
|
||||
- bash: cd samples/wasm && npm install wasm-pack && node ./node_modules/wasm-pack/run.js build
|
||||
|
47
.github/ISSUE_TEMPLATE/bug_report.md
vendored
47
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,47 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Configuration (please complete the following information):**
|
||||
|
||||
Run `version | pivot` and paste the output to show OS, features, etc.
|
||||
|
||||
```
|
||||
> version | pivot
|
||||
╭───┬────────────────────┬───────────────────────────────────────────────────────────────────────╮
|
||||
│ # │ Column0 │ Column1 │
|
||||
├───┼────────────────────┼───────────────────────────────────────────────────────────────────────┤
|
||||
│ 0 │ version │ 0.24.1 │
|
||||
│ 1 │ build_os │ macos-x86_64 │
|
||||
│ 2 │ rust_version │ rustc 1.48.0 │
|
||||
│ 3 │ cargo_version │ cargo 1.48.0 │
|
||||
│ 4 │ pkg_version │ 0.24.1 │
|
||||
│ 5 │ build_time │ 2020-12-18 09:54:09 │
|
||||
│ 6 │ build_rust_channel │ release │
|
||||
│ 7 │ features │ ctrlc, default, directories, dirs, git, ichwh, rich-benchmark, │
|
||||
│ │ │ rustyline, term, uuid, which, zip │
|
||||
╰───┴────────────────────┴───────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
|
||||
**Add any other context about the problem here.**
|
65
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
65
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
name: Bug Report
|
||||
description: Create a report to help us improve
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: How to reproduce
|
||||
description: Steps to reproduce the behavior
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
placeholder: I expected nu to...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: Please add any relevant screenshots here, if any
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: config
|
||||
attributes:
|
||||
label: Configuration
|
||||
description: "Please run `> version | pivot key value | to md` and paste the output to show OS, features, etc"
|
||||
placeholder: |
|
||||
> version | pivot key value | to md
|
||||
╭───┬────────────────────┬───────────────────────────────────────────────────────────────────────╮
|
||||
│ # │ key │ value │
|
||||
├───┼────────────────────┼───────────────────────────────────────────────────────────────────────┤
|
||||
│ 0 │ version │ 0.24.1 │
|
||||
│ 1 │ build_os │ macos-x86_64 │
|
||||
│ 2 │ rust_version │ rustc 1.48.0 │
|
||||
│ 3 │ cargo_version │ cargo 1.48.0 │
|
||||
│ 4 │ pkg_version │ 0.24.1 │
|
||||
│ 5 │ build_time │ 2020-12-18 09:54:09 │
|
||||
│ 6 │ build_rust_channel │ release │
|
||||
│ 7 │ features │ ctrlc, default, directories, dirs, git, ichwh, rich-benchmark, │
|
||||
│ │ │ rustyline, term, uuid, which, zip │
|
||||
╰───┴────────────────────┴───────────────────────────────────────────────────────────────────────╯
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context about the problem here.
|
||||
validations:
|
||||
required: false
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
34
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
34
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
name: Feature Request
|
||||
description: "When you want a new feature for something that doesn't already exist"
|
||||
body:
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Related problem
|
||||
description: Is your feature request related to a problem? Please describe.
|
||||
placeholder: |
|
||||
A clear and concise description of what the problem is.
|
||||
Example: I am trying to do [...] but [...]
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: desired
|
||||
attributes:
|
||||
label: "Describe the solution you'd like"
|
||||
description: A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: "Describe alternatives you've considered"
|
||||
description: "A clear and concise description of any alternative solutions or features you've considered."
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional context and details
|
||||
description: Add any other context or screenshots about the feature request here.
|
||||
validations:
|
||||
required: false
|
19
.github/workflows/release.yml
vendored
19
.github/workflows/release.yml
vendored
@ -162,8 +162,6 @@ jobs:
|
||||
- linux
|
||||
- macos
|
||||
- windows
|
||||
outputs:
|
||||
version: ${{ steps.info.outputs.version }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
@ -285,19 +283,4 @@ jobs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./nushell-windows.msi
|
||||
asset_name: ${{ steps.info.outputs.windowsdir }}.msi
|
||||
asset_content_type: applictaion/x-msi
|
||||
|
||||
winget:
|
||||
name: Publish winget package
|
||||
runs-on: windows-latest
|
||||
needs: release
|
||||
steps:
|
||||
- name: Submit package to Windows Package Manager Community Repository
|
||||
run: |
|
||||
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||
$nushellVersion="${{ needs.release.outputs.version }}"
|
||||
$nushellInstallerName="nu_$($nushellVersion -replace '\.','_')_windows.msi"
|
||||
$installerUrl = "https://github.com/nushell/nushell/releases/download/$nushellVersion/$nushellInstallerName"
|
||||
.\wingetcreate.exe update Nushell.Nushell -v $nushellVersion -u $installerUrl -t ${{ secrets.NUSHELL_PAT }}
|
||||
|
||||
|
||||
asset_content_type: application/x-msi
|
||||
|
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@ -19,11 +19,11 @@ jobs:
|
||||
operations-per-run: 520
|
||||
enable-statistics: true
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue is being marked stale because it has been open for 30 days without activity. If you feel that this is in error, please comment below and we will keep it marked as active.'
|
||||
stale-issue-message: 'This issue is being marked stale because it has been open for 90 days without activity. If you feel that this is in error, please comment below and we will keep it marked as active.'
|
||||
stale-pr-message: 'This PR is being marked stale because it has been open for 45 days without activity. If this PR is still active, please comment below and we will keep it marked as active.'
|
||||
close-issue-message: 'This issue has been marked stale for more than 10 days without activity. Closing this issue, but if you find that the issue is still valid, please reopen.'
|
||||
close-pr-message: 'This PR has been marked stale for more than 10 days without activity. Closing this PR, but if you are still working on it, please reopen.'
|
||||
days-before-issue-stale: 30
|
||||
days-before-issue-stale: 90
|
||||
days-before-pr-stale: 45
|
||||
days-before-issue-close: 10
|
||||
days-before-pr-close: 10
|
||||
|
18
.github/workflows/winget-submission.yml
vendored
Normal file
18
.github/workflows/winget-submission.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
name: Submit Nushell package to Windows Package Manager Community Repository
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
|
||||
winget:
|
||||
name: Publish winget package
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Submit package to Windows Package Manager Community Repository
|
||||
run: |
|
||||
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||
$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json
|
||||
$installerUrl = $github.release.assets | Where-Object -Property name -match 'windows.msi' | Select -ExpandProperty browser_download_url -First 1
|
||||
.\wingetcreate.exe update Nushell.Nushell -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.NUSHELL_PAT }}
|
2
.gitpod.Dockerfile
vendored
2
.gitpod.Dockerfile
vendored
@ -2,7 +2,7 @@ FROM gitpod/workspace-full
|
||||
|
||||
# Gitpod will not rebuild Nushell's dev image unless *some* change is made to this Dockerfile.
|
||||
# To force a rebuild, simply increase this counter:
|
||||
ENV TRIGGER_REBUILD 1
|
||||
ENV TRIGGER_REBUILD 2
|
||||
|
||||
USER gitpod
|
||||
|
||||
|
2837
Cargo.lock
generated
2837
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
102
Cargo.toml
102
Cargo.toml
@ -10,7 +10,7 @@ license = "MIT"
|
||||
name = "nu"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
version = "0.34.0"
|
||||
version = "0.38.0"
|
||||
|
||||
[workspace]
|
||||
members = ["crates/*/"]
|
||||
@ -18,38 +18,34 @@ members = ["crates/*/"]
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nu-cli = { version = "0.34.0", path="./crates/nu-cli", default-features=false }
|
||||
nu-command = { version = "0.34.0", path="./crates/nu-command" }
|
||||
nu-completion = { version = "0.34.0", path="./crates/nu-completion" }
|
||||
nu-data = { version = "0.34.0", path="./crates/nu-data" }
|
||||
nu-engine = { version = "0.34.0", path="./crates/nu-engine" }
|
||||
nu-errors = { version = "0.34.0", path="./crates/nu-errors" }
|
||||
nu-parser = { version = "0.34.0", path="./crates/nu-parser" }
|
||||
nu-path = { version = "0.34.0", path="./crates/nu-path" }
|
||||
nu-plugin = { version = "0.34.0", path="./crates/nu-plugin" }
|
||||
nu-protocol = { version = "0.34.0", path="./crates/nu-protocol" }
|
||||
nu-source = { version = "0.34.0", path="./crates/nu-source" }
|
||||
nu-value-ext = { version = "0.34.0", path="./crates/nu-value-ext" }
|
||||
nu-cli = { version = "0.38.0", path="./crates/nu-cli", default-features=false }
|
||||
nu-command = { version = "0.38.0", path="./crates/nu-command" }
|
||||
nu-completion = { version = "0.38.0", path="./crates/nu-completion" }
|
||||
nu-data = { version = "0.38.0", path="./crates/nu-data" }
|
||||
nu-engine = { version = "0.38.0", path="./crates/nu-engine" }
|
||||
nu-errors = { version = "0.38.0", path="./crates/nu-errors" }
|
||||
nu-parser = { version = "0.38.0", path="./crates/nu-parser" }
|
||||
nu-path = { version = "0.38.0", path="./crates/nu-path" }
|
||||
nu-plugin = { version = "0.38.0", path="./crates/nu-plugin" }
|
||||
nu-protocol = { version = "0.38.0", path="./crates/nu-protocol" }
|
||||
nu-source = { version = "0.38.0", path="./crates/nu-source" }
|
||||
nu-value-ext = { version = "0.38.0", path="./crates/nu-value-ext" }
|
||||
|
||||
nu_plugin_binaryview = { version = "0.34.0", path="./crates/nu_plugin_binaryview", optional=true }
|
||||
nu_plugin_chart = { version = "0.34.0", path="./crates/nu_plugin_chart", optional=true }
|
||||
nu_plugin_fetch = { version = "0.34.0", path="./crates/nu_plugin_fetch", optional=true }
|
||||
nu_plugin_from_bson = { version = "0.34.0", path="./crates/nu_plugin_from_bson", optional=true }
|
||||
nu_plugin_from_sqlite = { version = "0.34.0", path="./crates/nu_plugin_from_sqlite", optional=true }
|
||||
nu_plugin_inc = { version = "0.34.0", path="./crates/nu_plugin_inc", optional=true }
|
||||
nu_plugin_match = { version = "0.34.0", path="./crates/nu_plugin_match", optional=true }
|
||||
nu_plugin_post = { version = "0.34.0", path="./crates/nu_plugin_post", optional=true }
|
||||
nu_plugin_ps = { version = "0.34.0", path="./crates/nu_plugin_ps", optional=true }
|
||||
nu_plugin_query_json = { version = "0.34.0", path="./crates/nu_plugin_query_json", optional=true }
|
||||
nu_plugin_s3 = { version = "0.34.0", path="./crates/nu_plugin_s3", optional=true }
|
||||
nu_plugin_selector = { version = "0.34.0", path="./crates/nu_plugin_selector", optional=true }
|
||||
nu_plugin_start = { version = "0.34.0", path="./crates/nu_plugin_start", optional=true }
|
||||
nu_plugin_sys = { version = "0.34.0", path="./crates/nu_plugin_sys", optional=true }
|
||||
nu_plugin_textview = { version = "0.34.0", path="./crates/nu_plugin_textview", optional=true }
|
||||
nu_plugin_to_bson = { version = "0.34.0", path="./crates/nu_plugin_to_bson", optional=true }
|
||||
nu_plugin_to_sqlite = { version = "0.34.0", path="./crates/nu_plugin_to_sqlite", optional=true }
|
||||
nu_plugin_tree = { version = "0.34.0", path="./crates/nu_plugin_tree", optional=true }
|
||||
nu_plugin_xpath = { version = "0.34.0", path="./crates/nu_plugin_xpath", optional=true }
|
||||
nu_plugin_binaryview = { version = "0.38.0", path="./crates/nu_plugin_binaryview", optional=true }
|
||||
nu_plugin_chart = { version = "0.38.0", path="./crates/nu_plugin_chart", optional=true }
|
||||
nu_plugin_from_bson = { version = "0.38.0", path="./crates/nu_plugin_from_bson", optional=true }
|
||||
nu_plugin_from_sqlite = { version = "0.38.0", path="./crates/nu_plugin_from_sqlite", optional=true }
|
||||
nu_plugin_inc = { version = "0.38.0", path="./crates/nu_plugin_inc", optional=true }
|
||||
nu_plugin_match = { version = "0.38.0", path="./crates/nu_plugin_match", optional=true }
|
||||
nu_plugin_query_json = { version = "0.38.0", path="./crates/nu_plugin_query_json", optional=true }
|
||||
nu_plugin_s3 = { version = "0.38.0", path="./crates/nu_plugin_s3", optional=true }
|
||||
nu_plugin_selector = { version = "0.38.0", path="./crates/nu_plugin_selector", optional=true }
|
||||
nu_plugin_start = { version = "0.38.0", path="./crates/nu_plugin_start", optional=true }
|
||||
nu_plugin_textview = { version = "0.38.0", path="./crates/nu_plugin_textview", optional=true }
|
||||
nu_plugin_to_bson = { version = "0.38.0", path="./crates/nu_plugin_to_bson", optional=true }
|
||||
nu_plugin_to_sqlite = { version = "0.38.0", path="./crates/nu_plugin_to_sqlite", optional=true }
|
||||
nu_plugin_tree = { version = "0.38.0", path="./crates/nu_plugin_tree", optional=true }
|
||||
nu_plugin_xpath = { version = "0.38.0", path="./crates/nu_plugin_xpath", optional=true }
|
||||
|
||||
# Required to bootstrap the main binary
|
||||
ctrlc = { version="3.1.7", optional=true }
|
||||
@ -57,8 +53,7 @@ futures = { version="0.3.12", features=["compat", "io-compat"] }
|
||||
itertools = "0.10.0"
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { version = "0.34.0", path="./crates/nu-test-support" }
|
||||
dunce = "1.0.1"
|
||||
nu-test-support = { version = "0.38.0", path="./crates/nu-test-support" }
|
||||
serial_test = "0.5.1"
|
||||
hamcrest2 = "0.3.0"
|
||||
rstest = "0.10.0"
|
||||
@ -66,6 +61,8 @@ rstest = "0.10.0"
|
||||
[build-dependencies]
|
||||
|
||||
[features]
|
||||
fetch-support = ["nu-command/fetch", "nu-command/post"]
|
||||
sys-support = ["nu-command/sys", "nu-command/ps"]
|
||||
ctrlc-support = ["nu-cli/ctrlc", "nu-command/ctrlc"]
|
||||
rustyline-support = ["nu-cli/rustyline-support", "nu-command/rustyline-support"]
|
||||
term-support = ["nu-command/term"]
|
||||
@ -74,15 +71,13 @@ which-support = ["nu-command/which", "nu-engine/which"]
|
||||
|
||||
default = [
|
||||
"nu-cli/shadow-rs",
|
||||
"sys",
|
||||
"ps",
|
||||
"sys-support",
|
||||
"ctrlc-support",
|
||||
"which-support",
|
||||
"term-support",
|
||||
"rustyline-support",
|
||||
"match",
|
||||
"post",
|
||||
"fetch",
|
||||
"fetch-support",
|
||||
"zip-support",
|
||||
"dataframe",
|
||||
]
|
||||
@ -110,12 +105,8 @@ extra = [
|
||||
wasi = ["inc", "match", "match", "tree", "rustyline-support"]
|
||||
|
||||
# Stable (Default)
|
||||
fetch = ["nu_plugin_fetch"]
|
||||
inc = ["nu_plugin_inc"]
|
||||
match = ["nu_plugin_match"]
|
||||
post = ["nu_plugin_post"]
|
||||
ps = ["nu_plugin_ps"]
|
||||
sys = ["nu_plugin_sys"]
|
||||
textview = ["nu_plugin_textview"]
|
||||
|
||||
# Extra
|
||||
@ -136,9 +127,6 @@ tree = ["nu_plugin_tree"]
|
||||
xpath = ["nu_plugin_xpath"]
|
||||
zip-support = ["nu-command/zip"]
|
||||
|
||||
#This is disabled in extra for now
|
||||
table-pager = ["nu-command/table-pager"]
|
||||
|
||||
#dataframe feature for nushell
|
||||
dataframe = [
|
||||
"nu-engine/dataframe",
|
||||
@ -146,10 +134,12 @@ dataframe = [
|
||||
"nu-command/dataframe",
|
||||
"nu-value-ext/dataframe",
|
||||
"nu-data/dataframe",
|
||||
"nu_plugin_post/dataframe",
|
||||
"nu_plugin_to_bson/dataframe",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
opt-level = "s" # Optimize for size.
|
||||
|
||||
# Core plugins that ship with `cargo install nu` by default
|
||||
# Currently, Cargo limits us to installing only one binary
|
||||
# unless we use [[bin]], so we use this as a workaround
|
||||
@ -163,31 +153,11 @@ name = "nu_plugin_core_inc"
|
||||
path = "src/plugins/nu_plugin_core_inc.rs"
|
||||
required-features = ["inc"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_core_ps"
|
||||
path = "src/plugins/nu_plugin_core_ps.rs"
|
||||
required-features = ["ps"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_core_sys"
|
||||
path = "src/plugins/nu_plugin_core_sys.rs"
|
||||
required-features = ["sys"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_core_fetch"
|
||||
path = "src/plugins/nu_plugin_core_fetch.rs"
|
||||
required-features = ["fetch"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_core_match"
|
||||
path = "src/plugins/nu_plugin_core_match.rs"
|
||||
required-features = ["match"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_core_post"
|
||||
path = "src/plugins/nu_plugin_core_post.rs"
|
||||
required-features = ["post"]
|
||||
|
||||
# Extra plugins
|
||||
|
||||
[[bin]]
|
||||
|
57
README.md
57
README.md
@ -76,53 +76,6 @@ You can also build Nu yourself with all the bells and whistles (be sure to have
|
||||
```shell
|
||||
cargo build --workspace --features=extra
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
#### Quickstart
|
||||
|
||||
Want to try Nu right away? Execute the following to get started.
|
||||
|
||||
```shell
|
||||
docker run -it quay.io/nushell/nu:latest
|
||||
```
|
||||
|
||||
#### Guide
|
||||
|
||||
If you want to pull a pre-built container, you can browse tags for the [nushell organization](https://quay.io/organization/nushell)
|
||||
on Quay.io. Pulling a container would come down to:
|
||||
|
||||
```shell
|
||||
docker pull quay.io/nushell/nu
|
||||
docker pull quay.io/nushell/nu-base
|
||||
```
|
||||
|
||||
Both "nu-base" and "nu" provide the nu binary, however, nu-base also includes the source code at `/code`
|
||||
in the container and all dependencies.
|
||||
|
||||
Optionally, you can also build the containers locally using the [dockerfiles provided](docker):
|
||||
To build the base image:
|
||||
|
||||
```shell
|
||||
docker build -f docker/Dockerfile.nu-base -t nushell/nu-base .
|
||||
```
|
||||
|
||||
And then to build the smaller container (using a Multistage build):
|
||||
|
||||
```shell
|
||||
docker build -f docker/Dockerfile -t nushell/nu .
|
||||
```
|
||||
|
||||
Either way, you can run either container as follows:
|
||||
|
||||
```shell
|
||||
docker run -it nushell/nu-base
|
||||
docker run -it nushell/nu
|
||||
/> exit
|
||||
```
|
||||
|
||||
The second container is a bit smaller if the size is important to you.
|
||||
|
||||
### Packaging status
|
||||
|
||||
[](https://repology.org/project/nushell/versions)
|
||||
@ -310,9 +263,15 @@ Nu is in heavy development and will naturally change as it matures and people us
|
||||
| Completions | | | X | | | Completions for filepaths |
|
||||
| Type-checking | | | X | | | Commands check basic types, but input/output isn't checked |
|
||||
|
||||
## Current Roadmap
|
||||
## Officially Supported By
|
||||
|
||||
We've added a `Roadmap Board` to help collaboratively capture the direction we're going for the current release and capture some important issues we'd like to see in Nushell. You can find the Roadmap [here](https://github.com/nushell/nushell/projects/2).
|
||||
Please submit an issue or PR to be added to this list.
|
||||
|
||||
### Integrations
|
||||
- [zoxide](https://github.com/ajeetdsouza/zoxide)
|
||||
- [starship](https://github.com/starship/starship)
|
||||
### Mentions
|
||||
- [The Python Launcher for Unix](https://github.com/brettcannon/python-launcher#how-do-i-get-a-table-of-python-executables-in-nushell)
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -9,7 +9,7 @@ description = "Library for ANSI terminal colors and styles (bold, underline)"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-ansi-term"
|
||||
version = "0.34.0"
|
||||
version = "0.38.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
@ -21,7 +21,6 @@ derive_serde_style = ["serde"]
|
||||
[dependencies]
|
||||
overload = "0.1.1"
|
||||
serde = { version="1.0.90", features=["derive"], optional=true }
|
||||
itertools = "0.10.0"
|
||||
|
||||
# [dependencies.serde]
|
||||
# version = "1.0.90"
|
||||
|
@ -93,7 +93,7 @@ pub static RESET: &str = "\x1B[0m";
|
||||
|
||||
impl Color {
|
||||
fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
|
||||
match *self {
|
||||
match self {
|
||||
Color::Black => write!(f, "30"),
|
||||
Color::Red => write!(f, "31"),
|
||||
Color::Green => write!(f, "32"),
|
||||
@ -103,8 +103,8 @@ impl Color {
|
||||
Color::Magenta => write!(f, "35"),
|
||||
Color::Cyan => write!(f, "36"),
|
||||
Color::White => write!(f, "37"),
|
||||
Color::Fixed(num) => write!(f, "38;5;{}", &num),
|
||||
Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
|
||||
Color::Fixed(num) => write!(f, "38;5;{}", num),
|
||||
Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", r, g, b),
|
||||
Color::DarkGray => write!(f, "90"),
|
||||
Color::LightRed => write!(f, "91"),
|
||||
Color::LightGreen => write!(f, "92"),
|
||||
@ -118,7 +118,7 @@ impl Color {
|
||||
}
|
||||
|
||||
fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
|
||||
match *self {
|
||||
match self {
|
||||
Color::Black => write!(f, "40"),
|
||||
Color::Red => write!(f, "41"),
|
||||
Color::Green => write!(f, "42"),
|
||||
@ -128,8 +128,8 @@ impl Color {
|
||||
Color::Magenta => write!(f, "45"),
|
||||
Color::Cyan => write!(f, "46"),
|
||||
Color::White => write!(f, "47"),
|
||||
Color::Fixed(num) => write!(f, "48;5;{}", &num),
|
||||
Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
|
||||
Color::Fixed(num) => write!(f, "48;5;{}", num),
|
||||
Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", r, g, b),
|
||||
Color::DarkGray => write!(f, "100"),
|
||||
Color::LightRed => write!(f, "101"),
|
||||
Color::LightGreen => write!(f, "102"),
|
||||
|
@ -297,7 +297,7 @@ mod tests {
|
||||
fn no_control_codes_for_plain() {
|
||||
let one = Style::default().paint("one");
|
||||
let two = Style::default().paint("two");
|
||||
let output = format!("{}", AnsiStrings(&[one, two]));
|
||||
assert_eq!(&*output, "onetwo");
|
||||
let output = AnsiStrings(&[one, two]).to_string();
|
||||
assert_eq!(output, "onetwo");
|
||||
}
|
||||
}
|
||||
|
@ -602,14 +602,14 @@ mod serde_json_tests {
|
||||
|
||||
#[test]
|
||||
fn color_deserialization() {
|
||||
let colors = &[
|
||||
let colors = [
|
||||
Color::Red,
|
||||
Color::Blue,
|
||||
Color::Rgb(123, 123, 123),
|
||||
Color::Fixed(255),
|
||||
];
|
||||
|
||||
for color in colors.iter() {
|
||||
for color in colors {
|
||||
let serialized = serde_json::to_string(&color).unwrap();
|
||||
let deserialized: Color = serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
|
@ -75,6 +75,6 @@ mod test {
|
||||
assert_eq!(unstyled_len(&a), 18);
|
||||
|
||||
let l2 = [Black.paint("st"), Red.paint("-second"), White.paint("-t")];
|
||||
assert_eq!(sub_string(3, 11, &a).as_slice(), &l2);
|
||||
assert_eq!(sub_string(3, 11, &a), l2);
|
||||
}
|
||||
}
|
||||
|
@ -4,33 +4,35 @@ description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-cli"
|
||||
version = "0.34.0"
|
||||
version = "0.38.0"
|
||||
build = "build.rs"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-completion = { version = "0.34.0", path="../nu-completion" }
|
||||
nu-command = { version = "0.34.0", path="../nu-command" }
|
||||
nu-data = { version = "0.34.0", path="../nu-data" }
|
||||
nu-engine = { version = "0.34.0", path="../nu-engine" }
|
||||
nu-errors = { version = "0.34.0", path="../nu-errors" }
|
||||
nu-parser = { version = "0.34.0", path="../nu-parser" }
|
||||
nu-protocol = { version = "0.34.0", path="../nu-protocol" }
|
||||
nu-source = { version = "0.34.0", path="../nu-source" }
|
||||
nu-stream = { version = "0.34.0", path="../nu-stream" }
|
||||
nu-ansi-term = { version = "0.34.0", path="../nu-ansi-term" }
|
||||
nu-completion = { version = "0.38.0", path="../nu-completion" }
|
||||
nu-command = { version = "0.38.0", path="../nu-command" }
|
||||
nu-data = { version = "0.38.0", path="../nu-data" }
|
||||
nu-engine = { version = "0.38.0", path="../nu-engine" }
|
||||
nu-errors = { version = "0.38.0", path="../nu-errors" }
|
||||
nu-parser = { version = "0.38.0", path="../nu-parser" }
|
||||
nu-protocol = { version = "0.38.0", path="../nu-protocol" }
|
||||
nu-source = { version = "0.38.0", path="../nu-source" }
|
||||
nu-stream = { version = "0.38.0", path="../nu-stream" }
|
||||
nu-ansi-term = { version = "0.38.0", path="../nu-ansi-term" }
|
||||
nu-path = { version = "0.38.0", path="../nu-path" }
|
||||
|
||||
indexmap ="1.6.1"
|
||||
log = "0.4.14"
|
||||
pretty_env_logger = "0.4.0"
|
||||
strip-ansi-escapes = "0.1.0"
|
||||
rustyline = { version="8.1.0", optional=true }
|
||||
rustyline = { version="9.0.0", optional=true }
|
||||
ctrlc = { version="3.1.7", optional=true }
|
||||
shadow-rs = { version="0.6", default-features=false, optional=true }
|
||||
serde = { version="1.0.123", features=["derive"] }
|
||||
serde_yaml = "0.8.16"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = "0.6"
|
||||
|
@ -1,10 +1,10 @@
|
||||
mod logger;
|
||||
mod options;
|
||||
mod options_parser;
|
||||
pub mod stopwatch;
|
||||
|
||||
pub use options::{CliOptions, NuScript, Options};
|
||||
use options_parser::{NuParser, OptionsParser};
|
||||
|
||||
use self::stopwatch::Stopwatch;
|
||||
use lazy_static::lazy_static;
|
||||
use nu_command::{commands::NuSignature as Nu, utils::test_bins as binaries};
|
||||
use nu_engine::{get_full_help, EvaluationContext};
|
||||
use nu_errors::ShellError;
|
||||
@ -12,6 +12,18 @@ use nu_protocol::hir::{Call, Expression, SpannedExpression, Synthetic};
|
||||
use nu_protocol::{Primitive, UntaggedValue};
|
||||
use nu_source::{Span, Tag};
|
||||
use nu_stream::InputStream;
|
||||
pub use options::{CliOptions, NuScript, Options};
|
||||
use options_parser::{NuParser, OptionsParser};
|
||||
use std::sync::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref STOPWATCH: Mutex<Stopwatch> = {
|
||||
let mut sw = Stopwatch::default();
|
||||
sw.start();
|
||||
sw.stop();
|
||||
Mutex::new(sw)
|
||||
};
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
parser: Box<dyn OptionsParser>,
|
||||
@ -32,6 +44,14 @@ impl App {
|
||||
}
|
||||
|
||||
pub fn main(&self, argv: &[String]) -> Result<(), ShellError> {
|
||||
if self.perf() {
|
||||
// start the stopwatch running
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.start();
|
||||
}
|
||||
|
||||
let argv = quote_positionals(argv).join(" ");
|
||||
|
||||
if let Err(cause) = self.parse(&argv) {
|
||||
@ -52,6 +72,21 @@ impl App {
|
||||
|
||||
consume(context, stream)?;
|
||||
|
||||
if self.perf() {
|
||||
// stop the stopwatch since we're exiting
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.stop();
|
||||
eprintln!(
|
||||
"help {:?}",
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.elapsed()
|
||||
);
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
@ -93,6 +128,22 @@ impl App {
|
||||
};
|
||||
|
||||
consume(context, stream)?;
|
||||
|
||||
if self.perf() {
|
||||
// stop the stopwatch since we're exiting
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.stop();
|
||||
eprintln!(
|
||||
"version {:?}",
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.elapsed()
|
||||
);
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
@ -113,21 +164,29 @@ impl App {
|
||||
}
|
||||
|
||||
let mut opts = CliOptions::new();
|
||||
|
||||
opts.config = self.config().map(std::ffi::OsString::from);
|
||||
opts.stdin = self.takes_stdin();
|
||||
opts.save_history = self.save_history();
|
||||
opts.perf = self.perf();
|
||||
|
||||
use logger::{configure, debug_filters, logger, trace_filters};
|
||||
|
||||
logger(|builder| {
|
||||
configure(&self, builder)?;
|
||||
trace_filters(&self, builder)?;
|
||||
debug_filters(&self, builder)?;
|
||||
configure(self, builder)?;
|
||||
trace_filters(self, builder)?;
|
||||
debug_filters(self, builder)?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if self.perf() {
|
||||
// start a new split
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.start()
|
||||
}
|
||||
|
||||
if let Some(commands) = self.commands() {
|
||||
let commands = commands?;
|
||||
let script = NuScript::code(&commands)?;
|
||||
@ -136,6 +195,21 @@ impl App {
|
||||
return crate::run_script_file(context, opts);
|
||||
}
|
||||
|
||||
if self.perf() {
|
||||
// start a new spit
|
||||
eprintln!(
|
||||
"commands using -c at launch: {:?}",
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.elapsed_split()
|
||||
);
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.start();
|
||||
}
|
||||
|
||||
if let Some(scripts) = self.scripts() {
|
||||
let mut source_files = vec![];
|
||||
for script in scripts {
|
||||
@ -162,12 +236,38 @@ impl App {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.perf() {
|
||||
// start a new split
|
||||
eprintln!(
|
||||
"script file(s) passed in: {:?}",
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.elapsed_split()
|
||||
);
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.start();
|
||||
}
|
||||
|
||||
let context = crate::create_default_context(true)?;
|
||||
|
||||
if !self.skip_plugins() {
|
||||
let _ = crate::register_plugins(&context);
|
||||
}
|
||||
|
||||
if self.perf() {
|
||||
// start a new split
|
||||
eprintln!(
|
||||
"plugins registered: {:?}",
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.elapsed_split()
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
{
|
||||
crate::cli(context, opts)?;
|
||||
@ -189,6 +289,13 @@ impl App {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn perf(&self) -> bool {
|
||||
self.options
|
||||
.get("perf")
|
||||
.map(|v| matches!(v.as_bool(), Ok(true)))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn help(&self) -> bool {
|
||||
self.options
|
||||
.get("help")
|
||||
@ -298,7 +405,7 @@ impl App {
|
||||
}
|
||||
|
||||
pub fn parse(&self, args: &str) -> Result<(), ShellError> {
|
||||
self.parser.parse(&args).map(|options| {
|
||||
self.parser.parse(args).map(|options| {
|
||||
self.options.swap(&options);
|
||||
})
|
||||
}
|
||||
@ -401,14 +508,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_use_loglevels() -> Result<(), ShellError> {
|
||||
for level in &["error", "warn", "info", "debug", "trace"] {
|
||||
for level in ["error", "warn", "info", "debug", "trace"] {
|
||||
let ui = cli_app();
|
||||
let args = format!("nu --loglevel={}", *level);
|
||||
let args = format!("nu --loglevel={}", level);
|
||||
ui.parse(&args)?;
|
||||
assert_eq!(ui.loglevel().unwrap(), Ok(level.to_string()));
|
||||
|
||||
let ui = cli_app();
|
||||
let args = format!("nu -l {}", *level);
|
||||
let args = format!("nu -l {}", level);
|
||||
ui.parse(&args)?;
|
||||
assert_eq!(ui.loglevel().unwrap(), Ok(level.to_string()));
|
||||
}
|
||||
@ -434,11 +541,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn can_use_test_binaries() -> Result<(), ShellError> {
|
||||
for binarie_name in &[
|
||||
for binarie_name in [
|
||||
"echo_env", "cococo", "iecho", "fail", "nonu", "chop", "repeater", "meow",
|
||||
] {
|
||||
let ui = cli_app();
|
||||
let args = format!("nu --testbin={}", *binarie_name);
|
||||
let args = format!("nu --testbin={}", binarie_name);
|
||||
ui.parse(&args)?;
|
||||
assert_eq!(ui.testbin().unwrap(), Ok(binarie_name.to_string()));
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ pub struct CliOptions {
|
||||
pub stdin: bool,
|
||||
pub scripts: Vec<NuScript>,
|
||||
pub save_history: bool,
|
||||
pub perf: bool,
|
||||
}
|
||||
|
||||
impl Default for CliOptions {
|
||||
@ -25,6 +26,7 @@ impl CliOptions {
|
||||
stdin: false,
|
||||
scripts: vec![],
|
||||
save_history: true,
|
||||
perf: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -42,7 +44,7 @@ impl Options {
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &str) -> Option<Value> {
|
||||
self.inner.borrow().get(key).map(Clone::clone)
|
||||
self.inner.borrow().get(key).cloned()
|
||||
}
|
||||
|
||||
pub fn put(&self, key: &str, value: Value) {
|
||||
|
@ -67,51 +67,40 @@ impl OptionsParser for NuParser {
|
||||
}
|
||||
};
|
||||
|
||||
let value =
|
||||
value
|
||||
.map(|v| match k.as_ref() {
|
||||
"testbin" => {
|
||||
if let Ok(name) = v.as_string() {
|
||||
if testbins().iter().any(|n| name == *n) {
|
||||
Some(v)
|
||||
} else {
|
||||
Some(
|
||||
UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error(
|
||||
format!("{} is not supported.", name),
|
||||
),
|
||||
)
|
||||
.into_value(v.tag),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Some(v)
|
||||
}
|
||||
let value = value.map(|v| match k.as_ref() {
|
||||
"testbin" => {
|
||||
if let Ok(name) = v.as_string() {
|
||||
if testbins().iter().any(|n| name == *n) {
|
||||
v
|
||||
} else {
|
||||
UntaggedValue::Error(ShellError::untagged_runtime_error(
|
||||
format!("{} is not supported.", name),
|
||||
))
|
||||
.into_value(v.tag)
|
||||
}
|
||||
"loglevel" => {
|
||||
if let Ok(name) = v.as_string() {
|
||||
if loglevels().iter().any(|n| name == *n) {
|
||||
Some(v)
|
||||
} else {
|
||||
Some(
|
||||
UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error(
|
||||
format!("{} is not supported.", name),
|
||||
),
|
||||
)
|
||||
.into_value(v.tag),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Some(v)
|
||||
}
|
||||
} else {
|
||||
v
|
||||
}
|
||||
}
|
||||
"loglevel" => {
|
||||
if let Ok(name) = v.as_string() {
|
||||
if loglevels().iter().any(|n| name == *n) {
|
||||
v
|
||||
} else {
|
||||
UntaggedValue::Error(ShellError::untagged_runtime_error(
|
||||
format!("{} is not supported.", name),
|
||||
))
|
||||
.into_value(v.tag)
|
||||
}
|
||||
_ => Some(v),
|
||||
})
|
||||
.flatten();
|
||||
} else {
|
||||
v
|
||||
}
|
||||
}
|
||||
_ => v,
|
||||
});
|
||||
|
||||
if let Some(value) = value {
|
||||
options.put(&k, value);
|
||||
options.put(k, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
118
crates/nu-cli/src/app/stopwatch.rs
Normal file
118
crates/nu-cli/src/app/stopwatch.rs
Normal file
@ -0,0 +1,118 @@
|
||||
#![allow(dead_code)]
|
||||
use std::default::Default;
|
||||
use std::fmt;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Stopwatch {
|
||||
/// The time the stopwatch was started last, if ever.
|
||||
start_time: Option<Instant>,
|
||||
/// The time the stopwatch was split last, if ever.
|
||||
split_time: Option<Instant>,
|
||||
/// The time elapsed while the stopwatch was running (between start() and stop()).
|
||||
elapsed: Duration,
|
||||
}
|
||||
|
||||
impl Default for Stopwatch {
|
||||
fn default() -> Stopwatch {
|
||||
Stopwatch {
|
||||
start_time: None,
|
||||
split_time: None,
|
||||
elapsed: Duration::from_secs(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Stopwatch {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
return write!(f, "{}ms", self.elapsed_ms());
|
||||
}
|
||||
}
|
||||
|
||||
impl Stopwatch {
|
||||
/// Returns a new stopwatch.
|
||||
pub fn new() -> Stopwatch {
|
||||
let sw: Stopwatch = Default::default();
|
||||
sw
|
||||
}
|
||||
|
||||
/// Returns a new stopwatch which will immediately be started.
|
||||
pub fn start_new() -> Stopwatch {
|
||||
let mut sw = Stopwatch::new();
|
||||
sw.start();
|
||||
sw
|
||||
}
|
||||
|
||||
/// Starts the stopwatch.
|
||||
pub fn start(&mut self) {
|
||||
self.start_time = Some(Instant::now());
|
||||
}
|
||||
|
||||
/// Stops the stopwatch.
|
||||
pub fn stop(&mut self) {
|
||||
self.elapsed = self.elapsed();
|
||||
self.start_time = None;
|
||||
self.split_time = None;
|
||||
}
|
||||
|
||||
/// Resets all counters and stops the stopwatch.
|
||||
pub fn reset(&mut self) {
|
||||
self.elapsed = Duration::from_secs(0);
|
||||
self.start_time = None;
|
||||
self.split_time = None;
|
||||
}
|
||||
|
||||
/// Resets and starts the stopwatch again.
|
||||
pub fn restart(&mut self) {
|
||||
self.reset();
|
||||
self.start();
|
||||
}
|
||||
|
||||
/// Returns whether the stopwatch is running.
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.start_time.is_some()
|
||||
}
|
||||
|
||||
/// Returns the elapsed time since the start of the stopwatch.
|
||||
pub fn elapsed(&self) -> Duration {
|
||||
match self.start_time {
|
||||
// stopwatch is running
|
||||
Some(t1) => t1.elapsed() + self.elapsed,
|
||||
// stopwatch is not running
|
||||
None => self.elapsed,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the elapsed time since the start of the stopwatch in milliseconds.
|
||||
pub fn elapsed_ms(&self) -> i64 {
|
||||
let dur = self.elapsed();
|
||||
(dur.as_secs() * 1000 + dur.subsec_millis() as u64) as i64
|
||||
}
|
||||
|
||||
/// Returns the elapsed time since last split or start/restart.
|
||||
///
|
||||
/// If the stopwatch is in stopped state this will always return a zero Duration.
|
||||
pub fn elapsed_split(&mut self) -> Duration {
|
||||
match self.start_time {
|
||||
// stopwatch is running
|
||||
Some(start) => {
|
||||
let res = match self.split_time {
|
||||
Some(split) => split.elapsed(),
|
||||
None => start.elapsed(),
|
||||
};
|
||||
self.split_time = Some(Instant::now());
|
||||
res
|
||||
}
|
||||
// stopwatch is not running
|
||||
None => Duration::from_secs(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the elapsed time since last split or start/restart in milliseconds.
|
||||
///
|
||||
/// If the stopwatch is in stopped state this will always return zero.
|
||||
pub fn elapsed_split_ms(&mut self) -> i64 {
|
||||
let dur = self.elapsed_split();
|
||||
(dur.as_secs() * 1000 + dur.subsec_millis() as u64) as i64
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
use crate::app::STOPWATCH;
|
||||
use crate::line_editor::configure_ctrl_c;
|
||||
use nu_ansi_term::Color;
|
||||
use nu_engine::{maybe_print_errors, run_block, script::run_script_standalone, EvaluationContext};
|
||||
@ -23,6 +24,7 @@ use rustyline::{self, error::ReadlineError};
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::ParserScope;
|
||||
use nu_path::expand_tilde;
|
||||
use nu_protocol::{hir::ExternalRedirection, ConfigPath, UntaggedValue, Value};
|
||||
|
||||
use log::trace;
|
||||
@ -30,9 +32,12 @@ use std::error::Error;
|
||||
use std::iter::Iterator;
|
||||
use std::path::PathBuf;
|
||||
|
||||
// Name of environment variable where the prompt could be stored
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
const PROMPT_COMMAND: &str = "PROMPT_COMMAND";
|
||||
|
||||
pub fn search_paths() -> Vec<std::path::PathBuf> {
|
||||
use std::env;
|
||||
|
||||
let mut search_paths = Vec::new();
|
||||
|
||||
// Automatically add path `nu` is in as a search path
|
||||
@ -50,7 +55,7 @@ pub fn search_paths() -> Vec<std::path::PathBuf> {
|
||||
{
|
||||
for pipeline in pipelines {
|
||||
if let Ok(plugin_dir) = pipeline.as_string() {
|
||||
search_paths.push(PathBuf::from(plugin_dir));
|
||||
search_paths.push(expand_tilde(plugin_dir));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -82,6 +87,66 @@ pub fn run_script_file(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
fn default_prompt_string(cwd: &str) -> String {
|
||||
format!(
|
||||
"{}{}{}{}{}{}> ",
|
||||
Color::Green.bold().prefix().to_string(),
|
||||
cwd,
|
||||
nu_ansi_term::ansi::RESET,
|
||||
Color::Cyan.bold().prefix().to_string(),
|
||||
current_branch(),
|
||||
nu_ansi_term::ansi::RESET
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
fn evaluate_prompt_string(prompt_line: &str, context: &EvaluationContext, cwd: &str) -> String {
|
||||
context.scope.enter_scope();
|
||||
let (prompt_block, err) = nu_parser::parse(prompt_line, 0, &context.scope);
|
||||
|
||||
if err.is_some() {
|
||||
context.scope.exit_scope();
|
||||
default_prompt_string(cwd)
|
||||
} else {
|
||||
let run_result = run_block(
|
||||
&prompt_block,
|
||||
context,
|
||||
InputStream::empty(),
|
||||
ExternalRedirection::Stdout,
|
||||
);
|
||||
context.scope.exit_scope();
|
||||
|
||||
match run_result {
|
||||
Ok(result) => match result.collect_string(Tag::unknown()) {
|
||||
Ok(string_result) => {
|
||||
let errors = context.get_errors();
|
||||
maybe_print_errors(context, Text::from(prompt_line));
|
||||
context.clear_errors();
|
||||
|
||||
if !errors.is_empty() {
|
||||
"> ".into()
|
||||
} else {
|
||||
string_result.item
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
context.host().lock().print_err(e, &Text::from(prompt_line));
|
||||
context.clear_errors();
|
||||
|
||||
"> ".into()
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
context.host().lock().print_err(e, &Text::from(prompt_line));
|
||||
context.clear_errors();
|
||||
|
||||
"> ".into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub fn cli(
|
||||
context: EvaluationContext,
|
||||
@ -97,16 +162,30 @@ pub fn cli(
|
||||
} else {
|
||||
load_global_cfg(&context);
|
||||
}
|
||||
|
||||
// Store cmd duration in an env var
|
||||
context.scope.add_env_var(
|
||||
"CMD_DURATION_MS",
|
||||
format!("{}", startup_commands_start_time.elapsed().as_millis()),
|
||||
);
|
||||
trace!(
|
||||
"startup commands took {:?}",
|
||||
startup_commands_start_time.elapsed()
|
||||
startup_commands_start_time
|
||||
.elapsed()
|
||||
.as_millis()
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
if options.perf {
|
||||
eprintln!(
|
||||
"config loaded: {:?}",
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.elapsed_split()
|
||||
);
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.start();
|
||||
}
|
||||
|
||||
//Configure rustyline
|
||||
let mut rl = default_rustyline_editor_configuration();
|
||||
let history_path = if let Some(cfg) = &context.configs().lock().global_config {
|
||||
@ -118,11 +197,39 @@ pub fn cli(
|
||||
nu_data::config::path::default_history_path()
|
||||
};
|
||||
|
||||
if options.perf {
|
||||
eprintln!(
|
||||
"rustyline configuration: {:?}",
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.elapsed_split()
|
||||
);
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.start();
|
||||
}
|
||||
|
||||
// Don't load history if it's not necessary
|
||||
if options.save_history {
|
||||
let _ = rl.load_history(&history_path);
|
||||
}
|
||||
|
||||
if options.perf {
|
||||
eprintln!(
|
||||
"history load: {:?}",
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.elapsed_split()
|
||||
);
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.start();
|
||||
}
|
||||
|
||||
//set vars from cfg if present
|
||||
let (skip_welcome_message, prompt) = if let Some(cfg) = &context.configs().lock().global_config
|
||||
{
|
||||
@ -136,6 +243,20 @@ pub fn cli(
|
||||
(false, None)
|
||||
};
|
||||
|
||||
if options.perf {
|
||||
eprintln!(
|
||||
"load custom prompt: {:?}",
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.elapsed_split()
|
||||
);
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.start();
|
||||
}
|
||||
|
||||
//Check whether dir we start in contains local cfg file and if so load it.
|
||||
load_local_cfg_if_present(&context);
|
||||
|
||||
@ -159,6 +280,20 @@ pub fn cli(
|
||||
|
||||
let mut ctrlcbreak = false;
|
||||
|
||||
if options.perf {
|
||||
eprintln!(
|
||||
"timing stopped. starting run loop: {:?}",
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.elapsed_split()
|
||||
);
|
||||
STOPWATCH
|
||||
.lock()
|
||||
.expect("unable to lock the stopwatch")
|
||||
.stop();
|
||||
}
|
||||
|
||||
loop {
|
||||
if context.ctrl_c().load(Ordering::SeqCst) {
|
||||
context.ctrl_c().store(false, Ordering::SeqCst);
|
||||
@ -167,72 +302,17 @@ pub fn cli(
|
||||
|
||||
let cwd = context.shell_manager().path();
|
||||
|
||||
let colored_prompt = {
|
||||
if let Some(prompt) = &prompt {
|
||||
let prompt_line = prompt.as_string()?;
|
||||
|
||||
context.scope.enter_scope();
|
||||
let (prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
|
||||
|
||||
if err.is_some() {
|
||||
context.scope.exit_scope();
|
||||
|
||||
format!(
|
||||
"{}{}{}{}{}{}> ",
|
||||
Color::Green.bold().prefix().to_string(),
|
||||
cwd,
|
||||
nu_ansi_term::ansi::RESET,
|
||||
Color::Cyan.bold().prefix().to_string(),
|
||||
current_branch(),
|
||||
nu_ansi_term::ansi::RESET
|
||||
)
|
||||
// Check if the PROMPT_COMMAND env variable is set. This env variable
|
||||
// contains nu code that is used to overwrite the prompt
|
||||
let colored_prompt = match context.scope.get_env(PROMPT_COMMAND) {
|
||||
Some(env_prompt) => evaluate_prompt_string(&env_prompt, &context, &cwd),
|
||||
None => {
|
||||
if let Some(prompt) = &prompt {
|
||||
let prompt_line = prompt.as_string()?;
|
||||
evaluate_prompt_string(&prompt_line, &context, &cwd)
|
||||
} else {
|
||||
let run_result = run_block(
|
||||
&prompt_block,
|
||||
&context,
|
||||
InputStream::empty(),
|
||||
ExternalRedirection::Stdout,
|
||||
);
|
||||
context.scope.exit_scope();
|
||||
|
||||
match run_result {
|
||||
Ok(result) => match result.collect_string(Tag::unknown()) {
|
||||
Ok(string_result) => {
|
||||
let errors = context.get_errors();
|
||||
maybe_print_errors(&context, Text::from(prompt_line));
|
||||
context.clear_errors();
|
||||
|
||||
if !errors.is_empty() {
|
||||
"> ".to_string()
|
||||
} else {
|
||||
string_result.item
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
context.host().lock().print_err(e, &Text::from(prompt_line));
|
||||
context.clear_errors();
|
||||
|
||||
"> ".to_string()
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
context.host().lock().print_err(e, &Text::from(prompt_line));
|
||||
context.clear_errors();
|
||||
|
||||
"> ".to_string()
|
||||
}
|
||||
}
|
||||
default_prompt_string(&cwd)
|
||||
}
|
||||
} else {
|
||||
format!(
|
||||
"{}{}{}{}{}{}> ",
|
||||
Color::Green.bold().prefix().to_string(),
|
||||
cwd,
|
||||
nu_ansi_term::ansi::RESET,
|
||||
Color::Cyan.bold().prefix().to_string(),
|
||||
current_branch(),
|
||||
nu_ansi_term::ansi::RESET
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@ -277,7 +357,7 @@ pub fn cli(
|
||||
// Store cmd duration in an env var
|
||||
context.scope.add_env_var(
|
||||
"CMD_DURATION_MS",
|
||||
format!("{}", cmd_start_time.elapsed().as_millis()),
|
||||
cmd_start_time.elapsed().as_millis().to_string(),
|
||||
);
|
||||
|
||||
match line {
|
||||
@ -321,8 +401,7 @@ pub fn cli(
|
||||
.lock()
|
||||
.global_config
|
||||
.as_ref()
|
||||
.map(|cfg| cfg.var("ctrlc_exit"))
|
||||
.flatten()
|
||||
.and_then(|cfg| cfg.var("ctrlc_exit"))
|
||||
.map(|ctrl_c| ctrl_c.is_true())
|
||||
.unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells
|
||||
|
||||
@ -364,6 +443,7 @@ pub fn cli(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub fn load_local_cfg_if_present(context: &EvaluationContext) {
|
||||
trace!("Loading local cfg if present");
|
||||
match config::loadable_cfg_exists_in_dir(PathBuf::from(context.shell_manager().path())) {
|
||||
|
@ -461,7 +461,7 @@ pub(crate) fn load_keybindings(
|
||||
if let Ok(contents) = contents {
|
||||
let keybindings: Keybindings = serde_yaml::from_str(&contents)?;
|
||||
// eprintln!("{:#?}", keybindings);
|
||||
for keybinding in keybindings.into_iter() {
|
||||
for keybinding in keybindings {
|
||||
let (k, b) = convert_keybinding(keybinding);
|
||||
// eprintln!("{:?} {:?}", k, b);
|
||||
|
||||
|
@ -76,6 +76,8 @@ pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
|
||||
let config = Config::builder()
|
||||
.check_cursor_position(true)
|
||||
.color_mode(ColorMode::Forced)
|
||||
.history_ignore_dups(false)
|
||||
.max_history_size(10_000)
|
||||
.build();
|
||||
let mut rl: Editor<_> = Editor::with_config(config);
|
||||
|
||||
@ -247,7 +249,7 @@ pub fn rustyline_hinter(
|
||||
) -> Option<rustyline::hint::HistoryHinter> {
|
||||
if let Some(line_editor_vars) = config.var("line_editor") {
|
||||
for (idx, value) in line_editor_vars.row_entries() {
|
||||
if idx == "show_hints" && value.expect_string() == "false" {
|
||||
if idx == "show_hints" && value.as_bool() == Ok(false) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,10 @@ impl rustyline::completion::Completer for Helper {
|
||||
impl rustyline::hint::Hinter for Helper {
|
||||
type Hint = String;
|
||||
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
|
||||
self.hinter.as_ref().and_then(|h| h.hint(line, pos, ctx))
|
||||
match &self.hinter {
|
||||
Some(the_hinter) => the_hinter.hint(line, pos, ctx),
|
||||
None => Some("".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,52 +5,48 @@ description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-command"
|
||||
version = "0.34.0"
|
||||
version = "0.38.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-data = { version = "0.34.0", path="../nu-data" }
|
||||
nu-engine = { version = "0.34.0", path="../nu-engine" }
|
||||
nu-errors = { version = "0.34.0", path="../nu-errors" }
|
||||
nu-json = { version = "0.34.0", path="../nu-json" }
|
||||
nu-path = { version = "0.34.0", path="../nu-path" }
|
||||
nu-parser = { version = "0.34.0", path="../nu-parser" }
|
||||
nu-plugin = { version = "0.34.0", path="../nu-plugin" }
|
||||
nu-protocol = { version = "0.34.0", path="../nu-protocol" }
|
||||
nu-source = { version = "0.34.0", path="../nu-source" }
|
||||
nu-stream = { version = "0.34.0", path="../nu-stream" }
|
||||
nu-table = { version = "0.34.0", path="../nu-table" }
|
||||
nu-test-support = { version = "0.34.0", path="../nu-test-support" }
|
||||
nu-value-ext = { version = "0.34.0", path="../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.34.0", path="../nu-ansi-term" }
|
||||
nu-pretty-hex = { version = "0.34.0", path="../nu-pretty-hex" }
|
||||
nu-data = { version = "0.38.0", path="../nu-data" }
|
||||
nu-engine = { version = "0.38.0", path="../nu-engine" }
|
||||
nu-errors = { version = "0.38.0", path="../nu-errors" }
|
||||
nu-json = { version = "0.38.0", path="../nu-json" }
|
||||
nu-path = { version = "0.38.0", path="../nu-path" }
|
||||
nu-parser = { version = "0.38.0", path="../nu-parser" }
|
||||
nu-plugin = { version = "0.38.0", path="../nu-plugin" }
|
||||
nu-protocol = { version = "0.38.0", path="../nu-protocol" }
|
||||
nu-serde = { version = "0.38.0", path="../nu-serde" }
|
||||
nu-source = { version = "0.38.0", path="../nu-source" }
|
||||
nu-stream = { version = "0.38.0", path="../nu-stream" }
|
||||
nu-table = { version = "0.38.0", path="../nu-table" }
|
||||
nu-test-support = { version = "0.38.0", path="../nu-test-support" }
|
||||
nu-value-ext = { version = "0.38.0", path="../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.38.0", path="../nu-ansi-term" }
|
||||
nu-pretty-hex = { version = "0.38.0", path="../nu-pretty-hex" }
|
||||
|
||||
url = "2.2.1"
|
||||
mime = "0.3.16"
|
||||
Inflector = "0.11"
|
||||
arboard = { version="1.1.0", optional=true }
|
||||
base64 = "0.13.0"
|
||||
bigdecimal = { version="0.2.0", features=["serde"] }
|
||||
byte-unit = "4.0.9"
|
||||
bytes = "1.0.1"
|
||||
bigdecimal = { package = "bigdecimal-rs", version = "0.2.1", features = ["serde"] }
|
||||
calamine = "0.18.0"
|
||||
chrono = { version="0.4.19", features=["serde"] }
|
||||
chrono-tz = "0.5.3"
|
||||
codespan-reporting = "0.11.0"
|
||||
crossterm = { version="0.19.0", optional=true }
|
||||
csv = "1.1.3"
|
||||
ctrlc = { version="3.1.7", optional=true }
|
||||
derive-new = "0.5.8"
|
||||
directories-next = "2.0.0"
|
||||
dirs-next = "2.0.0"
|
||||
dtparse = "1.2.0"
|
||||
dunce = "1.0.1"
|
||||
eml-parser = "0.1.0"
|
||||
encoding_rs = "0.8.28"
|
||||
filesize = "0.2.0"
|
||||
fs_extra = "1.2.0"
|
||||
futures = { version="0.3.12", features=["compat", "io-compat"] }
|
||||
getset = "0.1.1"
|
||||
glob = "0.3.0"
|
||||
htmlescape = "0.3.1"
|
||||
ical = "0.7.0"
|
||||
@ -58,49 +54,44 @@ indexmap = { version="1.7", features=["serde-1"] }
|
||||
itertools = "0.10.0"
|
||||
lazy_static = "1.*"
|
||||
log = "0.4.14"
|
||||
md5 = "0.7.0"
|
||||
md-5 = "0.9.1"
|
||||
meval = "0.2.0"
|
||||
minus = { version="3.4.0", optional=true, features=["async_std_lib", "search"] }
|
||||
num-bigint = { version="0.3.1", features=["serde"] }
|
||||
num-format = { version="0.4.0", features=["with-num-bigint"] }
|
||||
num-traits = "0.2.14"
|
||||
parking_lot = "0.11.1"
|
||||
pin-utils = "0.1.0"
|
||||
query_interface = "0.3.5"
|
||||
quick-xml = "0.22"
|
||||
rand = "0.8"
|
||||
rayon = "1.5.0"
|
||||
regex = "1.4.3"
|
||||
reqwest = {version = "0.11", optional = true }
|
||||
roxmltree = "0.14.0"
|
||||
rust-embed = "5.9.0"
|
||||
rustyline = { version="8.1.0", optional=true }
|
||||
rustyline = { version="9.0.0", optional=true }
|
||||
serde = { version="1.0.123", features=["derive"] }
|
||||
serde_bytes = "0.11.5"
|
||||
serde_ini = "0.2.0"
|
||||
serde_json = "1.0.61"
|
||||
serde_urlencoded = "0.7.0"
|
||||
serde_yaml = "0.8.16"
|
||||
sha2 = "0.9.3"
|
||||
strip-ansi-escapes = "0.1.0"
|
||||
sxd-document = "0.3.2"
|
||||
sxd-xpath = "0.4.2"
|
||||
tempfile = "3.2.0"
|
||||
sysinfo = { version = "0.20.2", optional = true }
|
||||
thiserror = "1.0.26"
|
||||
term = { version="0.7.0", optional=true }
|
||||
term_size = "0.3.2"
|
||||
termcolor = "1.1.2"
|
||||
titlecase = "1.1.0"
|
||||
tokio = { version = "1", features = ["rt-multi-thread"], optional = true }
|
||||
toml = "0.5.8"
|
||||
trash = { version="1.3.0", optional=true }
|
||||
unicode-segmentation = "1.8"
|
||||
url = "2.2.0"
|
||||
uuid_crate = { package="uuid", version="0.8.2", features=["v4"], optional=true }
|
||||
which = { version="4.1.0", optional=true }
|
||||
zip = { version="0.5.9", optional=true }
|
||||
digest = "0.9.0"
|
||||
|
||||
[dependencies.polars]
|
||||
version = "0.14.5"
|
||||
version = "0.16.0"
|
||||
optional = true
|
||||
features = ["parquet", "json", "random", "pivot", "strings", "is_in"]
|
||||
features = ["parquet", "json", "random", "pivot", "strings", "is_in", "temporal", "cum_agg", "rolling_window"]
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
umask = "1.0.0"
|
||||
@ -109,13 +100,8 @@ users = "0.11.0"
|
||||
# TODO this will be possible with new dependency resolver
|
||||
# (currently on nightly behind -Zfeatures=itarget):
|
||||
# https://github.com/rust-lang/cargo/issues/7914
|
||||
#[target.'cfg(not(windows))'.dependencies]
|
||||
#num-format = {version = "0.4", features = ["with-system-locale"]}
|
||||
|
||||
[dependencies.rusqlite]
|
||||
features = ["bundled", "blob"]
|
||||
optional = true
|
||||
version = "0.25.3"
|
||||
# [target.'cfg(not(windows))'.dependencies]
|
||||
# num-format = { version = "0.4", features = ["with-system-locale"] }
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = "0.6"
|
||||
@ -130,5 +116,8 @@ clipboard-cli = ["arboard"]
|
||||
rustyline-support = ["rustyline"]
|
||||
stable = []
|
||||
trash-support = ["trash"]
|
||||
table-pager = ["minus", "crossterm"]
|
||||
dataframe = ["nu-protocol/dataframe", "polars"]
|
||||
fetch = ["reqwest", "tokio"]
|
||||
post = ["reqwest", "tokio"]
|
||||
sys = ["sysinfo"]
|
||||
ps = ["sysinfo"]
|
||||
|
@ -4,7 +4,10 @@ use nu_engine::{MaybeTextCodec, StringOrBinary};
|
||||
use nu_test_support::NATIVE_PATH_ENV_VAR;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
#[allow(unused)]
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::mpsc;
|
||||
use std::{borrow::Cow, io::BufReader};
|
||||
@ -17,6 +20,9 @@ use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
|
||||
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use which::which_in;
|
||||
|
||||
pub(crate) fn run_external_command(
|
||||
command: ExternalCommand,
|
||||
context: &mut EvaluationContext,
|
||||
@ -104,7 +110,7 @@ fn run_with_stdin(
|
||||
let process_args = command_args
|
||||
.iter()
|
||||
.map(|(arg, _is_literal)| {
|
||||
let arg = nu_path::expand_tilde_string(Cow::Borrowed(arg));
|
||||
let arg = nu_path::expand_tilde(arg).to_string_lossy().to_string();
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
@ -141,6 +147,79 @@ fn run_with_stdin(
|
||||
)
|
||||
}
|
||||
|
||||
/// Spawn a direct exe
|
||||
#[allow(unused)]
|
||||
fn spawn_exe(full_path: PathBuf, args: &[String]) -> Command {
|
||||
let mut process = Command::new(full_path);
|
||||
for arg in args {
|
||||
process.arg(&arg);
|
||||
}
|
||||
process
|
||||
}
|
||||
|
||||
/// Spawn a cmd command with `cmd /c args...`
|
||||
fn spawn_cmd_command(command: &ExternalCommand, args: &[String]) -> Command {
|
||||
let mut process = Command::new("cmd");
|
||||
process.arg("/c");
|
||||
process.arg(&command.name);
|
||||
for arg in args {
|
||||
// Clean the args before we use them:
|
||||
// https://stackoverflow.com/questions/1200235/how-to-pass-a-quoted-pipe-character-to-cmd-exe
|
||||
// cmd.exe needs to have a caret to escape a pipe
|
||||
let arg = arg.replace("|", "^|");
|
||||
process.arg(&arg);
|
||||
}
|
||||
process
|
||||
}
|
||||
|
||||
/// Spawn a sh command with `sh -c args...`
|
||||
fn spawn_sh_command(command: &ExternalCommand, args: &[String]) -> Command {
|
||||
let cmd_with_args = vec![command.name.clone(), args.join(" ")].join(" ");
|
||||
let mut process = Command::new("sh");
|
||||
process.arg("-c").arg(cmd_with_args);
|
||||
process
|
||||
}
|
||||
|
||||
/// a function to spawn any external command
|
||||
#[allow(unused)] // for minimal builds cwd is unused
|
||||
fn spawn_any(command: &ExternalCommand, args: &[String], cwd: &str) -> Command {
|
||||
// resolve the executable name if it is spawnable directly
|
||||
#[cfg(feature = "which")]
|
||||
// TODO add more available paths to `env::var_os("PATH")`?
|
||||
if let Result::Ok(full_path) = which_in(&command.name, env::var_os("PATH"), cwd) {
|
||||
if let Some(extension) = full_path.extension() {
|
||||
#[cfg(windows)]
|
||||
if extension.eq_ignore_ascii_case("exe") {
|
||||
// if exe spawn it directly
|
||||
return spawn_exe(full_path, args);
|
||||
} else {
|
||||
// TODO implement special care for various executable types such as .bat, .ps1, .cmd, etc
|
||||
// https://github.com/mklement0/Native/blob/e0e0b8785cad39a73053e35084d1f60d87fbac58/Native.psm1#L749
|
||||
// otherwise shell out to cmd
|
||||
return spawn_cmd_command(command, args);
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
if !["sh", "bash"]
|
||||
.iter()
|
||||
.any(|ext| extension.eq_ignore_ascii_case(ext))
|
||||
{
|
||||
// if exe spawn it directly
|
||||
return spawn_exe(full_path, args);
|
||||
} else {
|
||||
// otherwise shell out to sh
|
||||
return spawn_sh_command(command, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
// in all the other cases shell out
|
||||
if cfg!(windows) {
|
||||
spawn_cmd_command(command, args)
|
||||
} else {
|
||||
// TODO what happens if that os doesn't support spawning sh?
|
||||
spawn_sh_command(command, args)
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn(
|
||||
command: &ExternalCommand,
|
||||
path: &str,
|
||||
@ -151,31 +230,7 @@ fn spawn(
|
||||
) -> Result<InputStream, ShellError> {
|
||||
let command = command.clone();
|
||||
|
||||
let mut process = {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let mut process = Command::new("cmd");
|
||||
process.arg("/c");
|
||||
process.arg(&command.name);
|
||||
for arg in args {
|
||||
// Clean the args before we use them:
|
||||
// https://stackoverflow.com/questions/1200235/how-to-pass-a-quoted-pipe-character-to-cmd-exe
|
||||
// cmd.exe needs to have a caret to escape a pipe
|
||||
let arg = arg.replace("|", "^|");
|
||||
process.arg(&arg);
|
||||
}
|
||||
process
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let cmd_with_args = vec![command.name.clone(), args.join(" ")].join(" ");
|
||||
let mut process = Command::new("sh");
|
||||
process.arg("-c").arg(cmd_with_args);
|
||||
process
|
||||
}
|
||||
};
|
||||
|
||||
let mut process = spawn_any(&command, args, path);
|
||||
process.current_dir(path);
|
||||
trace!(target: "nu::run::external", "cwd = {:?}", &path);
|
||||
|
||||
@ -454,7 +509,7 @@ fn spawn(
|
||||
Ok(stream.into_input_stream())
|
||||
}
|
||||
Err(e) => Err(ShellError::labeled_error(
|
||||
format!("{}", e),
|
||||
e.to_string(),
|
||||
"failed to spawn",
|
||||
&command.name_tag,
|
||||
)),
|
||||
|
@ -22,6 +22,7 @@ impl WholeStreamCommand for Histogram {
|
||||
None,
|
||||
)
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column name to give the histogram's frequency column",
|
||||
)
|
||||
|
@ -13,6 +13,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into binary").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to binary (for table input)",
|
||||
)
|
||||
|
118
crates/nu-command/src/commands/conversions/into/column_path.rs
Normal file
118
crates/nu-command/src/commands/conversions/into/column_path.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into column_path"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into column_path").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"values to convert to column_path",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to column path"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_filepath(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Convert string to column_path in table",
|
||||
example: "echo [[name]; ['/dev/null'] ['C:\\Program Files'] ['../../Cargo.toml']] | into column_path name",
|
||||
result: Some(vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::column_path("/dev/null", Span::unknown()).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::column_path("C:\\Program Files", Span::unknown()).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::column_path("../../Cargo.toml", Span::unknown()).into(),
|
||||
})
|
||||
.into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert string to column_path",
|
||||
example: "echo 'Cargo.toml' | into column_path",
|
||||
result: Some(vec![UntaggedValue::column_path("Cargo.toml", Span::unknown()).into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_filepath(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag())),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.into_input_stream())
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::column_path(
|
||||
match prim {
|
||||
Primitive::String(a_string) => a_string,
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"'into column_path' for non-string primitives",
|
||||
))
|
||||
}
|
||||
},
|
||||
Span::unknown(),
|
||||
)
|
||||
.into_value(&tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column name to use, with 'into column_path COLUMN'",
|
||||
"found table",
|
||||
tag,
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented(
|
||||
"'into column_path' for unsupported type",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
127
crates/nu-command/src/commands/conversions/into/filepath.rs
Normal file
127
crates/nu-command/src/commands/conversions/into/filepath.rs
Normal file
@ -0,0 +1,127 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into path"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into path").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to filepath (for table input)",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to filepath"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_filepath(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Convert string to filepath in table",
|
||||
example: "echo [[name]; ['/dev/null'] ['C:\\Program Files'] ['../../Cargo.toml']] | into path name",
|
||||
result: Some(vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::filepath("/dev/null").into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::filepath("C:\\Program Files").into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"name".to_string() => UntaggedValue::filepath("../../Cargo.toml").into(),
|
||||
})
|
||||
.into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert string to filepath",
|
||||
example: "echo 'Cargo.toml' | into path",
|
||||
result: Some(vec![UntaggedValue::filepath("Cargo.toml").into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_filepath(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag())),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.into_input_stream())
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::filepath(match prim {
|
||||
Primitive::String(a_string) => match filepath_from_string(a_string, &tag) {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
},
|
||||
Primitive::FilePath(a_filepath) => a_filepath.clone(),
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"'into path' for non-string primitives",
|
||||
))
|
||||
}
|
||||
})
|
||||
.into_value(&tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column name to use, with 'into path COLUMN'",
|
||||
"found table",
|
||||
tag,
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented(
|
||||
"'into path' for unsupported type",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn filepath_from_string(a_string: &str, _tag: &Tag) -> Result<PathBuf, ShellError> {
|
||||
Ok(PathBuf::from(a_string))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
182
crates/nu-command/src/commands/conversions/into/filesize.rs
Normal file
182
crates/nu-command/src/commands/conversions/into/filesize.rs
Normal file
@ -0,0 +1,182 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use num_bigint::ToBigInt;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into filesize"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into filesize").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to filesize (for table input)",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to filesize"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_filesize(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Convert string to filesize in table",
|
||||
example: "echo [[bytes]; ['5'] [3.2] [4] [2kb]] | into filesize bytes",
|
||||
result: Some(vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"bytes".to_string() => UntaggedValue::filesize(5).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"bytes".to_string() => UntaggedValue::filesize(3).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"bytes".to_string() => UntaggedValue::filesize(4).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"bytes".to_string() => UntaggedValue::filesize(2000).into(),
|
||||
})
|
||||
.into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert string to filesize",
|
||||
example: "echo '2' | into filesize",
|
||||
result: Some(vec![UntaggedValue::filesize(2).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert decimal to filesize",
|
||||
example: "echo 8.3 | into filesize",
|
||||
result: Some(vec![UntaggedValue::filesize(8).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert int to filesize",
|
||||
example: "echo 5 | into filesize",
|
||||
result: Some(vec![UntaggedValue::filesize(5).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert file size to filesize",
|
||||
example: "echo 4KB | into filesize",
|
||||
result: Some(vec![UntaggedValue::filesize(4000).into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_filesize(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag())),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.into_input_stream())
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::filesize(match prim {
|
||||
Primitive::String(a_string) => match int_from_string(a_string.trim(), &tag) {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
},
|
||||
Primitive::Decimal(dec) => match dec.to_bigint() {
|
||||
Some(n) => match n.to_u64() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to filesize",
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to filesize",
|
||||
));
|
||||
}
|
||||
},
|
||||
Primitive::Int(n_ref) => (*n_ref).try_into().map_err(|_| {
|
||||
ShellError::unimplemented("cannot convert negative integer to filesize")
|
||||
})?,
|
||||
Primitive::Filesize(a_filesize) => *a_filesize,
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"'into filesize' for non-numeric primitives",
|
||||
))
|
||||
}
|
||||
})
|
||||
.into_value(&tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column name to use, with 'into filesize COLUMN'",
|
||||
"found table",
|
||||
tag,
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented(
|
||||
"'into filesize' for unsupported type",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn int_from_string(a_string: &str, tag: &Tag) -> Result<u64, ShellError> {
|
||||
match a_string.parse::<u64>() {
|
||||
Ok(n) => Ok(n),
|
||||
Err(_) => match a_string.parse::<f64>() {
|
||||
Ok(f) => match f.to_u64() {
|
||||
Some(i) => Ok(i),
|
||||
None => Err(ShellError::labeled_error(
|
||||
"Could not convert string value to filesize",
|
||||
"original value",
|
||||
tag.clone(),
|
||||
)),
|
||||
},
|
||||
Err(_) => Err(ShellError::labeled_error(
|
||||
"Could not convert string value to filesize",
|
||||
"original value",
|
||||
tag.clone(),
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into int").rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to int (for table input)",
|
||||
)
|
||||
|
@ -1,9 +1,15 @@
|
||||
mod binary;
|
||||
mod column_path;
|
||||
mod command;
|
||||
mod filepath;
|
||||
mod filesize;
|
||||
mod int;
|
||||
pub mod string;
|
||||
|
||||
pub use self::filesize::SubCommand as IntoFilesize;
|
||||
pub use binary::SubCommand as IntoBinary;
|
||||
pub use column_path::SubCommand as IntoColumnPath;
|
||||
pub use command::Command as Into;
|
||||
pub use filepath::SubCommand as IntoFilepath;
|
||||
pub use int::SubCommand as IntoInt;
|
||||
pub use string::SubCommand as IntoString;
|
||||
|
@ -20,6 +20,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into string")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to string (for table input)",
|
||||
)
|
||||
@ -158,7 +159,7 @@ pub fn action(
|
||||
}
|
||||
|
||||
fn format_int(int: i64) -> String {
|
||||
format!("{}", int)
|
||||
int.to_string()
|
||||
|
||||
// TODO once platform-specific dependencies are stable (see Cargo.toml)
|
||||
// #[cfg(windows)]
|
||||
@ -175,7 +176,7 @@ fn format_int(int: i64) -> String {
|
||||
}
|
||||
|
||||
fn format_bigint(int: &BigInt) -> String {
|
||||
format!("{}", int)
|
||||
int.to_string()
|
||||
|
||||
// TODO once platform-specific dependencies are stable (see Cargo.toml)
|
||||
// #[cfg(windows)]
|
||||
@ -229,7 +230,7 @@ fn format_decimal(mut decimal: BigDecimal, digits: Option<u64>, group_digits: bo
|
||||
let format_default_loc = |int_part: BigInt| {
|
||||
let loc = Locale::en;
|
||||
//TODO: when num_format is available for recent bigint, replace this with the locale-based format
|
||||
let (int_str, sep) = (format!("{}", int_part), String::from(loc.decimal()));
|
||||
let (int_str, sep) = (int_part.to_string(), String::from(loc.decimal()));
|
||||
|
||||
format!("{}{}{}", int_str, sep, dec_str)
|
||||
};
|
||||
|
@ -2,7 +2,7 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||
|
||||
pub struct Alias;
|
||||
|
||||
@ -15,7 +15,7 @@ impl WholeStreamCommand for Alias {
|
||||
Signature::build("alias")
|
||||
.required("name", SyntaxShape::String, "the name of the alias")
|
||||
.required("equals", SyntaxShape::String, "the equals sign")
|
||||
.rest(SyntaxShape::Any, "the expansion for the alias")
|
||||
.rest("rest", SyntaxShape::Any, "the expansion for the alias")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -35,6 +35,18 @@ impl WholeStreamCommand for Alias {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alias(_: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn alias(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
// TODO: is there a better way of checking whether no arguments were passed?
|
||||
if args.nth(0).is_none() {
|
||||
let aliases = UntaggedValue::string(
|
||||
&args
|
||||
.scope()
|
||||
.get_aliases()
|
||||
.iter()
|
||||
.map(|val| format!("{} = '{}'", val.0, val.1.iter().map(|x| &x.item).join(" ")))
|
||||
.join("\n"),
|
||||
);
|
||||
return Ok(OutputStream::one(aliases));
|
||||
}
|
||||
Ok(OutputStream::empty())
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ pub fn describe(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(args
|
||||
.input
|
||||
.map(|row| {
|
||||
let name = value::format_type(&row, 100);
|
||||
let name = value::plain_type(&row, 100);
|
||||
ReturnSuccess::value(
|
||||
UntaggedValue::string(name).into_value(Tag::unknown_anchor(row.tag.span)),
|
||||
)
|
||||
|
@ -27,7 +27,7 @@ impl WholeStreamCommand for Do {
|
||||
"ignore errors as the block runs",
|
||||
Some('i'),
|
||||
)
|
||||
.rest(SyntaxShape::Any, "the parameter(s) for the block")
|
||||
.rest("rest", SyntaxShape::Any, "the parameter(s) for the block")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -12,7 +12,7 @@ impl WholeStreamCommand for Echo {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("echo").rest(SyntaxShape::Any, "the values to echo")
|
||||
Signature::build("echo").rest("rest", SyntaxShape::Any, "the values to echo")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
109
crates/nu-command/src/commands/core_commands/error/make.rs
Normal file
109
crates/nu-command/src/commands/core_commands/error/make.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"error make"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("error make")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Create an error."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let input = args.input;
|
||||
|
||||
Ok(input
|
||||
.map(|value| {
|
||||
make_error(&value)
|
||||
.map(|err| UntaggedValue::Error(err).into_value(value.tag()))
|
||||
.unwrap_or_else(|| {
|
||||
UntaggedValue::Error(ShellError::untagged_runtime_error(
|
||||
"Creating error value not supported.",
|
||||
))
|
||||
.into_value(value.tag())
|
||||
})
|
||||
})
|
||||
.into_output_stream())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Creates a labeled error",
|
||||
example: r#"[
|
||||
[ msg, labels, span];
|
||||
["The message", "Helpful message here", ([[start, end]; [0, 141]])]
|
||||
] | error make"#,
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn make_error(value: &Value) -> Option<ShellError> {
|
||||
if let Value {
|
||||
value: UntaggedValue::Row(dict),
|
||||
..
|
||||
} = value
|
||||
{
|
||||
let msg = dict.get_data_by_key("msg".spanned_unknown());
|
||||
|
||||
let labels =
|
||||
dict.get_data_by_key("labels".spanned_unknown())
|
||||
.and_then(|table| match &table.value {
|
||||
UntaggedValue::Table(_) => table
|
||||
.table_entries()
|
||||
.map(|value| value.as_string().ok())
|
||||
.collect(),
|
||||
UntaggedValue::Primitive(Primitive::String(label)) => {
|
||||
Some(vec![label.to_string()])
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let _anchor = dict.get_data_by_key("tag".spanned_unknown());
|
||||
let span = dict.get_data_by_key("span".spanned_unknown());
|
||||
|
||||
if msg.is_none() || labels.is_none() || span.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let msg = msg.and_then(|msg| msg.as_string().ok());
|
||||
|
||||
if let Some(labels) = labels {
|
||||
if labels.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some(ShellError::labeled_error(
|
||||
msg.expect("Message will always be present."),
|
||||
&labels[0],
|
||||
span.map(|data| match data {
|
||||
Value {
|
||||
value: UntaggedValue::Row(vals),
|
||||
..
|
||||
} => match (vals.entries.get("start"), vals.entries.get("end")) {
|
||||
(Some(start), Some(end)) => {
|
||||
let start = start.as_usize().ok().unwrap_or(0);
|
||||
let end = end.as_usize().ok().unwrap_or(0);
|
||||
|
||||
Span::new(start, end)
|
||||
}
|
||||
(_, _) => Span::unknown(),
|
||||
},
|
||||
_ => Span::unknown(),
|
||||
})
|
||||
.unwrap_or_else(Span::unknown),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
mod make;
|
||||
|
||||
pub use make::SubCommand as ErrorMake;
|
110
crates/nu-command/src/commands/core_commands/find.rs
Normal file
110
crates/nu-command/src/commands/core_commands/find.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct Find;
|
||||
|
||||
impl WholeStreamCommand for Find {
|
||||
fn name(&self) -> &str {
|
||||
"find"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("find").rest("rest", SyntaxShape::String, "search term")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Find text in the output of a previous command"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
find(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Search pipeline output for multiple terms",
|
||||
example: r#"ls | find toml md sh"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Search strings for term(s)",
|
||||
example: r#"echo Cargo.toml | find toml"#,
|
||||
result: Some(vec![Value::from("Cargo.toml")]),
|
||||
},
|
||||
Example {
|
||||
description: "Search a number list for term(s)",
|
||||
example: r#"[1 2 3 4 5] | find 5"#,
|
||||
result: Some(vec![UntaggedValue::int(5).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Search string list for term(s)",
|
||||
example: r#"[moe larry curly] | find l"#,
|
||||
result: Some(vec![Value::from("larry"), Value::from("curly")]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn row_contains(row: &Dictionary, search_terms: Vec<String>) -> bool {
|
||||
for term in search_terms {
|
||||
for (k, v) in &row.entries {
|
||||
let key = k.to_string().trim().to_lowercase();
|
||||
let value = v.convert_to_string().trim().to_lowercase();
|
||||
if key.contains(&term) || value.contains(&term) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn find(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let rest: Vec<Value> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.filter(move |row| match &row.value {
|
||||
UntaggedValue::Row(row) => {
|
||||
let sterms: Vec<String> = rest
|
||||
.iter()
|
||||
.map(|t| t.convert_to_string().trim().to_lowercase())
|
||||
.collect();
|
||||
row_contains(row, sterms)
|
||||
}
|
||||
UntaggedValue::Primitive(_p) => {
|
||||
// eprint!("prim {}", p.type_name());
|
||||
let sterms: Vec<String> = rest
|
||||
.iter()
|
||||
.map(|t| t.convert_to_string().trim().to_lowercase())
|
||||
.collect();
|
||||
|
||||
let prim_string = &row.convert_to_string().trim().to_lowercase();
|
||||
for term in sterms {
|
||||
if prim_string.contains(&term) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
.into_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Find;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Find {})
|
||||
}
|
||||
}
|
@ -1,14 +1,12 @@
|
||||
use crate::prelude::*;
|
||||
use crate::TaggedListBuilder;
|
||||
use nu_engine::documentation::generate_docs;
|
||||
use nu_engine::{Command, WholeStreamCommand};
|
||||
use nu_engine::{documentation::generate_docs, Command, WholeStreamCommand};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder,
|
||||
UntaggedValue, Value,
|
||||
Dictionary, NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape,
|
||||
TaggedDictBuilder, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tag;
|
||||
use nu_source::{SpannedItem, Tagged};
|
||||
use nu_source::{SpannedItem, Tag, Tagged};
|
||||
use nu_value_ext::ValueExt;
|
||||
|
||||
pub struct Help;
|
||||
@ -19,7 +17,18 @@ impl WholeStreamCommand for Help {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("help").rest(SyntaxShape::String, "the name of command to get help on")
|
||||
Signature::build("help")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::String,
|
||||
"the name of command to get help on",
|
||||
)
|
||||
.named(
|
||||
"find",
|
||||
SyntaxShape::String,
|
||||
"string to find in command usage",
|
||||
Some('f'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -29,14 +38,81 @@ impl WholeStreamCommand for Help {
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
help(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "show all commands and sub-commands",
|
||||
example: "help commands",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "generate documentation",
|
||||
example: "help generate_docs",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "show help for single command",
|
||||
example: "help match",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "show help for single sub-command",
|
||||
example: "help str lpad",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "search for string in command usage",
|
||||
example: "help --find char",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn help(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let scope = args.scope().clone();
|
||||
|
||||
let find: Option<Tagged<String>> = args.get_flag("find")?;
|
||||
let rest: Vec<Tagged<String>> = args.rest(0)?;
|
||||
|
||||
if let Some(f) = find {
|
||||
let search_string = f.item;
|
||||
let full_commands = scope.get_commands_info();
|
||||
let mut found_cmds_vec = Vec::new();
|
||||
|
||||
for (key, cmd) in full_commands {
|
||||
let mut indexmap = IndexMap::new();
|
||||
|
||||
let c = cmd.usage().to_string();
|
||||
let e = cmd.extra_usage().to_string();
|
||||
if key.to_lowercase().contains(&search_string)
|
||||
|| c.to_lowercase().contains(&search_string)
|
||||
|| e.to_lowercase().contains(&search_string)
|
||||
{
|
||||
indexmap.insert(
|
||||
"name".to_string(),
|
||||
UntaggedValue::string(key).into_value(&name),
|
||||
);
|
||||
|
||||
indexmap.insert(
|
||||
"usage".to_string(),
|
||||
UntaggedValue::string(cmd.usage().to_string()).into_value(&name),
|
||||
);
|
||||
|
||||
indexmap.insert(
|
||||
"extra_usage".to_string(),
|
||||
UntaggedValue::string(cmd.extra_usage().to_string()).into_value(&name),
|
||||
);
|
||||
|
||||
found_cmds_vec
|
||||
.push(UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&name));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(found_cmds_vec.into_iter().into_action_stream());
|
||||
}
|
||||
|
||||
if !rest.is_empty() {
|
||||
if rest[0].item == "commands" {
|
||||
let mut sorted_names = scope.get_command_names();
|
||||
@ -226,7 +302,7 @@ pub fn signature_dict(signature: Signature, tag: impl Into<Tag>) -> Value {
|
||||
let tag = tag.into();
|
||||
let mut sig = TaggedListBuilder::new(&tag);
|
||||
|
||||
for arg in signature.positional.iter() {
|
||||
for arg in &signature.positional {
|
||||
let is_required = matches!(arg.0, PositionalType::Mandatory(_, _));
|
||||
|
||||
sig.push_value(for_spec(arg.0.name(), "argument", is_required, &tag));
|
||||
@ -237,7 +313,7 @@ pub fn signature_dict(signature: Signature, tag: impl Into<Tag>) -> Value {
|
||||
sig.push_value(for_spec("rest", "argument", is_required, &tag));
|
||||
}
|
||||
|
||||
for (name, ty) in signature.named.iter() {
|
||||
for (name, ty) in &signature.named {
|
||||
match ty.0 {
|
||||
NamedType::Mandatory(_, _) => sig.push_value(for_spec(name, "flag", true, &tag)),
|
||||
NamedType::Optional(_, _) => sig.push_value(for_spec(name, "flag", false, &tag)),
|
||||
|
@ -100,18 +100,15 @@ fn if_command(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
context.scope.add_vars(&condition.captured.entries);
|
||||
|
||||
//FIXME: should we use the scope that's brought in as well?
|
||||
let condition = evaluate_baseline_expr(cond, &*context);
|
||||
match condition {
|
||||
let condition = evaluate_baseline_expr(cond, &context);
|
||||
let result = match condition {
|
||||
Ok(condition) => match condition.as_bool() {
|
||||
Ok(b) => {
|
||||
let result = if b {
|
||||
run_block(&then_case.block, &*context, input, external_redirection)
|
||||
if b {
|
||||
run_block(&then_case.block, &context, input, external_redirection)
|
||||
} else {
|
||||
run_block(&else_case.block, &*context, input, external_redirection)
|
||||
};
|
||||
context.scope.exit_scope();
|
||||
|
||||
result
|
||||
run_block(&else_case.block, &context, input, external_redirection)
|
||||
}
|
||||
}
|
||||
Err(e) => Ok(OutputStream::from_stream(
|
||||
vec![UntaggedValue::Error(e).into_untagged_value()].into_iter(),
|
||||
@ -120,13 +117,16 @@ fn if_command(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Err(e) => Ok(OutputStream::from_stream(
|
||||
vec![UntaggedValue::Error(e).into_untagged_value()].into_iter(),
|
||||
)),
|
||||
}
|
||||
};
|
||||
context.scope.exit_scope();
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::If;
|
||||
use super::ShellError;
|
||||
use nu_test_support::nu;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
@ -134,4 +134,21 @@ mod tests {
|
||||
|
||||
test_examples(If {})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_doesnt_leak_on_error() {
|
||||
let actual = nu!(
|
||||
".",
|
||||
r#"
|
||||
def test-leak [] {
|
||||
let var = "hello"
|
||||
if 0 == "" {echo ok} {echo not}
|
||||
}
|
||||
test-leak
|
||||
echo $var
|
||||
"#
|
||||
);
|
||||
|
||||
assert!(actual.err.contains("unknown variable"));
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ pub fn letcmd(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
.expect("Internal error: type checker should require args");
|
||||
|
||||
let var_name = positional[0].var_name()?;
|
||||
let rhs_raw = evaluate_baseline_expr(&positional[2], &ctx)?;
|
||||
let rhs_raw = evaluate_baseline_expr(&positional[2], ctx)?;
|
||||
let tag: Tag = positional[2].span.into();
|
||||
|
||||
let rhs: CapturedBlock = FromValue::from_value(&rhs_raw)?;
|
||||
@ -98,7 +98,7 @@ pub fn letcmd(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
};
|
||||
|
||||
ctx.scope.enter_scope();
|
||||
let value = evaluate_baseline_expr(expr, &ctx);
|
||||
let value = evaluate_baseline_expr(expr, ctx);
|
||||
ctx.scope.exit_scope();
|
||||
|
||||
let value = value?;
|
||||
|
@ -4,6 +4,8 @@ mod def;
|
||||
mod describe;
|
||||
mod do_;
|
||||
pub(crate) mod echo;
|
||||
mod error;
|
||||
mod find;
|
||||
mod help;
|
||||
mod history;
|
||||
mod if_;
|
||||
@ -13,6 +15,8 @@ mod nu_plugin;
|
||||
mod nu_signature;
|
||||
mod source;
|
||||
mod tags;
|
||||
mod tutor;
|
||||
mod unalias;
|
||||
mod version;
|
||||
|
||||
pub use self::nu_plugin::SubCommand as NuPlugin;
|
||||
@ -25,6 +29,8 @@ pub use def::Def;
|
||||
pub use describe::Describe;
|
||||
pub use do_::Do;
|
||||
pub use echo::Echo;
|
||||
pub use error::*;
|
||||
pub use find::Find;
|
||||
pub use help::Help;
|
||||
pub use history::History;
|
||||
pub use if_::If;
|
||||
@ -32,4 +38,6 @@ pub use ignore::Ignore;
|
||||
pub use let_::Let;
|
||||
pub use source::Source;
|
||||
pub use tags::Tags;
|
||||
pub use tutor::Tutor;
|
||||
pub use unalias::Unalias;
|
||||
pub use version::{version, Version};
|
||||
|
@ -4,7 +4,7 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_path::canonicalize;
|
||||
use nu_path::canonicalize_with;
|
||||
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
|
||||
@ -56,7 +56,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
tag,
|
||||
}) = load_path
|
||||
{
|
||||
let path = canonicalize(shell_manager.path(), load_path).map_err(|_| {
|
||||
let path = canonicalize_with(load_path, shell_manager.path()).map_err(|_| {
|
||||
ShellError::labeled_error(
|
||||
"Cannot load plugins from directory",
|
||||
"directory not found",
|
||||
|
@ -14,6 +14,7 @@ impl WholeStreamCommand for Command {
|
||||
.switch("stdin", "redirect stdin", None)
|
||||
.switch("skip-plugins", "do not load plugins", None)
|
||||
.switch("no-history", "don't save history", None)
|
||||
.switch("perf", "show startup performance metrics", None)
|
||||
.named(
|
||||
"commands",
|
||||
SyntaxShape::String,
|
||||
@ -40,7 +41,7 @@ impl WholeStreamCommand for Command {
|
||||
"custom configuration source file",
|
||||
None,
|
||||
)
|
||||
.rest(SyntaxShape::String, "source file(s) to run")
|
||||
.rest("rest", SyntaxShape::String, "source file(s) to run")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -2,11 +2,11 @@ use crate::prelude::*;
|
||||
use nu_engine::{script, WholeStreamCommand};
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_path::expand_path;
|
||||
use nu_path::{canonicalize, canonicalize_with};
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
|
||||
use std::{borrow::Cow, path::Path};
|
||||
use std::path::Path;
|
||||
|
||||
pub struct Source;
|
||||
|
||||
@ -32,7 +32,7 @@ impl WholeStreamCommand for Source {
|
||||
"Runs a script file in the current context."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
source(args)
|
||||
}
|
||||
|
||||
@ -41,31 +41,83 @@ impl WholeStreamCommand for Source {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
pub fn source(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let ctx = &args.context;
|
||||
let filename: Tagged<String> = args.req(0)?;
|
||||
|
||||
let source_file = Path::new(&filename.item);
|
||||
|
||||
// Note: this is a special case for setting the context from a command
|
||||
// In this case, if we don't set it now, we'll lose the scope that this
|
||||
// variable should be set into.
|
||||
let contents = std::fs::read_to_string(&expand_path(Cow::Borrowed(Path::new(&filename.item))));
|
||||
|
||||
let lib_dirs = &ctx
|
||||
.configs()
|
||||
.lock()
|
||||
.global_config
|
||||
.as_ref()
|
||||
.map(|configuration| match configuration.var("lib_dirs") {
|
||||
Some(paths) => paths
|
||||
.table_entries()
|
||||
.cloned()
|
||||
.map(|path| path.as_string())
|
||||
.collect(),
|
||||
None => vec![],
|
||||
});
|
||||
|
||||
if let Some(dir) = lib_dirs {
|
||||
for lib_path in dir {
|
||||
match lib_path {
|
||||
Ok(name) => {
|
||||
let path = if let Ok(p) = canonicalize_with(&source_file, name) {
|
||||
p
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Ok(contents) = std::fs::read_to_string(path) {
|
||||
let result = script::run_script_standalone(contents, true, ctx, false);
|
||||
|
||||
if let Err(err) = result {
|
||||
ctx.error(err);
|
||||
}
|
||||
return Ok(OutputStream::empty());
|
||||
}
|
||||
}
|
||||
Err(reason) => {
|
||||
ctx.error(reason.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let path = canonicalize(source_file).map_err(|e| {
|
||||
ShellError::labeled_error(
|
||||
format!("Can't load source file. Reason: {}", e.to_string()),
|
||||
"Can't load this file",
|
||||
filename.span(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let contents = std::fs::read_to_string(path);
|
||||
|
||||
match contents {
|
||||
Ok(contents) => {
|
||||
let result = script::run_script_standalone(contents, true, &ctx, false);
|
||||
let result = script::run_script_standalone(contents, true, ctx, false);
|
||||
|
||||
if let Err(err) = result {
|
||||
ctx.error(err);
|
||||
}
|
||||
Ok(ActionStream::empty())
|
||||
Ok(OutputStream::empty())
|
||||
}
|
||||
Err(_) => {
|
||||
Err(e) => {
|
||||
ctx.error(ShellError::labeled_error(
|
||||
"Can't load file to source",
|
||||
"can't load file",
|
||||
format!("Can't load source file. Reason: {}", e.to_string()),
|
||||
"Can't load this file",
|
||||
filename.span(),
|
||||
));
|
||||
|
||||
Ok(ActionStream::empty())
|
||||
Ok(OutputStream::empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
|
||||
pub struct Tags;
|
||||
|
||||
@ -18,37 +18,64 @@ impl WholeStreamCommand for Tags {
|
||||
"Read the tags (metadata) for values."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(tags(args))
|
||||
}
|
||||
}
|
||||
|
||||
fn tags(args: CommandArgs) -> ActionStream {
|
||||
args.input
|
||||
.map(move |v| {
|
||||
let mut tags = TaggedDictBuilder::new(v.tag());
|
||||
{
|
||||
let anchor = v.anchor();
|
||||
let span = v.tag.span;
|
||||
let mut dict = TaggedDictBuilder::new(v.tag());
|
||||
dict.insert_untagged("start", UntaggedValue::int(span.start() as i64));
|
||||
dict.insert_untagged("end", UntaggedValue::int(span.end() as i64));
|
||||
tags.insert_value("span", dict.into_value());
|
||||
fn build_tag_table(tag: impl Into<Tag>) -> Value {
|
||||
let tag = tag.into();
|
||||
let span = tag.span;
|
||||
|
||||
match anchor {
|
||||
Some(AnchorLocation::File(source)) => {
|
||||
tags.insert_untagged("anchor", UntaggedValue::string(source));
|
||||
}
|
||||
Some(AnchorLocation::Url(source)) => {
|
||||
tags.insert_untagged("anchor", UntaggedValue::string(source));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
TaggedDictBuilder::build(tag.clone(), |tags| {
|
||||
if let Some(anchor) = anchor_as_value(&tag) {
|
||||
tags.insert_value("anchor", anchor);
|
||||
}
|
||||
|
||||
tags.insert_value(
|
||||
"span",
|
||||
TaggedDictBuilder::build(tag.clone(), |span_dict| {
|
||||
span_dict.insert_untagged("start", UntaggedValue::int(span.start() as i64));
|
||||
span_dict.insert_untagged("end", UntaggedValue::int(span.end() as i64));
|
||||
}),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
fn tags(args: CommandArgs) -> OutputStream {
|
||||
if args.input.is_empty() {
|
||||
OutputStream::one(build_tag_table(&args.name_tag()))
|
||||
} else {
|
||||
args.input
|
||||
.map(move |v| build_tag_table(v.tag()))
|
||||
.into_output_stream()
|
||||
}
|
||||
}
|
||||
|
||||
fn anchor_as_value(tag: &Tag) -> Option<Value> {
|
||||
let anchor = tag.anchor.as_ref();
|
||||
|
||||
anchor.as_ref()?;
|
||||
|
||||
Some(TaggedDictBuilder::build(tag, |table| {
|
||||
let value = match anchor {
|
||||
Some(AnchorLocation::File(path)) => {
|
||||
Some(("file", UntaggedValue::from(path.to_string())))
|
||||
}
|
||||
Some(AnchorLocation::Url(destination)) => {
|
||||
Some(("url", UntaggedValue::from(destination.to_string())))
|
||||
}
|
||||
Some(AnchorLocation::Source(text)) => Some((
|
||||
"source",
|
||||
UntaggedValue::Primitive(Primitive::String(text.to_string())),
|
||||
)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
tags.into_value()
|
||||
})
|
||||
.into_action_stream()
|
||||
if let Some((key, value)) = value {
|
||||
table.insert_untagged(key, value);
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
407
crates/nu-command/src/commands/core_commands/tutor.rs
Normal file
407
crates/nu-command/src/commands/core_commands/tutor.rs
Normal file
@ -0,0 +1,407 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||
|
||||
pub struct Tutor;
|
||||
|
||||
impl WholeStreamCommand for Tutor {
|
||||
fn name(&self) -> &str {
|
||||
"tutor"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("tutor")
|
||||
.optional(
|
||||
"search",
|
||||
SyntaxShape::String,
|
||||
"item to search for, or 'list' to list available tutorials",
|
||||
)
|
||||
.named(
|
||||
"find",
|
||||
SyntaxShape::String,
|
||||
"Search tutorial for a phrase",
|
||||
Some('f'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Run the tutorial. To begin, run: tutor"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
tutor(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Begin the tutorial",
|
||||
example: "tutor begin",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Search a tutorial by phrase",
|
||||
example: "tutor -f \"$in\"",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn tutor(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.name_tag();
|
||||
let scope = args.scope().clone();
|
||||
|
||||
let search: Option<String> = args.opt(0).unwrap_or(None);
|
||||
let find: Option<String> = args.get_flag("find")?;
|
||||
|
||||
let search_space = [
|
||||
(vec!["begin"], begin_tutor()),
|
||||
(
|
||||
vec!["table", "tables", "row", "rows", "column", "columns"],
|
||||
table_tutor(),
|
||||
),
|
||||
(vec!["cell", "cells"], cell_tutor()),
|
||||
(
|
||||
vec![
|
||||
"expr",
|
||||
"exprs",
|
||||
"expressions",
|
||||
"subexpression",
|
||||
"subexpressions",
|
||||
"sub-expression",
|
||||
"sub-expressions",
|
||||
],
|
||||
expression_tutor(),
|
||||
),
|
||||
(vec!["echo"], echo_tutor()),
|
||||
(vec!["each", "iteration", "iter"], each_tutor()),
|
||||
(
|
||||
vec!["var", "vars", "variable", "variables"],
|
||||
variable_tutor(),
|
||||
),
|
||||
(vec!["block", "blocks"], block_tutor()),
|
||||
(vec!["shorthand", "shorthands"], shorthand_tutor()),
|
||||
];
|
||||
|
||||
if let Some(find) = find {
|
||||
let mut results = vec![];
|
||||
for search_group in search_space {
|
||||
if search_group.1.contains(&find) {
|
||||
results.push(search_group.0[0].to_string())
|
||||
}
|
||||
}
|
||||
|
||||
let message = format!("You can find '{}' in the following topics:\n{}\n\nYou can learn about a topic using `tutor` followed by the name of the topic.\nFor example: `tutor table` to open the table topic.\n\n",
|
||||
find,
|
||||
results.into_iter().map(|x| format!("- {}", x)).join("\n")
|
||||
);
|
||||
|
||||
return Ok(display(tag, &scope, &message));
|
||||
} else if let Some(search) = search {
|
||||
for search_group in search_space {
|
||||
if search_group.0.contains(&search.as_str()) {
|
||||
return Ok(display(tag, &scope, search_group.1));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(display(tag, &scope, default_tutor()))
|
||||
}
|
||||
|
||||
fn default_tutor() -> &'static str {
|
||||
r#"
|
||||
Welcome to the Nushell tutorial!
|
||||
|
||||
With the `tutor` command, you'll be able to learn a lot about how Nushell
|
||||
works along with many fun tips and tricks to speed up everyday tasks.
|
||||
|
||||
To get started, you can use `tutor begin`.
|
||||
|
||||
"#
|
||||
}
|
||||
|
||||
fn begin_tutor() -> &'static str {
|
||||
r#"
|
||||
Nushell is a structured shell and programming language. One way to begin
|
||||
using it is to try a few of the commands.
|
||||
|
||||
The first command to try is `ls`. The `ls` command will show you a list
|
||||
of the files in the current directory. Notice that these files are shown
|
||||
as a table. Each column of this table not only tells us what is being
|
||||
shown, but also gives us a way to work with the data.
|
||||
|
||||
You can combine the `ls` command with other commands using the pipeline
|
||||
symbol '|'. This allows data to flow from one command to the next.
|
||||
|
||||
For example, if we only wanted the name column, we could do:
|
||||
```
|
||||
ls | select name
|
||||
```
|
||||
Notice that we still get a table, but this time it only has one column:
|
||||
the name column.
|
||||
|
||||
You can continue to learn more about tables by running:
|
||||
```
|
||||
tutor tables
|
||||
```
|
||||
If at any point, you'd like to restart this tutorial, you can run:
|
||||
```
|
||||
tutor begin
|
||||
```
|
||||
"#
|
||||
}
|
||||
|
||||
fn table_tutor() -> &'static str {
|
||||
r#"
|
||||
The most common form of data in Nushell is the table. Tables contain rows and
|
||||
columns of data. In each cell of the table, there is data that you can access
|
||||
using Nushell commands.
|
||||
|
||||
To get the 3rd row in the table, you can use the `nth` command:
|
||||
```
|
||||
ls | nth 2
|
||||
```
|
||||
This will get the 3rd (note that `nth` is zero-based) row in the table created
|
||||
by the `ls` command. You can use `nth` on any table created by other commands
|
||||
as well.
|
||||
|
||||
You can also access the column of data in one of two ways. If you want
|
||||
to keep the column as part of a new table, you can use `select`.
|
||||
```
|
||||
ls | select name
|
||||
```
|
||||
This runs `ls` and returns only the "name" column of the table.
|
||||
|
||||
If, instead, you'd like to get access to the values inside of the column, you
|
||||
can use the `get` command.
|
||||
```
|
||||
ls | get name
|
||||
```
|
||||
This allows us to get to the list of strings that are the filenames rather
|
||||
than having a full table. In some cases, this can make the names easier to
|
||||
work with.
|
||||
|
||||
You can continue to learn more about working with cells of the table by
|
||||
running:
|
||||
```
|
||||
tutor cells
|
||||
```
|
||||
"#
|
||||
}
|
||||
|
||||
fn cell_tutor() -> &'static str {
|
||||
r#"
|
||||
Working with cells of data in the table is a key part of working with data in
|
||||
Nushell. Because of this, there is a rich list of commands to work with cells
|
||||
as well as handy shorthands for accessing cells.
|
||||
|
||||
Cells can hold simple values like strings and numbers, or more complex values
|
||||
like lists and tables.
|
||||
|
||||
To reach a cell of data from a table, you can combine a row operation and a
|
||||
column operation.
|
||||
```
|
||||
ls | nth 4 | get name
|
||||
```
|
||||
You can combine these operations into one step using a shortcut.
|
||||
```
|
||||
(ls).4.name
|
||||
```
|
||||
Names/strings represent columns names and numbers represent row numbers.
|
||||
|
||||
The `(ls)` is a form of expression. You can continue to learn more about
|
||||
expressions by running:
|
||||
```
|
||||
tutor expressions
|
||||
```
|
||||
You can also learn about these cell shorthands by running:
|
||||
```
|
||||
tutor shorthands
|
||||
```
|
||||
"#
|
||||
}
|
||||
|
||||
fn expression_tutor() -> &'static str {
|
||||
r#"
|
||||
Expressions give you the power to mix calls to commands with math. The
|
||||
simplest expression is a single value like a string or number.
|
||||
```
|
||||
3
|
||||
```
|
||||
Expressions can also include math operations like addition or division.
|
||||
```
|
||||
10 / 2
|
||||
```
|
||||
Normally, an expression is one type of operation: math or commands. You can
|
||||
mix these types by using subexpressions. Subexpressions are just like
|
||||
expressions, but they're wrapped in parentheses `()`.
|
||||
```
|
||||
10 * (3 + 4)
|
||||
```
|
||||
Here we use parentheses to create a higher math precedence in the math
|
||||
expression.
|
||||
```
|
||||
echo (2 + 3)
|
||||
```
|
||||
You can continue to learn more about the `echo` command by running:
|
||||
```
|
||||
tutor echo
|
||||
```
|
||||
"#
|
||||
}
|
||||
|
||||
fn echo_tutor() -> &'static str {
|
||||
r#"
|
||||
The `echo` command in Nushell is a powerful tool for not only seeing values,
|
||||
but also for creating new ones.
|
||||
```
|
||||
echo "Hello"
|
||||
```
|
||||
You can echo output. This output, if it's not redirected using a "|" pipeline
|
||||
will be displayed to the screen.
|
||||
```
|
||||
echo 1..10
|
||||
```
|
||||
You can also use echo to work with individual values of a range. In this
|
||||
example, `echo` will create the values from 1 to 10 as a list.
|
||||
```
|
||||
echo 1 2 3 4 5
|
||||
```
|
||||
You can also create lists of values by passing `echo` multiple arguments.
|
||||
This can be helpful if you want to later processes these values.
|
||||
|
||||
The `echo` command can pair well with the `each` command which can run
|
||||
code on each row, or item, of input.
|
||||
|
||||
You can continue to learn more about the `each` command by running:
|
||||
```
|
||||
tutor each
|
||||
```
|
||||
"#
|
||||
}
|
||||
|
||||
fn each_tutor() -> &'static str {
|
||||
r#"
|
||||
The `each` command gives us a way of working with each individual row or
|
||||
element of a list one at a time. It reads these in from the pipeline and
|
||||
runs a block on each element. A block is a group of pipelines.
|
||||
```
|
||||
echo 1 2 3 | each { $it + 10}
|
||||
```
|
||||
This example iterates over each element sent by `echo`, giving us three new
|
||||
values that are the original value + 10. Here, the `$it` is a variable that
|
||||
is the name given to the block's parameter by default.
|
||||
|
||||
You can learn more about blocks by running:
|
||||
```
|
||||
tutor blocks
|
||||
```
|
||||
You can also learn more about variables by running:
|
||||
```
|
||||
tutor variables
|
||||
```
|
||||
"#
|
||||
}
|
||||
|
||||
fn variable_tutor() -> &'static str {
|
||||
r#"
|
||||
Variables are an important way to store values to be used later. To create a
|
||||
variable, you can use the `let` keyword. The `let` command will create a
|
||||
variable and then assign it a value in one step.
|
||||
```
|
||||
let $x = 3
|
||||
```
|
||||
Once created, we can refer to this variable by name.
|
||||
```
|
||||
$x
|
||||
```
|
||||
Nushell also comes with built-in variables. The `$nu` variable is a reserved
|
||||
variable that contains a lot of information about the currently running
|
||||
instance of Nushell. The `$it` variable is the name given to block parameters
|
||||
if you don't specify one. And `$in` is the variable that allows you to work
|
||||
with all of the data coming in from the pipeline in one place.
|
||||
|
||||
"#
|
||||
}
|
||||
|
||||
fn block_tutor() -> &'static str {
|
||||
r#"
|
||||
Blocks are a special form of expression that hold code to be run at a later
|
||||
time. Often, you'll see blocks as one of the arguments given to commands
|
||||
like `each` and `if`.
|
||||
```
|
||||
ls | each {|x| $x.name}
|
||||
```
|
||||
The above will create a list of the filenames in the directory.
|
||||
```
|
||||
if $true { echo "it's true" } { echo "it's not true" }
|
||||
```
|
||||
This `if` call will run the first block if the expression is true, or the
|
||||
second block if the expression is false.
|
||||
|
||||
"#
|
||||
}
|
||||
|
||||
fn shorthand_tutor() -> &'static str {
|
||||
r#"
|
||||
You can access cells in a table using a shorthand notation sometimes called a
|
||||
"column path" or "cell path". These paths allow you to go from a table to
|
||||
rows, columns, or cells inside of the table.
|
||||
|
||||
Shorthand paths are made from rows numbers, column names, or both. You can use
|
||||
them on any variable or subexpression.
|
||||
```
|
||||
$nu.cwd
|
||||
```
|
||||
The above accesses the built-in `$nu` variable, gets its table, and then uses
|
||||
the shorthand path to retrieve only the cell data inside the "cwd" column.
|
||||
```
|
||||
(ls).name.4
|
||||
```
|
||||
This will retrieve the cell data in the "name" column on the 5th row (note:
|
||||
row numbers are zero-based).
|
||||
|
||||
Rows and columns don't need to come in any specific order. You can get the
|
||||
same value using:
|
||||
```
|
||||
(ls).4.name
|
||||
```
|
||||
"#
|
||||
}
|
||||
|
||||
fn display(tag: Tag, scope: &Scope, help: &str) -> OutputStream {
|
||||
let help = help.split('`');
|
||||
|
||||
let mut build = String::new();
|
||||
let mut code_mode = false;
|
||||
let palette = nu_engine::DefaultPalette {};
|
||||
|
||||
for item in help {
|
||||
if code_mode {
|
||||
code_mode = false;
|
||||
|
||||
//TODO: support no-color mode
|
||||
let colored_example = nu_engine::Painter::paint_string(item, scope, &palette);
|
||||
build.push_str(&colored_example);
|
||||
} else {
|
||||
code_mode = true;
|
||||
build.push_str(item);
|
||||
}
|
||||
}
|
||||
|
||||
OutputStream::one(UntaggedValue::string(build).into_value(tag))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::Tutor;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Tutor {})
|
||||
}
|
||||
}
|
37
crates/nu-command/src/commands/core_commands/unalias.rs
Normal file
37
crates/nu-command/src/commands/core_commands/unalias.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
|
||||
pub struct Unalias;
|
||||
|
||||
impl WholeStreamCommand for Unalias {
|
||||
fn name(&self) -> &str {
|
||||
"unalias"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("unalias").required("name", SyntaxShape::String, "the name of the alias")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Removes an alias"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
unalias(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Remove the 'v' alias",
|
||||
example: "unalias v",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unalias(_: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::empty())
|
||||
}
|
@ -2,14 +2,11 @@ use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
dataframe::{Column, FrameStruct, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use polars::{
|
||||
frame::groupby::GroupBy,
|
||||
prelude::{DataType, PolarsError, Series},
|
||||
};
|
||||
use polars::{frame::groupby::GroupBy, prelude::PolarsError};
|
||||
|
||||
enum Operation {
|
||||
Mean,
|
||||
@ -111,17 +108,40 @@ impl WholeStreamCommand for DataFrame {
|
||||
description: "Aggregate sum by grouping by column a and summing on col b",
|
||||
example:
|
||||
"[[a b]; [one 1] [one 2]] | dataframe to-df | dataframe group-by a | dataframe aggregate sum",
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("a".to_string(), vec![UntaggedValue::string("one").into()]),
|
||||
Column::new("b".to_string(), vec![UntaggedValue::int(3).into()]),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Aggregate sum in dataframe columns",
|
||||
example: "[[a b]; [4 1] [5 2]] | dataframe to-df | dataframe aggregate sum",
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("a".to_string(), vec![UntaggedValue::int(9).into()]),
|
||||
Column::new("b".to_string(), vec![UntaggedValue::int(3).into()]),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Aggregate sum in series",
|
||||
example: "[4 1 5 6] | dataframe to-series | dataframe aggregate sum",
|
||||
result: None,
|
||||
example: "[4 1 5 6] | dataframe to-df | dataframe aggregate sum",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("0".to_string(), vec![UntaggedValue::int(16).into()]),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -139,7 +159,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
})?;
|
||||
|
||||
match value.value {
|
||||
UntaggedValue::DataFrame(PolarsData::GroupBy(nu_groupby)) => {
|
||||
UntaggedValue::FrameStruct(FrameStruct::GroupBy(nu_groupby)) => {
|
||||
let groupby = nu_groupby.to_groupby()?;
|
||||
|
||||
let res = perform_groupby_aggregation(
|
||||
@ -152,18 +172,13 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)) => {
|
||||
UntaggedValue::DataFrame(df) => {
|
||||
let df = df.as_ref();
|
||||
|
||||
let res = perform_dataframe_aggregation(&df, op, &operation.tag)?;
|
||||
let res = perform_dataframe_aggregation(df, op, &operation.tag)?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
UntaggedValue::DataFrame(PolarsData::Series(series)) => {
|
||||
let value = perform_series_aggregation(series.as_ref(), op, &operation.tag)?;
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"No groupby, dataframe or series in stream",
|
||||
"no groupby, dataframe or series found in input stream",
|
||||
@ -230,7 +245,7 @@ fn perform_groupby_aggregation(
|
||||
None => &col[..],
|
||||
};
|
||||
|
||||
res.rename(col.as_str(), new_col)
|
||||
res.rename(&col, new_col)
|
||||
.expect("Column is always there. Looping with known names");
|
||||
}
|
||||
}
|
||||
@ -264,162 +279,15 @@ fn perform_dataframe_aggregation(
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_series_aggregation(
|
||||
series: &Series,
|
||||
operation: Operation,
|
||||
operation_tag: &Tag,
|
||||
) -> Result<Value, ShellError> {
|
||||
match operation {
|
||||
Operation::Mean => {
|
||||
let res = match series.mean() {
|
||||
Some(val) => UntaggedValue::Primitive(val.into()),
|
||||
None => UntaggedValue::Primitive(0.into()),
|
||||
};
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
let value = Value {
|
||||
value: res,
|
||||
tag: operation_tag.clone(),
|
||||
};
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
let mut data = TaggedDictBuilder::new(operation_tag.clone());
|
||||
data.insert_value(series.name(), value);
|
||||
|
||||
Ok(data.into_value())
|
||||
}
|
||||
Operation::Median => {
|
||||
let res = match series.median() {
|
||||
Some(val) => UntaggedValue::Primitive(val.into()),
|
||||
None => UntaggedValue::Primitive(0.into()),
|
||||
};
|
||||
|
||||
let value = Value {
|
||||
value: res,
|
||||
tag: operation_tag.clone(),
|
||||
};
|
||||
|
||||
let mut data = TaggedDictBuilder::new(operation_tag.clone());
|
||||
data.insert_value(series.name(), value);
|
||||
|
||||
Ok(data.into_value())
|
||||
}
|
||||
Operation::Sum => {
|
||||
let untagged = match series.dtype() {
|
||||
DataType::Int8
|
||||
| DataType::Int16
|
||||
| DataType::Int32
|
||||
| DataType::Int64
|
||||
| DataType::UInt8
|
||||
| DataType::UInt16
|
||||
| DataType::UInt32
|
||||
| DataType::UInt64 => {
|
||||
let res: i64 = series.sum().unwrap_or(0);
|
||||
Ok(UntaggedValue::Primitive(res.into()))
|
||||
}
|
||||
DataType::Float32 | DataType::Float64 => {
|
||||
let res: f64 = series.sum().unwrap_or(0.0);
|
||||
Ok(UntaggedValue::Primitive(res.into()))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Not valid type",
|
||||
format!(
|
||||
"this operation can not be performed with series of type {}",
|
||||
series.dtype()
|
||||
),
|
||||
&operation_tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let value = Value {
|
||||
value: untagged,
|
||||
tag: operation_tag.clone(),
|
||||
};
|
||||
|
||||
let mut data = TaggedDictBuilder::new(operation_tag.clone());
|
||||
data.insert_value(series.name(), value);
|
||||
|
||||
Ok(data.into_value())
|
||||
}
|
||||
Operation::Max => {
|
||||
let untagged = match series.dtype() {
|
||||
DataType::Int8
|
||||
| DataType::Int16
|
||||
| DataType::Int32
|
||||
| DataType::Int64
|
||||
| DataType::UInt8
|
||||
| DataType::UInt16
|
||||
| DataType::UInt32
|
||||
| DataType::UInt64 => {
|
||||
let res: i64 = series.max().unwrap_or(0);
|
||||
Ok(UntaggedValue::Primitive(res.into()))
|
||||
}
|
||||
DataType::Float32 | DataType::Float64 => {
|
||||
let res: f64 = series.max().unwrap_or(0.0);
|
||||
Ok(UntaggedValue::Primitive(res.into()))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Not valid type",
|
||||
format!(
|
||||
"this operation can not be performed with series of type {}",
|
||||
series.dtype()
|
||||
),
|
||||
&operation_tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let value = Value {
|
||||
value: untagged,
|
||||
tag: operation_tag.clone(),
|
||||
};
|
||||
|
||||
let mut data = TaggedDictBuilder::new(operation_tag.clone());
|
||||
data.insert_value(series.name(), value);
|
||||
|
||||
Ok(data.into_value())
|
||||
}
|
||||
Operation::Min => {
|
||||
let untagged = match series.dtype() {
|
||||
DataType::Int8
|
||||
| DataType::Int16
|
||||
| DataType::Int32
|
||||
| DataType::Int64
|
||||
| DataType::UInt8
|
||||
| DataType::UInt16
|
||||
| DataType::UInt32
|
||||
| DataType::UInt64 => {
|
||||
let res: i64 = series.min().unwrap_or(0);
|
||||
Ok(UntaggedValue::Primitive(res.into()))
|
||||
}
|
||||
DataType::Float32 | DataType::Float64 => {
|
||||
let res: f64 = series.min().unwrap_or(0.0);
|
||||
Ok(UntaggedValue::Primitive(res.into()))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Not valid type",
|
||||
format!(
|
||||
"this operation can not be performed with series of type {}",
|
||||
series.dtype()
|
||||
),
|
||||
&operation_tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let value = Value {
|
||||
value: untagged,
|
||||
tag: operation_tag.clone(),
|
||||
};
|
||||
|
||||
let mut data = TaggedDictBuilder::new(operation_tag.clone());
|
||||
data.insert_value(series.name(), value);
|
||||
|
||||
Ok(data.into_value())
|
||||
}
|
||||
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Not valid operation",
|
||||
"operation not valid for series",
|
||||
&operation_tag.span,
|
||||
"Perhaps you want: mean, median, sum, max, min",
|
||||
&operation_tag.span,
|
||||
)),
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
138
crates/nu-command/src/commands/dataframe/append.rs
Normal file
138
crates/nu-command/src/commands/dataframe/append.rs
Normal file
@ -0,0 +1,138 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Axis, Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe append"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Appends a new dataframe"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe append")
|
||||
.required_named(
|
||||
"other",
|
||||
SyntaxShape::Any,
|
||||
"dataframe to be appended",
|
||||
Some('o'),
|
||||
)
|
||||
.required_named(
|
||||
"axis",
|
||||
SyntaxShape::String,
|
||||
"row or col axis orientation",
|
||||
Some('a'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Appends a dataframe as new columns",
|
||||
example: r#"let a = ([[a b]; [1 2] [3 4]] | dataframe to-df);
|
||||
$a | dataframe append -o $a -a row"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"a".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
|
||||
),
|
||||
Column::new(
|
||||
"a_x".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b_x".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Appends a dataframe merging at the end of columns",
|
||||
example: r#"let a = ([[a b]; [1 2] [3 4]] | dataframe to-df);
|
||||
$a | dataframe append -o $a -a col"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"a".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let other: Value = args.req_named("other")?;
|
||||
let axis: Tagged<String> = args.req_named("axis")?;
|
||||
|
||||
let axis = Axis::try_from_str(&axis.item, &axis.tag.span)?;
|
||||
|
||||
let df_other = match other.value {
|
||||
UntaggedValue::DataFrame(df) => Ok(df),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"can only append a dataframe to a dataframe",
|
||||
other.tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let df_new = df.append_df(&df_other, axis, &tag.span)?;
|
||||
Ok(OutputStream::one(df_new.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -2,8 +2,8 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, NuSeries},
|
||||
Signature, SyntaxShape,
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
@ -32,7 +32,15 @@ impl WholeStreamCommand for DataFrame {
|
||||
vec![Example {
|
||||
description: "Returns the selected column as series",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe column a",
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"a".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -41,15 +49,26 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let column: Tagged<String> = args.req(0)?;
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df
|
||||
.as_ref()
|
||||
.column(column.item.as_ref())
|
||||
.column(&column.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &column.tag.span, None))?;
|
||||
|
||||
Ok(OutputStream::one(NuSeries::series_to_value(
|
||||
res.clone(),
|
||||
tag,
|
||||
)))
|
||||
let df = NuDataFrame::try_from_series(vec![res.clone()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
232
crates/nu-command/src/commands/dataframe/describe.rs
Normal file
232
crates/nu-command/src/commands/dataframe/describe.rs
Normal file
@ -0,0 +1,232 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::{
|
||||
chunked_array::ChunkedArray,
|
||||
prelude::{
|
||||
AnyValue, DataFrame as PolarsDF, DataType, Float64Type, IntoSeries, NewChunkedArray,
|
||||
Series, Utf8Type,
|
||||
},
|
||||
};
|
||||
|
||||
use super::utils::parse_polars_error;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe describe"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] Describes dataframes numeric columns"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe describe")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Describes dataframe",
|
||||
example: "[[a b]; [1 1] [1 1]] | dataframe to-df | dataframe describe",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"descriptor".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("count").into(),
|
||||
UntaggedValue::string("sum").into(),
|
||||
UntaggedValue::string("mean").into(),
|
||||
UntaggedValue::string("median").into(),
|
||||
UntaggedValue::string("std").into(),
|
||||
UntaggedValue::string("min").into(),
|
||||
UntaggedValue::string("25%").into(),
|
||||
UntaggedValue::string("50%").into(),
|
||||
UntaggedValue::string("75%").into(),
|
||||
UntaggedValue::string("max").into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"a (i64)".to_string(),
|
||||
vec![
|
||||
UntaggedValue::decimal_from_float(2.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(2.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(0.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"b (i64)".to_string(),
|
||||
vec![
|
||||
UntaggedValue::decimal_from_float(2.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(2.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(0.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
UntaggedValue::decimal_from_float(1.0, Span::default()).into(),
|
||||
],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let names = ChunkedArray::<Utf8Type>::new_from_opt_slice(
|
||||
"descriptor",
|
||||
&[
|
||||
Some("count"),
|
||||
Some("sum"),
|
||||
Some("mean"),
|
||||
Some("median"),
|
||||
Some("std"),
|
||||
Some("min"),
|
||||
Some("25%"),
|
||||
Some("50%"),
|
||||
Some("75%"),
|
||||
Some("max"),
|
||||
],
|
||||
)
|
||||
.into_series();
|
||||
|
||||
let head = std::iter::once(names);
|
||||
|
||||
let tail = df.as_ref().get_columns().iter().map(|col| {
|
||||
let count = col.len() as f64;
|
||||
|
||||
let sum = match col.sum_as_series().cast_with_dtype(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let mean = match col.mean_as_series().get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let median = match col.median_as_series().get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let std = match col.std_as_series().get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let min = match col.min_as_series().cast_with_dtype(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let q_25 = match col.quantile_as_series(0.25) {
|
||||
Ok(ca) => match ca.cast_with_dtype(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let q_50 = match col.quantile_as_series(0.50) {
|
||||
Ok(ca) => match ca.cast_with_dtype(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let q_75 = match col.quantile_as_series(0.75) {
|
||||
Ok(ca) => match ca.cast_with_dtype(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let max = match col.max_as_series().cast_with_dtype(&DataType::Float64) {
|
||||
Ok(ca) => match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let name = format!("{} ({})", col.name(), col.dtype());
|
||||
ChunkedArray::<Float64Type>::new_from_opt_slice(
|
||||
&name,
|
||||
&[
|
||||
Some(count),
|
||||
sum,
|
||||
mean,
|
||||
median,
|
||||
std,
|
||||
min,
|
||||
q_25,
|
||||
q_50,
|
||||
q_75,
|
||||
max,
|
||||
],
|
||||
)
|
||||
.into_series()
|
||||
});
|
||||
|
||||
let res = head.chain(tail).collect::<Vec<Series>>();
|
||||
let df = PolarsDF::new(res).map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
let df = NuDataFrame::dataframe_to_value(df, tag);
|
||||
Ok(OutputStream::one(df))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::{convert_columns, parse_polars_error};
|
||||
|
||||
@ -17,7 +20,11 @@ impl WholeStreamCommand for DataFrame {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe drop").rest(SyntaxShape::Any, "column names to be dropped")
|
||||
Signature::build("dataframe drop").rest(
|
||||
"rest",
|
||||
SyntaxShape::Any,
|
||||
"column names to be dropped",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
@ -28,7 +35,15 @@ impl WholeStreamCommand for DataFrame {
|
||||
vec![Example {
|
||||
description: "drop column a",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe drop a",
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"b".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -39,7 +54,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let columns: Vec<Value> = args.rest(0)?;
|
||||
let (col_string, col_span) = convert_columns(&columns, &tag)?;
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let new_df = match col_string.get(0) {
|
||||
Some(col) => df
|
||||
@ -63,3 +78,16 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::{convert_columns, parse_polars_error};
|
||||
|
||||
@ -34,7 +37,21 @@ impl WholeStreamCommand for DataFrame {
|
||||
vec![Example {
|
||||
description: "drop duplicates",
|
||||
example: "[[a b]; [1 2] [3 4] [1 2]] | dataframe to-df | dataframe drop-duplicates",
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"a".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -52,7 +69,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
None => (None, Span::unknown()),
|
||||
};
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let subset_slice = subset.as_ref().map(|cols| &cols[..]);
|
||||
|
||||
@ -63,3 +80,16 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, NuSeries, PolarsData},
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
@ -38,15 +38,45 @@ impl WholeStreamCommand for DataFrame {
|
||||
example: r#"let df = ([[a b]; [1 2] [3 0] [1 2]] | dataframe to-df);
|
||||
let res = ($df.b / $df.b);
|
||||
let df = ($df | dataframe with-column $res --name res);
|
||||
$df | dataframe drop-nulls
|
||||
"#,
|
||||
result: None,
|
||||
$df | dataframe drop-nulls"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"a".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(1).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(2).into()],
|
||||
),
|
||||
Column::new(
|
||||
"res".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(1).into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "drop null values in dataframe",
|
||||
example: r#"let s = ([1 2 0 0 3 4] | dataframe to-series);
|
||||
example: r#"let s = ([1 2 0 0 3 4] | dataframe to-df);
|
||||
($s / $s) | dataframe drop-nulls"#,
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"div_0_0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -60,7 +90,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
})?;
|
||||
|
||||
match value.value {
|
||||
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)) => {
|
||||
UntaggedValue::DataFrame(df) => {
|
||||
// Extracting the selection columns of the columns to perform the aggregation
|
||||
let columns: Option<Vec<Value>> = args.opt(0)?;
|
||||
let (subset, col_span) = match columns {
|
||||
@ -80,10 +110,6 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
UntaggedValue::DataFrame(PolarsData::Series(series)) => {
|
||||
let res = series.as_ref().drop_nulls();
|
||||
Ok(OutputStream::one(NuSeries::series_to_value(res, tag)))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"drop nulls cannot be done with this value",
|
||||
@ -91,3 +117,16 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuDataFrame, Signature, TaggedDictBuilder};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
@ -26,7 +29,27 @@ impl WholeStreamCommand for DataFrame {
|
||||
vec![Example {
|
||||
description: "drop column a",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe dtypes",
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"column".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("a").into(),
|
||||
UntaggedValue::string("b").into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"dtype".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("i64").into(),
|
||||
UntaggedValue::string("i64").into(),
|
||||
],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -35,26 +58,49 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let col_names = df
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let mut dtypes: Vec<Value> = Vec::new();
|
||||
let names: Vec<Value> = df
|
||||
.as_ref()
|
||||
.get_column_names()
|
||||
.iter()
|
||||
.map(|v| v.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
.map(|v| {
|
||||
let dtype = df
|
||||
.as_ref()
|
||||
.column(v)
|
||||
.expect("using name from list of names from dataframe")
|
||||
.dtype();
|
||||
|
||||
let values = df
|
||||
.as_ref()
|
||||
.dtypes()
|
||||
.into_iter()
|
||||
.zip(col_names.into_iter())
|
||||
.map(move |(dtype, name)| {
|
||||
let mut data = TaggedDictBuilder::new(tag.clone());
|
||||
data.insert_value("column", name.as_ref());
|
||||
data.insert_value("dtype", format!("{}", dtype));
|
||||
let dtype_str = dtype.to_string();
|
||||
dtypes.push(Value {
|
||||
value: dtype_str.into(),
|
||||
tag: Tag::default(),
|
||||
});
|
||||
|
||||
data.into_value()
|
||||
});
|
||||
Value {
|
||||
value: v.to_string().into(),
|
||||
tag: Tag::default(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(OutputStream::from_stream(values))
|
||||
let names_col = Column::new("column".to_string(), names);
|
||||
let dtypes_col = Column::new("dtype".to_string(), dtypes);
|
||||
|
||||
let df = NuDataFrame::try_from_columns(vec![names_col, dtypes_col], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
@ -32,12 +32,70 @@ impl WholeStreamCommand for DataFrame {
|
||||
Example {
|
||||
description: "Create new dataframe with dummy variables from a dataframe",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe to-dummies",
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"a_1".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(0).into()],
|
||||
),
|
||||
Column::new(
|
||||
"a_3".to_string(),
|
||||
vec![UntaggedValue::int(0).into(), UntaggedValue::int(1).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b_2".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(0).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b_4".to_string(),
|
||||
vec![UntaggedValue::int(0).into(), UntaggedValue::int(1).into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Create new dataframe with dummy variables from a series",
|
||||
example: "[1 2 2 3 3] | dataframe to-series | dataframe to-dummies",
|
||||
result: None,
|
||||
example: "[1 2 2 3 3] | dataframe to-df | dataframe to-dummies",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"0_1".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"0_2".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"0_3".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -51,7 +109,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
})?;
|
||||
|
||||
match value.value {
|
||||
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)) => {
|
||||
UntaggedValue::DataFrame(df) => {
|
||||
let res = df.as_ref().to_dummies().map_err(|e| {
|
||||
parse_polars_error(
|
||||
&e,
|
||||
@ -62,17 +120,6 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
UntaggedValue::DataFrame(PolarsData::Series(series)) => {
|
||||
let res = series.as_ref().to_dummies().map_err(|e| {
|
||||
parse_polars_error(
|
||||
&e,
|
||||
&tag.span,
|
||||
Some("The only allowed column types for dummies are String or Int"),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"dummies cannot be done with this value",
|
||||
@ -80,3 +127,16 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
@ -34,13 +34,21 @@ impl WholeStreamCommand for DataFrame {
|
||||
vec![
|
||||
Example {
|
||||
description: "Filter dataframe using a bool mask",
|
||||
example: r#"let mask = ([$true $false] | dataframe to-series);
|
||||
example: r#"let mask = ([$true $false] | dataframe to-df);
|
||||
[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe filter-with $mask"#,
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("a".to_string(), vec![UntaggedValue::int(1).into()]),
|
||||
Column::new("b".to_string(), vec![UntaggedValue::int(2).into()]),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Filter dataframe by creating a mask from operation",
|
||||
example: r#"let mask = (([5 6] | dataframe to-series) > 5);
|
||||
example: r#"let mask = (([5 6] | dataframe to-df) > 5);
|
||||
[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe filter-with $mask"#,
|
||||
result: None,
|
||||
},
|
||||
@ -53,29 +61,42 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let value: Value = args.req(0)?;
|
||||
|
||||
let series_span = value.tag.span;
|
||||
let series = match value.value {
|
||||
UntaggedValue::DataFrame(PolarsData::Series(series)) => Ok(series),
|
||||
let df = match value.value {
|
||||
UntaggedValue::DataFrame(df) => Ok(df),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"can only add a series to a dataframe",
|
||||
value.tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let casted = series.as_ref().bool().map_err(|e| {
|
||||
let series = df.as_series(&series_span)?;
|
||||
let casted = series.bool().map_err(|e| {
|
||||
parse_polars_error(
|
||||
&e,
|
||||
&&series_span,
|
||||
&series_span,
|
||||
Some("Perhaps you want to use a series with booleans as mask"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df
|
||||
.as_ref()
|
||||
.filter(&casted)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
.filter(casted)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
|
||||
@ -31,8 +34,16 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create new dataframe with head rows",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe first",
|
||||
result: None,
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe first 1",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("a".to_string(), vec![UntaggedValue::int(1).into()]),
|
||||
Column::new("b".to_string(), vec![UntaggedValue::int(2).into()]),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -46,8 +57,21 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
None => 5,
|
||||
};
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let res = df.as_ref().head(Some(rows));
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::{convert_columns, parse_polars_error};
|
||||
pub struct DataFrame;
|
||||
@ -16,7 +19,11 @@ impl WholeStreamCommand for DataFrame {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get").rest(SyntaxShape::Any, "column names to sort dataframe")
|
||||
Signature::build("dataframe get").rest(
|
||||
"rest",
|
||||
SyntaxShape::Any,
|
||||
"column names to sort dataframe",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
@ -27,7 +34,15 @@ impl WholeStreamCommand for DataFrame {
|
||||
vec![Example {
|
||||
description: "Creates dataframe with selected columns",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe get a",
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"a".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -38,7 +53,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let (col_string, col_span) = convert_columns(&columns, &tag)?;
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df
|
||||
.as_ref()
|
||||
@ -47,3 +62,16 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, NuGroupBy, PolarsData},
|
||||
dataframe::{FrameStruct, NuDataFrame, NuGroupBy},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
@ -20,7 +20,7 @@ impl WholeStreamCommand for DataFrame {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe group-by").rest(SyntaxShape::Any, "groupby columns")
|
||||
Signature::build("dataframe group-by").rest("rest", SyntaxShape::Any, "groupby columns")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
@ -43,7 +43,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let by_columns: Vec<Value> = args.rest(0)?;
|
||||
let (columns_string, col_span) = convert_columns(&by_columns, &tag)?;
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
// This is the expensive part of the groupby; to create the
|
||||
// groups that will be used for grouping the data in the
|
||||
@ -57,7 +57,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let groups = groupby.get_groups().to_vec();
|
||||
let groupby = Value {
|
||||
tag,
|
||||
value: UntaggedValue::DataFrame(PolarsData::GroupBy(NuGroupBy::new(
|
||||
value: UntaggedValue::FrameStruct(FrameStruct::GroupBy(NuGroupBy::new(
|
||||
NuDataFrame::new(df.as_ref().clone()),
|
||||
columns_string,
|
||||
groups,
|
||||
|
@ -2,7 +2,7 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
@ -51,20 +51,50 @@ impl WholeStreamCommand for DataFrame {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "inner join dataframe",
|
||||
example: r#"let right = ([[a b c]; [1 2 5] [3 4 5] [5 6 6]] | dataframe to-df);
|
||||
vec![Example {
|
||||
description: "inner join dataframe",
|
||||
example: r#"let right = ([[a b c]; [1 2 5] [3 4 5] [5 6 6]] | dataframe to-df);
|
||||
$right | dataframe join $right -l [a b] -r [a b]"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "right join dataframe",
|
||||
example: r#"let right = ([[a b c]; [1 2 3] [3 4 5] [5 6 7]] | dataframe to-df);
|
||||
$right | dataframe join $right -l [a c] -r [a c] -t inner"#,
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"a".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(5).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"c".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"c_right".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,13 +127,14 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (l_col_string, l_col_span) = convert_columns(&l_col, &tag)?;
|
||||
let (r_col_string, r_col_span) = convert_columns(&r_col, &tag)?;
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = match r_df.value {
|
||||
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(r_df)) => {
|
||||
UntaggedValue::DataFrame(r_df) => {
|
||||
// Checking the column types before performing the join
|
||||
check_column_datatypes(
|
||||
df.as_ref(),
|
||||
r_df.as_ref(),
|
||||
&l_col_string,
|
||||
&l_col_span,
|
||||
&r_col_string,
|
||||
@ -125,7 +156,8 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
}
|
||||
|
||||
fn check_column_datatypes<T: AsRef<str>>(
|
||||
df: &polars::prelude::DataFrame,
|
||||
df_l: &polars::prelude::DataFrame,
|
||||
df_r: &polars::prelude::DataFrame,
|
||||
l_cols: &[T],
|
||||
l_col_span: &Span,
|
||||
r_cols: &[T],
|
||||
@ -145,14 +177,14 @@ fn check_column_datatypes<T: AsRef<str>>(
|
||||
));
|
||||
}
|
||||
|
||||
for (l, r) in l_cols.iter().zip(r_cols.iter()) {
|
||||
let l_series = df
|
||||
for (l, r) in l_cols.iter().zip(r_cols) {
|
||||
let l_series = df_l
|
||||
.column(l.as_ref())
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &l_col_span, None))?;
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, l_col_span, None))?;
|
||||
|
||||
let r_series = df
|
||||
let r_series = df_r
|
||||
.column(r.as_ref())
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &r_col_span, None))?;
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, r_col_span, None))?;
|
||||
|
||||
if l_series.dtype() != r_series.dtype() {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
@ -171,3 +203,16 @@ fn check_column_datatypes<T: AsRef<str>>(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
pub struct DataFrame;
|
||||
@ -30,8 +33,16 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create new dataframe with last rows",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe last",
|
||||
result: None,
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe last 1",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("a".to_string(), vec![UntaggedValue::int(3).into()]),
|
||||
Column::new("b".to_string(), vec![UntaggedValue::int(4).into()]),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -45,9 +56,22 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
None => 5,
|
||||
};
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df.as_ref().tail(Some(rows));
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::PolarsData, Signature, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
@ -19,46 +22,94 @@ impl WholeStreamCommand for DataFrame {
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let values = args
|
||||
let data = args
|
||||
.context
|
||||
.scope
|
||||
.get_vars()
|
||||
.into_iter()
|
||||
.filter_map(|(name, value)| {
|
||||
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)) = &value.value {
|
||||
let mut data = TaggedDictBuilder::new(value.tag.clone());
|
||||
if let UntaggedValue::DataFrame(df) = &value.value {
|
||||
let rows = Value {
|
||||
value: (df.as_ref().height() as i64).into(),
|
||||
tag: Tag::default(),
|
||||
};
|
||||
|
||||
let rows = df.as_ref().height();
|
||||
let cols = df.as_ref().width();
|
||||
let cols = Value {
|
||||
value: (df.as_ref().width() as i64).into(),
|
||||
tag: Tag::default(),
|
||||
};
|
||||
|
||||
data.insert_value("name", name.as_ref());
|
||||
data.insert_value("rows", format!("{}", rows));
|
||||
data.insert_value("columns", format!("{}", cols));
|
||||
let location = match value.tag.anchor {
|
||||
Some(AnchorLocation::File(name)) => name,
|
||||
Some(AnchorLocation::Url(name)) => name,
|
||||
Some(AnchorLocation::Source(text)) => text.slice(0..text.end).text,
|
||||
None => "stream".to_string(),
|
||||
};
|
||||
|
||||
match value.tag.anchor {
|
||||
Some(AnchorLocation::File(name)) => data.insert_value("location", name),
|
||||
Some(AnchorLocation::Url(name)) => data.insert_value("location", name),
|
||||
Some(AnchorLocation::Source(text)) => {
|
||||
let loc_name = text.slice(0..text.end);
|
||||
data.insert_value("location", loc_name.text)
|
||||
}
|
||||
None => data.insert_value("location", "stream"),
|
||||
}
|
||||
let location = Value {
|
||||
value: location.into(),
|
||||
tag: Tag::default(),
|
||||
};
|
||||
|
||||
Some(data.into_value())
|
||||
let name = Value {
|
||||
value: name.into(),
|
||||
tag: Tag::default(),
|
||||
};
|
||||
|
||||
Some((name, rows, cols, location))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
Ok(OutputStream::from_stream(values))
|
||||
let mut name = Column::new_empty("name".to_string());
|
||||
let mut rows = Column::new_empty("rows".to_string());
|
||||
let mut cols = Column::new_empty("columns".to_string());
|
||||
let mut location = Column::new_empty("location".to_string());
|
||||
|
||||
for tuple in data {
|
||||
name.push(tuple.0);
|
||||
rows.push(tuple.1);
|
||||
cols.push(tuple.2);
|
||||
location.push(tuple.3);
|
||||
}
|
||||
|
||||
let tag = args.call_info.name_tag;
|
||||
let df = NuDataFrame::try_from_columns(vec![name, rows, cols, location], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Lists loaded dataframes in current scope",
|
||||
example: "dataframe list",
|
||||
result: None,
|
||||
example: "let a = ([[a b];[1 2] [3 4]] | dataframe to-df); dataframe list",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new("name".to_string(), vec![UntaggedValue::string("$a").into()]),
|
||||
Column::new("rows".to_string(), vec![UntaggedValue::int(2).into()]),
|
||||
Column::new("columns".to_string(), vec![UntaggedValue::int(2).into()]),
|
||||
Column::new(
|
||||
"location".to_string(),
|
||||
vec![UntaggedValue::string("stream").into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
|
||||
use super::utils::convert_columns;
|
||||
|
||||
@ -18,8 +22,30 @@ impl WholeStreamCommand for DataFrame {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe melt")
|
||||
.required("id_columns", SyntaxShape::Table, "Id columns for melting")
|
||||
.rest(SyntaxShape::Any, "columns used as value columns")
|
||||
.required_named(
|
||||
"columns",
|
||||
SyntaxShape::Table,
|
||||
"column names for melting",
|
||||
Some('c'),
|
||||
)
|
||||
.required_named(
|
||||
"values",
|
||||
SyntaxShape::Table,
|
||||
"column names used as value columns",
|
||||
Some('v'),
|
||||
)
|
||||
.named(
|
||||
"variable_name",
|
||||
SyntaxShape::String,
|
||||
"optional name for variable column",
|
||||
Some('r'),
|
||||
)
|
||||
.named(
|
||||
"value_name",
|
||||
SyntaxShape::String,
|
||||
"optional name for value column",
|
||||
Some('l'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
@ -29,8 +55,59 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "melt dataframe",
|
||||
example: "[[a b]; [a 2] [b 4] [a 6]] | dataframe to-df | dataframe melt a b",
|
||||
result: None,
|
||||
example:
|
||||
"[[a b c d]; [x 1 4 a] [y 2 5 b] [z 3 6 c]] | dataframe to-df | dataframe melt -c [b c] -v [a d]",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"c".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(4).into(),
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
UntaggedValue::int(5).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"variable".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("a").into(),
|
||||
UntaggedValue::string("a").into(),
|
||||
UntaggedValue::string("a").into(),
|
||||
UntaggedValue::string("d").into(),
|
||||
UntaggedValue::string("d").into(),
|
||||
UntaggedValue::string("d").into(),
|
||||
],
|
||||
),
|
||||
Column::new(
|
||||
"value".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("x").into(),
|
||||
UntaggedValue::string("y").into(),
|
||||
UntaggedValue::string("z").into(),
|
||||
UntaggedValue::string("a").into(),
|
||||
UntaggedValue::string("b").into(),
|
||||
UntaggedValue::string("c").into(),
|
||||
],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -38,22 +115,35 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let id_col: Vec<Value> = args.req(0)?;
|
||||
let val_col: Vec<Value> = args.rest(1)?;
|
||||
let id_col: Vec<Value> = args.req_named("columns")?;
|
||||
let val_col: Vec<Value> = args.req_named("values")?;
|
||||
|
||||
let value_name: Option<Tagged<String>> = args.get_flag("value_name")?;
|
||||
let variable_name: Option<Tagged<String>> = args.get_flag("variable_name")?;
|
||||
|
||||
let (id_col_string, id_col_span) = convert_columns(&id_col, &tag)?;
|
||||
let (val_col_string, val_col_span) = convert_columns(&val_col, &tag)?;
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
check_column_datatypes(df.as_ref(), &id_col_string, &id_col_span)?;
|
||||
check_column_datatypes(df.as_ref(), &val_col_string, &val_col_span)?;
|
||||
|
||||
let res = df
|
||||
let mut res = df
|
||||
.as_ref()
|
||||
.melt(&id_col_string, &val_col_string)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
|
||||
if let Some(name) = &variable_name {
|
||||
res.rename("variable", &name.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &name.tag.span, None))?;
|
||||
}
|
||||
|
||||
if let Some(name) = &value_name {
|
||||
res.rename("value", &name.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &name.tag.span, None))?;
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
@ -75,11 +165,11 @@ fn check_column_datatypes<T: AsRef<str>>(
|
||||
for w in cols.windows(2) {
|
||||
let l_series = df
|
||||
.column(w[0].as_ref())
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col_span, None))?;
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, col_span, None))?;
|
||||
|
||||
let r_series = df
|
||||
.column(w[1].as_ref())
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col_span, None))?;
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, col_span, None))?;
|
||||
|
||||
if l_series.dtype() != r_series.dtype() {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
@ -99,3 +189,16 @@ fn check_column_datatypes<T: AsRef<str>>(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
pub mod aggregate;
|
||||
pub mod append;
|
||||
pub mod column;
|
||||
pub mod command;
|
||||
pub mod describe;
|
||||
pub mod drop;
|
||||
pub mod drop_duplicates;
|
||||
pub mod drop_nulls;
|
||||
@ -16,8 +18,10 @@ pub mod list;
|
||||
pub mod melt;
|
||||
pub mod open;
|
||||
pub mod pivot;
|
||||
pub mod rename;
|
||||
pub mod sample;
|
||||
pub mod select;
|
||||
pub mod shape;
|
||||
pub mod show;
|
||||
pub mod slice;
|
||||
pub mod sort;
|
||||
@ -25,14 +29,15 @@ pub mod take;
|
||||
pub mod to_csv;
|
||||
pub mod to_df;
|
||||
pub mod to_parquet;
|
||||
pub mod to_series;
|
||||
pub(crate) mod utils;
|
||||
pub mod where_;
|
||||
pub mod with_column;
|
||||
|
||||
pub use aggregate::DataFrame as DataFrameAggregate;
|
||||
pub use append::DataFrame as DataFrameAppend;
|
||||
pub use column::DataFrame as DataFrameColumn;
|
||||
pub use command::Command as DataFrame;
|
||||
pub use describe::DataFrame as DataFrameDescribe;
|
||||
pub use drop::DataFrame as DataFrameDrop;
|
||||
pub use drop_duplicates::DataFrame as DataFrameDropDuplicates;
|
||||
pub use drop_nulls::DataFrame as DataFrameDropNulls;
|
||||
@ -48,8 +53,10 @@ pub use list::DataFrame as DataFrameList;
|
||||
pub use melt::DataFrame as DataFrameMelt;
|
||||
pub use open::DataFrame as DataFrameOpen;
|
||||
pub use pivot::DataFrame as DataFramePivot;
|
||||
pub use rename::DataFrame as DataFrameRename;
|
||||
pub use sample::DataFrame as DataFrameSample;
|
||||
pub use select::DataFrame as DataFrameSelect;
|
||||
pub use shape::DataFrame as DataFrameShape;
|
||||
pub use show::DataFrame as DataFrameShow;
|
||||
pub use slice::DataFrame as DataFrameSlice;
|
||||
pub use sort::DataFrame as DataFrameSort;
|
||||
@ -57,7 +64,6 @@ pub use take::DataFrame as DataFrameTake;
|
||||
pub use to_csv::DataFrame as DataFrameToCsv;
|
||||
pub use to_df::DataFrame as DataFrameToDF;
|
||||
pub use to_parquet::DataFrame as DataFrameToParquet;
|
||||
pub use to_series::DataFrame as DataFrameToSeries;
|
||||
pub use where_::DataFrame as DataFrameWhere;
|
||||
pub use with_column::DataFrame as DataFrameWithColumn;
|
||||
|
||||
@ -69,6 +75,19 @@ pub use series::DataFrameArgMin;
|
||||
pub use series::DataFrameArgSort;
|
||||
pub use series::DataFrameArgTrue;
|
||||
pub use series::DataFrameArgUnique;
|
||||
pub use series::DataFrameConcatenate;
|
||||
pub use series::DataFrameContains;
|
||||
pub use series::DataFrameCumulative;
|
||||
pub use series::DataFrameGetDay;
|
||||
pub use series::DataFrameGetHour;
|
||||
pub use series::DataFrameGetMinute;
|
||||
pub use series::DataFrameGetMonth;
|
||||
pub use series::DataFrameGetNanoSecond;
|
||||
pub use series::DataFrameGetOrdinal;
|
||||
pub use series::DataFrameGetSecond;
|
||||
pub use series::DataFrameGetWeek;
|
||||
pub use series::DataFrameGetWeekDay;
|
||||
pub use series::DataFrameGetYear;
|
||||
pub use series::DataFrameIsDuplicated;
|
||||
pub use series::DataFrameIsIn;
|
||||
pub use series::DataFrameIsNotNull;
|
||||
@ -77,9 +96,17 @@ pub use series::DataFrameIsUnique;
|
||||
pub use series::DataFrameNNull;
|
||||
pub use series::DataFrameNUnique;
|
||||
pub use series::DataFrameNot;
|
||||
pub use series::DataFrameReplace;
|
||||
pub use series::DataFrameReplaceAll;
|
||||
pub use series::DataFrameRolling;
|
||||
pub use series::DataFrameSeriesRename;
|
||||
pub use series::DataFrameSet;
|
||||
pub use series::DataFrameSetWithIdx;
|
||||
pub use series::DataFrameShift;
|
||||
pub use series::DataFrameStrFTime;
|
||||
pub use series::DataFrameStringLengths;
|
||||
pub use series::DataFrameStringSlice;
|
||||
pub use series::DataFrameToLowercase;
|
||||
pub use series::DataFrameToUppercase;
|
||||
pub use series::DataFrameUnique;
|
||||
pub use series::DataFrameValueCounts;
|
||||
|
@ -8,7 +8,7 @@ use nu_protocol::{
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
use polars::prelude::{CsvReader, JsonReader, ParquetReader, SerReader};
|
||||
use polars::prelude::{CsvEncoding, CsvReader, JsonReader, ParquetReader, SerReader};
|
||||
use std::fs::File;
|
||||
|
||||
pub struct DataFrame;
|
||||
@ -151,7 +151,8 @@ fn from_csv(args: CommandArgs) -> Result<polars::prelude::DataFrame, ShellError>
|
||||
let columns: Option<Vec<Value>> = args.get_flag("columns")?;
|
||||
|
||||
let csv_reader = CsvReader::from_path(&file.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &file.tag.span, None))?;
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &file.tag.span, None))?
|
||||
.with_encoding(CsvEncoding::LossyUtf8);
|
||||
|
||||
let csv_reader = match delimiter {
|
||||
None => csv_reader,
|
||||
|
@ -73,7 +73,7 @@ impl WholeStreamCommand for DataFrame {
|
||||
description: "Pivot a dataframe on b and aggregation on col c",
|
||||
example:
|
||||
"[[a b c]; [one x 1] [two y 2]] | dataframe to-df | dataframe group-by a | dataframe pivot b c sum",
|
||||
result: None,
|
||||
result: None, // No sample because there are nulls in the result dataframe
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -100,7 +100,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let mut groupby = nu_groupby.to_groupby()?;
|
||||
|
||||
let pivot = groupby.pivot(pivot_col.item.as_ref(), value_col.item.as_ref());
|
||||
let pivot = groupby.pivot(&pivot_col.item, &value_col.item);
|
||||
|
||||
let res = match op {
|
||||
Operation::Mean => pivot.mean(),
|
||||
@ -120,7 +120,7 @@ fn check_pivot_column(
|
||||
col: &Tagged<String>,
|
||||
) -> Result<(), ShellError> {
|
||||
let series = df
|
||||
.column(col.item.as_ref())
|
||||
.column(&col.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col.tag.span, None))?;
|
||||
|
||||
match series.dtype() {
|
||||
@ -146,7 +146,7 @@ fn check_value_column(
|
||||
col: &Tagged<String>,
|
||||
) -> Result<(), ShellError> {
|
||||
let series = df
|
||||
.column(col.item.as_ref())
|
||||
.column(&col.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &col.tag.span, None))?;
|
||||
|
||||
match series.dtype() {
|
||||
|
81
crates/nu-command/src/commands/dataframe/rename.rs
Normal file
81
crates/nu-command/src/commands/dataframe/rename.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
|
||||
use super::utils::parse_polars_error;
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe rename-col"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[DataFrame] rename a dataframe column"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe rename-col")
|
||||
.required("from", SyntaxShape::String, "column name to be renamed")
|
||||
.required("to", SyntaxShape::String, "new column name")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Renames a dataframe column",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe rename-col a ab",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![
|
||||
Column::new(
|
||||
"ab".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
),
|
||||
Column::new(
|
||||
"b".to_string(),
|
||||
vec![UntaggedValue::int(2).into(), UntaggedValue::int(4).into()],
|
||||
),
|
||||
],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let from: Tagged<String> = args.req(0)?;
|
||||
let to: Tagged<String> = args.req(1)?;
|
||||
|
||||
let (mut df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
df.as_mut()
|
||||
.rename(&from.item, &to.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -41,14 +41,14 @@ impl WholeStreamCommand for DataFrame {
|
||||
vec![
|
||||
Example {
|
||||
description: "Sample rows from dataframe",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe sample -r 1",
|
||||
result: None,
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe sample -n 1",
|
||||
result: None, // No expected value because sampling is random
|
||||
},
|
||||
Example {
|
||||
description: "Shows sample row using fraction and replace",
|
||||
example:
|
||||
"[[a b]; [1 2] [3 4] [5 6]] | dataframe to-df | dataframe sample -f 0.5 -e",
|
||||
result: None,
|
||||
result: None, // No expected value because sampling is random
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -61,7 +61,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let fraction: Option<Tagged<f64>> = args.get_flag("fraction")?;
|
||||
let replace: bool = args.has_flag("replace");
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = match (rows, fraction) {
|
||||
(Some(rows), None) => df
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuDataFrame, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::{convert_columns, parse_polars_error};
|
||||
|
||||
@ -17,7 +20,7 @@ impl WholeStreamCommand for DataFrame {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe select").rest(SyntaxShape::Any, "selected column names")
|
||||
Signature::build("dataframe select").rest("rest", SyntaxShape::Any, "selected column names")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
@ -28,7 +31,15 @@ impl WholeStreamCommand for DataFrame {
|
||||
vec![Example {
|
||||
description: "Create new dataframe with column a",
|
||||
example: "[[a b]; [1 2] [3 4]] | dataframe to-df | dataframe select a",
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"a".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(3).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -40,7 +51,7 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let (col_string, col_span) = convert_columns(&columns, &tag)?;
|
||||
|
||||
let df = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, _) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = df
|
||||
.as_ref()
|
||||
@ -49,3 +60,16 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
Ok(OutputStream::one(NuDataFrame::dataframe_to_value(res, tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuSeries, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
@ -26,15 +29,31 @@ impl WholeStreamCommand for DataFrame {
|
||||
vec![
|
||||
Example {
|
||||
description: "Returns true if all values are false",
|
||||
example: "[$false $false $false] | dataframe to-series | dataframe all-false",
|
||||
result: None,
|
||||
example: "[$false $false $false] | dataframe to-df | dataframe all-false",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"all_false".to_string(),
|
||||
vec![UntaggedValue::boolean(true).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Checks the result from a comparison",
|
||||
example: r#"let s = ([5 6 2 8] | dataframe to-series);
|
||||
example: r#"let s = ([5 6 2 10] | dataframe to-df);
|
||||
let res = ($s > 9);
|
||||
$res | dataframe all-false"#,
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"all_false".to_string(),
|
||||
vec![UntaggedValue::boolean(false).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -43,9 +62,10 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let bool = series.as_ref().bool().map_err(|e| {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
let bool = series.bool().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&tag.span,
|
||||
@ -60,8 +80,23 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
tag: tag.clone(),
|
||||
};
|
||||
|
||||
let mut data = TaggedDictBuilder::new(tag);
|
||||
data.insert_value("all_false", value);
|
||||
let df = NuDataFrame::try_from_columns(
|
||||
vec![Column::new("all_false".to_string(), vec![value])],
|
||||
&tag.span,
|
||||
)?;
|
||||
|
||||
Ok(OutputStream::one(data.into_value()))
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuSeries, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
@ -26,15 +29,31 @@ impl WholeStreamCommand for DataFrame {
|
||||
vec![
|
||||
Example {
|
||||
description: "Returns true if all values are true",
|
||||
example: "[$true $true $true] | dataframe to-series | dataframe all-true",
|
||||
result: None,
|
||||
example: "[$true $true $true] | dataframe to-df | dataframe all-true",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"all_true".to_string(),
|
||||
vec![UntaggedValue::boolean(true).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
Example {
|
||||
description: "Checks the result from a comparison",
|
||||
example: r#"let s = ([5 6 2 8] | dataframe to-series);
|
||||
example: r#"let s = ([5 6 2 8] | dataframe to-df);
|
||||
let res = ($s > 9);
|
||||
$res | dataframe all-true"#,
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"all_true".to_string(),
|
||||
vec![UntaggedValue::boolean(false).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -43,9 +62,10 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let bool = series.as_ref().bool().map_err(|e| {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
let bool = series.bool().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&tag.span,
|
||||
@ -60,8 +80,23 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
tag: tag.clone(),
|
||||
};
|
||||
|
||||
let mut data = TaggedDictBuilder::new(tag);
|
||||
data.insert_value("all_true", value);
|
||||
let df = NuDataFrame::try_from_columns(
|
||||
vec![Column::new("all_true".to_string(), vec![value])],
|
||||
&tag.span,
|
||||
)?;
|
||||
|
||||
Ok(OutputStream::one(data.into_value()))
|
||||
Ok(OutputStream::one(df.into_value(tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuSeries, Signature};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::{IntoSeries, NewChunkedArray, UInt32Chunked};
|
||||
|
||||
@ -27,8 +30,16 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns index for max value",
|
||||
example: "[1 3 2] | dataframe to-series | dataframe arg-max",
|
||||
result: None,
|
||||
example: "[1 3 2] | dataframe to-df | dataframe arg-max",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"arg_max".to_string(),
|
||||
vec![UntaggedValue::int(1).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -36,9 +47,10 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let res = series.as_ref().arg_max();
|
||||
let res = series.arg_max();
|
||||
|
||||
let chunked = match res {
|
||||
Some(index) => UInt32Chunked::new_from_slice("arg_max", &[index as u32]),
|
||||
@ -46,6 +58,20 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
};
|
||||
|
||||
let res = chunked.into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
|
||||
Ok(OutputStream::one(NuSeries::series_to_value(res, tag)))
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuSeries, Signature};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::{IntoSeries, NewChunkedArray, UInt32Chunked};
|
||||
|
||||
@ -27,8 +30,16 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns index for min value",
|
||||
example: "[1 3 2] | dataframe to-series | dataframe arg-min",
|
||||
result: None,
|
||||
example: "[1 3 2] | dataframe to-df | dataframe arg-min",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"arg_min".to_string(),
|
||||
vec![UntaggedValue::int(0).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -36,9 +47,9 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = series.as_ref().arg_min();
|
||||
let res = df.as_series(&df_tag.span)?.arg_min();
|
||||
|
||||
let chunked = match res {
|
||||
Some(index) => UInt32Chunked::new_from_slice("arg_min", &[index as u32]),
|
||||
@ -46,6 +57,20 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
};
|
||||
|
||||
let res = chunked.into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
|
||||
Ok(OutputStream::one(NuSeries::series_to_value(res, tag)))
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuSeries, Signature};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
@ -26,8 +29,22 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns indexes for a sorted series",
|
||||
example: "[1 2 2 3 3] | dataframe to-series | dataframe arg-sort",
|
||||
result: None,
|
||||
example: "[1 2 2 3 3] | dataframe to-df | dataframe arg-sort",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"arg_sort".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -36,12 +53,24 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let reverse = args.has_flag("reverse");
|
||||
|
||||
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = series.as_ref().argsort(reverse);
|
||||
let mut res = df.as_series(&df_tag.span)?.argsort(reverse).into_series();
|
||||
res.rename("arg_sort");
|
||||
|
||||
Ok(OutputStream::one(NuSeries::series_to_value(
|
||||
res.into_series(),
|
||||
tag,
|
||||
)))
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuSeries, Signature};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
@ -26,8 +29,16 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns indexes where values are true",
|
||||
example: "[$false $true $false] | dataframe to-series | dataframe arg-true",
|
||||
result: None,
|
||||
example: "[$false $true $false] | dataframe to-df | dataframe arg-true",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"arg_true".to_string(),
|
||||
vec![UntaggedValue::int(1).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -35,9 +46,10 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let bool = series.as_ref().bool().map_err(|e| {
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
let bool = series.bool().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&tag.span,
|
||||
@ -46,7 +58,21 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
})?;
|
||||
|
||||
let mut res = bool.arg_true().into_series();
|
||||
res.rename("int");
|
||||
res.rename("arg_true");
|
||||
|
||||
Ok(OutputStream::one(NuSeries::series_to_value(res, tag)))
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuSeries, Signature};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
@ -26,8 +29,20 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns indexes for unique values",
|
||||
example: "[1 2 2 3 3] | dataframe to-series | dataframe arg-unique",
|
||||
result: None,
|
||||
example: "[1 2 2 3 3] | dataframe to-df | dataframe arg-unique",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"arg_unique".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -35,15 +50,29 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = series
|
||||
.as_ref()
|
||||
let mut res = df
|
||||
.as_series(&df_tag.span)?
|
||||
.arg_unique()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?
|
||||
.into_series();
|
||||
|
||||
Ok(OutputStream::one(NuSeries::series_to_value(
|
||||
res.into_series(),
|
||||
tag,
|
||||
)))
|
||||
res.rename("arg_unique");
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
107
crates/nu-command/src/commands/dataframe/series/concatenate.rs
Normal file
107
crates/nu-command/src/commands/dataframe/series/concatenate.rs
Normal file
@ -0,0 +1,107 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe concatenate"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Concatenates strings with other array"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe concatenate").required(
|
||||
"other",
|
||||
SyntaxShape::Any,
|
||||
"Other array with string to be concatenated",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Concatenate string",
|
||||
example: r#"let other = ([za xs cd] | dataframe to-df);
|
||||
[abc abc abc] | dataframe to-df | dataframe concatenate $other"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::string("abcza").into(),
|
||||
UntaggedValue::string("abcxs").into(),
|
||||
UntaggedValue::string("abccd").into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let other: Value = args.req(0)?;
|
||||
|
||||
let other_df = match &other.value {
|
||||
UntaggedValue::DataFrame(df) => Ok(df),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"can only concatenate another series",
|
||||
other.tag.span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let other_series = other_df.as_series(&other.tag.span)?;
|
||||
let other_chunked = other_series.utf8().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&other.tag.span,
|
||||
Some("The concatenate command can only be used with string columns"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
let chunked = series.utf8().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&df_tag.span,
|
||||
Some("The concatenate command can only be used with string columns"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut res = chunked.concat(other_chunked);
|
||||
|
||||
res.rename(series.name());
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
89
crates/nu-command/src/commands/dataframe/series/contains.rs
Normal file
89
crates/nu-command/src/commands/dataframe/series/contains.rs
Normal file
@ -0,0 +1,89 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe contains"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Checks if a pattern is contained in a string"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe contains").required(
|
||||
"pattern",
|
||||
SyntaxShape::String,
|
||||
"Regex pattern to be searched",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns boolean indicating if pattern was found",
|
||||
example: "[abc acb acb] | dataframe to-df | dataframe contains ab",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let pattern: Tagged<String> = args.req(0)?;
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
let chunked = series.utf8().map_err(|e| {
|
||||
parse_polars_error::<&str>(
|
||||
&e,
|
||||
&df_tag.span,
|
||||
Some("The contains command can only be used with string columns"),
|
||||
)
|
||||
})?;
|
||||
|
||||
let res = chunked
|
||||
.contains(&pattern.item)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
127
crates/nu-command/src/commands/dataframe/series/cumulative.rs
Normal file
127
crates/nu-command/src/commands/dataframe/series/cumulative.rs
Normal file
@ -0,0 +1,127 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use polars::prelude::DataType;
|
||||
|
||||
enum CumType {
|
||||
Min,
|
||||
Max,
|
||||
Sum,
|
||||
}
|
||||
|
||||
impl CumType {
|
||||
fn from_str(roll_type: &str, span: &Span) -> Result<Self, ShellError> {
|
||||
match roll_type {
|
||||
"min" => Ok(Self::Min),
|
||||
"max" => Ok(Self::Max),
|
||||
"sum" => Ok(Self::Sum),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Wrong operation",
|
||||
"Operation not valid for cumulative",
|
||||
span,
|
||||
"Perhaps you want to use: max, min, sum",
|
||||
span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
CumType::Min => "cum_min",
|
||||
CumType::Max => "cum_max",
|
||||
CumType::Sum => "cum_sum",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe cum"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Cumulative calculation for a series"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe cum")
|
||||
.required("type", SyntaxShape::String, "rolling operation")
|
||||
.switch("reverse", "Reverse cumulative calculation", Some('r'))
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Cumulative sum for a series",
|
||||
example: "[1 2 3 4 5] | dataframe to-df | dataframe cum sum",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0_cum_sum".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(6).into(),
|
||||
UntaggedValue::int(10).into(),
|
||||
UntaggedValue::int(15).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let cum_type: Tagged<String> = args.req(0)?;
|
||||
let reverse = args.has_flag("reverse");
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
if let DataType::Object(_) = series.dtype() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Found object series",
|
||||
"Series of type object cannot be used for cumulative operation",
|
||||
&df_tag.span,
|
||||
));
|
||||
}
|
||||
|
||||
let cum_type = CumType::from_str(&cum_type.item, &cum_type.tag.span)?;
|
||||
let mut res = match cum_type {
|
||||
CumType::Max => series.cummax(reverse),
|
||||
CumType::Min => series.cummin(reverse),
|
||||
CumType::Sum => series.cumsum(reverse),
|
||||
};
|
||||
|
||||
let name = format!("{}_{}", series.name(), cum_type.to_str());
|
||||
res.rename(&name);
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
75
crates/nu-command/src/commands/dataframe/series/get_day.rs
Normal file
75
crates/nu-command/src/commands/dataframe/series/get_day.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-day"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets day from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-day")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns day from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-day"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(4).into(), UntaggedValue::int(4).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.day().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
75
crates/nu-command/src/commands/dataframe/series/get_hour.rs
Normal file
75
crates/nu-command/src/commands/dataframe/series/get_hour.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-hour"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets hour from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-hour")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns hour from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-hour"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(16).into(), UntaggedValue::int(16).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.hour().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-minute"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets minute from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-minute")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns minute from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-minute"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(39).into(), UntaggedValue::int(39).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.minute().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
75
crates/nu-command/src/commands/dataframe/series/get_month.rs
Normal file
75
crates/nu-command/src/commands/dataframe/series/get_month.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-month"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets month from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-month")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns month from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-month"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(8).into(), UntaggedValue::int(8).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.month().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-nanosecond"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets nanosecond from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-nanosecond")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns nanosecond from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-nanosecond"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(0).into(), UntaggedValue::int(0).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.nanosecond().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-ordinal"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets ordinal date from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-ordinal")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns ordinal from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-ordinal"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(217).into(),
|
||||
UntaggedValue::int(217).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.ordinal().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-second"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets second from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-second")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns second from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-second"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(18).into(), UntaggedValue::int(18).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.second().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
75
crates/nu-command/src/commands/dataframe/series/get_week.rs
Normal file
75
crates/nu-command/src/commands/dataframe/series/get_week.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-week"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets week from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-week")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns week from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-week"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(32).into(), UntaggedValue::int(32).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.week().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-weekday"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets weekday from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-weekday")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns weekday from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-weekday"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![UntaggedValue::int(1).into(), UntaggedValue::int(1).into()],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.weekday().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
78
crates/nu-command/src/commands/dataframe/series/get_year.rs
Normal file
78
crates/nu-command/src/commands/dataframe/series/get_year.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"dataframe get-year"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"[Series] Gets year from date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("dataframe get-year")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Returns year from a date",
|
||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | str to-datetime -z 'UTC');
|
||||
let df = ([$dt $dt] | dataframe to-df);
|
||||
$df | dataframe get-year"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"0".to_string(),
|
||||
vec![
|
||||
UntaggedValue::int(2020).into(),
|
||||
UntaggedValue::int(2020).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let series = df.as_series(&df_tag.span)?;
|
||||
|
||||
let casted = series
|
||||
.date64()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &df_tag.span, None))?;
|
||||
|
||||
let res = casted.year().into_series();
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuSeries, Signature};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
@ -26,8 +29,24 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create mask indicating duplicated values",
|
||||
example: "[5 6 6 6 8 8 8] | dataframe to-series | dataframe is-duplicated",
|
||||
result: None,
|
||||
example: "[5 6 6 6 8 8 8] | dataframe to-df | dataframe is-duplicated",
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"is_duplicated".to_string(),
|
||||
vec![
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -35,15 +54,29 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = series
|
||||
.as_ref()
|
||||
let mut res = df
|
||||
.as_series(&df_tag.span)?
|
||||
.is_duplicated()
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?
|
||||
.into_series();
|
||||
|
||||
Ok(OutputStream::one(NuSeries::series_to_value(
|
||||
res.into_series(),
|
||||
tag,
|
||||
)))
|
||||
res.rename("is_duplicated");
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use crate::{commands::dataframe::utils::parse_polars_error, prelude::*};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuSeries, PolarsData},
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
@ -29,9 +29,25 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Checks if elements from a series are contained in right series",
|
||||
example: r#"let other = ([1 3 6] | dataframe to-series);
|
||||
[5 6 6 6 8 8 8] | dataframe to-series | dataframe is-in $other"#,
|
||||
result: None,
|
||||
example: r#"let other = ([1 3 6] | dataframe to-df);
|
||||
[5 6 6 6 8 8 8] | dataframe to-df | dataframe is-in $other"#,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"is_in".to_string(),
|
||||
vec![
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -40,8 +56,8 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let value: Value = args.req(0)?;
|
||||
|
||||
let other = match value.value {
|
||||
UntaggedValue::DataFrame(PolarsData::Series(series)) => Ok(series),
|
||||
let other_df = match value.value {
|
||||
UntaggedValue::DataFrame(df) => Ok(df),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type",
|
||||
"can only search in a series",
|
||||
@ -49,15 +65,31 @@ fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
)),
|
||||
}?;
|
||||
|
||||
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let other = other_df.as_series(&value.tag.span)?;
|
||||
|
||||
let res = series
|
||||
.as_ref()
|
||||
.is_in(other.as_ref())
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
Ok(OutputStream::one(NuSeries::series_to_value(
|
||||
res.into_series(),
|
||||
tag,
|
||||
)))
|
||||
let mut res = df
|
||||
.as_series(&df_tag.span)?
|
||||
.is_in(&other)
|
||||
.map_err(|e| parse_polars_error::<&str>(&e, &tag.span, None))?
|
||||
.into_series();
|
||||
|
||||
res.rename("is_in");
|
||||
|
||||
let df = NuDataFrame::try_from_series(vec![res], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuSeries, Signature};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
@ -26,10 +29,23 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create mask where values are not null",
|
||||
example: r#"let s = ([5 6 0 8] | dataframe to-series);
|
||||
example: r#"let s = ([5 6 0 8] | dataframe to-df);
|
||||
let res = ($s / $s);
|
||||
$res | dataframe is-not-null"#,
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"is_not_null".to_string(),
|
||||
vec![
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -37,12 +53,23 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = series.as_ref().is_not_null();
|
||||
let res = df.as_series(&df_tag.span)?.is_not_null();
|
||||
|
||||
Ok(OutputStream::one(NuSeries::series_to_value(
|
||||
res.into_series(),
|
||||
tag,
|
||||
)))
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::NuSeries, Signature};
|
||||
use nu_protocol::{
|
||||
dataframe::{Column, NuDataFrame},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
use polars::prelude::IntoSeries;
|
||||
|
||||
pub struct DataFrame;
|
||||
@ -26,10 +29,23 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create mask where values are null",
|
||||
example: r#"let s = ([5 6 0 8] | dataframe to-series);
|
||||
example: r#"let s = ([5 6 0 8] | dataframe to-df);
|
||||
let res = ($s / $s);
|
||||
$res | dataframe is-null"#,
|
||||
result: None,
|
||||
result: Some(vec![NuDataFrame::try_from_columns(
|
||||
vec![Column::new(
|
||||
"is_null".to_string(),
|
||||
vec![
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
UntaggedValue::boolean(true).into(),
|
||||
UntaggedValue::boolean(false).into(),
|
||||
],
|
||||
)],
|
||||
&Span::default(),
|
||||
)
|
||||
.expect("simple df for test should not fail")
|
||||
.into_value(Tag::default())]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -37,12 +53,23 @@ impl WholeStreamCommand for DataFrame {
|
||||
fn command(mut args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let series = NuSeries::try_from_stream(&mut args.input, &tag.span)?;
|
||||
let (df, df_tag) = NuDataFrame::try_from_stream(&mut args.input, &tag.span)?;
|
||||
|
||||
let res = series.as_ref().is_null();
|
||||
let res = df.as_series(&df_tag.span)?.is_null();
|
||||
|
||||
Ok(OutputStream::one(NuSeries::series_to_value(
|
||||
res.into_series(),
|
||||
tag,
|
||||
)))
|
||||
let df = NuDataFrame::try_from_series(vec![res.into_series()], &tag.span)?;
|
||||
Ok(OutputStream::one(df.into_value(df_tag)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::DataFrame;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test_dataframe as test_examples;
|
||||
|
||||
test_examples(DataFrame {})
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user