mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 07:00:37 +02:00
Compare commits
91 Commits
Author | SHA1 | Date | |
---|---|---|---|
02d5729941 | |||
ce35689d2e | |||
da81e21bf2 | |||
3b2ed7631f | |||
1a46e70dfb | |||
0fc9b6cfa2 | |||
61768aa2fd | |||
ea5bf9db36 | |||
9d24afcfe3 | |||
033df9457b | |||
d8e105fe34 | |||
d34068da18 | |||
611103d211 | |||
528c1c5fd8 | |||
f73732bf1e | |||
fd7875e572 | |||
e8bc319f08 | |||
a92ff57270 | |||
004230d02d | |||
a148c640b2 | |||
ea0205f2ff | |||
005649b6fc | |||
e09e3b01d6 | |||
fc15e0e27d | |||
b2fe5fabb1 | |||
52d69bb021 | |||
5f550a355b | |||
dbecbdccd4 | |||
734877338d | |||
2e439ca77f | |||
a853880e07 | |||
93f3ed98e1 | |||
a131eddf54 | |||
b19a7aa8a6 | |||
f5aa53c530 | |||
80f5e14512 | |||
41390cd963 | |||
0b5e131410 | |||
556596bce8 | |||
b791d1ab0d | |||
ac070ae942 | |||
111ad868a7 | |||
a7274115d0 | |||
81160bcefb | |||
2880109f31 | |||
09a1f5acb9 | |||
5fcf11fcb0 | |||
42fac722bb | |||
073e5727c6 | |||
ad1c4f5e39 | |||
dc8a68c98f | |||
e5621dea58 | |||
00acf22f5f | |||
4c09716ad8 | |||
1c941557c3 | |||
28e1a7915d | |||
4bc9d9fd3b | |||
e278ca61d1 | |||
2146ede15d | |||
e737222a5d | |||
f03f1949bf | |||
0fe6c7c558 | |||
b13202bbfc | |||
90fae903ce | |||
06b154f4b2 | |||
419a0665c8 | |||
387098fc87 | |||
c42b588782 | |||
4faaa5310e | |||
c448abd44e | |||
2517588d7d | |||
8fc8fc89aa | |||
b243b3ee1d | |||
28e08afada | |||
7e184b58b2 | |||
589fc0b8ad | |||
f0c7c1b500 | |||
840bd98e01 | |||
a5cdd22bfe | |||
0c7bcae9b1 | |||
ab666c170c | |||
d2213d18fa | |||
82b6300dcb | |||
b69cda9e07 | |||
56adc7c3c6 | |||
2ace20fade | |||
c13fe83784 | |||
6cf8df8685 | |||
86a89404be | |||
0d305d7c3e | |||
ee5bd2b4b3 |
@ -2,7 +2,7 @@
|
||||
|
||||
Welcome to nushell!
|
||||
|
||||
*Note: for a more complete guide see [The nu contributor book](https://github.com/nushell/contributor-book)*
|
||||
*Note: for a more complete guide see [The nu contributor book](https://www.nushell.sh/contributor-book/)*
|
||||
|
||||
For speedy contributions open it in Gitpod, nu will be pre-installed with the latest build in a VSCode like editor all from your browser.
|
||||
|
||||
|
1263
Cargo.lock
generated
1263
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
84
Cargo.toml
84
Cargo.toml
@ -10,7 +10,7 @@ license = "MIT"
|
||||
name = "nu"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
version = "0.28.0"
|
||||
version = "0.30.0"
|
||||
|
||||
[workspace]
|
||||
members = ["crates/*/"]
|
||||
@ -18,35 +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.28.0", path = "./crates/nu-cli", default-features = false }
|
||||
nu-command = { version = "0.28.0", path = "./crates/nu-command" }
|
||||
nu-data = { version = "0.28.0", path = "./crates/nu-data" }
|
||||
nu-engine = { version = "0.28.0", path = "./crates/nu-engine" }
|
||||
nu-errors = { version = "0.28.0", path = "./crates/nu-errors" }
|
||||
nu-parser = { version = "0.28.0", path = "./crates/nu-parser" }
|
||||
nu-plugin = { version = "0.28.0", path = "./crates/nu-plugin" }
|
||||
nu-protocol = { version = "0.28.0", path = "./crates/nu-protocol" }
|
||||
nu-source = { version = "0.28.0", path = "./crates/nu-source" }
|
||||
nu-value-ext = { version = "0.28.0", path = "./crates/nu-value-ext" }
|
||||
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_plugin_binaryview = { version = "0.28.0", path = "./crates/nu_plugin_binaryview", optional = true }
|
||||
nu_plugin_chart = { version = "0.28.0", path = "./crates/nu_plugin_chart", optional = true }
|
||||
nu_plugin_fetch = { version = "0.28.0", path = "./crates/nu_plugin_fetch", optional = true }
|
||||
nu_plugin_from_bson = { version = "0.28.0", path = "./crates/nu_plugin_from_bson", optional = true }
|
||||
nu_plugin_from_sqlite = { version = "0.28.0", path = "./crates/nu_plugin_from_sqlite", optional = true }
|
||||
nu_plugin_inc = { version = "0.28.0", path = "./crates/nu_plugin_inc", optional = true }
|
||||
nu_plugin_match = { version = "0.28.0", path = "./crates/nu_plugin_match", optional = true }
|
||||
nu_plugin_post = { version = "0.28.0", path = "./crates/nu_plugin_post", optional = true }
|
||||
nu_plugin_ps = { version = "0.28.0", path = "./crates/nu_plugin_ps", optional = true }
|
||||
nu_plugin_s3 = { version = "0.28.0", path = "./crates/nu_plugin_s3", optional = true }
|
||||
nu_plugin_selector = { version = "0.28.0", path = "./crates/nu_plugin_selector", optional = true }
|
||||
nu_plugin_start = { version = "0.28.0", path = "./crates/nu_plugin_start", optional = true }
|
||||
nu_plugin_sys = { version = "0.28.0", path = "./crates/nu_plugin_sys", optional = true }
|
||||
nu_plugin_textview = { version = "0.28.0", path = "./crates/nu_plugin_textview", optional = true }
|
||||
nu_plugin_to_bson = { version = "0.28.0", path = "./crates/nu_plugin_to_bson", optional = true }
|
||||
nu_plugin_to_sqlite = { version = "0.28.0", path = "./crates/nu_plugin_to_sqlite", optional = true }
|
||||
nu_plugin_tree = { version = "0.28.0", path = "./crates/nu_plugin_tree", optional = true }
|
||||
nu_plugin_xpath = { version = "0.28.0", path = "./crates/nu_plugin_xpath", optional = true }
|
||||
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 }
|
||||
|
||||
# Required to bootstrap the main binary
|
||||
clap = "2.33.3"
|
||||
@ -57,9 +58,10 @@ log = "0.4.14"
|
||||
pretty_env_logger = "0.4.0"
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { version = "0.28.0", path = "./crates/nu-test-support" }
|
||||
nu-test-support = { version = "0.30.0", path = "./crates/nu-test-support" }
|
||||
dunce = "1.0.1"
|
||||
serial_test = "0.5.1"
|
||||
hamcrest2 = "0.3.0"
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
@ -84,20 +86,17 @@ which-support = [
|
||||
"nu-cli/which",
|
||||
"nu-command/ichwh",
|
||||
"nu-command/which",
|
||||
"nu-engine/which",
|
||||
]
|
||||
|
||||
default = [
|
||||
"nu-cli/shadow-rs",
|
||||
"sys",
|
||||
"ps",
|
||||
"textview",
|
||||
"inc",
|
||||
"directories-support",
|
||||
"ctrlc-support",
|
||||
"which-support",
|
||||
"ptree-support",
|
||||
"term-support",
|
||||
"uuid-support",
|
||||
"rustyline-support",
|
||||
"match",
|
||||
"post",
|
||||
@ -109,9 +108,13 @@ stable = ["default"]
|
||||
extra = [
|
||||
"default",
|
||||
"binaryview",
|
||||
"inc",
|
||||
"tree",
|
||||
"ptree-support",
|
||||
"textview",
|
||||
"clipboard-cli",
|
||||
"trash-support",
|
||||
"uuid-support",
|
||||
"start",
|
||||
"bson",
|
||||
"sqlite",
|
||||
@ -119,6 +122,7 @@ extra = [
|
||||
"chart",
|
||||
"xpath",
|
||||
"selector",
|
||||
"query-json",
|
||||
]
|
||||
|
||||
wasi = ["inc", "match", "ptree-support", "match", "tree", "rustyline-support"]
|
||||
@ -133,13 +137,13 @@ post = ["nu_plugin_post"]
|
||||
ps = ["nu_plugin_ps"]
|
||||
sys = ["nu_plugin_sys"]
|
||||
textview = ["nu_plugin_textview"]
|
||||
zip-support = ["nu-cli/zip", "nu-command/zip"]
|
||||
|
||||
# Extra
|
||||
binaryview = ["nu_plugin_binaryview"]
|
||||
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
|
||||
chart = ["nu_plugin_chart"]
|
||||
clipboard-cli = ["nu-cli/clipboard-cli", "nu-command/clipboard-cli"]
|
||||
query-json = ["nu_plugin_query_json"]
|
||||
s3 = ["nu_plugin_s3"]
|
||||
selector = ["nu_plugin_selector"]
|
||||
sqlite = ["nu_plugin_from_sqlite", "nu_plugin_to_sqlite"]
|
||||
@ -151,6 +155,8 @@ trash-support = [
|
||||
]
|
||||
tree = ["nu_plugin_tree"]
|
||||
xpath = ["nu_plugin_xpath"]
|
||||
zip-support = ["nu-cli/zip", "nu-command/zip"]
|
||||
|
||||
#This is disabled in extra for now
|
||||
table-pager = ["nu-command/table-pager"]
|
||||
|
||||
@ -158,7 +164,8 @@ table-pager = ["nu-command/table-pager"]
|
||||
#strip = "symbols" #Couldn't get working +nightly
|
||||
codegen-units = 1 #Reduce parallel codegen units
|
||||
lto = true #Link Time Optimization
|
||||
opt-level = 'z' #Optimize for size
|
||||
# opt-level = 'z' #Optimize for size
|
||||
# debug = true
|
||||
|
||||
# Core plugins that ship with `cargo install nu` by default
|
||||
# Currently, Cargo limits us to installing only one binary
|
||||
@ -210,6 +217,11 @@ name = "nu_plugin_extra_tree"
|
||||
path = "src/plugins/nu_plugin_extra_tree.rs"
|
||||
required-features = ["tree"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_extra_query_json"
|
||||
path = "src/plugins/nu_plugin_extra_query_json.rs"
|
||||
required-features = ["query-json"]
|
||||
|
||||
[[bin]]
|
||||
name = "nu_plugin_extra_start"
|
||||
path = "src/plugins/nu_plugin_extra_start.rs"
|
||||
|
@ -9,7 +9,7 @@ description = "Library for ANSI terminal colors and styles (bold, underline)"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-ansi-term"
|
||||
version = "0.28.0"
|
||||
version = "0.30.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
@ -15,7 +15,7 @@ fn main() {
|
||||
let g = (col * 255 / WIDTH) as u8;
|
||||
let b = 128;
|
||||
|
||||
print!("{}", Style::default().on(Color::RGB(r, g, b)).paint(" "));
|
||||
print!("{}", Style::default().on(Color::Rgb(r, g, b)).paint(" "));
|
||||
}
|
||||
|
||||
println!();
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![allow(missing_docs)]
|
||||
use crate::style::{Color, Style};
|
||||
use crate::write::AnyWrite;
|
||||
use std::fmt;
|
||||
@ -103,7 +104,7 @@ impl Color {
|
||||
Color::Cyan => write!(f, "36"),
|
||||
Color::White => write!(f, "37"),
|
||||
Color::Fixed(num) => write!(f, "38;5;{}", &num),
|
||||
Color::RGB(r, g, b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
|
||||
Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
|
||||
Color::DarkGray => write!(f, "90"),
|
||||
Color::LightRed => write!(f, "91"),
|
||||
Color::LightGreen => write!(f, "92"),
|
||||
@ -128,7 +129,7 @@ impl Color {
|
||||
Color::Cyan => write!(f, "46"),
|
||||
Color::White => write!(f, "47"),
|
||||
Color::Fixed(num) => write!(f, "48;5;{}", &num),
|
||||
Color::RGB(r, g, b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
|
||||
Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
|
||||
Color::DarkGray => write!(f, "100"),
|
||||
Color::LightRed => write!(f, "101"),
|
||||
Color::LightGreen => write!(f, "102"),
|
||||
@ -372,10 +373,10 @@ mod test {
|
||||
test!(fixed: Fixed(100); "hi" => "\x1B[38;5;100mhi\x1B[0m");
|
||||
test!(fixed_on_purple: Fixed(100).on(Purple); "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
|
||||
test!(fixed_on_fixed: Fixed(100).on(Fixed(200)); "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
|
||||
test!(rgb: RGB(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
|
||||
test!(rgb_on_blue: RGB(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
|
||||
test!(blue_on_rgb: Blue.on(RGB(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
|
||||
test!(rgb_on_rgb: RGB(70,130,180).on(RGB(5,10,15)); "hi" => "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m");
|
||||
test!(rgb: Rgb(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
|
||||
test!(rgb_on_blue: Rgb(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
|
||||
test!(blue_on_rgb: Blue.on(Rgb(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
|
||||
test!(rgb_on_rgb: Rgb(70,130,180).on(Rgb(5,10,15)); "hi" => "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m");
|
||||
test!(bold: Style::new().bold(); "hi" => "\x1B[1mhi\x1B[0m");
|
||||
test!(underline: Style::new().underline(); "hi" => "\x1B[4mhi\x1B[0m");
|
||||
test!(bunderline: Style::new().bold().underline(); "hi" => "\x1B[1;4mhi\x1B[0m");
|
||||
|
@ -112,7 +112,7 @@ mod test {
|
||||
test!(both: style().bold().italic() => "Style { bold, italic }");
|
||||
|
||||
test!(red: Red.normal() => "Style { fg(Red) }");
|
||||
test!(redblue: Red.normal().on(RGB(3, 2, 4)) => "Style { fg(Red), on(RGB(3, 2, 4)) }");
|
||||
test!(redblue: Red.normal().on(Rgb(3, 2, 4)) => "Style { fg(Red), on(Rgb(3, 2, 4)) }");
|
||||
|
||||
test!(everything:
|
||||
Red.on(Blue).blink().bold().dimmed().hidden().italic().reverse().strikethrough().underline() =>
|
||||
|
@ -11,7 +11,7 @@ use std::ops::Deref;
|
||||
/// display that string. `ANSIString` and `ANSIByteString` are aliases for
|
||||
/// this type on `str` and `\[u8]`, respectively.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct ANSIGenericString<'a, S: 'a + ToOwned + ?Sized>
|
||||
pub struct AnsiGenericString<'a, S: 'a + ToOwned + ?Sized>
|
||||
where
|
||||
<S as ToOwned>::Owned: fmt::Debug,
|
||||
{
|
||||
@ -30,12 +30,12 @@ where
|
||||
/// let clone_string = plain_string.clone();
|
||||
/// assert_eq!(clone_string, plain_string);
|
||||
/// ```
|
||||
impl<'a, S: 'a + ToOwned + ?Sized> Clone for ANSIGenericString<'a, S>
|
||||
impl<'a, S: 'a + ToOwned + ?Sized> Clone for AnsiGenericString<'a, S>
|
||||
where
|
||||
<S as ToOwned>::Owned: fmt::Debug,
|
||||
{
|
||||
fn clone(&self) -> ANSIGenericString<'a, S> {
|
||||
ANSIGenericString {
|
||||
fn clone(&self) -> AnsiGenericString<'a, S> {
|
||||
AnsiGenericString {
|
||||
style: self.style,
|
||||
string: self.string.clone(),
|
||||
}
|
||||
@ -85,26 +85,26 @@ where
|
||||
/// let plain_string = ANSIString::from("a plain string");
|
||||
/// assert_eq!(&*plain_string, "a plain string");
|
||||
/// ```
|
||||
pub type ANSIString<'a> = ANSIGenericString<'a, str>;
|
||||
pub type AnsiString<'a> = AnsiGenericString<'a, str>;
|
||||
|
||||
/// An `ANSIByteString` represents a formatted series of bytes. Use
|
||||
/// `ANSIByteString` when styling text with an unknown encoding.
|
||||
pub type ANSIByteString<'a> = ANSIGenericString<'a, [u8]>;
|
||||
/// An `AnsiByteString` represents a formatted series of bytes. Use
|
||||
/// `AnsiByteString` when styling text with an unknown encoding.
|
||||
pub type AnsiByteString<'a> = AnsiGenericString<'a, [u8]>;
|
||||
|
||||
impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for ANSIGenericString<'a, S>
|
||||
impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for AnsiGenericString<'a, S>
|
||||
where
|
||||
I: Into<Cow<'a, S>>,
|
||||
<S as ToOwned>::Owned: fmt::Debug,
|
||||
{
|
||||
fn from(input: I) -> ANSIGenericString<'a, S> {
|
||||
ANSIGenericString {
|
||||
fn from(input: I) -> AnsiGenericString<'a, S> {
|
||||
AnsiGenericString {
|
||||
string: input.into(),
|
||||
style: Style::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + ToOwned + ?Sized> ANSIGenericString<'a, S>
|
||||
impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S>
|
||||
where
|
||||
<S as ToOwned>::Owned: fmt::Debug,
|
||||
{
|
||||
@ -119,7 +119,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + ToOwned + ?Sized> Deref for ANSIGenericString<'a, S>
|
||||
impl<'a, S: 'a + ToOwned + ?Sized> Deref for AnsiGenericString<'a, S>
|
||||
where
|
||||
<S as ToOwned>::Owned: fmt::Debug,
|
||||
{
|
||||
@ -130,32 +130,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of `ANSIGenericString`s collected together, in order to be
|
||||
/// A set of `AnsiGenericStrings`s collected together, in order to be
|
||||
/// written with a minimum of control characters.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ANSIGenericStrings<'a, S: 'a + ToOwned + ?Sized>(pub &'a [ANSIGenericString<'a, S>])
|
||||
pub struct AnsiGenericStrings<'a, S: 'a + ToOwned + ?Sized>(pub &'a [AnsiGenericString<'a, S>])
|
||||
where
|
||||
<S as ToOwned>::Owned: fmt::Debug,
|
||||
S: PartialEq;
|
||||
|
||||
/// A set of `ANSIString`s collected together, in order to be written with a
|
||||
/// A set of `AnsiString`s collected together, in order to be written with a
|
||||
/// minimum of control characters.
|
||||
pub type ANSIStrings<'a> = ANSIGenericStrings<'a, str>;
|
||||
pub type AnsiStrings<'a> = AnsiGenericStrings<'a, str>;
|
||||
|
||||
/// A function to construct an `ANSIStrings` instance.
|
||||
/// A function to construct an `AnsiStrings` instance.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn ANSIStrings<'a>(arg: &'a [ANSIString<'a>]) -> ANSIStrings<'a> {
|
||||
ANSIGenericStrings(arg)
|
||||
pub fn AnsiStrings<'a>(arg: &'a [AnsiString<'a>]) -> AnsiStrings<'a> {
|
||||
AnsiGenericStrings(arg)
|
||||
}
|
||||
|
||||
/// A set of `ANSIByteString`s collected together, in order to be
|
||||
/// A set of `AnsiByteString`s collected together, in order to be
|
||||
/// written with a minimum of control characters.
|
||||
pub type ANSIByteStrings<'a> = ANSIGenericStrings<'a, [u8]>;
|
||||
pub type AnsiByteStrings<'a> = AnsiGenericStrings<'a, [u8]>;
|
||||
|
||||
/// A function to construct an `ANSIByteStrings` instance.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn ANSIByteStrings<'a>(arg: &'a [ANSIByteString<'a>]) -> ANSIByteStrings<'a> {
|
||||
ANSIGenericStrings(arg)
|
||||
pub fn ANSIByteStrings<'a>(arg: &'a [AnsiByteString<'a>]) -> AnsiByteStrings<'a> {
|
||||
AnsiGenericStrings(arg)
|
||||
}
|
||||
|
||||
// ---- paint functions ----
|
||||
@ -163,12 +163,12 @@ pub fn ANSIByteStrings<'a>(arg: &'a [ANSIByteString<'a>]) -> ANSIByteStrings<'a>
|
||||
impl Style {
|
||||
/// Paints the given text with this color, returning an ANSI string.
|
||||
#[must_use]
|
||||
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> ANSIGenericString<'a, S>
|
||||
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S>
|
||||
where
|
||||
I: Into<Cow<'a, S>>,
|
||||
<S as ToOwned>::Owned: fmt::Debug,
|
||||
{
|
||||
ANSIGenericString {
|
||||
AnsiGenericString {
|
||||
string: input.into(),
|
||||
style: self,
|
||||
}
|
||||
@ -185,12 +185,12 @@ impl Color {
|
||||
/// println!("{}", Blue.paint("da ba dee"));
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> ANSIGenericString<'a, S>
|
||||
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S>
|
||||
where
|
||||
I: Into<Cow<'a, S>>,
|
||||
<S as ToOwned>::Owned: fmt::Debug,
|
||||
{
|
||||
ANSIGenericString {
|
||||
AnsiGenericString {
|
||||
string: input.into(),
|
||||
style: self.normal(),
|
||||
}
|
||||
@ -199,14 +199,14 @@ impl Color {
|
||||
|
||||
// ---- writers for individual ANSI strings ----
|
||||
|
||||
impl<'a> fmt::Display for ANSIString<'a> {
|
||||
impl<'a> fmt::Display for AnsiString<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let w: &mut dyn fmt::Write = f;
|
||||
self.write_to_any(w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ANSIByteString<'a> {
|
||||
impl<'a> AnsiByteString<'a> {
|
||||
/// Write an `ANSIByteString` to an `io::Write`. This writes the escape
|
||||
/// sequences for the associated `Style` around the bytes.
|
||||
pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
@ -215,7 +215,7 @@ impl<'a> ANSIByteString<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + ToOwned + ?Sized> ANSIGenericString<'a, S>
|
||||
impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S>
|
||||
where
|
||||
<S as ToOwned>::Owned: fmt::Debug,
|
||||
&'a S: AsRef<[u8]>,
|
||||
@ -229,14 +229,14 @@ where
|
||||
|
||||
// ---- writers for combined ANSI strings ----
|
||||
|
||||
impl<'a> fmt::Display for ANSIStrings<'a> {
|
||||
impl<'a> fmt::Display for AnsiStrings<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let f: &mut dyn fmt::Write = f;
|
||||
self.write_to_any(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ANSIByteStrings<'a> {
|
||||
impl<'a> AnsiByteStrings<'a> {
|
||||
/// Write `ANSIByteStrings` to an `io::Write`. This writes the minimal
|
||||
/// escape sequences for the associated `Style`s around each set of
|
||||
/// bytes.
|
||||
@ -246,7 +246,7 @@ impl<'a> ANSIByteStrings<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + ToOwned + ?Sized + PartialEq> ANSIGenericStrings<'a, S>
|
||||
impl<'a, S: 'a + ToOwned + ?Sized + PartialEq> AnsiGenericStrings<'a, S>
|
||||
where
|
||||
<S as ToOwned>::Owned: fmt::Debug,
|
||||
&'a S: AsRef<[u8]>,
|
||||
@ -289,7 +289,7 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub use super::super::ANSIStrings;
|
||||
pub use super::super::AnsiStrings;
|
||||
pub use crate::style::Color::*;
|
||||
pub use crate::style::Style;
|
||||
|
||||
@ -297,7 +297,7 @@ mod tests {
|
||||
fn no_control_codes_for_plain() {
|
||||
let one = Style::default().paint("one");
|
||||
let two = Style::default().paint("two");
|
||||
let output = format!("{}", ANSIStrings(&[one, two]));
|
||||
let output = format!("{}", AnsiStrings(&[one, two]));
|
||||
assert_eq!(&*output, "onetwo");
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ extern crate doc_comment;
|
||||
#[cfg(test)]
|
||||
doctest!("../README.md");
|
||||
|
||||
mod ansi;
|
||||
pub mod ansi;
|
||||
pub use ansi::{Infix, Prefix, Suffix};
|
||||
|
||||
mod style;
|
||||
|
@ -365,7 +365,7 @@ pub enum Color {
|
||||
Fixed(u8),
|
||||
|
||||
/// A 24-bit RGB color, as specified by ISO-8613-3.
|
||||
RGB(u8, u8, u8),
|
||||
Rgb(u8, u8, u8),
|
||||
}
|
||||
|
||||
impl Color {
|
||||
|
@ -1,12 +1,12 @@
|
||||
use crate::display::{ANSIString, ANSIStrings};
|
||||
use crate::display::{AnsiString, AnsiStrings};
|
||||
use std::ops::Deref;
|
||||
|
||||
/// Return a substring of the given ANSIStrings sequence, while keeping the formatting.
|
||||
pub fn sub_string<'a>(
|
||||
start: usize,
|
||||
len: usize,
|
||||
strs: &ANSIStrings<'a>,
|
||||
) -> Vec<ANSIString<'static>> {
|
||||
strs: &AnsiStrings<'a>,
|
||||
) -> Vec<AnsiString<'static>> {
|
||||
let mut vec = Vec::new();
|
||||
let mut pos = start;
|
||||
let mut len_rem = len;
|
||||
@ -39,7 +39,7 @@ pub fn sub_string<'a>(
|
||||
}
|
||||
|
||||
/// Return a concatenated copy of `strs` without the formatting, as an allocated `String`.
|
||||
pub fn unstyle(strs: &ANSIStrings) -> String {
|
||||
pub fn unstyle(strs: &AnsiStrings) -> String {
|
||||
let mut s = String::new();
|
||||
|
||||
for i in strs.0.iter() {
|
||||
@ -50,7 +50,7 @@ pub fn unstyle(strs: &ANSIStrings) -> String {
|
||||
}
|
||||
|
||||
/// Return the unstyled length of ANSIStrings. This is equaivalent to `unstyle(strs).len()`.
|
||||
pub fn unstyled_len(strs: &ANSIStrings) -> usize {
|
||||
pub fn unstyled_len(strs: &AnsiStrings) -> usize {
|
||||
let mut l = 0;
|
||||
for i in strs.0.iter() {
|
||||
l += i.deref().len();
|
||||
@ -70,7 +70,7 @@ mod test {
|
||||
Red.paint("-second"),
|
||||
White.paint("-third"),
|
||||
];
|
||||
let a = ANSIStrings(&l);
|
||||
let a = AnsiStrings(&l);
|
||||
assert_eq!(unstyle(&a), "first-second-third");
|
||||
assert_eq!(unstyled_len(&a), 18);
|
||||
|
||||
|
@ -5,26 +5,26 @@ description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-cli"
|
||||
version = "0.28.0"
|
||||
version = "0.30.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-command = { version = "0.28.0", path = "../nu-command" }
|
||||
nu-data = { version = "0.28.0", path = "../nu-data" }
|
||||
nu-engine = { version = "0.28.0", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.28.0", path = "../nu-errors" }
|
||||
nu-json = { version = "0.28.0", path = "../nu-json" }
|
||||
nu-parser = { version = "0.28.0", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.28.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.28.0", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.28.0", path = "../nu-source" }
|
||||
nu-stream = { version = "0.28.0", path = "../nu-stream" }
|
||||
nu-table = { version = "0.28.0", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.28.0", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.28.0", path = "../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.28.0", path = "../nu-ansi-term" }
|
||||
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" }
|
||||
|
||||
Inflector = "0.11"
|
||||
arboard = { version = "1.1.0", optional = true }
|
||||
@ -77,7 +77,7 @@ rayon = "1.5.0"
|
||||
regex = "1.4.3"
|
||||
roxmltree = "0.14.0"
|
||||
rust-embed = "5.9.0"
|
||||
rustyline = { version = "6.3.0", optional = true }
|
||||
rustyline = { version = "8.0.0", optional = true }
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
serde_bytes = "0.11.5"
|
||||
serde_ini = "0.2.0"
|
||||
|
@ -1,12 +1,9 @@
|
||||
use crate::line_editor::configure_ctrl_c;
|
||||
use nu_command::commands::default_context::create_default_context;
|
||||
#[allow(unused_imports)]
|
||||
use nu_command::maybe_print_errors;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::EvaluationContext;
|
||||
use nu_ansi_term::Color;
|
||||
use nu_engine::{maybe_print_errors, run_block, script::run_script_standalone, EvaluationContext};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_command::script::{process_script, LineResult};
|
||||
pub(crate) use nu_engine::script::{process_script, LineResult};
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
use crate::line_editor::{
|
||||
@ -18,24 +15,85 @@ use crate::line_editor::{
|
||||
use nu_data::config;
|
||||
use nu_source::{Tag, Text};
|
||||
use nu_stream::InputStream;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
#[allow(unused_imports)]
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use nu_command::script::{print_err, run_script_standalone};
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
use rustyline::{self, error::ReadlineError};
|
||||
|
||||
use crate::EnvironmentSyncer;
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::ParserScope;
|
||||
use nu_protocol::{hir::ExternalRedirection, UntaggedValue, Value};
|
||||
use nu_protocol::{hir::ExternalRedirection, ConfigPath, UntaggedValue, Value};
|
||||
|
||||
use log::trace;
|
||||
use std::error::Error;
|
||||
use std::iter::Iterator;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct Options {
|
||||
pub config: Option<OsString>,
|
||||
pub stdin: bool,
|
||||
pub scripts: Vec<NuScript>,
|
||||
pub save_history: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
config: None,
|
||||
stdin: false,
|
||||
scripts: vec![],
|
||||
save_history: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NuScript {
|
||||
pub filepath: Option<OsString>,
|
||||
pub contents: String,
|
||||
}
|
||||
|
||||
impl NuScript {
|
||||
pub fn code<'a>(content: impl Iterator<Item = &'a str>) -> Result<Self, ShellError> {
|
||||
let text = content
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
Ok(Self {
|
||||
filepath: None,
|
||||
contents: text,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_code(&self) -> &str {
|
||||
&self.contents
|
||||
}
|
||||
|
||||
pub fn source_file(path: &OsStr) -> Result<Self, ShellError> {
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
let path = path.to_os_string();
|
||||
let mut file = File::open(&path)?;
|
||||
let mut buffer = String::new();
|
||||
|
||||
file.read_to_string(&mut buffer)?;
|
||||
|
||||
Ok(Self {
|
||||
filepath: Some(path),
|
||||
contents: buffer,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search_paths() -> Vec<std::path::PathBuf> {
|
||||
use std::env;
|
||||
|
||||
@ -65,62 +123,38 @@ pub fn search_paths() -> Vec<std::path::PathBuf> {
|
||||
search_paths
|
||||
}
|
||||
|
||||
pub async fn run_script_file(
|
||||
file_contents: String,
|
||||
redirect_stdin: bool,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut syncer = EnvironmentSyncer::new();
|
||||
let mut context = create_default_context(false)?;
|
||||
let config = syncer.get_config();
|
||||
pub fn run_script_file(context: EvaluationContext, options: Options) -> Result<(), Box<dyn Error>> {
|
||||
if let Some(cfg) = options.config {
|
||||
load_cfg_as_global_cfg(&context, PathBuf::from(cfg));
|
||||
} else {
|
||||
load_global_cfg(&context);
|
||||
}
|
||||
|
||||
context.configure(&config, |_, ctx| {
|
||||
syncer.load_environment();
|
||||
syncer.sync_env_vars(ctx);
|
||||
syncer.sync_path_vars(ctx);
|
||||
let _ = register_plugins(&context);
|
||||
let _ = configure_ctrl_c(&context);
|
||||
|
||||
if let Err(reason) = syncer.autoenv(ctx) {
|
||||
print_err(reason, &Text::from(""), ctx);
|
||||
}
|
||||
let script = options
|
||||
.scripts
|
||||
.get(0)
|
||||
.ok_or_else(|| ShellError::unexpected("Nu source code not available"))?;
|
||||
|
||||
let _ = register_plugins(ctx);
|
||||
let _ = configure_ctrl_c(ctx);
|
||||
});
|
||||
|
||||
let _ = run_startup_commands(&mut context, &config).await;
|
||||
|
||||
run_script_standalone(file_contents, redirect_stdin, &context, true).await?;
|
||||
run_script_standalone(script.get_code().to_string(), options.stdin, &context, true)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The entry point for the CLI. Will register all known internal commands, load experimental commands, load plugins, then prepare the prompt and line reader for input.
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
let mut syncer = EnvironmentSyncer::new();
|
||||
let configuration = syncer.get_config();
|
||||
pub fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn Error>> {
|
||||
let _ = configure_ctrl_c(&context);
|
||||
|
||||
let mut rl = default_rustyline_editor_configuration();
|
||||
|
||||
context.configure(&configuration, |config, ctx| {
|
||||
syncer.load_environment();
|
||||
syncer.sync_env_vars(ctx);
|
||||
syncer.sync_path_vars(ctx);
|
||||
|
||||
if let Err(reason) = syncer.autoenv(ctx) {
|
||||
print_err(reason, &Text::from(""), ctx);
|
||||
}
|
||||
|
||||
let _ = configure_ctrl_c(ctx);
|
||||
let _ = configure_rustyline_editor(&mut rl, config);
|
||||
|
||||
let helper = Some(nu_line_editor_helper(ctx, config));
|
||||
rl.set_helper(helper);
|
||||
});
|
||||
|
||||
// start time for command duration
|
||||
// start time for running startup scripts (this metric includes loading of the cfg, but w/e)
|
||||
let startup_commands_start_time = std::time::Instant::now();
|
||||
// run the startup commands
|
||||
let _ = run_startup_commands(&mut context, &configuration).await;
|
||||
|
||||
if let Some(cfg) = options.config {
|
||||
load_cfg_as_global_cfg(&context, PathBuf::from(cfg));
|
||||
} else {
|
||||
load_global_cfg(&context);
|
||||
}
|
||||
// Store cmd duration in an env var
|
||||
context.scope.add_env_var(
|
||||
"CMD_DURATION",
|
||||
@ -131,19 +165,43 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
startup_commands_start_time.elapsed()
|
||||
);
|
||||
|
||||
//Configure rustyline
|
||||
let mut rl = default_rustyline_editor_configuration();
|
||||
let history_path = if let Some(cfg) = &context.configs.lock().global_config {
|
||||
let _ = configure_rustyline_editor(&mut rl, cfg);
|
||||
let helper = Some(nu_line_editor_helper(&context, cfg));
|
||||
rl.set_helper(helper);
|
||||
nu_data::config::path::history_path_or_default(cfg)
|
||||
} else {
|
||||
nu_data::config::path::default_history_path()
|
||||
};
|
||||
|
||||
// Don't load history if it's not necessary
|
||||
if options.save_history {
|
||||
let _ = rl.load_history(&history_path);
|
||||
}
|
||||
|
||||
//set vars from cfg if present
|
||||
let (skip_welcome_message, prompt) = if let Some(cfg) = &context.configs.lock().global_config {
|
||||
(
|
||||
cfg.var("skip_welcome_message")
|
||||
.map(|x| x.is_true())
|
||||
.unwrap_or(false),
|
||||
cfg.var("prompt"),
|
||||
)
|
||||
} else {
|
||||
(false, None)
|
||||
};
|
||||
|
||||
//Check whether dir we start in contains local cfg file and if so load it.
|
||||
load_local_cfg_if_present(&context);
|
||||
|
||||
// Give ourselves a scope to work in
|
||||
context.scope.enter_scope();
|
||||
|
||||
let history_path = nu_engine::history_path(&configuration);
|
||||
let _ = rl.load_history(&history_path);
|
||||
|
||||
let mut session_text = String::new();
|
||||
let mut line_start: usize = 0;
|
||||
|
||||
let skip_welcome_message = configuration
|
||||
.var("skip_welcome_message")
|
||||
.map(|x| x.is_true())
|
||||
.unwrap_or(false);
|
||||
if !skip_welcome_message {
|
||||
println!(
|
||||
"Welcome to Nushell {} (type 'help' for more info)",
|
||||
@ -167,26 +225,36 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
let cwd = context.shell_manager.path();
|
||||
|
||||
let colored_prompt = {
|
||||
if let Some(prompt) = configuration.var("prompt") {
|
||||
if let Some(prompt) = &prompt {
|
||||
let prompt_line = prompt.as_string()?;
|
||||
|
||||
context.scope.enter_scope();
|
||||
let (mut prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
|
||||
|
||||
prompt_block.set_redirect(ExternalRedirection::Stdout);
|
||||
if let Some(block) =
|
||||
std::sync::Arc::<nu_protocol::hir::Block>::get_mut(&mut prompt_block)
|
||||
{
|
||||
block.set_redirect(ExternalRedirection::Stdout);
|
||||
}
|
||||
|
||||
if err.is_some() {
|
||||
context.scope.exit_scope();
|
||||
|
||||
format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch())
|
||||
format!(
|
||||
"{}{}{}{}{}{}> ",
|
||||
Color::Green.bold().prefix().to_string(),
|
||||
cwd,
|
||||
nu_ansi_term::ansi::RESET,
|
||||
Color::Cyan.bold().prefix().to_string(),
|
||||
current_branch(),
|
||||
nu_ansi_term::ansi::RESET
|
||||
)
|
||||
} else {
|
||||
// let env = context.get_env();
|
||||
|
||||
let run_result = run_block(&prompt_block, &context, InputStream::empty()).await;
|
||||
let run_result = run_block(&prompt_block, &context, InputStream::empty());
|
||||
context.scope.exit_scope();
|
||||
|
||||
match run_result {
|
||||
Ok(result) => match result.collect_string(Tag::unknown()).await {
|
||||
Ok(result) => match result.collect_string(Tag::unknown()) {
|
||||
Ok(string_result) => {
|
||||
let errors = context.get_errors();
|
||||
maybe_print_errors(&context, Text::from(prompt_line));
|
||||
@ -199,14 +267,14 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
crate::cli::print_err(e, &Text::from(prompt_line), &context);
|
||||
context.host.lock().print_err(e, &Text::from(prompt_line));
|
||||
context.clear_errors();
|
||||
|
||||
"> ".to_string()
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
crate::cli::print_err(e, &Text::from(prompt_line), &context);
|
||||
context.host.lock().print_err(e, &Text::from(prompt_line));
|
||||
context.clear_errors();
|
||||
|
||||
"> ".to_string()
|
||||
@ -214,7 +282,15 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch())
|
||||
format!(
|
||||
"{}{}{}{}{}{}> ",
|
||||
Color::Green.bold().prefix().to_string(),
|
||||
cwd,
|
||||
nu_ansi_term::ansi::RESET,
|
||||
Color::Cyan.bold().prefix().to_string(),
|
||||
current_branch(),
|
||||
nu_ansi_term::ansi::RESET
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@ -244,16 +320,13 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
let cmd_start_time = std::time::Instant::now();
|
||||
|
||||
let line = match convert_rustyline_result_to_string(readline) {
|
||||
LineResult::Success(_) => {
|
||||
process_script(
|
||||
&session_text[line_start..],
|
||||
&context,
|
||||
false,
|
||||
line_start,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
}
|
||||
LineResult::Success(_) => process_script(
|
||||
&session_text[line_start..],
|
||||
&context,
|
||||
false,
|
||||
line_start,
|
||||
true,
|
||||
),
|
||||
x => x,
|
||||
};
|
||||
|
||||
@ -262,49 +335,50 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
.scope
|
||||
.add_env_var("CMD_DURATION", format!("{:?}", cmd_start_time.elapsed()));
|
||||
|
||||
// Check the config to see if we need to update the path
|
||||
// TODO: make sure config is cached so we don't path this load every call
|
||||
// FIXME: we probably want to be a bit more graceful if we can't set the environment
|
||||
|
||||
context.configure(&configuration, |config, ctx| {
|
||||
if syncer.did_config_change() {
|
||||
syncer.reload();
|
||||
syncer.sync_env_vars(ctx);
|
||||
syncer.sync_path_vars(ctx);
|
||||
}
|
||||
|
||||
if let Err(reason) = syncer.autoenv(ctx) {
|
||||
print_err(reason, &Text::from(""), ctx);
|
||||
}
|
||||
|
||||
let _ = configure_rustyline_editor(&mut rl, config);
|
||||
});
|
||||
|
||||
match line {
|
||||
LineResult::Success(line) => {
|
||||
rl.add_history_entry(&line);
|
||||
let _ = rl.save_history(&history_path);
|
||||
if options.save_history && !line.trim().is_empty() {
|
||||
rl.add_history_entry(&line);
|
||||
let _ = rl.append_history(&history_path);
|
||||
}
|
||||
maybe_print_errors(&context, Text::from(session_text.clone()));
|
||||
}
|
||||
|
||||
LineResult::ClearHistory => {
|
||||
rl.clear_history();
|
||||
let _ = rl.save_history(&history_path);
|
||||
if options.save_history {
|
||||
rl.clear_history();
|
||||
let _ = rl.append_history(&history_path);
|
||||
}
|
||||
}
|
||||
|
||||
LineResult::Error(line, err) => {
|
||||
rl.add_history_entry(&line);
|
||||
let _ = rl.save_history(&history_path);
|
||||
if options.save_history && !line.trim().is_empty() {
|
||||
rl.add_history_entry(&line);
|
||||
let _ = rl.append_history(&history_path);
|
||||
}
|
||||
|
||||
print_err(err, &Text::from(session_text.clone()), &context);
|
||||
context
|
||||
.host
|
||||
.lock()
|
||||
.print_err(err, &Text::from(session_text.clone()));
|
||||
|
||||
// I am not so sure, we don't need maybe_print_errors here (as we printed an err
|
||||
// above), because maybe_print_errors also clears the errors.
|
||||
// TODO Analyze where above err comes from, and whether we need to clear
|
||||
// context.errors here
|
||||
// Or just be consistent and return errors always in context.errors...
|
||||
maybe_print_errors(&context, Text::from(session_text.clone()));
|
||||
}
|
||||
|
||||
LineResult::CtrlC => {
|
||||
let config_ctrlc_exit = config::config(Tag::unknown())?
|
||||
.get("ctrlc_exit")
|
||||
.map(|s| s.value.is_true())
|
||||
let config_ctrlc_exit = context
|
||||
.configs
|
||||
.lock()
|
||||
.global_config
|
||||
.as_ref()
|
||||
.map(|cfg| cfg.var("ctrlc_exit"))
|
||||
.flatten()
|
||||
.map(|ctrl_c| ctrl_c.is_true())
|
||||
.unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells
|
||||
|
||||
if !config_ctrlc_exit {
|
||||
@ -312,7 +386,9 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
|
||||
if ctrlcbreak {
|
||||
let _ = rl.save_history(&history_path);
|
||||
if options.save_history {
|
||||
let _ = rl.append_history(&history_path);
|
||||
}
|
||||
std::process::exit(0);
|
||||
} else {
|
||||
context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)"));
|
||||
@ -336,12 +412,49 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
|
||||
// we are ok if we can not save history
|
||||
let _ = rl.save_history(&history_path);
|
||||
if options.save_history {
|
||||
let _ = rl.append_history(&history_path);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn register_plugins(context: &mut EvaluationContext) -> Result<(), ShellError> {
|
||||
pub fn load_local_cfg_if_present(context: &EvaluationContext) {
|
||||
trace!("Loading local cfg if present");
|
||||
match config::loadable_cfg_exists_in_dir(PathBuf::from(context.shell_manager.path())) {
|
||||
Ok(Some(cfg_path)) => {
|
||||
if let Err(err) = context.load_config(&ConfigPath::Local(cfg_path)) {
|
||||
context.host.lock().print_err(err, &Text::from(""))
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
//Report error while checking for local cfg file
|
||||
context.host.lock().print_err(e, &Text::from(""))
|
||||
}
|
||||
Ok(None) => {
|
||||
//No local cfg file present in start dir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_cfg_as_global_cfg(context: &EvaluationContext, path: PathBuf) {
|
||||
if let Err(err) = context.load_config(&ConfigPath::Global(path)) {
|
||||
context.host.lock().print_err(err, &Text::from(""));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_global_cfg(context: &EvaluationContext) {
|
||||
match config::default_path() {
|
||||
Ok(path) => {
|
||||
load_cfg_as_global_cfg(context, path);
|
||||
}
|
||||
Err(e) => {
|
||||
context.host.lock().print_err(e, &Text::from(""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_plugins(context: &EvaluationContext) -> Result<(), ShellError> {
|
||||
if let Ok(plugins) = nu_engine::plugin::build_plugin::scan(search_paths()) {
|
||||
context.add_commands(
|
||||
plugins
|
||||
@ -354,35 +467,7 @@ pub fn register_plugins(context: &mut EvaluationContext) -> Result<(), ShellErro
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_startup_commands(
|
||||
context: &mut EvaluationContext,
|
||||
config: &dyn nu_data::config::Conf,
|
||||
) -> Result<(), ShellError> {
|
||||
if let Some(commands) = config.var("startup") {
|
||||
match commands {
|
||||
Value {
|
||||
value: UntaggedValue::Table(pipelines),
|
||||
..
|
||||
} => {
|
||||
let mut script_file = String::new();
|
||||
for pipeline in pipelines {
|
||||
script_file.push_str(&pipeline.as_string()?);
|
||||
script_file.push('\n');
|
||||
}
|
||||
let _ = run_script_standalone(script_file, false, context, false).await;
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::untagged_runtime_error(
|
||||
"expected a table of pipeline strings as startup commands",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, ShellError> {
|
||||
pub fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, ShellError> {
|
||||
// FIXME: do we still need this?
|
||||
let line = if let Some(s) = line.strip_suffix('\n') {
|
||||
s
|
||||
@ -399,13 +484,11 @@ pub async fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<Strin
|
||||
}
|
||||
|
||||
let input_stream = InputStream::empty();
|
||||
let env = ctx.get_env();
|
||||
ctx.scope.add_env(env);
|
||||
|
||||
let result = run_block(&classified_block, ctx, input_stream).await;
|
||||
let result = run_block(&classified_block, ctx, input_stream);
|
||||
ctx.scope.exit_scope();
|
||||
|
||||
result?.collect_string(Tag::unknown()).await.map(|x| x.item)
|
||||
result?.collect_string(Tag::unknown()).map(|x| x.item)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -254,6 +254,8 @@ pub fn completion_location(line: &str, block: &Block, pos: usize) -> Vec<Complet
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::*;
|
||||
|
||||
use nu_parser::{classify_block, lex, parse_block, ParserScope};
|
||||
@ -285,9 +287,9 @@ mod tests {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn add_definition(&self, _block: Block) {}
|
||||
fn add_definition(&self, _block: Arc<Block>) {}
|
||||
|
||||
fn get_definitions(&self) -> Vec<Block> {
|
||||
fn get_definitions(&self) -> Vec<Arc<Block>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ impl matchers::Matcher for Matcher {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// TODO: check some unicode matches if this becomes relevant
|
||||
// TODO: check some Unicode matches if this becomes relevant
|
||||
|
||||
// FIXME: could work exhaustively through ['-', '--'. ''] in a loop for each test
|
||||
#[test]
|
||||
|
@ -15,7 +15,8 @@ pub struct PathSuggestion {
|
||||
impl PathCompleter {
|
||||
pub fn path_suggestions(&self, partial: &str, matcher: &dyn Matcher) -> Vec<PathSuggestion> {
|
||||
let expanded = nu_parser::expand_ndots(partial);
|
||||
let expanded = expanded.as_ref();
|
||||
let expanded = expanded.replace(std::path::is_separator, &SEP.to_string());
|
||||
let expanded: &str = expanded.as_ref();
|
||||
|
||||
let (base_dir_name, partial) = match expanded.rfind(SEP) {
|
||||
Some(pos) => expanded.split_at(pos + SEP.len_utf8()),
|
||||
|
@ -1,3 +0,0 @@
|
||||
pub(crate) mod directory_specific_environment;
|
||||
pub(crate) mod environment;
|
||||
pub(crate) mod environment_syncer;
|
@ -1,259 +0,0 @@
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use nu_command::commands::autoenv;
|
||||
use nu_errors::ShellError;
|
||||
use serde::Deserialize;
|
||||
use std::env::*;
|
||||
use std::process::Command;
|
||||
|
||||
use std::{
|
||||
ffi::OsString,
|
||||
fmt::Debug,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
//Tests reside in /nushell/tests/shell/pipeline/commands/internal.rs
|
||||
|
||||
type EnvKey = String;
|
||||
type EnvVal = OsString;
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DirectorySpecificEnvironment {
|
||||
pub last_seen_directory: PathBuf,
|
||||
//If an environment var has been added from a .nu in a directory, we track it here so we can remove it when the user leaves the directory.
|
||||
//If setting the var overwrote some value, we save the old value in an option so we can restore it later.
|
||||
added_vars: IndexMap<PathBuf, IndexMap<EnvKey, Option<EnvVal>>>,
|
||||
|
||||
//We track directories that we have read .nu-env from. This is different from the keys in added_vars since sometimes a file only wants to run scripts.
|
||||
visited_dirs: IndexSet<PathBuf>,
|
||||
exitscripts: IndexMap<PathBuf, Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
pub struct NuEnvDoc {
|
||||
pub env: Option<IndexMap<String, String>>,
|
||||
pub scriptvars: Option<IndexMap<String, String>>,
|
||||
pub scripts: Option<IndexMap<String, Vec<String>>>,
|
||||
pub entryscripts: Option<Vec<String>>,
|
||||
pub exitscripts: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl DirectorySpecificEnvironment {
|
||||
pub fn new() -> DirectorySpecificEnvironment {
|
||||
let root_dir = if cfg!(target_os = "windows") {
|
||||
PathBuf::from("c:\\")
|
||||
} else {
|
||||
PathBuf::from("/")
|
||||
};
|
||||
DirectorySpecificEnvironment {
|
||||
last_seen_directory: root_dir,
|
||||
added_vars: IndexMap::new(),
|
||||
visited_dirs: IndexSet::new(),
|
||||
exitscripts: IndexMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn toml_if_trusted(&mut self, nu_env_file: &Path) -> Result<NuEnvDoc, ShellError> {
|
||||
let content = std::fs::read(&nu_env_file)?;
|
||||
|
||||
if autoenv::file_is_trusted(&nu_env_file, &content)? {
|
||||
let mut doc: NuEnvDoc = toml::de::from_slice(&content)
|
||||
.map_err(|e| ShellError::untagged_runtime_error(format!("{:?}", e)))?;
|
||||
|
||||
if let Some(scripts) = doc.scripts.as_ref() {
|
||||
for (k, v) in scripts {
|
||||
if k == "entryscripts" {
|
||||
doc.entryscripts = Some(v.clone());
|
||||
} else if k == "exitscripts" {
|
||||
doc.exitscripts = Some(v.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(doc);
|
||||
}
|
||||
Err(ShellError::untagged_runtime_error(
|
||||
format!("{:?} is untrusted. Run 'autoenv trust {:?}' to trust it.\nThis needs to be done after each change to the file.", nu_env_file, nu_env_file.parent().unwrap_or_else(|| &Path::new("")))))
|
||||
}
|
||||
|
||||
pub fn maintain_autoenv(&mut self) -> Result<(), ShellError> {
|
||||
let mut dir = current_dir()?;
|
||||
|
||||
if self.last_seen_directory == dir {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
//We track which keys we set as we go up the directory hierarchy, so that we don't overwrite a value we set in a subdir.
|
||||
let mut added_keys = IndexSet::new();
|
||||
|
||||
let mut new_visited_dirs = IndexSet::new();
|
||||
let mut popped = true;
|
||||
while popped {
|
||||
let nu_env_file = dir.join(".nu-env");
|
||||
if nu_env_file.exists() && !self.visited_dirs.contains(&dir) {
|
||||
let nu_env_doc = self.toml_if_trusted(&nu_env_file)?;
|
||||
|
||||
//add regular variables from the [env section]
|
||||
if let Some(env) = nu_env_doc.env {
|
||||
for (env_key, env_val) in env {
|
||||
self.maybe_add_key(&mut added_keys, &dir, &env_key, &env_val);
|
||||
}
|
||||
}
|
||||
|
||||
//Add variables that need to evaluate scripts to run, from [scriptvars] section
|
||||
if let Some(sv) = nu_env_doc.scriptvars {
|
||||
for (key, script) in sv {
|
||||
self.maybe_add_key(
|
||||
&mut added_keys,
|
||||
&dir,
|
||||
&key,
|
||||
value_from_script(&script)?.as_str(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(es) = nu_env_doc.entryscripts {
|
||||
for s in es {
|
||||
run(s.as_str(), None)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(es) = nu_env_doc.exitscripts {
|
||||
self.exitscripts.insert(dir.clone(), es);
|
||||
}
|
||||
}
|
||||
new_visited_dirs.insert(dir.clone());
|
||||
popped = dir.pop();
|
||||
}
|
||||
|
||||
//Time to clear out vars set by directories that we have left.
|
||||
let mut new_vars = IndexMap::new();
|
||||
for (dir, dirmap) in self.added_vars.drain(..) {
|
||||
if new_visited_dirs.contains(&dir) {
|
||||
new_vars.insert(dir, dirmap);
|
||||
} else {
|
||||
for (k, v) in dirmap {
|
||||
if let Some(v) = v {
|
||||
std::env::set_var(k, v);
|
||||
} else {
|
||||
std::env::remove_var(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Run exitscripts, can not be done in same loop as new vars as some files can contain only exitscripts
|
||||
let mut new_exitscripts = IndexMap::new();
|
||||
for (dir, scripts) in self.exitscripts.drain(..) {
|
||||
if new_visited_dirs.contains(&dir) {
|
||||
new_exitscripts.insert(dir, scripts);
|
||||
} else {
|
||||
for s in scripts {
|
||||
run(s.as_str(), Some(&dir))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.visited_dirs = new_visited_dirs;
|
||||
self.exitscripts = new_exitscripts;
|
||||
self.added_vars = new_vars;
|
||||
self.last_seen_directory = current_dir()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn maybe_add_key(
|
||||
&mut self,
|
||||
seen_vars: &mut IndexSet<EnvKey>,
|
||||
dir: &Path,
|
||||
key: &str,
|
||||
val: &str,
|
||||
) {
|
||||
//This condition is to make sure variables in parent directories don't overwrite variables set by subdirectories.
|
||||
if !seen_vars.contains(key) {
|
||||
seen_vars.insert(key.to_string());
|
||||
self.added_vars
|
||||
.entry(PathBuf::from(dir))
|
||||
.or_insert(IndexMap::new())
|
||||
.insert(key.to_string(), var_os(key));
|
||||
|
||||
std::env::set_var(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
// If the user recently ran autoenv untrust on a file, we clear the environment variables it set and make sure to not run any possible exitscripts.
|
||||
pub fn clear_recently_untrusted_file(&mut self) -> Result<(), ShellError> {
|
||||
// Figure out which file was untrusted
|
||||
// Remove all vars set by it
|
||||
let current_trusted_files: IndexSet<PathBuf> = autoenv::read_trusted()?
|
||||
.files
|
||||
.iter()
|
||||
.map(|(k, _)| PathBuf::from(k))
|
||||
.collect();
|
||||
|
||||
// We figure out which file(s) the user untrusted by taking the set difference of current trusted files in .config/nu/nu-env.toml and the files tracked by self.added_env_vars
|
||||
// If a file is in self.added_env_vars but not in nu-env.toml, it was just untrusted.
|
||||
let untrusted_files: IndexSet<PathBuf> = self
|
||||
.added_vars
|
||||
.iter()
|
||||
.filter_map(|(path, _)| {
|
||||
if !current_trusted_files.contains(path) {
|
||||
return Some(path.clone());
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect();
|
||||
|
||||
for path in untrusted_files {
|
||||
if let Some(added_keys) = self.added_vars.get(&path) {
|
||||
for (key, _) in added_keys {
|
||||
remove_var(key);
|
||||
}
|
||||
}
|
||||
self.exitscripts.remove(&path);
|
||||
self.added_vars.remove(&path);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn run(cmd: &str, dir: Option<&PathBuf>) -> Result<(), ShellError> {
|
||||
if cfg!(target_os = "windows") {
|
||||
if let Some(dir) = dir {
|
||||
let command = format!("cd {} & {}", dir.to_string_lossy(), cmd);
|
||||
Command::new("cmd")
|
||||
.args(&["/C", command.as_str()])
|
||||
.output()?
|
||||
} else {
|
||||
Command::new("cmd").args(&["/C", cmd]).output()?
|
||||
}
|
||||
} else if let Some(dir) = dir {
|
||||
// FIXME: When nu scripting is added, cding like might not be a good idea. If nu decides to execute entryscripts when entering the dir this way, it will cause troubles.
|
||||
// For now only standard shell scripts are used, so this is an issue for the future.
|
||||
Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(format!("cd {:?}; {}", dir, cmd))
|
||||
.output()?
|
||||
} else {
|
||||
Command::new("sh").arg("-c").arg(&cmd).output()?
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
fn value_from_script(cmd: &str) -> Result<String, ShellError> {
|
||||
let command = if cfg!(target_os = "windows") {
|
||||
Command::new("cmd").args(&["/C", cmd]).output()?
|
||||
} else {
|
||||
Command::new("sh").arg("-c").arg(&cmd).output()?
|
||||
};
|
||||
if command.stdout.is_empty() {
|
||||
return Err(ShellError::untagged_runtime_error(format!(
|
||||
"{:?} did not return any output",
|
||||
cmd
|
||||
)));
|
||||
}
|
||||
let response = std::str::from_utf8(&command.stdout[..command.stdout.len()]).map_err(|e| {
|
||||
ShellError::untagged_runtime_error(format!(
|
||||
"Couldn't parse stdout from command {:?}: {:?}",
|
||||
command, e
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(response.trim().to_string())
|
||||
}
|
274
crates/nu-cli/src/env/environment.rs
vendored
274
crates/nu-cli/src/env/environment.rs
vendored
@ -1,274 +0,0 @@
|
||||
use crate::env::directory_specific_environment::*;
|
||||
use indexmap::{indexmap, IndexSet};
|
||||
use nu_data::config::Conf;
|
||||
use nu_engine::Env;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{UntaggedValue, Value};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Environment {
|
||||
environment_vars: Option<Value>,
|
||||
path_vars: Option<Value>,
|
||||
pub autoenv: DirectorySpecificEnvironment,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
pub fn new() -> Environment {
|
||||
Environment {
|
||||
environment_vars: None,
|
||||
path_vars: None,
|
||||
autoenv: DirectorySpecificEnvironment::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_config<T: Conf>(configuration: &T) -> Environment {
|
||||
let env = configuration.env();
|
||||
let path = configuration.path();
|
||||
Environment {
|
||||
environment_vars: env,
|
||||
path_vars: path,
|
||||
autoenv: DirectorySpecificEnvironment::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn autoenv(&mut self, reload_trusted: bool) -> Result<(), ShellError> {
|
||||
self.autoenv.maintain_autoenv()?;
|
||||
if reload_trusted {
|
||||
self.autoenv.clear_recently_untrusted_file()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn morph<T: Conf>(&mut self, configuration: &T) {
|
||||
self.environment_vars = configuration.env();
|
||||
self.path_vars = configuration.path();
|
||||
}
|
||||
}
|
||||
|
||||
impl Env for Environment {
|
||||
fn env(&self) -> Option<Value> {
|
||||
if let Some(vars) = &self.environment_vars {
|
||||
return Some(vars.clone());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn path(&self) -> Option<Value> {
|
||||
if let Some(vars) = &self.path_vars {
|
||||
return Some(vars.clone());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn add_env(&mut self, key: &str, value: &str) {
|
||||
let value = UntaggedValue::string(value);
|
||||
|
||||
let new_envs = {
|
||||
if let Some(Value {
|
||||
value: UntaggedValue::Row(ref envs),
|
||||
ref tag,
|
||||
}) = self.environment_vars
|
||||
{
|
||||
let mut new_envs = envs.clone();
|
||||
|
||||
if !new_envs.contains_key(key) {
|
||||
new_envs.insert_data_at_key(key, value.into_value(tag.clone()));
|
||||
}
|
||||
|
||||
Value {
|
||||
value: UntaggedValue::Row(new_envs),
|
||||
tag: tag.clone(),
|
||||
}
|
||||
} else {
|
||||
UntaggedValue::Row(indexmap! { key.into() => value.into_untagged_value() }.into())
|
||||
.into_untagged_value()
|
||||
}
|
||||
};
|
||||
|
||||
self.environment_vars = Some(new_envs);
|
||||
}
|
||||
|
||||
fn add_path(&mut self, paths: std::ffi::OsString) {
|
||||
let new_paths = {
|
||||
if let Some(Value {
|
||||
value: UntaggedValue::Table(ref current_paths),
|
||||
ref tag,
|
||||
}) = self.path_vars
|
||||
{
|
||||
let mut new_paths = current_paths.clone();
|
||||
|
||||
let new_path_candidates = std::env::split_paths(&paths).map(|path| {
|
||||
UntaggedValue::string(path.to_string_lossy()).into_value(tag.clone())
|
||||
});
|
||||
|
||||
new_paths.extend(new_path_candidates);
|
||||
|
||||
let paths: IndexSet<Value> = new_paths.into_iter().collect();
|
||||
|
||||
Value {
|
||||
value: UntaggedValue::Table(paths.into_iter().collect()),
|
||||
tag: tag.clone(),
|
||||
}
|
||||
} else {
|
||||
let p = paths.into_string().unwrap_or_else(|_| String::from(""));
|
||||
let p = UntaggedValue::string(p).into_untagged_value();
|
||||
UntaggedValue::Table(vec![p]).into_untagged_value()
|
||||
}
|
||||
};
|
||||
|
||||
self.path_vars = Some(new_paths);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Env, Environment};
|
||||
use nu_data::config::{tests::FakeConfig, Conf};
|
||||
use nu_protocol::UntaggedValue;
|
||||
use nu_test_support::fs::Stub::FileWithContent;
|
||||
use nu_test_support::playground::Playground;
|
||||
|
||||
#[test]
|
||||
fn picks_up_environment_variables_from_configuration() {
|
||||
Playground::setup("environment_test_1", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
mosquetero_1 = "Andrés N. Robalino"
|
||||
mosquetero_2 = "Jonathan Turner"
|
||||
mosquetero_3 = "Yehuda katz"
|
||||
mosquetero_4 = "Jason Gedge"
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let actual = Environment::from_config(&fake_config);
|
||||
|
||||
assert_eq!(actual.env(), fake_config.env());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn picks_up_path_variables_from_configuration() {
|
||||
Playground::setup("environment_test_2", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
path = ["/Users/andresrobalino/.volta/bin", "/users/mosqueteros/bin"]
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let actual = Environment::from_config(&fake_config);
|
||||
|
||||
assert_eq!(actual.path(), fake_config.path());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn updates_env_variable() {
|
||||
Playground::setup("environment_test_3", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
SHELL = "/usr/bin/you_already_made_the_nu_choice"
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = Environment::from_config(&fake_config);
|
||||
|
||||
actual.add_env("USER", "NUNO");
|
||||
|
||||
assert_eq!(
|
||||
actual.env(),
|
||||
Some(
|
||||
UntaggedValue::row(
|
||||
indexmap! {
|
||||
"USER".into() => UntaggedValue::string("NUNO").into_untagged_value(),
|
||||
"SHELL".into() => UntaggedValue::string("/usr/bin/you_already_made_the_nu_choice").into_untagged_value(),
|
||||
}
|
||||
).into_untagged_value()
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_update_env_variable_if_it_exists() {
|
||||
Playground::setup("environment_test_4", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
SHELL = "/usr/bin/you_already_made_the_nu_choice"
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = Environment::from_config(&fake_config);
|
||||
|
||||
actual.add_env("SHELL", "/usr/bin/sh");
|
||||
|
||||
assert_eq!(
|
||||
actual.env(),
|
||||
Some(
|
||||
UntaggedValue::row(
|
||||
indexmap! {
|
||||
"SHELL".into() => UntaggedValue::string("/usr/bin/you_already_made_the_nu_choice").into_untagged_value(),
|
||||
}
|
||||
).into_untagged_value()
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn updates_path_variable() {
|
||||
Playground::setup("environment_test_5", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
path = ["/Users/andresrobalino/.volta/bin", "/users/mosqueteros/bin"]
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = Environment::from_config(&fake_config);
|
||||
|
||||
actual.add_path(std::ffi::OsString::from("/path/to/be/added"));
|
||||
|
||||
assert_eq!(
|
||||
actual.path(),
|
||||
Some(
|
||||
UntaggedValue::table(&[
|
||||
UntaggedValue::string("/Users/andresrobalino/.volta/bin")
|
||||
.into_untagged_value(),
|
||||
UntaggedValue::string("/users/mosqueteros/bin").into_untagged_value(),
|
||||
UntaggedValue::string("/path/to/be/added").into_untagged_value(),
|
||||
])
|
||||
.into_untagged_value()
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
617
crates/nu-cli/src/env/environment_syncer.rs
vendored
617
crates/nu-cli/src/env/environment_syncer.rs
vendored
@ -1,617 +0,0 @@
|
||||
use crate::env::environment::Environment;
|
||||
use nu_data::config::{Conf, NuConfig};
|
||||
use nu_engine::Env;
|
||||
use nu_engine::EvaluationContext;
|
||||
use nu_errors::ShellError;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::{atomic::Ordering, Arc};
|
||||
|
||||
pub struct EnvironmentSyncer {
|
||||
pub env: Arc<Mutex<Box<Environment>>>,
|
||||
pub config: Arc<Mutex<Box<dyn Conf>>>,
|
||||
}
|
||||
|
||||
impl Default for EnvironmentSyncer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl EnvironmentSyncer {
|
||||
pub fn with_config(config: Box<dyn Conf>) -> Self {
|
||||
EnvironmentSyncer {
|
||||
env: Arc::new(Mutex::new(Box::new(Environment::new()))),
|
||||
config: Arc::new(Mutex::new(config)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> EnvironmentSyncer {
|
||||
EnvironmentSyncer {
|
||||
env: Arc::new(Mutex::new(Box::new(Environment::new()))),
|
||||
config: Arc::new(Mutex::new(Box::new(NuConfig::new()))),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn set_config(&mut self, config: Box<dyn Conf>) {
|
||||
self.config = Arc::new(Mutex::new(config));
|
||||
}
|
||||
|
||||
pub fn get_config(&self) -> Box<dyn Conf> {
|
||||
let config = self.config.lock();
|
||||
|
||||
config.clone_box()
|
||||
}
|
||||
|
||||
pub fn load_environment(&mut self) {
|
||||
let config = self.config.lock();
|
||||
|
||||
self.env = Arc::new(Mutex::new(Box::new(Environment::from_config(&*config))));
|
||||
}
|
||||
|
||||
pub fn did_config_change(&mut self) -> bool {
|
||||
let config = self.config.lock();
|
||||
config.is_modified().unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn reload(&mut self) {
|
||||
let mut config = self.config.lock();
|
||||
config.reload();
|
||||
|
||||
let mut environment = self.env.lock();
|
||||
environment.morph(&*config);
|
||||
}
|
||||
|
||||
pub fn autoenv(&self, ctx: &mut EvaluationContext) -> Result<(), ShellError> {
|
||||
let mut environment = self.env.lock();
|
||||
let recently_used = ctx
|
||||
.user_recently_used_autoenv_untrust
|
||||
.load(Ordering::SeqCst);
|
||||
let auto = environment.autoenv(recently_used);
|
||||
ctx.user_recently_used_autoenv_untrust
|
||||
.store(false, Ordering::SeqCst);
|
||||
auto
|
||||
}
|
||||
|
||||
pub fn sync_env_vars(&mut self, ctx: &mut EvaluationContext) {
|
||||
let mut environment = self.env.lock();
|
||||
|
||||
if environment.env().is_some() {
|
||||
for (name, value) in ctx.with_host(|host| host.vars()) {
|
||||
if name != "path" && name != "PATH" {
|
||||
// account for new env vars present in the current session
|
||||
// that aren't loaded from config.
|
||||
environment.add_env(&name, &value);
|
||||
|
||||
// clear the env var from the session
|
||||
// we are about to replace them
|
||||
ctx.with_host(|host| host.env_rm(std::ffi::OsString::from(name)));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(variables) = environment.env() {
|
||||
for var in variables.row_entries() {
|
||||
if let Ok(string) = var.1.as_string() {
|
||||
ctx.with_host(|host| {
|
||||
host.env_set(
|
||||
std::ffi::OsString::from(var.0),
|
||||
std::ffi::OsString::from(&string),
|
||||
)
|
||||
});
|
||||
|
||||
ctx.scope.add_env_var_to_base(var.0, string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sync_path_vars(&mut self, ctx: &mut EvaluationContext) {
|
||||
let mut environment = self.env.lock();
|
||||
|
||||
if environment.path().is_some() {
|
||||
let native_paths = ctx.with_host(|host| host.env_get(std::ffi::OsString::from("PATH")));
|
||||
|
||||
if let Some(native_paths) = native_paths {
|
||||
environment.add_path(native_paths);
|
||||
|
||||
ctx.with_host(|host| {
|
||||
host.env_rm(std::ffi::OsString::from("PATH"));
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(new_paths) = environment.path() {
|
||||
let prepared = std::env::join_paths(
|
||||
new_paths
|
||||
.table_entries()
|
||||
.map(|p| p.as_string())
|
||||
.filter_map(Result::ok),
|
||||
);
|
||||
|
||||
if let Ok(paths_ready) = prepared {
|
||||
ctx.with_host(|host| {
|
||||
host.env_set(
|
||||
std::ffi::OsString::from("PATH"),
|
||||
std::ffi::OsString::from(&paths_ready),
|
||||
);
|
||||
});
|
||||
|
||||
ctx.scope
|
||||
.add_env_var_to_base("PATH", paths_ready.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn clear_env_vars(&mut self, ctx: &mut EvaluationContext) {
|
||||
for (key, _value) in ctx.with_host(|host| host.vars()) {
|
||||
if key != "path" && key != "PATH" {
|
||||
ctx.with_host(|host| host.env_rm(std::ffi::OsString::from(key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn clear_path_var(&mut self, ctx: &mut EvaluationContext) {
|
||||
ctx.with_host(|host| host.env_rm(std::ffi::OsString::from("PATH")));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::EnvironmentSyncer;
|
||||
use indexmap::IndexMap;
|
||||
use nu_data::config::tests::FakeConfig;
|
||||
use nu_engine::basic_evaluation_context;
|
||||
use nu_engine::Env;
|
||||
use nu_errors::ShellError;
|
||||
use nu_test_support::fs::Stub::FileWithContent;
|
||||
use nu_test_support::playground::Playground;
|
||||
use parking_lot::Mutex;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
// This test fails on Linux.
|
||||
// It's possible it has something to do with the fake configuration
|
||||
// TODO: More tests.
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[test]
|
||||
fn syncs_env_if_new_env_entry_is_added_to_an_existing_configuration() -> Result<(), ShellError>
|
||||
{
|
||||
let mut ctx = basic_evaluation_context()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let mut expected = IndexMap::new();
|
||||
expected.insert(
|
||||
"SHELL".to_string(),
|
||||
"/usr/bin/you_already_made_the_nu_choice".to_string(),
|
||||
);
|
||||
|
||||
Playground::setup("syncs_env_from_config_updated_test_1", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![
|
||||
FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
SHELL = "/usr/bin/you_already_made_the_nu_choice"
|
||||
"#,
|
||||
),
|
||||
FileWithContent(
|
||||
"updated_configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
SHELL = "/usr/bin/you_already_made_the_nu_choice"
|
||||
USER = "NUNO"
|
||||
"#,
|
||||
),
|
||||
]);
|
||||
|
||||
let file = dirs.test().join("configuration.toml");
|
||||
let new_file = dirs.test().join("updated_configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = EnvironmentSyncer::with_config(Box::new(fake_config));
|
||||
|
||||
// Here, the environment variables from the current session
|
||||
// are cleared since we will load and set them from the
|
||||
// configuration file
|
||||
actual.clear_env_vars(&mut ctx);
|
||||
|
||||
// Nu loads the environment variables from the configuration file
|
||||
actual.load_environment();
|
||||
actual.sync_env_vars(&mut ctx);
|
||||
|
||||
{
|
||||
let environment = actual.env.lock();
|
||||
let mut vars = IndexMap::new();
|
||||
environment
|
||||
.env()
|
||||
.expect("No variables in the environment.")
|
||||
.row_entries()
|
||||
.for_each(|(name, value)| {
|
||||
vars.insert(
|
||||
name.to_string(),
|
||||
value.as_string().expect("Couldn't convert to string"),
|
||||
);
|
||||
});
|
||||
|
||||
for k in expected.keys() {
|
||||
assert!(vars.contains_key(k));
|
||||
}
|
||||
}
|
||||
|
||||
assert!(!actual.did_config_change());
|
||||
|
||||
// Replacing the newer configuration file to the existing one.
|
||||
let new_config_contents = std::fs::read_to_string(new_file).expect("Failed");
|
||||
std::fs::write(&file, &new_config_contents).expect("Failed");
|
||||
|
||||
// A change has happened
|
||||
assert!(actual.did_config_change());
|
||||
|
||||
// Syncer should reload and add new envs
|
||||
actual.reload();
|
||||
actual.sync_env_vars(&mut ctx);
|
||||
|
||||
expected.insert("USER".to_string(), "NUNO".to_string());
|
||||
|
||||
{
|
||||
let environment = actual.env.lock();
|
||||
let mut vars = IndexMap::new();
|
||||
environment
|
||||
.env()
|
||||
.expect("No variables in the environment.")
|
||||
.row_entries()
|
||||
.for_each(|(name, value)| {
|
||||
vars.insert(
|
||||
name.to_string(),
|
||||
value.as_string().expect("Couldn't convert to string"),
|
||||
);
|
||||
});
|
||||
|
||||
for k in expected.keys() {
|
||||
assert!(vars.contains_key(k));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syncs_env_if_new_env_entry_in_session_is_not_in_configuration_file() -> Result<(), ShellError>
|
||||
{
|
||||
let mut ctx = basic_evaluation_context()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let mut expected = IndexMap::new();
|
||||
expected.insert(
|
||||
"SHELL".to_string(),
|
||||
"/usr/bin/you_already_made_the_nu_choice".to_string(),
|
||||
);
|
||||
expected.insert("USER".to_string(), "NUNO".to_string());
|
||||
|
||||
Playground::setup("syncs_env_test_1", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
SHELL = "/usr/bin/you_already_made_the_nu_choice"
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = EnvironmentSyncer::new();
|
||||
actual.set_config(Box::new(fake_config));
|
||||
|
||||
// Here, the environment variables from the current session
|
||||
// are cleared since we will load and set them from the
|
||||
// configuration file (if any)
|
||||
actual.clear_env_vars(&mut ctx);
|
||||
|
||||
// We explicitly simulate and add the USER variable to the current
|
||||
// session's environment variables with the value "NUNO".
|
||||
ctx.with_host(|test_host| {
|
||||
test_host.env_set(
|
||||
std::ffi::OsString::from("USER"),
|
||||
std::ffi::OsString::from("NUNO"),
|
||||
)
|
||||
});
|
||||
|
||||
// Nu loads the environment variables from the configuration file (if any)
|
||||
actual.load_environment();
|
||||
|
||||
// By this point, Nu has already loaded the environment variables
|
||||
// stored in the configuration file. Before continuing we check
|
||||
// if any new environment variables have been added from the ones loaded
|
||||
// in the configuration file.
|
||||
//
|
||||
// Nu sees the missing "USER" variable and accounts for it.
|
||||
actual.sync_env_vars(&mut ctx);
|
||||
|
||||
// Confirms session environment variables are replaced from Nu configuration file
|
||||
// including the newer one accounted for.
|
||||
ctx.with_host(|test_host| {
|
||||
let var_user = test_host
|
||||
.env_get(std::ffi::OsString::from("USER"))
|
||||
.expect("Couldn't get USER var from host.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
let var_shell = test_host
|
||||
.env_get(std::ffi::OsString::from("SHELL"))
|
||||
.expect("Couldn't get SHELL var from host.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
let mut found = IndexMap::new();
|
||||
found.insert("SHELL".to_string(), var_shell);
|
||||
found.insert("USER".to_string(), var_user);
|
||||
|
||||
for k in found.keys() {
|
||||
assert!(expected.contains_key(k));
|
||||
}
|
||||
});
|
||||
|
||||
// Now confirm in-memory environment variables synced appropriately
|
||||
// including the newer one accounted for.
|
||||
let environment = actual.env.lock();
|
||||
|
||||
let mut vars = IndexMap::new();
|
||||
environment
|
||||
.env()
|
||||
.expect("No variables in the environment.")
|
||||
.row_entries()
|
||||
.for_each(|(name, value)| {
|
||||
vars.insert(
|
||||
name.to_string(),
|
||||
value.as_string().expect("Couldn't convert to string"),
|
||||
);
|
||||
});
|
||||
for k in expected.keys() {
|
||||
assert!(vars.contains_key(k));
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nu_envs_have_higher_priority_and_does_not_get_overwritten() -> Result<(), ShellError> {
|
||||
let mut ctx = basic_evaluation_context()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let mut expected = IndexMap::new();
|
||||
expected.insert(
|
||||
"SHELL".to_string(),
|
||||
"/usr/bin/you_already_made_the_nu_choice".to_string(),
|
||||
);
|
||||
|
||||
Playground::setup("syncs_env_test_2", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
[env]
|
||||
SHELL = "/usr/bin/you_already_made_the_nu_choice"
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = EnvironmentSyncer::new();
|
||||
actual.set_config(Box::new(fake_config));
|
||||
|
||||
actual.clear_env_vars(&mut ctx);
|
||||
|
||||
ctx.with_host(|test_host| {
|
||||
test_host.env_set(
|
||||
std::ffi::OsString::from("SHELL"),
|
||||
std::ffi::OsString::from("/usr/bin/sh"),
|
||||
)
|
||||
});
|
||||
|
||||
actual.load_environment();
|
||||
actual.sync_env_vars(&mut ctx);
|
||||
|
||||
ctx.with_host(|test_host| {
|
||||
let var_shell = test_host
|
||||
.env_get(std::ffi::OsString::from("SHELL"))
|
||||
.expect("Couldn't get SHELL var from host.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
let mut found = IndexMap::new();
|
||||
found.insert("SHELL".to_string(), var_shell);
|
||||
|
||||
for k in found.keys() {
|
||||
assert!(expected.contains_key(k));
|
||||
}
|
||||
});
|
||||
|
||||
let environment = actual.env.lock();
|
||||
|
||||
let mut vars = IndexMap::new();
|
||||
environment
|
||||
.env()
|
||||
.expect("No variables in the environment.")
|
||||
.row_entries()
|
||||
.for_each(|(name, value)| {
|
||||
vars.insert(
|
||||
name.to_string(),
|
||||
value.as_string().expect("couldn't convert to string"),
|
||||
);
|
||||
});
|
||||
for k in expected.keys() {
|
||||
assert!(vars.contains_key(k));
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syncs_path_if_new_path_entry_in_session_is_not_in_configuration_file(
|
||||
) -> Result<(), ShellError> {
|
||||
let mut ctx = basic_evaluation_context()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let expected = std::env::join_paths(vec![
|
||||
PathBuf::from("/Users/andresrobalino/.volta/bin"),
|
||||
PathBuf::from("/Users/mosqueteros/bin"),
|
||||
PathBuf::from("/path/to/be/added"),
|
||||
])
|
||||
.expect("Couldn't join paths.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
Playground::setup("syncs_path_test_1", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
path = ["/Users/andresrobalino/.volta/bin", "/Users/mosqueteros/bin"]
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = EnvironmentSyncer::new();
|
||||
actual.set_config(Box::new(fake_config));
|
||||
|
||||
// Here, the environment variables from the current session
|
||||
// are cleared since we will load and set them from the
|
||||
// configuration file (if any)
|
||||
actual.clear_path_var(&mut ctx);
|
||||
|
||||
// We explicitly simulate and add the PATH variable to the current
|
||||
// session with the path "/path/to/be/added".
|
||||
ctx.with_host(|test_host| {
|
||||
test_host.env_set(
|
||||
std::ffi::OsString::from("PATH"),
|
||||
std::env::join_paths(vec![PathBuf::from("/path/to/be/added")])
|
||||
.expect("Couldn't join paths."),
|
||||
)
|
||||
});
|
||||
|
||||
// Nu loads the path variables from the configuration file (if any)
|
||||
actual.load_environment();
|
||||
|
||||
// By this point, Nu has already loaded environment path variable
|
||||
// stored in the configuration file. Before continuing we check
|
||||
// if any new paths have been added from the ones loaded in the
|
||||
// configuration file.
|
||||
//
|
||||
// Nu sees the missing "/path/to/be/added" and accounts for it.
|
||||
actual.sync_path_vars(&mut ctx);
|
||||
|
||||
ctx.with_host(|test_host| {
|
||||
let actual = test_host
|
||||
.env_get(std::ffi::OsString::from("PATH"))
|
||||
.expect("Couldn't get PATH var from host.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
});
|
||||
|
||||
let environment = actual.env.lock();
|
||||
|
||||
let paths = std::env::join_paths(
|
||||
&environment
|
||||
.path()
|
||||
.expect("No path variable in the environment.")
|
||||
.table_entries()
|
||||
.map(|value| value.as_string().expect("Couldn't convert to string"))
|
||||
.map(PathBuf::from)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.expect("Couldn't join paths.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
assert_eq!(paths, expected);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nu_paths_have_higher_priority_and_new_paths_get_appended_to_the_end(
|
||||
) -> Result<(), ShellError> {
|
||||
let mut ctx = basic_evaluation_context()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let expected = std::env::join_paths(vec![
|
||||
PathBuf::from("/Users/andresrobalino/.volta/bin"),
|
||||
PathBuf::from("/Users/mosqueteros/bin"),
|
||||
PathBuf::from("/path/to/be/added"),
|
||||
])
|
||||
.expect("Couldn't join paths.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
Playground::setup("syncs_path_test_2", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"configuration.toml",
|
||||
r#"
|
||||
path = ["/Users/andresrobalino/.volta/bin", "/Users/mosqueteros/bin"]
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let mut file = dirs.test().clone();
|
||||
file.push("configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = EnvironmentSyncer::new();
|
||||
actual.set_config(Box::new(fake_config));
|
||||
|
||||
actual.clear_path_var(&mut ctx);
|
||||
|
||||
ctx.with_host(|test_host| {
|
||||
test_host.env_set(
|
||||
std::ffi::OsString::from("PATH"),
|
||||
std::env::join_paths(vec![PathBuf::from("/path/to/be/added")])
|
||||
.expect("Couldn't join paths."),
|
||||
)
|
||||
});
|
||||
|
||||
actual.load_environment();
|
||||
actual.sync_path_vars(&mut ctx);
|
||||
|
||||
ctx.with_host(|test_host| {
|
||||
let actual = test_host
|
||||
.env_get(std::ffi::OsString::from("PATH"))
|
||||
.expect("Couldn't get PATH var from host.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
});
|
||||
|
||||
let environment = actual.env.lock();
|
||||
|
||||
let paths = std::env::join_paths(
|
||||
&environment
|
||||
.path()
|
||||
.expect("No path variable in the environment.")
|
||||
.table_entries()
|
||||
.map(|value| value.as_string().expect("Couldn't convert to string"))
|
||||
.map(PathBuf::from)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.expect("Couldn't join paths.")
|
||||
.into_string()
|
||||
.expect("Couldn't convert to string.");
|
||||
|
||||
assert_eq!(paths, expected);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,38 +1,64 @@
|
||||
use rustyline::{KeyCode, Modifiers};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
fn convert_keypress(keypress: KeyPress) -> rustyline::KeyPress {
|
||||
match keypress {
|
||||
KeyPress::UnknownEscSeq => rustyline::KeyPress::UnknownEscSeq,
|
||||
KeyPress::Backspace => rustyline::KeyPress::Backspace,
|
||||
KeyPress::BackTab => rustyline::KeyPress::BackTab,
|
||||
KeyPress::BracketedPasteStart => rustyline::KeyPress::BracketedPasteStart,
|
||||
KeyPress::BracketedPasteEnd => rustyline::KeyPress::BracketedPasteEnd,
|
||||
KeyPress::Char(c) => rustyline::KeyPress::Char(c),
|
||||
KeyPress::ControlDown => rustyline::KeyPress::ControlDown,
|
||||
KeyPress::ControlLeft => rustyline::KeyPress::ControlLeft,
|
||||
KeyPress::ControlRight => rustyline::KeyPress::ControlRight,
|
||||
KeyPress::ControlUp => rustyline::KeyPress::ControlUp,
|
||||
KeyPress::Ctrl(c) => rustyline::KeyPress::Ctrl(c),
|
||||
KeyPress::Delete => rustyline::KeyPress::Delete,
|
||||
KeyPress::Down => rustyline::KeyPress::Down,
|
||||
KeyPress::End => rustyline::KeyPress::End,
|
||||
KeyPress::Enter => rustyline::KeyPress::Enter,
|
||||
KeyPress::Esc => rustyline::KeyPress::Esc,
|
||||
KeyPress::F(u) => rustyline::KeyPress::F(u),
|
||||
KeyPress::Home => rustyline::KeyPress::Home,
|
||||
KeyPress::Insert => rustyline::KeyPress::Insert,
|
||||
KeyPress::Left => rustyline::KeyPress::Left,
|
||||
KeyPress::Meta(c) => rustyline::KeyPress::Meta(c),
|
||||
KeyPress::Null => rustyline::KeyPress::Null,
|
||||
KeyPress::PageDown => rustyline::KeyPress::PageDown,
|
||||
KeyPress::PageUp => rustyline::KeyPress::PageUp,
|
||||
KeyPress::Right => rustyline::KeyPress::Right,
|
||||
KeyPress::ShiftDown => rustyline::KeyPress::ShiftDown,
|
||||
KeyPress::ShiftLeft => rustyline::KeyPress::ShiftLeft,
|
||||
KeyPress::ShiftRight => rustyline::KeyPress::ShiftRight,
|
||||
KeyPress::ShiftUp => rustyline::KeyPress::ShiftUp,
|
||||
KeyPress::Tab => rustyline::KeyPress::Tab,
|
||||
KeyPress::Up => rustyline::KeyPress::Up,
|
||||
pub fn convert_keyevent(key_event: KeyEvent) -> 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)
|
||||
}
|
||||
KeyEvent::BracketedPasteEnd => {
|
||||
convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteEnd, None)
|
||||
}
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_rl_keyevent(key_event: KeyCode, modifier: Option<Modifiers>) -> rustyline::KeyEvent {
|
||||
rustyline::KeyEvent {
|
||||
0: key_event,
|
||||
1: modifier.unwrap_or(Modifiers::NONE),
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,7 +123,9 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
|
||||
match cmd {
|
||||
Cmd::Abort => rustyline::Cmd::Abort,
|
||||
Cmd::AcceptLine => rustyline::Cmd::AcceptLine,
|
||||
Cmd::AcceptOrInsertLine => rustyline::Cmd::AcceptOrInsertLine,
|
||||
Cmd::AcceptOrInsertLine => rustyline::Cmd::AcceptOrInsertLine {
|
||||
accept_in_the_middle: false,
|
||||
},
|
||||
Cmd::BeginningOfHistory => rustyline::Cmd::BeginningOfHistory,
|
||||
Cmd::CapitalizeWord => rustyline::Cmd::CapitalizeWord,
|
||||
Cmd::ClearScreen => rustyline::Cmd::ClearScreen,
|
||||
@ -140,18 +168,18 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_keybinding(keybinding: Keybinding) -> (rustyline::KeyPress, rustyline::Cmd) {
|
||||
fn convert_keybinding(keybinding: Keybinding) -> (rustyline::KeyEvent, rustyline::Cmd) {
|
||||
(
|
||||
convert_keypress(keybinding.key),
|
||||
convert_keyevent(keybinding.key),
|
||||
convert_cmd(keybinding.binding),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum KeyPress {
|
||||
pub enum KeyEvent {
|
||||
/// Unsupported escape sequence (on unix platform)
|
||||
UnknownEscSeq,
|
||||
/// ⌫ or `KeyPress::Ctrl('H')`
|
||||
/// ⌫ or `KeyEvent::Ctrl('H')`
|
||||
Backspace,
|
||||
/// ⇤ (usually Shift-Tab)
|
||||
BackTab,
|
||||
@ -177,9 +205,9 @@ pub enum KeyPress {
|
||||
Down,
|
||||
/// ⇲
|
||||
End,
|
||||
/// ↵ or `KeyPress::Ctrl('M')`
|
||||
/// ↵ or `KeyEvent::Ctrl('M')`
|
||||
Enter,
|
||||
/// Escape or `KeyPress::Ctrl('[')`
|
||||
/// Escape or `KeyEvent::Ctrl('[')`
|
||||
Esc,
|
||||
/// Function key
|
||||
F(u8),
|
||||
@ -191,7 +219,7 @@ pub enum KeyPress {
|
||||
Left,
|
||||
/// Escape-char or Alt-char
|
||||
Meta(char),
|
||||
/// `KeyPress::Char('\0')`
|
||||
/// `KeyEvent::Char('\0')`
|
||||
Null,
|
||||
/// ⇟
|
||||
PageDown,
|
||||
@ -207,7 +235,7 @@ pub enum KeyPress {
|
||||
ShiftRight,
|
||||
/// Shift-↑
|
||||
ShiftUp,
|
||||
/// ⇥ or `KeyPress::Ctrl('I')`
|
||||
/// ⇥ or `KeyEvent::Ctrl('I')`
|
||||
Tab,
|
||||
/// ↑ arrow key
|
||||
Up,
|
||||
@ -399,7 +427,7 @@ pub type RepeatCount = usize;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Keybinding {
|
||||
key: KeyPress,
|
||||
key: KeyEvent,
|
||||
binding: Cmd,
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,5 @@
|
||||
#![recursion_limit = "2048"]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate indexmap;
|
||||
|
||||
#[macro_use]
|
||||
mod prelude;
|
||||
|
||||
@ -16,7 +12,6 @@ extern crate quickcheck_macros;
|
||||
mod cli;
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
mod completion;
|
||||
mod env;
|
||||
mod format;
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
mod keybinding;
|
||||
@ -28,14 +23,14 @@ pub mod types;
|
||||
pub use crate::cli::cli;
|
||||
|
||||
pub use crate::cli::{parse_and_eval, register_plugins, run_script_file};
|
||||
pub use crate::cli::{NuScript, Options};
|
||||
|
||||
pub use crate::env::environment_syncer::EnvironmentSyncer;
|
||||
pub use nu_command::commands::default_context::create_default_context;
|
||||
pub use nu_data::config;
|
||||
pub use nu_data::dict::TaggedListBuilder;
|
||||
pub use nu_data::primitive;
|
||||
pub use nu_data::value;
|
||||
pub use nu_stream::{InputStream, InterruptibleStream, OutputStream};
|
||||
pub use nu_stream::{ActionStream, InputStream, InterruptibleStream};
|
||||
pub use nu_value_ext::ValueExt;
|
||||
pub use num_traits::cast::ToPrimitive;
|
||||
|
||||
|
@ -5,7 +5,10 @@ use std::error::Error;
|
||||
use crate::prelude::*;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use nu_command::script::LineResult;
|
||||
use nu_engine::script::LineResult;
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
use crate::keybinding::{convert_keyevent, KeyEvent};
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
use crate::shell::Helper;
|
||||
@ -16,7 +19,7 @@ use rustyline::{
|
||||
config::Configurer,
|
||||
config::{ColorMode, CompletionType, Config},
|
||||
error::ReadlineError,
|
||||
At, Cmd, Editor, KeyPress, Movement, Word,
|
||||
At, Cmd, Editor, Movement, Word,
|
||||
};
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
@ -40,22 +43,27 @@ pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
|
||||
#[cfg(not(windows))]
|
||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
|
||||
|
||||
let config = Config::builder().color_mode(ColorMode::Forced).build();
|
||||
let config = Config::builder()
|
||||
.check_cursor_position(true)
|
||||
.color_mode(ColorMode::Forced)
|
||||
.build();
|
||||
let mut rl: Editor<_> = Editor::with_config(config);
|
||||
|
||||
// add key bindings to move over a whole word with Ctrl+ArrowLeft and Ctrl+ArrowRight
|
||||
rl.bind_sequence(
|
||||
KeyPress::ControlLeft,
|
||||
convert_keyevent(KeyEvent::ControlLeft),
|
||||
Cmd::Move(Movement::BackwardWord(1, Word::Vi)),
|
||||
);
|
||||
rl.bind_sequence(
|
||||
KeyPress::ControlRight,
|
||||
convert_keyevent(KeyEvent::ControlRight),
|
||||
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
|
||||
);
|
||||
|
||||
// workaround for multiline-paste hang in rustyline (see https://github.com/kkawakam/rustyline/issues/202)
|
||||
rl.bind_sequence(KeyPress::BracketedPasteStart, rustyline::Cmd::Noop);
|
||||
|
||||
rl.bind_sequence(
|
||||
convert_keyevent(KeyEvent::BracketedPasteStart),
|
||||
rustyline::Cmd::Noop,
|
||||
);
|
||||
// Let's set the defaults up front and then override them later if the user indicates
|
||||
// defaults taken from here https://github.com/kkawakam/rustyline/blob/2fe886c9576c1ea13ca0e5808053ad491a6fe049/src/config.rs#L150-L167
|
||||
rl.set_max_history_size(100);
|
||||
@ -194,7 +202,7 @@ pub fn configure_rustyline_editor(
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub fn nu_line_editor_helper(
|
||||
context: &mut EvaluationContext,
|
||||
context: &EvaluationContext,
|
||||
config: &dyn nu_data::config::Conf,
|
||||
) -> crate::shell::Helper {
|
||||
let hinter = rustyline_hinter(config);
|
||||
@ -216,7 +224,7 @@ pub fn rustyline_hinter(
|
||||
Some(rustyline::hint::HistoryHinter {})
|
||||
}
|
||||
|
||||
pub fn configure_ctrl_c(_context: &mut EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
pub fn configure_ctrl_c(_context: &EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
#[cfg(feature = "ctrlc")]
|
||||
{
|
||||
let cc = _context.ctrl_c.clone();
|
||||
|
@ -8,25 +8,10 @@ macro_rules! return_err {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! stream {
|
||||
($($expr:expr),*) => {{
|
||||
let mut v = VecDeque::new();
|
||||
|
||||
$(
|
||||
v.push_back($expr);
|
||||
)*
|
||||
|
||||
v
|
||||
}}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! trace_out_stream {
|
||||
(target: $target:tt, $desc:tt = $expr:expr) => {{
|
||||
if log::log_enabled!(target: $target, log::Level::Trace) {
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
let objects = $expr.inspect(move |o| {
|
||||
trace!(
|
||||
target: $target,
|
||||
@ -46,13 +31,12 @@ macro_rules! trace_out_stream {
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) use futures::{Stream, StreamExt};
|
||||
pub(crate) use nu_engine::Host;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_errors::ShellError;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_protocol::outln;
|
||||
pub(crate) use nu_stream::OutputStream;
|
||||
pub(crate) use nu_stream::ActionStream;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_value_ext::ValueExt;
|
||||
#[allow(unused_imports)]
|
||||
@ -60,33 +44,16 @@ pub(crate) use std::sync::atomic::Ordering;
|
||||
|
||||
#[allow(clippy::clippy::wrong_self_convention)]
|
||||
pub trait FromInputStream {
|
||||
fn from_input_stream(self) -> OutputStream;
|
||||
fn from_input_stream(self) -> ActionStream;
|
||||
}
|
||||
|
||||
impl<T> FromInputStream for T
|
||||
where
|
||||
T: Stream<Item = nu_protocol::Value> + Send + 'static,
|
||||
T: Iterator<Item = nu_protocol::Value> + Send + Sync + 'static,
|
||||
{
|
||||
fn from_input_stream(self) -> OutputStream {
|
||||
OutputStream {
|
||||
values: self.map(nu_protocol::ReturnSuccess::value).boxed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::clippy::wrong_self_convention)]
|
||||
pub trait ToOutputStream {
|
||||
fn to_output_stream(self) -> OutputStream;
|
||||
}
|
||||
|
||||
impl<T, U> ToOutputStream for T
|
||||
where
|
||||
T: Stream<Item = U> + Send + 'static,
|
||||
U: Into<nu_protocol::ReturnValue>,
|
||||
{
|
||||
fn to_output_stream(self) -> OutputStream {
|
||||
OutputStream {
|
||||
values: self.map(|item| item.into()).boxed(),
|
||||
fn from_input_stream(self) -> ActionStream {
|
||||
ActionStream {
|
||||
values: Box::new(self.map(nu_protocol::ReturnSuccess::value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::completion;
|
||||
use crate::shell::completer::NuCompleter;
|
||||
use nu_ansi_term::Color;
|
||||
use nu_engine::{DefaultPalette, EvaluationContext, Painter};
|
||||
use nu_source::{Tag, Tagged};
|
||||
use std::borrow::Cow::{self, Owned};
|
||||
@ -57,6 +58,7 @@ impl rustyline::completion::Completer for Helper {
|
||||
}
|
||||
|
||||
impl rustyline::hint::Hinter for Helper {
|
||||
type Hint = String;
|
||||
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
|
||||
self.hinter.as_ref().and_then(|h| h.hint(line, pos, &ctx))
|
||||
}
|
||||
@ -78,7 +80,7 @@ impl rustyline::highlight::Highlighter for Helper {
|
||||
}
|
||||
|
||||
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
|
||||
Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
|
||||
Owned(Color::DarkGray.prefix().to_string() + hint + nu_ansi_term::ansi::RESET)
|
||||
}
|
||||
|
||||
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
|
||||
|
@ -285,7 +285,7 @@ fn get_shape_of_expr(expr: &SpannedExpression) -> Option<SyntaxShape> {
|
||||
nu_protocol::hir::Literal::Bare(_) => Some(SyntaxShape::String),
|
||||
}
|
||||
}
|
||||
//Synthetic are expressions that are generated by the parser and not inputed by the user
|
||||
//Synthetic are expressions that are generated by the parser and not inputted by the user
|
||||
//ExternalWord is anything sent to external commands (?)
|
||||
Expression::ExternalWord => Some(SyntaxShape::String),
|
||||
Expression::Synthetic(_) => Some(SyntaxShape::String),
|
||||
@ -387,7 +387,7 @@ impl VarSyntaxShapeDeductor {
|
||||
}
|
||||
|
||||
fn infer_shape(&mut self, block: &Block, scope: &Scope) -> Result<(), ShellError> {
|
||||
trace!("Infering vars in shape");
|
||||
trace!("Inferring vars in shape");
|
||||
for group in &block.block {
|
||||
for pipeline in &group.pipelines {
|
||||
self.infer_pipeline(pipeline, scope)?;
|
||||
@ -397,7 +397,7 @@ impl VarSyntaxShapeDeductor {
|
||||
}
|
||||
|
||||
pub fn infer_pipeline(&mut self, pipeline: &Pipeline, scope: &Scope) -> Result<(), ShellError> {
|
||||
trace!("Infering vars in pipeline");
|
||||
trace!("Inferring vars in pipeline");
|
||||
for (cmd_pipeline_idx, classified) in pipeline.list.iter().enumerate() {
|
||||
match &classified {
|
||||
ClassifiedCommand::Internal(internal) => {
|
||||
@ -429,7 +429,7 @@ impl VarSyntaxShapeDeductor {
|
||||
}
|
||||
}
|
||||
if let Some(named) = &internal.args.named {
|
||||
trace!("Infering vars in named exprs");
|
||||
trace!("Inferring vars in named exprs");
|
||||
for (_name, val) in named.iter() {
|
||||
if let NamedValue::Value(_, named_expr) = val {
|
||||
self.infer_shapes_in_expr(
|
||||
@ -443,7 +443,7 @@ impl VarSyntaxShapeDeductor {
|
||||
}
|
||||
ClassifiedCommand::Expr(spanned_expr) => {
|
||||
trace!(
|
||||
"Infering shapes in ClassifiedCommand::Expr: {:?}",
|
||||
"Inferring shapes in ClassifiedCommand::Expr: {:?}",
|
||||
spanned_expr
|
||||
);
|
||||
self.infer_shapes_in_expr((cmd_pipeline_idx, pipeline), spanned_expr, scope)?;
|
||||
@ -459,7 +459,7 @@ impl VarSyntaxShapeDeductor {
|
||||
positionals: &[SpannedExpression],
|
||||
signature: &Signature,
|
||||
) -> Result<(), ShellError> {
|
||||
trace!("Infering vars in positionals");
|
||||
trace!("Inferring vars in positionals");
|
||||
//TODO currently correct inference for optional positionals is not implemented.
|
||||
// See https://github.com/nushell/nushell/pull/2486 for a discussion about this
|
||||
// For now we assume every variable in an optional positional is used as this optional
|
||||
@ -500,7 +500,7 @@ impl VarSyntaxShapeDeductor {
|
||||
named: &NamedArguments,
|
||||
signature: &Signature,
|
||||
) -> Result<(), ShellError> {
|
||||
trace!("Infering vars in named");
|
||||
trace!("Inferring vars in named");
|
||||
for (name, val) in named.iter() {
|
||||
if let NamedValue::Value(span, spanned_expr) = &val {
|
||||
if let Expression::Variable(var_name, _) = &spanned_expr.expr {
|
||||
@ -534,15 +534,15 @@ impl VarSyntaxShapeDeductor {
|
||||
) -> Result<(), ShellError> {
|
||||
match &spanned_expr.expr {
|
||||
Expression::Binary(_) => {
|
||||
trace!("Infering vars in bin expr");
|
||||
trace!("Inferring vars in bin expr");
|
||||
self.infer_shapes_in_binary_expr((pipeline_idx, pipeline), spanned_expr, scope)?;
|
||||
}
|
||||
Expression::Block(b) => {
|
||||
trace!("Infering vars in block");
|
||||
trace!("Inferring vars in block");
|
||||
self.infer_shape(&b, scope)?;
|
||||
}
|
||||
Expression::Path(path) => {
|
||||
trace!("Infering vars in path");
|
||||
trace!("Inferring vars in path");
|
||||
match &path.head.expr {
|
||||
//PathMember can't be var yet (?)
|
||||
//TODO Iterate over path parts and find var when implemented
|
||||
@ -560,7 +560,7 @@ impl VarSyntaxShapeDeductor {
|
||||
}
|
||||
}
|
||||
Expression::Range(range) => {
|
||||
trace!("Infering vars in range");
|
||||
trace!("Inferring vars in range");
|
||||
if let Some(range_left) = &range.left {
|
||||
if let Expression::Variable(var_name, _) = &range_left.expr {
|
||||
self.checked_insert(
|
||||
@ -585,13 +585,13 @@ impl VarSyntaxShapeDeductor {
|
||||
}
|
||||
}
|
||||
Expression::List(inner_exprs) => {
|
||||
trace!("Infering vars in list");
|
||||
trace!("Inferring vars in list");
|
||||
for expr in inner_exprs {
|
||||
self.infer_shapes_in_expr((pipeline_idx, pipeline), expr, scope)?;
|
||||
}
|
||||
}
|
||||
Expression::Invocation(invoc) => {
|
||||
trace!("Infering vars in invocation: {:?}", invoc);
|
||||
trace!("Inferring vars in invocation: {:?}", invoc);
|
||||
self.infer_shape(invoc, scope)?;
|
||||
}
|
||||
Expression::Table(header, _rows) => {
|
||||
@ -738,7 +738,7 @@ impl VarSyntaxShapeDeductor {
|
||||
(pipeline_idx, pipeline): (usize, &Pipeline),
|
||||
scope: &Scope,
|
||||
) -> Result<(), ShellError> {
|
||||
trace!("Infering shapes between var {:?} and expr {:?}", var, expr);
|
||||
trace!("Inferring shapes between var {:?} and expr {:?}", var, expr);
|
||||
let bin = spanned_to_binary(bin_spanned);
|
||||
if let Expression::Literal(Literal::Operator(op)) = bin.op.expr {
|
||||
match &op {
|
||||
|
@ -5,30 +5,28 @@ description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-command"
|
||||
version = "0.28.0"
|
||||
version = "0.30.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-data = { version = "0.28.0", path = "../nu-data" }
|
||||
nu-engine = { version = "0.28.0", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.28.0", path = "../nu-errors" }
|
||||
nu-json = { version = "0.28.0", path = "../nu-json" }
|
||||
nu-parser = { version = "0.28.0", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.28.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.28.0", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.28.0", path = "../nu-source" }
|
||||
nu-stream = { version = "0.28.0", path = "../nu-stream" }
|
||||
nu-table = { version = "0.28.0", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.28.0", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.28.0", path = "../nu-value-ext" }
|
||||
nu-ansi-term = { version = "0.28.0", path = "../nu-ansi-term" }
|
||||
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" }
|
||||
|
||||
Inflector = "0.11"
|
||||
arboard = { version = "1.1.0", optional = true }
|
||||
async-recursion = "0.3.2"
|
||||
async-trait = "0.1.42"
|
||||
base64 = "0.13.0"
|
||||
bigdecimal = { version = "0.2.0", features = ["serde"] }
|
||||
byte-unit = "4.0.9"
|
||||
@ -51,8 +49,6 @@ encoding_rs = "0.8.28"
|
||||
filesize = "0.2.0"
|
||||
fs_extra = "1.2.0"
|
||||
futures = { version = "0.3.12", features = ["compat", "io-compat"] }
|
||||
futures-util = "0.3.12"
|
||||
futures_codec = "0.4.1"
|
||||
getset = "0.1.1"
|
||||
glob = "0.3.0"
|
||||
htmlescape = "0.3.1"
|
||||
@ -62,6 +58,7 @@ indexmap = { version = "1.6.1", features = ["serde-1"] }
|
||||
itertools = "0.10.0"
|
||||
lazy_static = "1.*"
|
||||
log = "0.4.14"
|
||||
md5 = "0.7.0"
|
||||
meval = "0.2.0"
|
||||
minus = { version = "3.3.0", optional = true, features = ["async_std_lib", "search"] }
|
||||
num-bigint = { version = "0.3.1", features = ["serde"] }
|
||||
@ -78,7 +75,7 @@ rayon = "1.5.0"
|
||||
regex = "1.4.3"
|
||||
roxmltree = "0.14.0"
|
||||
rust-embed = "5.9.0"
|
||||
rustyline = { version = "7.1.0", optional = true }
|
||||
rustyline = { version = "8.0.0", optional = true }
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
serde_bytes = "0.11.5"
|
||||
serde_ini = "0.2.0"
|
||||
@ -124,6 +121,7 @@ shadow-rs = "0.5"
|
||||
[dev-dependencies]
|
||||
quickcheck = "1.0.3"
|
||||
quickcheck_macros = "1.0.0"
|
||||
hamcrest2 = "0.3.0"
|
||||
|
||||
[features]
|
||||
clipboard-cli = ["arboard"]
|
||||
|
@ -4,7 +4,9 @@ pub(crate) mod macros;
|
||||
mod from_delimited_data;
|
||||
mod to_delimited_data;
|
||||
|
||||
pub(crate) mod all;
|
||||
pub(crate) mod ansi;
|
||||
pub(crate) mod any;
|
||||
pub(crate) mod append;
|
||||
pub(crate) mod args;
|
||||
pub mod autoenv;
|
||||
@ -20,11 +22,9 @@ pub(crate) mod chart;
|
||||
pub(crate) mod classified;
|
||||
#[cfg(feature = "clipboard-cli")]
|
||||
pub(crate) mod clip;
|
||||
pub mod command;
|
||||
pub(crate) mod compact;
|
||||
pub(crate) mod config;
|
||||
pub(crate) mod constants;
|
||||
pub(crate) mod count;
|
||||
pub(crate) mod cp;
|
||||
pub(crate) mod date;
|
||||
pub(crate) mod debug;
|
||||
@ -70,9 +70,10 @@ pub(crate) mod histogram;
|
||||
pub(crate) mod history;
|
||||
pub(crate) mod if_;
|
||||
pub(crate) mod insert;
|
||||
pub(crate) mod into_int;
|
||||
pub(crate) mod into;
|
||||
pub(crate) mod keep;
|
||||
pub(crate) mod last;
|
||||
pub(crate) mod length;
|
||||
pub(crate) mod let_;
|
||||
pub(crate) mod let_env;
|
||||
pub(crate) mod lines;
|
||||
@ -153,9 +154,8 @@ pub(crate) use char_::Char;
|
||||
pub(crate) use chart::Chart;
|
||||
pub(crate) use compact::Compact;
|
||||
pub(crate) use config::{
|
||||
Config, ConfigClear, ConfigGet, ConfigLoad, ConfigPath, ConfigRemove, ConfigSet, ConfigSetInto,
|
||||
Config, ConfigClear, ConfigGet, ConfigPath, ConfigRemove, ConfigSet, ConfigSetInto,
|
||||
};
|
||||
pub(crate) use count::Count;
|
||||
pub(crate) use cp::Cpy;
|
||||
pub(crate) use date::{Date, DateFormat, DateListTimeZone, DateNow, DateToTable, DateToTimeZone};
|
||||
pub(crate) use debug::Debug;
|
||||
@ -171,6 +171,8 @@ pub(crate) use each::EachWindow;
|
||||
pub(crate) use echo::Echo;
|
||||
pub(crate) use empty::Command as Empty;
|
||||
pub(crate) use if_::If;
|
||||
pub(crate) use into::Into;
|
||||
pub(crate) use into::IntoInt;
|
||||
pub(crate) use nu::NuPlugin;
|
||||
pub(crate) use update::Command as Update;
|
||||
pub(crate) mod kill;
|
||||
@ -178,6 +180,8 @@ pub(crate) use kill::Kill;
|
||||
pub(crate) mod clear;
|
||||
pub(crate) use clear::Clear;
|
||||
pub(crate) mod touch;
|
||||
pub(crate) use all::Command as All;
|
||||
pub(crate) use any::Command as Any;
|
||||
pub(crate) use enter::Enter;
|
||||
pub(crate) use every::Every;
|
||||
pub(crate) use exec::Exec;
|
||||
@ -186,40 +190,41 @@ pub(crate) use first::First;
|
||||
pub(crate) use flatten::Command as Flatten;
|
||||
pub(crate) use format::{FileSize, Format};
|
||||
pub(crate) use from::From;
|
||||
pub(crate) use from_csv::FromCSV;
|
||||
pub(crate) use from_eml::FromEML;
|
||||
pub(crate) use from_csv::FromCsv;
|
||||
pub(crate) use from_eml::FromEml;
|
||||
pub(crate) use from_ics::FromIcs;
|
||||
pub(crate) use from_ini::FromINI;
|
||||
pub(crate) use from_json::FromJSON;
|
||||
pub(crate) use from_ods::FromODS;
|
||||
pub(crate) use from_ssv::FromSSV;
|
||||
pub(crate) use from_toml::FromTOML;
|
||||
pub(crate) use from_tsv::FromTSV;
|
||||
pub(crate) use from_url::FromURL;
|
||||
pub(crate) use from_ini::FromIni;
|
||||
pub(crate) use from_json::FromJson;
|
||||
pub(crate) use from_ods::FromOds;
|
||||
pub(crate) use from_ssv::FromSsv;
|
||||
pub(crate) use from_toml::FromToml;
|
||||
pub(crate) use from_tsv::FromTsv;
|
||||
pub(crate) use from_url::FromUrl;
|
||||
pub(crate) use from_vcf::FromVcf;
|
||||
pub(crate) use from_xlsx::FromXLSX;
|
||||
pub(crate) use from_xml::FromXML;
|
||||
pub(crate) use from_yaml::FromYAML;
|
||||
pub(crate) use from_yaml::FromYML;
|
||||
pub(crate) use from_xlsx::FromXlsx;
|
||||
pub(crate) use from_xml::FromXml;
|
||||
pub(crate) use from_yaml::FromYaml;
|
||||
pub(crate) use from_yaml::FromYml;
|
||||
pub(crate) use get::Command as Get;
|
||||
pub(crate) use group_by::Command as GroupBy;
|
||||
pub(crate) use group_by_date::GroupByDate;
|
||||
pub(crate) use hash_::{Hash, HashBase64};
|
||||
pub(crate) use hash_::{Hash, HashBase64, HashMd5};
|
||||
pub(crate) use headers::Headers;
|
||||
pub(crate) use help::Help;
|
||||
pub(crate) use histogram::Histogram;
|
||||
pub(crate) use history::History;
|
||||
pub(crate) use insert::Command as Insert;
|
||||
pub(crate) use into_int::IntoInt;
|
||||
pub(crate) use keep::{Keep, KeepUntil, KeepWhile};
|
||||
pub(crate) use last::Last;
|
||||
pub(crate) use length::Length;
|
||||
pub(crate) use let_::Let;
|
||||
pub(crate) use let_env::LetEnv;
|
||||
pub(crate) use lines::Lines;
|
||||
pub(crate) use ls::Ls;
|
||||
pub(crate) use math::{
|
||||
Math, MathAbs, MathAverage, MathCeil, MathEval, MathFloor, MathMaximum, MathMedian,
|
||||
MathMinimum, MathMode, MathProduct, MathRound, MathStddev, MathSummation, MathVariance,
|
||||
MathMinimum, MathMode, MathProduct, MathRound, MathSqrt, MathStddev, MathSummation,
|
||||
MathVariance,
|
||||
};
|
||||
pub(crate) use merge::Merge;
|
||||
pub(crate) use mkdir::Mkdir;
|
||||
@ -229,8 +234,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, PathExtension, PathFilestem,
|
||||
PathJoin, PathType,
|
||||
PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathJoin, PathParse, PathSplit,
|
||||
PathType,
|
||||
};
|
||||
pub(crate) use pivot::Pivot;
|
||||
pub(crate) use prepend::Prepend;
|
||||
@ -273,20 +278,20 @@ pub(crate) use table::Table;
|
||||
pub(crate) use tags::Tags;
|
||||
pub(crate) use termsize::TermSize;
|
||||
pub(crate) use to::To;
|
||||
pub(crate) use to_csv::ToCSV;
|
||||
pub(crate) use to_html::ToHTML;
|
||||
pub(crate) use to_json::ToJSON;
|
||||
pub(crate) use to_csv::ToCsv;
|
||||
pub(crate) use to_html::ToHtml;
|
||||
pub(crate) use to_json::ToJson;
|
||||
pub(crate) use to_md::Command as ToMarkdown;
|
||||
pub(crate) use to_toml::ToTOML;
|
||||
pub(crate) use to_tsv::ToTSV;
|
||||
pub(crate) use to_url::ToURL;
|
||||
pub(crate) use to_xml::ToXML;
|
||||
pub(crate) use to_yaml::ToYAML;
|
||||
pub(crate) use to_toml::ToToml;
|
||||
pub(crate) use to_tsv::ToTsv;
|
||||
pub(crate) use to_url::ToUrl;
|
||||
pub(crate) use to_xml::ToXml;
|
||||
pub(crate) use to_yaml::ToYaml;
|
||||
pub(crate) use touch::Touch;
|
||||
pub(crate) use uniq::Uniq;
|
||||
pub(crate) use url_::{UrlCommand, UrlHost, UrlPath, UrlQuery, UrlScheme};
|
||||
pub(crate) use version::Version;
|
||||
pub(crate) use where_::Where;
|
||||
pub(crate) use where_::Command as Where;
|
||||
pub(crate) use which_::Which;
|
||||
pub(crate) use with_env::WithEnv;
|
||||
pub(crate) use wrap::Wrap;
|
||||
|
136
crates/nu-command/src/commands/all.rs
Normal file
136
crates/nu-command/src/commands/all.rs
Normal file
@ -0,0 +1,136 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::evaluate_baseline_expr;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
struct AllArgs {
|
||||
predicate: CapturedBlock,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"all?"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("all?").required(
|
||||
"condition",
|
||||
SyntaxShape::RowCondition,
|
||||
"the condition that must match",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Find if the table rows matches the condition."
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
all(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
use nu_protocol::Value;
|
||||
|
||||
vec![
|
||||
Example {
|
||||
description: "Find if services are running",
|
||||
example: "echo [[status]; [UP] [UP]] | all? status == UP",
|
||||
result: Some(vec![Value::from(true)]),
|
||||
},
|
||||
Example {
|
||||
description: "Check that all values are even",
|
||||
example: "echo [2 4 6 8] | all? $(= $it mod 2) == 0",
|
||||
result: Some(vec![Value::from(true)]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn all(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let args = args.evaluate_once()?;
|
||||
let all_args = AllArgs {
|
||||
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 all_args.predicate.block.block.len() != 1 {
|
||||
return err;
|
||||
}
|
||||
match all_args.predicate.block.block[0].pipelines.get(0) {
|
||||
Some(item) => match item.list.get(0) {
|
||||
Some(ClassifiedCommand::Expr(expr)) => expr.clone(),
|
||||
_ => {
|
||||
return err;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let scope = args.scope.clone();
|
||||
|
||||
let init = Ok(InputStream::one(
|
||||
UntaggedValue::boolean(true).into_value(&tag),
|
||||
));
|
||||
|
||||
// Variables in nu are immutable. Having the same variable accross invocations
|
||||
// of evaluate_baseline_expr does not mutate the variables and those each
|
||||
// invocations are independent of each other!
|
||||
scope.enter_scope();
|
||||
scope.add_vars(&all_args.predicate.captured.entries);
|
||||
let result = args.input.fold(init, move |acc, row| {
|
||||
let condition = condition.clone();
|
||||
let ctx = ctx.clone();
|
||||
ctx.scope.add_var("$it", row);
|
||||
|
||||
let condition = evaluate_baseline_expr(&condition, &ctx);
|
||||
|
||||
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),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
});
|
||||
scope.exit_scope();
|
||||
|
||||
Ok(result?.to_output_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Command;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Command {})
|
||||
}
|
||||
}
|
@ -7,14 +7,6 @@ use nu_source::Tagged;
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AnsiArgs {
|
||||
code: Value,
|
||||
escape: Option<Tagged<String>>,
|
||||
osc: Option<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"ansi"
|
||||
@ -120,8 +112,12 @@ Format: #
|
||||
]
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (AnsiArgs { code, escape, osc }, _) = args.process().await?;
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let code: Option<Tagged<String>> = args.opt(0)?;
|
||||
let escape: Option<Tagged<String>> = args.get_flag("escape")?;
|
||||
let osc: Option<Tagged<String>> = args.get_flag("osc")?;
|
||||
|
||||
if let Some(e) = escape {
|
||||
let esc_vec: Vec<char> = e.item.chars().collect();
|
||||
@ -133,7 +129,7 @@ Format: #
|
||||
));
|
||||
}
|
||||
let output = format!("\x1b[{}", e.item);
|
||||
return Ok(OutputStream::one(ReturnSuccess::value(
|
||||
return Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(e.tag()),
|
||||
)));
|
||||
}
|
||||
@ -151,30 +147,37 @@ 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(OutputStream::one(ReturnSuccess::value(
|
||||
return Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(o.tag()),
|
||||
)));
|
||||
}
|
||||
|
||||
let code_string = code.as_string()?;
|
||||
let ansi_code = str_to_ansi(code_string);
|
||||
if let Some(code) = code {
|
||||
let ansi_code = str_to_ansi(&code.item);
|
||||
|
||||
if let Some(output) = ansi_code {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(code.tag()),
|
||||
)))
|
||||
if let Some(output) = ansi_code {
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(code.tag()),
|
||||
)))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Unknown ansi code",
|
||||
"unknown ansi code",
|
||||
code.tag(),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Unknown ansi code",
|
||||
"unknown ansi code",
|
||||
code.tag(),
|
||||
"Expected ansi code",
|
||||
"expect ansi code",
|
||||
args.call_info.name_tag.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn str_to_ansi(s: String) -> Option<String> {
|
||||
match s.as_str() {
|
||||
pub fn str_to_ansi(s: &str) -> Option<String> {
|
||||
match s {
|
||||
"g" | "green" => Some(Color::Green.prefix().to_string()),
|
||||
"gb" | "green_bold" => Some(Color::Green.bold().prefix().to_string()),
|
||||
"gu" | "green_underline" => Some(Color::Green.underline().prefix().to_string()),
|
||||
@ -330,7 +333,7 @@ pub fn str_to_ansi(s: String) -> Option<String> {
|
||||
|
||||
// Ansi RGB - Needs to be 32;2;r;g;b or 48;2;r;g;b
|
||||
// assuming the rgb will be passed via command and no here
|
||||
"rgb_fg" => Some("\x1b[32;2;".to_string()),
|
||||
"rgb_fg" => Some("\x1b[38;2;".to_string()),
|
||||
"rgb_bg" => Some("\x1b[48;2;".to_string()),
|
||||
|
||||
// Ansi color index - Needs 38;5;idx or 48;5;idx where idx = 0 to 255
|
||||
|
@ -15,7 +15,6 @@ struct Arguments {
|
||||
rest: Vec<ColumnPath>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"ansi strip"
|
||||
@ -32,8 +31,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"strip ansi escape sequences from string"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
operate(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
operate(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -45,8 +44,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { rest }, input) = args.process().await?;
|
||||
fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { rest }, input) = args.process()?;
|
||||
let column_paths: Vec<_> = rest;
|
||||
|
||||
Ok(input
|
||||
@ -66,7 +65,7 @@ async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
ReturnSuccess::value(ret)
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
|
134
crates/nu-command/src/commands/any.rs
Normal file
134
crates/nu-command/src/commands/any.rs
Normal file
@ -0,0 +1,134 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::evaluate_baseline_expr;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, hir::ClassifiedCommand, Signature, SyntaxShape, UntaggedValue,
|
||||
};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
block: CapturedBlock,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"any?"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("any?").required(
|
||||
"condition",
|
||||
SyntaxShape::RowCondition,
|
||||
"the condition that must match",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Find if the table rows matches the condition."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
any(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
use nu_protocol::Value;
|
||||
|
||||
vec![
|
||||
Example {
|
||||
description: "Find if a service is not running",
|
||||
example: "echo [[status]; [UP] [DOWN] [UP]] | any? status == DOWN",
|
||||
result: Some(vec![Value::from(true)]),
|
||||
},
|
||||
Example {
|
||||
description: "Check if any of the values is odd",
|
||||
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));
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (Arguments { block }, input) = args.process()?;
|
||||
|
||||
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,
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let cond = 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);
|
||||
|
||||
let condition = evaluate_baseline_expr(&condition, &*ctx);
|
||||
ctx.scope.exit_scope();
|
||||
|
||||
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()?;
|
||||
|
||||
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),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
})?
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Command;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Command {})
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
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};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Arguments {
|
||||
@ -10,7 +10,6 @@ struct Arguments {
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"append"
|
||||
@ -28,13 +27,14 @@ impl WholeStreamCommand for Command {
|
||||
"Append a row to the table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { mut value }, input) = args.process().await?;
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { mut value }, mut input) = args.process()?;
|
||||
|
||||
let input: Vec<Value> = input.collect().await;
|
||||
let mut prepend = vec![];
|
||||
|
||||
if let Some(first) = input.get(0) {
|
||||
if let Some(first) = input.next() {
|
||||
value.tag = first.tag();
|
||||
prepend.push(first);
|
||||
}
|
||||
|
||||
// Checks if we are trying to append a row literal
|
||||
@ -48,18 +48,13 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(futures::stream::iter(
|
||||
input
|
||||
.into_iter()
|
||||
.chain(vec![value])
|
||||
.map(ReturnSuccess::value),
|
||||
)
|
||||
.to_output_stream())
|
||||
Ok(prepend
|
||||
.into_iter()
|
||||
.chain(input.into_iter().chain(vec![value]))
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
use nu_protocol::row;
|
||||
|
||||
vec![
|
||||
Example {
|
||||
description: "Add values to the end of the table",
|
||||
|
@ -2,50 +2,8 @@ use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
pub struct Autoenv;
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Default)]
|
||||
pub struct Trusted {
|
||||
pub files: IndexMap<String, Vec<u8>>,
|
||||
}
|
||||
impl Trusted {
|
||||
pub fn new() -> Self {
|
||||
Trusted {
|
||||
files: IndexMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn file_is_trusted(nu_env_file: &Path, content: &[u8]) -> Result<bool, ShellError> {
|
||||
let contentdigest = Sha256::digest(&content).as_slice().to_vec();
|
||||
let nufile = std::fs::canonicalize(nu_env_file)?;
|
||||
|
||||
let trusted = read_trusted()?;
|
||||
|
||||
Ok(trusted.files.get(&nufile.to_string_lossy().to_string()) == Some(&contentdigest))
|
||||
}
|
||||
|
||||
pub fn read_trusted() -> Result<Trusted, ShellError> {
|
||||
let config_path = config::default_path_for(&Some(PathBuf::from("nu-env.toml")))?;
|
||||
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(config_path)
|
||||
.map_err(|_| ShellError::untagged_runtime_error("Couldn't open nu-env.toml"))?;
|
||||
let mut doc = String::new();
|
||||
file.read_to_string(&mut doc)?;
|
||||
|
||||
let allowed = toml::de::from_str(doc.as_str()).unwrap_or_else(|_| Trusted::new());
|
||||
Ok(allowed)
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Autoenv {
|
||||
fn name(&self) -> &str {
|
||||
"autoenv"
|
||||
@ -56,18 +14,19 @@ impl WholeStreamCommand for Autoenv {
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
// "Mark a .nu-env file in a directory as trusted. Needs to be re-run after each change to the file or its filepath."
|
||||
r#"Create a file called .nu-env in any directory and run 'autoenv trust' to let nushell read it when entering the directory.
|
||||
The file can contain several optional sections:
|
||||
env: environment variables to set when visiting the directory. The variables are unset after leaving the directory and any overwritten values are restored.
|
||||
scriptvars: environment variables that should be set to the return value of a script. After they have been set, they behave in the same way as variables set in the env section.
|
||||
scripts: scripts to run when entering the directory or leaving it."#
|
||||
r#"Create a file called .nu-env in any directory and run 'autoenv trust' to let nushell load it when entering the directory.
|
||||
The .nu-env file has the same format as your $HOME/nu/config.toml file. By loading a .nu-env file the following applies:
|
||||
- environment variables (section \"[env]\") are loaded from the .nu-env file. Those env variables only exist in this directory (and children directories)
|
||||
- the \"startup\" commands are run when entering the directory
|
||||
- the \"on_exit\" commands are run when leaving the directory
|
||||
"#
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("autoenv")
|
||||
}
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
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()),
|
||||
)))
|
||||
}
|
||||
@ -76,15 +35,12 @@ The file can contain several optional sections:
|
||||
vec![Example {
|
||||
description: "Example .nu-env file",
|
||||
example: r#"cat .nu-env
|
||||
startup = ["echo ...entering the directory", "echo 1 2 3"]
|
||||
on_exit = ["echo ...leaving the directory"]
|
||||
|
||||
[env]
|
||||
mykey = "myvalue"
|
||||
|
||||
[scriptvars]
|
||||
myscript = "echo myval"
|
||||
|
||||
[scripts]
|
||||
entryscripts = ["touch hello.txt", "touch hello2.txt"]
|
||||
exitscripts = ["touch bye.txt"]"#,
|
||||
"#,
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::autoenv::read_trusted;
|
||||
use crate::prelude::*;
|
||||
use nu_data::config::read_trusted;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::SyntaxShape;
|
||||
@ -8,7 +8,6 @@ use sha2::{Digest, Sha256};
|
||||
use std::{fs, path::PathBuf};
|
||||
pub struct AutoenvTrust;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for AutoenvTrust {
|
||||
fn name(&self) -> &str {
|
||||
"autoenv trust"
|
||||
@ -22,11 +21,11 @@ impl WholeStreamCommand for AutoenvTrust {
|
||||
"Trust a .nu-env file in the current or given directory"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
|
||||
let file_to_trust = match args.call_info.evaluate(&ctx).await?.args.nth(0) {
|
||||
let file_to_trust = match args.call_info.evaluate(&ctx)?.args.nth(0) {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
||||
tag: _,
|
||||
@ -56,7 +55,7 @@ impl WholeStreamCommand for AutoenvTrust {
|
||||
})?;
|
||||
fs::write(config_path, tomlstr).expect("Couldn't write to toml file");
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(".nu-env trusted!").into_value(tag),
|
||||
)))
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::autoenv::Trusted;
|
||||
use crate::prelude::*;
|
||||
use nu_data::config::Trusted;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::SyntaxShape;
|
||||
@ -8,7 +8,6 @@ use std::io::Read;
|
||||
use std::{fs, path::PathBuf};
|
||||
pub struct AutoenvUnTrust;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for AutoenvUnTrust {
|
||||
fn name(&self) -> &str {
|
||||
"autoenv untrust"
|
||||
@ -26,10 +25,10 @@ impl WholeStreamCommand for AutoenvUnTrust {
|
||||
"Untrust a .nu-env file in the current or given directory"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let file_to_untrust = match args.call_info.evaluate(&ctx).await?.args.nth(0) {
|
||||
let file_to_untrust = match args.call_info.evaluate(&ctx)?.args.nth(0) {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
||||
tag: _,
|
||||
@ -80,7 +79,7 @@ impl WholeStreamCommand for AutoenvUnTrust {
|
||||
})?;
|
||||
fs::write(config_path, tomlstr).expect("Couldn't write to toml file");
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(".nu-env untrusted!").into_value(tag),
|
||||
)))
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::commands::autoview::options::{ConfigExtensions, NuConfig as AutoViewConfiguration};
|
||||
use crate::commands::autoview::options::ConfigExtensions;
|
||||
use crate::prelude::*;
|
||||
use crate::primitive::get_color_config;
|
||||
use nu_data::value::format_leaf;
|
||||
@ -7,12 +7,9 @@ use nu_errors::ShellError;
|
||||
use nu_protocol::hir::{self, Expression, ExternalRedirection, Literal, SpannedExpression};
|
||||
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
|
||||
use nu_table::TextStyle;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"autoview"
|
||||
@ -26,17 +23,8 @@ impl WholeStreamCommand for Command {
|
||||
"View the contents of the pipeline as a table or list."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
autoview(RunnableContext {
|
||||
input: args.input,
|
||||
scope: args.scope.clone(),
|
||||
shell_manager: args.shell_manager,
|
||||
host: args.host,
|
||||
ctrl_c: args.ctrl_c,
|
||||
current_errors: args.current_errors,
|
||||
name: args.call_info.name_tag,
|
||||
})
|
||||
.await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
autoview(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -55,57 +43,28 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RunnableContextWithoutInput {
|
||||
pub shell_manager: ShellManager,
|
||||
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub scope: Scope,
|
||||
pub name: Tag,
|
||||
}
|
||||
pub fn autoview(context: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let configuration = context.configs.lock().global_config();
|
||||
|
||||
impl RunnableContextWithoutInput {
|
||||
pub fn convert(context: RunnableContext) -> (InputStream, RunnableContextWithoutInput) {
|
||||
let new_context = RunnableContextWithoutInput {
|
||||
shell_manager: context.shell_manager,
|
||||
host: context.host,
|
||||
ctrl_c: context.ctrl_c,
|
||||
current_errors: context.current_errors,
|
||||
scope: context.scope,
|
||||
name: context.name,
|
||||
};
|
||||
(context.input, new_context)
|
||||
}
|
||||
}
|
||||
let binary = context.scope.get_command("binaryview");
|
||||
let text = context.scope.get_command("textview");
|
||||
let table = context.scope.get_command("table");
|
||||
|
||||
pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
let configuration = AutoViewConfiguration::new();
|
||||
let (mut input_stream, context) = context.split();
|
||||
|
||||
let binary = context.get_command("binaryview");
|
||||
let text = context.get_command("textview");
|
||||
let table = context.get_command("table");
|
||||
|
||||
let pivot_mode = configuration.pivot_mode();
|
||||
|
||||
let (mut input_stream, context) = RunnableContextWithoutInput::convert(context);
|
||||
let term_width = context.host.lock().width();
|
||||
let color_hm = get_color_config();
|
||||
|
||||
if let Some(x) = input_stream.next().await {
|
||||
match input_stream.next().await {
|
||||
if let Some(x) = input_stream.next() {
|
||||
match input_stream.next() {
|
||||
Some(y) => {
|
||||
let ctrl_c = context.ctrl_c.clone();
|
||||
let xy = vec![x, y];
|
||||
let xy_stream = futures::stream::iter(xy)
|
||||
.chain(input_stream)
|
||||
.interruptible(ctrl_c);
|
||||
let xy_stream = xy.into_iter().chain(input_stream).interruptible(ctrl_c);
|
||||
|
||||
let stream = InputStream::from_stream(xy_stream);
|
||||
|
||||
if let Some(table) = table {
|
||||
let command_args = create_default_command_args(&context).with_input(stream);
|
||||
let result = table.run(command_args).await?;
|
||||
result.collect::<Vec<_>>().await;
|
||||
let result = table.run(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@ -121,8 +80,8 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
||||
);
|
||||
let command_args =
|
||||
create_default_command_args(&context).with_input(stream);
|
||||
let result = text.run(command_args).await?;
|
||||
result.collect::<Vec<_>>().await;
|
||||
let result = text.run_with_actions(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
} else {
|
||||
out!("{}", s);
|
||||
}
|
||||
@ -203,8 +162,8 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
||||
stream.push_back(x);
|
||||
let command_args =
|
||||
create_default_command_args(&context).with_input(stream);
|
||||
let result = binary.run(command_args).await?;
|
||||
result.collect::<Vec<_>>().await;
|
||||
let result = binary.run_with_actions(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
} else {
|
||||
use pretty_hex::*;
|
||||
out!("{:?}", b.hex_dump());
|
||||
@ -219,41 +178,57 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
||||
}
|
||||
|
||||
Value {
|
||||
value: UntaggedValue::Row(row),
|
||||
value: UntaggedValue::Row(ref row),
|
||||
..
|
||||
} if pivot_mode.is_always()
|
||||
|| (pivot_mode.is_auto()
|
||||
&& (row
|
||||
.entries
|
||||
.iter()
|
||||
.map(|(_, v)| v.convert_to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.iter()
|
||||
.fold(0usize, |acc, len| acc + len.len())
|
||||
+ row.entries.iter().count() * 2)
|
||||
> term_width) =>
|
||||
{
|
||||
let mut entries = vec![];
|
||||
for (key, value) in row.entries.iter() {
|
||||
entries.push(vec![
|
||||
nu_table::StyledString::new(
|
||||
key.to_string(),
|
||||
TextStyle::new()
|
||||
.alignment(nu_table::Alignment::Left)
|
||||
.fg(nu_ansi_term::Color::Green)
|
||||
.bold(Some(true)),
|
||||
),
|
||||
nu_table::StyledString::new(
|
||||
format_leaf(value).plain_string(100_000),
|
||||
nu_table::TextStyle::basic_left(),
|
||||
),
|
||||
]);
|
||||
} => {
|
||||
let pivot_mode = configuration.pivot_mode();
|
||||
|
||||
let term_width = context.host.lock().width();
|
||||
if pivot_mode.is_always()
|
||||
|| (pivot_mode.is_auto()
|
||||
&& (row
|
||||
.entries
|
||||
.iter()
|
||||
.map(|(_, v)| v.convert_to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.iter()
|
||||
.fold(0usize, |acc, len| acc + len.len())
|
||||
+ row.entries.iter().count() * 2)
|
||||
> term_width)
|
||||
{
|
||||
let mut entries = vec![];
|
||||
for (key, value) in row.entries.iter() {
|
||||
entries.push(vec![
|
||||
nu_table::StyledString::new(
|
||||
key.to_string(),
|
||||
TextStyle::new()
|
||||
.alignment(nu_table::Alignment::Left)
|
||||
.fg(nu_ansi_term::Color::Green)
|
||||
.bold(Some(true)),
|
||||
),
|
||||
nu_table::StyledString::new(
|
||||
format_leaf(value).plain_string(100_000),
|
||||
nu_table::TextStyle::basic_left(),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
let color_hm = get_color_config(&configuration);
|
||||
|
||||
let table =
|
||||
nu_table::Table::new(vec![], entries, nu_table::Theme::compact());
|
||||
|
||||
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);
|
||||
let result = table.run(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
} else {
|
||||
out!("{:?}", row);
|
||||
}
|
||||
|
||||
let table =
|
||||
nu_table::Table::new(vec![], entries, nu_table::Theme::compact());
|
||||
|
||||
println!("{}", nu_table::draw_table(&table, term_width, &color_hm));
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Nothing),
|
||||
@ -269,8 +244,8 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
||||
stream.push_back(x);
|
||||
let command_args =
|
||||
create_default_command_args(&context).with_input(stream);
|
||||
let result = table.run(command_args).await?;
|
||||
result.collect::<Vec<_>>().await;
|
||||
let result = table.run(command_args)?;
|
||||
let _ = result.collect::<Vec<_>>();
|
||||
} else {
|
||||
out!("{:?}", item);
|
||||
}
|
||||
@ -280,7 +255,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
||||
}
|
||||
}
|
||||
|
||||
Ok(OutputStream::empty())
|
||||
Ok(InputStream::empty())
|
||||
}
|
||||
|
||||
fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawCommandArgs {
|
||||
@ -288,6 +263,7 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
|
||||
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(),
|
||||
call_info: UnevaluatedCallInfo {
|
||||
|
@ -23,7 +23,6 @@ struct BenchmarkArgs {
|
||||
passthrough: Option<CapturedBlock>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Benchmark {
|
||||
fn name(&self) -> &str {
|
||||
"benchmark"
|
||||
@ -48,8 +47,8 @@ impl WholeStreamCommand for Benchmark {
|
||||
"Runs a block and returns the time it took to execute it."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
benchmark(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
benchmark(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -68,11 +67,11 @@ impl WholeStreamCommand for Benchmark {
|
||||
}
|
||||
}
|
||||
|
||||
async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
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().await?;
|
||||
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process()?;
|
||||
|
||||
let env = scope.get_env_vars();
|
||||
let name = generate_free_name(&env);
|
||||
@ -82,15 +81,15 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let start_time = Instant::now();
|
||||
|
||||
// #[cfg(feature = "rich-benchmark")]
|
||||
// let start = time().await;
|
||||
// let start = time();
|
||||
|
||||
context.scope.enter_scope();
|
||||
let result = run_block(&block.block, &context, input).await;
|
||||
let result = run_block(&block.block, &context, input);
|
||||
context.scope.exit_scope();
|
||||
let output = result?.into_vec().await;
|
||||
let output = result?.into_vec();
|
||||
|
||||
// #[cfg(feature = "rich-benchmark")]
|
||||
// let end = time().await;
|
||||
// let end = time();
|
||||
|
||||
let end_time = Instant::now();
|
||||
context.clear_errors();
|
||||
@ -102,7 +101,7 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, 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).await
|
||||
benchmark_output(indexmap, output, passthrough, &tag, &mut context)
|
||||
}
|
||||
// return advanced stats
|
||||
// #[cfg(feature = "rich-benchmark")]
|
||||
@ -121,7 +120,7 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
// let idle_time = into_big_int(end.idle() - start.idle());
|
||||
// indexmap.insert("idle time".to_string(), idle_time);
|
||||
|
||||
// benchmark_output(indexmap, output, passthrough, &tag, &mut context).await
|
||||
// benchmark_output(indexmap, output, passthrough, &tag, &mut context)
|
||||
// } else {
|
||||
// Err(ShellError::untagged_runtime_error(
|
||||
// "Could not retrieve CPU time",
|
||||
@ -129,16 +128,16 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
// }
|
||||
}
|
||||
|
||||
async fn benchmark_output<T, Output>(
|
||||
fn benchmark_output<T, Output>(
|
||||
indexmap: IndexMap<String, BigInt>,
|
||||
block_output: Output,
|
||||
passthrough: Option<CapturedBlock>,
|
||||
tag: T,
|
||||
context: &mut EvaluationContext,
|
||||
) -> Result<OutputStream, ShellError>
|
||||
) -> Result<ActionStream, ShellError>
|
||||
where
|
||||
T: Into<Tag> + Copy,
|
||||
Output: Into<OutputStream>,
|
||||
Output: Into<ActionStream>,
|
||||
{
|
||||
let value = UntaggedValue::Row(Dictionary::from(
|
||||
indexmap
|
||||
@ -155,33 +154,35 @@ where
|
||||
let time_block = add_implicit_autoview(time_block.block);
|
||||
|
||||
context.scope.enter_scope();
|
||||
let result = run_block(&time_block, context, benchmark_output).await;
|
||||
let result = run_block(&time_block, context, benchmark_output);
|
||||
context.scope.exit_scope();
|
||||
result?;
|
||||
context.clear_errors();
|
||||
|
||||
Ok(block_output.into())
|
||||
} else {
|
||||
let benchmark_output = OutputStream::one(value);
|
||||
let benchmark_output = ActionStream::one(value);
|
||||
Ok(benchmark_output)
|
||||
}
|
||||
}
|
||||
|
||||
fn add_implicit_autoview(mut block: Block) -> Block {
|
||||
if block.block.is_empty() {
|
||||
let group = Group::new(
|
||||
vec![{
|
||||
let mut commands = Pipeline::new(block.span);
|
||||
commands.push(ClassifiedCommand::Internal(InternalCommand::new(
|
||||
"autoview".to_string(),
|
||||
block.span,
|
||||
block.span,
|
||||
)));
|
||||
commands
|
||||
}],
|
||||
block.span,
|
||||
);
|
||||
block.push(group);
|
||||
fn add_implicit_autoview(mut block: Arc<Block>) -> Arc<Block> {
|
||||
if let Some(block) = std::sync::Arc::<nu_protocol::hir::Block>::get_mut(&mut block) {
|
||||
if block.block.is_empty() {
|
||||
let group = Group::new(
|
||||
vec![{
|
||||
let mut commands = Pipeline::new(block.span);
|
||||
commands.push(ClassifiedCommand::Internal(InternalCommand::new(
|
||||
"autoview".to_string(),
|
||||
block.span,
|
||||
block.span,
|
||||
)));
|
||||
commands
|
||||
}],
|
||||
block.span,
|
||||
);
|
||||
block.push(group);
|
||||
}
|
||||
}
|
||||
block
|
||||
}
|
||||
|
@ -5,14 +5,8 @@ use nu_data::value::format_leaf;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BuildStringArgs {
|
||||
rest: Vec<Value>,
|
||||
}
|
||||
|
||||
pub struct BuildString;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for BuildString {
|
||||
fn name(&self) -> &str {
|
||||
"build-string"
|
||||
@ -27,9 +21,10 @@ impl WholeStreamCommand for BuildString {
|
||||
"Builds a string from the arguments."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (BuildStringArgs { rest }, _) = args.process().await?;
|
||||
let args = args.evaluate_once()?;
|
||||
let rest: Vec<Value> = args.rest(0)?;
|
||||
|
||||
let mut output_string = String::new();
|
||||
|
||||
@ -37,7 +32,7 @@ impl WholeStreamCommand for BuildString {
|
||||
output_string.push_str(&format_leaf(&r).plain_string(100_000))
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output_string).into_value(tag),
|
||||
)))
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct Cal;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Cal {
|
||||
fn name(&self) -> &str {
|
||||
"cal"
|
||||
@ -41,8 +40,8 @@ impl WholeStreamCommand for Cal {
|
||||
"Display a calendar."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
cal(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
cal(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -66,8 +65,8 @@ impl WholeStreamCommand for Cal {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
pub fn cal(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let mut calendar_vec_deque = VecDeque::new();
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
@ -76,7 +75,7 @@ pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let mut selected_year: i32 = current_year;
|
||||
let mut current_day_option: Option<u32> = Some(current_day);
|
||||
|
||||
let month_range = if let Some(full_year_value) = args.get("full-year") {
|
||||
let month_range = if let Some(full_year_value) = args.call_info.args.get("full-year") {
|
||||
if let Ok(year_u64) = full_year_value.as_u64() {
|
||||
selected_year = year_u64 as i32;
|
||||
|
||||
@ -102,7 +101,7 @@ pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
current_day_option,
|
||||
)?;
|
||||
|
||||
Ok(futures::stream::iter(calendar_vec_deque).to_output_stream())
|
||||
Ok(calendar_vec_deque.into_iter().to_action_stream())
|
||||
}
|
||||
|
||||
fn get_invalid_year_shell_error(year_tag: &Tag) -> ShellError {
|
||||
@ -210,7 +209,7 @@ fn add_month_to_table(
|
||||
|
||||
let month_helper = match month_helper_result {
|
||||
Ok(month_helper) => month_helper,
|
||||
Err(()) => match args.get("full-year") {
|
||||
Err(()) => match args.call_info.args.get("full-year") {
|
||||
Some(full_year_value) => {
|
||||
return Err(get_invalid_year_shell_error(&full_year_value.tag()))
|
||||
}
|
||||
@ -236,7 +235,7 @@ fn add_month_to_table(
|
||||
|
||||
let mut week_start_day = days_of_the_week[0].to_string();
|
||||
|
||||
if let Some(week_start_value) = args.get("week-start") {
|
||||
if let Some(week_start_value) = args.call_info.args.get("week-start") {
|
||||
if let Ok(day) = week_start_value.as_string() {
|
||||
if days_of_the_week.contains(&day.as_str()) {
|
||||
week_start_day = day;
|
||||
@ -265,10 +264,10 @@ fn add_month_to_table(
|
||||
let mut day_number: u32 = 1;
|
||||
let day_limit: u32 = total_start_offset + month_helper.number_of_days_in_month;
|
||||
|
||||
let should_show_year_column = args.has("year");
|
||||
let should_show_quarter_column = args.has("quarter");
|
||||
let should_show_month_column = args.has("month");
|
||||
let should_show_month_names = args.has("month-names");
|
||||
let should_show_year_column = args.has_flag("year");
|
||||
let should_show_quarter_column = args.has_flag("quarter");
|
||||
let should_show_month_column = args.has_flag("month");
|
||||
let should_show_month_names = args.has_flag("month-names");
|
||||
|
||||
while day_number <= day_limit {
|
||||
let mut indexmap = IndexMap::new();
|
||||
|
@ -7,7 +7,6 @@ use nu_protocol::{Signature, SyntaxShape};
|
||||
|
||||
pub struct Cd;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Cd {
|
||||
fn name(&self) -> &str {
|
||||
"cd"
|
||||
@ -25,10 +24,10 @@ impl WholeStreamCommand for Cd {
|
||||
"Change to a new path."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
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 (args, _): (CdArgs, _) = args.process().await?;
|
||||
let (args, _): (CdArgs, _) = args.process()?;
|
||||
shell_manager.cd(args, name)
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,11 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_engine::{FromValue, WholeStreamCommand};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Char;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CharArgs {
|
||||
name: Tagged<String>,
|
||||
unicode: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Char {
|
||||
fn name(&self) -> &str {
|
||||
"char"
|
||||
@ -25,7 +18,8 @@ impl WholeStreamCommand for Char {
|
||||
SyntaxShape::Any,
|
||||
"the name of the character to output",
|
||||
)
|
||||
.switch("unicode", "unicode string i.e. 1f378", Some('u'))
|
||||
.rest(SyntaxShape::String, "multiple Unicode bytes")
|
||||
.switch("unicode", "Unicode string i.e. 1f378", Some('u'))
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -49,33 +43,67 @@ impl WholeStreamCommand for Char {
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Output unicode character",
|
||||
description: "Output Unicode character",
|
||||
example: r#"char -u 1f378"#,
|
||||
result: Some(vec![Value::from("\u{1f378}")]),
|
||||
},
|
||||
Example {
|
||||
description: "Output multi-byte Unicode character",
|
||||
example: r#"char -u 1F468 200D 1F466 200D 1F466"#,
|
||||
result: Some(vec![Value::from(
|
||||
"\u{1F468}\u{200D}\u{1F466}\u{200D}\u{1F466}",
|
||||
)]),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (CharArgs { name, unicode }, _) = args.process().await?;
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let name: Tagged<String> = args.req(0)?;
|
||||
let rest: Vec<Value> = args.rest(1)?;
|
||||
let unicode = args.has_flag("unicode");
|
||||
|
||||
if unicode {
|
||||
let decoded_char = string_to_unicode_char(&name.item);
|
||||
if let Some(output) = decoded_char {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(name.tag()),
|
||||
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);
|
||||
match decoded_char {
|
||||
Ok(ch) => multi_byte.push(ch),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(multi_byte).into_value(name.tag),
|
||||
)))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"error decoding unicode character",
|
||||
"error decoding unicode character",
|
||||
name.tag(),
|
||||
))
|
||||
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()),
|
||||
)))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"error decoding Unicode character",
|
||||
"error decoding Unicode character",
|
||||
name.tag(),
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let special_character = str_to_character(&name.item);
|
||||
if let Some(output) = special_character {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(name.tag()),
|
||||
)))
|
||||
} else {
|
||||
@ -89,10 +117,20 @@ impl WholeStreamCommand for Char {
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_unicode_char(s: &str) -> Option<char> {
|
||||
u32::from_str_radix(s, 16)
|
||||
fn string_to_unicode_char(s: &str, t: &Tag) -> Result<char, ShellError> {
|
||||
let decoded_char = u32::from_str_radix(s, 16)
|
||||
.ok()
|
||||
.and_then(std::char::from_u32)
|
||||
.and_then(std::char::from_u32);
|
||||
|
||||
if let Some(ch) = decoded_char {
|
||||
Ok(ch)
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"error decoding Unicode character",
|
||||
"error decoding Unicode character",
|
||||
t,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn str_to_character(s: &str) -> Option<String> {
|
||||
|
@ -6,7 +6,6 @@ use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
#[derive(Clone)]
|
||||
pub struct Chart;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Chart {
|
||||
fn name(&self) -> &str {
|
||||
"chart"
|
||||
@ -20,14 +19,14 @@ impl WholeStreamCommand for Chart {
|
||||
"Displays charts."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
if args.scope.get_command("chart bar").is_none() {
|
||||
return Err(ShellError::untagged_runtime_error(
|
||||
"nu_plugin_chart not installed.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
|
||||
Ok(ActionStream::one(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(get_full_help(&Chart, &args.scope)).into_value(Tag::unknown()),
|
||||
))))
|
||||
}
|
||||
|
@ -1,16 +1,14 @@
|
||||
use crate::futures::ThreadedReceiver;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::evaluate_baseline_expr;
|
||||
use nu_engine::{evaluate_baseline_expr, BufCodecReader};
|
||||
use nu_engine::{MaybeTextCodec, StringOrBinary};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
use std::ops::Deref;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::mpsc;
|
||||
use std::{borrow::Cow, io::BufReader};
|
||||
|
||||
use futures::executor::block_on_stream;
|
||||
use futures_codec::FramedRead;
|
||||
use log::trace;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
@ -18,9 +16,8 @@ use nu_protocol::hir::Expression;
|
||||
use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
|
||||
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
use nu_stream::trace_stream;
|
||||
|
||||
pub(crate) async fn run_external_command(
|
||||
pub(crate) fn run_external_command(
|
||||
command: ExternalCommand,
|
||||
context: &mut EvaluationContext,
|
||||
input: InputStream,
|
||||
@ -28,18 +25,19 @@ pub(crate) async fn run_external_command(
|
||||
) -> Result<InputStream, ShellError> {
|
||||
trace!(target: "nu::run::external", "-> {}", command.name);
|
||||
|
||||
if !did_find_command(&command.name) {
|
||||
context.sync_path_to_env();
|
||||
if !context.host.lock().is_external_cmd(&command.name) {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Command not found",
|
||||
"command not found",
|
||||
format!("command {} not found", &command.name),
|
||||
&command.name_tag,
|
||||
));
|
||||
}
|
||||
|
||||
run_with_stdin(command, context, input, external_redirection).await
|
||||
run_with_stdin(command, context, input, external_redirection)
|
||||
}
|
||||
|
||||
async fn run_with_stdin(
|
||||
fn run_with_stdin(
|
||||
command: ExternalCommand,
|
||||
context: &mut EvaluationContext,
|
||||
input: InputStream,
|
||||
@ -47,12 +45,10 @@ async fn run_with_stdin(
|
||||
) -> Result<InputStream, ShellError> {
|
||||
let path = context.shell_manager.path();
|
||||
|
||||
let input = trace_stream!(target: "nu::trace_stream::external::stdin", "input" = input);
|
||||
|
||||
let mut command_args = vec![];
|
||||
for arg in command.args.iter() {
|
||||
let is_literal = matches!(arg.expr, Expression::Literal(_));
|
||||
let value = evaluate_baseline_expr(arg, context).await?;
|
||||
let value = evaluate_baseline_expr(arg, context)?;
|
||||
|
||||
// Skip any arguments that don't really exist, treating them as optional
|
||||
// FIXME: we may want to preserve the gap in the future, though it's hard to say
|
||||
@ -219,7 +215,7 @@ fn spawn(
|
||||
.take()
|
||||
.expect("Internal error: could not get stdin pipe for external command");
|
||||
|
||||
for value in block_on_stream(input) {
|
||||
for value in input {
|
||||
match &value.value {
|
||||
UntaggedValue::Primitive(Primitive::Nothing) => continue,
|
||||
UntaggedValue::Primitive(Primitive::String(s)) => {
|
||||
@ -274,10 +270,12 @@ fn spawn(
|
||||
return Err(());
|
||||
};
|
||||
|
||||
let file = futures::io::AllowStdIo::new(stdout);
|
||||
let stream = FramedRead::new(file, MaybeTextCodec::default());
|
||||
// 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 block_on_stream(stream) {
|
||||
for line in buf_codec {
|
||||
match line {
|
||||
Ok(line) => match line {
|
||||
StringOrBinary::String(s) => {
|
||||
@ -345,10 +343,12 @@ fn spawn(
|
||||
return Err(());
|
||||
};
|
||||
|
||||
let file = futures::io::AllowStdIo::new(stderr);
|
||||
let stream = FramedRead::new(file, MaybeTextCodec::default());
|
||||
// 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());
|
||||
|
||||
for line in block_on_stream(stream) {
|
||||
for line in buf_codec {
|
||||
match line {
|
||||
Ok(line) => match line {
|
||||
StringOrBinary::String(s) => {
|
||||
@ -432,7 +432,7 @@ fn spawn(
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let stream = ThreadedReceiver::new(rx);
|
||||
let stream = ChannelReceiver::new(rx);
|
||||
Ok(stream.to_input_stream())
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
@ -443,31 +443,26 @@ fn spawn(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn did_find_command(#[allow(unused)] name: &str) -> bool {
|
||||
#[cfg(not(feature = "which"))]
|
||||
{
|
||||
// we can't perform this check, so just assume it can be found
|
||||
true
|
||||
struct ChannelReceiver {
|
||||
rx: Arc<Mutex<mpsc::Receiver<Result<Value, ShellError>>>>,
|
||||
}
|
||||
|
||||
impl ChannelReceiver {
|
||||
pub fn new(rx: mpsc::Receiver<Result<Value, ShellError>>) -> Self {
|
||||
Self {
|
||||
rx: Arc::new(Mutex::new(rx)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "which", unix))]
|
||||
{
|
||||
which::which(name).is_ok()
|
||||
}
|
||||
impl Iterator for ChannelReceiver {
|
||||
type Item = Result<Value, ShellError>;
|
||||
|
||||
#[cfg(all(feature = "which", windows))]
|
||||
{
|
||||
if which::which(name).is_ok() {
|
||||
true
|
||||
} else {
|
||||
// Reference: https://ss64.com/nt/syntax-internal.html
|
||||
let cmd_builtins = [
|
||||
"assoc", "break", "color", "copy", "date", "del", "dir", "dpath", "echo", "erase",
|
||||
"for", "ftype", "md", "mkdir", "mklink", "move", "path", "ren", "rename", "rd",
|
||||
"rmdir", "start", "time", "title", "type", "ver", "verify", "vol",
|
||||
];
|
||||
|
||||
cmd_builtins.contains(&name)
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let rx = self.rx.lock();
|
||||
match rx.recv() {
|
||||
Ok(v) => Some(v),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -535,16 +530,13 @@ mod tests {
|
||||
#[cfg(feature = "which")]
|
||||
use super::{run_external_command, InputStream};
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use futures::executor::block_on;
|
||||
#[cfg(feature = "which")]
|
||||
use nu_engine::basic_evaluation_context;
|
||||
#[cfg(feature = "which")]
|
||||
use nu_errors::ShellError;
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use nu_test_support::commands::ExternalBuilder;
|
||||
// async fn read(mut stream: OutputStream) -> Option<Value> {
|
||||
// match stream.try_next().await {
|
||||
// fn read(mut stream: OutputStream) -> Option<Value> {
|
||||
// match stream.try_next() {
|
||||
// Ok(val) => {
|
||||
// if let Some(val) = val {
|
||||
// val.raw_value()
|
||||
@ -557,7 +549,7 @@ mod tests {
|
||||
// }
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
async fn non_existent_run() -> Result<(), ShellError> {
|
||||
fn non_existent_run() {
|
||||
use nu_protocol::hir::ExternalRedirection;
|
||||
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
|
||||
|
||||
@ -565,24 +557,18 @@ mod tests {
|
||||
let mut ctx =
|
||||
basic_evaluation_context().expect("There was a problem creating a basic context.");
|
||||
|
||||
assert!(
|
||||
run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout)
|
||||
.await
|
||||
.is_err()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
assert!(run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout).is_err());
|
||||
}
|
||||
|
||||
// async fn failure_run() -> Result<(), ShellError> {
|
||||
// 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 stream = run_external_command(cmd, &mut ctx, None, false)
|
||||
// .await?
|
||||
// ?
|
||||
// .expect("There was a problem running the external command.");
|
||||
|
||||
// match read(stream.into()).await {
|
||||
// match read(stream.into()) {
|
||||
// Some(Value {
|
||||
// value: UntaggedValue::Error(_),
|
||||
// ..
|
||||
@ -600,8 +586,8 @@ mod tests {
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
#[test]
|
||||
fn identifies_command_not_found() -> Result<(), ShellError> {
|
||||
block_on(non_existent_run())
|
||||
fn identifies_command_not_found() {
|
||||
non_existent_run()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -6,7 +6,6 @@ use std::process::Command;
|
||||
|
||||
pub struct Clear;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Clear {
|
||||
fn name(&self) -> &str {
|
||||
"clear"
|
||||
@ -20,7 +19,7 @@ impl WholeStreamCommand for Clear {
|
||||
"Clears the terminal."
|
||||
}
|
||||
|
||||
async fn run(&self, _: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run(&self, _: CommandArgs) -> Result<InputStream, ShellError> {
|
||||
if cfg!(windows) {
|
||||
Command::new("cmd")
|
||||
.args(&["/C", "cls"])
|
||||
@ -32,7 +31,7 @@ impl WholeStreamCommand for Clear {
|
||||
.status()
|
||||
.expect("failed to execute process");
|
||||
}
|
||||
Ok(OutputStream::empty())
|
||||
Ok(InputStream::empty())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::prelude::*;
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, Value};
|
||||
@ -8,7 +8,6 @@ use arboard::Clipboard;
|
||||
|
||||
pub struct Clip;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Clip {
|
||||
fn name(&self) -> &str {
|
||||
"clip"
|
||||
@ -22,8 +21,8 @@ impl WholeStreamCommand for Clip {
|
||||
"Copy the contents of the pipeline to the copy/paste buffer."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
clip(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
clip(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -42,10 +41,10 @@ impl WholeStreamCommand for Clip {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn clip(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn clip(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let input = args.input;
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let values: Vec<Value> = input.collect().await;
|
||||
let name = args.call_info.name_tag;
|
||||
let values: Vec<Value> = input.collect();
|
||||
|
||||
if let Ok(mut clip_context) = Clipboard::new() {
|
||||
let mut new_copy_data = String::new();
|
||||
@ -89,7 +88,7 @@ pub async fn clip(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
name,
|
||||
));
|
||||
}
|
||||
Ok(OutputStream::empty())
|
||||
Ok(ActionStream::empty())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,21 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::Command;
|
||||
use nu_errors::ShellError;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
pub struct RunnableContext {
|
||||
pub input: InputStream,
|
||||
pub shell_manager: ShellManager,
|
||||
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||
pub scope: Scope,
|
||||
pub name: Tag,
|
||||
}
|
||||
|
||||
impl RunnableContext {
|
||||
pub fn get_command(&self, name: &str) -> Option<Command> {
|
||||
self.scope.get_command(name)
|
||||
}
|
||||
}
|
@ -1,19 +1,16 @@
|
||||
use crate::prelude::*;
|
||||
use futures::future;
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
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 Compact;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CompactArgs {
|
||||
rest: Vec<Tagged<String>>,
|
||||
columns: Vec<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Compact {
|
||||
fn name(&self) -> &str {
|
||||
"compact"
|
||||
@ -27,8 +24,8 @@ impl WholeStreamCommand for Compact {
|
||||
"Creates a table with non-empty rows."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
compact(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
compact(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -40,34 +37,28 @@ impl WholeStreamCommand for Compact {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn compact(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (CompactArgs { rest: columns }, input) = args.process().await?;
|
||||
pub fn compact(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (args, input) = args.extract(|params| {
|
||||
Ok(CompactArgs {
|
||||
columns: params.rest(0)?,
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(input
|
||||
.filter_map(move |item| {
|
||||
future::ready(if columns.is_empty() {
|
||||
if !item.is_empty() {
|
||||
Some(ReturnSuccess::value(item))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.filter(move |item| {
|
||||
if args.columns.is_empty() {
|
||||
!item.is_empty()
|
||||
} else if let Value {
|
||||
value: UntaggedValue::Row(ref r),
|
||||
..
|
||||
} = item
|
||||
{
|
||||
args.columns
|
||||
.iter()
|
||||
.all(|field| r.get_data(field).borrow().is_some())
|
||||
} else {
|
||||
match item {
|
||||
Value {
|
||||
value: UntaggedValue::Row(ref r),
|
||||
..
|
||||
} => {
|
||||
if columns
|
||||
.iter()
|
||||
.all(|field| r.get_data(field).borrow().is_some())
|
||||
{
|
||||
Some(ReturnSuccess::value(item))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
false
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config clear"
|
||||
@ -19,8 +18,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"clear the config"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
clear(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
clear(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -32,18 +31,23 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn clear(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_span = args.call_info.name_tag.clone();
|
||||
pub fn clear(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
|
||||
// NOTE: None because we are not loading a new config file, we just want to read from the
|
||||
// existing config
|
||||
let mut result = nu_data::config::read(name_span, &None)?;
|
||||
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),
|
||||
)))
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
};
|
||||
|
||||
result.clear();
|
||||
|
||||
config::write(&result, &None)?;
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(args.call_info.name_tag),
|
||||
)))
|
||||
result
|
||||
}
|
||||
|
@ -3,11 +3,10 @@ use nu_engine::CommandArgs;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use nu_stream::OutputStream;
|
||||
use nu_stream::ActionStream;
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"config"
|
||||
@ -21,14 +20,22 @@ impl WholeStreamCommand for Command {
|
||||
"Configuration management."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_span = args.call_info.name_tag.clone();
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag;
|
||||
let result = nu_data::config::read(name_span, &None)?;
|
||||
|
||||
Ok(futures::stream::iter(vec![ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(name),
|
||||
)])
|
||||
.to_output_stream())
|
||||
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())
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,11 +6,10 @@ use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedVal
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GetArgs {
|
||||
path: ColumnPath,
|
||||
pub struct Arguments {
|
||||
column_path: ColumnPath,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config get"
|
||||
@ -28,8 +27,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Gets a value from the config"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
get(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
get(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -41,28 +40,29 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_tag = args.call_info.name_tag.clone();
|
||||
let (GetArgs { path }, _) = args.process().await?;
|
||||
pub fn get(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
|
||||
// NOTE: None because we are not loading a new config file, we just want to read from the
|
||||
// existing config
|
||||
let result = UntaggedValue::row(nu_data::config::read(&name_tag, &None)?).into_value(&name_tag);
|
||||
let (Arguments { column_path }, _) = args.process()?;
|
||||
|
||||
let value = crate::commands::get::get_column_path(&path, &result)?;
|
||||
let result = if let Some(global_cfg) = &ctx.configs.lock().global_config {
|
||||
let result = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
|
||||
let value = crate::commands::get::get_column_path(&column_path, &result)?;
|
||||
Ok(match value {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => list.into_iter().to_action_stream(),
|
||||
x => ActionStream::one(ReturnSuccess::value(x)),
|
||||
})
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
};
|
||||
|
||||
Ok(match value {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => {
|
||||
let list: Vec<_> = list
|
||||
.iter()
|
||||
.map(|x| ReturnSuccess::value(x.clone()))
|
||||
.collect();
|
||||
|
||||
futures::stream::iter(list).to_output_stream()
|
||||
}
|
||||
x => OutputStream::one(ReturnSuccess::value(x)),
|
||||
})
|
||||
result
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LoadArgs {
|
||||
load: Tagged<PathBuf>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config load"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("config load").required(
|
||||
"load",
|
||||
SyntaxShape::FilePath,
|
||||
"Path to load the config from",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Loads the config from the path given"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
set(args).await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let name_span = args.call_info.name_tag.clone();
|
||||
let (LoadArgs { load }, _) = args.process().await?;
|
||||
|
||||
let configuration = load.item().clone();
|
||||
|
||||
let result = nu_data::config::read(name_span, &Some(configuration))?;
|
||||
|
||||
Ok(futures::stream::iter(vec![ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(name),
|
||||
)])
|
||||
.to_output_stream())
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
pub mod clear;
|
||||
pub mod command;
|
||||
pub mod get;
|
||||
pub mod load;
|
||||
pub mod path;
|
||||
pub mod remove;
|
||||
pub mod set;
|
||||
@ -10,8 +9,13 @@ pub mod set_into;
|
||||
pub use clear::SubCommand as ConfigClear;
|
||||
pub use command::Command as Config;
|
||||
pub use get::SubCommand as ConfigGet;
|
||||
pub use load::SubCommand as ConfigLoad;
|
||||
pub use path::SubCommand as ConfigPath;
|
||||
pub use remove::SubCommand as ConfigRemove;
|
||||
pub use set::SubCommand as ConfigSet;
|
||||
pub use set_into::SubCommand as ConfigSetInto;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
|
||||
pub fn err_no_global_cfg_present() -> ShellError {
|
||||
ShellError::untagged_runtime_error("No global config found!")
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config path"
|
||||
@ -19,8 +18,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"return the path to the config file"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
path(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
path(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -32,10 +31,16 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn path(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let path = config::default_path()?;
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Primitive(Primitive::FilePath(path)).into_value(args.call_info.name_tag),
|
||||
)))
|
||||
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())),
|
||||
)))
|
||||
} else {
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,10 @@ use nu_source::Tagged;
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RemoveArgs {
|
||||
pub struct Arguments {
|
||||
remove: Tagged<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config remove"
|
||||
@ -29,8 +28,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Removes a value from the config"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
remove(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
remove(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -42,26 +41,36 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_span = args.call_info.name_tag.clone();
|
||||
let (RemoveArgs { remove }, _) = args.process().await?;
|
||||
|
||||
let mut result = nu_data::config::read(name_span, &None)?;
|
||||
pub fn remove(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let (Arguments { remove }, _) = args.process()?;
|
||||
|
||||
let key = remove.to_string();
|
||||
|
||||
if result.contains_key(&key) {
|
||||
result.swap_remove(&key);
|
||||
config::write(&result, &None)?;
|
||||
Ok(futures::stream::iter(vec![ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(remove.tag()),
|
||||
)])
|
||||
.to_output_stream())
|
||||
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
|
||||
if global_cfg.vars.contains_key(&key) {
|
||||
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())
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Key does not exist in config",
|
||||
"key",
|
||||
remove.tag(),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Key does not exist in config",
|
||||
"key",
|
||||
remove.tag(),
|
||||
))
|
||||
}
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
|
@ -6,12 +6,11 @@ use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedVal
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SetArgs {
|
||||
path: ColumnPath,
|
||||
pub struct Arguments {
|
||||
column_path: ColumnPath,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config set"
|
||||
@ -27,8 +26,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Sets a value in the config"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
set(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
set(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -57,33 +56,49 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_tag = args.call_info.name_tag.clone();
|
||||
let (SetArgs { path, mut value }, _) = args.process().await?;
|
||||
pub fn set(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let (
|
||||
Arguments {
|
||||
column_path,
|
||||
mut value,
|
||||
},
|
||||
_,
|
||||
) = args.process()?;
|
||||
|
||||
// NOTE: None because we are not loading a new config file, we just want to read from the
|
||||
// existing config
|
||||
let raw_entries = nu_data::config::read(&name_tag, &None)?;
|
||||
let configuration = UntaggedValue::row(raw_entries).into_value(&name_tag);
|
||||
let result = if let Some(global_cfg) = &mut ctx.configs.lock().global_config {
|
||||
let configuration = UntaggedValue::row(global_cfg.vars.clone()).into_value(&name);
|
||||
|
||||
if let UntaggedValue::Table(rows) = &value.value {
|
||||
if rows.len() == 1 && rows[0].is_row() {
|
||||
value = rows[0].clone();
|
||||
if let UntaggedValue::Table(rows) = &value.value {
|
||||
if rows.len() == 1 && rows[0].is_row() {
|
||||
value = rows[0].clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match configuration.forgiving_insert_data_at_column_path(&path, value) {
|
||||
Ok(Value {
|
||||
value: UntaggedValue::Row(changes),
|
||||
..
|
||||
}) => {
|
||||
config::write(&changes.entries, &None)?;
|
||||
match configuration.forgiving_insert_data_at_column_path(&column_path, value) {
|
||||
Ok(Value {
|
||||
value: UntaggedValue::Row(changes),
|
||||
..
|
||||
}) => {
|
||||
global_cfg.vars = changes.entries;
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Row(changes).into_value(name_tag),
|
||||
)))
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
|
||||
)))
|
||||
}
|
||||
Ok(_) => Ok(ActionStream::empty()),
|
||||
Err(reason) => Err(reason),
|
||||
}
|
||||
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())
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
|
@ -7,11 +7,10 @@ use nu_source::Tagged;
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SetIntoArgs {
|
||||
pub struct Arguments {
|
||||
set_into: Tagged<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"config set_into"
|
||||
@ -29,8 +28,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Sets a value in the config"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
set_into(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
set_into(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -42,49 +41,46 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_span = args.call_info.name_tag.clone();
|
||||
pub fn set_into(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let (Arguments { set_into: v }, input) = args.process()?;
|
||||
|
||||
let (SetIntoArgs { set_into: v }, input) = args.process().await?;
|
||||
|
||||
// NOTE: None because we are not loading a new config file, we just want to read from the
|
||||
// existing config
|
||||
let mut result = nu_data::config::read(name_span, &None)?;
|
||||
|
||||
// In the original code, this is set to `Some` if the `load flag is set`
|
||||
let configuration = None;
|
||||
|
||||
let rows: Vec<Value> = input.collect().await;
|
||||
let rows: Vec<Value> = input.collect();
|
||||
let key = v.to_string();
|
||||
|
||||
Ok(if rows.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"No values given for set_into",
|
||||
"needs value(s) from pipeline",
|
||||
v.tag(),
|
||||
));
|
||||
} else if rows.len() == 1 {
|
||||
// A single value
|
||||
let value = &rows[0];
|
||||
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(),
|
||||
));
|
||||
} else if rows.len() == 1 {
|
||||
// A single value
|
||||
let value = &rows[0];
|
||||
|
||||
result.insert(key, value.clone());
|
||||
global_cfg.vars.insert(key, value.clone());
|
||||
} else {
|
||||
// Take in the pipeline as a table
|
||||
let value = UntaggedValue::Table(rows).into_value(name.clone());
|
||||
|
||||
config::write(&result, &configuration)?;
|
||||
global_cfg.vars.insert(key, value);
|
||||
}
|
||||
|
||||
OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(name),
|
||||
))
|
||||
global_cfg.write()?;
|
||||
ctx.reload_config(global_cfg)?;
|
||||
|
||||
Ok(ActionStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::row(global_cfg.vars.clone()).into_value(name),
|
||||
)))
|
||||
} else {
|
||||
// Take in the pipeline as a table
|
||||
let value = UntaggedValue::Table(rows).into_value(name.clone());
|
||||
Ok(vec![ReturnSuccess::value(UntaggedValue::Error(
|
||||
crate::commands::config::err_no_global_cfg_present(),
|
||||
))]
|
||||
.into_iter()
|
||||
.to_action_stream())
|
||||
};
|
||||
|
||||
result.insert(key, value);
|
||||
|
||||
config::write(&result, &configuration)?;
|
||||
|
||||
OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Row(result.into()).into_value(name),
|
||||
))
|
||||
})
|
||||
result
|
||||
}
|
||||
|
@ -1,86 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
use futures::stream::StreamExt;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue, Value};
|
||||
|
||||
pub struct Count;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CountArgs {
|
||||
column: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Count {
|
||||
fn name(&self) -> &str {
|
||||
"count"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("count").switch(
|
||||
"column",
|
||||
"Calculate number of columns in table",
|
||||
Some('c'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Show the total number of rows or items."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (CountArgs { column }, input) = args.process().await?;
|
||||
let rows: Vec<Value> = input.collect().await;
|
||||
|
||||
let count = if column {
|
||||
if rows.is_empty() {
|
||||
0
|
||||
} else {
|
||||
match &rows[0].value {
|
||||
UntaggedValue::Row(dictionary) => dictionary.length(),
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Cannot obtain column count",
|
||||
"cannot obtain column count",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rows.len()
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(UntaggedValue::int(count).into_value(tag)))
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Count the number of entries in a list",
|
||||
example: "echo [1 2 3 4 5] | count",
|
||||
result: Some(vec![UntaggedValue::int(5).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Count the number of columns in the calendar table",
|
||||
example: "cal | count -c",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Count;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Count {})
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ use nu_protocol::{Signature, SyntaxShape};
|
||||
|
||||
pub struct Cpy;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Cpy {
|
||||
fn name(&self) -> &str {
|
||||
"cp"
|
||||
@ -26,10 +25,10 @@ impl WholeStreamCommand for Cpy {
|
||||
"Copy files."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (args, _) = args.process().await?;
|
||||
let (args, _) = args.process()?;
|
||||
shell_manager.cp(args, name)
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"date"
|
||||
@ -19,8 +18,8 @@ impl WholeStreamCommand for Command {
|
||||
"Apply date function."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
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()),
|
||||
)))
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ pub struct FormatArgs {
|
||||
table: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date format"
|
||||
@ -31,8 +30,8 @@ impl WholeStreamCommand for Date {
|
||||
"Format a given date using the given format string."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
format(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
format(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -51,9 +50,9 @@ impl WholeStreamCommand for Date {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn format(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn format(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (FormatArgs { format, table }, input) = args.process().await?;
|
||||
let (FormatArgs { format, table }, input) = args.process()?;
|
||||
|
||||
Ok(input
|
||||
.map(move |value| match value {
|
||||
@ -92,7 +91,7 @@ pub async fn format(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
&tag,
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -7,7 +7,6 @@ use nu_protocol::{Dictionary, ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date list-timezone"
|
||||
@ -21,8 +20,8 @@ impl WholeStreamCommand for Date {
|
||||
"List supported time zones."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
list_timezone(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
list_timezone(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -41,8 +40,8 @@ impl WholeStreamCommand for Date {
|
||||
}
|
||||
}
|
||||
|
||||
async fn list_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn list_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let list = TZ_VARIANTS.iter().map(move |tz| {
|
||||
@ -58,7 +57,7 @@ async fn list_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
))
|
||||
});
|
||||
|
||||
Ok(futures::stream::iter(list).to_output_stream())
|
||||
Ok(list.into_iter().to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -6,7 +6,6 @@ use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date now"
|
||||
@ -20,20 +19,20 @@ impl WholeStreamCommand for Date {
|
||||
"Get the current date."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
now(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
now(args)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn now(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
pub fn now(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
|
||||
let now: DateTime<Local> = Local::now();
|
||||
|
||||
let value = UntaggedValue::date(now.with_timezone(now.offset())).into_value(&tag);
|
||||
|
||||
Ok(OutputStream::one(value))
|
||||
Ok(ActionStream::one(value))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -7,7 +7,6 @@ use nu_protocol::{Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date to-table"
|
||||
@ -21,8 +20,8 @@ impl WholeStreamCommand for Date {
|
||||
"Print the date in a structured table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
to_table(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
to_table(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -34,8 +33,8 @@ impl WholeStreamCommand for Date {
|
||||
}
|
||||
}
|
||||
|
||||
async fn to_table(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn to_table(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let input = args.input;
|
||||
|
||||
@ -88,7 +87,7 @@ async fn to_table(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
&tag,
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -12,7 +12,6 @@ struct DateToTimeZoneArgs {
|
||||
timezone: Tagged<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date to-timezone"
|
||||
@ -27,14 +26,15 @@ impl WholeStreamCommand for Date {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert a date to a given time zone.
|
||||
|
||||
Use `date list-timezone` to list all supported time zones.
|
||||
"
|
||||
"Convert a date to a given time zone."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
to_timezone(args).await
|
||||
fn extra_usage(&self) -> &str {
|
||||
"Use 'date list-timezone' to list all supported time zones."
|
||||
}
|
||||
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
to_timezone(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -58,9 +58,9 @@ Use `date list-timezone` to list all supported time zones.
|
||||
}
|
||||
}
|
||||
|
||||
async fn to_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn to_timezone(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (DateToTimeZoneArgs { timezone }, input) = args.process().await?;
|
||||
let (DateToTimeZoneArgs { timezone }, input) = args.process()?;
|
||||
|
||||
Ok(input
|
||||
.map(move |value| match value {
|
||||
@ -85,7 +85,7 @@ async fn to_timezone(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
&tag,
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
fn error_message(err: ParseErrorKind) -> &'static str {
|
||||
|
@ -8,7 +8,6 @@ use nu_protocol::Signature;
|
||||
|
||||
pub struct Date;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Date {
|
||||
fn name(&self) -> &str {
|
||||
"date utc"
|
||||
@ -22,13 +21,13 @@ impl WholeStreamCommand for Date {
|
||||
"return the current date in utc."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
utc(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
utc(args)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn utc(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
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();
|
||||
|
@ -10,7 +10,6 @@ pub struct DebugArgs {
|
||||
raw: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Debug {
|
||||
fn name(&self) -> &str {
|
||||
"debug"
|
||||
@ -24,13 +23,13 @@ impl WholeStreamCommand for Debug {
|
||||
"Print the Rust debug representation of the values."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
debug_value(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
debug_value(args)
|
||||
}
|
||||
}
|
||||
|
||||
async fn debug_value(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (DebugArgs { raw }, input) = args.process().await?;
|
||||
fn debug_value(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (DebugArgs { raw }, input) = args.process()?;
|
||||
Ok(input
|
||||
.map(move |v| {
|
||||
if raw {
|
||||
@ -41,7 +40,7 @@ async fn debug_value(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
ReturnSuccess::debug_value(v)
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -14,7 +14,6 @@ pub struct DefArgs {
|
||||
pub block: CapturedBlock,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Def {
|
||||
fn name(&self) -> &str {
|
||||
"def"
|
||||
@ -35,11 +34,11 @@ impl WholeStreamCommand for Def {
|
||||
"Create a command and set it to a definition."
|
||||
}
|
||||
|
||||
async fn run(&self, _args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, _args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
// Currently, we don't do anything here because we should have already
|
||||
// installed the definition as we entered the scope
|
||||
// We just create a command so that we can get proper coloring
|
||||
Ok(OutputStream::empty())
|
||||
Ok(ActionStream::empty())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -13,7 +13,6 @@ struct DefaultArgs {
|
||||
|
||||
pub struct Default;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Default {
|
||||
fn name(&self) -> &str {
|
||||
"default"
|
||||
@ -33,8 +32,8 @@ impl WholeStreamCommand for Default {
|
||||
"Sets a default row's column if missing."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
default(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
default(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -46,8 +45,8 @@ impl WholeStreamCommand for Default {
|
||||
}
|
||||
}
|
||||
|
||||
async fn default(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (DefaultArgs { column, value }, input) = args.process().await?;
|
||||
fn default(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (DefaultArgs { column, value }, input) = args.process()?;
|
||||
|
||||
Ok(input
|
||||
.map(move |item| {
|
||||
@ -68,7 +67,7 @@ async fn default(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
ReturnSuccess::value(item)
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -29,7 +29,6 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(ConfigSet),
|
||||
whole_stream_command(ConfigSetInto),
|
||||
whole_stream_command(ConfigClear),
|
||||
whole_stream_command(ConfigLoad),
|
||||
whole_stream_command(ConfigRemove),
|
||||
whole_stream_command(ConfigPath),
|
||||
whole_stream_command(Help),
|
||||
@ -57,7 +56,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(Sleep),
|
||||
// Statistics
|
||||
whole_stream_command(Size),
|
||||
whole_stream_command(Count),
|
||||
whole_stream_command(Length),
|
||||
whole_stream_command(Benchmark),
|
||||
// Metadata
|
||||
whole_stream_command(Tags),
|
||||
@ -75,6 +74,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
// Text manipulation
|
||||
whole_stream_command(Hash),
|
||||
whole_stream_command(HashBase64),
|
||||
whole_stream_command(HashMd5),
|
||||
whole_stream_command(Split),
|
||||
whole_stream_command(SplitColumn),
|
||||
whole_stream_command(SplitRow),
|
||||
@ -121,9 +121,12 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(Get),
|
||||
whole_stream_command(Update),
|
||||
whole_stream_command(Insert),
|
||||
whole_stream_command(Into),
|
||||
whole_stream_command(IntoInt),
|
||||
whole_stream_command(SplitBy),
|
||||
// Row manipulation
|
||||
whole_stream_command(All),
|
||||
whole_stream_command(Any),
|
||||
whole_stream_command(Reverse),
|
||||
whole_stream_command(Append),
|
||||
whole_stream_command(Prepend),
|
||||
@ -188,32 +191,33 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(MathRound),
|
||||
whole_stream_command(MathFloor),
|
||||
whole_stream_command(MathCeil),
|
||||
whole_stream_command(MathSqrt),
|
||||
// File format output
|
||||
whole_stream_command(To),
|
||||
whole_stream_command(ToCSV),
|
||||
whole_stream_command(ToHTML),
|
||||
whole_stream_command(ToJSON),
|
||||
whole_stream_command(ToCsv),
|
||||
whole_stream_command(ToHtml),
|
||||
whole_stream_command(ToJson),
|
||||
whole_stream_command(ToMarkdown),
|
||||
whole_stream_command(ToTOML),
|
||||
whole_stream_command(ToTSV),
|
||||
whole_stream_command(ToURL),
|
||||
whole_stream_command(ToYAML),
|
||||
whole_stream_command(ToXML),
|
||||
whole_stream_command(ToToml),
|
||||
whole_stream_command(ToTsv),
|
||||
whole_stream_command(ToUrl),
|
||||
whole_stream_command(ToYaml),
|
||||
whole_stream_command(ToXml),
|
||||
// File format input
|
||||
whole_stream_command(From),
|
||||
whole_stream_command(FromCSV),
|
||||
whole_stream_command(FromEML),
|
||||
whole_stream_command(FromTSV),
|
||||
whole_stream_command(FromSSV),
|
||||
whole_stream_command(FromINI),
|
||||
whole_stream_command(FromJSON),
|
||||
whole_stream_command(FromODS),
|
||||
whole_stream_command(FromTOML),
|
||||
whole_stream_command(FromURL),
|
||||
whole_stream_command(FromXLSX),
|
||||
whole_stream_command(FromXML),
|
||||
whole_stream_command(FromYAML),
|
||||
whole_stream_command(FromYML),
|
||||
whole_stream_command(FromCsv),
|
||||
whole_stream_command(FromEml),
|
||||
whole_stream_command(FromTsv),
|
||||
whole_stream_command(FromSsv),
|
||||
whole_stream_command(FromIni),
|
||||
whole_stream_command(FromJson),
|
||||
whole_stream_command(FromOds),
|
||||
whole_stream_command(FromToml),
|
||||
whole_stream_command(FromUrl),
|
||||
whole_stream_command(FromXlsx),
|
||||
whole_stream_command(FromXml),
|
||||
whole_stream_command(FromYaml),
|
||||
whole_stream_command(FromYml),
|
||||
whole_stream_command(FromIcs),
|
||||
whole_stream_command(FromVcf),
|
||||
// "Private" commands (not intended to be accessed directly)
|
||||
@ -233,9 +237,9 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(PathDirname),
|
||||
whole_stream_command(PathExists),
|
||||
whole_stream_command(PathExpand),
|
||||
whole_stream_command(PathExtension),
|
||||
whole_stream_command(PathFilestem),
|
||||
whole_stream_command(PathJoin),
|
||||
whole_stream_command(PathParse),
|
||||
whole_stream_command(PathSplit),
|
||||
whole_stream_command(PathType),
|
||||
// Url
|
||||
whole_stream_command(UrlCommand),
|
||||
|
@ -9,7 +9,6 @@ pub struct Describe;
|
||||
#[derive(Deserialize)]
|
||||
pub struct DescribeArgs {}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Describe {
|
||||
fn name(&self) -> &str {
|
||||
"describe"
|
||||
@ -23,12 +22,12 @@ impl WholeStreamCommand for Describe {
|
||||
"Describes the objects in the stream."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
describe(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
describe(args)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn describe(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
pub fn describe(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Ok(args
|
||||
.input
|
||||
.map(|row| {
|
||||
@ -37,7 +36,7 @@ pub async fn describe(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
UntaggedValue::string(name).into_value(Tag::unknown_anchor(row.tag.span)),
|
||||
)
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -12,7 +12,6 @@ struct DoArgs {
|
||||
ignore_errors: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Do {
|
||||
fn name(&self) -> &str {
|
||||
"do"
|
||||
@ -32,8 +31,8 @@ impl WholeStreamCommand for Do {
|
||||
"Runs a block, optionally ignoring errors."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
do_(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
do_(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -52,7 +51,7 @@ impl WholeStreamCommand for Do {
|
||||
}
|
||||
}
|
||||
|
||||
async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn do_(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let external_redirection = raw_args.call_info.args.external_redirection;
|
||||
|
||||
let context = EvaluationContext::from_args(&raw_args);
|
||||
@ -62,7 +61,7 @@ async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
mut block,
|
||||
},
|
||||
input,
|
||||
) = raw_args.process().await?;
|
||||
) = raw_args.process()?;
|
||||
|
||||
let block_redirection = match external_redirection {
|
||||
ExternalRedirection::None => {
|
||||
@ -82,9 +81,11 @@ async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
x => x,
|
||||
};
|
||||
|
||||
block.block.set_redirect(block_redirection);
|
||||
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).await;
|
||||
let result = run_block(&block.block, &context, input);
|
||||
context.scope.exit_scope();
|
||||
|
||||
if ignore_errors {
|
||||
@ -93,14 +94,14 @@ async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
match result {
|
||||
Ok(mut stream) => {
|
||||
let output = stream.drain_vec().await;
|
||||
let output = stream.drain_vec();
|
||||
context.clear_errors();
|
||||
Ok(futures::stream::iter(output).to_output_stream())
|
||||
Ok(output.into_iter().to_action_stream())
|
||||
}
|
||||
Err(_) => Ok(OutputStream::empty()),
|
||||
Err(_) => Ok(ActionStream::empty()),
|
||||
}
|
||||
} else {
|
||||
result.map(|x| x.to_output_stream())
|
||||
result.map(|x| x.to_action_stream())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ pub struct Arguments {
|
||||
columns: Option<Tagged<u64>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"drop column"
|
||||
@ -30,12 +29,12 @@ impl WholeStreamCommand for SubCommand {
|
||||
"Remove the last number of columns. If you want to remove columns by name, try 'reject'."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
drop(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
drop(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
use nu_protocol::{row, Value};
|
||||
use nu_protocol::Value;
|
||||
|
||||
vec![Example {
|
||||
description: "Remove the last column of a table",
|
||||
@ -48,8 +47,8 @@ impl WholeStreamCommand for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { columns }, input) = args.process().await?;
|
||||
fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { columns }, input) = args.process()?;
|
||||
|
||||
let to_drop = if let Some(quantity) = columns {
|
||||
*quantity as usize
|
||||
@ -70,7 +69,7 @@ async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
select_fields(&item, descs, item.tag())
|
||||
})
|
||||
.map(ReturnSuccess::value)
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -11,7 +11,6 @@ pub struct Arguments {
|
||||
rows: Option<Tagged<u64>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"drop"
|
||||
@ -29,8 +28,8 @@ impl WholeStreamCommand for Command {
|
||||
"Remove the last number of rows or columns."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
drop(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
drop(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -52,9 +51,9 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { rows }, input) = args.process().await?;
|
||||
let v: Vec<_> = input.into_vec().await;
|
||||
fn drop(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { rows }, input) = args.process()?;
|
||||
let v: Vec<_> = input.into_vec();
|
||||
|
||||
let rows_to_drop = if let Some(quantity) = rows {
|
||||
*quantity as usize
|
||||
@ -63,7 +62,7 @@ async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
};
|
||||
|
||||
Ok(if rows_to_drop == 0 {
|
||||
futures::stream::iter(v).to_output_stream()
|
||||
v.into_iter().to_action_stream()
|
||||
} else {
|
||||
let k = if v.len() < rows_to_drop {
|
||||
0
|
||||
@ -73,6 +72,6 @@ async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let iter = v.into_iter().take(k);
|
||||
|
||||
futures::stream::iter(iter).to_output_stream()
|
||||
iter.to_action_stream()
|
||||
})
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ pub struct DuArgs {
|
||||
min_size: Option<Tagged<u64>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Du {
|
||||
fn name(&self) -> &str {
|
||||
NAME
|
||||
@ -71,8 +70,8 @@ impl WholeStreamCommand for Du {
|
||||
"Find disk usage sizes of specified items."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
du(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
du(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -84,12 +83,12 @@ impl WholeStreamCommand for Du {
|
||||
}
|
||||
}
|
||||
|
||||
async fn du(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
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_copy = ctrl_c.clone();
|
||||
|
||||
let (args, _): (DuArgs, _) = args.process().await?;
|
||||
let (args, _): (DuArgs, _) = args.process()?;
|
||||
let exclude = args.exclude.map_or(Ok(None), move |x| {
|
||||
Pattern::new(&x.item)
|
||||
.map(Option::Some)
|
||||
@ -131,7 +130,7 @@ async fn du(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
all,
|
||||
};
|
||||
|
||||
let inp = futures::stream::iter(paths);
|
||||
let inp = paths;
|
||||
|
||||
Ok(inp
|
||||
.flat_map(move |path| match path {
|
||||
@ -146,12 +145,12 @@ async fn du(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
output.push(Ok(ReturnSuccess::Value(v.into())));
|
||||
}
|
||||
}
|
||||
futures::stream::iter(output)
|
||||
output
|
||||
}
|
||||
Err(e) => futures::stream::iter(vec![Err(e)]),
|
||||
Err(e) => vec![Err(e)],
|
||||
})
|
||||
.interruptible(ctrl_c_copy)
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
fn glob_err_into(e: GlobError) -> ShellError {
|
||||
|
@ -2,22 +2,13 @@ use crate::prelude::*;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use futures::stream::once;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Each;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EachArgs {
|
||||
block: CapturedBlock,
|
||||
numbered: Tagged<bool>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Each {
|
||||
fn name(&self) -> &str {
|
||||
"each"
|
||||
@ -37,8 +28,8 @@ impl WholeStreamCommand for Each {
|
||||
"Run a block on each row of the table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
each(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
each(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -67,7 +58,7 @@ impl WholeStreamCommand for Each {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn process_row(
|
||||
pub fn process_row(
|
||||
captured_block: Arc<Box<CapturedBlock>>,
|
||||
context: Arc<EvaluationContext>,
|
||||
input: Value,
|
||||
@ -80,7 +71,7 @@ pub async fn process_row(
|
||||
let input_stream = if !captured_block.block.params.positional.is_empty() {
|
||||
InputStream::empty()
|
||||
} else {
|
||||
once(async { Ok(input_clone) }).to_input_stream()
|
||||
vec![Ok(input_clone)].into_iter().to_input_stream()
|
||||
};
|
||||
|
||||
context.scope.enter_scope();
|
||||
@ -95,11 +86,11 @@ pub async fn process_row(
|
||||
context.scope.add_var("$it", input);
|
||||
}
|
||||
|
||||
let result = run_block(&captured_block.block, &*context, input_stream).await;
|
||||
let result = run_block(&captured_block.block, &*context, input_stream);
|
||||
|
||||
context.scope.exit_scope();
|
||||
|
||||
Ok(result?.to_output_stream())
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
|
||||
@ -110,40 +101,41 @@ pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
|
||||
dict.into_value()
|
||||
}
|
||||
|
||||
async fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
let args = raw_args.evaluate_once()?;
|
||||
|
||||
let (each_args, input): (EachArgs, _) = raw_args.process().await?;
|
||||
let block = Arc::new(Box::new(each_args.block));
|
||||
let block: CapturedBlock = args.req(0)?;
|
||||
let numbered: bool = args.has_flag("numbered");
|
||||
|
||||
if each_args.numbered.item {
|
||||
Ok(input
|
||||
let block = Arc::new(Box::new(block));
|
||||
|
||||
if numbered {
|
||||
Ok(args
|
||||
.input
|
||||
.enumerate()
|
||||
.then(move |input| {
|
||||
.map(move |input| {
|
||||
let block = block.clone();
|
||||
let context = context.clone();
|
||||
let row = make_indexed_item(input.0, input.1);
|
||||
|
||||
async {
|
||||
match process_row(block, context, row).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
match process_row(block, context, row) {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Value::error(e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
} else {
|
||||
Ok(input
|
||||
.then(move |input| {
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |input| {
|
||||
let block = block.clone();
|
||||
let context = context.clone();
|
||||
|
||||
async {
|
||||
match process_row(block, context, input).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
match process_row(block, context, input) {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Value::error(e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
|
@ -2,9 +2,7 @@ use crate::commands::each::process_row;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{hir::CapturedBlock, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
use serde::Deserialize;
|
||||
|
||||
@ -17,7 +15,6 @@ pub struct EachGroupArgs {
|
||||
//numbered: Tagged<bool>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for EachGroup {
|
||||
fn name(&self) -> &str {
|
||||
"each group"
|
||||
@ -45,16 +42,54 @@ impl WholeStreamCommand for EachGroup {
|
||||
}]
|
||||
}
|
||||
|
||||
async fn run(&self, raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
let (each_args, input): (EachGroupArgs, _) = raw_args.process().await?;
|
||||
let (each_args, input): (EachGroupArgs, _) = raw_args.process()?;
|
||||
let block = Arc::new(Box::new(each_args.block));
|
||||
|
||||
Ok(input
|
||||
.chunks(each_args.group_size.item)
|
||||
.then(move |input| run_block_on_vec(input, block.clone(), context.clone()))
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
let each_group_iterator = EachGroupIterator {
|
||||
block,
|
||||
context,
|
||||
group_size: each_args.group_size.item,
|
||||
input,
|
||||
};
|
||||
|
||||
Ok(each_group_iterator.flatten().to_action_stream())
|
||||
}
|
||||
}
|
||||
|
||||
struct EachGroupIterator {
|
||||
block: Arc<Box<CapturedBlock>>,
|
||||
context: Arc<EvaluationContext>,
|
||||
group_size: usize,
|
||||
input: InputStream,
|
||||
}
|
||||
|
||||
impl Iterator for EachGroupIterator {
|
||||
type Item = OutputStream;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut group = vec![];
|
||||
let mut current_count = 0;
|
||||
|
||||
while let Some(next) = self.input.next() {
|
||||
group.push(next);
|
||||
|
||||
current_count += 1;
|
||||
if current_count >= self.group_size {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if group.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(run_block_on_vec(
|
||||
group,
|
||||
self.block.clone(),
|
||||
self.context.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,43 +97,32 @@ pub(crate) fn run_block_on_vec(
|
||||
input: Vec<Value>,
|
||||
block: Arc<Box<CapturedBlock>>,
|
||||
context: Arc<EvaluationContext>,
|
||||
) -> impl Future<Output = OutputStream> {
|
||||
) -> OutputStream {
|
||||
let value = Value {
|
||||
value: UntaggedValue::Table(input),
|
||||
tag: Tag::unknown(),
|
||||
};
|
||||
|
||||
async {
|
||||
match process_row(block, context, value).await {
|
||||
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.
|
||||
let vec = s.collect::<Vec<_>>().await;
|
||||
match process_row(block, context, value) {
|
||||
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.
|
||||
let vec = s.collect::<Vec<_>>();
|
||||
|
||||
// If it returned just one value, just take that value
|
||||
if vec.len() == 1 {
|
||||
return OutputStream::one(vec.into_iter().next().expect(
|
||||
"This should be impossible, we just checked that vec.len() == 1.",
|
||||
));
|
||||
}
|
||||
|
||||
// If it returned multiple values, we need to put them into a table and
|
||||
// return that.
|
||||
let result = vec.into_iter().collect::<Result<Vec<ReturnSuccess>, _>>();
|
||||
let result_table = match result {
|
||||
Ok(t) => t,
|
||||
Err(e) => return OutputStream::one(Err(e)),
|
||||
};
|
||||
|
||||
let table = result_table
|
||||
.into_iter()
|
||||
.filter_map(|x| x.raw_value())
|
||||
.collect();
|
||||
|
||||
OutputStream::one(Ok(ReturnSuccess::Value(UntaggedValue::Table(table).into())))
|
||||
// If it returned just one value, just take that value
|
||||
if vec.len() == 1 {
|
||||
return OutputStream::one(
|
||||
vec.into_iter()
|
||||
.next()
|
||||
.expect("This should be impossible, we just checked that vec.len() == 1."),
|
||||
);
|
||||
}
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
|
||||
// If it returned multiple values, we need to put them into a table and
|
||||
// return that.
|
||||
OutputStream::one(UntaggedValue::Table(vec).into_untagged_value())
|
||||
}
|
||||
Err(e) => OutputStream::one(Value::error(e)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@ pub struct EachWindowArgs {
|
||||
stride: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for EachWindow {
|
||||
fn name(&self) -> &str {
|
||||
"each window"
|
||||
@ -50,16 +49,15 @@ impl WholeStreamCommand for EachWindow {
|
||||
}]
|
||||
}
|
||||
|
||||
async fn run(&self, raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn run_with_actions(&self, raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
let (each_args, mut input): (EachWindowArgs, _) = raw_args.process().await?;
|
||||
let (each_args, mut input): (EachWindowArgs, _) = raw_args.process()?;
|
||||
let block = Arc::new(Box::new(each_args.block));
|
||||
|
||||
let mut window: Vec<_> = input
|
||||
.by_ref()
|
||||
.take(*each_args.window_size - 1)
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
.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);
|
||||
@ -67,7 +65,7 @@ impl WholeStreamCommand for EachWindow {
|
||||
|
||||
Ok(input
|
||||
.enumerate()
|
||||
.then(move |(i, input)| {
|
||||
.map(move |(i, input)| {
|
||||
// This would probably be more efficient if `last` was a VecDeque
|
||||
// But we can't have that because it needs to be put into a Table
|
||||
window.remove(0);
|
||||
@ -77,17 +75,15 @@ impl WholeStreamCommand for EachWindow {
|
||||
let context = context.clone();
|
||||
let local_window = window.clone();
|
||||
|
||||
async move {
|
||||
if i % stride == 0 {
|
||||
Some(run_block_on_vec(local_window, block, context).await)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if i % stride == 0 {
|
||||
Some(run_block_on_vec(local_window, block, context))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(|x| async { x })
|
||||
.filter_map(|x| x)
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,12 @@
|
||||
use crate::prelude::*;
|
||||
use bigdecimal::Zero;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::Operator;
|
||||
use nu_protocol::{
|
||||
Primitive, Range, RangeInclusion, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{Primitive, Range, RangeInclusion, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct Echo;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct EchoArgs {
|
||||
pub rest: Vec<Value>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Echo {
|
||||
fn name(&self) -> &str {
|
||||
"echo"
|
||||
@ -27,8 +20,8 @@ impl WholeStreamCommand for Echo {
|
||||
"Echo the arguments back to the user."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
echo(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<InputStream, ShellError> {
|
||||
echo(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -47,36 +40,37 @@ impl WholeStreamCommand for Echo {
|
||||
}
|
||||
}
|
||||
|
||||
async fn echo(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (args, _): (EchoArgs, _) = args.process().await?;
|
||||
fn echo(args: CommandArgs) -> Result<InputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let rest: Vec<Value> = args.rest(0)?;
|
||||
|
||||
let stream = args.rest.into_iter().map(|i| match i.as_string() {
|
||||
Ok(s) => OutputStream::one(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(s).into_value(i.tag.clone()),
|
||||
))),
|
||||
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),
|
||||
..
|
||||
} => futures::stream::iter(table.into_iter().map(ReturnSuccess::value))
|
||||
.to_output_stream(),
|
||||
} => InputStream::from_stream(table.into_iter()),
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Range(range)),
|
||||
tag,
|
||||
} => futures::stream::iter(RangeIterator::new(*range, tag)).to_output_stream(),
|
||||
x => OutputStream::one(Ok(ReturnSuccess::Value(x))),
|
||||
} => InputStream::from_stream(RangeIterator::new(*range, tag)),
|
||||
x => InputStream::one(x),
|
||||
},
|
||||
});
|
||||
|
||||
Ok(futures::stream::iter(stream).flatten().to_output_stream())
|
||||
Ok(InputStream::from_stream(stream.flatten()))
|
||||
}
|
||||
|
||||
struct RangeIterator {
|
||||
curr: Primitive,
|
||||
end: Primitive,
|
||||
curr: UntaggedValue,
|
||||
end: UntaggedValue,
|
||||
tag: Tag,
|
||||
is_end_inclusive: bool,
|
||||
moves_up: bool,
|
||||
one: UntaggedValue,
|
||||
negative_one: UntaggedValue,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
impl RangeIterator {
|
||||
@ -93,98 +87,104 @@ impl RangeIterator {
|
||||
|
||||
RangeIterator {
|
||||
moves_up: start <= end,
|
||||
curr: start,
|
||||
end,
|
||||
curr: UntaggedValue::Primitive(start),
|
||||
end: UntaggedValue::Primitive(end),
|
||||
tag,
|
||||
is_end_inclusive: matches!(range.to.1, RangeInclusion::Inclusive),
|
||||
one: UntaggedValue::int(1),
|
||||
negative_one: UntaggedValue::int(-1),
|
||||
done: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for RangeIterator {
|
||||
type Item = Result<ReturnSuccess, ShellError>;
|
||||
type Item = Value;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let ordering = if self.end == Primitive::Nothing {
|
||||
use std::cmp::Ordering;
|
||||
if self.done {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ordering = if self.end == UntaggedValue::Primitive(Primitive::Nothing) {
|
||||
Ordering::Less
|
||||
} else {
|
||||
let result =
|
||||
nu_data::base::coerce_compare_primitive(&self.curr, &self.end).map_err(|_| {
|
||||
ShellError::labeled_error(
|
||||
"Cannot create range",
|
||||
"unsupported range",
|
||||
self.tag.span,
|
||||
)
|
||||
});
|
||||
|
||||
if let Err(result) = result {
|
||||
return Some(Err(result));
|
||||
match (&self.curr, &self.end) {
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Int(x)),
|
||||
UntaggedValue::Primitive(Primitive::Int(y)),
|
||||
) => x.cmp(y),
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Decimal(x)),
|
||||
UntaggedValue::Primitive(Primitive::Decimal(y)),
|
||||
) => x.cmp(y),
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Decimal(x)),
|
||||
UntaggedValue::Primitive(Primitive::Int(y)),
|
||||
) => x.cmp(&(BigDecimal::zero() + y)),
|
||||
(
|
||||
UntaggedValue::Primitive(Primitive::Int(x)),
|
||||
UntaggedValue::Primitive(Primitive::Decimal(y)),
|
||||
) => (BigDecimal::zero() + x).cmp(y),
|
||||
_ => {
|
||||
self.done = true;
|
||||
return Some(
|
||||
UntaggedValue::Error(ShellError::labeled_error(
|
||||
"Cannot create range",
|
||||
"unsupported range",
|
||||
self.tag.span,
|
||||
))
|
||||
.into_untagged_value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let result = result
|
||||
.expect("Internal error: the error case was already protected, but that failed");
|
||||
|
||||
result.compare()
|
||||
};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
if self.moves_up
|
||||
&& (ordering == Ordering::Less || self.is_end_inclusive && ordering == Ordering::Equal)
|
||||
{
|
||||
let output = UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone());
|
||||
let next_value = nu_data::value::compute_values(Operator::Plus, &self.curr, &self.one);
|
||||
|
||||
let next_value = nu_data::value::compute_values(
|
||||
Operator::Plus,
|
||||
&UntaggedValue::Primitive(self.curr.clone()),
|
||||
&UntaggedValue::int(1),
|
||||
);
|
||||
let mut next = match next_value {
|
||||
Ok(result) => result,
|
||||
|
||||
self.curr = match next_value {
|
||||
Ok(result) => match result {
|
||||
UntaggedValue::Primitive(p) => p,
|
||||
_ => {
|
||||
return Some(Err(ShellError::unimplemented(
|
||||
"Internal error: expected a primitive result from increment",
|
||||
)));
|
||||
}
|
||||
},
|
||||
Err((left_type, right_type)) => {
|
||||
return Some(Err(ShellError::coerce_error(
|
||||
left_type.spanned(self.tag.span),
|
||||
right_type.spanned(self.tag.span),
|
||||
)));
|
||||
self.done = true;
|
||||
return Some(
|
||||
UntaggedValue::Error(ShellError::coerce_error(
|
||||
left_type.spanned(self.tag.span),
|
||||
right_type.spanned(self.tag.span),
|
||||
))
|
||||
.into_untagged_value(),
|
||||
);
|
||||
}
|
||||
};
|
||||
Some(ReturnSuccess::value(output))
|
||||
std::mem::swap(&mut self.curr, &mut next);
|
||||
|
||||
Some(next.into_value(self.tag.clone()))
|
||||
} else if !self.moves_up
|
||||
&& (ordering == Ordering::Greater
|
||||
|| self.is_end_inclusive && ordering == Ordering::Equal)
|
||||
{
|
||||
let output = UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone());
|
||||
let next_value =
|
||||
nu_data::value::compute_values(Operator::Plus, &self.curr, &self.negative_one);
|
||||
|
||||
let next_value = nu_data::value::compute_values(
|
||||
Operator::Plus,
|
||||
&UntaggedValue::Primitive(self.curr.clone()),
|
||||
&UntaggedValue::int(-1),
|
||||
);
|
||||
|
||||
self.curr = match next_value {
|
||||
Ok(result) => match result {
|
||||
UntaggedValue::Primitive(p) => p,
|
||||
_ => {
|
||||
return Some(Err(ShellError::unimplemented(
|
||||
"Internal error: expected a primitive result from increment",
|
||||
)));
|
||||
}
|
||||
},
|
||||
let mut next = match next_value {
|
||||
Ok(result) => result,
|
||||
Err((left_type, right_type)) => {
|
||||
return Some(Err(ShellError::coerce_error(
|
||||
left_type.spanned(self.tag.span),
|
||||
right_type.spanned(self.tag.span),
|
||||
)));
|
||||
self.done = true;
|
||||
return Some(
|
||||
UntaggedValue::Error(ShellError::coerce_error(
|
||||
left_type.spanned(self.tag.span),
|
||||
right_type.spanned(self.tag.span),
|
||||
))
|
||||
.into_untagged_value(),
|
||||
);
|
||||
}
|
||||
};
|
||||
Some(ReturnSuccess::value(output))
|
||||
std::mem::swap(&mut self.curr, &mut next);
|
||||
|
||||
Some(next.into_value(self.tag.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ use nu_protocol::{
|
||||
};
|
||||
|
||||
use crate::utils::arguments::arguments;
|
||||
use futures::stream::once;
|
||||
use nu_value_ext::{as_string, ValueExt};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -18,7 +17,6 @@ pub struct Arguments {
|
||||
|
||||
pub struct Command;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"empty?"
|
||||
@ -35,8 +33,8 @@ impl WholeStreamCommand for Command {
|
||||
"Check for empty values."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
is_empty(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
is_empty(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -81,81 +79,75 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
async fn is_empty(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn is_empty(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let name_tag = Arc::new(args.call_info.name_tag.clone());
|
||||
let context = Arc::new(EvaluationContext::from_args(&args));
|
||||
let (Arguments { mut rest }, input) = args.process().await?;
|
||||
let (Arguments { mut rest }, input) = args.process()?;
|
||||
let (columns, default_block): (Vec<ColumnPath>, Option<Box<CapturedBlock>>) =
|
||||
arguments(&mut rest)?;
|
||||
let default_block = Arc::new(default_block);
|
||||
|
||||
if input.is_empty() {
|
||||
let stream = futures::stream::iter(vec![
|
||||
UntaggedValue::Primitive(Primitive::Nothing).into_value(tag)
|
||||
]);
|
||||
let stream = vec![UntaggedValue::Primitive(Primitive::Nothing).into_value(tag)].into_iter();
|
||||
|
||||
return Ok(InputStream::from_stream(stream)
|
||||
.then(move |input| {
|
||||
.map(move |input| {
|
||||
let tag = name_tag.clone();
|
||||
let context = context.clone();
|
||||
let block = default_block.clone();
|
||||
let columns = vec![];
|
||||
|
||||
async {
|
||||
match process_row(context, input, block, columns, tag).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
match process_row(context, input, block, columns, tag) {
|
||||
Ok(s) => s,
|
||||
Err(e) => ActionStream::one(Err(e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream());
|
||||
.to_action_stream());
|
||||
}
|
||||
|
||||
Ok(input
|
||||
.then(move |input| {
|
||||
.map(move |input| {
|
||||
let tag = name_tag.clone();
|
||||
let context = context.clone();
|
||||
let block = default_block.clone();
|
||||
let columns = columns.clone();
|
||||
|
||||
async {
|
||||
match process_row(context, input, block, columns, tag).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
match process_row(context, input, block, columns, tag) {
|
||||
Ok(s) => s,
|
||||
Err(e) => ActionStream::one(Err(e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
async fn process_row(
|
||||
fn process_row(
|
||||
context: Arc<EvaluationContext>,
|
||||
input: Value,
|
||||
default_block: Arc<Option<Box<CapturedBlock>>>,
|
||||
column_paths: Vec<ColumnPath>,
|
||||
tag: Arc<Tag>,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
) -> Result<ActionStream, ShellError> {
|
||||
let _tag = &*tag;
|
||||
let mut out = Arc::new(None);
|
||||
let results = Arc::make_mut(&mut out);
|
||||
|
||||
if let Some(default_block) = &*default_block {
|
||||
let for_block = input.clone();
|
||||
let input_stream = once(async { Ok(for_block) }).to_input_stream();
|
||||
let input_stream = vec![Ok(for_block)].into_iter().to_input_stream();
|
||||
|
||||
context.scope.enter_scope();
|
||||
context.scope.add_vars(&default_block.captured.entries);
|
||||
context.scope.add_var("$it", input.clone());
|
||||
|
||||
let stream = run_block(&default_block.block, &*context, input_stream).await;
|
||||
let stream = run_block(&default_block.block, &*context, input_stream);
|
||||
context.scope.exit_scope();
|
||||
|
||||
let mut stream = stream?;
|
||||
*results = Some({
|
||||
let values = stream.drain_vec().await;
|
||||
let values = stream.drain_vec();
|
||||
|
||||
let errors = context.get_errors();
|
||||
|
||||
@ -186,7 +178,7 @@ async fn process_row(
|
||||
ref tag,
|
||||
} => {
|
||||
if column_paths.is_empty() {
|
||||
Ok(OutputStream::one(ReturnSuccess::value({
|
||||
Ok(ActionStream::one(ReturnSuccess::value({
|
||||
let is_empty = input.is_empty();
|
||||
|
||||
if default_block.is_some() {
|
||||
@ -229,10 +221,10 @@ async fn process_row(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(obj)))
|
||||
Ok(ActionStream::one(ReturnSuccess::value(obj)))
|
||||
}
|
||||
}
|
||||
other => Ok(OutputStream::one(ReturnSuccess::value({
|
||||
other => Ok(ActionStream::one(ReturnSuccess::value({
|
||||
if other.is_empty() {
|
||||
results
|
||||
.clone()
|
||||
|
@ -17,7 +17,6 @@ pub struct EnterArgs {
|
||||
encoding: Option<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Enter {
|
||||
fn name(&self) -> &str {
|
||||
"enter"
|
||||
@ -51,8 +50,8 @@ For a more complete list of encodings please refer to the encoding_rs
|
||||
documentation link at https://docs.rs/encoding_rs/0.8.28/encoding_rs/#statics"#
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
enter(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
enter(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -76,38 +75,21 @@ documentation link at https://docs.rs/encoding_rs/0.8.28/encoding_rs/#statics"#
|
||||
}
|
||||
}
|
||||
|
||||
async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
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().await?;
|
||||
let (EnterArgs { location, encoding }, _) = raw_args.process()?;
|
||||
let location_string = location.display().to_string();
|
||||
let location_clone = location_string.clone();
|
||||
|
||||
if location_string.starts_with("help") {
|
||||
let spec = location_string.split(':').collect::<Vec<&str>>();
|
||||
|
||||
if spec.len() == 2 {
|
||||
let (_, command) = (spec[0], spec[1]);
|
||||
|
||||
if scope.has_command(command) {
|
||||
return Ok(OutputStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterHelpShell(
|
||||
UntaggedValue::string(command).into_value(Tag::unknown()),
|
||||
),
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(OutputStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterHelpShell(UntaggedValue::nothing().into_value(Tag::unknown())),
|
||||
)))
|
||||
} else if location.is_dir() {
|
||||
Ok(OutputStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterShell(location_clone),
|
||||
if location.is_dir() {
|
||||
Ok(ActionStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterShell(location_string),
|
||||
)))
|
||||
} else {
|
||||
// If it's a file, attempt to open the file as a value and enter it
|
||||
@ -118,11 +100,10 @@ async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let (file_extension, tagged_contents) = crate::commands::open::fetch(
|
||||
&full_path,
|
||||
&PathBuf::from(location_clone),
|
||||
&PathBuf::from(location_string),
|
||||
span,
|
||||
encoding,
|
||||
)
|
||||
.await?;
|
||||
)?;
|
||||
|
||||
match tagged_contents.value {
|
||||
UntaggedValue::Primitive(Primitive::String(_)) => {
|
||||
@ -132,6 +113,7 @@ async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let new_args = RawCommandArgs {
|
||||
host,
|
||||
ctrl_c,
|
||||
configs,
|
||||
current_errors,
|
||||
shell_manager,
|
||||
call_info: UnevaluatedCallInfo {
|
||||
@ -142,40 +124,38 @@ async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
span: Span::unknown(),
|
||||
external_redirection: ExternalRedirection::Stdout,
|
||||
},
|
||||
name_tag: tag.clone(),
|
||||
name_tag: tag,
|
||||
},
|
||||
scope: scope.clone(),
|
||||
scope,
|
||||
};
|
||||
let tag = tagged_contents.tag.clone();
|
||||
let mut result = converter
|
||||
.run(new_args.with_input(vec![tagged_contents]))
|
||||
.await?;
|
||||
let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
|
||||
result.drain_vec().await;
|
||||
Ok(futures::stream::iter(result_vec.into_iter().map(
|
||||
move |res| match res {
|
||||
Ok(ReturnSuccess::Value(Value { value, .. })) => Ok(
|
||||
ReturnSuccess::Action(CommandAction::EnterValueShell(Value {
|
||||
let mut result =
|
||||
converter.run(new_args.with_input(vec![tagged_contents]))?;
|
||||
let result_vec: Vec<Value> = result.drain_vec();
|
||||
Ok(result_vec
|
||||
.into_iter()
|
||||
.map(move |res| {
|
||||
let Value { value, .. } = res;
|
||||
Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
|
||||
Value {
|
||||
value,
|
||||
tag: tag.clone(),
|
||||
})),
|
||||
),
|
||||
x => x,
|
||||
},
|
||||
))
|
||||
.to_output_stream())
|
||||
},
|
||||
)))
|
||||
})
|
||||
.to_action_stream())
|
||||
} else {
|
||||
Ok(OutputStream::one(ReturnSuccess::action(
|
||||
Ok(ActionStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterValueShell(tagged_contents),
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Ok(OutputStream::one(ReturnSuccess::action(
|
||||
Ok(ActionStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterValueShell(tagged_contents),
|
||||
)))
|
||||
}
|
||||
}
|
||||
_ => Ok(OutputStream::one(ReturnSuccess::action(
|
||||
_ => Ok(ActionStream::one(ReturnSuccess::action(
|
||||
CommandAction::EnterValueShell(tagged_contents),
|
||||
))),
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ pub struct EveryArgs {
|
||||
skip: Tagged<bool>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Every {
|
||||
fn name(&self) -> &str {
|
||||
"every"
|
||||
@ -36,8 +35,8 @@ impl WholeStreamCommand for Every {
|
||||
"Show (or skip) every n-th row, starting from the first one."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
every(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
every(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -63,15 +62,15 @@ impl WholeStreamCommand for Every {
|
||||
}
|
||||
}
|
||||
|
||||
async fn every(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (EveryArgs { stride, skip }, input) = args.process().await?;
|
||||
fn every(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (EveryArgs { stride, skip }, input) = args.process()?;
|
||||
|
||||
let stride = stride.item;
|
||||
let skip = skip.item;
|
||||
|
||||
Ok(input
|
||||
.enumerate()
|
||||
.filter_map(move |(i, value)| async move {
|
||||
.filter_map(move |(i, value)| {
|
||||
let stride_desired = if stride < 1 { 1 } else { stride } as usize;
|
||||
let should_include = skip == (i % stride_desired != 0);
|
||||
|
||||
@ -81,7 +80,7 @@ async fn every(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
None
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -13,7 +13,6 @@ pub struct ExecArgs {
|
||||
pub rest: Vec<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Exec {
|
||||
fn name(&self) -> &str {
|
||||
"exec"
|
||||
@ -32,8 +31,8 @@ impl WholeStreamCommand for Exec {
|
||||
"Execute command."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
exec(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
exec(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -53,12 +52,12 @@ impl WholeStreamCommand for Exec {
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn exec(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn exec(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process::Command;
|
||||
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (args, _): (ExecArgs, _) = args.process().await?;
|
||||
let (args, _): (ExecArgs, _) = args.process()?;
|
||||
|
||||
let mut command = Command::new(args.command.item);
|
||||
for tagged_arg in args.rest {
|
||||
@ -75,7 +74,7 @@ async fn exec(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
async fn exec(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn exec(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
Err(ShellError::labeled_error(
|
||||
"Error on exec",
|
||||
"exec is not supported on your platform",
|
||||
|
@ -4,7 +4,6 @@ use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape};
|
||||
|
||||
pub struct Exit;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Exit {
|
||||
fn name(&self) -> &str {
|
||||
"exit"
|
||||
@ -24,8 +23,8 @@ impl WholeStreamCommand for Exit {
|
||||
"Exit the current shell (or all shells)."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
exit(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
exit(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -44,8 +43,8 @@ impl WholeStreamCommand for Exit {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn exit(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
pub fn exit(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let code = if let Some(value) = args.call_info.args.nth(0) {
|
||||
value.as_i32()?
|
||||
@ -59,7 +58,7 @@ pub async fn exit(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
CommandAction::LeaveShell(code)
|
||||
};
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::action(command_action)))
|
||||
Ok(ActionStream::one(ReturnSuccess::action(command_action)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -11,7 +11,6 @@ pub struct FirstArgs {
|
||||
rows: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for First {
|
||||
fn name(&self) -> &str {
|
||||
"first"
|
||||
@ -29,8 +28,8 @@ impl WholeStreamCommand for First {
|
||||
"Show only the first number of rows."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
first(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
first(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -52,15 +51,15 @@ impl WholeStreamCommand for First {
|
||||
}
|
||||
}
|
||||
|
||||
async fn first(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (FirstArgs { rows }, input) = args.process().await?;
|
||||
fn first(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (FirstArgs { rows }, input) = args.process()?;
|
||||
let rows_desired = if let Some(quantity) = rows {
|
||||
*quantity
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
Ok(input.take(rows_desired).to_output_stream())
|
||||
Ok(input.take(rows_desired).to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -13,7 +13,6 @@ pub struct Arguments {
|
||||
rest: Vec<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"flatten"
|
||||
@ -27,8 +26,8 @@ impl WholeStreamCommand for Command {
|
||||
"Flatten the table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
flatten(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
flatten(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -52,14 +51,14 @@ impl WholeStreamCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
async fn flatten(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn flatten(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (Arguments { rest: columns }, input) = args.process().await?;
|
||||
let (Arguments { rest: columns }, input) = args.process()?;
|
||||
|
||||
Ok(input
|
||||
.map(move |item| futures::stream::iter(flat_value(&columns, &item, &tag).into_iter()))
|
||||
.map(move |item| flat_value(&columns, &item, &tag).into_iter())
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
enum TableInside<'a> {
|
||||
|
@ -13,7 +13,6 @@ pub struct FormatArgs {
|
||||
pattern: Tagged<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Format {
|
||||
fn name(&self) -> &str {
|
||||
"format"
|
||||
@ -31,8 +30,8 @@ impl WholeStreamCommand for Format {
|
||||
"Format columns into a string using a simple pattern."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
format_command(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
format_command(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -44,51 +43,48 @@ impl WholeStreamCommand for Format {
|
||||
}
|
||||
}
|
||||
|
||||
async fn format_command(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn format_command(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let ctx = Arc::new(EvaluationContext::from_args(&args));
|
||||
let (FormatArgs { pattern }, input) = args.process().await?;
|
||||
let (FormatArgs { pattern }, input) = args.process()?;
|
||||
|
||||
let format_pattern = format(&pattern);
|
||||
let commands = Arc::new(format_pattern);
|
||||
|
||||
Ok(input
|
||||
.then(move |value| {
|
||||
.map(move |value| {
|
||||
let mut output = String::new();
|
||||
let commands = commands.clone();
|
||||
let ctx = ctx.clone();
|
||||
|
||||
async move {
|
||||
for command in &*commands {
|
||||
match command {
|
||||
FormatCommand::Text(s) => {
|
||||
output.push_str(&s);
|
||||
}
|
||||
FormatCommand::Column(c) => {
|
||||
// FIXME: use the correct spans
|
||||
let full_column_path = nu_parser::parse_full_column_path(
|
||||
&(c.to_string()).spanned(Span::unknown()),
|
||||
&ctx.scope,
|
||||
);
|
||||
for command in &*commands {
|
||||
match command {
|
||||
FormatCommand::Text(s) => {
|
||||
output.push_str(&s);
|
||||
}
|
||||
FormatCommand::Column(c) => {
|
||||
// FIXME: use the correct spans
|
||||
let full_column_path = nu_parser::parse_full_column_path(
|
||||
&(c.to_string()).spanned(Span::unknown()),
|
||||
&ctx.scope,
|
||||
);
|
||||
|
||||
ctx.scope.enter_scope();
|
||||
ctx.scope.add_var("$it", value.clone());
|
||||
let result = evaluate_baseline_expr(&full_column_path.0, &*ctx).await;
|
||||
ctx.scope.exit_scope();
|
||||
ctx.scope.enter_scope();
|
||||
ctx.scope.add_var("$it", value.clone());
|
||||
let result = evaluate_baseline_expr(&full_column_path.0, &*ctx);
|
||||
ctx.scope.exit_scope();
|
||||
|
||||
if let Ok(c) = result {
|
||||
output
|
||||
.push_str(&value::format_leaf(c.borrow()).plain_string(100_000))
|
||||
} else {
|
||||
// That column doesn't match, so don't emit anything
|
||||
}
|
||||
if let Ok(c) = result {
|
||||
output.push_str(&value::format_leaf(c.borrow()).plain_string(100_000))
|
||||
} else {
|
||||
// That column doesn't match, so don't emit anything
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReturnSuccess::value(UntaggedValue::string(output).into_untagged_value())
|
||||
}
|
||||
|
||||
ReturnSuccess::value(UntaggedValue::string(output).into_untagged_value())
|
||||
})
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -16,7 +16,6 @@ pub struct Arguments {
|
||||
format: Tagged<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FileSize {
|
||||
fn name(&self) -> &str {
|
||||
"format filesize"
|
||||
@ -40,8 +39,8 @@ impl WholeStreamCommand for FileSize {
|
||||
"Converts a column of filesizes to some specified format"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
filesize(args).await
|
||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
filesize(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -60,11 +59,11 @@ impl WholeStreamCommand for FileSize {
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_row(
|
||||
fn process_row(
|
||||
input: Value,
|
||||
format: Tagged<String>,
|
||||
field: Arc<ColumnPath>,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
) -> Result<ActionStream, ShellError> {
|
||||
Ok({
|
||||
let replace_for = get_data_by_column_path(&input, &field, move |_, _, error| error);
|
||||
match replace_for {
|
||||
@ -76,7 +75,7 @@ async fn process_row(
|
||||
{
|
||||
let byte_format = InlineShape::format_bytes(&fs, Some(&format.item));
|
||||
let byte_value = Value::from(byte_format.1);
|
||||
OutputStream::one(ReturnSuccess::value(
|
||||
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 {
|
||||
@ -87,29 +86,27 @@ async fn process_row(
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
Err(e) => ActionStream::one(Err(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn filesize(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { field, format }, input) = raw_args.process().await?;
|
||||
fn filesize(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||
let (Arguments { field, format }, input) = raw_args.process()?;
|
||||
let field = Arc::new(field);
|
||||
|
||||
Ok(input
|
||||
.then(move |input| {
|
||||
.map(move |input| {
|
||||
let format = format.clone();
|
||||
let field = field.clone();
|
||||
|
||||
async {
|
||||
match process_row(input, format, field).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => OutputStream::one(Err(e)),
|
||||
}
|
||||
match process_row(input, format, field) {
|
||||
Ok(s) => s,
|
||||
Err(e) => ActionStream::one(Err(e)),
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.to_output_stream())
|
||||
.to_action_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
||||
pub struct From;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for From {
|
||||
fn name(&self) -> &str {
|
||||
"from"
|
||||
@ -19,10 +18,10 @@ impl WholeStreamCommand for From {
|
||||
"Parse content (string or binary) as a table (input format based on subcommand, like csv, ini, json, toml)."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(
|
||||
UntaggedValue::string(get_full_help(&From, &args.scope)).into_value(Tag::unknown()),
|
||||
)))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,16 +4,9 @@ use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
pub struct FromCSV;
|
||||
pub struct FromCsv;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromCSVArgs {
|
||||
noheaders: bool,
|
||||
separator: Option<Value>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromCSV {
|
||||
impl WholeStreamCommand for FromCsv {
|
||||
fn name(&self) -> &str {
|
||||
"from csv"
|
||||
}
|
||||
@ -37,8 +30,8 @@ impl WholeStreamCommand for FromCSV {
|
||||
"Parse text as .csv and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_csv(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_csv(args)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -67,16 +60,14 @@ impl WholeStreamCommand for FromCSV {
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let noheaders = args.has_flag("noheaders");
|
||||
let separator: Option<Value> = args.get_flag("separator")?;
|
||||
let input = args.input;
|
||||
|
||||
let (
|
||||
FromCSVArgs {
|
||||
noheaders,
|
||||
separator,
|
||||
},
|
||||
input,
|
||||
) = args.process().await?;
|
||||
let sep = match separator {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(s)),
|
||||
@ -100,18 +91,18 @@ async fn from_csv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
_ => ',',
|
||||
};
|
||||
|
||||
from_delimited_data(noheaders, sep, "CSV", input, name).await
|
||||
from_delimited_data(noheaders, sep, "CSV", input, name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromCSV;
|
||||
use super::FromCsv;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromCSV {})
|
||||
test_examples(FromCsv {})
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ fn from_delimited_string_to_value(
|
||||
Ok(UntaggedValue::Table(rows).into_value(&tag))
|
||||
}
|
||||
|
||||
pub async fn from_delimited_data(
|
||||
pub fn from_delimited_data(
|
||||
noheaders: bool,
|
||||
sep: char,
|
||||
format_name: &'static str,
|
||||
@ -53,7 +53,7 @@ pub async fn from_delimited_data(
|
||||
name: Tag,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let name_tag = name;
|
||||
let concat_string = input.collect_string(name_tag.clone()).await?;
|
||||
let concat_string = input.collect_string(name_tag.clone())?;
|
||||
let sample_lines = concat_string.item.lines().take(3).collect_vec().join("\n");
|
||||
|
||||
match from_delimited_string_to_value(concat_string.item, noheaders, sep, name_tag.clone()) {
|
||||
@ -61,7 +61,7 @@ pub async fn from_delimited_data(
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => Ok(futures::stream::iter(list).to_output_stream()),
|
||||
} => Ok(list.into_iter().to_output_stream()),
|
||||
x => Ok(OutputStream::one(x)),
|
||||
},
|
||||
Err(err) => {
|
||||
@ -80,7 +80,7 @@ pub async fn from_delimited_data(
|
||||
Err(ShellError::labeled_error_with_secondary(
|
||||
line_one,
|
||||
line_two,
|
||||
name_tag.clone(),
|
||||
name_tag,
|
||||
"value originates from here",
|
||||
concat_string.tag,
|
||||
))
|
||||
|
@ -3,21 +3,14 @@ use ::eml_parser::eml::*;
|
||||
use ::eml_parser::EmlParser;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_protocol::{Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct FromEML;
|
||||
pub struct FromEml;
|
||||
|
||||
const DEFAULT_BODY_PREVIEW: usize = 50;
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct FromEMLArgs {
|
||||
#[serde(rename(deserialize = "preview-body"))]
|
||||
preview_body: Option<Tagged<usize>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromEML {
|
||||
impl WholeStreamCommand for FromEml {
|
||||
fn name(&self) -> &str {
|
||||
"from eml"
|
||||
}
|
||||
@ -35,8 +28,8 @@ impl WholeStreamCommand for FromEML {
|
||||
"Parse text as .eml and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_eml(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_eml(args)
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,15 +66,15 @@ fn headerfieldvalue_to_value(tag: &Tag, value: &HeaderFieldValue) -> UntaggedVal
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_eml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_eml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (eml_args, input): (FromEMLArgs, _) = args.process().await?;
|
||||
let value = input.collect_string(tag.clone()).await?;
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let body_preview = eml_args
|
||||
.preview_body
|
||||
.map(|b| b.item)
|
||||
.unwrap_or(DEFAULT_BODY_PREVIEW);
|
||||
let preview_body: Option<Tagged<usize>> = args.get_flag("preview-body")?;
|
||||
|
||||
let value = args.input.collect_string(tag.clone())?;
|
||||
|
||||
let body_preview = preview_body.map(|b| b.item).unwrap_or(DEFAULT_BODY_PREVIEW);
|
||||
|
||||
let eml = EmlParser::from_string(value.item)
|
||||
.with_body_preview(body_preview)
|
||||
@ -116,18 +109,18 @@ async fn from_eml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
dict.insert_untagged("Body", UntaggedValue::string(body));
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(dict.into_value())))
|
||||
Ok(OutputStream::one(dict.into_value()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromEML;
|
||||
use super::FromEml;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromEML {})
|
||||
test_examples(FromEml {})
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,11 @@ use ical::parser::ical::component::*;
|
||||
use ical::property::Property;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use std::io::BufReader;
|
||||
|
||||
pub struct FromIcs;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromIcs {
|
||||
fn name(&self) -> &str {
|
||||
"from ics"
|
||||
@ -23,17 +22,17 @@ impl WholeStreamCommand for FromIcs {
|
||||
"Parse text as .ics and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ics(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ics(args)
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_ics(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn from_ics(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let input_string = input.collect_string(tag.clone()).await?.item;
|
||||
let input_string = input.collect_string(tag.clone())?.item;
|
||||
let input_bytes = input_string.as_bytes();
|
||||
let buf_reader = BufReader::new(input_bytes);
|
||||
let parser = ical::IcalParser::new(buf_reader);
|
||||
@ -44,8 +43,8 @@ async fn from_ics(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
for calendar in parser {
|
||||
match calendar {
|
||||
Ok(c) => output.push(ReturnSuccess::value(calendar_to_value(c, tag.clone()))),
|
||||
Err(_) => output.push(Err(ShellError::labeled_error(
|
||||
Ok(c) => output.push(calendar_to_value(c, tag.clone())),
|
||||
Err(_) => output.push(Value::error(ShellError::labeled_error(
|
||||
"Could not parse as .ics",
|
||||
"input cannot be parsed as .ics",
|
||||
tag.clone(),
|
||||
@ -53,7 +52,7 @@ async fn from_ics(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(futures::stream::iter(output).to_output_stream())
|
||||
Ok(output.into_iter().to_output_stream())
|
||||
}
|
||||
|
||||
fn calendar_to_value(calendar: IcalCalendar, tag: Tag) -> Value {
|
||||
|
@ -4,10 +4,9 @@ use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct FromINI;
|
||||
pub struct FromIni;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromINI {
|
||||
impl WholeStreamCommand for FromIni {
|
||||
fn name(&self) -> &str {
|
||||
"from ini"
|
||||
}
|
||||
@ -20,8 +19,8 @@ impl WholeStreamCommand for FromINI {
|
||||
"Parse text as .ini and create table"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ini(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ini(args)
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,18 +59,18 @@ pub fn from_ini_string_to_value(
|
||||
Ok(convert_ini_top_to_nu_value(&v, tag))
|
||||
}
|
||||
|
||||
async fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
let concat_string = input.collect_string(tag.clone()).await?;
|
||||
let concat_string = input.collect_string(tag.clone())?;
|
||||
|
||||
match from_ini_string_to_value(concat_string.item, tag.clone()) {
|
||||
Ok(x) => match x {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => Ok(futures::stream::iter(list).to_output_stream()),
|
||||
} => Ok(list.into_iter().to_output_stream()),
|
||||
x => Ok(OutputStream::one(x)),
|
||||
},
|
||||
Err(_) => Err(ShellError::labeled_error_with_secondary(
|
||||
@ -86,13 +85,13 @@ async fn from_ini(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromINI;
|
||||
use super::FromIni;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromINI {})
|
||||
test_examples(FromIni {})
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,11 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
|
||||
pub struct FromJSON;
|
||||
pub struct FromJson;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromJSONArgs {
|
||||
objects: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromJSON {
|
||||
impl WholeStreamCommand for FromJson {
|
||||
fn name(&self) -> &str {
|
||||
"from json"
|
||||
}
|
||||
@ -28,8 +22,8 @@ impl WholeStreamCommand for FromJSON {
|
||||
"Parse text as .json and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_json(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_json(args)
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,29 +62,32 @@ pub fn from_json_string_to_value(s: String, tag: impl Into<Tag>) -> nu_json::Res
|
||||
Ok(convert_json_value_to_nu_value(&v, tag))
|
||||
}
|
||||
|
||||
async fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (FromJSONArgs { objects }, input) = args.process().await?;
|
||||
let concat_string = input.collect_string(name_tag.clone()).await?;
|
||||
let args = args.evaluate_once()?;
|
||||
let objects = args.has_flag("objects");
|
||||
|
||||
let concat_string = args.input.collect_string(name_tag.clone())?;
|
||||
|
||||
let string_clone: Vec<_> = concat_string.item.lines().map(|x| x.to_string()).collect();
|
||||
|
||||
if objects {
|
||||
Ok(
|
||||
futures::stream::iter(string_clone.into_iter().filter_map(move |json_str| {
|
||||
Ok(string_clone
|
||||
.into_iter()
|
||||
.filter_map(move |json_str| {
|
||||
if json_str.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
match from_json_string_to_value(json_str, &name_tag) {
|
||||
Ok(x) => Some(ReturnSuccess::value(x)),
|
||||
Ok(x) => Some(x),
|
||||
Err(e) => {
|
||||
let mut message = "Could not parse as JSON (".to_string();
|
||||
message.push_str(&e.to_string());
|
||||
message.push(')');
|
||||
|
||||
Some(Err(ShellError::labeled_error_with_secondary(
|
||||
Some(Value::error(ShellError::labeled_error_with_secondary(
|
||||
message,
|
||||
"input cannot be parsed as JSON",
|
||||
name_tag.clone(),
|
||||
@ -99,27 +96,24 @@ async fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
)))
|
||||
}
|
||||
}
|
||||
}))
|
||||
.to_output_stream(),
|
||||
)
|
||||
})
|
||||
.to_output_stream())
|
||||
} else {
|
||||
match from_json_string_to_value(concat_string.item, name_tag.clone()) {
|
||||
Ok(x) => match x {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => Ok(
|
||||
futures::stream::iter(list.into_iter().map(ReturnSuccess::value))
|
||||
.to_output_stream(),
|
||||
),
|
||||
x => Ok(OutputStream::one(ReturnSuccess::value(x))),
|
||||
} => Ok(list.into_iter().to_output_stream()),
|
||||
|
||||
x => Ok(OutputStream::one(x)),
|
||||
},
|
||||
Err(e) => {
|
||||
let mut message = "Could not parse as JSON (".to_string();
|
||||
message.push_str(&e.to_string());
|
||||
message.push(')');
|
||||
|
||||
Ok(OutputStream::one(Err(
|
||||
Ok(OutputStream::one(Value::error(
|
||||
ShellError::labeled_error_with_secondary(
|
||||
message,
|
||||
"input cannot be parsed as JSON",
|
||||
@ -135,13 +129,13 @@ async fn from_json(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromJSON;
|
||||
use super::FromJson;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromJSON {})
|
||||
test_examples(FromJson {})
|
||||
}
|
||||
}
|
||||
|
@ -3,50 +3,34 @@ use calamine::*;
|
||||
use nu_data::TaggedListBuilder;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
|
||||
use std::io::Cursor;
|
||||
|
||||
pub struct FromODS;
|
||||
pub struct FromOds;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromODSArgs {
|
||||
noheaders: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromODS {
|
||||
impl WholeStreamCommand for FromOds {
|
||||
fn name(&self) -> &str {
|
||||
"from ods"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("from ods").switch(
|
||||
"noheaders",
|
||||
"don't treat the first row as column names",
|
||||
Some('n'),
|
||||
)
|
||||
Signature::build("from ods")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Parse OpenDocument Spreadsheet(.ods) data and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ods(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ods(args)
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_ods(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_ods(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let span = tag.span;
|
||||
|
||||
let (
|
||||
FromODSArgs {
|
||||
noheaders: _noheaders,
|
||||
},
|
||||
input,
|
||||
) = args.process().await?;
|
||||
let bytes = input.collect_binary(tag.clone()).await?;
|
||||
let bytes = args.input.collect_binary(tag.clone())?;
|
||||
let buf: Cursor<Vec<u8>> = Cursor::new(bytes.item);
|
||||
let mut ods = Ods::<_>::new(buf).map_err(|_| {
|
||||
ShellError::labeled_error("Could not load ods file", "could not load ods file", &tag)
|
||||
@ -88,18 +72,18 @@ async fn from_ods(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(dict.into_value())))
|
||||
Ok(OutputStream::one(dict.into_value()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromODS;
|
||||
use super::FromOds;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromODS {})
|
||||
test_examples(FromOds {})
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,15 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
Primitive, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
};
|
||||
use nu_protocol::{Primitive, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct FromSSV;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromSSVArgs {
|
||||
noheaders: bool,
|
||||
#[serde(rename(deserialize = "aligned-columns"))]
|
||||
aligned_columns: bool,
|
||||
#[serde(rename(deserialize = "minimum-spaces"))]
|
||||
minimum_spaces: Option<Tagged<usize>>,
|
||||
}
|
||||
pub struct FromSsv;
|
||||
|
||||
const STRING_REPRESENTATION: &str = "from ssv";
|
||||
const DEFAULT_MINIMUM_SPACES: usize = 2;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromSSV {
|
||||
impl WholeStreamCommand for FromSsv {
|
||||
fn name(&self) -> &str {
|
||||
STRING_REPRESENTATION
|
||||
}
|
||||
@ -46,8 +34,8 @@ impl WholeStreamCommand for FromSSV {
|
||||
"Parse text as space-separated values and create a table. The default minimum number of spaces counted as a separator is 2."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ssv(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_ssv(args)
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,17 +235,15 @@ fn from_ssv_string_to_value(
|
||||
UntaggedValue::Table(rows).into_value(&tag)
|
||||
}
|
||||
|
||||
async fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (
|
||||
FromSSVArgs {
|
||||
noheaders,
|
||||
aligned_columns,
|
||||
minimum_spaces,
|
||||
},
|
||||
input,
|
||||
) = args.process().await?;
|
||||
let concat_string = input.collect_string(name.clone()).await?;
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let noheaders = args.has_flag("noheaders");
|
||||
let aligned_columns = args.has_flag("aligned-columns");
|
||||
let minimum_spaces: Option<Tagged<usize>> = args.get_flag("minimum-spaces")?;
|
||||
|
||||
let concat_string = args.input.collect_string(name.clone())?;
|
||||
let split_at = match minimum_spaces {
|
||||
Some(number) => number.item,
|
||||
None => DEFAULT_MINIMUM_SPACES,
|
||||
@ -269,15 +255,13 @@ async fn from_ssv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
noheaders,
|
||||
aligned_columns,
|
||||
split_at,
|
||||
name.clone(),
|
||||
name,
|
||||
) {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => {
|
||||
futures::stream::iter(list.into_iter().map(ReturnSuccess::value)).to_output_stream()
|
||||
}
|
||||
x => OutputStream::one(ReturnSuccess::value(x)),
|
||||
} => list.into_iter().to_output_stream(),
|
||||
x => OutputStream::one(x),
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -489,9 +473,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use super::FromSSV;
|
||||
use super::FromSsv;
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromSSV {})
|
||||
test_examples(FromSsv {})
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
|
||||
pub struct FromTOML;
|
||||
pub struct FromToml;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromTOML {
|
||||
impl WholeStreamCommand for FromToml {
|
||||
fn name(&self) -> &str {
|
||||
"from toml"
|
||||
}
|
||||
@ -19,8 +18,8 @@ impl WholeStreamCommand for FromTOML {
|
||||
"Parse text as .toml and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_toml(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_toml(args)
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,21 +60,20 @@ pub fn from_toml_string_to_value(s: String, tag: impl Into<Tag>) -> Result<Value
|
||||
Ok(convert_toml_value_to_nu_value(&v, tag))
|
||||
}
|
||||
|
||||
pub async fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
pub fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let concat_string = input.collect_string(tag.clone()).await?;
|
||||
let concat_string = input.collect_string(tag.clone())?;
|
||||
Ok(
|
||||
match from_toml_string_to_value(concat_string.item, tag.clone()) {
|
||||
Ok(x) => match x {
|
||||
Value {
|
||||
value: UntaggedValue::Table(list),
|
||||
..
|
||||
} => futures::stream::iter(list.into_iter().map(ReturnSuccess::value))
|
||||
.to_output_stream(),
|
||||
x => OutputStream::one(ReturnSuccess::value(x)),
|
||||
} => list.into_iter().to_output_stream(),
|
||||
x => OutputStream::one(x),
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(ShellError::labeled_error_with_secondary(
|
||||
@ -92,13 +90,13 @@ pub async fn from_toml(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromTOML;
|
||||
use super::FromToml;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromTOML {})
|
||||
test_examples(FromToml {})
|
||||
}
|
||||
}
|
||||
|
@ -4,15 +4,9 @@ use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::Signature;
|
||||
|
||||
pub struct FromTSV;
|
||||
pub struct FromTsv;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FromTSVArgs {
|
||||
noheaders: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromTSV {
|
||||
impl WholeStreamCommand for FromTsv {
|
||||
fn name(&self) -> &str {
|
||||
"from tsv"
|
||||
}
|
||||
@ -29,27 +23,29 @@ impl WholeStreamCommand for FromTSV {
|
||||
"Parse text as .tsv and create table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_tsv(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_tsv(args)
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
fn from_tsv(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.clone();
|
||||
let (FromTSVArgs { noheaders }, input) = args.process().await?;
|
||||
let args = args.evaluate_once()?;
|
||||
let noheaders = args.has_flag("noheaders");
|
||||
let input = args.input;
|
||||
|
||||
from_delimited_data(noheaders, '\t', "TSV", input, name).await
|
||||
from_delimited_data(noheaders, '\t', "TSV", input, name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromTSV;
|
||||
use super::FromTsv;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromTSV {})
|
||||
test_examples(FromTsv {})
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue};
|
||||
use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
|
||||
|
||||
pub struct FromURL;
|
||||
pub struct FromUrl;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for FromURL {
|
||||
impl WholeStreamCommand for FromUrl {
|
||||
fn name(&self) -> &str {
|
||||
"from url"
|
||||
}
|
||||
@ -19,17 +18,17 @@ impl WholeStreamCommand for FromURL {
|
||||
"Parse url-encoded string as a table."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_url(args).await
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
from_url(args)
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once().await?;
|
||||
fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let concat_string = input.collect_string(tag.clone()).await?;
|
||||
let concat_string = input.collect_string(tag.clone())?;
|
||||
|
||||
let result = serde_urlencoded::from_str::<Vec<(String, String)>>(&concat_string.item);
|
||||
|
||||
@ -41,7 +40,7 @@ async fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
row.insert_untagged(k, UntaggedValue::string(v));
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(row.into_value())))
|
||||
Ok(OutputStream::one(row.into_value()))
|
||||
}
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"String not compatible with url-encoding",
|
||||
@ -55,13 +54,13 @@ async fn from_url(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FromURL;
|
||||
use super::FromUrl;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(FromURL {})
|
||||
test_examples(FromUrl {})
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user