forked from extern/nushell
Compare commits
104 Commits
Author | SHA1 | Date | |
---|---|---|---|
7bf10b980c | |||
55c243a17b | |||
df526f73be | |||
29a77fd6ae | |||
be9ebd9e18 | |||
01f1208ad1 | |||
9dbb3e80fe | |||
a5c14ba7d4 | |||
4b11b283ac | |||
6fdfc84904 | |||
bff81f24aa | |||
ed515cbc0c | |||
87a6d7166c | |||
55baee9a9a | |||
0886afe650 | |||
872f6166e1 | |||
fe348e236f | |||
e0f083d117 | |||
48171f8e24 | |||
bcdf74562b | |||
3a5ee1aed0 | |||
d8c4b9c4fb | |||
6ae7884786 | |||
41834d16d6 | |||
1ee51f2afa | |||
65ee7aa372 | |||
ac38ee82f4 | |||
5fcc7f2328 | |||
3bcc2aad80 | |||
6165b6ae77 | |||
e335e4fddc | |||
5ab4199d71 | |||
f075e2459d | |||
94a26abf21 | |||
bcbdc33049 | |||
21ef3895b3 | |||
3e99dc01b0 | |||
3e325a1974 | |||
2b92e3e8a7 | |||
cb90b90cbf | |||
9776a252ee | |||
751de20f93 | |||
28388b4e3a | |||
4fdbf30308 | |||
722f191e82 | |||
20f6114617 | |||
3075e2cfbf | |||
e2973d2176 | |||
0ff08bb63a | |||
08c0bf52bc | |||
d0229cb96e | |||
0612e5ccfb | |||
1b4f7b34c8 | |||
86e6fcd309 | |||
dc9cd7d8b9 | |||
c0cc9ce7cd | |||
be2f66397b | |||
07760b4129 | |||
d79a3130b8 | |||
440a589f9e | |||
f5fcf9d635 | |||
9b8b1bad57 | |||
874ecd6c88 | |||
57e2fec497 | |||
0905a2c3a2 | |||
3aa00b78f9 | |||
6769d46dbb | |||
efac712f62 | |||
2bb23c57df | |||
bc699a2cc1 | |||
758c128147 | |||
3795c2a39d | |||
311c0e3f50 | |||
25a8caa9b0 | |||
c80a9585b0 | |||
e73491441a | |||
b93b80ccaa | |||
48128c9db6 | |||
6dafaa197d | |||
1634d8e087 | |||
7a583083b8 | |||
75156ab0c9 | |||
9fd6923821 | |||
91a929b2a9 | |||
0f8e31af06 | |||
bd71c2f34d | |||
001123dbd6 | |||
cfee151d4e | |||
fc59291191 | |||
4fc05cac56 | |||
cc4616f25b | |||
e82fbb7bcf | |||
8cd639f6a2 | |||
a8f555856a | |||
3792562046 | |||
d05c48a1d7 | |||
36cc5eb933 | |||
f9f74a0f7d | |||
77f42931ff | |||
73f62266c6 | |||
df2f3d25b0 | |||
599c43ce04 | |||
5c2199e7f4 | |||
3ad4e0348f |
1759
Cargo.lock
generated
1759
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
81
Cargo.toml
81
Cargo.toml
@ -10,7 +10,7 @@ license = "MIT"
|
||||
name = "nu"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
version = "0.30.0"
|
||||
version = "0.32.0"
|
||||
|
||||
[workspace]
|
||||
members = ["crates/*/"]
|
||||
@ -18,36 +18,36 @@ members = ["crates/*/"]
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nu-cli = { version = "0.30.0", path = "./crates/nu-cli", default-features = false }
|
||||
nu-command = { version = "0.30.0", path = "./crates/nu-command" }
|
||||
nu-data = { version = "0.30.0", path = "./crates/nu-data" }
|
||||
nu-engine = { version = "0.30.0", path = "./crates/nu-engine" }
|
||||
nu-errors = { version = "0.30.0", path = "./crates/nu-errors" }
|
||||
nu-parser = { version = "0.30.0", path = "./crates/nu-parser" }
|
||||
nu-plugin = { version = "0.30.0", path = "./crates/nu-plugin" }
|
||||
nu-protocol = { version = "0.30.0", path = "./crates/nu-protocol" }
|
||||
nu-source = { version = "0.30.0", path = "./crates/nu-source" }
|
||||
nu-value-ext = { version = "0.30.0", path = "./crates/nu-value-ext" }
|
||||
nu-cli = { version = "0.32.0", path = "./crates/nu-cli", default-features = false }
|
||||
nu-command = { version = "0.32.0", path = "./crates/nu-command" }
|
||||
nu-data = { version = "0.32.0", path = "./crates/nu-data" }
|
||||
nu-engine = { version = "0.32.0", path = "./crates/nu-engine" }
|
||||
nu-errors = { version = "0.32.0", path = "./crates/nu-errors" }
|
||||
nu-parser = { version = "0.32.0", path = "./crates/nu-parser" }
|
||||
nu-plugin = { version = "0.32.0", path = "./crates/nu-plugin" }
|
||||
nu-protocol = { version = "0.32.0", path = "./crates/nu-protocol" }
|
||||
nu-source = { version = "0.32.0", path = "./crates/nu-source" }
|
||||
nu-value-ext = { version = "0.32.0", path = "./crates/nu-value-ext" }
|
||||
|
||||
nu_plugin_binaryview = { version = "0.30.0", path = "./crates/nu_plugin_binaryview", optional = true }
|
||||
nu_plugin_chart = { version = "0.30.0", path = "./crates/nu_plugin_chart", optional = true }
|
||||
nu_plugin_fetch = { version = "0.30.0", path = "./crates/nu_plugin_fetch", optional = true }
|
||||
nu_plugin_from_bson = { version = "0.30.0", path = "./crates/nu_plugin_from_bson", optional = true }
|
||||
nu_plugin_from_sqlite = { version = "0.30.0", path = "./crates/nu_plugin_from_sqlite", optional = true }
|
||||
nu_plugin_inc = { version = "0.30.0", path = "./crates/nu_plugin_inc", optional = true }
|
||||
nu_plugin_match = { version = "0.30.0", path = "./crates/nu_plugin_match", optional = true }
|
||||
nu_plugin_post = { version = "0.30.0", path = "./crates/nu_plugin_post", optional = true }
|
||||
nu_plugin_ps = { version = "0.30.0", path = "./crates/nu_plugin_ps", optional = true }
|
||||
nu_plugin_query_json = { version = "0.30.0", path = "./crates/nu_plugin_query_json", optional = true }
|
||||
nu_plugin_s3 = { version = "0.30.0", path = "./crates/nu_plugin_s3", optional = true }
|
||||
nu_plugin_selector = { version = "0.30.0", path = "./crates/nu_plugin_selector", optional = true }
|
||||
nu_plugin_start = { version = "0.30.0", path = "./crates/nu_plugin_start", optional = true }
|
||||
nu_plugin_sys = { version = "0.30.0", path = "./crates/nu_plugin_sys", optional = true }
|
||||
nu_plugin_textview = { version = "0.30.0", path = "./crates/nu_plugin_textview", optional = true }
|
||||
nu_plugin_to_bson = { version = "0.30.0", path = "./crates/nu_plugin_to_bson", optional = true }
|
||||
nu_plugin_to_sqlite = { version = "0.30.0", path = "./crates/nu_plugin_to_sqlite", optional = true }
|
||||
nu_plugin_tree = { version = "0.30.0", path = "./crates/nu_plugin_tree", optional = true }
|
||||
nu_plugin_xpath = { version = "0.30.0", path = "./crates/nu_plugin_xpath", optional = true }
|
||||
nu_plugin_binaryview = { version = "0.32.0", path = "./crates/nu_plugin_binaryview", optional = true }
|
||||
nu_plugin_chart = { version = "0.32.0", path = "./crates/nu_plugin_chart", optional = true }
|
||||
nu_plugin_fetch = { version = "0.32.0", path = "./crates/nu_plugin_fetch", optional = true }
|
||||
nu_plugin_from_bson = { version = "0.32.0", path = "./crates/nu_plugin_from_bson", optional = true }
|
||||
nu_plugin_from_sqlite = { version = "0.32.0", path = "./crates/nu_plugin_from_sqlite", optional = true }
|
||||
nu_plugin_inc = { version = "0.32.0", path = "./crates/nu_plugin_inc", optional = true }
|
||||
nu_plugin_match = { version = "0.32.0", path = "./crates/nu_plugin_match", optional = true }
|
||||
nu_plugin_post = { version = "0.32.0", path = "./crates/nu_plugin_post", optional = true }
|
||||
nu_plugin_ps = { version = "0.32.0", path = "./crates/nu_plugin_ps", optional = true }
|
||||
nu_plugin_query_json = { version = "0.32.0", path = "./crates/nu_plugin_query_json", optional = true }
|
||||
nu_plugin_s3 = { version = "0.32.0", path = "./crates/nu_plugin_s3", optional = true }
|
||||
nu_plugin_selector = { version = "0.32.0", path = "./crates/nu_plugin_selector", optional = true }
|
||||
nu_plugin_start = { version = "0.32.0", path = "./crates/nu_plugin_start", optional = true }
|
||||
nu_plugin_sys = { version = "0.32.0", path = "./crates/nu_plugin_sys", optional = true }
|
||||
nu_plugin_textview = { version = "0.32.0", path = "./crates/nu_plugin_textview", optional = true }
|
||||
nu_plugin_to_bson = { version = "0.32.0", path = "./crates/nu_plugin_to_bson", optional = true }
|
||||
nu_plugin_to_sqlite = { version = "0.32.0", path = "./crates/nu_plugin_to_sqlite", optional = true }
|
||||
nu_plugin_tree = { version = "0.32.0", path = "./crates/nu_plugin_tree", optional = true }
|
||||
nu_plugin_xpath = { version = "0.32.0", path = "./crates/nu_plugin_xpath", optional = true }
|
||||
|
||||
# Required to bootstrap the main binary
|
||||
clap = "2.33.3"
|
||||
@ -58,7 +58,7 @@ log = "0.4.14"
|
||||
pretty_env_logger = "0.4.0"
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { version = "0.30.0", path = "./crates/nu-test-support" }
|
||||
nu-test-support = { version = "0.32.0", path = "./crates/nu-test-support" }
|
||||
dunce = "1.0.1"
|
||||
serial_test = "0.5.1"
|
||||
hamcrest2 = "0.3.0"
|
||||
@ -81,13 +81,7 @@ ptree-support = ["nu-cli/ptree", "nu-command/ptree"]
|
||||
rustyline-support = ["nu-cli/rustyline-support", "nu-command/rustyline-support"]
|
||||
term-support = ["nu-cli/term", "nu-command/term"]
|
||||
uuid-support = ["nu-cli/uuid_crate", "nu-command/uuid_crate"]
|
||||
which-support = [
|
||||
"nu-cli/ichwh",
|
||||
"nu-cli/which",
|
||||
"nu-command/ichwh",
|
||||
"nu-command/which",
|
||||
"nu-engine/which",
|
||||
]
|
||||
which-support = ["nu-cli/which", "nu-command/which", "nu-engine/which"]
|
||||
|
||||
default = [
|
||||
"nu-cli/shadow-rs",
|
||||
@ -160,6 +154,17 @@ zip-support = ["nu-cli/zip", "nu-command/zip"]
|
||||
#This is disabled in extra for now
|
||||
table-pager = ["nu-command/table-pager"]
|
||||
|
||||
#dataframe feature for nushell
|
||||
dataframe = [
|
||||
"nu-protocol/dataframe",
|
||||
"nu-command/dataframe",
|
||||
"nu-value-ext/dataframe",
|
||||
"nu-data/dataframe",
|
||||
"nu_plugin_post/dataframe",
|
||||
"nu_plugin_to_bson/dataframe",
|
||||
]
|
||||
|
||||
|
||||
[profile.release]
|
||||
#strip = "symbols" #Couldn't get working +nightly
|
||||
codegen-units = 1 #Reduce parallel codegen units
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 - 2020 Yehuda Katz, Jonathan Turner
|
||||
Copyright (c) 2019 - 2021 Yehuda Katz, Jonathan Turner
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -47,7 +47,7 @@ Try it in Gitpod.
|
||||
|
||||
Up-to-date installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). **Windows users**: please note that Nu works on Windows 10 and does not currently have Windows 7/8.1 support.
|
||||
|
||||
To build Nu, you will need to use the **latest stable (1.47 or later)** version of the compiler.
|
||||
To build Nu, you will need to use the **latest stable (1.51 or later)** version of the compiler.
|
||||
|
||||
Required dependencies:
|
||||
|
||||
@ -220,7 +220,7 @@ We can pipeline this into a command that gets the contents of one of the columns
|
||||
name │ nu
|
||||
readme │ README.md
|
||||
repository │ https://github.com/nushell/nushell
|
||||
version │ 0.21.0
|
||||
version │ 0.32.0
|
||||
───────────────┴────────────────────────────────────
|
||||
```
|
||||
|
||||
@ -228,7 +228,7 @@ Finally, we can use commands outside of Nu once we have the data we want:
|
||||
|
||||
```shell
|
||||
> open Cargo.toml | get package.version
|
||||
0.21.0
|
||||
0.32.0
|
||||
```
|
||||
|
||||
Here we use the variable `$it` to refer to the value being piped to the external command.
|
||||
|
@ -9,7 +9,7 @@ description = "Library for ANSI terminal colors and styles (bold, underline)"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-ansi-term"
|
||||
version = "0.30.0"
|
||||
version = "0.32.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
@ -5,26 +5,27 @@ description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-cli"
|
||||
version = "0.30.0"
|
||||
version = "0.32.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-command = { version = "0.30.0", path = "../nu-command" }
|
||||
nu-data = { version = "0.30.0", path = "../nu-data" }
|
||||
nu-engine = { version = "0.30.0", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.30.0", path = "../nu-errors" }
|
||||
nu-json = { version = "0.30.0", path = "../nu-json" }
|
||||
nu-parser = { version = "0.30.0", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.30.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.30.0", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.30.0", path = "../nu-source" }
|
||||
nu-stream = { version = "0.30.0", path = "../nu-stream" }
|
||||
nu-table = { version = "0.30.0", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.30.0", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.30.0", path = "../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.30.0", path = "../nu-ansi-term" }
|
||||
nu-command = { version = "0.32.0", path = "../nu-command" }
|
||||
nu-data = { version = "0.32.0", path = "../nu-data" }
|
||||
nu-engine = { version = "0.32.0", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.32.0", path = "../nu-errors" }
|
||||
nu-json = { version = "0.32.0", path = "../nu-json" }
|
||||
nu-parser = { version = "0.32.0", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.32.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.32.0", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.32.0", path = "../nu-source" }
|
||||
nu-stream = { version = "0.32.0", path = "../nu-stream" }
|
||||
nu-table = { version = "0.32.0", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.32.0", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.32.0", path = "../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.32.0", path = "../nu-ansi-term" }
|
||||
nu-pretty-hex = { version = "0.32.0", path = "../nu-pretty-hex" }
|
||||
|
||||
Inflector = "0.11"
|
||||
arboard = { version = "1.1.0", optional = true }
|
||||
@ -57,7 +58,6 @@ getset = "0.1.1"
|
||||
glob = "0.3.0"
|
||||
htmlescape = "0.3.1"
|
||||
ical = "0.7.0"
|
||||
ichwh = { version = "0.3.4", optional = true }
|
||||
indexmap = { version = "1.6.1", features = ["serde-1"] }
|
||||
itertools = "0.10.0"
|
||||
lazy_static = "1.*"
|
||||
@ -68,7 +68,6 @@ num-format = { version = "0.4.0", features = ["with-num-bigint"] }
|
||||
num-traits = "0.2.14"
|
||||
parking_lot = "0.11.1"
|
||||
pin-utils = "0.1.0"
|
||||
pretty-hex = "0.2.1"
|
||||
ptree = { version = "0.3.1", optional = true }
|
||||
query_interface = "0.3.5"
|
||||
quick-xml = "0.21.0"
|
||||
@ -77,7 +76,7 @@ rayon = "1.5.0"
|
||||
regex = "1.4.3"
|
||||
roxmltree = "0.14.0"
|
||||
rust-embed = "5.9.0"
|
||||
rustyline = { version = "8.0.0", optional = true }
|
||||
rustyline = { version = "8.1.0", optional = true }
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
serde_bytes = "0.11.5"
|
||||
serde_ini = "0.2.0"
|
||||
@ -116,7 +115,7 @@ users = "0.11.0"
|
||||
[dependencies.rusqlite]
|
||||
features = ["bundled", "blob"]
|
||||
optional = true
|
||||
version = "0.24.2"
|
||||
version = "0.25.3"
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = "0.5"
|
||||
|
@ -229,13 +229,7 @@ pub fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn E
|
||||
let prompt_line = prompt.as_string()?;
|
||||
|
||||
context.scope.enter_scope();
|
||||
let (mut prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
|
||||
|
||||
if let Some(block) =
|
||||
std::sync::Arc::<nu_protocol::hir::Block>::get_mut(&mut prompt_block)
|
||||
{
|
||||
block.set_redirect(ExternalRedirection::Stdout);
|
||||
}
|
||||
let (prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
|
||||
|
||||
if err.is_some() {
|
||||
context.scope.exit_scope();
|
||||
@ -250,7 +244,12 @@ pub fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn E
|
||||
nu_ansi_term::ansi::RESET
|
||||
)
|
||||
} else {
|
||||
let run_result = run_block(&prompt_block, &context, InputStream::empty());
|
||||
let run_result = run_block(
|
||||
&prompt_block,
|
||||
&context,
|
||||
InputStream::empty(),
|
||||
ExternalRedirection::Stdout,
|
||||
);
|
||||
context.scope.exit_scope();
|
||||
|
||||
match run_result {
|
||||
@ -302,7 +301,9 @@ pub fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn E
|
||||
}
|
||||
};
|
||||
|
||||
rl.helper_mut().expect("No helper").colored_prompt = colored_prompt;
|
||||
if let Some(helper) = rl.helper_mut() {
|
||||
helper.colored_prompt = colored_prompt;
|
||||
}
|
||||
let mut initial_command = Some(String::new());
|
||||
let mut readline = Err(ReadlineError::Eof);
|
||||
while let Some(ref cmd) = initial_command {
|
||||
@ -485,7 +486,12 @@ pub fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, She
|
||||
|
||||
let input_stream = InputStream::empty();
|
||||
|
||||
let result = run_block(&classified_block, ctx, input_stream);
|
||||
let result = run_block(
|
||||
&classified_block,
|
||||
ctx,
|
||||
input_stream,
|
||||
ExternalRedirection::Stdout,
|
||||
);
|
||||
ctx.scope.exit_scope();
|
||||
|
||||
result?.collect_string(Tag::unknown()).map(|x| x.item)
|
||||
@ -509,14 +515,14 @@ fn current_branch() -> String {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nu_engine::basic_evaluation_context;
|
||||
use nu_engine::EvaluationContext;
|
||||
|
||||
#[quickcheck]
|
||||
fn quickcheck_parse(data: String) -> bool {
|
||||
let (tokens, err) = nu_parser::lex(&data, 0);
|
||||
let (lite_block, err2) = nu_parser::parse_block(tokens);
|
||||
if err.is_none() && err2.is_none() {
|
||||
let context = basic_evaluation_context().unwrap();
|
||||
let context = EvaluationContext::basic();
|
||||
let _ = nu_parser::classify_block(&lite_block, &context.scope);
|
||||
}
|
||||
true
|
||||
|
@ -6,6 +6,7 @@ use indexmap::set::IndexSet;
|
||||
use super::matchers::Matcher;
|
||||
use crate::completion::{Completer, CompletionContext, Suggestion};
|
||||
use nu_engine::EvaluationContext;
|
||||
use nu_test_support::NATIVE_PATH_ENV_VAR;
|
||||
|
||||
pub struct CommandCompleter;
|
||||
|
||||
@ -121,7 +122,7 @@ fn is_executable(path: &Path) -> bool {
|
||||
|
||||
// TODO cache these, but watch for changes to PATH
|
||||
fn find_path_executables() -> Option<IndexSet<String>> {
|
||||
let path_var = std::env::var_os("PATH")?;
|
||||
let path_var = std::env::var_os(NATIVE_PATH_ENV_VAR)?;
|
||||
let paths: Vec<_> = std::env::split_paths(&path_var).collect();
|
||||
|
||||
let mut executables: IndexSet<String> = IndexSet::new();
|
||||
|
@ -37,7 +37,7 @@ impl<'s> Flatten<'s> {
|
||||
)
|
||||
.collect(),
|
||||
Expression::Command => vec![LocationType::Command.spanned(e.span)],
|
||||
Expression::Path(path) => self.expression(&path.head),
|
||||
Expression::FullColumnPath(path) => self.expression(&path.head),
|
||||
Expression::Variable(_, _) => vec![LocationType::Variable.spanned(e.span)],
|
||||
|
||||
Expression::Boolean(_)
|
||||
@ -384,7 +384,7 @@ mod tests {
|
||||
#[test]
|
||||
fn completes_incomplete_nested_structure() {
|
||||
let registry: VecRegistry = vec![Signature::build("sys")].into();
|
||||
let line = "echo $(sy";
|
||||
let line = "echo (sy";
|
||||
|
||||
assert_eq!(
|
||||
completion_location(line, ®istry, 8),
|
||||
|
@ -1,63 +1,42 @@
|
||||
use rustyline::{KeyCode, Modifiers};
|
||||
use rustyline::{KeyCode as RustyKeyCode, Modifiers};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub fn convert_keyevent(key_event: KeyEvent) -> rustyline::KeyEvent {
|
||||
pub fn convert_keyevent(key_event: KeyCode, modifiers: Option<Modifiers>) -> rustyline::KeyEvent {
|
||||
match key_event {
|
||||
KeyEvent::UnknownEscSeq => convert_to_rl_keyevent(rustyline::KeyCode::UnknownEscSeq, None),
|
||||
KeyEvent::Backspace => convert_to_rl_keyevent(rustyline::KeyCode::Backspace, None),
|
||||
KeyEvent::BackTab => convert_to_rl_keyevent(rustyline::KeyCode::BackTab, None),
|
||||
KeyEvent::BracketedPasteStart => {
|
||||
convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteStart, None)
|
||||
KeyCode::UnknownEscSeq => convert_to_rl_keyevent(RustyKeyCode::UnknownEscSeq, modifiers),
|
||||
KeyCode::Backspace => convert_to_rl_keyevent(RustyKeyCode::Backspace, modifiers),
|
||||
KeyCode::BackTab => convert_to_rl_keyevent(RustyKeyCode::BackTab, modifiers),
|
||||
KeyCode::BracketedPasteStart => {
|
||||
convert_to_rl_keyevent(RustyKeyCode::BracketedPasteStart, modifiers)
|
||||
}
|
||||
KeyEvent::BracketedPasteEnd => {
|
||||
convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteEnd, None)
|
||||
KeyCode::BracketedPasteEnd => {
|
||||
convert_to_rl_keyevent(RustyKeyCode::BracketedPasteEnd, modifiers)
|
||||
}
|
||||
KeyEvent::Char(c) => convert_to_rl_keyevent(rustyline::KeyCode::Char(c), None),
|
||||
KeyEvent::ControlDown => {
|
||||
convert_to_rl_keyevent(rustyline::KeyCode::Down, Some(Modifiers::CTRL))
|
||||
}
|
||||
KeyEvent::ControlLeft => {
|
||||
convert_to_rl_keyevent(rustyline::KeyCode::Left, Some(Modifiers::CTRL))
|
||||
}
|
||||
KeyEvent::ControlRight => {
|
||||
convert_to_rl_keyevent(rustyline::KeyCode::Right, Some(Modifiers::CTRL))
|
||||
}
|
||||
KeyEvent::ControlUp => {
|
||||
convert_to_rl_keyevent(rustyline::KeyCode::Up, Some(Modifiers::CTRL))
|
||||
}
|
||||
KeyEvent::Ctrl(c) => rustyline::KeyEvent::ctrl(c),
|
||||
KeyEvent::Delete => convert_to_rl_keyevent(rustyline::KeyCode::Delete, None),
|
||||
KeyEvent::Down => convert_to_rl_keyevent(rustyline::KeyCode::Down, None),
|
||||
KeyEvent::End => convert_to_rl_keyevent(rustyline::KeyCode::End, None),
|
||||
KeyEvent::Enter => convert_to_rl_keyevent(rustyline::KeyCode::Enter, None),
|
||||
KeyEvent::Esc => convert_to_rl_keyevent(rustyline::KeyCode::Esc, None),
|
||||
KeyEvent::F(u) => convert_to_rl_keyevent(rustyline::KeyCode::F(u), None),
|
||||
KeyEvent::Home => convert_to_rl_keyevent(rustyline::KeyCode::Home, None),
|
||||
KeyEvent::Insert => convert_to_rl_keyevent(rustyline::KeyCode::Insert, None),
|
||||
KeyEvent::Left => convert_to_rl_keyevent(rustyline::KeyCode::Left, None),
|
||||
KeyEvent::Meta(c) => rustyline::KeyEvent::new(c, Modifiers::NONE),
|
||||
KeyEvent::Null => convert_to_rl_keyevent(rustyline::KeyCode::Null, None),
|
||||
KeyEvent::PageDown => convert_to_rl_keyevent(rustyline::KeyCode::PageDown, None),
|
||||
KeyEvent::PageUp => convert_to_rl_keyevent(rustyline::KeyCode::PageUp, None),
|
||||
KeyEvent::Right => convert_to_rl_keyevent(rustyline::KeyCode::Right, None),
|
||||
KeyEvent::ShiftDown => {
|
||||
convert_to_rl_keyevent(rustyline::KeyCode::Down, Some(Modifiers::SHIFT))
|
||||
}
|
||||
KeyEvent::ShiftLeft => {
|
||||
convert_to_rl_keyevent(rustyline::KeyCode::Left, Some(Modifiers::SHIFT))
|
||||
}
|
||||
KeyEvent::ShiftRight => {
|
||||
convert_to_rl_keyevent(rustyline::KeyCode::Right, Some(Modifiers::SHIFT))
|
||||
}
|
||||
KeyEvent::ShiftUp => convert_to_rl_keyevent(rustyline::KeyCode::Up, Some(Modifiers::SHIFT)),
|
||||
KeyEvent::Tab => convert_to_rl_keyevent(rustyline::KeyCode::Tab, None),
|
||||
KeyEvent::Up => convert_to_rl_keyevent(rustyline::KeyCode::Up, None),
|
||||
KeyCode::Char(c) => convert_to_rl_keyevent(RustyKeyCode::Char(c), modifiers),
|
||||
KeyCode::Delete => convert_to_rl_keyevent(RustyKeyCode::Delete, modifiers),
|
||||
KeyCode::Down => convert_to_rl_keyevent(RustyKeyCode::Down, modifiers),
|
||||
KeyCode::End => convert_to_rl_keyevent(RustyKeyCode::End, modifiers),
|
||||
KeyCode::Enter => convert_to_rl_keyevent(RustyKeyCode::Enter, modifiers),
|
||||
KeyCode::Esc => convert_to_rl_keyevent(RustyKeyCode::Esc, modifiers),
|
||||
KeyCode::F(u) => convert_to_rl_keyevent(RustyKeyCode::F(u), modifiers),
|
||||
KeyCode::Home => convert_to_rl_keyevent(RustyKeyCode::Home, modifiers),
|
||||
KeyCode::Insert => convert_to_rl_keyevent(RustyKeyCode::Insert, modifiers),
|
||||
KeyCode::Left => convert_to_rl_keyevent(RustyKeyCode::Left, modifiers),
|
||||
KeyCode::Null => convert_to_rl_keyevent(RustyKeyCode::Null, modifiers),
|
||||
KeyCode::PageDown => convert_to_rl_keyevent(RustyKeyCode::PageDown, modifiers),
|
||||
KeyCode::PageUp => convert_to_rl_keyevent(RustyKeyCode::PageUp, modifiers),
|
||||
KeyCode::Right => convert_to_rl_keyevent(RustyKeyCode::Right, modifiers),
|
||||
KeyCode::Tab => convert_to_rl_keyevent(RustyKeyCode::Tab, modifiers),
|
||||
KeyCode::Up => convert_to_rl_keyevent(RustyKeyCode::Up, modifiers),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_rl_keyevent(key_event: KeyCode, modifier: Option<Modifiers>) -> rustyline::KeyEvent {
|
||||
fn convert_to_rl_keyevent(
|
||||
key_code: RustyKeyCode,
|
||||
modifier: Option<Modifiers>,
|
||||
) -> rustyline::KeyEvent {
|
||||
rustyline::KeyEvent {
|
||||
0: key_event,
|
||||
0: key_code,
|
||||
1: modifier.unwrap_or(Modifiers::NONE),
|
||||
}
|
||||
}
|
||||
@ -132,12 +111,14 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
|
||||
Cmd::Complete => rustyline::Cmd::Complete,
|
||||
Cmd::CompleteBackward => rustyline::Cmd::CompleteBackward,
|
||||
Cmd::CompleteHint => rustyline::Cmd::CompleteHint,
|
||||
Cmd::Dedent(movement) => rustyline::Cmd::Dedent(convert_movement(movement)),
|
||||
Cmd::DowncaseWord => rustyline::Cmd::DowncaseWord,
|
||||
Cmd::EndOfFile => rustyline::Cmd::EndOfFile,
|
||||
Cmd::EndOfHistory => rustyline::Cmd::EndOfHistory,
|
||||
Cmd::ForwardSearchHistory => rustyline::Cmd::ForwardSearchHistory,
|
||||
Cmd::HistorySearchBackward => rustyline::Cmd::HistorySearchBackward,
|
||||
Cmd::HistorySearchForward => rustyline::Cmd::HistorySearchForward,
|
||||
Cmd::Indent(movement) => rustyline::Cmd::Indent(convert_movement(movement)),
|
||||
Cmd::Insert { repeat, string } => rustyline::Cmd::Insert(repeat, string),
|
||||
Cmd::Interrupt => rustyline::Cmd::Interrupt,
|
||||
Cmd::Kill(movement) => rustyline::Cmd::Kill(convert_movement(movement)),
|
||||
@ -145,8 +126,11 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
|
||||
Cmd::LineUpOrPreviousHistory(u) => rustyline::Cmd::LineUpOrPreviousHistory(u),
|
||||
Cmd::Move(movement) => rustyline::Cmd::Move(convert_movement(movement)),
|
||||
Cmd::NextHistory => rustyline::Cmd::NextHistory,
|
||||
Cmd::Newline => rustyline::Cmd::Newline,
|
||||
Cmd::Noop => rustyline::Cmd::Noop,
|
||||
Cmd::Overwrite(c) => rustyline::Cmd::Overwrite(c),
|
||||
#[cfg(windows)]
|
||||
Cmd::PasteFromClipboard => rustyline::Cmd::PasteFromClipboard,
|
||||
Cmd::PreviousHistory => rustyline::Cmd::PreviousHistory,
|
||||
Cmd::QuotedInsert => rustyline::Cmd::QuotedInsert,
|
||||
Cmd::Replace {
|
||||
@ -169,14 +153,28 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
|
||||
}
|
||||
|
||||
fn convert_keybinding(keybinding: Keybinding) -> (rustyline::KeyEvent, rustyline::Cmd) {
|
||||
let rusty_modifiers = match keybinding.modifiers {
|
||||
Some(mods) => match mods {
|
||||
NuModifiers::CTRL => Some(Modifiers::CTRL),
|
||||
NuModifiers::ALT => Some(Modifiers::ALT),
|
||||
NuModifiers::SHIFT => Some(Modifiers::SHIFT),
|
||||
NuModifiers::NONE => Some(Modifiers::NONE),
|
||||
NuModifiers::CTRL_SHIFT => Some(Modifiers::CTRL_SHIFT),
|
||||
NuModifiers::ALT_SHIFT => Some(Modifiers::ALT_SHIFT),
|
||||
NuModifiers::CTRL_ALT => Some(Modifiers::CTRL_ALT),
|
||||
NuModifiers::CTRL_ALT_SHIFT => Some(Modifiers::CTRL_ALT_SHIFT),
|
||||
// _ => None,
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
(
|
||||
convert_keyevent(keybinding.key),
|
||||
convert_keyevent(keybinding.key, rusty_modifiers),
|
||||
convert_cmd(keybinding.binding),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum KeyEvent {
|
||||
pub enum KeyCode {
|
||||
/// Unsupported escape sequence (on unix platform)
|
||||
UnknownEscSeq,
|
||||
/// ⌫ or `KeyEvent::Ctrl('H')`
|
||||
@ -189,16 +187,6 @@ pub enum KeyEvent {
|
||||
BracketedPasteEnd,
|
||||
/// Single char
|
||||
Char(char),
|
||||
/// Ctrl-↓
|
||||
ControlDown,
|
||||
/// Ctrl-←
|
||||
ControlLeft,
|
||||
/// Ctrl-→
|
||||
ControlRight,
|
||||
/// Ctrl-↑
|
||||
ControlUp,
|
||||
/// Ctrl-char
|
||||
Ctrl(char),
|
||||
/// ⌦
|
||||
Delete,
|
||||
/// ↓ arrow key
|
||||
@ -217,9 +205,7 @@ pub enum KeyEvent {
|
||||
Insert,
|
||||
/// ← arrow key
|
||||
Left,
|
||||
/// Escape-char or Alt-char
|
||||
Meta(char),
|
||||
/// `KeyEvent::Char('\0')`
|
||||
// /// `KeyEvent::Char('\0')`
|
||||
Null,
|
||||
/// ⇟
|
||||
PageDown,
|
||||
@ -227,14 +213,6 @@ pub enum KeyEvent {
|
||||
PageUp,
|
||||
/// → arrow key
|
||||
Right,
|
||||
/// Shift-↓
|
||||
ShiftDown,
|
||||
/// Shift-←
|
||||
ShiftLeft,
|
||||
/// Shift-→
|
||||
ShiftRight,
|
||||
/// Shift-↑
|
||||
ShiftUp,
|
||||
/// ⇥ or `KeyEvent::Ctrl('I')`
|
||||
Tab,
|
||||
/// ↑ arrow key
|
||||
@ -259,6 +237,8 @@ pub enum Cmd {
|
||||
CompleteBackward,
|
||||
/// complete-hint
|
||||
CompleteHint,
|
||||
/// Dedent current line
|
||||
Dedent(Movement),
|
||||
/// downcase-word
|
||||
DowncaseWord,
|
||||
/// vi-eof-maybe
|
||||
@ -271,6 +251,8 @@ pub enum Cmd {
|
||||
HistorySearchBackward,
|
||||
/// history-search-forward
|
||||
HistorySearchForward,
|
||||
/// Indent current line
|
||||
Indent(Movement),
|
||||
/// Insert text
|
||||
Insert { repeat: RepeatCount, string: String },
|
||||
/// Interrupt signal (Ctrl-C)
|
||||
@ -283,12 +265,17 @@ pub enum Cmd {
|
||||
/// forward-char, forward-word, vi-char-search, vi-end-word, vi-next-word,
|
||||
/// vi-prev-word
|
||||
Move(Movement),
|
||||
/// Inserts a newline
|
||||
Newline,
|
||||
/// next-history
|
||||
NextHistory,
|
||||
/// No action
|
||||
Noop,
|
||||
/// vi-replace
|
||||
Overwrite(char),
|
||||
/// Paste from the clipboard
|
||||
#[cfg(windows)]
|
||||
PasteFromClipboard,
|
||||
/// previous-history
|
||||
PreviousHistory,
|
||||
/// quoted-insert
|
||||
@ -422,12 +409,36 @@ pub enum CharSearch {
|
||||
BackwardAfter(char),
|
||||
}
|
||||
|
||||
/// The set of modifier keys that were triggered along with a key press.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(clippy::clippy::upper_case_acronyms)]
|
||||
pub enum NuModifiers {
|
||||
/// Control modifier
|
||||
CTRL = 8,
|
||||
/// Escape or Alt modifier
|
||||
ALT = 4,
|
||||
/// Shift modifier
|
||||
SHIFT = 2,
|
||||
/// No modifier
|
||||
NONE = 0,
|
||||
/// Ctrl + Shift
|
||||
CTRL_SHIFT = 10,
|
||||
/// Alt + Shift
|
||||
ALT_SHIFT = 6,
|
||||
/// Ctrl + Alt
|
||||
CTRL_ALT = 12,
|
||||
/// Ctrl + Alt + Shift
|
||||
CTRL_ALT_SHIFT = 14,
|
||||
}
|
||||
|
||||
/// The number of times one command should be repeated.
|
||||
pub type RepeatCount = usize;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Keybinding {
|
||||
key: KeyEvent,
|
||||
key: KeyCode,
|
||||
modifiers: Option<NuModifiers>,
|
||||
binding: Cmd,
|
||||
}
|
||||
|
||||
@ -442,7 +453,7 @@ pub(crate) fn load_keybindings(
|
||||
// Silently fail if there is no file there
|
||||
if let Ok(contents) = contents {
|
||||
let keybindings: Keybindings = serde_yaml::from_str(&contents)?;
|
||||
|
||||
// eprint!("{}{}{}", keybindings.key, keybindings.mo);
|
||||
for keybinding in keybindings.into_iter() {
|
||||
let (k, b) = convert_keybinding(keybinding);
|
||||
|
||||
|
@ -8,7 +8,7 @@ use crate::prelude::*;
|
||||
use nu_engine::script::LineResult;
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
use crate::keybinding::{convert_keyevent, KeyEvent};
|
||||
use crate::keybinding::{convert_keyevent, KeyCode};
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
use crate::shell::Helper;
|
||||
@ -19,7 +19,8 @@ use rustyline::{
|
||||
config::Configurer,
|
||||
config::{ColorMode, CompletionType, Config},
|
||||
error::ReadlineError,
|
||||
At, Cmd, Editor, Movement, Word,
|
||||
line_buffer::LineBuffer,
|
||||
At, Cmd, ConditionalEventHandler, Editor, EventHandler, Modifiers, Movement, Word,
|
||||
};
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
@ -36,6 +37,34 @@ pub fn convert_rustyline_result_to_string(input: Result<String, ReadlineError>)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
struct PartialCompleteHintHandler;
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
impl ConditionalEventHandler for PartialCompleteHintHandler {
|
||||
fn handle(
|
||||
&self,
|
||||
_evt: &rustyline::Event,
|
||||
_n: rustyline::RepeatCount,
|
||||
_positive: bool,
|
||||
ctx: &rustyline::EventContext,
|
||||
) -> Option<Cmd> {
|
||||
Some(match ctx.hint_text() {
|
||||
Some(hint_text) if ctx.pos() == ctx.line().len() => {
|
||||
let mut line_buffer = LineBuffer::with_capacity(hint_text.len());
|
||||
line_buffer.update(hint_text, 0);
|
||||
line_buffer.move_to_next_word(At::AfterEnd, Word::Vi, 1);
|
||||
|
||||
let text = hint_text[0..line_buffer.pos()].to_string();
|
||||
|
||||
Cmd::Insert(1, text)
|
||||
}
|
||||
_ => Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
|
||||
#[cfg(windows)]
|
||||
@ -50,18 +79,20 @@ pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
|
||||
let mut rl: Editor<_> = Editor::with_config(config);
|
||||
|
||||
// add key bindings to move over a whole word with Ctrl+ArrowLeft and Ctrl+ArrowRight
|
||||
//M modifier, E KeyEvent, K KeyCode
|
||||
rl.bind_sequence(
|
||||
convert_keyevent(KeyEvent::ControlLeft),
|
||||
convert_keyevent(KeyCode::Left, Some(Modifiers::CTRL)),
|
||||
Cmd::Move(Movement::BackwardWord(1, Word::Vi)),
|
||||
);
|
||||
|
||||
rl.bind_sequence(
|
||||
convert_keyevent(KeyEvent::ControlRight),
|
||||
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
|
||||
convert_keyevent(KeyCode::Right, Some(Modifiers::CTRL)),
|
||||
EventHandler::Conditional(Box::new(PartialCompleteHintHandler)),
|
||||
);
|
||||
|
||||
// workaround for multiline-paste hang in rustyline (see https://github.com/kkawakam/rustyline/issues/202)
|
||||
rl.bind_sequence(
|
||||
convert_keyevent(KeyEvent::BracketedPasteStart),
|
||||
convert_keyevent(KeyCode::BracketedPasteStart, None),
|
||||
rustyline::Cmd::Noop,
|
||||
);
|
||||
// Let's set the defaults up front and then override them later if the user indicates
|
||||
|
@ -44,6 +44,10 @@ impl NuCompleter {
|
||||
let matcher = matcher.as_str();
|
||||
let matcher: &dyn Matcher = match matcher {
|
||||
"case-insensitive" => &matchers::case_insensitive::Matcher,
|
||||
"case-sensitive" => &matchers::case_sensitive::Matcher,
|
||||
#[cfg(target_os = "windows")]
|
||||
_ => &matchers::case_insensitive::Matcher,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
_ => &matchers::case_sensitive::Matcher,
|
||||
};
|
||||
|
||||
@ -134,7 +138,7 @@ fn requote(orig_value: String) -> String {
|
||||
let mut quotes = vec!['"', '\'', '`'];
|
||||
let mut should_quote = false;
|
||||
for c in value.chars() {
|
||||
if c.is_whitespace() {
|
||||
if c.is_whitespace() || c == '#' {
|
||||
should_quote = true;
|
||||
} else if let Some(index) = quotes.iter().position(|q| *q == c) {
|
||||
should_quote = true;
|
||||
@ -145,7 +149,7 @@ fn requote(orig_value: String) -> String {
|
||||
if should_quote {
|
||||
if quotes.is_empty() {
|
||||
// TODO we don't really have an escape character, so there isn't a great option right
|
||||
// now. One possibility is `{{$(char backtick)}}`
|
||||
// now. One possibility is `{{(char backtick)}}`
|
||||
value.to_string()
|
||||
} else {
|
||||
let quote = quotes[0];
|
||||
|
@ -150,7 +150,7 @@ impl rustyline::Helper for Helper {}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nu_engine::basic_evaluation_context;
|
||||
use nu_engine::EvaluationContext;
|
||||
use rustyline::completion::Completer;
|
||||
use rustyline::line_buffer::LineBuffer;
|
||||
|
||||
@ -164,7 +164,7 @@ mod tests {
|
||||
buffer.insert_str(0, text);
|
||||
buffer.set_pos(text.len() - 1);
|
||||
|
||||
let helper = Helper::new(basic_evaluation_context().unwrap(), None);
|
||||
let helper = Helper::new(EvaluationContext::basic(), None);
|
||||
|
||||
helper.update(&mut buffer, "cd ".len(), &replacement);
|
||||
|
||||
@ -184,7 +184,7 @@ mod tests {
|
||||
buffer.insert_str(0, text);
|
||||
buffer.set_pos(text.len() - 30);
|
||||
|
||||
let helper = Helper::new(basic_evaluation_context().unwrap(), None);
|
||||
let helper = Helper::new(EvaluationContext::basic(), None);
|
||||
|
||||
helper.update(&mut buffer, "cd ".len(), &replacement);
|
||||
|
||||
|
@ -81,29 +81,29 @@ lazy_static! {
|
||||
static ref MULT_DIV_LOOKUP_TABLE: HashMap<(Operator, BinarySide, SyntaxShape), Vec<SyntaxShape>> = {
|
||||
vec![
|
||||
((Operator::Divide, BinarySide::Left, SyntaxShape::Number), // expr => possible var shapes
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //$var / number => Unit, Int, Number
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Duration, SyntaxShape::Number, SyntaxShape::Int]), //$var / number => Unit, Int, Number
|
||||
((Operator::Divide, BinarySide::Left, SyntaxShape::Int),
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //$var / int => Unit, Int, Number
|
||||
((Operator::Divide, BinarySide::Left, SyntaxShape::Unit),
|
||||
vec![SyntaxShape::Unit]), //$var / unit => Unit
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Duration, SyntaxShape::Number, SyntaxShape::Int]), //$var / int => Unit, Int, Number
|
||||
((Operator::Divide, BinarySide::Left, SyntaxShape::Filesize),
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Duration, SyntaxShape::Filesize]), //$var / unit => Unit
|
||||
((Operator::Divide, BinarySide::Right, SyntaxShape::Number),
|
||||
vec![SyntaxShape::Number, SyntaxShape::Int]), //number / $var => Int, Number
|
||||
((Operator::Divide, BinarySide::Right, SyntaxShape::Int),
|
||||
vec![SyntaxShape::Number, SyntaxShape::Int]), //int / $var => Int, Number
|
||||
((Operator::Divide, BinarySide::Right, SyntaxShape::Unit),
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //unit / $var => unit, int, number
|
||||
((Operator::Divide, BinarySide::Right, SyntaxShape::Filesize),
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //unit / $var => unit, int, number
|
||||
|
||||
((Operator::Multiply, BinarySide::Left, SyntaxShape::Number),
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //$var * number => Unit, Int, Number
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //$var * number => Unit, Int, Number
|
||||
((Operator::Multiply, BinarySide::Left, SyntaxShape::Int),
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //$var * int => Unit, Int, Number
|
||||
((Operator::Multiply, BinarySide::Left, SyntaxShape::Unit),
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //$var * int => Unit, Int, Number
|
||||
((Operator::Multiply, BinarySide::Left, SyntaxShape::Filesize),
|
||||
vec![SyntaxShape::Int, SyntaxShape::Number]), //$var * unit => int, number //TODO this changes as soon as more complex units arrive
|
||||
((Operator::Multiply, BinarySide::Right, SyntaxShape::Number),
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //number * $var => Unit, Int, Number
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //number * $var => Unit, Int, Number
|
||||
((Operator::Multiply, BinarySide::Right, SyntaxShape::Int),
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //int * $var => Unit, Int, Number
|
||||
((Operator::Multiply, BinarySide::Right, SyntaxShape::Unit),
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //int * $var => Unit, Int, Number
|
||||
((Operator::Multiply, BinarySide::Right, SyntaxShape::Filesize),
|
||||
vec![SyntaxShape::Int, SyntaxShape::Number]), //unit * $var => int, number //TODO this changes as soon as more complex units arrive
|
||||
].into_iter().collect()
|
||||
};
|
||||
@ -241,8 +241,8 @@ fn get_result_shape_of(
|
||||
l_shape
|
||||
}
|
||||
Operator::Multiply => {
|
||||
if l_shape == SyntaxShape::Unit || r_shape == SyntaxShape::Unit {
|
||||
SyntaxShape::Unit
|
||||
if l_shape == SyntaxShape::Duration || r_shape == SyntaxShape::Duration {
|
||||
SyntaxShape::Duration
|
||||
} else {
|
||||
SyntaxShape::Number
|
||||
}
|
||||
@ -250,7 +250,7 @@ fn get_result_shape_of(
|
||||
Operator::Divide => {
|
||||
if l_shape == r_shape {
|
||||
SyntaxShape::Number
|
||||
} else if l_shape == SyntaxShape::Unit {
|
||||
} else if l_shape == SyntaxShape::Duration {
|
||||
l_shape
|
||||
} else {
|
||||
SyntaxShape::Number
|
||||
@ -273,10 +273,11 @@ fn get_shape_of_expr(expr: &SpannedExpression) -> Option<SyntaxShape> {
|
||||
Expression::Literal(literal) => {
|
||||
match literal {
|
||||
nu_protocol::hir::Literal::Number(number) => match number {
|
||||
nu_protocol::hir::Number::BigInt(_) => Some(SyntaxShape::Int),
|
||||
nu_protocol::hir::Number::Int(_) => Some(SyntaxShape::Int),
|
||||
nu_protocol::hir::Number::Decimal(_) => Some(SyntaxShape::Number),
|
||||
},
|
||||
nu_protocol::hir::Literal::Size(_, _) => Some(SyntaxShape::Unit),
|
||||
nu_protocol::hir::Literal::Size(_, _) => Some(SyntaxShape::Duration),
|
||||
nu_protocol::hir::Literal::String(_) => Some(SyntaxShape::String),
|
||||
//Rest should have failed at parsing stage?
|
||||
nu_protocol::hir::Literal::GlobPattern(_) => Some(SyntaxShape::String),
|
||||
@ -295,7 +296,7 @@ fn get_shape_of_expr(expr: &SpannedExpression) -> Option<SyntaxShape> {
|
||||
Expression::List(_) => Some(SyntaxShape::Table),
|
||||
Expression::Boolean(_) => Some(SyntaxShape::String),
|
||||
|
||||
Expression::Path(_) => Some(SyntaxShape::ColumnPath),
|
||||
Expression::FullColumnPath(_) => Some(SyntaxShape::ColumnPath),
|
||||
Expression::FilePath(_) => Some(SyntaxShape::FilePath),
|
||||
Expression::Block(_) => Some(SyntaxShape::Block),
|
||||
Expression::ExternalCommand(_) => Some(SyntaxShape::String),
|
||||
@ -377,10 +378,7 @@ impl VarSyntaxShapeDeductor {
|
||||
.iter()
|
||||
.map(|decl| {
|
||||
let usage: VarUsage = decl.into();
|
||||
let deduction = match deducer.inferences.get(&usage) {
|
||||
Some(vec) => Some(vec.clone()),
|
||||
None => None,
|
||||
};
|
||||
let deduction = deducer.inferences.get(&usage).cloned();
|
||||
(decl.clone(), deduction)
|
||||
})
|
||||
.collect())
|
||||
@ -541,7 +539,7 @@ impl VarSyntaxShapeDeductor {
|
||||
trace!("Inferring vars in block");
|
||||
self.infer_shape(&b, scope)?;
|
||||
}
|
||||
Expression::Path(path) => {
|
||||
Expression::FullColumnPath(path) => {
|
||||
trace!("Inferring vars in path");
|
||||
match &path.head.expr {
|
||||
//PathMember can't be var yet (?)
|
||||
@ -792,7 +790,7 @@ impl VarSyntaxShapeDeductor {
|
||||
| Expression::Binary(_)
|
||||
| Expression::Range(_)
|
||||
| Expression::Block(_)
|
||||
| Expression::Path(_)
|
||||
| Expression::FullColumnPath(_)
|
||||
| Expression::FilePath(_)
|
||||
| Expression::ExternalCommand(_)
|
||||
| Expression::Command
|
||||
@ -845,12 +843,21 @@ impl VarSyntaxShapeDeductor {
|
||||
),
|
||||
)?;
|
||||
}
|
||||
SyntaxShape::Unit => {
|
||||
SyntaxShape::Duration => {
|
||||
self.checked_insert(
|
||||
var,
|
||||
VarShapeDeduction::from_usage_with_alternatives(
|
||||
&var.span,
|
||||
&[SyntaxShape::Unit],
|
||||
&[SyntaxShape::Duration],
|
||||
),
|
||||
)?;
|
||||
}
|
||||
SyntaxShape::Filesize => {
|
||||
self.checked_insert(
|
||||
var,
|
||||
VarShapeDeduction::from_usage_with_alternatives(
|
||||
&var.span,
|
||||
&[SyntaxShape::Filesize],
|
||||
),
|
||||
)?;
|
||||
}
|
||||
@ -1023,7 +1030,7 @@ impl VarSyntaxShapeDeductor {
|
||||
Some(combination)
|
||||
}
|
||||
})
|
||||
.filter_map(|elem| elem)
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
//No any's intersection of both is result
|
||||
@ -1044,7 +1051,7 @@ impl VarSyntaxShapeDeductor {
|
||||
Some(combination)
|
||||
}
|
||||
})
|
||||
.filter_map(|elem| elem)
|
||||
.flatten()
|
||||
.collect();
|
||||
if intersection.is_empty() {
|
||||
//TODO pass all labels somehow
|
||||
|
@ -5,25 +5,26 @@ description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-command"
|
||||
version = "0.30.0"
|
||||
version = "0.32.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-data = { version = "0.30.0", path = "../nu-data" }
|
||||
nu-engine = { version = "0.30.0", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.30.0", path = "../nu-errors" }
|
||||
nu-json = { version = "0.30.0", path = "../nu-json" }
|
||||
nu-parser = { version = "0.30.0", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.30.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.30.0", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.30.0", path = "../nu-source" }
|
||||
nu-stream = { version = "0.30.0", path = "../nu-stream" }
|
||||
nu-table = { version = "0.30.0", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.30.0", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.30.0", path = "../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.30.0", path = "../nu-ansi-term" }
|
||||
nu-data = { version = "0.32.0", path = "../nu-data" }
|
||||
nu-engine = { version = "0.32.0", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.32.0", path = "../nu-errors" }
|
||||
nu-json = { version = "0.32.0", path = "../nu-json" }
|
||||
nu-parser = { version = "0.32.0", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.32.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.32.0", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.32.0", path = "../nu-source" }
|
||||
nu-stream = { version = "0.32.0", path = "../nu-stream" }
|
||||
nu-table = { version = "0.32.0", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.32.0", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.32.0", path = "../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.32.0", path = "../nu-ansi-term" }
|
||||
nu-pretty-hex = { version = "0.32.0", path = "../nu-pretty-hex" }
|
||||
|
||||
Inflector = "0.11"
|
||||
arboard = { version = "1.1.0", optional = true }
|
||||
@ -53,7 +54,6 @@ getset = "0.1.1"
|
||||
glob = "0.3.0"
|
||||
htmlescape = "0.3.1"
|
||||
ical = "0.7.0"
|
||||
ichwh = { version = "0.3.4", optional = true }
|
||||
indexmap = { version = "1.6.1", features = ["serde-1"] }
|
||||
itertools = "0.10.0"
|
||||
lazy_static = "1.*"
|
||||
@ -66,7 +66,6 @@ num-format = { version = "0.4.0", features = ["with-num-bigint"] }
|
||||
num-traits = "0.2.14"
|
||||
parking_lot = "0.11.1"
|
||||
pin-utils = "0.1.0"
|
||||
pretty-hex = "0.2.1"
|
||||
ptree = { version = "0.3.1", optional = true }
|
||||
query_interface = "0.3.5"
|
||||
quick-xml = "0.21.0"
|
||||
@ -75,7 +74,7 @@ rayon = "1.5.0"
|
||||
regex = "1.4.3"
|
||||
roxmltree = "0.14.0"
|
||||
rust-embed = "5.9.0"
|
||||
rustyline = { version = "8.0.0", optional = true }
|
||||
rustyline = { version = "8.1.0", optional = true }
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
serde_bytes = "0.11.5"
|
||||
serde_ini = "0.2.0"
|
||||
@ -97,9 +96,14 @@ trash = { version = "1.3.0", optional = true }
|
||||
unicode-segmentation = "1.7.1"
|
||||
url = "2.2.0"
|
||||
uuid_crate = { package = "uuid", version = "0.8.2", features = ["v4"], optional = true }
|
||||
which = { version = "4.0.2", optional = true }
|
||||
which = { version = "4.1.0", optional = true }
|
||||
zip = { version = "0.5.9", optional = true }
|
||||
|
||||
[dependencies.polars]
|
||||
version = "0.13.4"
|
||||
optional = true
|
||||
features = ["parquet", "json", "random"]
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
umask = "1.0.0"
|
||||
users = "0.11.0"
|
||||
@ -113,7 +117,7 @@ users = "0.11.0"
|
||||
[dependencies.rusqlite]
|
||||
features = ["bundled", "blob"]
|
||||
optional = true
|
||||
version = "0.24.2"
|
||||
version = "0.25.3"
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = "0.5"
|
||||
@ -131,3 +135,4 @@ trash-support = ["trash"]
|
||||
directories = ["directories-next"]
|
||||
dirs = ["dirs-next"]
|
||||
table-pager = ["minus", "crossterm"]
|
||||
dataframe = ["nu-protocol/dataframe", "polars"]
|
||||
|
@ -26,6 +26,8 @@ pub(crate) mod compact;
|
||||
pub(crate) mod config;
|
||||
pub(crate) mod constants;
|
||||
pub(crate) mod cp;
|
||||
#[cfg(feature = "dataframe")]
|
||||
pub(crate) mod dataframe;
|
||||
pub(crate) mod date;
|
||||
pub(crate) mod debug;
|
||||
pub(crate) mod def;
|
||||
@ -44,6 +46,7 @@ pub(crate) mod exec;
|
||||
pub(crate) mod exit;
|
||||
pub(crate) mod first;
|
||||
pub(crate) mod flatten;
|
||||
pub(crate) mod for_in;
|
||||
pub(crate) mod format;
|
||||
pub(crate) mod from;
|
||||
pub(crate) mod from_csv;
|
||||
@ -77,6 +80,7 @@ pub(crate) mod length;
|
||||
pub(crate) mod let_;
|
||||
pub(crate) mod let_env;
|
||||
pub(crate) mod lines;
|
||||
pub(crate) mod load_env;
|
||||
pub(crate) mod ls;
|
||||
pub(crate) mod math;
|
||||
pub(crate) mod merge;
|
||||
@ -170,9 +174,12 @@ pub(crate) use each::EachGroup;
|
||||
pub(crate) use each::EachWindow;
|
||||
pub(crate) use echo::Echo;
|
||||
pub(crate) use empty::Command as Empty;
|
||||
pub(crate) use for_in::ForIn;
|
||||
pub(crate) use if_::If;
|
||||
pub(crate) use into::Into;
|
||||
pub(crate) use into::IntoBinary;
|
||||
pub(crate) use into::IntoInt;
|
||||
pub(crate) use into::IntoString;
|
||||
pub(crate) use nu::NuPlugin;
|
||||
pub(crate) use update::Command as Update;
|
||||
pub(crate) mod kill;
|
||||
@ -182,6 +189,12 @@ pub(crate) use clear::Clear;
|
||||
pub(crate) mod touch;
|
||||
pub(crate) use all::Command as All;
|
||||
pub(crate) use any::Command as Any;
|
||||
#[cfg(feature = "dataframe")]
|
||||
pub(crate) use dataframe::{
|
||||
DataFrame, DataFrameAggregate, DataFrameConvert, DataFrameDTypes, DataFrameDrop,
|
||||
DataFrameGroupBy, DataFrameJoin, DataFrameList, DataFrameLoad, DataFrameSample,
|
||||
DataFrameSelect, DataFrameShow,
|
||||
};
|
||||
pub(crate) use enter::Enter;
|
||||
pub(crate) use every::Every;
|
||||
pub(crate) use exec::Exec;
|
||||
@ -220,6 +233,7 @@ pub(crate) use length::Length;
|
||||
pub(crate) use let_::Let;
|
||||
pub(crate) use let_env::LetEnv;
|
||||
pub(crate) use lines::Lines;
|
||||
pub(crate) use load_env::LoadEnv;
|
||||
pub(crate) use ls::Ls;
|
||||
pub(crate) use math::{
|
||||
Math, MathAbs, MathAverage, MathCeil, MathEval, MathFloor, MathMaximum, MathMedian,
|
||||
@ -234,8 +248,8 @@ pub(crate) use nth::Nth;
|
||||
pub(crate) use open::Open;
|
||||
pub(crate) use parse::Parse;
|
||||
pub(crate) use path::{
|
||||
PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathJoin, PathParse, PathSplit,
|
||||
PathType,
|
||||
PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathJoin, PathParse,
|
||||
PathRelativeTo, PathSplit, PathType,
|
||||
};
|
||||
pub(crate) use pivot::Pivot;
|
||||
pub(crate) use prepend::Prepend;
|
||||
@ -270,7 +284,7 @@ pub(crate) use split::{Split, SplitChars, SplitColumn, SplitRow};
|
||||
pub(crate) use split_by::SplitBy;
|
||||
pub(crate) use str_::{
|
||||
Str, StrCamelCase, StrCapitalize, StrCollect, StrContains, StrDowncase, StrEndsWith,
|
||||
StrFindReplace, StrFrom, StrIndexOf, StrKebabCase, StrLPad, StrLength, StrPascalCase, StrRPad,
|
||||
StrFindReplace, StrIndexOf, StrKebabCase, StrLPad, StrLength, StrPascalCase, StrRPad,
|
||||
StrReverse, StrScreamingSnakeCase, StrSnakeCase, StrStartsWith, StrSubstring, StrToDatetime,
|
||||
StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
|
||||
};
|
||||
@ -321,7 +335,6 @@ mod tests {
|
||||
whole_stream_command(StrUpcase),
|
||||
whole_stream_command(StrCapitalize),
|
||||
whole_stream_command(StrFindReplace),
|
||||
whole_stream_command(StrFrom),
|
||||
whole_stream_command(StrSubstring),
|
||||
whole_stream_command(StrToDatetime),
|
||||
whole_stream_command(StrContains),
|
||||
@ -354,6 +367,7 @@ mod tests {
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
for cmd in only_examples() {
|
||||
println!("cmd: {}", cmd.name());
|
||||
test_examples(cmd)?;
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ impl WholeStreamCommand for Command {
|
||||
},
|
||||
Example {
|
||||
description: "Check that all values are even",
|
||||
example: "echo [2 4 6 8] | all? $(= $it mod 2) == 0",
|
||||
example: "echo [2 4 6 8] | all? ($it mod 2) == 0",
|
||||
result: Some(vec![Value::from(true)]),
|
||||
},
|
||||
]
|
||||
@ -83,13 +83,13 @@ fn all(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
}
|
||||
};
|
||||
|
||||
let scope = args.scope.clone();
|
||||
let scope = args.scope();
|
||||
|
||||
let init = Ok(InputStream::one(
|
||||
UntaggedValue::boolean(true).into_value(&tag),
|
||||
));
|
||||
|
||||
// Variables in nu are immutable. Having the same variable accross invocations
|
||||
// Variables in nu are immutable. Having the same variable across invocations
|
||||
// of evaluate_baseline_expr does not mutate the variables and those each
|
||||
// invocations are independent of each other!
|
||||
scope.enter_scope();
|
||||
|
@ -2,7 +2,7 @@ use crate::prelude::*;
|
||||
use nu_ansi_term::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Command;
|
||||
@ -69,7 +69,7 @@ following values:
|
||||
https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
|
||||
OSC: '\x1b]' is not required for --osc parameter
|
||||
Example: echo [$(ansi -o '0') 'some title' $(char bel)] | str collect
|
||||
Example: echo [(ansi -o '0') 'some title' (char bel)] | str collect
|
||||
Format: #
|
||||
0 Set window title and icon name
|
||||
1 Set icon name
|
||||
@ -96,7 +96,7 @@ Format: #
|
||||
Example {
|
||||
description:
|
||||
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
|
||||
example: r#"echo [$(ansi rb) Hello " " $(ansi gb) Nu " " $(ansi pb) World] | str collect"#,
|
||||
example: r#"echo [(ansi rb) Hello " " (ansi gb) Nu " " (ansi pb) World] | str collect"#,
|
||||
result: Some(vec![Value::from(
|
||||
"\u{1b}[1;31mHello \u{1b}[1;32mNu \u{1b}[1;35mWorld",
|
||||
)]),
|
||||
@ -104,7 +104,7 @@ Format: #
|
||||
Example {
|
||||
description:
|
||||
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
|
||||
example: r#"echo [$(ansi -e '3;93;41m') Hello $(ansi reset) " " $(ansi gb) Nu " " $(ansi pb) World] | str collect"#,
|
||||
example: r#"echo [(ansi -e '3;93;41m') Hello (ansi reset) " " (ansi gb) Nu " " (ansi pb) World] | str collect"#,
|
||||
result: Some(vec![Value::from(
|
||||
"\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld",
|
||||
)]),
|
||||
@ -112,7 +112,7 @@ Format: #
|
||||
]
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let code: Option<Tagged<String>> = args.opt(0)?;
|
||||
@ -129,9 +129,9 @@ Format: #
|
||||
));
|
||||
}
|
||||
let output = format!("\x1b[{}", e.item);
|
||||
return Ok(ActionStream::one(ReturnSuccess::value(
|
||||
return Ok(OutputStream::one(
|
||||
UntaggedValue::string(output).into_value(e.tag()),
|
||||
)));
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(o) = osc {
|
||||
@ -147,18 +147,18 @@ Format: #
|
||||
//Operating system command aka osc ESC ] <- note the right brace, not left brace for osc
|
||||
// OCS's need to end with a bell '\x07' char
|
||||
let output = format!("\x1b]{};", o.item);
|
||||
return Ok(ActionStream::one(ReturnSuccess::value(
|
||||
return Ok(OutputStream::one(
|
||||
UntaggedValue::string(output).into_value(o.tag()),
|
||||
)));
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(code) = code {
|
||||
let ansi_code = str_to_ansi(&code.item);
|
||||
|
||||
if let Some(output) = ansi_code {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(output).into_value(code.tag()),
|
||||
)))
|
||||
))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Unknown ansi code",
|
||||
@ -299,7 +299,7 @@ pub fn str_to_ansi(s: &str) -> Option<String> {
|
||||
// Reference for ansi codes https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
|
||||
// Another good reference http://ascii-table.com/ansi-escape-sequences.php
|
||||
|
||||
// For setting title like `echo [$(char title) $(pwd) $(char bel)] | str collect`
|
||||
// For setting title like `echo [(char title) (pwd) (char bel)] | str collect`
|
||||
"title" => Some("\x1b]2;".to_string()), // ESC]2; xterm sets window title using OSC syntax escapes
|
||||
|
||||
// Ansi Erase Sequences
|
||||
|
@ -2,19 +2,12 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::ShellTypeName;
|
||||
use nu_protocol::{
|
||||
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
use strip_ansi_escapes::strip;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Arguments {
|
||||
rest: Vec<ColumnPath>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"ansi strip"
|
||||
@ -31,27 +24,29 @@ impl WholeStreamCommand for SubCommand {
|
||||
"strip ansi escape sequences from string"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
operate(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "strip ansi escape sequences from string",
|
||||
example: "echo [$(ansi gb) 'hello' $(ansi reset)] | str collect | ansi strip",
|
||||
example: "echo [(ansi gb) 'hello' (ansi reset)] | str collect | ansi strip",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { rest }, input) = args.process()?;
|
||||
let column_paths: Vec<_> = rest;
|
||||
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
Ok(input
|
||||
let column_paths: Vec<_> = args.rest(0)?;
|
||||
|
||||
let result: Vec<Value> = args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
ReturnSuccess::value(action(&v, v.tag())?)
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
|
||||
@ -62,10 +57,12 @@ fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
)?;
|
||||
}
|
||||
|
||||
ReturnSuccess::value(ret)
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.to_action_stream())
|
||||
.collect::<Result<Vec<Value>, _>>()?;
|
||||
|
||||
Ok(OutputStream::from_stream(result.into_iter()))
|
||||
}
|
||||
|
||||
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
|
@ -8,9 +8,8 @@ use nu_protocol::{
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
block: CapturedBlock,
|
||||
struct AnyArgs {
|
||||
predicate: CapturedBlock,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
@ -30,7 +29,7 @@ impl WholeStreamCommand for Command {
|
||||
"Find if the table rows matches the condition."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
any(args)
|
||||
}
|
||||
|
||||
@ -45,79 +44,85 @@ impl WholeStreamCommand for Command {
|
||||
},
|
||||
Example {
|
||||
description: "Check if any of the values is odd",
|
||||
example: "echo [2 4 1 6 8] | any? $(= $it mod 2) == 1",
|
||||
example: "echo [2 4 1 6 8] | any? ($it mod 2) == 1",
|
||||
result: Some(vec![Value::from(true)]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn any(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let ctx = Arc::new(EvaluationContext::from_args(&args));
|
||||
fn any(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (Arguments { block }, input) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
let any_args = AnyArgs {
|
||||
predicate: args.req(0)?,
|
||||
};
|
||||
|
||||
let err = Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
args.call_info.name_tag.clone(),
|
||||
));
|
||||
|
||||
//This seems a little odd. Can't we have predicates with pipelines/multiple statements?
|
||||
let condition = {
|
||||
if block.block.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
if any_args.predicate.block.block.len() != 1 {
|
||||
return err;
|
||||
}
|
||||
match block.block.block[0].pipelines.get(0) {
|
||||
match any_args.predicate.block.block[0].pipelines.get(0) {
|
||||
Some(item) => match item.list.get(0) {
|
||||
Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
return err;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let cond = Ok(InputStream::one(
|
||||
let scope = args.scope();
|
||||
|
||||
let init = Ok(InputStream::one(
|
||||
UntaggedValue::boolean(false).into_value(&tag),
|
||||
));
|
||||
|
||||
Ok(input
|
||||
.fold(cond, move |cond, row| {
|
||||
let condition = condition.clone();
|
||||
let ctx = ctx.clone();
|
||||
ctx.scope.enter_scope();
|
||||
ctx.scope.add_vars(&block.captured.entries);
|
||||
ctx.scope.add_var("$it", row);
|
||||
// Variables in nu are immutable. Having the same variable accross invocations
|
||||
// of evaluate_baseline_expr does not mutate the variables and thus each
|
||||
// invocations are independent of each other!
|
||||
scope.enter_scope();
|
||||
scope.add_vars(&any_args.predicate.captured.entries);
|
||||
|
||||
let condition = evaluate_baseline_expr(&condition, &*ctx);
|
||||
ctx.scope.exit_scope();
|
||||
let result = args.input.fold(init, move |acc, row| {
|
||||
let condition = condition.clone();
|
||||
let ctx = ctx.clone();
|
||||
if let Some((arg, _)) = any_args.predicate.block.params.positional.first() {
|
||||
ctx.scope.add_var(arg.name(), row);
|
||||
}
|
||||
|
||||
let curr = cond?.drain_vec();
|
||||
let curr = curr
|
||||
.get(0)
|
||||
.ok_or_else(|| ShellError::unexpected("No value to check with"))?;
|
||||
let cond = curr.as_bool()?;
|
||||
let condition = evaluate_baseline_expr(&condition, &ctx);
|
||||
|
||||
match condition {
|
||||
Ok(condition) => match condition.as_bool() {
|
||||
Ok(b) => Ok(InputStream::one(
|
||||
UntaggedValue::boolean(cond || b).into_value(&curr.tag),
|
||||
)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
let curr = acc?.drain_vec();
|
||||
let curr = curr
|
||||
.get(0)
|
||||
.ok_or_else(|| ShellError::unexpected("No value to check with"))?;
|
||||
let cond = curr.as_bool()?;
|
||||
|
||||
match condition {
|
||||
Ok(condition) => match condition.as_bool() {
|
||||
Ok(b) => Ok(InputStream::one(
|
||||
UntaggedValue::boolean(cond || b).into_value(&curr.tag),
|
||||
)),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
})?
|
||||
.to_action_stream())
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
});
|
||||
scope.exit_scope();
|
||||
|
||||
Ok(result?.to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -27,7 +27,7 @@ The .nu-env file has the same format as your $HOME/nu/config.toml file. By loadi
|
||||
}
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(&Autoenv, &args.scope)).into_value(Tag::unknown()),
|
||||
UntaggedValue::string(get_full_help(&Autoenv, args.scope())).into_value(Tag::unknown()),
|
||||
)))
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,9 @@ use nu_protocol::hir::{self, Expression, ExternalRedirection, Literal, SpannedEx
|
||||
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
|
||||
use nu_table::TextStyle;
|
||||
|
||||
#[cfg(feature = "dataframe")]
|
||||
use nu_protocol::dataframe::PolarsData;
|
||||
|
||||
pub struct Command;
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
@ -43,14 +46,15 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let configuration = context.configs.lock().global_config();
|
||||
pub fn autoview(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let configuration = args.configs().lock().global_config();
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let binary = context.scope.get_command("binaryview");
|
||||
let text = context.scope.get_command("textview");
|
||||
let table = context.scope.get_command("table");
|
||||
|
||||
let (mut input_stream, context) = context.split();
|
||||
let binary = args.scope().get_command("binaryview");
|
||||
let text = args.scope().get_command("textview");
|
||||
let table = args.scope().get_command("table");
|
||||
let context = args.context;
|
||||
let mut input_stream = args.input;
|
||||
|
||||
if let Some(x) = input_stream.next() {
|
||||
match input_stream.next() {
|
||||
@ -62,7 +66,7 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let stream = InputStream::from_stream(xy_stream);
|
||||
|
||||
if let Some(table) = table {
|
||||
let command_args = create_default_command_args(&context).with_input(stream);
|
||||
let command_args = create_default_command_args(&context, stream, tag);
|
||||
let result = table.run(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
}
|
||||
@ -74,12 +78,13 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
tag: Tag { anchor, span },
|
||||
} if anchor.is_some() => {
|
||||
if let Some(text) = text {
|
||||
let mut stream = VecDeque::new();
|
||||
stream.push_back(
|
||||
UntaggedValue::string(s).into_value(Tag { anchor, span }),
|
||||
let command_args = create_default_command_args(
|
||||
&context,
|
||||
InputStream::one(
|
||||
UntaggedValue::string(s).into_value(Tag { anchor, span }),
|
||||
),
|
||||
tag,
|
||||
);
|
||||
let command_args =
|
||||
create_default_command_args(&context).with_input(stream);
|
||||
let result = text.run_with_actions(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
} else {
|
||||
@ -104,6 +109,12 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
} => {
|
||||
out!("{}", n);
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::BigInt(n)),
|
||||
..
|
||||
} => {
|
||||
out!("{}", n);
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Decimal(n)),
|
||||
..
|
||||
@ -158,14 +169,12 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
..
|
||||
} => {
|
||||
if let Some(binary) = binary {
|
||||
let mut stream = VecDeque::new();
|
||||
stream.push_back(x);
|
||||
let command_args =
|
||||
create_default_command_args(&context).with_input(stream);
|
||||
create_default_command_args(&context, InputStream::one(x), tag);
|
||||
let result = binary.run_with_actions(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
} else {
|
||||
use pretty_hex::*;
|
||||
use nu_pretty_hex::*;
|
||||
out!("{:?}", b.hex_dump());
|
||||
}
|
||||
}
|
||||
@ -220,16 +229,42 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
println!("{}", nu_table::draw_table(&table, term_width, &color_hm));
|
||||
} else if let Some(table) = table {
|
||||
let mut stream = VecDeque::new();
|
||||
stream.push_back(x);
|
||||
let command_args =
|
||||
create_default_command_args(&context).with_input(stream);
|
||||
create_default_command_args(&context, InputStream::one(x), tag);
|
||||
let result = table.run(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
} else {
|
||||
out!("{:?}", row);
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "dataframe")]
|
||||
Value {
|
||||
value: UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)),
|
||||
tag,
|
||||
} => {
|
||||
if let Some(table) = table {
|
||||
// TODO. Configure the parameter rows from file. It can be
|
||||
// adjusted to see a certain amount of values in the head
|
||||
let command_args =
|
||||
create_default_command_args(&context, df.print()?.into(), tag);
|
||||
let result = table.run(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "dataframe")]
|
||||
Value {
|
||||
value: UntaggedValue::DataFrame(PolarsData::GroupBy(groupby)),
|
||||
tag,
|
||||
} => {
|
||||
if let Some(table) = table {
|
||||
// TODO. Configure the parameter rows from file. It can be
|
||||
// adjusted to see a certain amount of values in the head
|
||||
let command_args =
|
||||
create_default_command_args(&context, groupby.print()?.into(), tag);
|
||||
let result = table.run(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
}
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Nothing),
|
||||
..
|
||||
@ -240,10 +275,8 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
value: ref item, ..
|
||||
} => {
|
||||
if let Some(table) = table {
|
||||
let mut stream = VecDeque::new();
|
||||
stream.push_back(x);
|
||||
let command_args =
|
||||
create_default_command_args(&context).with_input(stream);
|
||||
create_default_command_args(&context, InputStream::one(x), tag);
|
||||
let result = table.run(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
} else {
|
||||
@ -258,14 +291,14 @@ pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(InputStream::empty())
|
||||
}
|
||||
|
||||
fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawCommandArgs {
|
||||
let span = context.name.span;
|
||||
RawCommandArgs {
|
||||
host: context.host.clone(),
|
||||
ctrl_c: context.ctrl_c.clone(),
|
||||
configs: context.configs.clone(),
|
||||
current_errors: context.current_errors.clone(),
|
||||
shell_manager: context.shell_manager.clone(),
|
||||
fn create_default_command_args(
|
||||
context: &EvaluationContext,
|
||||
input: InputStream,
|
||||
tag: Tag,
|
||||
) -> CommandArgs {
|
||||
let span = tag.span;
|
||||
CommandArgs {
|
||||
context: context.clone(),
|
||||
call_info: UnevaluatedCallInfo {
|
||||
args: hir::Call {
|
||||
head: Box::new(SpannedExpression::new(
|
||||
@ -277,9 +310,9 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
|
||||
span,
|
||||
external_redirection: ExternalRedirection::Stdout,
|
||||
},
|
||||
name_tag: context.name.clone(),
|
||||
name_tag: tag,
|
||||
},
|
||||
scope: Scope::new(),
|
||||
input,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,10 @@ use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::{Block, CapturedBlock, ClassifiedCommand, Group, InternalCommand, Pipeline},
|
||||
hir::{
|
||||
Block, CapturedBlock, ClassifiedCommand, ExternalRedirection, Group, InternalCommand,
|
||||
Pipeline,
|
||||
},
|
||||
Dictionary, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use rand::{
|
||||
@ -47,7 +50,7 @@ impl WholeStreamCommand for Benchmark {
|
||||
"Runs a block and returns the time it took to execute it."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
benchmark(args)
|
||||
}
|
||||
|
||||
@ -67,11 +70,16 @@ impl WholeStreamCommand for Benchmark {
|
||||
}
|
||||
}
|
||||
|
||||
fn benchmark(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = raw_args.call_info.args.span;
|
||||
let mut context = EvaluationContext::from_args(&raw_args);
|
||||
let scope = raw_args.scope.clone();
|
||||
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process()?;
|
||||
fn benchmark(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.args.span;
|
||||
let mut context = EvaluationContext::from_args(&args);
|
||||
let scope = args.scope().clone();
|
||||
|
||||
let args = args.evaluate_once()?;
|
||||
let cmd_args = BenchmarkArgs {
|
||||
block: args.req(0)?,
|
||||
passthrough: args.get_flag("passthrough")?,
|
||||
};
|
||||
|
||||
let env = scope.get_env_vars();
|
||||
let name = generate_free_name(&env);
|
||||
@ -84,7 +92,13 @@ fn benchmark(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
// let start = time();
|
||||
|
||||
context.scope.enter_scope();
|
||||
let result = run_block(&block.block, &context, input);
|
||||
let result = run_block(
|
||||
&cmd_args.block.block,
|
||||
&context,
|
||||
args.input,
|
||||
ExternalRedirection::StdoutAndStderr,
|
||||
);
|
||||
|
||||
context.scope.exit_scope();
|
||||
let output = result?.into_vec();
|
||||
|
||||
@ -101,7 +115,7 @@ fn benchmark(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
|
||||
let real_time = into_big_int(end_time - start_time);
|
||||
indexmap.insert("real time".to_string(), real_time);
|
||||
benchmark_output(indexmap, output, passthrough, &tag, &mut context)
|
||||
benchmark_output(indexmap, output, cmd_args.passthrough, &tag, &mut context)
|
||||
}
|
||||
// return advanced stats
|
||||
// #[cfg(feature = "rich-benchmark")]
|
||||
@ -134,10 +148,10 @@ fn benchmark_output<T, Output>(
|
||||
passthrough: Option<CapturedBlock>,
|
||||
tag: T,
|
||||
context: &mut EvaluationContext,
|
||||
) -> Result<ActionStream, ShellError>
|
||||
) -> Result<OutputStream, ShellError>
|
||||
where
|
||||
T: Into<Tag> + Copy,
|
||||
Output: Into<ActionStream>,
|
||||
Output: Into<OutputStream>,
|
||||
{
|
||||
let value = UntaggedValue::Row(Dictionary::from(
|
||||
indexmap
|
||||
@ -154,14 +168,19 @@ where
|
||||
let time_block = add_implicit_autoview(time_block.block);
|
||||
|
||||
context.scope.enter_scope();
|
||||
let result = run_block(&time_block, context, benchmark_output);
|
||||
let result = run_block(
|
||||
&time_block,
|
||||
context,
|
||||
benchmark_output,
|
||||
ExternalRedirection::StdoutAndStderr,
|
||||
);
|
||||
context.scope.exit_scope();
|
||||
result?;
|
||||
context.clear_errors();
|
||||
|
||||
Ok(block_output.into())
|
||||
} else {
|
||||
let benchmark_output = ActionStream::one(value);
|
||||
let benchmark_output = OutputStream::one(value);
|
||||
Ok(benchmark_output)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use nu_errors::ShellError;
|
||||
|
||||
use nu_data::value::format_leaf;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct BuildString;
|
||||
|
||||
@ -21,7 +21,7 @@ impl WholeStreamCommand for BuildString {
|
||||
"Builds a string from the arguments."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let args = args.evaluate_once()?;
|
||||
let rest: Vec<Value> = args.rest(0)?;
|
||||
@ -32,9 +32,9 @@ impl WholeStreamCommand for BuildString {
|
||||
output_string.push_str(&format_leaf(&r).plain_string(100_000))
|
||||
}
|
||||
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(output_string).into_value(tag),
|
||||
)))
|
||||
))
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use chrono::{Datelike, Local, NaiveDate};
|
||||
use indexmap::IndexMap;
|
||||
use nu_engine::{EvaluatedWholeStreamCommandArgs, WholeStreamCommand};
|
||||
use nu_engine::{EvaluatedCommandArgs, WholeStreamCommand};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
@ -165,7 +165,7 @@ fn get_current_date() -> (i32, u32, u32) {
|
||||
}
|
||||
|
||||
fn add_months_of_year_to_table(
|
||||
args: &EvaluatedWholeStreamCommandArgs,
|
||||
args: &EvaluatedCommandArgs,
|
||||
mut calendar_vec_deque: &mut VecDeque<Value>,
|
||||
tag: &Tag,
|
||||
selected_year: i32,
|
||||
@ -198,7 +198,7 @@ fn add_months_of_year_to_table(
|
||||
}
|
||||
|
||||
fn add_month_to_table(
|
||||
args: &EvaluatedWholeStreamCommandArgs,
|
||||
args: &EvaluatedCommandArgs,
|
||||
calendar_vec_deque: &mut VecDeque<Value>,
|
||||
tag: &Tag,
|
||||
selected_year: i32,
|
||||
|
@ -26,7 +26,7 @@ impl WholeStreamCommand for Cd {
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
let shell_manager = args.shell_manager();
|
||||
let (args, _): (CdArgs, _) = args.process()?;
|
||||
shell_manager.cd(args, name)
|
||||
}
|
||||
|
@ -1,11 +1,108 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::{FromValue, WholeStreamCommand};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
use indexmap::indexmap;
|
||||
use indexmap::map::IndexMap;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
pub struct Char;
|
||||
|
||||
struct CharArgs {
|
||||
name: Option<Tagged<String>>,
|
||||
rest: Vec<Value>,
|
||||
list: bool,
|
||||
unicode: bool,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref CHAR_MAP: IndexMap<&'static str, String> = indexmap! {
|
||||
// These are some regular characters that either can't used or
|
||||
// it's just easier to use them like this.
|
||||
"newline" => '\n'.to_string(),
|
||||
"enter" => '\n'.to_string(),
|
||||
"nl" => '\n'.to_string(),
|
||||
"tab" => '\t'.to_string(),
|
||||
"sp" => ' '.to_string(),
|
||||
"space" => ' '.to_string(),
|
||||
"pipe" => '|'.to_string(),
|
||||
"left_brace" => '{'.to_string(),
|
||||
"lbrace" => '{'.to_string(),
|
||||
"right_brace" => '}'.to_string(),
|
||||
"rbrace" => '}'.to_string(),
|
||||
"left_paren" => '('.to_string(),
|
||||
"lparen" => '('.to_string(),
|
||||
"right_paren" => ')'.to_string(),
|
||||
"rparen" => ')'.to_string(),
|
||||
"left_bracket" => '['.to_string(),
|
||||
"lbracket" => '['.to_string(),
|
||||
"right_bracket" => ']'.to_string(),
|
||||
"rbracket" => ']'.to_string(),
|
||||
"sep" => std::path::MAIN_SEPARATOR.to_string(),
|
||||
"separator" => std::path::MAIN_SEPARATOR.to_string(),
|
||||
|
||||
// Unicode names came from https://www.compart.com/en/unicode
|
||||
// Private Use Area (U+E000-U+F8FF)
|
||||
// Unicode can't be mixed with Ansi or it will break width calculation
|
||||
"branch" => '\u{e0a0}'.to_string(), //
|
||||
"segment" => '\u{e0b0}'.to_string(), //
|
||||
|
||||
"identical_to" => '\u{2261}'.to_string(), // ≡
|
||||
"hamburger" => '\u{2261}'.to_string(), // ≡
|
||||
"not_identical_to" => '\u{2262}'.to_string(), // ≢
|
||||
"branch_untracked" => '\u{2262}'.to_string(), // ≢
|
||||
"strictly_equivalent_to" => '\u{2263}'.to_string(), // ≣
|
||||
"branch_identical" => '\u{2263}'.to_string(), // ≣
|
||||
|
||||
"upwards_arrow" => '\u{2191}'.to_string(), // ↑
|
||||
"branch_ahead" => '\u{2191}'.to_string(), // ↑
|
||||
"downwards_arrow" => '\u{2193}'.to_string(), // ↓
|
||||
"branch_behind" => '\u{2193}'.to_string(), // ↓
|
||||
"up_down_arrow" => '\u{2195}'.to_string(), // ↕
|
||||
"branch_ahead_behind" => '\u{2195}'.to_string(), // ↕
|
||||
|
||||
"black_right_pointing_triangle" => '\u{25b6}'.to_string(), // ▶
|
||||
"prompt" => '\u{25b6}'.to_string(), // ▶
|
||||
"vector_or_cross_product" => '\u{2a2f}'.to_string(), // ⨯
|
||||
"failed" => '\u{2a2f}'.to_string(), // ⨯
|
||||
"high_voltage_sign" => '\u{26a1}'.to_string(), // ⚡
|
||||
"elevated" => '\u{26a1}'.to_string(), // ⚡
|
||||
"tilde" => '~'.to_string(), // ~
|
||||
"twiddle" => '~'.to_string(), // ~
|
||||
"squiggly" => '~'.to_string(), // ~
|
||||
"home" => '~'.to_string(), // ~
|
||||
"hash" => '#'.to_string(), // #
|
||||
"hashtag" => '#'.to_string(), // #
|
||||
"pound_sign" => '#'.to_string(), // #
|
||||
"sharp" => '#'.to_string(), // #
|
||||
"root" => '#'.to_string(), // #
|
||||
|
||||
// Weather symbols
|
||||
"sun" => "☀️".to_string(),
|
||||
"sunny" => "☀️".to_string(),
|
||||
"sunrise" => "☀️".to_string(),
|
||||
"moon" => "🌛".to_string(),
|
||||
"cloudy" => "☁️".to_string(),
|
||||
"cloud" => "☁️".to_string(),
|
||||
"clouds" => "☁️".to_string(),
|
||||
"rainy" => "🌦️".to_string(),
|
||||
"rain" => "🌦️".to_string(),
|
||||
"foggy" => "🌫️".to_string(),
|
||||
"fog" => "🌫️".to_string(),
|
||||
"mist" => '\u{2591}'.to_string(),
|
||||
"haze" => '\u{2591}'.to_string(),
|
||||
"snowy" => "❄️".to_string(),
|
||||
"snow" => "❄️".to_string(),
|
||||
"thunderstorm" => "🌩️".to_string(),
|
||||
"thunder" => "🌩️".to_string(),
|
||||
|
||||
"bel" => '\x07'.to_string(), // Terminal Bell
|
||||
"backspace" => '\x08'.to_string(), // Backspace
|
||||
};
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Char {
|
||||
fn name(&self) -> &str {
|
||||
"char"
|
||||
@ -13,17 +110,18 @@ impl WholeStreamCommand for Char {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("char")
|
||||
.required(
|
||||
.optional(
|
||||
"character",
|
||||
SyntaxShape::Any,
|
||||
"the name of the character to output",
|
||||
)
|
||||
.rest(SyntaxShape::String, "multiple Unicode bytes")
|
||||
.switch("list", "List all supported character names", Some('l'))
|
||||
.switch("unicode", "Unicode string i.e. 1f378", Some('u'))
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Output special characters (eg. 'newline')."
|
||||
"Output special characters (e.g., 'newline')."
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -35,7 +133,7 @@ impl WholeStreamCommand for Char {
|
||||
},
|
||||
Example {
|
||||
description: "Output prompt character, newline and a hamburger character",
|
||||
example: r#"echo $(char prompt) $(char newline) $(char hamburger)"#,
|
||||
example: r#"echo (char prompt) (char newline) (char hamburger)"#,
|
||||
result: Some(vec![
|
||||
UntaggedValue::string("\u{25b6}").into(),
|
||||
UntaggedValue::string("\n").into(),
|
||||
@ -57,62 +155,86 @@ impl WholeStreamCommand for Char {
|
||||
]
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args_tag = args.call_info.name_tag.clone();
|
||||
let args = args.evaluate_once()?;
|
||||
let args = CharArgs {
|
||||
name: args.opt(0)?,
|
||||
rest: args.rest(1)?,
|
||||
list: args.has_flag("list"),
|
||||
unicode: args.has_flag("unicode"),
|
||||
};
|
||||
|
||||
let name: Tagged<String> = args.req(0)?;
|
||||
let rest: Vec<Value> = args.rest(1)?;
|
||||
let unicode = args.has_flag("unicode");
|
||||
|
||||
if unicode {
|
||||
if !rest.is_empty() {
|
||||
// Setup a new buffer to put all the Unicode bytes in
|
||||
let mut multi_byte = String::new();
|
||||
// Get the first byte
|
||||
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
|
||||
match decoded_char {
|
||||
Ok(ch) => multi_byte.push(ch),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
// Get the rest of the bytes
|
||||
for byte_part in rest {
|
||||
let byte_part: Tagged<String> = FromValue::from_value(&byte_part)?;
|
||||
let decoded_char = string_to_unicode_char(&byte_part, &byte_part.tag);
|
||||
if args.list {
|
||||
Ok(CHAR_MAP
|
||||
.iter()
|
||||
.map(move |(name, s)| {
|
||||
let mut dict = TaggedDictBuilder::with_capacity(&args_tag, 2);
|
||||
dict.insert_untagged("name", UntaggedValue::string(*name));
|
||||
dict.insert_untagged("character", UntaggedValue::string(s));
|
||||
let unicode_parts: Vec<String> =
|
||||
s.chars().map(|c| format!("{:x}", c as u32)).collect();
|
||||
dict.insert_untagged("unicode", UntaggedValue::string(unicode_parts.join(" ")));
|
||||
dict.into_value()
|
||||
})
|
||||
.to_output_stream())
|
||||
} else if let Some(name) = args.name {
|
||||
if args.unicode {
|
||||
if !args.rest.is_empty() {
|
||||
// Setup a new buffer to put all the Unicode bytes in
|
||||
let mut multi_byte = String::new();
|
||||
// Get the first byte
|
||||
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
|
||||
match decoded_char {
|
||||
Ok(ch) => multi_byte.push(ch),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
// Get the rest of the bytes
|
||||
for byte_part in args.rest {
|
||||
let byte_part: Tagged<String> = FromValue::from_value(&byte_part)?;
|
||||
let decoded_char = string_to_unicode_char(&byte_part, &byte_part.tag);
|
||||
match decoded_char {
|
||||
Ok(ch) => multi_byte.push(ch),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(multi_byte).into_value(name.tag),
|
||||
))
|
||||
} else {
|
||||
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
|
||||
if let Ok(ch) = decoded_char {
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(ch).into_value(name.tag()),
|
||||
))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"error decoding Unicode character",
|
||||
"error decoding Unicode character",
|
||||
name.tag(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(multi_byte).into_value(name.tag),
|
||||
)))
|
||||
} else {
|
||||
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
|
||||
if let Ok(ch) = decoded_char {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(ch).into_value(name.tag()),
|
||||
)))
|
||||
let special_character = str_to_character(&name.item);
|
||||
if let Some(output) = special_character {
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(output).into_value(name.tag()),
|
||||
))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"error decoding Unicode character",
|
||||
"error decoding Unicode character",
|
||||
"error finding named character",
|
||||
"error finding named character",
|
||||
name.tag(),
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let special_character = str_to_character(&name.item);
|
||||
if let Some(output) = special_character {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(name.tag()),
|
||||
)))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"error finding named character",
|
||||
"error finding named character",
|
||||
name.tag(),
|
||||
))
|
||||
}
|
||||
Err(ShellError::labeled_error(
|
||||
"char requires the name of the character",
|
||||
"missing name of the character",
|
||||
&args_tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,45 +256,7 @@ fn string_to_unicode_char(s: &str, t: &Tag) -> Result<char, ShellError> {
|
||||
}
|
||||
|
||||
fn str_to_character(s: &str) -> Option<String> {
|
||||
match s {
|
||||
"newline" | "enter" | "nl" => Some("\n".into()),
|
||||
"tab" => Some("\t".into()),
|
||||
"sp" | "space" => Some(" ".into()),
|
||||
// Unicode names came from https://www.compart.com/en/unicode
|
||||
// Private Use Area (U+E000-U+F8FF)
|
||||
// Unicode can't be mixed with Ansi or it will break width calculation
|
||||
"branch" => Some('\u{e0a0}'.to_string()), //
|
||||
"segment" => Some('\u{e0b0}'.to_string()), //
|
||||
|
||||
"identical_to" | "hamburger" => Some('\u{2261}'.to_string()), // ≡
|
||||
"not_identical_to" | "branch_untracked" => Some('\u{2262}'.to_string()), // ≢
|
||||
"strictly_equivalent_to" | "branch_identical" => Some('\u{2263}'.to_string()), // ≣
|
||||
|
||||
"upwards_arrow" | "branch_ahead" => Some('\u{2191}'.to_string()), // ↑
|
||||
"downwards_arrow" | "branch_behind" => Some('\u{2193}'.to_string()), // ↓
|
||||
"up_down_arrow" | "branch_ahead_behind" => Some('\u{2195}'.to_string()), // ↕
|
||||
|
||||
"black_right_pointing_triangle" | "prompt" => Some('\u{25b6}'.to_string()), // ▶
|
||||
"vector_or_cross_product" | "failed" => Some('\u{2a2f}'.to_string()), // ⨯
|
||||
"high_voltage_sign" | "elevated" => Some('\u{26a1}'.to_string()), // ⚡
|
||||
"tilde" | "twiddle" | "squiggly" | "home" => Some("~".into()), // ~
|
||||
"hash" | "hashtag" | "pound_sign" | "sharp" | "root" => Some("#".into()), // #
|
||||
|
||||
// Weather symbols
|
||||
"sun" | "sunny" | "sunrise" => Some("☀️".to_string()),
|
||||
"moon" => Some("🌛".to_string()),
|
||||
"cloudy" | "cloud" | "clouds" => Some("☁️".to_string()),
|
||||
"rainy" | "rain" => Some("🌦️".to_string()),
|
||||
"foggy" | "fog" => Some("🌫️".to_string()),
|
||||
"mist" | "haze" => Some("\u{2591}".to_string()),
|
||||
"snowy" | "snow" => Some("❄️".to_string()),
|
||||
"thunderstorm" | "thunder" => Some("🌩️".to_string()),
|
||||
|
||||
"bel" => Some('\x07'.to_string()), // Terminal Bell
|
||||
"backspace" => Some('\x08'.to_string()), // Backspace
|
||||
|
||||
_ => None,
|
||||
}
|
||||
CHAR_MAP.get(s).map(|s| s.into())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -20,14 +20,14 @@ impl WholeStreamCommand for Chart {
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
if args.scope.get_command("chart bar").is_none() {
|
||||
if args.scope().get_command("chart bar").is_none() {
|
||||
return Err(ShellError::untagged_runtime_error(
|
||||
"nu_plugin_chart not installed.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(ActionStream::one(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(get_full_help(&Chart, &args.scope)).into_value(Tag::unknown()),
|
||||
UntaggedValue::string(get_full_help(&Chart, args.scope())).into_value(Tag::unknown()),
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::{evaluate_baseline_expr, BufCodecReader};
|
||||
use nu_engine::{MaybeTextCodec, StringOrBinary};
|
||||
use nu_test_support::NATIVE_PATH_ENV_VAR;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use std::io::Write;
|
||||
@ -149,7 +150,9 @@ fn spawn(
|
||||
process.arg(&command.name);
|
||||
for arg in args {
|
||||
// Clean the args before we use them:
|
||||
let arg = arg.replace("|", "\\|");
|
||||
// https://stackoverflow.com/questions/1200235/how-to-pass-a-quoted-pipe-character-to-cmd-exe
|
||||
// cmd.exe needs to have a caret to escape a pipe
|
||||
let arg = arg.replace("|", "^|");
|
||||
process.arg(&arg);
|
||||
}
|
||||
process
|
||||
@ -199,247 +202,250 @@ fn spawn(
|
||||
trace!(target: "nu::run::external", "built command {:?}", process);
|
||||
|
||||
// TODO Switch to async_std::process once it's stabilized
|
||||
if let Ok(mut child) = process.spawn() {
|
||||
let (tx, rx) = mpsc::sync_channel(0);
|
||||
match process.spawn() {
|
||||
Ok(mut child) => {
|
||||
let (tx, rx) = mpsc::sync_channel(0);
|
||||
|
||||
let mut stdin = child.stdin.take();
|
||||
let mut stdin = child.stdin.take();
|
||||
|
||||
let stdin_write_tx = tx.clone();
|
||||
let stdout_read_tx = tx;
|
||||
let stdin_name_tag = command.name_tag.clone();
|
||||
let stdout_name_tag = command.name_tag;
|
||||
let stdin_write_tx = tx.clone();
|
||||
let stdout_read_tx = tx;
|
||||
let stdin_name_tag = command.name_tag.clone();
|
||||
let stdout_name_tag = command.name_tag;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
if !input.is_empty() {
|
||||
let mut stdin_write = stdin
|
||||
.take()
|
||||
.expect("Internal error: could not get stdin pipe for external command");
|
||||
std::thread::spawn(move || {
|
||||
if !input.is_empty() {
|
||||
let mut stdin_write = stdin
|
||||
.take()
|
||||
.expect("Internal error: could not get stdin pipe for external command");
|
||||
|
||||
for value in input {
|
||||
match &value.value {
|
||||
UntaggedValue::Primitive(Primitive::Nothing) => continue,
|
||||
UntaggedValue::Primitive(Primitive::String(s)) => {
|
||||
if stdin_write.write(s.as_bytes()).is_err() {
|
||||
// Other side has closed, so exit
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
UntaggedValue::Primitive(Primitive::Binary(b)) => {
|
||||
if stdin_write.write(b).is_err() {
|
||||
// Other side has closed, so exit
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
unsupported => {
|
||||
println!("Unsupported: {:?}", unsupported);
|
||||
let _ = stdin_write_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!(
|
||||
"Received unexpected type from pipeline ({})",
|
||||
unsupported.type_name()
|
||||
),
|
||||
"expected a string",
|
||||
stdin_name_tag.clone(),
|
||||
)),
|
||||
tag: stdin_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
std::thread::spawn(move || {
|
||||
if external_redirection == ExternalRedirection::Stdout
|
||||
|| external_redirection == ExternalRedirection::StdoutAndStderr
|
||||
{
|
||||
let stdout = if let Some(stdout) = child.stdout.take() {
|
||||
stdout
|
||||
} else {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"Can't redirect the stdout for external command",
|
||||
"can't redirect stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
};
|
||||
|
||||
// let file = futures::io::AllowStdIo::new(stdout);
|
||||
// let stream = FramedRead::new(file, MaybeTextCodec::default());
|
||||
let buf_read = BufReader::new(stdout);
|
||||
let buf_codec = BufCodecReader::new(buf_read, MaybeTextCodec::default());
|
||||
|
||||
for line in buf_codec {
|
||||
match line {
|
||||
Ok(line) => match line {
|
||||
StringOrBinary::String(s) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(s.clone())),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
for value in input {
|
||||
match &value.value {
|
||||
UntaggedValue::Primitive(Primitive::Nothing) => continue,
|
||||
UntaggedValue::Primitive(Primitive::String(s)) => {
|
||||
if stdin_write.write(s.as_bytes()).is_err() {
|
||||
// Other side has closed, so exit
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
StringOrBinary::Binary(b) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Binary(
|
||||
b.into_iter().collect(),
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
UntaggedValue::Primitive(Primitive::Binary(b)) => {
|
||||
if stdin_write.write(b).is_err() {
|
||||
// Other side has closed, so exit
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
// If there's an exit status, it makes sense that we may error when
|
||||
// trying to read from its stdout pipe (likely been closed). In that
|
||||
// case, don't emit an error.
|
||||
let should_error = match child.wait() {
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
Err(_) => true,
|
||||
};
|
||||
|
||||
if should_error {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
unsupported => {
|
||||
println!("Unsupported: {:?}", unsupported);
|
||||
let _ = stdin_write_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!("Unable to read from stdout ({})", e),
|
||||
"unable to read from stdout",
|
||||
&stdout_name_tag,
|
||||
format!(
|
||||
"Received unexpected type from pipeline ({})",
|
||||
unsupported.type_name()
|
||||
),
|
||||
"expected a string",
|
||||
stdin_name_tag.clone(),
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
tag: stdin_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if external_redirection == ExternalRedirection::Stderr
|
||||
|| external_redirection == ExternalRedirection::StdoutAndStderr
|
||||
{
|
||||
let stderr = if let Some(stderr) = child.stderr.take() {
|
||||
stderr
|
||||
} else {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"Can't redirect the stderr for external command",
|
||||
"can't redirect stderr",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
};
|
||||
|
||||
// let file = futures::io::AllowStdIo::new(stderr);
|
||||
// let stream = FramedRead::new(file, MaybeTextCodec::default());
|
||||
let buf_reader = BufReader::new(stderr);
|
||||
let buf_codec = BufCodecReader::new(buf_reader, MaybeTextCodec::default());
|
||||
Ok(())
|
||||
});
|
||||
|
||||
for line in buf_codec {
|
||||
match line {
|
||||
Ok(line) => match line {
|
||||
StringOrBinary::String(s) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error(s),
|
||||
),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
StringOrBinary::Binary(_) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error("<binary stderr>"),
|
||||
),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
// If there's an exit status, it makes sense that we may error when
|
||||
// trying to read from its stdout pipe (likely been closed). In that
|
||||
// case, don't emit an error.
|
||||
let should_error = match child.wait() {
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
Err(_) => true,
|
||||
};
|
||||
|
||||
if should_error {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!("Unable to read from stdout ({})", e),
|
||||
"unable to read from stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can give an error when we see a non-zero exit code, but this is different
|
||||
// than what other shells will do.
|
||||
let external_failed = match child.wait() {
|
||||
Err(_) => true,
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
};
|
||||
|
||||
if external_failed {
|
||||
let cfg = nu_data::config::config(Tag::unknown());
|
||||
if let Ok(cfg) = cfg {
|
||||
if cfg.contains_key("nonzero_exit_errors") {
|
||||
std::thread::spawn(move || {
|
||||
if external_redirection == ExternalRedirection::Stdout
|
||||
|| external_redirection == ExternalRedirection::StdoutAndStderr
|
||||
{
|
||||
let stdout = if let Some(stdout) = child.stdout.take() {
|
||||
stdout
|
||||
} else {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"External command failed",
|
||||
"command failed",
|
||||
"Can't redirect the stdout for external command",
|
||||
"can't redirect stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
};
|
||||
|
||||
// let file = futures::io::AllowStdIo::new(stdout);
|
||||
// let stream = FramedRead::new(file, MaybeTextCodec::default());
|
||||
let buf_read = BufReader::new(stdout);
|
||||
let buf_codec = BufCodecReader::new(buf_read, MaybeTextCodec::default());
|
||||
|
||||
for line in buf_codec {
|
||||
match line {
|
||||
Ok(line) => match line {
|
||||
StringOrBinary::String(s) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(
|
||||
s.clone(),
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
StringOrBinary::Binary(b) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Binary(
|
||||
b.into_iter().collect(),
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
// If there's an exit status, it makes sense that we may error when
|
||||
// trying to read from its stdout pipe (likely been closed). In that
|
||||
// case, don't emit an error.
|
||||
let should_error = match child.wait() {
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
Err(_) => true,
|
||||
};
|
||||
|
||||
if should_error {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!("Unable to read from stdout ({})", e),
|
||||
"unable to read from stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::external_non_zero()),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
}
|
||||
if external_redirection == ExternalRedirection::Stderr
|
||||
|| external_redirection == ExternalRedirection::StdoutAndStderr
|
||||
{
|
||||
let stderr = if let Some(stderr) = child.stderr.take() {
|
||||
stderr
|
||||
} else {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"Can't redirect the stderr for external command",
|
||||
"can't redirect stderr",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
return Err(());
|
||||
};
|
||||
|
||||
Ok(())
|
||||
});
|
||||
// let file = futures::io::AllowStdIo::new(stderr);
|
||||
// let stream = FramedRead::new(file, MaybeTextCodec::default());
|
||||
let buf_reader = BufReader::new(stderr);
|
||||
let buf_codec = BufCodecReader::new(buf_reader, MaybeTextCodec::default());
|
||||
|
||||
let stream = ChannelReceiver::new(rx);
|
||||
Ok(stream.to_input_stream())
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Failed to spawn process",
|
||||
for line in buf_codec {
|
||||
match line {
|
||||
Ok(line) => match line {
|
||||
StringOrBinary::String(s) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error(s),
|
||||
),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
StringOrBinary::Binary(_) => {
|
||||
let result = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(
|
||||
ShellError::untagged_runtime_error("<binary stderr>"),
|
||||
),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
// If there's an exit status, it makes sense that we may error when
|
||||
// trying to read from its stdout pipe (likely been closed). In that
|
||||
// case, don't emit an error.
|
||||
let should_error = match child.wait() {
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
Err(_) => true,
|
||||
};
|
||||
|
||||
if should_error {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
format!("Unable to read from stdout ({})", e),
|
||||
"unable to read from stdout",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can give an error when we see a non-zero exit code, but this is different
|
||||
// than what other shells will do.
|
||||
let external_failed = match child.wait() {
|
||||
Err(_) => true,
|
||||
Ok(exit_status) => !exit_status.success(),
|
||||
};
|
||||
|
||||
if external_failed {
|
||||
let cfg = nu_data::config::config(Tag::unknown());
|
||||
if let Ok(cfg) = cfg {
|
||||
if cfg.contains_key("nonzero_exit_errors") {
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||
"External command failed",
|
||||
"command failed",
|
||||
&stdout_name_tag,
|
||||
)),
|
||||
tag: stdout_name_tag.clone(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
let _ = stdout_read_tx.send(Ok(Value {
|
||||
value: UntaggedValue::Error(ShellError::external_non_zero()),
|
||||
tag: stdout_name_tag,
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let stream = ChannelReceiver::new(rx);
|
||||
Ok(stream.to_input_stream())
|
||||
}
|
||||
Err(e) => Err(ShellError::labeled_error(
|
||||
format!("{}", e),
|
||||
"failed to spawn",
|
||||
&command.name_tag,
|
||||
))
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -515,7 +521,7 @@ fn remove_quotes(argument: &str) -> Option<&str> {
|
||||
fn shell_os_paths() -> Vec<std::path::PathBuf> {
|
||||
let mut original_paths = vec![];
|
||||
|
||||
if let Some(paths) = std::env::var_os("PATH") {
|
||||
if let Some(paths) = std::env::var_os(NATIVE_PATH_ENV_VAR) {
|
||||
original_paths = std::env::split_paths(&paths).collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
@ -531,7 +537,7 @@ mod tests {
|
||||
use super::{run_external_command, InputStream};
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use nu_engine::basic_evaluation_context;
|
||||
use nu_engine::EvaluationContext;
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use nu_test_support::commands::ExternalBuilder;
|
||||
@ -554,8 +560,7 @@ mod tests {
|
||||
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
|
||||
|
||||
let input = InputStream::empty();
|
||||
let mut ctx =
|
||||
basic_evaluation_context().expect("There was a problem creating a basic context.");
|
||||
let mut ctx = EvaluationContext::basic();
|
||||
|
||||
assert!(run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout).is_err());
|
||||
}
|
||||
@ -563,7 +568,7 @@ mod tests {
|
||||
// fn failure_run() -> Result<(), ShellError> {
|
||||
// let cmd = ExternalBuilder::for_name("fail").build();
|
||||
|
||||
// let mut ctx = crate::cli::basic_evaluation_context().expect("There was a problem creating a basic context.");
|
||||
// let mut ctx = crate::cli::EvaluationContext::basic().expect("There was a problem creating a basic context.");
|
||||
// let stream = run_external_command(cmd, &mut ctx, None, false)
|
||||
// ?
|
||||
// .expect("There was a problem running the external command.");
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
@ -18,7 +18,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
"clear the config"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
clear(args)
|
||||
}
|
||||
|
||||
@ -31,22 +31,22 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
pub fn clear(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
|
||||
let result = if let Some(global_cfg) = &mut args.configs.lock().global_config {
|
||||
let result = if let Some(global_cfg) = &mut args.configs().lock().global_config {
|
||||
global_cfg.vars.clear();
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Row(global_cfg.vars.clone().into()).into_value(args.call_info.name_tag),
|
||||
)))
|
||||
|
||||
let value = UntaggedValue::Row(global_cfg.vars.clone().into()).into_value(name);
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
};
|
||||
|
||||
result
|
||||
|
@ -2,8 +2,7 @@ use crate::prelude::*;
|
||||
use nu_engine::CommandArgs;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use nu_stream::ActionStream;
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
@ -20,22 +19,19 @@ impl WholeStreamCommand for Command {
|
||||
"Configuration management."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag;
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
|
||||
if let Some(global_cfg) = &args.configs.lock().global_config {
|
||||
if let Some(global_cfg) = &args.configs().lock().global_config {
|
||||
let result = global_cfg.vars.clone();
|
||||
Ok(vec![ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(name),
|
||||
)]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
let value = UntaggedValue::Row(result.into()).into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
column_path: ColumnPath,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config get"
|
||||
@ -27,7 +22,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Gets a value from the config"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
get(args)
|
||||
}
|
||||
|
||||
@ -40,11 +35,12 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
pub fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let (Arguments { column_path }, _) = args.process()?;
|
||||
let column_path = args.req(0)?;
|
||||
|
||||
let result = if let Some(global_cfg) = &ctx.configs.lock().global_config {
|
||||
let result = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
|
||||
@ -53,15 +49,14 @@ pub fn get(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => list.into_iter().to_action_stream(),
|
||||
x => ActionStream::one(ReturnSuccess::value(x)),
|
||||
} => OutputStream::from_stream(list.into_iter()),
|
||||
x => OutputStream::one(x),
|
||||
})
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
};
|
||||
|
||||
result
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
|
||||
use nu_protocol::{Primitive, Signature, UntaggedValue};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
@ -18,7 +18,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
"return the path to the config file"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
path(args)
|
||||
}
|
||||
|
||||
@ -31,16 +31,18 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
if let Some(global_cfg) = &mut args.configs.lock().global_config {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Primitive(Primitive::FilePath(global_cfg.file_path.clone())),
|
||||
)))
|
||||
pub fn path(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
|
||||
if let Some(global_cfg) = &mut args.configs().lock().global_config {
|
||||
let value = UntaggedValue::Primitive(Primitive::FilePath(global_cfg.file_path.clone()))
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,11 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
remove: Tagged<String>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config remove"
|
||||
@ -28,7 +23,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Removes a value from the config"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
remove(args)
|
||||
}
|
||||
|
||||
@ -41,9 +36,11 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
pub fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let (Arguments { remove }, _) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
let remove: Tagged<String> = args.req(0)?;
|
||||
|
||||
let key = remove.to_string();
|
||||
|
||||
@ -52,11 +49,10 @@ pub fn remove(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
global_cfg.vars.swap_remove(&key);
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
Ok(vec![ReturnSuccess::value(
|
||||
UntaggedValue::row(global_cfg.vars.clone()).into_value(remove.tag()),
|
||||
)]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
|
||||
let value: Value = UntaggedValue::row(global_cfg.vars.clone()).into_value(remove.tag);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Key does not exist in config",
|
||||
@ -65,11 +61,10 @@ pub fn remove(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
};
|
||||
|
||||
result
|
||||
|
@ -1,16 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
column_path: ColumnPath,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config set"
|
||||
@ -26,7 +20,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Sets a value in the config"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
set(args)
|
||||
}
|
||||
|
||||
@ -56,16 +50,13 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
pub fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let (
|
||||
Arguments {
|
||||
column_path,
|
||||
mut value,
|
||||
},
|
||||
_,
|
||||
) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let column_path = args.req(0)?;
|
||||
let mut value: Value = args.req(1)?;
|
||||
|
||||
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
|
||||
let configuration = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
|
||||
@ -85,19 +76,17 @@ pub fn set(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
|
||||
)))
|
||||
let value = UntaggedValue::row(global_cfg.vars.clone()).into_value(name);
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
Ok(_) => Ok(ActionStream::empty()),
|
||||
Ok(_) => Ok(OutputStream::empty()),
|
||||
Err(reason) => Err(reason),
|
||||
}
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
};
|
||||
|
||||
result
|
||||
|
@ -1,16 +1,11 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
set_into: Tagged<String>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config set_into"
|
||||
@ -28,7 +23,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Sets a value in the config"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
set_into(args)
|
||||
}
|
||||
|
||||
@ -41,20 +36,22 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_into(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
pub fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let (Arguments { set_into: v }, input) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let rows: Vec<Value> = input.collect();
|
||||
let key = v.to_string();
|
||||
let set_into: Tagged<String> = args.req(0)?;
|
||||
|
||||
let rows: Vec<Value> = args.input.collect();
|
||||
let key = set_into.to_string();
|
||||
|
||||
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
|
||||
if rows.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"No values given for set_into",
|
||||
"needs value(s) from pipeline",
|
||||
v.tag(),
|
||||
set_into.tag(),
|
||||
));
|
||||
} else if rows.len() == 1 {
|
||||
// A single value
|
||||
@ -71,15 +68,14 @@ pub fn set_into(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
|
||||
)))
|
||||
let value = UntaggedValue::row(global_cfg.vars.clone()).into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
let value = UntaggedValue::Error(crate::commands::config::err_no_global_cfg_present())
|
||||
.into_value(name);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
};
|
||||
|
||||
result
|
||||
|
@ -26,7 +26,7 @@ impl WholeStreamCommand for Cpy {
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
let shell_manager = args.shell_manager();
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (args, _) = args.process()?;
|
||||
shell_manager.cp(args, name)
|
||||
|
202
crates/nu-command/src/commands/dataframe/aggregate.rs
Normal file
202
crates/nu-command/src/commands/dataframe/aggregate.rs
Normal file
@ -0,0 +1,202 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use polars::frame::groupby::GroupBy;
|
||||
|
||||
use super::utils::convert_columns;
|
||||
|
||||
enum Operation {
|
||||
Mean,
|
||||
Sum,
|
||||
Min,
|
||||
Max,
|
||||
First,
|
||||
Last,
|
||||
Nunique,
|
||||
Quantile(f64),
|
||||
Median,
|
||||
Var,
|
||||
Std,
|
||||
Count,
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
fn from_tagged(
|
||||
name: &Tagged<String>,
|
||||
quantile: Option<Tagged<f64>>,
|
||||
) -> Result<Operation, ShellError> {
|
||||
match name.item.as_ref() {
|
||||
"mean" => Ok(Operation::Mean),
|
||||
"sum" => Ok(Operation::Sum),
|
||||
"min" => Ok(Operation::Min),
|
||||
"max" => Ok(Operation::Max),
|
||||
"first" => Ok(Operation::First),
|
||||
"last" => Ok(Operation::Last),
|
||||
"nunique" => Ok(Operation::Nunique),
|
||||
"quantile" => {
|
||||
match quantile {
|
||||
None => Err(ShellError::labeled_error(
|
||||
"Quantile value not fount",
|
||||
"Quantile operation requires quantile value",
|
||||
&name.tag,
|
||||
)),
|
||||
Some(value ) => {
|
||||
if (value.item < 0.0) | (value.item > 1.0) {
|
||||
Err(ShellError::labeled_error(
|
||||
"Inappropriate quantile",
|
||||
"Quantile value should be between 0.0 and 1.0",
|
||||
&value.tag,
|
||||
))
|
||||
} else {
|
||||
Ok(Operation::Quantile(value.item))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"median" => Ok(Operation::Median),
|
||||
"var" => Ok(Operation::Var),
|
||||
"std" => Ok(Operation::Std),
|
||||
"count" => Ok(Operation::Count),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Operation not fount",
|
||||
"Operation does not exist",
|
||||
&name.tag,
|
||||
"Perhaps you want: mean, sum, min, max, first, last, nunique, quantile, median, count",
|
||||
&name.tag,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"pls aggregate"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Performs an aggregation operation on a groupby object"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pls aggregate")
|
||||
.required("operation", SyntaxShape::String, "aggregate operation")
|
||||
.optional(
|
||||
"selection",
|
||||
SyntaxShape::Table,
|
||||
"columns to perform aggregation",
|
||||
)
|
||||
.named(
|
||||
"quantile",
|
||||
SyntaxShape::Number,
|
||||
"quantile value for quantile operation",
|
||||
Some('q'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
aggregate(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Aggregate sum by grouping by column a and summing on col b",
|
||||
example:
|
||||
"echo [[a b]; [one 1] [one 2]] | pls convert | pls groupby [a] | pls aggregate sum",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn aggregate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let mut args = args.evaluate_once()?;
|
||||
|
||||
let quantile: Option<Tagged<f64>> = args.get_flag("quantile")?;
|
||||
let operation: Tagged<String> = args.req(0)?;
|
||||
let op = Operation::from_tagged(&operation, quantile)?;
|
||||
|
||||
// Extracting the selection columns of the columns to perform the aggregation
|
||||
let agg_cols: Option<Vec<Value>> = args.opt(1)?;
|
||||
let (selection, agg_span) = match agg_cols {
|
||||
Some(cols) => {
|
||||
let (agg_string, agg_span) = convert_columns(&cols, &tag)?;
|
||||
(Some(agg_string), agg_span)
|
||||
}
|
||||
None => (None, Span::unknown()),
|
||||
};
|
||||
|
||||
// The operation is only done in one dataframe. Only one input is
|
||||
// expected from the InputStream
|
||||
match args.input.next() {
|
||||
None => Err(ShellError::labeled_error(
|
||||
"No input received",
|
||||
"missing dataframe input from stream",
|
||||
&tag,
|
||||
)),
|
||||
Some(value) => {
|
||||
if let UntaggedValue::DataFrame(PolarsData::GroupBy(nu_groupby)) = value.value {
|
||||
let groupby = nu_groupby.to_groupby()?;
|
||||
|
||||
let groupby = match &selection {
|
||||
Some(cols) => groupby.select(cols),
|
||||
None => groupby,
|
||||
};
|
||||
|
||||
let res = perform_aggregation(groupby, op, &operation.tag, &agg_span)?;
|
||||
|
||||
let final_df = Value {
|
||||
tag,
|
||||
value: UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame::new(
|
||||
res,
|
||||
))),
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(final_df))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"No groupby in stream",
|
||||
"no groupby found in input stream",
|
||||
&tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_aggregation(
|
||||
groupby: GroupBy,
|
||||
operation: Operation,
|
||||
operation_tag: &Tag,
|
||||
agg_span: &Span,
|
||||
) -> Result<polars::prelude::DataFrame, ShellError> {
|
||||
match operation {
|
||||
Operation::Mean => groupby.mean(),
|
||||
Operation::Sum => groupby.sum(),
|
||||
Operation::Min => groupby.min(),
|
||||
Operation::Max => groupby.max(),
|
||||
Operation::First => groupby.first(),
|
||||
Operation::Last => groupby.last(),
|
||||
Operation::Nunique => groupby.n_unique(),
|
||||
Operation::Quantile(quantile) => groupby.quantile(quantile),
|
||||
Operation::Median => groupby.median(),
|
||||
Operation::Var => groupby.var(),
|
||||
Operation::Std => groupby.std(),
|
||||
Operation::Count => groupby.count(),
|
||||
}
|
||||
.map_err(|e| {
|
||||
let span = if e.to_string().contains("Not found") {
|
||||
agg_span
|
||||
} else {
|
||||
&operation_tag.span
|
||||
};
|
||||
|
||||
ShellError::labeled_error("Aggregation error", format!("{}", e), span)
|
||||
})
|
||||
}
|
26
crates/nu-command/src/commands/dataframe/command.rs
Normal file
26
crates/nu-command/src/commands/dataframe/command.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"pls"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Commands to work with polars dataframes"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pls")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
|
||||
))
|
||||
}
|
||||
}
|
43
crates/nu-command/src/commands/dataframe/convert.rs
Normal file
43
crates/nu-command/src/commands/dataframe/convert.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
Signature, UntaggedValue,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"pls convert"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Converts a pipelined Table or List into a polars dataframe"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pls convert")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let df = NuDataFrame::try_from_iter(args.input, &tag)?;
|
||||
let init = InputStream::one(
|
||||
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)).into_value(&tag),
|
||||
);
|
||||
|
||||
Ok(init.to_output_stream())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Takes an input stream and converts it to a polars dataframe",
|
||||
example: "echo [[a b];[1 2] [3 4]] | pls convert",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
97
crates/nu-command/src/commands/dataframe/drop.rs
Normal file
97
crates/nu-command/src/commands/dataframe/drop.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::convert_columns;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"pls drop"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Creates a new dataframe by dropping the selected columns"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pls drop").required(
|
||||
"columns",
|
||||
SyntaxShape::Table,
|
||||
"column names to be dropped",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
drop(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "drop column a",
|
||||
example: "echo [[a b]; [1 2] [3 4]] | pls convert | pls drop [a]",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let mut args = args.evaluate_once()?;
|
||||
|
||||
let columns: Vec<Value> = args.req(0)?;
|
||||
|
||||
let (col_string, col_span) = convert_columns(&columns, &tag)?;
|
||||
|
||||
match args.input.next() {
|
||||
None => Err(ShellError::labeled_error(
|
||||
"No input received",
|
||||
"missing dataframe input from stream",
|
||||
&tag,
|
||||
)),
|
||||
Some(value) => {
|
||||
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
|
||||
dataframe: Some(ref df),
|
||||
..
|
||||
})) = value.value
|
||||
{
|
||||
let new_df = match col_string.iter().next() {
|
||||
Some(col) => df.drop(col).map_err(|e| {
|
||||
ShellError::labeled_error("Join error", format!("{}", e), &col_span)
|
||||
}),
|
||||
None => Err(ShellError::labeled_error(
|
||||
"Empty names list",
|
||||
"No column names where found",
|
||||
&col_span,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let res = col_string.iter().skip(1).try_fold(new_df, |new_df, col| {
|
||||
new_df.drop(col).map_err(|e| {
|
||||
ShellError::labeled_error("Drop error", format!("{}", e), &col_span)
|
||||
})
|
||||
})?;
|
||||
|
||||
let value = Value {
|
||||
value: UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame::new(
|
||||
res,
|
||||
))),
|
||||
tag: tag.clone(),
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"No dataframe in stream",
|
||||
"no dataframe found in input stream",
|
||||
&tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
81
crates/nu-command/src/commands/dataframe/dtypes.rs
Normal file
81
crates/nu-command/src/commands/dataframe/dtypes.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
Signature, TaggedDictBuilder, UntaggedValue,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"pls dtypes"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Show dataframe data types"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pls dtypes")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
dtypes(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "drop column a",
|
||||
example: "echo [[a b]; [1 2] [3 4]] | pls convert | pls dtypes",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn dtypes(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let mut args = args.evaluate_once()?;
|
||||
|
||||
match args.input.next() {
|
||||
None => Err(ShellError::labeled_error(
|
||||
"No input received",
|
||||
"missing dataframe input from stream",
|
||||
&tag,
|
||||
)),
|
||||
Some(value) => {
|
||||
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
|
||||
dataframe: Some(df),
|
||||
..
|
||||
})) = value.value
|
||||
{
|
||||
let col_names = df
|
||||
.get_column_names()
|
||||
.iter()
|
||||
.map(|v| v.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let values =
|
||||
df.dtypes()
|
||||
.into_iter()
|
||||
.zip(col_names.into_iter())
|
||||
.map(move |(dtype, name)| {
|
||||
let mut data = TaggedDictBuilder::new(tag.clone());
|
||||
data.insert_value("column", name.as_ref());
|
||||
data.insert_value("dtype", format!("{}", dtype));
|
||||
|
||||
data.into_value()
|
||||
});
|
||||
|
||||
Ok(OutputStream::from_stream(values))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"No dataframe in stream",
|
||||
"no dataframe found in input stream",
|
||||
&tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
94
crates/nu-command/src/commands/dataframe/groupby.rs
Normal file
94
crates/nu-command/src/commands/dataframe/groupby.rs
Normal file
@ -0,0 +1,94 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, NuGroupBy, PolarsData},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::convert_columns;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"pls groupby"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Creates a groupby object that can be used for other aggregations"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pls groupby").required(
|
||||
"by columns",
|
||||
SyntaxShape::Table,
|
||||
"groupby columns",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
groupby(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Grouping by column a",
|
||||
example: "echo [[a b]; [one 1] [one 2]] | pls convert | pls groupby [a]",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn groupby(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let mut args = args.evaluate_once()?;
|
||||
|
||||
// Extracting the names of the columns to perform the groupby
|
||||
let by_columns: Vec<Value> = args.req(0)?;
|
||||
let (columns_string, col_span) = convert_columns(&by_columns, &tag)?;
|
||||
|
||||
// The operation is only done in one dataframe. Only one input is
|
||||
// expected from the InputStream
|
||||
match args.input.next() {
|
||||
None => Err(ShellError::labeled_error(
|
||||
"No input received",
|
||||
"missing dataframe input from stream",
|
||||
&tag,
|
||||
)),
|
||||
Some(value) => {
|
||||
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(nu_df)) = value.value {
|
||||
let df = match nu_df.dataframe {
|
||||
Some(df) => df,
|
||||
None => unreachable!("No dataframe in nu_dataframe"),
|
||||
};
|
||||
|
||||
// This is the expensive part of the groupby; to create the
|
||||
// groups that will be used for grouping the data in the
|
||||
// dataframe. Once it has been done these values can be stored
|
||||
// in the NuGroupBy
|
||||
let groupby = df.groupby(&columns_string).map_err(|e| {
|
||||
ShellError::labeled_error("Groupby error", format!("{}", e), col_span)
|
||||
})?;
|
||||
|
||||
let groups = groupby.get_groups().to_vec();
|
||||
let groupby = Value {
|
||||
tag: value.tag,
|
||||
value: UntaggedValue::DataFrame(PolarsData::GroupBy(NuGroupBy::new(
|
||||
NuDataFrame::new_with_name(df, nu_df.name),
|
||||
columns_string,
|
||||
groups,
|
||||
))),
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(groupby))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"No dataframe in stream",
|
||||
"no dataframe found in input stream",
|
||||
&tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
205
crates/nu-command/src/commands/dataframe/join.rs
Normal file
205
crates/nu-command/src/commands/dataframe/join.rs
Normal file
@ -0,0 +1,205 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::convert_columns;
|
||||
|
||||
use polars::prelude::JoinType;
|
||||
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"pls join"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Joins a dataframe using columns as reference"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pls join")
|
||||
.required("dataframe", SyntaxShape::Any, "right dataframe to join")
|
||||
.required(
|
||||
"l_columns",
|
||||
SyntaxShape::Table,
|
||||
"left column names to perform join",
|
||||
)
|
||||
.required(
|
||||
"r_columns",
|
||||
SyntaxShape::Table,
|
||||
"right column names to perform join",
|
||||
)
|
||||
.named(
|
||||
"type",
|
||||
SyntaxShape::String,
|
||||
"type of join. Inner by default",
|
||||
Some('t'),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
join(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "inner join dataframe",
|
||||
example: "echo [[a b]; [1 2] [3 4]] | pls convert | pls join $right [a] [a]",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "right join dataframe",
|
||||
example:
|
||||
"echo [[a b]; [1 2] [3 4] [5 6]] | pls convert | pls join $right [b] [b] -t right",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn join(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let mut args = args.evaluate_once()?;
|
||||
|
||||
let r_df: Value = args.req(0)?;
|
||||
let l_col: Vec<Value> = args.req(1)?;
|
||||
let r_col: Vec<Value> = args.req(2)?;
|
||||
let join_type_op: Option<Tagged<String>> = args.get_flag("type")?;
|
||||
|
||||
let join_type = match join_type_op {
|
||||
None => JoinType::Inner,
|
||||
Some(val) => match val.item.as_ref() {
|
||||
"inner" => JoinType::Inner,
|
||||
"outer" => JoinType::Outer,
|
||||
"left" => JoinType::Left,
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
"Incorrect join type",
|
||||
"Invalid join type",
|
||||
&val.tag,
|
||||
"Perhaps you mean: inner, outer or left",
|
||||
&val.tag,
|
||||
))
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let (l_col_string, l_col_span) = convert_columns(&l_col, &tag)?;
|
||||
let (r_col_string, r_col_span) = convert_columns(&r_col, &tag)?;
|
||||
|
||||
match args.input.next() {
|
||||
None => Err(ShellError::labeled_error(
|
||||
"No input received",
|
||||
"missing dataframe input from stream",
|
||||
&tag,
|
||||
)),
|
||||
Some(value) => {
|
||||
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
|
||||
dataframe: Some(ref df),
|
||||
..
|
||||
})) = value.value
|
||||
{
|
||||
let res = match r_df.value {
|
||||
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
|
||||
dataframe: Some(r_df),
|
||||
..
|
||||
})) => {
|
||||
// Checking the column types before performing the join
|
||||
check_column_datatypes(
|
||||
df,
|
||||
&l_col_string,
|
||||
&l_col_span,
|
||||
&r_col_string,
|
||||
&r_col_span,
|
||||
)?;
|
||||
|
||||
df.join(&r_df, &l_col_string, &r_col_string, join_type)
|
||||
.map_err(|e| {
|
||||
ShellError::labeled_error(
|
||||
"Join error",
|
||||
format!("{}", e),
|
||||
&l_col_span,
|
||||
)
|
||||
})
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Not a dataframe",
|
||||
"not a dataframe type value",
|
||||
&r_df.tag,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let value = Value {
|
||||
value: UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame::new(
|
||||
res,
|
||||
))),
|
||||
tag: tag.clone(),
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"No dataframe in stream",
|
||||
"no dataframe found in input stream",
|
||||
&tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_column_datatypes<T: AsRef<str>>(
|
||||
df: &polars::prelude::DataFrame,
|
||||
l_cols: &[T],
|
||||
l_col_span: &Span,
|
||||
r_cols: &[T],
|
||||
r_col_span: &Span,
|
||||
) -> Result<(), ShellError> {
|
||||
if l_cols.len() != r_cols.len() {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
"Mismatched number of column names",
|
||||
format!(
|
||||
"found {} left names vs {} right names",
|
||||
l_cols.len(),
|
||||
r_cols.len()
|
||||
),
|
||||
l_col_span,
|
||||
"perhaps you need to change the number of columns to join",
|
||||
r_col_span,
|
||||
));
|
||||
}
|
||||
|
||||
for (l, r) in l_cols.iter().zip(r_cols.iter()) {
|
||||
let l_series = df
|
||||
.column(l.as_ref())
|
||||
.map_err(|e| ShellError::labeled_error("Join error", format!("{}", e), l_col_span))?;
|
||||
|
||||
let r_series = df
|
||||
.column(r.as_ref())
|
||||
.map_err(|e| ShellError::labeled_error("Join error", format!("{}", e), r_col_span))?;
|
||||
|
||||
if l_series.dtype() != r_series.dtype() {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
"Mismatched datatypes",
|
||||
format!(
|
||||
"left column type '{}' doesn't match '{}' right column match",
|
||||
l_series.dtype(),
|
||||
r_series.dtype()
|
||||
),
|
||||
l_col_span,
|
||||
"perhaps you need to select other column to match",
|
||||
r_col_span,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
64
crates/nu-command/src/commands/dataframe/list.rs
Normal file
64
crates/nu-command/src/commands/dataframe/list.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
Signature, TaggedDictBuilder, UntaggedValue,
|
||||
};
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"pls list"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Lists stored dataframes"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pls list")
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let values = args
|
||||
.context
|
||||
.scope
|
||||
.get_vars()
|
||||
.into_iter()
|
||||
.filter_map(|(name, value)| {
|
||||
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
|
||||
dataframe: Some(df),
|
||||
name: file_name,
|
||||
})) = &value.value
|
||||
{
|
||||
let mut data = TaggedDictBuilder::new(value.tag.clone());
|
||||
|
||||
let rows = df.height();
|
||||
let cols = df.width();
|
||||
|
||||
data.insert_value("name", name.as_ref());
|
||||
data.insert_value("file", file_name.as_ref());
|
||||
data.insert_value("rows", format!("{}", rows));
|
||||
data.insert_value("columns", format!("{}", cols));
|
||||
|
||||
Some(data.into_value())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
Ok(OutputStream::from_stream(values))
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Lists loaded dataframes in current scope",
|
||||
example: "pls list",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
221
crates/nu-command/src/commands/dataframe/load.rs
Normal file
221
crates/nu-command/src/commands/dataframe/load.rs
Normal file
@ -0,0 +1,221 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_engine::{EvaluatedCommandArgs, WholeStreamCommand};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
Primitive, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
use polars::prelude::{CsvReader, JsonReader, ParquetReader, SerReader};
|
||||
use std::fs::File;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"pls load"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Loads dataframe form csv file"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pls load")
|
||||
.required(
|
||||
"file",
|
||||
SyntaxShape::FilePath,
|
||||
"the file path to load values from",
|
||||
)
|
||||
.named(
|
||||
"delimiter",
|
||||
SyntaxShape::String,
|
||||
"file delimiter character. CSV file",
|
||||
Some('d'),
|
||||
)
|
||||
.switch(
|
||||
"no_header",
|
||||
"Indicates if file doesn't have header. CSV file",
|
||||
None,
|
||||
)
|
||||
.named(
|
||||
"infer_schema",
|
||||
SyntaxShape::Number,
|
||||
"Set number of row to infer the schema of the file. CSV file",
|
||||
None,
|
||||
)
|
||||
.named(
|
||||
"skip_rows",
|
||||
SyntaxShape::Number,
|
||||
"Number of rows to skip from file. CSV file",
|
||||
None,
|
||||
)
|
||||
.named(
|
||||
"columns",
|
||||
SyntaxShape::Table,
|
||||
"Columns to be selected from csv file. CSV file",
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
create_from_file(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Takes a file name and creates a dataframe",
|
||||
example: "pls load test.csv",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn create_from_file(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let args = args.evaluate_once()?;
|
||||
let file: Tagged<PathBuf> = args.req(0)?;
|
||||
|
||||
let df = match file.item().extension() {
|
||||
Some(e) => match e.to_str() {
|
||||
Some("csv") => from_csv(args),
|
||||
Some("parquet") => from_parquet(args),
|
||||
Some("json") => from_json(args),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Error with file",
|
||||
"Not a csv, parquet or json file",
|
||||
&file.tag,
|
||||
)),
|
||||
},
|
||||
None => Err(ShellError::labeled_error(
|
||||
"Error with file",
|
||||
"File without extension",
|
||||
&file.tag,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let file_name = match file.item.into_os_string().into_string() {
|
||||
Ok(name) => name,
|
||||
Err(e) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Error with file name",
|
||||
format!("{:?}", e),
|
||||
&file.tag,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let init = InputStream::one(
|
||||
UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame::new_with_name(
|
||||
df, file_name,
|
||||
)))
|
||||
.into_value(&tag),
|
||||
);
|
||||
|
||||
Ok(init.to_output_stream())
|
||||
}
|
||||
|
||||
fn from_parquet(args: EvaluatedCommandArgs) -> Result<polars::prelude::DataFrame, ShellError> {
|
||||
let file: Tagged<PathBuf> = args.req(0)?;
|
||||
|
||||
let r = File::open(&file.item)
|
||||
.map_err(|e| ShellError::labeled_error("Error with file", format!("{:?}", e), &file.tag))?;
|
||||
|
||||
let reader = ParquetReader::new(r);
|
||||
|
||||
reader
|
||||
.finish()
|
||||
.map_err(|e| ShellError::labeled_error("Error with file", format!("{:?}", e), &file.tag))
|
||||
}
|
||||
|
||||
fn from_json(args: EvaluatedCommandArgs) -> Result<polars::prelude::DataFrame, ShellError> {
|
||||
let file: Tagged<PathBuf> = args.req(0)?;
|
||||
|
||||
let r = File::open(&file.item)
|
||||
.map_err(|e| ShellError::labeled_error("Error with file", format!("{:?}", e), &file.tag))?;
|
||||
|
||||
let reader = JsonReader::new(r);
|
||||
|
||||
reader
|
||||
.finish()
|
||||
.map_err(|e| ShellError::labeled_error("Error with file", format!("{:?}", e), &file.tag))
|
||||
}
|
||||
|
||||
fn from_csv(args: EvaluatedCommandArgs) -> Result<polars::prelude::DataFrame, ShellError> {
|
||||
let file: Tagged<PathBuf> = args.req(0)?;
|
||||
let delimiter: Option<Tagged<String>> = args.get_flag("delimiter")?;
|
||||
let no_header: bool = args.has_flag("no_header");
|
||||
let infer_schema: Option<Tagged<usize>> = args.get_flag("infer_schema")?;
|
||||
let skip_rows: Option<Tagged<usize>> = args.get_flag("skip_rows")?;
|
||||
let columns: Option<Vec<Value>> = args.get_flag("columns")?;
|
||||
|
||||
let csv_reader = CsvReader::from_path(&file.item).map_err(|e| {
|
||||
ShellError::labeled_error("Unable to parse file", format!("{}", e), &file.tag)
|
||||
})?;
|
||||
|
||||
let csv_reader = match delimiter {
|
||||
None => csv_reader,
|
||||
Some(d) => {
|
||||
if d.item.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Incorrect delimiter",
|
||||
"Delimiter has to be one char",
|
||||
&d.tag,
|
||||
));
|
||||
} else {
|
||||
let delimiter = match d.item.chars().nth(0) {
|
||||
Some(d) => d as u8,
|
||||
None => unreachable!(),
|
||||
};
|
||||
csv_reader.with_delimiter(delimiter)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let csv_reader = if no_header {
|
||||
csv_reader.has_header(false)
|
||||
} else {
|
||||
csv_reader.has_header(true)
|
||||
};
|
||||
|
||||
let csv_reader = match infer_schema {
|
||||
None => csv_reader.infer_schema(None),
|
||||
Some(r) => csv_reader.infer_schema(Some(r.item)),
|
||||
};
|
||||
|
||||
let csv_reader = match skip_rows {
|
||||
None => csv_reader,
|
||||
Some(r) => csv_reader.with_skip_rows(r.item),
|
||||
};
|
||||
|
||||
let csv_reader = match columns {
|
||||
None => csv_reader,
|
||||
Some(c) => {
|
||||
let columns = c
|
||||
.into_iter()
|
||||
.map(|value| match value.value {
|
||||
UntaggedValue::Primitive(Primitive::String(s)) => Ok(s),
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect type for column",
|
||||
"Only string as columns",
|
||||
&value.tag,
|
||||
)),
|
||||
})
|
||||
.collect::<Result<Vec<String>, ShellError>>();
|
||||
|
||||
csv_reader.with_columns(Some(columns?))
|
||||
}
|
||||
};
|
||||
|
||||
match csv_reader.finish() {
|
||||
Ok(csv_reader) => Ok(csv_reader),
|
||||
Err(e) => Err(ShellError::labeled_error(
|
||||
"Error while parsing dataframe",
|
||||
format!("{}", e),
|
||||
&file.tag,
|
||||
)),
|
||||
}
|
||||
}
|
26
crates/nu-command/src/commands/dataframe/mod.rs
Normal file
26
crates/nu-command/src/commands/dataframe/mod.rs
Normal file
@ -0,0 +1,26 @@
|
||||
pub mod aggregate;
|
||||
pub mod command;
|
||||
pub mod convert;
|
||||
pub mod drop;
|
||||
pub mod dtypes;
|
||||
pub mod groupby;
|
||||
pub mod join;
|
||||
pub mod list;
|
||||
pub mod load;
|
||||
pub mod sample;
|
||||
pub mod select;
|
||||
pub mod show;
|
||||
pub(crate) mod utils;
|
||||
|
||||
pub use aggregate::DataFrame as DataFrameAggregate;
|
||||
pub use command::Command as DataFrame;
|
||||
pub use convert::DataFrame as DataFrameConvert;
|
||||
pub use drop::DataFrame as DataFrameDrop;
|
||||
pub use dtypes::DataFrame as DataFrameDTypes;
|
||||
pub use groupby::DataFrame as DataFrameGroupBy;
|
||||
pub use join::DataFrame as DataFrameJoin;
|
||||
pub use list::DataFrame as DataFrameList;
|
||||
pub use load::DataFrame as DataFrameLoad;
|
||||
pub use sample::DataFrame as DataFrameSample;
|
||||
pub use select::DataFrame as DataFrameSelect;
|
||||
pub use show::DataFrame as DataFrameShow;
|
117
crates/nu-command/src/commands/dataframe/sample.rs
Normal file
117
crates/nu-command/src/commands/dataframe/sample.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"pls sample"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Create sample dataframe"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pls load")
|
||||
.named(
|
||||
"n_rows",
|
||||
SyntaxShape::Number,
|
||||
"number of rows to be taken from dataframe",
|
||||
Some('n'),
|
||||
)
|
||||
.named(
|
||||
"fraction",
|
||||
SyntaxShape::Number,
|
||||
"fraction of dataframe to be taken",
|
||||
Some('f'),
|
||||
)
|
||||
.switch("replace", "sample with replace", Some('e'))
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
sample(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Sample rows from dataframe",
|
||||
example: "echo [[a b]; [1 2] [3 4]] | pls load | pls sample -r 1",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Shows sample row using fraction and replace",
|
||||
example: "echo [[a b]; [1 2] [3 4] [5 6]] | pls load | pls sample -f 0.5 -e",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn sample(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let mut args = args.evaluate_once()?;
|
||||
|
||||
let rows: Option<Tagged<usize>> = args.get_flag("n_rows")?;
|
||||
let fraction: Option<Tagged<f64>> = args.get_flag("fraction")?;
|
||||
let replace: bool = args.has_flag("replace");
|
||||
|
||||
match args.input.next() {
|
||||
None => Err(ShellError::labeled_error(
|
||||
"No input received",
|
||||
"missing dataframe input from stream",
|
||||
&tag,
|
||||
)),
|
||||
Some(value) => {
|
||||
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
|
||||
dataframe: Some(ref df),
|
||||
..
|
||||
})) = value.value
|
||||
{
|
||||
let res = match (rows, fraction) {
|
||||
(Some(rows), None) => df.sample_n(rows.item, replace).map_err(|e| {
|
||||
ShellError::labeled_error("Polars error", format!("{}", e), &rows.tag)
|
||||
}),
|
||||
(None, Some(frac)) => df.sample_frac(frac.item, replace).map_err(|e| {
|
||||
ShellError::labeled_error("Polars error", format!("{}", e), &frac.tag)
|
||||
}),
|
||||
(Some(_), Some(_)) => Err(ShellError::labeled_error(
|
||||
"Incompatible flags",
|
||||
"Only one selection criterion allowed",
|
||||
&tag,
|
||||
)),
|
||||
(None, None) => Err(ShellError::labeled_error_with_secondary(
|
||||
"No selection",
|
||||
"No selection criterion was found",
|
||||
&tag,
|
||||
"Perhaps you want to use the flag -n or -f",
|
||||
&tag,
|
||||
)),
|
||||
}?;
|
||||
|
||||
let value = Value {
|
||||
value: UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame::new(
|
||||
res,
|
||||
))),
|
||||
tag: tag.clone(),
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"No dataframe in stream",
|
||||
"no dataframe found in input stream",
|
||||
&tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
84
crates/nu-command/src/commands/dataframe/select.rs
Normal file
84
crates/nu-command/src/commands/dataframe/select.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
dataframe::{NuDataFrame, PolarsData},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use super::utils::convert_columns;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"pls select"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Creates a new dataframe with the selected columns"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pls select").required(
|
||||
"columns",
|
||||
SyntaxShape::Table,
|
||||
"selected column names",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
select(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Create new dataframe with column a",
|
||||
example: "echo [[a b]; [1 2] [3 4]] | pls convert | pls select [a]",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn select(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let mut args = args.evaluate_once()?;
|
||||
|
||||
let columns: Vec<Value> = args.req(0)?;
|
||||
|
||||
let (col_string, col_span) = convert_columns(&columns, &tag)?;
|
||||
|
||||
match args.input.next() {
|
||||
None => Err(ShellError::labeled_error(
|
||||
"No input received",
|
||||
"missing dataframe input from stream",
|
||||
&tag,
|
||||
)),
|
||||
Some(value) => {
|
||||
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame {
|
||||
dataframe: Some(ref df),
|
||||
..
|
||||
})) = value.value
|
||||
{
|
||||
let res = df.select(&col_string).map_err(|e| {
|
||||
ShellError::labeled_error("Drop error", format!("{}", e), &col_span)
|
||||
})?;
|
||||
|
||||
let value = Value {
|
||||
value: UntaggedValue::DataFrame(PolarsData::EagerDataFrame(NuDataFrame::new(
|
||||
res,
|
||||
))),
|
||||
tag: tag.clone(),
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"No dataframe in stream",
|
||||
"no dataframe found in input stream",
|
||||
&tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
78
crates/nu-command/src/commands/dataframe/show.rs
Normal file
78
crates/nu-command/src/commands/dataframe/show.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{dataframe::PolarsData, Signature, SyntaxShape, UntaggedValue};
|
||||
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct DataFrame;
|
||||
|
||||
impl WholeStreamCommand for DataFrame {
|
||||
fn name(&self) -> &str {
|
||||
"pls show"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Show dataframe"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pls show")
|
||||
.named(
|
||||
"n_rows",
|
||||
SyntaxShape::Number,
|
||||
"number of rows to be shown",
|
||||
Some('n'),
|
||||
)
|
||||
.switch("tail", "shows tail rows", Some('t'))
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
show(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Shows head rows from dataframe",
|
||||
example: "echo [[a b]; [1 2] [3 4]] | pls convert | pls show",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Shows tail rows from dataframe",
|
||||
example: "echo [[a b]; [1 2] [3 4] [5 6]] | pls convert | pls show -t -n 1",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn show(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let mut args = args.evaluate_once()?;
|
||||
|
||||
let rows: Option<Tagged<usize>> = args.get_flag("rows")?;
|
||||
let tail: bool = args.has_flag("tail");
|
||||
|
||||
match args.input.next() {
|
||||
None => Err(ShellError::labeled_error(
|
||||
"No input received",
|
||||
"missing dataframe input from stream",
|
||||
&tag,
|
||||
)),
|
||||
Some(value) => {
|
||||
if let UntaggedValue::DataFrame(PolarsData::EagerDataFrame(df)) = value.value {
|
||||
let rows = rows.map(|v| v.item);
|
||||
let values = if tail { df.tail(rows)? } else { df.head(rows)? };
|
||||
|
||||
Ok(OutputStream::from_stream(values.into_iter()))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"No dataframe in stream",
|
||||
"no dataframe found in input stream",
|
||||
&tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
42
crates/nu-command/src/commands/dataframe/utils.rs
Normal file
42
crates/nu-command/src/commands/dataframe/utils.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, UntaggedValue, Value};
|
||||
|
||||
// Converts a Vec<Value> to a Vec<String> with a Span marking the whole
|
||||
// location of the columns for error referencing
|
||||
pub(crate) fn convert_columns<'columns>(
|
||||
columns: &'columns [Value],
|
||||
tag: &Tag,
|
||||
) -> Result<(Vec<String>, Span), ShellError> {
|
||||
let mut col_span = match columns
|
||||
.iter()
|
||||
.nth(0)
|
||||
.map(|v| Span::new(v.tag.span.start(), v.tag.span.end()))
|
||||
{
|
||||
Some(span) => span,
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Empty column list",
|
||||
"Empty list found for command",
|
||||
tag,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let res = columns
|
||||
.iter()
|
||||
.map(|value| match &value.value {
|
||||
UntaggedValue::Primitive(Primitive::String(s)) => {
|
||||
col_span = col_span.until(value.tag.span);
|
||||
Ok(s.clone())
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Incorrect column format",
|
||||
"Only string as column name",
|
||||
&value.tag,
|
||||
)),
|
||||
})
|
||||
.collect::<Result<Vec<String>, _>>()?;
|
||||
|
||||
Ok((res, col_span))
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
@ -18,10 +18,10 @@ impl WholeStreamCommand for Command {
|
||||
"Apply date function."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(&Command, &args.scope)).into_value(Tag::unknown()),
|
||||
)))
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,20 +1,12 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
Dictionary, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{Dictionary, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use std::fmt::{self, write};
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FormatArgs {
|
||||
format: Tagged<String>,
|
||||
table: bool,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date format"
|
||||
@ -30,7 +22,7 @@ impl WholeStreamCommand for Date {
|
||||
"Format a given date using the given format string."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
format(args)
|
||||
}
|
||||
|
||||
@ -50,9 +42,18 @@ impl WholeStreamCommand for Date {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
pub fn format(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (FormatArgs { format, table }, input) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let format: Tagged<String> = args.req(0)?;
|
||||
let table: Option<bool> = args.get_flag("table")?;
|
||||
|
||||
let input = if args.input.is_empty() {
|
||||
InputStream::one(crate::commands::date::now::date_now(&tag))
|
||||
} else {
|
||||
args.input
|
||||
};
|
||||
|
||||
Ok(input
|
||||
.map(move |value| match value {
|
||||
@ -70,7 +71,7 @@ pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
&format.tag,
|
||||
))
|
||||
} else {
|
||||
let value = if table {
|
||||
let value = if table.is_some() {
|
||||
let mut indexmap = IndexMap::new();
|
||||
indexmap.insert(
|
||||
"formatted".to_string(),
|
||||
@ -82,7 +83,7 @@ pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
UntaggedValue::string(&output).into_value(&tag)
|
||||
};
|
||||
|
||||
ReturnSuccess::value(value)
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
@ -91,7 +92,7 @@ pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
&tag,
|
||||
)),
|
||||
})
|
||||
.to_action_stream())
|
||||
.to_input_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -3,7 +3,7 @@ use chrono_tz::TZ_VARIANTS;
|
||||
use indexmap::IndexMap;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, ReturnSuccess, Signature, UntaggedValue};
|
||||
use nu_protocol::{Dictionary, Signature, UntaggedValue};
|
||||
|
||||
pub struct Date;
|
||||
|
||||
@ -20,7 +20,7 @@ impl WholeStreamCommand for Date {
|
||||
"List supported time zones."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
list_timezone(args)
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ impl WholeStreamCommand for Date {
|
||||
}
|
||||
}
|
||||
|
||||
fn list_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn list_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
@ -52,12 +52,10 @@ fn list_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
UntaggedValue::string(tz.name()).into_value(&tag),
|
||||
);
|
||||
|
||||
Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::Row(Dictionary { entries }).into_value(&tag),
|
||||
))
|
||||
Ok(UntaggedValue::Row(Dictionary { entries }).into_value(&tag))
|
||||
});
|
||||
|
||||
Ok(list.into_iter().to_action_stream())
|
||||
Ok(list.into_iter().to_input_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -2,7 +2,7 @@ use crate::prelude::*;
|
||||
use chrono::{DateTime, Local};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
use nu_protocol::{Signature, UntaggedValue, Value};
|
||||
|
||||
pub struct Date;
|
||||
|
||||
@ -19,20 +19,23 @@ impl WholeStreamCommand for Date {
|
||||
"Get the current date."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
now(args)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn now(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
pub fn date_now(tag: &Tag) -> Value {
|
||||
let now: DateTime<Local> = Local::now();
|
||||
|
||||
let value = UntaggedValue::date(now.with_timezone(now.offset())).into_value(&tag);
|
||||
UntaggedValue::date(now.with_timezone(now.offset())).into_value(tag)
|
||||
}
|
||||
|
||||
Ok(ActionStream::one(value))
|
||||
pub fn now(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let value = date_now(&args.call_info.name_tag);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -3,7 +3,7 @@ use chrono::{Datelike, Timelike};
|
||||
use indexmap::IndexMap;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
||||
use nu_protocol::{Dictionary, Primitive, Signature, UntaggedValue, Value};
|
||||
|
||||
pub struct Date;
|
||||
|
||||
@ -20,7 +20,7 @@ impl WholeStreamCommand for Date {
|
||||
"Print the date in a structured table."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
to_table(args)
|
||||
}
|
||||
|
||||
@ -33,10 +33,14 @@ impl WholeStreamCommand for Date {
|
||||
}
|
||||
}
|
||||
|
||||
fn to_table(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn to_table(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let input = args.input;
|
||||
let input = if args.input.is_empty() {
|
||||
InputStream::one(crate::commands::date::now::date_now(&tag))
|
||||
} else {
|
||||
args.input
|
||||
};
|
||||
|
||||
Ok(input
|
||||
.map(move |value| match value {
|
||||
@ -79,7 +83,7 @@ fn to_table(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
|
||||
let value = UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag);
|
||||
|
||||
ReturnSuccess::value(value)
|
||||
Ok(value)
|
||||
}
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Expected a date from pipeline",
|
||||
@ -87,7 +91,7 @@ fn to_table(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
&tag,
|
||||
)),
|
||||
})
|
||||
.to_action_stream())
|
||||
.to_input_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -2,16 +2,11 @@ use crate::commands::date::parser::{datetime_in_timezone, ParseErrorKind};
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DateToTimeZoneArgs {
|
||||
timezone: Tagged<String>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date to-timezone"
|
||||
@ -33,7 +28,7 @@ impl WholeStreamCommand for Date {
|
||||
"Use 'date list-timezone' to list all supported time zones."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
to_timezone(args)
|
||||
}
|
||||
|
||||
@ -58,11 +53,14 @@ impl WholeStreamCommand for Date {
|
||||
}
|
||||
}
|
||||
|
||||
fn to_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn to_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (DateToTimeZoneArgs { timezone }, input) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
Ok(input
|
||||
let timezone: Tagged<String> = args.req(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |value| match value {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Date(dt)),
|
||||
@ -71,7 +69,7 @@ fn to_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(dt) => {
|
||||
let value = UntaggedValue::date(dt).into_value(&tag);
|
||||
|
||||
ReturnSuccess::value(value)
|
||||
Ok(value)
|
||||
}
|
||||
Err(e) => Err(ShellError::labeled_error(
|
||||
error_message(e),
|
||||
@ -85,7 +83,7 @@ fn to_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
&tag,
|
||||
)),
|
||||
})
|
||||
.to_action_stream())
|
||||
.to_input_stream())
|
||||
}
|
||||
|
||||
fn error_message(err: ParseErrorKind) -> &'static str {
|
||||
|
@ -1,54 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use chrono::{DateTime, Utc};
|
||||
use nu_errors::ShellError;
|
||||
|
||||
use crate::commands::date::utils::date_to_value;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_protocol::Signature;
|
||||
|
||||
pub struct Date;
|
||||
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date utc"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("date utc")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"return the current date in utc."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
utc(args)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn utc(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let no_fmt = "".to_string();
|
||||
|
||||
let value = {
|
||||
let local: DateTime<Utc> = Utc::now();
|
||||
date_to_value(local, tag, no_fmt)
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Date;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Date {})
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::basic_evaluation_context;
|
||||
use nu_engine::whole_stream_command;
|
||||
use std::error::Error;
|
||||
|
||||
pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Box<dyn Error>> {
|
||||
let context = basic_evaluation_context()?;
|
||||
let context = EvaluationContext::basic();
|
||||
|
||||
{
|
||||
use crate::commands::*;
|
||||
@ -14,6 +13,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(NuPlugin),
|
||||
whole_stream_command(Let),
|
||||
whole_stream_command(LetEnv),
|
||||
whole_stream_command(LoadEnv),
|
||||
whole_stream_command(Def),
|
||||
whole_stream_command(Source),
|
||||
// System/file operations
|
||||
@ -89,7 +89,6 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(StrUpcase),
|
||||
whole_stream_command(StrCapitalize),
|
||||
whole_stream_command(StrFindReplace),
|
||||
whole_stream_command(StrFrom),
|
||||
whole_stream_command(StrSubstring),
|
||||
whole_stream_command(StrToDatetime),
|
||||
whole_stream_command(StrContains),
|
||||
@ -122,7 +121,9 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(Update),
|
||||
whole_stream_command(Insert),
|
||||
whole_stream_command(Into),
|
||||
whole_stream_command(IntoBinary),
|
||||
whole_stream_command(IntoInt),
|
||||
whole_stream_command(IntoString),
|
||||
whole_stream_command(SplitBy),
|
||||
// Row manipulation
|
||||
whole_stream_command(All),
|
||||
@ -157,6 +158,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(EachGroup),
|
||||
whole_stream_command(EachWindow),
|
||||
whole_stream_command(Empty),
|
||||
whole_stream_command(ForIn),
|
||||
// Table manipulation
|
||||
whole_stream_command(Flatten),
|
||||
whole_stream_command(Move),
|
||||
@ -239,6 +241,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(PathExpand),
|
||||
whole_stream_command(PathJoin),
|
||||
whole_stream_command(PathParse),
|
||||
whole_stream_command(PathRelativeTo),
|
||||
whole_stream_command(PathSplit),
|
||||
whole_stream_command(PathType),
|
||||
// Url
|
||||
@ -250,6 +253,31 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(Seq),
|
||||
whole_stream_command(SeqDates),
|
||||
whole_stream_command(TermSize),
|
||||
//Dataframe commands
|
||||
#[cfg(feature = "dataframe")]
|
||||
whole_stream_command(DataFrame),
|
||||
#[cfg(feature = "dataframe")]
|
||||
whole_stream_command(DataFrameConvert),
|
||||
#[cfg(feature = "dataframe")]
|
||||
whole_stream_command(DataFrameLoad),
|
||||
#[cfg(feature = "dataframe")]
|
||||
whole_stream_command(DataFrameList),
|
||||
#[cfg(feature = "dataframe")]
|
||||
whole_stream_command(DataFrameGroupBy),
|
||||
#[cfg(feature = "dataframe")]
|
||||
whole_stream_command(DataFrameAggregate),
|
||||
#[cfg(feature = "dataframe")]
|
||||
whole_stream_command(DataFrameShow),
|
||||
#[cfg(feature = "dataframe")]
|
||||
whole_stream_command(DataFrameSample),
|
||||
#[cfg(feature = "dataframe")]
|
||||
whole_stream_command(DataFrameJoin),
|
||||
#[cfg(feature = "dataframe")]
|
||||
whole_stream_command(DataFrameDrop),
|
||||
#[cfg(feature = "dataframe")]
|
||||
whole_stream_command(DataFrameSelect),
|
||||
#[cfg(feature = "dataframe")]
|
||||
whole_stream_command(DataFrameDTypes),
|
||||
]);
|
||||
|
||||
#[cfg(feature = "clipboard-cli")]
|
||||
|
@ -2,14 +2,16 @@ use crate::prelude::*;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::CapturedBlock, hir::ExternalRedirection, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, hir::ExternalRedirection, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct Do;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct DoArgs {
|
||||
block: CapturedBlock,
|
||||
ignore_errors: bool,
|
||||
rest: Vec<Value>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Do {
|
||||
@ -21,17 +23,18 @@ impl WholeStreamCommand for Do {
|
||||
Signature::build("do")
|
||||
.required("block", SyntaxShape::Block, "the block to run ")
|
||||
.switch(
|
||||
"ignore_errors",
|
||||
"ignore-errors",
|
||||
"ignore errors as the block runs",
|
||||
Some('i'),
|
||||
)
|
||||
.rest(SyntaxShape::Any, "the parameter(s) for the block")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Runs a block, optionally ignoring errors."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
do_(args)
|
||||
}
|
||||
|
||||
@ -47,32 +50,36 @@ impl WholeStreamCommand for Do {
|
||||
example: r#"do -i { thisisnotarealcommand }"#,
|
||||
result: Some(vec![]),
|
||||
},
|
||||
Example {
|
||||
description: "Run the block with a parameter",
|
||||
example: r#"do { |x| $x + 100 } 55"#,
|
||||
result: Some(vec![UntaggedValue::int(155).into()]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn do_(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
||||
|
||||
let context = EvaluationContext::from_args(&raw_args);
|
||||
let (
|
||||
DoArgs {
|
||||
ignore_errors,
|
||||
mut block,
|
||||
},
|
||||
input,
|
||||
) = raw_args.process()?;
|
||||
let args = raw_args.evaluate_once()?;
|
||||
let do_args = DoArgs {
|
||||
block: args.req(0)?,
|
||||
ignore_errors: args.has_flag("ignore-errors"),
|
||||
rest: args.rest(1)?,
|
||||
};
|
||||
|
||||
let block_redirection = match external_redirection {
|
||||
ExternalRedirection::None => {
|
||||
if ignore_errors {
|
||||
if do_args.ignore_errors {
|
||||
ExternalRedirection::Stderr
|
||||
} else {
|
||||
ExternalRedirection::None
|
||||
}
|
||||
}
|
||||
ExternalRedirection::Stdout => {
|
||||
if ignore_errors {
|
||||
if do_args.ignore_errors {
|
||||
ExternalRedirection::StdoutAndStderr
|
||||
} else {
|
||||
ExternalRedirection::Stdout
|
||||
@ -81,14 +88,30 @@ fn do_(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
x => x,
|
||||
};
|
||||
|
||||
if let Some(block) = std::sync::Arc::<nu_protocol::hir::Block>::get_mut(&mut block.block) {
|
||||
block.set_redirect(block_redirection);
|
||||
}
|
||||
context.scope.enter_scope();
|
||||
let result = run_block(&block.block, &context, input);
|
||||
|
||||
context.scope.add_vars(&do_args.block.captured.entries);
|
||||
|
||||
for (param, value) in do_args
|
||||
.block
|
||||
.block
|
||||
.params
|
||||
.positional
|
||||
.iter()
|
||||
.zip(do_args.rest)
|
||||
{
|
||||
context.scope.add_var(param.0.name(), value.clone());
|
||||
}
|
||||
|
||||
let result = run_block(
|
||||
&do_args.block.block,
|
||||
&context,
|
||||
args.input,
|
||||
block_redirection,
|
||||
);
|
||||
context.scope.exit_scope();
|
||||
|
||||
if ignore_errors {
|
||||
if do_args.ignore_errors {
|
||||
// To properly ignore errors we need to redirect stderr, consume it, and remove
|
||||
// any errors we see in the process.
|
||||
|
||||
@ -96,12 +119,12 @@ fn do_(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(mut stream) => {
|
||||
let output = stream.drain_vec();
|
||||
context.clear_errors();
|
||||
Ok(output.into_iter().to_action_stream())
|
||||
Ok(output.into_iter().to_output_stream())
|
||||
}
|
||||
Err(_) => Ok(ActionStream::empty()),
|
||||
Err(_) => Ok(OutputStream::empty()),
|
||||
}
|
||||
} else {
|
||||
result.map(|x| x.to_action_stream())
|
||||
result.map(|x| x.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,16 +2,11 @@ use crate::prelude::*;
|
||||
use nu_data::base::select_fields;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape};
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
columns: Option<Tagged<u64>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"drop column"
|
||||
@ -29,7 +24,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Remove the last number of columns. If you want to remove columns by name, try 'reject'."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
drop(args)
|
||||
}
|
||||
|
||||
@ -47,8 +42,9 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { columns }, input) = args.process()?;
|
||||
fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let columns: Option<Tagged<u64>> = args.opt(0)?;
|
||||
|
||||
let to_drop = if let Some(quantity) = columns {
|
||||
*quantity as usize
|
||||
@ -56,7 +52,8 @@ fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
1
|
||||
};
|
||||
|
||||
Ok(input
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |item| {
|
||||
let headers = item.data_descriptors();
|
||||
|
||||
@ -66,10 +63,9 @@ fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
n => &headers[..n - to_drop],
|
||||
};
|
||||
|
||||
select_fields(&item, descs, item.tag())
|
||||
Ok(select_fields(&item, descs, item.tag()))
|
||||
})
|
||||
.map(ReturnSuccess::value)
|
||||
.to_action_stream())
|
||||
.to_input_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -6,11 +6,6 @@ use nu_source::Tagged;
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
rows: Option<Tagged<u64>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"drop"
|
||||
@ -28,7 +23,7 @@ impl WholeStreamCommand for Command {
|
||||
"Remove the last number of rows or columns."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
drop(args)
|
||||
}
|
||||
|
||||
@ -51,9 +46,10 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { rows }, input) = args.process()?;
|
||||
let v: Vec<_> = input.into_vec();
|
||||
fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let rows: Option<Tagged<u64>> = args.opt(0)?;
|
||||
let v: Vec<_> = args.input.into_vec();
|
||||
|
||||
let rows_to_drop = if let Some(quantity) = rows {
|
||||
*quantity as usize
|
||||
@ -62,7 +58,7 @@ fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
};
|
||||
|
||||
Ok(if rows_to_drop == 0 {
|
||||
v.into_iter().to_action_stream()
|
||||
v.into_iter().map(Ok).to_input_stream()
|
||||
} else {
|
||||
let k = if v.len() < rows_to_drop {
|
||||
0
|
||||
@ -70,8 +66,8 @@ fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
v.len() - rows_to_drop
|
||||
};
|
||||
|
||||
let iter = v.into_iter().take(k);
|
||||
let iter = v.into_iter().map(Ok).take(k);
|
||||
|
||||
iter.to_action_stream()
|
||||
iter.to_input_stream()
|
||||
})
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ impl WholeStreamCommand for Du {
|
||||
|
||||
fn du(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let ctrl_c = args.ctrl_c.clone();
|
||||
let ctrl_c = args.ctrl_c();
|
||||
let ctrl_c_copy = ctrl_c.clone();
|
||||
|
||||
let (args, _): (DuArgs, _) = args.process()?;
|
||||
|
@ -4,7 +4,8 @@ use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
hir::{CapturedBlock, ExternalRedirection},
|
||||
Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct Each;
|
||||
@ -41,7 +42,7 @@ impl WholeStreamCommand for Each {
|
||||
},
|
||||
Example {
|
||||
description: "Echo the square of each integer",
|
||||
example: "echo [1 2 3] | each { echo $(= $it * $it) }",
|
||||
example: "echo [1 2 3] | each { echo ($it * $it) }",
|
||||
result: Some(vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
@ -51,9 +52,18 @@ impl WholeStreamCommand for Each {
|
||||
Example {
|
||||
description: "Number each item and echo a message",
|
||||
example:
|
||||
"echo ['bob' 'fred'] | each --numbered { echo `{{$it.index}} is {{$it.item}}` }",
|
||||
"echo ['bob' 'fred'] | each --numbered { echo $\"($it.index) is ($it.item)\" }",
|
||||
result: Some(vec![Value::from("0 is bob"), Value::from("1 is fred")]),
|
||||
},
|
||||
Example {
|
||||
description: "Name the block variable that each uses",
|
||||
example: "[1, 2, 3] | each {|x| $x + 100}",
|
||||
result: Some(vec![
|
||||
UntaggedValue::int(101).into(),
|
||||
UntaggedValue::int(102).into(),
|
||||
UntaggedValue::int(103).into(),
|
||||
]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -62,6 +72,7 @@ pub fn process_row(
|
||||
captured_block: Arc<Box<CapturedBlock>>,
|
||||
context: Arc<EvaluationContext>,
|
||||
input: Value,
|
||||
external_redirection: ExternalRedirection,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let input_clone = input.clone();
|
||||
// When we process a row, we need to know whether the block wants to have the contents of the row as
|
||||
@ -77,16 +88,18 @@ pub fn process_row(
|
||||
context.scope.enter_scope();
|
||||
context.scope.add_vars(&captured_block.captured.entries);
|
||||
|
||||
if !captured_block.block.params.positional.is_empty() {
|
||||
// FIXME: add check for more than parameter, once that's supported
|
||||
context
|
||||
.scope
|
||||
.add_var(captured_block.block.params.positional[0].0.name(), input);
|
||||
if let Some((arg, _)) = captured_block.block.params.positional.first() {
|
||||
context.scope.add_var(arg.name(), input);
|
||||
} else {
|
||||
context.scope.add_var("$it", input);
|
||||
}
|
||||
|
||||
let result = run_block(&captured_block.block, &*context, input_stream);
|
||||
let result = run_block(
|
||||
&captured_block.block,
|
||||
&*context,
|
||||
input_stream,
|
||||
external_redirection,
|
||||
);
|
||||
|
||||
context.scope.exit_scope();
|
||||
|
||||
@ -95,7 +108,7 @@ pub fn process_row(
|
||||
|
||||
pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
|
||||
let mut dict = TaggedDictBuilder::new(item.tag());
|
||||
dict.insert_untagged("index", UntaggedValue::int(index));
|
||||
dict.insert_untagged("index", UntaggedValue::int(index as i64));
|
||||
dict.insert_value("item", item);
|
||||
|
||||
dict.into_value()
|
||||
@ -103,6 +116,7 @@ pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
|
||||
|
||||
fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
||||
let args = raw_args.evaluate_once()?;
|
||||
|
||||
let block: CapturedBlock = args.req(0)?;
|
||||
@ -119,7 +133,7 @@ fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let context = context.clone();
|
||||
let row = make_indexed_item(input.0, input.1);
|
||||
|
||||
match process_row(block, context, row) {
|
||||
match process_row(block, context, row, external_redirection) {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Value::error(e)),
|
||||
}
|
||||
@ -133,7 +147,7 @@ fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let block = block.clone();
|
||||
let context = context.clone();
|
||||
|
||||
match process_row(block, context, input) {
|
||||
match process_row(block, context, input, external_redirection) {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Value::error(e)),
|
||||
}
|
||||
|
@ -2,19 +2,14 @@ use crate::commands::each::process_row;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::CapturedBlock, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{
|
||||
hir::{CapturedBlock, ExternalRedirection},
|
||||
Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub struct EachGroup;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EachGroupArgs {
|
||||
group_size: Tagged<usize>,
|
||||
block: CapturedBlock,
|
||||
//numbered: Tagged<bool>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for EachGroup {
|
||||
fn name(&self) -> &str {
|
||||
"each group"
|
||||
@ -42,19 +37,24 @@ impl WholeStreamCommand for EachGroup {
|
||||
}]
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
let (each_args, input): (EachGroupArgs, _) = raw_args.process()?;
|
||||
let block = Arc::new(Box::new(each_args.block));
|
||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
||||
let args = raw_args.evaluate_once()?;
|
||||
|
||||
let group_size: Tagged<usize> = args.req(0)?;
|
||||
let block: CapturedBlock = args.req(1)?;
|
||||
let block = Arc::new(Box::new(block));
|
||||
|
||||
let each_group_iterator = EachGroupIterator {
|
||||
block,
|
||||
context,
|
||||
group_size: each_args.group_size.item,
|
||||
input,
|
||||
group_size: group_size.item,
|
||||
input: args.input,
|
||||
external_redirection,
|
||||
};
|
||||
|
||||
Ok(each_group_iterator.flatten().to_action_stream())
|
||||
Ok(each_group_iterator.flatten().map(Ok).to_input_stream())
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,6 +63,7 @@ struct EachGroupIterator {
|
||||
context: Arc<EvaluationContext>,
|
||||
group_size: usize,
|
||||
input: InputStream,
|
||||
external_redirection: ExternalRedirection,
|
||||
}
|
||||
|
||||
impl Iterator for EachGroupIterator {
|
||||
@ -89,6 +90,7 @@ impl Iterator for EachGroupIterator {
|
||||
group,
|
||||
self.block.clone(),
|
||||
self.context.clone(),
|
||||
self.external_redirection,
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -97,13 +99,14 @@ pub(crate) fn run_block_on_vec(
|
||||
input: Vec<Value>,
|
||||
block: Arc<Box<CapturedBlock>>,
|
||||
context: Arc<EvaluationContext>,
|
||||
external_redirection: ExternalRedirection,
|
||||
) -> OutputStream {
|
||||
let value = Value {
|
||||
value: UntaggedValue::Table(input),
|
||||
tag: Tag::unknown(),
|
||||
};
|
||||
|
||||
match process_row(block, context, value) {
|
||||
match process_row(block, context, value, external_redirection) {
|
||||
Ok(s) => {
|
||||
// We need to handle this differently depending on whether process_row
|
||||
// returned just 1 value or if it returned multiple as a stream.
|
||||
|
@ -5,17 +5,9 @@ use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::CapturedBlock, Primitive, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub struct EachWindow;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EachWindowArgs {
|
||||
window_size: Tagged<usize>,
|
||||
block: CapturedBlock,
|
||||
stride: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for EachWindow {
|
||||
fn name(&self) -> &str {
|
||||
"each window"
|
||||
@ -49,21 +41,29 @@ impl WholeStreamCommand for EachWindow {
|
||||
}]
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
let (each_args, mut input): (EachWindowArgs, _) = raw_args.process()?;
|
||||
let block = Arc::new(Box::new(each_args.block));
|
||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
||||
|
||||
let mut window: Vec<_> = input
|
||||
let mut args = raw_args.evaluate_once()?;
|
||||
let window_size: Tagged<usize> = args.req(0)?;
|
||||
let block: CapturedBlock = args.req(1)?;
|
||||
let stride: Option<Tagged<usize>> = args.get_flag("stride")?;
|
||||
|
||||
let block = Arc::new(Box::new(block));
|
||||
|
||||
let mut window: Vec<_> = args
|
||||
.input
|
||||
.by_ref()
|
||||
.take(*each_args.window_size - 1)
|
||||
.take(*window_size - 1)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// `window` must start with dummy values, which will be removed on the first iteration
|
||||
let stride = each_args.stride.map(|x| *x).unwrap_or(1);
|
||||
let stride = stride.map(|x| *x).unwrap_or(1);
|
||||
window.insert(0, UntaggedValue::Primitive(Primitive::Nothing).into());
|
||||
|
||||
Ok(input
|
||||
Ok(args
|
||||
.input
|
||||
.enumerate()
|
||||
.map(move |(i, input)| {
|
||||
// This would probably be more efficient if `last` was a VecDeque
|
||||
@ -76,14 +76,20 @@ impl WholeStreamCommand for EachWindow {
|
||||
let local_window = window.clone();
|
||||
|
||||
if i % stride == 0 {
|
||||
Some(run_block_on_vec(local_window, block, context))
|
||||
Some(run_block_on_vec(
|
||||
local_window,
|
||||
block,
|
||||
context,
|
||||
external_redirection,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(|x| x)
|
||||
.flatten()
|
||||
.to_action_stream())
|
||||
.flatten()
|
||||
.map(Ok)
|
||||
.to_input_stream())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::prelude::*;
|
||||
use bigdecimal::Zero;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::Operator;
|
||||
@ -40,23 +39,27 @@ impl WholeStreamCommand for Echo {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_value_to_stream(v: Value) -> InputStream {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Table(table),
|
||||
..
|
||||
} => InputStream::from_stream(table.into_iter()),
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Range(range)),
|
||||
tag,
|
||||
} => InputStream::from_stream(RangeIterator::new(*range, tag)),
|
||||
x => InputStream::one(x),
|
||||
}
|
||||
}
|
||||
|
||||
fn echo(args: CommandArgs) -> Result<InputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let rest: Vec<Value> = args.rest(0)?;
|
||||
|
||||
let stream = rest.into_iter().map(|i| match i.as_string() {
|
||||
Ok(s) => InputStream::one(UntaggedValue::string(s).into_value(i.tag.clone())),
|
||||
_ => match i {
|
||||
Value {
|
||||
value: UntaggedValue::Table(table),
|
||||
..
|
||||
} => InputStream::from_stream(table.into_iter()),
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Range(range)),
|
||||
tag,
|
||||
} => InputStream::from_stream(RangeIterator::new(*range, tag)),
|
||||
x => InputStream::one(x),
|
||||
},
|
||||
Ok(s) => InputStream::one(UntaggedValue::string(s).into_value(i.tag)),
|
||||
_ => expand_value_to_stream(i),
|
||||
});
|
||||
|
||||
Ok(InputStream::from_stream(stream.flatten()))
|
||||
@ -81,7 +84,7 @@ impl RangeIterator {
|
||||
};
|
||||
|
||||
let end = match range.to.0.item {
|
||||
Primitive::Nothing => Primitive::Int(u64::MAX.into()),
|
||||
Primitive::Nothing => Primitive::Int(i64::MAX),
|
||||
x => x,
|
||||
};
|
||||
|
||||
@ -121,11 +124,11 @@ impl Iterator for RangeIterator {
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Decimal(x)),
|
||||
UntaggedValue::Primitive(Primitive::Int(y)),
|
||||
) => x.cmp(&(BigDecimal::zero() + y)),
|
||||
) => x.cmp(&(BigDecimal::from(*y))),
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Int(x)),
|
||||
UntaggedValue::Primitive(Primitive::Decimal(y)),
|
||||
) => (BigDecimal::zero() + x).cmp(y),
|
||||
) => (BigDecimal::from(*x)).cmp(y),
|
||||
_ => {
|
||||
self.done = true;
|
||||
return Some(
|
||||
|
@ -3,8 +3,8 @@ use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape,
|
||||
UntaggedValue, Value,
|
||||
hir::CapturedBlock, hir::ExternalRedirection, ColumnPath, Primitive, ReturnSuccess, Signature,
|
||||
SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use crate::utils::arguments::arguments;
|
||||
@ -63,7 +63,7 @@ impl WholeStreamCommand for Command {
|
||||
),
|
||||
},Example {
|
||||
description: "use a block if setting the empty cell contents is wanted",
|
||||
example: "echo [[2020/04/16 2020/07/10 2020/11/16]; ['' [27] [37]]] | empty? 2020/04/16 { = [33 37] }",
|
||||
example: "echo [[2020/04/16 2020/07/10 2020/11/16]; ['' [27] [37]]] | empty? 2020/04/16 { [33 37] }",
|
||||
result: Some(
|
||||
vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
@ -140,9 +140,16 @@ fn process_row(
|
||||
|
||||
context.scope.enter_scope();
|
||||
context.scope.add_vars(&default_block.captured.entries);
|
||||
context.scope.add_var("$it", input.clone());
|
||||
if let Some((arg, _)) = default_block.block.params.positional.first() {
|
||||
context.scope.add_var(arg.name(), input.clone());
|
||||
}
|
||||
|
||||
let stream = run_block(&default_block.block, &*context, input_stream);
|
||||
let stream = run_block(
|
||||
&default_block.block,
|
||||
&*context,
|
||||
input_stream,
|
||||
ExternalRedirection::Stdout,
|
||||
);
|
||||
context.scope.exit_scope();
|
||||
|
||||
let mut stream = stream?;
|
||||
|
@ -11,12 +11,6 @@ use std::path::PathBuf;
|
||||
|
||||
pub struct Enter;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EnterArgs {
|
||||
location: Tagged<PathBuf>,
|
||||
encoding: Option<Tagged<String>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Enter {
|
||||
fn name(&self) -> &str {
|
||||
"enter"
|
||||
@ -75,16 +69,15 @@ documentation link at https://docs.rs/encoding_rs/0.8.28/encoding_rs/#statics"#
|
||||
}
|
||||
}
|
||||
|
||||
fn enter(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let scope = raw_args.scope.clone();
|
||||
let shell_manager = raw_args.shell_manager.clone();
|
||||
let head = raw_args.call_info.args.head.clone();
|
||||
let ctrl_c = raw_args.ctrl_c.clone();
|
||||
let configs = raw_args.configs.clone();
|
||||
let current_errors = raw_args.current_errors.clone();
|
||||
let host = raw_args.host.clone();
|
||||
let tag = raw_args.call_info.name_tag.clone();
|
||||
let (EnterArgs { location, encoding }, _) = raw_args.process()?;
|
||||
fn enter(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let head = args.call_info.args.head.clone();
|
||||
let context = args.context.clone();
|
||||
let scope = args.scope().clone();
|
||||
let path = args.context.shell_manager.path();
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let location: Tagged<PathBuf> = args.req(0)?;
|
||||
let encoding: Option<Tagged<String>> = args.get_flag("encoding")?;
|
||||
let location_string = location.display().to_string();
|
||||
|
||||
if location.is_dir() {
|
||||
@ -93,7 +86,7 @@ fn enter(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
)))
|
||||
} else {
|
||||
// If it's a file, attempt to open the file as a value and enter it
|
||||
let cwd = shell_manager.path();
|
||||
let cwd = path;
|
||||
|
||||
let full_path = std::path::PathBuf::from(cwd);
|
||||
let span = location.span();
|
||||
@ -110,12 +103,9 @@ fn enter(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
if let Some(extension) = file_extension {
|
||||
let command_name = format!("from {}", extension);
|
||||
if let Some(converter) = scope.get_command(&command_name) {
|
||||
let new_args = RawCommandArgs {
|
||||
host,
|
||||
ctrl_c,
|
||||
configs,
|
||||
current_errors,
|
||||
shell_manager,
|
||||
let tag = tagged_contents.tag.clone();
|
||||
let new_args = CommandArgs {
|
||||
context,
|
||||
call_info: UnevaluatedCallInfo {
|
||||
args: nu_protocol::hir::Call {
|
||||
head,
|
||||
@ -124,13 +114,11 @@ fn enter(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
span: Span::unknown(),
|
||||
external_redirection: ExternalRedirection::Stdout,
|
||||
},
|
||||
name_tag: tag,
|
||||
name_tag: tag.clone(),
|
||||
},
|
||||
scope,
|
||||
input: InputStream::one(tagged_contents),
|
||||
};
|
||||
let tag = tagged_contents.tag.clone();
|
||||
let mut result =
|
||||
converter.run(new_args.with_input(vec![tagged_contents]))?;
|
||||
let mut result = converter.run(new_args)?;
|
||||
let result_vec: Vec<Value> = result.drain_vec();
|
||||
Ok(result_vec
|
||||
.into_iter()
|
||||
|
@ -39,12 +39,12 @@ impl WholeStreamCommand for Command {
|
||||
},
|
||||
Example {
|
||||
description: "flatten a column having a nested table",
|
||||
example: "echo [[origin, people]; [Ecuador, $(echo [[name, meal]; ['Andres', 'arepa']])]] | flatten | get meal",
|
||||
example: "echo [[origin, people]; [Ecuador, (echo [[name, meal]; ['Andres', 'arepa']])]] | flatten | get meal",
|
||||
result: Some(vec![Value::from("arepa")]),
|
||||
},
|
||||
Example {
|
||||
description: "restrict the flattening by passing column names",
|
||||
example: "echo [[origin, crate, versions]; [World, $(echo [[name]; ['nu-cli']]), ['0.21', '0.22']]] | flatten versions | last | get versions",
|
||||
example: "echo [[origin, crate, versions]; [World, (echo [[name]; ['nu-cli']]), ['0.21', '0.22']]] | flatten versions | last | get versions",
|
||||
result: Some(vec![Value::from("0.22")]),
|
||||
}
|
||||
]
|
||||
|
174
crates/nu-command/src/commands/for_in.rs
Normal file
174
crates/nu-command/src/commands/for_in.rs
Normal file
@ -0,0 +1,174 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::{evaluate_baseline_expr, run_block};
|
||||
use nu_engine::{FromValue, WholeStreamCommand};
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::{CapturedBlock, ExternalRedirection},
|
||||
Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct ForIn;
|
||||
|
||||
impl WholeStreamCommand for ForIn {
|
||||
fn name(&self) -> &str {
|
||||
"for"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("for")
|
||||
.required("var", SyntaxShape::String, "the name of the variable")
|
||||
.required("in", SyntaxShape::String, "the word 'in'")
|
||||
.required("value", SyntaxShape::Any, "the value we want to iterate")
|
||||
.required("block", SyntaxShape::Block, "the block to run on each item")
|
||||
.switch(
|
||||
"numbered",
|
||||
"returned a numbered item ($it.index and $it.item)",
|
||||
Some('n'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Run a block on each row of the table."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
for_in(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Echo the square of each integer",
|
||||
example: "for x in [1 2 3] { $x * $x }",
|
||||
result: Some(vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(4).into(),
|
||||
UntaggedValue::int(9).into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Work with elements of a range",
|
||||
example: "for $x in 1..3 { $x }",
|
||||
result: Some(vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Number each item and echo a message",
|
||||
example: "for $it in ['bob' 'fred'] --numbered { $\"($it.index) is ($it.item)\" }",
|
||||
result: Some(vec![Value::from("0 is bob"), Value::from("1 is fred")]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_row(
|
||||
captured_block: Arc<Box<CapturedBlock>>,
|
||||
context: Arc<EvaluationContext>,
|
||||
input: Value,
|
||||
var_name: &str,
|
||||
external_redirection: ExternalRedirection,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let input_clone = input.clone();
|
||||
// When we process a row, we need to know whether the block wants to have the contents of the row as
|
||||
// a parameter to the block (so it gets assigned to a variable that can be used inside the block) or
|
||||
// if it wants the contents as as an input stream
|
||||
|
||||
let input_stream = if !captured_block.block.params.positional.is_empty() {
|
||||
InputStream::empty()
|
||||
} else {
|
||||
vec![Ok(input_clone)].into_iter().to_input_stream()
|
||||
};
|
||||
|
||||
context.scope.enter_scope();
|
||||
context.scope.add_vars(&captured_block.captured.entries);
|
||||
|
||||
context.scope.add_var(var_name, input);
|
||||
|
||||
let result = run_block(
|
||||
&captured_block.block,
|
||||
&*context,
|
||||
input_stream,
|
||||
external_redirection,
|
||||
);
|
||||
|
||||
context.scope.exit_scope();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
|
||||
let mut dict = TaggedDictBuilder::new(item.tag());
|
||||
dict.insert_untagged("index", UntaggedValue::int(index as i64));
|
||||
dict.insert_value("item", item);
|
||||
|
||||
dict.into_value()
|
||||
}
|
||||
|
||||
fn for_in(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
||||
//let args = raw_args.evaluate_once()?;
|
||||
|
||||
let numbered: bool = raw_args.call_info.switch_present("numbered");
|
||||
let positional = raw_args
|
||||
.call_info
|
||||
.args
|
||||
.positional
|
||||
.expect("Internal error: type checker should require args");
|
||||
|
||||
let var_name = positional[0].var_name()?;
|
||||
let rhs = evaluate_baseline_expr(&positional[2], &context)?;
|
||||
|
||||
let block: CapturedBlock =
|
||||
FromValue::from_value(&evaluate_baseline_expr(&positional[3], &context)?)?;
|
||||
|
||||
let input = crate::commands::echo::expand_value_to_stream(rhs);
|
||||
let block = Arc::new(Box::new(block));
|
||||
|
||||
if numbered {
|
||||
Ok(input
|
||||
.enumerate()
|
||||
.map(move |input| {
|
||||
let block = block.clone();
|
||||
let context = context.clone();
|
||||
let row = make_indexed_item(input.0, input.1);
|
||||
|
||||
match process_row(block, context, row, &var_name, external_redirection) {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Value::error(e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
} else {
|
||||
Ok(input
|
||||
.map(move |input| {
|
||||
let block = block.clone();
|
||||
let context = context.clone();
|
||||
|
||||
match process_row(block, context, input, &var_name, external_redirection) {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Value::error(e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ForIn;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(ForIn {})
|
||||
}
|
||||
}
|
@ -2,17 +2,12 @@ use crate::prelude::*;
|
||||
use nu_engine::evaluate_baseline_expr;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
use std::borrow::Borrow;
|
||||
|
||||
pub struct Format;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FormatArgs {
|
||||
pattern: Tagged<String>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Format {
|
||||
fn name(&self) -> &str {
|
||||
"format"
|
||||
@ -30,7 +25,7 @@ impl WholeStreamCommand for Format {
|
||||
"Format columns into a string using a simple pattern."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
format_command(args)
|
||||
}
|
||||
|
||||
@ -43,14 +38,16 @@ impl WholeStreamCommand for Format {
|
||||
}
|
||||
}
|
||||
|
||||
fn format_command(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn format_command(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let ctx = Arc::new(EvaluationContext::from_args(&args));
|
||||
let (FormatArgs { pattern }, input) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
let pattern: Tagged<String> = args.req(0)?;
|
||||
|
||||
let format_pattern = format(&pattern);
|
||||
let commands = Arc::new(format_pattern);
|
||||
|
||||
Ok(input
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |value| {
|
||||
let mut output = String::new();
|
||||
let commands = commands.clone();
|
||||
@ -82,9 +79,9 @@ fn format_command(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
}
|
||||
}
|
||||
|
||||
ReturnSuccess::value(UntaggedValue::string(output).into_untagged_value())
|
||||
Ok(UntaggedValue::string(output).into_untagged_value())
|
||||
})
|
||||
.to_action_stream())
|
||||
.to_input_stream())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -2,20 +2,12 @@ use crate::prelude::*;
|
||||
use nu_data::base::shape::InlineShape;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
ColumnPath, Primitive::Filesize, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{ColumnPath, Primitive::Filesize, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use nu_value_ext::get_data_by_column_path;
|
||||
|
||||
pub struct FileSize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
field: ColumnPath,
|
||||
format: Tagged<String>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for FileSize {
|
||||
fn name(&self) -> &str {
|
||||
"format filesize"
|
||||
@ -39,7 +31,7 @@ impl WholeStreamCommand for FileSize {
|
||||
"Converts a column of filesizes to some specified format"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
filesize(args)
|
||||
}
|
||||
|
||||
@ -63,50 +55,55 @@ fn process_row(
|
||||
input: Value,
|
||||
format: Tagged<String>,
|
||||
field: Arc<ColumnPath>,
|
||||
) -> Result<ActionStream, ShellError> {
|
||||
Ok({
|
||||
let replace_for = get_data_by_column_path(&input, &field, move |_, _, error| error);
|
||||
match replace_for {
|
||||
Ok(s) => {
|
||||
if let Value {
|
||||
value: UntaggedValue::Primitive(Filesize(fs)),
|
||||
..
|
||||
} = s
|
||||
{
|
||||
let byte_format = InlineShape::format_bytes(&fs, Some(&format.item));
|
||||
let byte_value = Value::from(byte_format.1);
|
||||
ActionStream::one(ReturnSuccess::value(
|
||||
input.replace_data_at_column_path(&field, byte_value).expect("Given that the existence check was already done, this shouldn't trigger never"),
|
||||
))
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
"the data in this row is not of the type filesize",
|
||||
"invalid datatype in row",
|
||||
input.tag(),
|
||||
));
|
||||
}
|
||||
) -> Result<Value, ShellError> {
|
||||
let replace_for = get_data_by_column_path(&input, &field, move |_, _, error| error);
|
||||
match replace_for {
|
||||
Ok(s) => {
|
||||
if let Value {
|
||||
value: UntaggedValue::Primitive(Filesize(fs)),
|
||||
..
|
||||
} = s
|
||||
{
|
||||
let byte_format = InlineShape::format_bytes(fs, Some(&format.item));
|
||||
let byte_value = Value::from(byte_format.1);
|
||||
Ok(input
|
||||
.replace_data_at_column_path(&field, byte_value)
|
||||
.expect(
|
||||
"Given that the existence check was already done, this shouldn't trigger never",
|
||||
))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"the data in this row is not of the type filesize",
|
||||
"invalid datatype in row",
|
||||
input.tag(),
|
||||
))
|
||||
}
|
||||
Err(e) => ActionStream::one(Err(e)),
|
||||
}
|
||||
})
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn filesize(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { field, format }, input) = raw_args.process()?;
|
||||
fn filesize(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = raw_args.evaluate_once()?;
|
||||
|
||||
let field: ColumnPath = args.req(0)?;
|
||||
let format: Tagged<String> = args.req(1)?;
|
||||
let field = Arc::new(field);
|
||||
|
||||
Ok(input
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |input| {
|
||||
let format = format.clone();
|
||||
let field = field.clone();
|
||||
|
||||
match process_row(input, format, field) {
|
||||
Ok(s) => s,
|
||||
Err(e) => ActionStream::one(Err(e)),
|
||||
Ok(s) => Ok(s),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_action_stream())
|
||||
.map(Ok)
|
||||
.to_input_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -20,7 +20,7 @@ impl WholeStreamCommand for From {
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(get_full_help(&From, &args.scope)).into_value(Tag::unknown()),
|
||||
UntaggedValue::string(get_full_help(&From, args.scope())).into_value(Tag::unknown()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ fn convert_json_value_to_nu_value(v: &nu_json::Value, tag: impl Into<Tag>) -> Va
|
||||
nu_json::Value::Null => UntaggedValue::Primitive(Primitive::Nothing).into_value(&tag),
|
||||
nu_json::Value::Bool(b) => UntaggedValue::boolean(*b).into_value(&tag),
|
||||
nu_json::Value::F64(n) => UntaggedValue::decimal_from_float(*n, span).into_value(&tag),
|
||||
nu_json::Value::U64(n) => UntaggedValue::int(*n).into_value(&tag),
|
||||
nu_json::Value::U64(n) => UntaggedValue::big_int(*n).into_value(&tag),
|
||||
nu_json::Value::I64(n) => UntaggedValue::int(*n).into_value(&tag),
|
||||
nu_json::Value::String(s) => {
|
||||
UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(&tag)
|
||||
|
@ -228,8 +228,8 @@ pub fn get_column_from_row_error(
|
||||
} => {
|
||||
let primary_label = format!("There isn't a column named '{}'", &column);
|
||||
|
||||
if let Some(suggestions) = did_you_mean(&obj_source, column_path_tried.as_string()) {
|
||||
Some(ShellError::labeled_error_with_secondary(
|
||||
did_you_mean(&obj_source, column_path_tried.as_string()).map(|suggestions| {
|
||||
ShellError::labeled_error_with_secondary(
|
||||
"Unknown column",
|
||||
primary_label,
|
||||
column_path_tried.span,
|
||||
@ -239,10 +239,8 @@ pub fn get_column_from_row_error(
|
||||
&obj_source.data_descriptors().join(", ")
|
||||
),
|
||||
column_path_tried.span.since(path_members_span),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
PathMember {
|
||||
unspanned: UnspannedPathMember::Int(idx),
|
||||
|
@ -2,17 +2,13 @@ use crate::prelude::*;
|
||||
use crate::utils::suggestions::suggestions;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::ExternalRedirection;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use nu_value_ext::as_string;
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
grouper: Option<Value>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"group-by"
|
||||
@ -99,7 +95,7 @@ impl WholeStreamCommand for Command {
|
||||
Example {
|
||||
description:
|
||||
"use the block form to generate a grouping key when each row gets processed",
|
||||
example: "echo [1 3 1 3 2 1 1] | group-by { = ($it - 1) mod 3 }",
|
||||
example: "echo [1 3 1 3 2 1 1] | group-by { ($it - 1) mod 3 }",
|
||||
result: Some(vec![UntaggedValue::row(indexmap! {
|
||||
"0".to_string() => UntaggedValue::Table(vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
@ -130,9 +126,10 @@ enum Grouper {
|
||||
pub fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let context = Arc::new(EvaluationContext::from_args(&args));
|
||||
let (Arguments { grouper }, input) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let values: Vec<Value> = input.collect();
|
||||
let grouper = args.opt(0)?;
|
||||
let values: Vec<Value> = args.input.collect();
|
||||
let mut keys: Vec<Result<String, ShellError>> = vec![];
|
||||
let mut group_strategy = Grouper::ByColumn(None);
|
||||
|
||||
@ -148,7 +145,12 @@ pub fn group_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let run = block.clone();
|
||||
let context = context.clone();
|
||||
|
||||
match crate::commands::each::process_row(run, context, value.clone()) {
|
||||
match crate::commands::each::process_row(
|
||||
run,
|
||||
context,
|
||||
value.clone(),
|
||||
ExternalRedirection::Stdout,
|
||||
) {
|
||||
Ok(mut s) => {
|
||||
let collection: Vec<Value> = s.drain_vec();
|
||||
|
||||
|
@ -2,21 +2,11 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::ShellTypeName;
|
||||
use nu_protocol::{
|
||||
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::{Tag, Tagged};
|
||||
|
||||
use base64::{decode_config, encode_config};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
pub rest: Vec<ColumnPath>,
|
||||
pub character_set: Option<Tagged<String>>,
|
||||
pub encode: Tagged<bool>,
|
||||
pub decode: Tagged<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Base64Config {
|
||||
pub character_set: String,
|
||||
@ -46,13 +36,13 @@ impl WholeStreamCommand for SubCommand {
|
||||
Some('c'),
|
||||
)
|
||||
.switch(
|
||||
"encode",
|
||||
"encode the input as base64. This is the default behavior if not specified.",
|
||||
"encode",
|
||||
"encode the input as base64. This is the default behavior if not specified.",
|
||||
Some('e')
|
||||
)
|
||||
.switch(
|
||||
"decode",
|
||||
"decode the input from base64",
|
||||
"decode",
|
||||
"decode the input from base64",
|
||||
Some('d'))
|
||||
.rest(
|
||||
SyntaxShape::ColumnPath,
|
||||
@ -64,7 +54,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
"base64 encode or decode a value"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
operate(args)
|
||||
}
|
||||
|
||||
@ -95,29 +85,25 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_tag = args.call_info.name_tag.clone();
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let (
|
||||
Arguments {
|
||||
encode,
|
||||
decode,
|
||||
character_set,
|
||||
rest,
|
||||
},
|
||||
input,
|
||||
) = args.process()?;
|
||||
let encode = args.has_flag("encode");
|
||||
let decode = args.has_flag("decode");
|
||||
let character_set: Option<Tagged<String>> = args.get_flag("character_set")?;
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
if encode.item && decode.item {
|
||||
return Ok(ActionStream::one(Err(ShellError::labeled_error(
|
||||
if encode && decode {
|
||||
return Err(ShellError::labeled_error(
|
||||
"only one of --decode and --encode flags can be used",
|
||||
"conflicting flags",
|
||||
name_tag,
|
||||
))));
|
||||
));
|
||||
}
|
||||
|
||||
// Default the action to be encoding if no flags are specified.
|
||||
let action_type = if *decode.item() {
|
||||
let action_type = if decode {
|
||||
ActionType::Decode
|
||||
} else {
|
||||
ActionType::Encode
|
||||
@ -134,12 +120,11 @@ fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
action_type,
|
||||
};
|
||||
|
||||
let column_paths: Vec<_> = rest;
|
||||
|
||||
Ok(input
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
ReturnSuccess::value(action(&v, &encoding_config, v.tag())?)
|
||||
action(&v, &encoding_config, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
|
||||
@ -151,10 +136,10 @@ fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
)?;
|
||||
}
|
||||
|
||||
ReturnSuccess::value(ret)
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.to_action_stream())
|
||||
.to_input_stream())
|
||||
}
|
||||
|
||||
fn action(
|
||||
@ -182,7 +167,7 @@ fn action(
|
||||
return Err(ShellError::labeled_error(
|
||||
"value is not an accepted character set",
|
||||
format!(
|
||||
"{} is not a valid character-set.\nPlease use `help hash base64` to see a list of valid character sets.",
|
||||
"{} is not a valid character-set.\nPlease use `help hash base64` to see a list of valid character sets.",
|
||||
&base64_config.character_set
|
||||
),
|
||||
tag.into().span,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
@ -21,10 +21,10 @@ impl WholeStreamCommand for Command {
|
||||
"Apply hash function."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(&Command, &args.scope)).into_value(Tag::unknown()),
|
||||
)))
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,16 +2,9 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::ShellTypeName;
|
||||
use nu_protocol::{
|
||||
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
pub rest: Vec<ColumnPath>,
|
||||
}
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
@ -30,7 +23,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
"md5 encode a value"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
operate(args)
|
||||
}
|
||||
|
||||
@ -56,15 +49,15 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { rest }, input) = args.process()?;
|
||||
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
let column_paths: Vec<_> = rest;
|
||||
|
||||
Ok(input
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
ReturnSuccess::value(action(&v, v.tag())?)
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
|
||||
@ -75,10 +68,10 @@ fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
)?;
|
||||
}
|
||||
|
||||
ReturnSuccess::value(ret)
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.to_action_stream())
|
||||
.to_input_stream())
|
||||
}
|
||||
|
||||
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
|
@ -38,7 +38,7 @@ impl WholeStreamCommand for Help {
|
||||
|
||||
fn help(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let scope = args.scope.clone();
|
||||
let scope = args.scope().clone();
|
||||
let (HelpArgs { rest }, ..) = args.process()?;
|
||||
|
||||
if !rest.is_empty() {
|
||||
|
@ -76,10 +76,10 @@ pub fn histogram(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
};
|
||||
|
||||
let column_grouper = if !columns.is_empty() {
|
||||
match columns.remove(0).split_last() {
|
||||
Some((key, _)) => Some(key.as_string().tagged(&name)),
|
||||
None => None,
|
||||
}
|
||||
columns
|
||||
.remove(0)
|
||||
.split_last()
|
||||
.map(|(key, _)| key.as_string().tagged(&name))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -160,7 +160,7 @@ pub fn histogram(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
"{}%",
|
||||
// Some(2) < the number of digits
|
||||
// true < group the digits
|
||||
crate::commands::str_::from::action(&percentage, &name, Some(2), true)?
|
||||
crate::commands::into::string::action(&percentage, &name, Some(2), true)?
|
||||
.as_string()?
|
||||
);
|
||||
fact.insert_untagged("percentage", UntaggedValue::string(fmt_percentage));
|
||||
|
@ -59,6 +59,7 @@ impl WholeStreamCommand for If {
|
||||
}
|
||||
fn if_command(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = raw_args.call_info.name_tag.clone();
|
||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
|
||||
let args = raw_args.evaluate_once()?;
|
||||
@ -105,9 +106,9 @@ fn if_command(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(condition) => match condition.as_bool() {
|
||||
Ok(b) => {
|
||||
let result = if b {
|
||||
run_block(&then_case.block, &*context, input)
|
||||
run_block(&then_case.block, &*context, input, external_redirection)
|
||||
} else {
|
||||
run_block(&else_case.block, &*context, input)
|
||||
run_block(&else_case.block, &*context, input, external_redirection)
|
||||
};
|
||||
context.scope.exit_scope();
|
||||
|
||||
|
@ -2,6 +2,7 @@ use crate::prelude::*;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::ExternalRedirection;
|
||||
use nu_protocol::{
|
||||
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
@ -50,7 +51,7 @@ impl WholeStreamCommand for Command {
|
||||
.into()]),
|
||||
},Example {
|
||||
description: "Use in block form for more involved insertion logic",
|
||||
example: "echo [[author, lucky_number]; ['Yehuda', 4]] | insert success { = $it.lucky_number * 10 }",
|
||||
example: "echo [[author, lucky_number]; ['Yehuda', 4]] | insert success { $it.lucky_number * 10 }",
|
||||
result: Some(vec![UntaggedValue::row(indexmap! {
|
||||
"author".to_string() => Value::from("Yehuda"),
|
||||
"lucky_number".to_string() => UntaggedValue::int(4).into(),
|
||||
@ -79,9 +80,16 @@ fn process_row(
|
||||
|
||||
context.scope.enter_scope();
|
||||
context.scope.add_vars(&block.captured.entries);
|
||||
context.scope.add_var("$it", input.clone());
|
||||
if let Some((arg, _)) = block.block.params.positional.first() {
|
||||
context.scope.add_var(arg.name(), input.clone());
|
||||
}
|
||||
|
||||
let result = run_block(&block.block, &*context, input_stream);
|
||||
let result = run_block(
|
||||
&block.block,
|
||||
&*context,
|
||||
input_stream,
|
||||
ExternalRedirection::Stdout,
|
||||
);
|
||||
|
||||
context.scope.exit_scope();
|
||||
|
||||
|
270
crates/nu-command/src/commands/into/binary.rs
Normal file
270
crates/nu-command/src/commands/into/binary.rs
Normal file
@ -0,0 +1,270 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use num_bigint::{BigInt, ToBigInt};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into binary"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into binary")
|
||||
.rest(
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to binary (for table input)",
|
||||
)
|
||||
.named(
|
||||
"skip",
|
||||
SyntaxShape::Int,
|
||||
"skip x number of bytes",
|
||||
Some('s'),
|
||||
)
|
||||
.named(
|
||||
"bytes",
|
||||
SyntaxShape::Int,
|
||||
"show y number of bytes",
|
||||
Some('b'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to a binary primitive"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_binary(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "convert string to a nushell binary primitive",
|
||||
example:
|
||||
"echo 'This is a string that is exactly 52 characters long.' | into binary",
|
||||
result: Some(vec![UntaggedValue::binary(
|
||||
"This is a string that is exactly 52 characters long."
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
)
|
||||
.into()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert string to a nushell binary primitive",
|
||||
example:
|
||||
"echo 'This is a string that is exactly 52 characters long.' | into binary --skip 10",
|
||||
result: Some(vec![UntaggedValue::binary(
|
||||
"string that is exactly 52 characters long."
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
)
|
||||
.into()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert string to a nushell binary primitive",
|
||||
example:
|
||||
"echo 'This is a string that is exactly 52 characters long.' | into binary --skip 10 --bytes 10",
|
||||
result: Some(vec![UntaggedValue::binary(
|
||||
"string tha"
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
)
|
||||
.into()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert a number to a nushell binary primitive",
|
||||
example: "echo 1 | into binary",
|
||||
result: Some(vec![
|
||||
UntaggedValue::binary(i64::from(1).to_le_bytes().to_vec()).into()
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "convert a boolean to a nushell binary primitive",
|
||||
example: "echo $true | into binary",
|
||||
result: Some(vec![
|
||||
UntaggedValue::binary(i64::from(1).to_le_bytes().to_vec()).into()
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "convert a filesize to a nushell binary primitive",
|
||||
example: "ls | where name == LICENSE | get size | into binary",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "convert a filepath to a nushell binary primitive",
|
||||
example: "ls | where name == LICENSE | get name | path expand | into binary",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "convert a decimal to a nushell binary primitive",
|
||||
example: "echo 1.234 | into binary",
|
||||
result: Some(vec![
|
||||
UntaggedValue::binary(BigInt::from(1).to_bytes_le().1).into()
|
||||
]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_binary(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let skip: Option<Value> = args.get_flag("skip")?;
|
||||
let bytes: Option<Value> = args.get_flag("bytes")?;
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag(), &skip, &bytes)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
let skip_clone = skip.clone();
|
||||
let bytes_clone = bytes.clone();
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag(), &skip_clone, &bytes_clone)),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.to_input_stream())
|
||||
}
|
||||
|
||||
fn int_to_endian(n: i64) -> Vec<u8> {
|
||||
if cfg!(target_endian = "little") {
|
||||
// eprintln!("Little Endian");
|
||||
n.to_le_bytes().to_vec()
|
||||
} else {
|
||||
// eprintln!("Big Endian");
|
||||
n.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
fn bigint_to_endian(n: &BigInt) -> Vec<u8> {
|
||||
if cfg!(target_endian = "little") {
|
||||
// eprintln!("Little Endian");
|
||||
n.to_bytes_le().1
|
||||
} else {
|
||||
// eprintln!("Big Endian");
|
||||
n.to_bytes_be().1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action(
|
||||
input: &Value,
|
||||
tag: impl Into<Tag>,
|
||||
skip: &Option<Value>,
|
||||
bytes: &Option<Value>,
|
||||
) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
let skip_bytes = match skip {
|
||||
Some(s) => s.as_usize().unwrap_or(0),
|
||||
None => 0usize,
|
||||
};
|
||||
|
||||
let num_bytes = match bytes {
|
||||
Some(b) => b.as_usize().unwrap_or(0),
|
||||
None => 0usize,
|
||||
};
|
||||
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::binary(match prim {
|
||||
Primitive::Binary(b) => {
|
||||
if num_bytes == 0usize {
|
||||
b.to_vec().into_iter().skip(skip_bytes).collect()
|
||||
} else {
|
||||
b.to_vec()
|
||||
.into_iter()
|
||||
.skip(skip_bytes)
|
||||
.take(num_bytes)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
Primitive::Int(n_ref) => int_to_endian(*n_ref),
|
||||
Primitive::BigInt(n_ref) => bigint_to_endian(n_ref),
|
||||
Primitive::Decimal(dec) => match dec.to_bigint() {
|
||||
Some(n) => bigint_to_endian(&n),
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to int",
|
||||
));
|
||||
}
|
||||
},
|
||||
Primitive::Filesize(a_filesize) => match a_filesize.to_bigint() {
|
||||
Some(n) => bigint_to_endian(&n),
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert filesize to bigint",
|
||||
));
|
||||
}
|
||||
},
|
||||
Primitive::String(a_string) => {
|
||||
// a_string.as_bytes().to_vec()
|
||||
if num_bytes == 0usize {
|
||||
a_string
|
||||
.as_bytes()
|
||||
.to_vec()
|
||||
.into_iter()
|
||||
.skip(skip_bytes)
|
||||
.collect()
|
||||
} else {
|
||||
a_string
|
||||
.as_bytes()
|
||||
.to_vec()
|
||||
.into_iter()
|
||||
.skip(skip_bytes)
|
||||
.take(num_bytes)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
Primitive::Boolean(a_bool) => match a_bool {
|
||||
false => int_to_endian(0),
|
||||
true => int_to_endian(1),
|
||||
},
|
||||
Primitive::Date(a_date) => a_date.format("%c").to_string().as_bytes().to_vec(),
|
||||
Primitive::FilePath(a_filepath) => a_filepath
|
||||
.as_path()
|
||||
.display()
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"'into binary' for non-numeric primitives",
|
||||
))
|
||||
}
|
||||
})
|
||||
.into_value(&tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column name to use, with 'into binary COLUMN'",
|
||||
"found table",
|
||||
tag,
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented(
|
||||
"'into binary' for unsupported type",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
@ -18,10 +18,10 @@ impl WholeStreamCommand for Command {
|
||||
"Apply into function."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(get_full_help(&Command, &args.scope)).into_value(Tag::unknown()),
|
||||
)))
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(get_full_help(&Command, args.scope())).into_value(Tag::unknown()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,11 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use num_bigint::{BigInt, ToBigInt};
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use num_bigint::ToBigInt;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
pub rest: Vec<ColumnPath>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into int"
|
||||
@ -29,7 +22,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Convert value to integer"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_int(args)
|
||||
}
|
||||
|
||||
@ -85,13 +78,15 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
fn into_int(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { rest: column_paths }, input) = args.process()?;
|
||||
fn into_int(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(input
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
ReturnSuccess::value(action(&v, v.tag())?)
|
||||
action(&v, v.tag())
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
@ -101,10 +96,10 @@ fn into_int(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
)?;
|
||||
}
|
||||
|
||||
ReturnSuccess::value(ret)
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.to_action_stream())
|
||||
.to_input_stream())
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
@ -118,20 +113,34 @@ pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
}
|
||||
},
|
||||
Primitive::Decimal(dec) => match dec.to_bigint() {
|
||||
Some(n) => n,
|
||||
Some(n) => match n.to_i64() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to int",
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to int",
|
||||
));
|
||||
}
|
||||
},
|
||||
Primitive::Int(n_ref) => n_ref.to_owned(),
|
||||
Primitive::Int(n_ref) => *n_ref,
|
||||
Primitive::Boolean(a_bool) => match a_bool {
|
||||
false => BigInt::from(0),
|
||||
true => BigInt::from(1),
|
||||
false => 0,
|
||||
true => 1,
|
||||
},
|
||||
Primitive::Filesize(a_filesize) => match a_filesize.to_bigint() {
|
||||
Some(n) => n,
|
||||
Some(n) => match n.to_i64() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert filesize to bigint",
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert filesize to bigint",
|
||||
@ -154,13 +163,17 @@ pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
}
|
||||
}
|
||||
|
||||
fn int_from_string(a_string: &str, tag: &Tag) -> Result<BigInt, ShellError> {
|
||||
match a_string.parse::<BigInt>() {
|
||||
fn int_from_string(a_string: &str, tag: &Tag) -> Result<i64, ShellError> {
|
||||
match a_string.parse::<i64>() {
|
||||
Ok(n) => Ok(n),
|
||||
Err(_) => match a_string.parse::<f64>() {
|
||||
Ok(res_float) => match res_float.to_bigint() {
|
||||
Some(n) => Ok(n),
|
||||
None => Err(ShellError::unimplemented("failed to convert f64 to int")),
|
||||
Ok(f) => match f.to_i64() {
|
||||
Some(i) => Ok(i),
|
||||
None => Err(ShellError::labeled_error(
|
||||
"Could not convert string value to int",
|
||||
"original value",
|
||||
tag.clone(),
|
||||
)),
|
||||
},
|
||||
Err(_) => Err(ShellError::labeled_error(
|
||||
"Could not convert string value to int",
|
||||
|
@ -1,5 +1,9 @@
|
||||
mod binary;
|
||||
mod command;
|
||||
mod int;
|
||||
pub mod string;
|
||||
|
||||
pub use binary::SubCommand as IntoBinary;
|
||||
pub use command::Command as Into;
|
||||
pub use int::SubCommand as IntoInt;
|
||||
pub use string::SubCommand as IntoString;
|
||||
|
@ -1,9 +1,7 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use num_bigint::{BigInt, BigUint, ToBigInt};
|
||||
// TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml)
|
||||
@ -14,22 +12,16 @@ use std::iter;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
struct Arguments {
|
||||
decimals: Option<Tagged<u64>>,
|
||||
group_digits: bool,
|
||||
column_paths: Vec<ColumnPath>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str from"
|
||||
"into string"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("str from")
|
||||
Signature::build("into string")
|
||||
.rest(
|
||||
SyntaxShape::ColumnPath,
|
||||
"optionally convert to string by column paths",
|
||||
"column paths to convert to string (for table input)",
|
||||
)
|
||||
.named(
|
||||
"decimals",
|
||||
@ -40,66 +32,83 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Converts numeric types to strings. Trims trailing zeros unless decimals parameter is specified."
|
||||
"Convert value to string"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
operate(args)
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
into_string(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "round to nearest integer",
|
||||
example: "echo 1.7 | str from -d 0",
|
||||
description: "convert decimal to string and round to nearest integer",
|
||||
example: "echo 1.7 | into string -d 0",
|
||||
result: Some(vec![UntaggedValue::string("2").into_untagged_value()]),
|
||||
},
|
||||
/*
|
||||
FIXME: this isn't currently supported because of num_format being out of date. Once it's updated, re-enable this
|
||||
Example {
|
||||
description: "format large number with localized digit grouping",
|
||||
example: "= 1000000.2 | str from -g",
|
||||
result: Some(vec![
|
||||
UntaggedValue::string("1,000,000.2").into_untagged_value()
|
||||
]),
|
||||
description: "convert decimal to string",
|
||||
example: "echo 4.3 | into string",
|
||||
result: Some(vec![UntaggedValue::string("4.3").into_untagged_value()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert string to string",
|
||||
example: "echo '1234' | into string",
|
||||
result: Some(vec![UntaggedValue::string("1234").into_untagged_value()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert boolean to string",
|
||||
example: "echo $true | into string",
|
||||
result: Some(vec![UntaggedValue::string("true").into_untagged_value()]),
|
||||
},
|
||||
Example {
|
||||
description: "convert date to string",
|
||||
example: "date now | into string",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "convert filepath to string",
|
||||
example: "ls Cargo.toml | get name | into string",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "convert filesize to string",
|
||||
example: "ls Cargo.toml | get size | into string",
|
||||
result: None,
|
||||
},
|
||||
*/
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (options, input) = args.extract(|params| {
|
||||
Ok(Arguments {
|
||||
decimals: params.get_flag("decimals")?,
|
||||
group_digits: false,
|
||||
column_paths: params.rest_args()?,
|
||||
})
|
||||
})?;
|
||||
fn into_string(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let digits = options.decimals.as_ref().map(|tagged| tagged.item);
|
||||
let group_digits = options.group_digits;
|
||||
let decimals: Option<Tagged<u64>> = args.get_flag("decimals")?;
|
||||
let column_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
Ok(input
|
||||
let digits = decimals.as_ref().map(|tagged| tagged.item);
|
||||
let group_digits = false;
|
||||
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |v| {
|
||||
if options.column_paths.is_empty() {
|
||||
ReturnSuccess::value(action(&v, v.tag(), digits, group_digits)?)
|
||||
if column_paths.is_empty() {
|
||||
action(&v, v.tag(), digits, group_digits)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &options.column_paths {
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag(), digits, group_digits)),
|
||||
)?;
|
||||
}
|
||||
|
||||
ReturnSuccess::value(ret)
|
||||
Ok(ret)
|
||||
}
|
||||
})
|
||||
.to_action_stream())
|
||||
.to_input_stream())
|
||||
}
|
||||
|
||||
// TODO If you're using the with-system-locale feature and you're on Windows, Clang 3.9 or higher is also required.
|
||||
pub fn action(
|
||||
input: &Value,
|
||||
tag: impl Into<Tag>,
|
||||
@ -109,6 +118,13 @@ pub fn action(
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::string(match prim {
|
||||
Primitive::Int(int) => {
|
||||
if group_digits {
|
||||
format_int(*int) // int.to_formatted_string(*locale)
|
||||
} else {
|
||||
int.to_string()
|
||||
}
|
||||
}
|
||||
Primitive::BigInt(int) => {
|
||||
if group_digits {
|
||||
format_bigint(int) // int.to_formatted_string(*locale)
|
||||
} else {
|
||||
@ -121,27 +137,45 @@ pub fn action(
|
||||
Primitive::Date(a_date) => a_date.format("%c").to_string(),
|
||||
Primitive::FilePath(a_filepath) => a_filepath.as_path().display().to_string(),
|
||||
Primitive::Filesize(a_filesize) => {
|
||||
let byte_string = InlineShape::format_bytes(a_filesize, None);
|
||||
let byte_string = InlineShape::format_bytes(*a_filesize, None);
|
||||
byte_string.1
|
||||
}
|
||||
Primitive::Nothing => "nothing".to_string(),
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"str from for non-numeric primitives",
|
||||
))
|
||||
return Err(ShellError::unimplemented(&format!(
|
||||
"into string for primitive: {:?}",
|
||||
prim
|
||||
)))
|
||||
}
|
||||
})
|
||||
.into_value(tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column to use 'str from'",
|
||||
"specify column to use 'into string'",
|
||||
"found table",
|
||||
input.tag.clone(),
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented(
|
||||
"str from for non-primitive, non-table types",
|
||||
)),
|
||||
UntaggedValue::Table(_) => Err(ShellError::unimplemented("into string for table")),
|
||||
_ => Err(ShellError::unimplemented("into string for non-primitive")),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_int(int: i64) -> String {
|
||||
format!("{}", int)
|
||||
|
||||
// TODO once platform-specific dependencies are stable (see Cargo.toml)
|
||||
// #[cfg(windows)]
|
||||
// {
|
||||
// int.to_formatted_string(&Locale::en)
|
||||
// }
|
||||
// #[cfg(not(windows))]
|
||||
// {
|
||||
// match SystemLocale::default() {
|
||||
// Ok(locale) => int.to_formatted_string(&locale),
|
||||
// Err(_) => int.to_formatted_string(&Locale::en),
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn format_bigint(int: &BigInt) -> String {
|
||||
format!("{}", int)
|
||||
|
@ -6,11 +6,6 @@ use nu_source::Tagged;
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
rows: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"keep"
|
||||
@ -28,7 +23,7 @@ impl WholeStreamCommand for Command {
|
||||
"Keep the number of rows only."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
keep(args)
|
||||
}
|
||||
|
||||
@ -53,15 +48,17 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
fn keep(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { rows }, input) = args.process()?;
|
||||
fn keep(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let rows: Option<Tagged<usize>> = args.opt(0)?;
|
||||
|
||||
let rows_desired = if let Some(quantity) = rows {
|
||||
*quantity
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
Ok(input.take(rows_desired).to_action_stream())
|
||||
Ok(args.input.take(rows_desired).to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,10 +1,12 @@
|
||||
use crate::prelude::*;
|
||||
use log::trace;
|
||||
use nu_engine::evaluate_baseline_expr;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::ParserScope;
|
||||
use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{
|
||||
hir::{CapturedBlock, ClassifiedCommand},
|
||||
Signature, SyntaxShape,
|
||||
};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
@ -27,54 +29,40 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Keeps rows until the condition matches."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let ctx = Arc::new(EvaluationContext::from_args(&args));
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let call_info = args.evaluate_once()?;
|
||||
|
||||
let block = call_info.args.expect_nth(0)?.clone();
|
||||
|
||||
let (condition, captured) = match block {
|
||||
Value {
|
||||
value: UntaggedValue::Block(captured_block),
|
||||
tag,
|
||||
} => {
|
||||
if captured_block.block.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
match captured_block.block.block[0].pipelines.get(0) {
|
||||
Some(item) => match item.list.get(0) {
|
||||
Some(ClassifiedCommand::Expr(expr)) => {
|
||||
(Arc::new(expr.clone()), captured_block.captured.clone())
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let block: CapturedBlock = call_info.req(0)?;
|
||||
let condition = {
|
||||
if block.block.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
match block.block.block[0].pipelines.get(0) {
|
||||
Some(item) => match item.list.get(0) {
|
||||
Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
Value { tag, .. } => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(call_info
|
||||
@ -83,17 +71,17 @@ impl WholeStreamCommand for SubCommand {
|
||||
let condition = condition.clone();
|
||||
let ctx = ctx.clone();
|
||||
ctx.scope.enter_scope();
|
||||
ctx.scope.add_vars(&captured.entries);
|
||||
ctx.scope.add_var("$it", item.clone());
|
||||
trace!("ITEM = {:?}", item);
|
||||
ctx.scope.add_vars(&block.captured.entries);
|
||||
if let Some((arg, _)) = block.block.params.positional.first() {
|
||||
ctx.scope.add_var(arg.name(), item.clone());
|
||||
}
|
||||
|
||||
let result = evaluate_baseline_expr(&*condition, &*ctx);
|
||||
ctx.scope.exit_scope();
|
||||
trace!("RESULT = {:?}", result);
|
||||
|
||||
!matches!(result, Ok(ref v) if v.is_true())
|
||||
})
|
||||
.to_action_stream())
|
||||
.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,10 @@ use log::trace;
|
||||
use nu_engine::evaluate_baseline_expr;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{
|
||||
hir::{CapturedBlock, ClassifiedCommand},
|
||||
Signature, SyntaxShape,
|
||||
};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
@ -26,53 +29,39 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Keeps rows while the condition matches."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let ctx = Arc::new(EvaluationContext::from_args(&args));
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let call_info = args.evaluate_once()?;
|
||||
|
||||
let block = call_info.args.expect_nth(0)?.clone();
|
||||
|
||||
let (condition, captured) = match block {
|
||||
Value {
|
||||
value: UntaggedValue::Block(captured_block),
|
||||
tag,
|
||||
} => {
|
||||
if captured_block.block.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
match captured_block.block.block[0].pipelines.get(0) {
|
||||
Some(item) => match item.list.get(0) {
|
||||
Some(ClassifiedCommand::Expr(expr)) => {
|
||||
(Arc::new(expr.clone()), captured_block.captured.clone())
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let block: CapturedBlock = call_info.req(0)?;
|
||||
let condition = {
|
||||
if block.block.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
match block.block.block[0].pipelines.get(0) {
|
||||
Some(item) => match item.list.get(0) {
|
||||
Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
Value { tag, .. } => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"expected a condition",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(call_info
|
||||
@ -82,8 +71,10 @@ impl WholeStreamCommand for SubCommand {
|
||||
let ctx = ctx.clone();
|
||||
|
||||
ctx.scope.enter_scope();
|
||||
ctx.scope.add_var("$it", item.clone());
|
||||
ctx.scope.add_vars(&captured.entries);
|
||||
ctx.scope.add_vars(&block.captured.entries);
|
||||
if let Some((arg, _)) = block.block.params.positional.first() {
|
||||
ctx.scope.add_var(arg.name(), item.clone());
|
||||
}
|
||||
trace!("ITEM = {:?}", item);
|
||||
|
||||
let result = evaluate_baseline_expr(&*condition, &*ctx);
|
||||
@ -92,7 +83,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
|
||||
matches!(result, Ok(ref v) if v.is_true())
|
||||
})
|
||||
.to_action_stream())
|
||||
.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||
|
||||
pub struct Last;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LastArgs {
|
||||
rows: Option<Tagged<u64>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Last {
|
||||
fn name(&self) -> &str {
|
||||
"last"
|
||||
@ -28,7 +22,7 @@ impl WholeStreamCommand for Last {
|
||||
"Show only the last number of rows."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
last(args)
|
||||
}
|
||||
|
||||
@ -37,7 +31,7 @@ impl WholeStreamCommand for Last {
|
||||
Example {
|
||||
description: "Get the last row",
|
||||
example: "echo [1 2 3] | last",
|
||||
result: Some(vec![Value::from(UntaggedValue::from(BigInt::from(3)))]),
|
||||
result: Some(vec![UntaggedValue::int(3).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Get the last three rows",
|
||||
@ -52,12 +46,13 @@ impl WholeStreamCommand for Last {
|
||||
}
|
||||
}
|
||||
|
||||
fn last(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (LastArgs { rows }, input) = args.process()?;
|
||||
let v: Vec<_> = input.into_vec();
|
||||
fn last(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let rows = args.nth(0).cloned();
|
||||
let v: Vec<_> = args.input.into_vec();
|
||||
|
||||
let end_rows_desired = if let Some(quantity) = rows {
|
||||
*quantity as usize
|
||||
quantity.as_usize()?
|
||||
} else {
|
||||
1
|
||||
};
|
||||
@ -70,7 +65,7 @@ fn last(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
|
||||
let iter = v.into_iter().skip(beginning_rows_to_skip);
|
||||
|
||||
Ok((iter).to_action_stream())
|
||||
Ok(OutputStream::from_stream(iter))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -94,7 +94,7 @@ impl Iterator for CountIterator {
|
||||
input.count()
|
||||
};
|
||||
|
||||
Some(UntaggedValue::int(length).into_value(self.tag.clone()))
|
||||
Some(UntaggedValue::int(length as i64).into_value(self.tag.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::{evaluate_baseline_expr, WholeStreamCommand};
|
||||
use nu_engine::{evaluate_baseline_expr, FromValue, WholeStreamCommand};
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::CapturedBlock, hir::ClassifiedCommand, Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
use nu_protocol::{
|
||||
hir::{CapturedBlock, ClassifiedCommand},
|
||||
Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
pub struct Let;
|
||||
|
||||
@ -32,20 +34,41 @@ impl WholeStreamCommand for Let {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![]
|
||||
vec![
|
||||
Example {
|
||||
description: "Assign a simple value to a variable",
|
||||
example: "let x = 3",
|
||||
result: Some(vec![]),
|
||||
},
|
||||
Example {
|
||||
description: "Assign the result of an expression to a variable",
|
||||
example: "let result = (3 + 7); echo $result",
|
||||
result: Some(vec![UntaggedValue::int(1).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Create a variable using the full name",
|
||||
example: "let $three = 3",
|
||||
result: Some(vec![]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn letcmd(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let args = args.evaluate_once()?;
|
||||
let positional = args
|
||||
.call_info
|
||||
.args
|
||||
.positional
|
||||
.expect("Internal error: type checker should require args");
|
||||
|
||||
//let (LetArgs { name, rhs, .. }, _) = args.process()?;
|
||||
let name: Tagged<String> = args.req(0)?;
|
||||
let rhs: CapturedBlock = args.req(2)?;
|
||||
let var_name = positional[0].var_name()?;
|
||||
let rhs_raw = evaluate_baseline_expr(&positional[2], &ctx)?;
|
||||
let tag: Tag = positional[2].span.into();
|
||||
|
||||
let (expr, captured) = {
|
||||
let rhs: CapturedBlock = FromValue::from_value(&rhs_raw)?;
|
||||
|
||||
let (expr, _) = {
|
||||
if rhs.block.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a value",
|
||||
@ -75,24 +98,15 @@ pub fn letcmd(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
};
|
||||
|
||||
ctx.scope.enter_scope();
|
||||
ctx.scope.add_vars(&captured.entries);
|
||||
|
||||
let value = evaluate_baseline_expr(&expr, &ctx);
|
||||
|
||||
ctx.scope.exit_scope();
|
||||
|
||||
let value = value?;
|
||||
|
||||
let name = if name.item.starts_with('$') {
|
||||
name.item
|
||||
} else {
|
||||
format!("${}", name.item)
|
||||
};
|
||||
|
||||
// Note: this is a special case for setting the context from a command
|
||||
// In this case, if we don't set it now, we'll lose the scope that this
|
||||
// variable should be set into.
|
||||
ctx.scope.add_var(name, value);
|
||||
ctx.scope.add_var(var_name, value);
|
||||
|
||||
Ok(ActionStream::empty())
|
||||
}
|
||||
|
95
crates/nu-command/src/commands/load_env.rs
Normal file
95
crates/nu-command/src/commands/load_env.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||
|
||||
pub struct LoadEnv;
|
||||
|
||||
impl WholeStreamCommand for LoadEnv {
|
||||
fn name(&self) -> &str {
|
||||
"load-env"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("load-env").optional(
|
||||
"environ",
|
||||
SyntaxShape::Any,
|
||||
"Optional environment table to load in. If not provided, will use the table provided on the input stream",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Set environment variables using a table stream"
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
load_env(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Load variables from an input stream",
|
||||
example: r#"echo [[name, value]; ["NAME", "JT"] ["AGE", "UNKNOWN"]] | load-env; echo $nu.env.NAME"#,
|
||||
result: Some(vec![Value::from("JT")]),
|
||||
},
|
||||
Example {
|
||||
description: "Load variables from an argument",
|
||||
example: r#"load-env [[name, value]; ["NAME", "JT"] ["AGE", "UNKNOWN"]]; echo $nu.env.NAME"#,
|
||||
result: Some(vec![Value::from("JT")]),
|
||||
},
|
||||
Example {
|
||||
description: "Load variables from an argument and an input stream",
|
||||
example: r#"echo [[name, value]; ["NAME", "JT"]] | load-env [[name, value]; ["VALUE", "FOO"]]; echo $nu.env.NAME $nu.env.VALUE"#,
|
||||
result: Some(vec![Value::from("JT"), Value::from("UNKNOWN")]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn load_env_from_table(
|
||||
values: impl IntoIterator<Item = Value>,
|
||||
ctx: &EvaluationContext,
|
||||
) -> Result<(), ShellError> {
|
||||
for value in values {
|
||||
let mut var_name = None;
|
||||
let mut var_value = None;
|
||||
|
||||
let tag = value.tag();
|
||||
|
||||
for (key, value) in value.row_entries() {
|
||||
if key == "name" {
|
||||
var_name = Some(value.as_string()?);
|
||||
} else if key == "value" {
|
||||
var_value = Some(value.as_string()?);
|
||||
}
|
||||
}
|
||||
|
||||
match (var_name, var_value) {
|
||||
(Some(name), Some(value)) => ctx.scope.add_env_var(name, value),
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
r#"Expected each row in the table to have a "name" and "value" field."#,
|
||||
r#"expected a "name" and "value" field"#,
|
||||
tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_env(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
if let Some(values) = args.opt::<Vec<Value>>(0)? {
|
||||
load_env_from_table(values, &ctx)?;
|
||||
}
|
||||
|
||||
load_env_from_table(args.input, &ctx)?;
|
||||
|
||||
Ok(ActionStream::empty())
|
||||
}
|
@ -41,8 +41,8 @@ impl WholeStreamCommand for Ls {
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctrl_c = args.ctrl_c.clone();
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
let ctrl_c = args.ctrl_c();
|
||||
let shell_manager = args.shell_manager();
|
||||
let (args, _) = args.process()?;
|
||||
shell_manager.ls(args, name, ctrl_c)
|
||||
}
|
||||
|
@ -1,352 +1,351 @@
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! command {
|
||||
(
|
||||
Named { $export:tt $args:ident $body:block }
|
||||
Positional { $($number:tt)* }
|
||||
Rest {}
|
||||
Signature {
|
||||
name: $config_name:tt,
|
||||
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
|
||||
optional_positional: vec![ $($optional_positional:tt)* ],
|
||||
rest_positional: $rest_positional:tt,
|
||||
named: {
|
||||
$(
|
||||
($named_param:tt : $named_type:ty : $named_kind:tt)
|
||||
)*
|
||||
}
|
||||
}
|
||||
// #[doc(hidden)]
|
||||
// #[macro_export]
|
||||
// macro_rules! command {
|
||||
// (
|
||||
// Named { $export:tt $args:ident $body:block }
|
||||
// Positional { $($number:tt)* }
|
||||
// Rest {}
|
||||
// Signature {
|
||||
// name: $config_name:tt,
|
||||
// mandatory_positional: vec![ $($mandatory_positional:tt)* ],
|
||||
// optional_positional: vec![ $($optional_positional:tt)* ],
|
||||
// rest_positional: $rest_positional:tt,
|
||||
// named: {
|
||||
// $(
|
||||
// ($named_param:tt : $named_type:ty : $named_kind:tt)
|
||||
// )*
|
||||
// }
|
||||
// }
|
||||
|
||||
Function {
|
||||
$( ( $param_name:tt : $param_type:tt ) )*
|
||||
}
|
||||
// Function {
|
||||
// $( ( $param_name:tt : $param_type:tt ) )*
|
||||
// }
|
||||
|
||||
Extract {
|
||||
$($extract:tt)*
|
||||
}
|
||||
) => {
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct $export;
|
||||
// Extract {
|
||||
// $($extract:tt)*
|
||||
// }
|
||||
// ) => {
|
||||
// #[allow(non_camel_case_types)]
|
||||
// pub struct $export;
|
||||
|
||||
impl Command for $export {
|
||||
fn run(&self, $args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn command($args: EvaluatedCommandArgs, ( $($param_name),*, ): ( $($param_type),*, )) -> Result<OutputStream, ShellError> {
|
||||
let output = $body;
|
||||
// impl Command for $export {
|
||||
// fn run(&self, $args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
// fn command($args: EvaluatedCommandArgs, ( $($param_name),*, ): ( $($param_type),*, )) -> Result<OutputStream, ShellError> {
|
||||
// let output = $body;
|
||||
|
||||
Ok(output.to_action_stream())
|
||||
}
|
||||
// Ok(output.to_action_stream())
|
||||
// }
|
||||
|
||||
let $args = $args.evaluate_once(registry)?;
|
||||
let tuple = ( $($extract ,)* );
|
||||
command( $args, tuple )
|
||||
}
|
||||
// let $args = $args.evaluate_once(registry)?;
|
||||
// let tuple = ( $($extract ,)* );
|
||||
// command( $args, tuple )
|
||||
// }
|
||||
|
||||
fn name(&self) -> &str {
|
||||
stringify!($config_name)
|
||||
}
|
||||
// fn name(&self) -> &str {
|
||||
// stringify!($config_name)
|
||||
// }
|
||||
|
||||
fn config(&self) -> $nu_parser::registry::Signature {
|
||||
$nu_parser::registry::Signature {
|
||||
name: self.name().to_string(),
|
||||
positional: vec![$($mandatory_positional)*],
|
||||
rest_positional: false,
|
||||
is_filter: false,
|
||||
is_sink: false,
|
||||
// fn config(&self) -> $nu_parser::registry::Signature {
|
||||
// $nu_parser::registry::Signature {
|
||||
// name: self.name().to_string(),
|
||||
// positional: vec![$($mandatory_positional)*],
|
||||
// rest_positional: false,
|
||||
// is_filter: false,
|
||||
// is_sink: false,
|
||||
|
||||
named: {
|
||||
use $nu_parser::registry::NamedType;
|
||||
// named: {
|
||||
// use $nu_parser::registry::NamedType;
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut named: indexmap::IndexMap<String, NamedType> = indexmap::IndexMap::new();
|
||||
// #[allow(unused_mut)]
|
||||
// let mut named: indexmap::IndexMap<String, NamedType> = indexmap::IndexMap::new();
|
||||
|
||||
$(
|
||||
named.insert(stringify!($named_param).to_string(), $nu_parser::registry::NamedType::$named_kind);
|
||||
)*
|
||||
// $(
|
||||
// named.insert(stringify!($named_param).to_string(), $nu_parser::registry::NamedType::$named_kind);
|
||||
// )*
|
||||
|
||||
named
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// named
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
// switch
|
||||
(
|
||||
Named { $export:tt $args:ident $body:block }
|
||||
Positional { $($positional_count:tt)* }
|
||||
Rest { -- $param_name:ident : Switch , $($rest:tt)* }
|
||||
Signature {
|
||||
name: $config_name:tt,
|
||||
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
|
||||
optional_positional: vec![ $($optional_positional:tt)* ],
|
||||
rest_positional: $rest_positional:tt,
|
||||
named: {
|
||||
$($config_named:tt)*
|
||||
}
|
||||
}
|
||||
Function {
|
||||
$($function:tt)*
|
||||
}
|
||||
Extract {
|
||||
$($extract:tt)*
|
||||
}
|
||||
) => {
|
||||
command!(
|
||||
Named { $export $args $body }
|
||||
Positional { $($positional_count)* + 1 }
|
||||
Rest { $($rest)* }
|
||||
Signature {
|
||||
name: $config_name,
|
||||
mandatory_positional: vec![ $($mandatory_positional)* ],
|
||||
optional_positional: vec![ $($optional_positional)* ],
|
||||
rest_positional: $rest_positional,
|
||||
named: {
|
||||
$($config_named)*
|
||||
($param_name : Switch : Switch)
|
||||
}
|
||||
}
|
||||
// // switch
|
||||
// (
|
||||
// Named { $export:tt $args:ident $body:block }
|
||||
// Positional { $($positional_count:tt)* }
|
||||
// Rest { -- $param_name:ident : Switch , $($rest:tt)* }
|
||||
// Signature {
|
||||
// name: $config_name:tt,
|
||||
// mandatory_positional: vec![ $($mandatory_positional:tt)* ],
|
||||
// optional_positional: vec![ $($optional_positional:tt)* ],
|
||||
// rest_positional: $rest_positional:tt,
|
||||
// named: {
|
||||
// $($config_named:tt)*
|
||||
// }
|
||||
// }
|
||||
// Function {
|
||||
// $($function:tt)*
|
||||
// }
|
||||
// Extract {
|
||||
// $($extract:tt)*
|
||||
// }
|
||||
// ) => {
|
||||
// command!(
|
||||
// Named { $export $args $body }
|
||||
// Positional { $($positional_count)* + 1 }
|
||||
// Rest { $($rest)* }
|
||||
// Signature {
|
||||
// name: $config_name,
|
||||
// mandatory_positional: vec![ $($mandatory_positional)* ],
|
||||
// optional_positional: vec![ $($optional_positional)* ],
|
||||
// rest_positional: $rest_positional,
|
||||
// named: {
|
||||
// $($config_named)*
|
||||
// ($param_name : Switch : Switch)
|
||||
// }
|
||||
// }
|
||||
|
||||
Function {
|
||||
$($function)* ($param_name : Switch)
|
||||
}
|
||||
// Function {
|
||||
// $($function)* ($param_name : Switch)
|
||||
// }
|
||||
|
||||
Extract {
|
||||
$($extract)* {
|
||||
use std::convert::TryInto;
|
||||
// Extract {
|
||||
// $($extract)* {
|
||||
// use std::convert::TryInto;
|
||||
|
||||
$args.get(stringify!($param_name)).try_into()?
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
// $args.get(stringify!($param_name)).try_into()?
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// };
|
||||
|
||||
// mandatory named arguments
|
||||
(
|
||||
Named { $export:tt $args:ident $body:block }
|
||||
Positional { $($positional_count:tt)* }
|
||||
Rest { -- $param_name:ident : $param_kind:ty , $($rest:tt)* }
|
||||
Signature {
|
||||
name: $config_name:tt,
|
||||
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
|
||||
optional_positional: vec![ $($optional_positional:tt)* ],
|
||||
rest_positional: $rest_positional:tt,
|
||||
named: {
|
||||
$($config_named:tt)*
|
||||
}
|
||||
}
|
||||
Function {
|
||||
$($function:tt)*
|
||||
}
|
||||
Extract {
|
||||
$($extract:tt)*
|
||||
}
|
||||
) => {
|
||||
command!(
|
||||
Named { $export $args $body }
|
||||
Positional { $($positional_count)* + 1 }
|
||||
Rest { $($rest)* }
|
||||
Signature {
|
||||
name: $config_name,
|
||||
mandatory_positional: vec![ $($mandatory_positional)* ],
|
||||
optional_positional: vec![ $($optional_positional)* ],
|
||||
rest_positional: $rest_positional,
|
||||
named: {
|
||||
$($config_named)*
|
||||
($param_name : Mandatory(NamedValue::Single))
|
||||
}
|
||||
}
|
||||
// // mandatory named arguments
|
||||
// (
|
||||
// Named { $export:tt $args:ident $body:block }
|
||||
// Positional { $($positional_count:tt)* }
|
||||
// Rest { -- $param_name:ident : $param_kind:ty , $($rest:tt)* }
|
||||
// Signature {
|
||||
// name: $config_name:tt,
|
||||
// mandatory_positional: vec![ $($mandatory_positional:tt)* ],
|
||||
// optional_positional: vec![ $($optional_positional:tt)* ],
|
||||
// rest_positional: $rest_positional:tt,
|
||||
// named: {
|
||||
// $($config_named:tt)*
|
||||
// }
|
||||
// }
|
||||
// Function {
|
||||
// $($function:tt)*
|
||||
// }
|
||||
// Extract {
|
||||
// $($extract:tt)*
|
||||
// }
|
||||
// ) => {
|
||||
// command!(
|
||||
// Named { $export $args $body }
|
||||
// Positional { $($positional_count)* + 1 }
|
||||
// Rest { $($rest)* }
|
||||
// Signature {
|
||||
// name: $config_name,
|
||||
// mandatory_positional: vec![ $($mandatory_positional)* ],
|
||||
// optional_positional: vec![ $($optional_positional)* ],
|
||||
// rest_positional: $rest_positional,
|
||||
// named: {
|
||||
// $($config_named)*
|
||||
// ($param_name : Mandatory(NamedValue::Single))
|
||||
// }
|
||||
// }
|
||||
|
||||
Function {
|
||||
$($function)* ($param_name : $param_kind)
|
||||
}
|
||||
// Function {
|
||||
// $($function)* ($param_name : $param_kind)
|
||||
// }
|
||||
|
||||
Extract {
|
||||
$($extract)* {
|
||||
use std::convert::TryInto;
|
||||
// Extract {
|
||||
// $($extract)* {
|
||||
// use std::convert::TryInto;
|
||||
|
||||
$args.get(stringify!($param_name)).try_into()?
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
// $args.get(stringify!($param_name)).try_into()?
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// };
|
||||
|
||||
// optional named arguments
|
||||
(
|
||||
Named { $export:tt $args:ident $body:block }
|
||||
Positional { $($positional_count:tt)* }
|
||||
Rest { -- $param_name:ident ? : $param_kind:ty , $($rest:tt)* }
|
||||
Signature {
|
||||
name: $config_name:tt,
|
||||
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
|
||||
optional_positional: vec![ $($optional_positional:tt)* ],
|
||||
rest_positional: $rest_positional:tt,
|
||||
named: {
|
||||
$($config_named:tt)*
|
||||
}
|
||||
}
|
||||
Function {
|
||||
$($function:tt)*
|
||||
}
|
||||
Extract {
|
||||
$($extract:tt)*
|
||||
}
|
||||
) => {
|
||||
command!(
|
||||
Named { $export $args $body }
|
||||
Positional { $($positional_count)* + 1 }
|
||||
Rest { $($rest)* }
|
||||
Signature {
|
||||
name: $config_name,
|
||||
mandatory_positional: vec![ $($mandatory_positional)* ],
|
||||
optional_positional: vec![ $($optional_positional)* ],
|
||||
rest_positional: $rest_positional,
|
||||
named: {
|
||||
$($config_named)*
|
||||
($param_name : Optional(NamedValue::Single))
|
||||
}
|
||||
}
|
||||
// // optional named arguments
|
||||
// (
|
||||
// Named { $export:tt $args:ident $body:block }
|
||||
// Positional { $($positional_count:tt)* }
|
||||
// Rest { -- $param_name:ident ? : $param_kind:ty , $($rest:tt)* }
|
||||
// Signature {
|
||||
// name: $config_name:tt,
|
||||
// mandatory_positional: vec![ $($mandatory_positional:tt)* ],
|
||||
// optional_positional: vec![ $($optional_positional:tt)* ],
|
||||
// rest_positional: $rest_positional:tt,
|
||||
// named: {
|
||||
// $($config_named:tt)*
|
||||
// }
|
||||
// }
|
||||
// Function {
|
||||
// $($function:tt)*
|
||||
// }
|
||||
// Extract {
|
||||
// $($extract:tt)*
|
||||
// }
|
||||
// ) => {
|
||||
// command!(
|
||||
// Named { $export $args $body }
|
||||
// Positional { $($positional_count)* + 1 }
|
||||
// Rest { $($rest)* }
|
||||
// Signature {
|
||||
// name: $config_name,
|
||||
// mandatory_positional: vec![ $($mandatory_positional)* ],
|
||||
// optional_positional: vec![ $($optional_positional)* ],
|
||||
// rest_positional: $rest_positional,
|
||||
// named: {
|
||||
// $($config_named)*
|
||||
// ($param_name : Optional(NamedValue::Single))
|
||||
// }
|
||||
// }
|
||||
|
||||
Function {
|
||||
$($function)* ($param_name : $param_kind)
|
||||
}
|
||||
// Function {
|
||||
// $($function)* ($param_name : $param_kind)
|
||||
// }
|
||||
|
||||
Extract {
|
||||
$($extract)* {
|
||||
use std::convert::TryInto;
|
||||
// Extract {
|
||||
// $($extract)* {
|
||||
// use std::convert::TryInto;
|
||||
|
||||
$args.get(stringify!($param_name)).try_into()?
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
// $args.get(stringify!($param_name)).try_into()?
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// };
|
||||
|
||||
// mandatory positional block
|
||||
(
|
||||
Named { $export:ident $args:ident $body:block }
|
||||
Positional { $($positional_count:tt)* }
|
||||
Rest { $param_name:ident : Block , $($rest:tt)* }
|
||||
Signature {
|
||||
name: $config_name:tt,
|
||||
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
|
||||
optional_positional: vec![ $($optional_positional:tt)* ],
|
||||
rest_positional: $rest_positional:tt,
|
||||
named: {
|
||||
$($config_named:tt)*
|
||||
}
|
||||
}
|
||||
// // mandatory positional block
|
||||
// (
|
||||
// Named { $export:ident $args:ident $body:block }
|
||||
// Positional { $($positional_count:tt)* }
|
||||
// Rest { $param_name:ident : Block , $($rest:tt)* }
|
||||
// Signature {
|
||||
// name: $config_name:tt,
|
||||
// mandatory_positional: vec![ $($mandatory_positional:tt)* ],
|
||||
// optional_positional: vec![ $($optional_positional:tt)* ],
|
||||
// rest_positional: $rest_positional:tt,
|
||||
// named: {
|
||||
// $($config_named:tt)*
|
||||
// }
|
||||
// }
|
||||
|
||||
Function {
|
||||
$($function:tt)*
|
||||
}
|
||||
// Function {
|
||||
// $($function:tt)*
|
||||
// }
|
||||
|
||||
Extract {
|
||||
$($extract:tt)*
|
||||
}
|
||||
// Extract {
|
||||
// $($extract:tt)*
|
||||
// }
|
||||
|
||||
) => {
|
||||
command!(
|
||||
Named { $export $args $body }
|
||||
Positional { $($positional_count)* + 1 }
|
||||
Rest { $($rest)* }
|
||||
Signature {
|
||||
name: $config_name,
|
||||
mandatory_positional: vec![ $($mandatory_positional)* $nu_parser::registry::PositionalType::mandatory_block(
|
||||
stringify!($param_name)
|
||||
), ],
|
||||
optional_positional: vec![ $($optional_positional)* ],
|
||||
rest_positional: $rest_positional,
|
||||
named: {
|
||||
$($config_named)*
|
||||
}
|
||||
}
|
||||
// ) => {
|
||||
// command!(
|
||||
// Named { $export $args $body }
|
||||
// Positional { $($positional_count)* + 1 }
|
||||
// Rest { $($rest)* }
|
||||
// Signature {
|
||||
// name: $config_name,
|
||||
// mandatory_positional: vec![ $($mandatory_positional)* $nu_parser::registry::PositionalType::mandatory_block(
|
||||
// stringify!($param_name)
|
||||
// ), ],
|
||||
// optional_positional: vec![ $($optional_positional)* ],
|
||||
// rest_positional: $rest_positional,
|
||||
// named: {
|
||||
// $($config_named)*
|
||||
// }
|
||||
// }
|
||||
|
||||
Function {
|
||||
$($function)* ($param_name : Block)
|
||||
}
|
||||
// Function {
|
||||
// $($function)* ($param_name : Block)
|
||||
// }
|
||||
|
||||
Extract {
|
||||
$($extract:tt)* {
|
||||
use $nu_data::types::ExtractType;
|
||||
let value = $args.expect_nth($($positional_count)*)?;
|
||||
Block::extract(value)?
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
// Extract {
|
||||
// $($extract:tt)* {
|
||||
// use $nu_data::types::ExtractType;
|
||||
// let value = $args.expect_nth($($positional_count)*)?;
|
||||
// Block::extract(value)?
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// };
|
||||
|
||||
// // mandatory positional argument
|
||||
// (
|
||||
// Named { $export:ident $args:ident $body:block }
|
||||
// Positional { $($positional_count:tt)* }
|
||||
// Rest { $param_name:ident : $param_kind:ty , $($rest:tt)* }
|
||||
// Signature {
|
||||
// name: $config_name:tt,
|
||||
// mandatory_positional: vec![ $($mandatory_positional:tt)* ],
|
||||
// optional_positional: vec![ $($optional_positional:tt)* ],
|
||||
// rest_positional: $rest_positional:tt,
|
||||
// named: {
|
||||
// $($config_named:tt)*
|
||||
// }
|
||||
// }
|
||||
|
||||
// mandatory positional argument
|
||||
(
|
||||
Named { $export:ident $args:ident $body:block }
|
||||
Positional { $($positional_count:tt)* }
|
||||
Rest { $param_name:ident : $param_kind:ty , $($rest:tt)* }
|
||||
Signature {
|
||||
name: $config_name:tt,
|
||||
mandatory_positional: vec![ $($mandatory_positional:tt)* ],
|
||||
optional_positional: vec![ $($optional_positional:tt)* ],
|
||||
rest_positional: $rest_positional:tt,
|
||||
named: {
|
||||
$($config_named:tt)*
|
||||
}
|
||||
}
|
||||
// Function {
|
||||
// $($function:tt)*
|
||||
// }
|
||||
|
||||
Function {
|
||||
$($function:tt)*
|
||||
}
|
||||
// Extract {
|
||||
// $($extract:tt)*
|
||||
// }
|
||||
|
||||
Extract {
|
||||
$($extract:tt)*
|
||||
}
|
||||
// ) => {
|
||||
// command!(
|
||||
// Named { $export $args $body }
|
||||
// Positional { $($positional_count)* + 1 }
|
||||
// Rest { $($rest)* }
|
||||
// Signature {
|
||||
// name: $config_name,
|
||||
// mandatory_positional: vec![ $($mandatory_positional)* $nu_parser::registry::PositionalType::mandatory(
|
||||
// stringify!($param_name), <$param_kind>::syntax_type()
|
||||
// ), ],
|
||||
// optional_positional: vec![ $($optional_positional)* ],
|
||||
// rest_positional: $rest_positional,
|
||||
// named: {
|
||||
// $($config_named)*
|
||||
// }
|
||||
// }
|
||||
|
||||
) => {
|
||||
command!(
|
||||
Named { $export $args $body }
|
||||
Positional { $($positional_count)* + 1 }
|
||||
Rest { $($rest)* }
|
||||
Signature {
|
||||
name: $config_name,
|
||||
mandatory_positional: vec![ $($mandatory_positional)* $nu_parser::registry::PositionalType::mandatory(
|
||||
stringify!($param_name), <$param_kind>::syntax_type()
|
||||
), ],
|
||||
optional_positional: vec![ $($optional_positional)* ],
|
||||
rest_positional: $rest_positional,
|
||||
named: {
|
||||
$($config_named)*
|
||||
}
|
||||
}
|
||||
// Function {
|
||||
// $($function)* ($param_name : $param_kind)
|
||||
// }
|
||||
|
||||
Function {
|
||||
$($function)* ($param_name : $param_kind)
|
||||
}
|
||||
// Extract {
|
||||
// $($extract:tt)* {
|
||||
// use $nu_data::types::ExtractType;
|
||||
// let value = $args.expect_nth($($positional_count)*)?;
|
||||
// <$param_kind>::extract(&value)?
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// };
|
||||
|
||||
Extract {
|
||||
$($extract:tt)* {
|
||||
use $nu_data::types::ExtractType;
|
||||
let value = $args.expect_nth($($positional_count)*)?;
|
||||
<$param_kind>::extract(&value)?
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
// ($export:ident as $config_name:tt ( $args:ident , $($command_rest:tt)* ) $body:block) => {
|
||||
// command!(
|
||||
// Named { $export $args $body }
|
||||
// Positional { 0 }
|
||||
// Rest { $($command_rest)* }
|
||||
// Signature {
|
||||
// name: $config_name,
|
||||
// mandatory_positional: vec![],
|
||||
// optional_positional: vec![],
|
||||
// rest_positional: false,
|
||||
// named: {}
|
||||
// }
|
||||
|
||||
($export:ident as $config_name:tt ( $args:ident , $($command_rest:tt)* ) $body:block) => {
|
||||
command!(
|
||||
Named { $export $args $body }
|
||||
Positional { 0 }
|
||||
Rest { $($command_rest)* }
|
||||
Signature {
|
||||
name: $config_name,
|
||||
mandatory_positional: vec![],
|
||||
optional_positional: vec![],
|
||||
rest_positional: false,
|
||||
named: {}
|
||||
}
|
||||
// Function {
|
||||
// }
|
||||
|
||||
Function {
|
||||
}
|
||||
|
||||
Extract {
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
// Extract {
|
||||
// }
|
||||
// );
|
||||
// };
|
||||
// }
|
||||
|
@ -20,8 +20,9 @@ impl WholeStreamCommand for SubCommand {
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let mapped = args.input.map(move |val| match val.value {
|
||||
UntaggedValue::Primitive(Primitive::Int(val)) => {
|
||||
UntaggedValue::int(val.magnitude().clone()).into()
|
||||
UntaggedValue::Primitive(Primitive::Int(val)) => UntaggedValue::int(val.abs()).into(),
|
||||
UntaggedValue::Primitive(Primitive::BigInt(val)) => {
|
||||
UntaggedValue::big_int(val.magnitude().clone()).into()
|
||||
}
|
||||
UntaggedValue::Primitive(Primitive::Decimal(val)) => {
|
||||
UntaggedValue::decimal(val.abs()).into()
|
||||
|
@ -5,10 +5,7 @@ use crate::commands::math::utils::run_with_function;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::{convert_number_to_u64, Number, Operator},
|
||||
Primitive, Signature, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{hir::Operator, Primitive, Signature, UntaggedValue, Value};
|
||||
|
||||
use bigdecimal::FromPrimitive;
|
||||
|
||||
@ -47,7 +44,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
fn to_byte(value: &Value) -> Option<Value> {
|
||||
match &value.value {
|
||||
UntaggedValue::Primitive(Primitive::Int(num)) => {
|
||||
Some(UntaggedValue::Primitive(Primitive::Filesize(num.clone())).into_untagged_value())
|
||||
Some(UntaggedValue::Primitive(Primitive::Filesize(*num as u64)).into_untagged_value())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@ -79,7 +76,7 @@ pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Filesize(num)),
|
||||
..
|
||||
} => UntaggedValue::int(num.clone()).into_untagged_value(),
|
||||
} => UntaggedValue::int(*num as i64).into_untagged_value(),
|
||||
other => other.clone(),
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
@ -100,15 +97,18 @@ pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
|
||||
value: UntaggedValue::Primitive(Primitive::Filesize(num)),
|
||||
..
|
||||
} => {
|
||||
let left = UntaggedValue::from(Primitive::Int(num));
|
||||
let left = UntaggedValue::from(Primitive::Int(num as i64));
|
||||
let result = nu_data::value::compute_values(Operator::Divide, &left, &total_rows);
|
||||
|
||||
match result {
|
||||
Ok(UntaggedValue::Primitive(Primitive::Decimal(result))) => {
|
||||
let number = Number::Decimal(result);
|
||||
let number = convert_number_to_u64(&number);
|
||||
Ok(UntaggedValue::filesize(number).into_value(name))
|
||||
}
|
||||
Ok(UntaggedValue::Primitive(Primitive::Decimal(result))) => match result.to_u64() {
|
||||
Some(number) => Ok(UntaggedValue::filesize(number).into_value(name)),
|
||||
None => Err(ShellError::labeled_error(
|
||||
"could not calculate average of non-integer or unrelated types",
|
||||
"source",
|
||||
name,
|
||||
)),
|
||||
},
|
||||
Ok(_) => Err(ShellError::labeled_error(
|
||||
"could not calculate average of non-integer or unrelated types",
|
||||
"source",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user