Merge branch 'master' of github.com:nushell/nushell

This commit is contained in:
Sam Hedin 2020-06-13 22:10:12 +02:00
commit 3ed608d9da
76 changed files with 2526 additions and 2185 deletions

1
Cargo.lock generated
View File

@ -2247,6 +2247,7 @@ dependencies = [
"dirs 2.0.2", "dirs 2.0.2",
"dunce", "dunce",
"eml-parser", "eml-parser",
"encoding_rs",
"filesize", "filesize",
"futures 0.3.5", "futures 0.3.5",
"futures-util", "futures-util",

View File

@ -303,6 +303,10 @@ Nu is in heavy development, and will naturally change as it matures and people u
| Completions | | X | | | | Completions are currently barebones, at best | Completions | | X | | | | Completions are currently barebones, at best
| Type-checking | | X | | | | Commands check basic types, but input/output isn't checked | Type-checking | | X | | | | Commands check basic types, but input/output isn't checked
# Current Roadmap
We've added a `Roadmap Board` to help collaboratively capture the direction we're going for the current release as well as capture some important issues we'd like to see in NuShell. You can find the Roadmap [here](https://github.com/nushell/nushell/projects/2).
# Contributing # Contributing
See [Contributing](CONTRIBUTING.md) for details. See [Contributing](CONTRIBUTING.md) for details.

60
TODO.md
View File

@ -1,60 +0,0 @@
This pattern is extremely repetitive and can be abstracted:
```rs
let args = args.evaluate_once(registry)?;
let tag = args.name_tag();
let input = args.input;
let stream = async_stream! {
let values: Vec<Value> = input.values.collect().await;
let mut concat_string = String::new();
let mut latest_tag: Option<Tag> = None;
for value in values {
latest_tag = Some(value_tag.clone());
let value_span = value.tag.span;
match &value.value {
UntaggedValue::Primitive(Primitive::String(s)) => {
concat_string.push_str(&s);
concat_string.push_str("\n");
}
_ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline",
"requires string input",
name_span,
"value originates from here",
value_span,
)),
}
}
```
Mandatory and Optional in parse_command
trace_remaining?
select_fields and select_fields take unnecessary Tag
Value#value should be Value#untagged
Unify dictionary building, probably around a macro
sys plugin in own crate
textview in own crate
Combine atomic and atomic_parse in parser
at_end_possible_ws needs to be comment and separator sensitive
Eliminate unnecessary `nodes` parser
#[derive(HasSpan)]
Figure out a solution for the duplication in stuff like NumberShape vs. NumberExpressionShape
use `struct Expander` from signature.rs

View File

@ -92,6 +92,7 @@ trash = { version = "1.0.1", optional = true }
clipboard = { version = "0.5", optional = true } clipboard = { version = "0.5", optional = true }
starship = { version = "0.41.3", optional = true } starship = { version = "0.41.3", optional = true }
rayon = "1.3.0" rayon = "1.3.0"
encoding_rs = "0.8.23"
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
users = "0.10.0" users = "0.10.0"

View File

@ -45,8 +45,7 @@ impl WholeStreamCommand for Alias {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
// args.process(registry, alias)?.run() alias(args, registry).await
alias(args, registry)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -65,14 +64,21 @@ impl WholeStreamCommand for Alias {
} }
} }
// <<<<<<< HEAD pub async fn alias(
// pub fn alias(alias_args: AliasArgs, ctx: RunnableContext) -> Result<OutputStream, ShellError> { args: CommandArgs,
// ======= registry: &CommandRegistry,
pub fn alias(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let mut raw_input = args.raw_input.clone(); let mut raw_input = args.raw_input.clone();
let (AliasArgs { name, args: list, block, save}, ctx) = args.process(&registry).await?; let (
AliasArgs {
name,
args: list,
block,
save,
},
_ctx,
) = args.process(&registry).await?;
let mut processed_args: Vec<String> = vec![]; let mut processed_args: Vec<String> = vec![];
if let Some(true) = save { if let Some(true) = save {
@ -80,14 +86,18 @@ pub fn alias(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
// process the alias to remove the --save flag // process the alias to remove the --save flag
let left_brace = raw_input.find('{').unwrap_or(0); let left_brace = raw_input.find('{').unwrap_or(0);
let right_brace = raw_input.rfind('}').unwrap_or(raw_input.len()); let right_brace = raw_input.rfind('}').unwrap_or_else(|| raw_input.len());
let mut left = raw_input[..left_brace].replace("--save", "").replace("-s", ""); let left = raw_input[..left_brace]
let mut right = raw_input[right_brace..].replace("--save", "").replace("-s", ""); .replace("--save", "")
.replace("-s", "");
let right = raw_input[right_brace..]
.replace("--save", "")
.replace("-s", "");
raw_input = format!("{}{}{}", left, &raw_input[left_brace..right_brace], right); raw_input = format!("{}{}{}", left, &raw_input[left_brace..right_brace], right);
// create a value from raw_input alias // create a value from raw_input alias
let alias: Value = raw_input.trim().to_string().into(); let alias: Value = raw_input.trim().to_string().into();
let alias_start = raw_input.find("[").unwrap_or(0); // used to check if the same alias already exists let alias_start = raw_input.find('[').unwrap_or(0); // used to check if the same alias already exists
// add to startup if alias doesn't exist and replce if it does // add to startup if alias doesn't exist and replce if it does
match result.get_mut("startup") { match result.get_mut("startup") {
@ -104,7 +114,7 @@ pub fn alias(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
} }
} }
None => { None => {
let mut table = UntaggedValue::table(&[alias]); let table = UntaggedValue::table(&[alias]);
result.insert("startup".to_string(), table.into_value(Tag::default())); result.insert("startup".to_string(), table.into_value(Tag::default()));
} }
} }
@ -115,13 +125,17 @@ pub fn alias(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
if let Ok(string) = item.as_string() { if let Ok(string) = item.as_string() {
processed_args.push(format!("${}", string)); processed_args.push(format!("${}", string));
} else { } else {
yield Err(ShellError::labeled_error("Expected a string", "expected a string", item.tag())); return Err(ShellError::labeled_error(
"Expected a string",
"expected a string",
item.tag(),
));
} }
} }
yield ReturnSuccess::action(CommandAction::AddAlias(name.to_string(), processed_args, block.clone()))
};
Ok(stream.to_output_stream()) Ok(OutputStream::one(ReturnSuccess::action(
CommandAction::AddAlias(name.to_string(), processed_args, block),
)))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -9,7 +9,6 @@ use parking_lot::Mutex;
use prettytable::format::{FormatBuilder, LinePosition, LineSeparator}; use prettytable::format::{FormatBuilder, LinePosition, LineSeparator};
use prettytable::{color, Attr, Cell, Row, Table}; use prettytable::{color, Attr, Cell, Row, Table};
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use textwrap::fill; use textwrap::fill;
pub struct Autoview; pub struct Autoview;
@ -115,23 +114,12 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
match input_stream.next().await { match input_stream.next().await {
Some(y) => { Some(y) => {
let ctrl_c = context.ctrl_c.clone(); let ctrl_c = context.ctrl_c.clone();
let stream = async_stream! { let xy = vec![x, y];
yield Ok(x); let xy_stream = futures::stream::iter(xy)
yield Ok(y); .chain(input_stream)
.interruptible(ctrl_c);
loop { let stream = InputStream::from_stream(xy_stream);
match input_stream.next().await {
Some(z) => {
if ctrl_c.load(Ordering::SeqCst) {
break;
}
yield Ok(z);
}
_ => break,
}
}
};
let stream = stream.to_input_stream();
if let Some(table) = table { if let Some(table) = table {
let command_args = create_default_command_args(&context).with_input(stream); let command_args = create_default_command_args(&context).with_input(stream);

View File

@ -4,9 +4,7 @@ use crate::utils::data_processing::{reducer_for, Reduce};
use bigdecimal::FromPrimitive; use bigdecimal::FromPrimitive;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::hir::{convert_number_to_u64, Number, Operator}; use nu_protocol::hir::{convert_number_to_u64, Number, Operator};
use nu_protocol::{ use nu_protocol::{Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
Dictionary, Primitive, ReturnSuccess, ReturnValue, Signature, UntaggedValue, Value,
};
use num_traits::identities::Zero; use num_traits::identities::Zero;
use indexmap::map::IndexMap; use indexmap::map::IndexMap;
@ -42,6 +40,7 @@ impl WholeStreamCommand for Average {
name: args.call_info.name_tag, name: args.call_info.name_tag,
raw_input: args.raw_input, raw_input: args.raw_input,
}) })
.await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -53,34 +52,29 @@ impl WholeStreamCommand for Average {
} }
} }
fn average( async fn average(
RunnableContext { RunnableContext {
mut input, name, .. mut input, name, ..
}: RunnableContext, }: RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let stream = async_stream! { let values: Vec<Value> = input.drain_vec().await;
let mut values: Vec<Value> = input.drain_vec().await;
let action = reducer_for(Reduce::Sum);
if values.iter().all(|v| if let UntaggedValue::Primitive(_) = v.value {true} else {false}) { if values.iter().all(|v| v.is_primitive()) {
match avg(&values, name) { match avg(&values, name) {
Ok(result) => yield ReturnSuccess::value(result), Ok(result) => Ok(OutputStream::one(ReturnSuccess::value(result))),
Err(err) => yield Err(err), Err(err) => Err(err),
} }
} else { } else {
let mut column_values = IndexMap::new(); let mut column_values = IndexMap::new();
for value in values { for value in values {
match value.value { if let UntaggedValue::Row(row_dict) = value.value {
UntaggedValue::Row(row_dict) => {
for (key, value) in row_dict.entries.iter() { for (key, value) in row_dict.entries.iter() {
column_values column_values
.entry(key.clone()) .entry(key.clone())
.and_modify(|v: &mut Vec<Value>| v.push(value.clone())) .and_modify(|v: &mut Vec<Value>| v.push(value.clone()))
.or_insert(vec![value.clone()]); .or_insert(vec![value.clone()]);
} }
}, }
table => {},
};
} }
let mut column_totals = IndexMap::new(); let mut column_totals = IndexMap::new();
@ -89,17 +83,17 @@ fn average(
Ok(result) => { Ok(result) => {
column_totals.insert(col_name, result); column_totals.insert(col_name, result);
} }
Err(err) => yield Err(err), Err(err) => return Err(err),
} }
} }
yield ReturnSuccess::value(
UntaggedValue::Row(Dictionary {entries: column_totals}).into_untagged_value())
}
};
let stream: BoxStream<'static, ReturnValue> = stream.boxed(); Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(Dictionary {
Ok(stream.to_output_stream()) entries: column_totals,
})
.into_untagged_value(),
)))
}
} }
fn avg(values: &[Value], name: impl Into<Tag>) -> Result<Value, ShellError> { fn avg(values: &[Value], name: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -389,20 +389,26 @@ impl WholeStreamCommand for FnFilterCommand {
ctrl_c, ctrl_c,
shell_manager, shell_manager,
call_info, call_info,
mut input, input,
.. ..
}: CommandArgs, }: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let host: Arc<parking_lot::Mutex<dyn Host>> = host.clone(); let registry = Arc::new(registry.clone());
let registry: CommandRegistry = registry.clone();
let func = self.func; let func = self.func;
let stream = async_stream! { Ok(input
while let Some(it) = input.next().await { .then(move |it| {
let host = host.clone();
let registry = registry.clone(); let registry = registry.clone();
let call_info = match call_info.clone().evaluate_with_new_it(&registry, &it).await { let ctrl_c = ctrl_c.clone();
Err(err) => { yield Err(err); return; }, let shell_manager = shell_manager.clone();
let call_info = call_info.clone();
async move {
let call_info = match call_info.evaluate_with_new_it(&*registry, &it).await {
Err(err) => {
return OutputStream::one(Err(err));
}
Ok(args) => args, Ok(args) => args,
}; };
@ -414,17 +420,13 @@ impl WholeStreamCommand for FnFilterCommand {
); );
match func(args) { match func(args) {
Err(err) => yield Err(err), Err(err) => return OutputStream::one(Err(err)),
Ok(mut stream) => { Ok(stream) => stream,
while let Some(value) = stream.values.next().await {
yield value;
} }
} }
} })
} .flatten()
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
} }

View File

@ -32,7 +32,7 @@ impl WholeStreamCommand for Echo {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
echo(args, registry) echo(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -51,67 +51,62 @@ impl WholeStreamCommand for Echo {
} }
} }
fn echo(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn echo(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let (args, _): (EchoArgs, _) = args.process(&registry).await?; let (args, _): (EchoArgs, _) = args.process(&registry).await?;
for i in args.rest { let stream = args.rest.into_iter().map(|i| {
match i.as_string() { match i.as_string() {
Ok(s) => { Ok(s) => {
yield Ok(ReturnSuccess::Value( OutputStream::one(Ok(ReturnSuccess::Value(
UntaggedValue::string(s).into_value(i.tag.clone()), UntaggedValue::string(s).into_value(i.tag.clone()),
)); )))
} }
_ => match i { _ => match i {
Value { Value {
value: UntaggedValue::Table(table), value: UntaggedValue::Table(table),
.. ..
} => { } => {
for value in table { futures::stream::iter(table.into_iter().map(ReturnSuccess::value)).to_output_stream()
yield Ok(ReturnSuccess::Value(value.clone()));
}
} }
Value { Value {
value: UntaggedValue::Primitive(Primitive::Range(range)), value: UntaggedValue::Primitive(Primitive::Range(range)),
tag tag
} => { } => {
let mut output_vec = vec![];
let mut current = range.from.0.item; let mut current = range.from.0.item;
while current != range.to.0.item { while current != range.to.0.item {
yield Ok(ReturnSuccess::Value(UntaggedValue::Primitive(current.clone()).into_value(&tag))); output_vec.push(Ok(ReturnSuccess::Value(UntaggedValue::Primitive(current.clone()).into_value(&tag))));
current = match crate::data::value::compute_values(Operator::Plus, &UntaggedValue::Primitive(current), &UntaggedValue::int(1)) { current = match crate::data::value::compute_values(Operator::Plus, &UntaggedValue::Primitive(current), &UntaggedValue::int(1)) {
Ok(result) => match result { Ok(result) => match result {
UntaggedValue::Primitive(p) => p, UntaggedValue::Primitive(p) => p,
_ => { _ => {
yield Err(ShellError::unimplemented("Internal error: expected a primitive result from increment")); return OutputStream::one(Err(ShellError::unimplemented("Internal error: expected a primitive result from increment")));
return;
} }
}, },
Err((left_type, right_type)) => { Err((left_type, right_type)) => {
yield Err(ShellError::coerce_error( return OutputStream::one(Err(ShellError::coerce_error(
left_type.spanned(tag.span), left_type.spanned(tag.span),
right_type.spanned(tag.span), right_type.spanned(tag.span),
)); )));
return;
} }
} }
} }
match range.to.1 { if let RangeInclusion::Inclusive = range.to.1 {
RangeInclusion::Inclusive => { output_vec.push(Ok(ReturnSuccess::Value(UntaggedValue::Primitive(current).into_value(&tag))));
yield Ok(ReturnSuccess::Value(UntaggedValue::Primitive(current.clone()).into_value(&tag)));
}
_ => {}
} }
futures::stream::iter(output_vec.into_iter()).to_output_stream()
} }
_ => { _ => {
yield Ok(ReturnSuccess::Value(i.clone())); OutputStream::one(Ok(ReturnSuccess::Value(i.clone())))
} }
}, },
} }
} });
};
Ok(stream.to_output_stream()) Ok(futures::stream::iter(stream).flatten().to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -14,6 +14,7 @@ pub struct Enter;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct EnterArgs { pub struct EnterArgs {
location: Tagged<PathBuf>, location: Tagged<PathBuf>,
encoding: Option<Tagged<String>>,
} }
#[async_trait] #[async_trait]
@ -23,15 +24,29 @@ impl WholeStreamCommand for Enter {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("enter").required( Signature::build("enter")
.required(
"location", "location",
SyntaxShape::Path, SyntaxShape::Path,
"the location to create a new shell from", "the location to create a new shell from",
) )
.named(
"encoding",
SyntaxShape::String,
"encoding to use to open file",
Some('e'),
)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Create a new shell and begin at this path." r#"Create a new shell and begin at this path.
Multiple encodings are supported for reading text files by using
the '--encoding <encoding>' parameter. Here is an example of a few:
big5, euc-jp, euc-kr, gbk, iso-8859-1, utf-16, cp1252, latin5
For a more complete list of encodings please refer to the encoding_rs
documentation link at https://docs.rs/encoding_rs/0.8.23/encoding_rs/#statics"#
} }
async fn run( async fn run(
@ -39,7 +54,7 @@ impl WholeStreamCommand for Enter {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
enter(args, registry) enter(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -54,13 +69,20 @@ impl WholeStreamCommand for Enter {
example: "enter package.json", example: "enter package.json",
result: None, result: None,
}, },
Example {
description: "Enters file with iso-8859-1 encoding",
example: "enter file.csv --encoding iso-8859-1",
result: None,
},
] ]
} }
} }
fn enter(raw_args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn enter(
raw_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let scope = raw_args.call_info.scope.clone(); let scope = raw_args.call_info.scope.clone();
let shell_manager = raw_args.shell_manager.clone(); let shell_manager = raw_args.shell_manager.clone();
let head = raw_args.call_info.args.head.clone(); let head = raw_args.call_info.args.head.clone();
@ -68,7 +90,7 @@ fn enter(raw_args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
let current_errors = raw_args.current_errors.clone(); let current_errors = raw_args.current_errors.clone();
let host = raw_args.host.clone(); let host = raw_args.host.clone();
let tag = raw_args.call_info.name_tag.clone(); let tag = raw_args.call_info.name_tag.clone();
let (EnterArgs { location }, _) = raw_args.process(&registry).await?; let (EnterArgs { location, encoding }, _) = raw_args.process(&registry).await?;
let location_string = location.display().to_string(); let location_string = location.display().to_string();
let location_clone = location_string.clone(); let location_clone = location_string.clone();
@ -79,31 +101,36 @@ fn enter(raw_args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
let (_, command) = (spec[0], spec[1]); let (_, command) = (spec[0], spec[1]);
if registry.has(command) { if registry.has(command) {
yield Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( return Ok(OutputStream::one(ReturnSuccess::action(
CommandAction::EnterHelpShell(
UntaggedValue::string(command).into_value(Tag::unknown()), UntaggedValue::string(command).into_value(Tag::unknown()),
),
))); )));
return;
} }
} }
yield Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( Ok(OutputStream::one(ReturnSuccess::action(
UntaggedValue::nothing().into_value(Tag::unknown()), CommandAction::EnterHelpShell(UntaggedValue::nothing().into_value(Tag::unknown())),
))); )))
} else if location.is_dir() { } else if location.is_dir() {
yield Ok(ReturnSuccess::Action(CommandAction::EnterShell( Ok(OutputStream::one(ReturnSuccess::action(
location_clone, CommandAction::EnterShell(location_clone),
))); )))
} else { } else {
// If it's a file, attempt to open the file as a value and enter it // If it's a file, attempt to open the file as a value and enter it
let cwd = shell_manager.path(); let cwd = shell_manager.path();
let full_path = std::path::PathBuf::from(cwd); let full_path = std::path::PathBuf::from(cwd);
let (file_extension, contents, contents_tag) = let (file_extension, contents, contents_tag) = crate::commands::open::fetch(
crate::commands::open::fetch(
&full_path, &full_path,
&PathBuf::from(location_clone), &PathBuf::from(location_clone),
tag.span, tag.span,
).await?; match encoding {
Some(e) => e.to_string(),
_ => "".to_string(),
},
)
.await?;
match contents { match contents {
UntaggedValue::Primitive(Primitive::String(_)) => { UntaggedValue::Primitive(Primitive::String(_)) => {
@ -111,9 +138,7 @@ fn enter(raw_args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
if let Some(extension) = file_extension { if let Some(extension) = file_extension {
let command_name = format!("from {}", extension); let command_name = format!("from {}", extension);
if let Some(converter) = if let Some(converter) = registry.get_command(&command_name) {
registry.get_command(&command_name)
{
let new_args = RawCommandArgs { let new_args = RawCommandArgs {
host, host,
ctrl_c, ctrl_c,
@ -128,47 +153,47 @@ fn enter(raw_args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
is_last: false, is_last: false,
}, },
name_tag: tag.clone(), name_tag: tag.clone(),
scope: scope.clone() scope: scope.clone(),
}, },
}; };
let mut result = converter.run( let mut result = converter
new_args.with_input(vec![tagged_contents]), .run(new_args.with_input(vec![tagged_contents]), &registry)
&registry, .await;
).await;
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
result.drain_vec().await; result.drain_vec().await;
for res in result_vec {
match res { Ok(futures::stream::iter(result_vec.into_iter().map(
Ok(ReturnSuccess::Value(Value { move |res| match res {
value, Ok(ReturnSuccess::Value(Value { value, .. })) => Ok(
.. ReturnSuccess::Action(CommandAction::EnterValueShell(Value {
})) => {
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(
Value {
value, value,
tag: contents_tag.clone(), tag: contents_tag.clone(),
}))); })),
} ),
x => yield x, x => x,
} },
))
.to_output_stream())
} else {
Ok(OutputStream::one(ReturnSuccess::action(
CommandAction::EnterValueShell(tagged_contents),
)))
} }
} else { } else {
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents))); Ok(OutputStream::one(ReturnSuccess::action(
} CommandAction::EnterValueShell(tagged_contents),
} else { )))
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents)));
} }
} }
_ => { _ => {
let tagged_contents = contents.into_value(contents_tag); let tagged_contents = contents.into_value(contents_tag);
yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents))); Ok(OutputStream::one(ReturnSuccess::action(
CommandAction::EnterValueShell(tagged_contents),
)))
} }
} }
} }
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -25,14 +25,10 @@ impl WholeStreamCommand for From {
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { Ok(OutputStream::one(ReturnSuccess::value(
yield Ok(ReturnSuccess::Value(
UntaggedValue::string(crate::commands::help::get_help(&From, &registry)) UntaggedValue::string(crate::commands::help::get_help(&From, &registry))
.into_value(Tag::unknown()), .into_value(Tag::unknown()),
)); )))
};
Ok(stream.to_output_stream())
} }
} }

View File

@ -33,7 +33,7 @@ impl WholeStreamCommand for FromJSON {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
from_json(args, registry) from_json(args, registry).await
} }
} }
@ -71,65 +71,73 @@ pub fn from_json_string_to_value(s: String, tag: impl Into<Tag>) -> serde_hjson:
Ok(convert_json_value_to_nu_value(&v, tag)) Ok(convert_json_value_to_nu_value(&v, tag))
} }
fn from_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn from_json(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone(); let name_tag = args.call_info.name_tag.clone();
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (FromJSONArgs { objects }, input) = args.process(&registry).await?;
let (FromJSONArgs { objects }, mut input) = args.process(&registry).await?;
let concat_string = input.collect_string(name_tag.clone()).await?; let concat_string = input.collect_string(name_tag.clone()).await?;
let string_clone: Vec<_> = concat_string.item.lines().map(|x| x.to_string()).collect();
if objects { if objects {
for json_str in concat_string.item.lines() { Ok(
futures::stream::iter(string_clone.into_iter().filter_map(move |json_str| {
if json_str.is_empty() { if json_str.is_empty() {
continue; return None;
} }
match from_json_string_to_value(json_str.to_string(), &name_tag) { match from_json_string_to_value(json_str, &name_tag) {
Ok(x) => Ok(x) => Some(ReturnSuccess::value(x)),
yield ReturnSuccess::value(x),
Err(e) => { Err(e) => {
let mut message = "Could not parse as JSON (".to_string(); let mut message = "Could not parse as JSON (".to_string();
message.push_str(&e.to_string()); message.push_str(&e.to_string());
message.push_str(")"); message.push_str(")");
yield Err(ShellError::labeled_error_with_secondary( Some(Err(ShellError::labeled_error_with_secondary(
message, message,
"input cannot be parsed as JSON", "input cannot be parsed as JSON",
&name_tag, name_tag.clone(),
"value originates from here", "value originates from here",
concat_string.tag.clone())) concat_string.tag.clone(),
} )))
} }
} }
}))
.to_output_stream(),
)
} else { } else {
match from_json_string_to_value(concat_string.item, name_tag.clone()) { match from_json_string_to_value(concat_string.item, name_tag.clone()) {
Ok(x) => Ok(x) => match x {
match x { Value {
Value { value: UntaggedValue::Table(list), .. } => { value: UntaggedValue::Table(list),
for l in list { ..
yield ReturnSuccess::value(l); } => Ok(
} futures::stream::iter(list.into_iter().map(ReturnSuccess::value))
} .to_output_stream(),
x => yield ReturnSuccess::value(x), ),
} x => Ok(OutputStream::one(ReturnSuccess::value(x))),
},
Err(e) => { Err(e) => {
let mut message = "Could not parse as JSON (".to_string(); let mut message = "Could not parse as JSON (".to_string();
message.push_str(&e.to_string()); message.push_str(&e.to_string());
message.push_str(")"); message.push_str(")");
yield Err(ShellError::labeled_error_with_secondary( Ok(OutputStream::one(Err(
ShellError::labeled_error_with_secondary(
message, message,
"input cannot be parsed as JSON", "input cannot be parsed as JSON",
name_tag, name_tag,
"value originates from here", "value originates from here",
concat_string.tag)) concat_string.tag,
),
)))
} }
} }
} }
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -36,22 +36,28 @@ impl WholeStreamCommand for FromODS {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
from_ods(args, registry) from_ods(args, registry).await
} }
} }
fn from_ods(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn from_ods(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (
let (FromODSArgs { headerless: _headerless }, mut input) = args.process(&registry).await?; FromODSArgs {
headerless: _headerless,
},
input,
) = args.process(&registry).await?;
let bytes = input.collect_binary(tag.clone()).await?; let bytes = input.collect_binary(tag.clone()).await?;
let mut buf: Cursor<Vec<u8>> = Cursor::new(bytes.item); let buf: Cursor<Vec<u8>> = Cursor::new(bytes.item);
let mut ods = Ods::<_>::new(buf).map_err(|_| ShellError::labeled_error( let mut ods = Ods::<_>::new(buf).map_err(|_| {
"Could not load ods file", ShellError::labeled_error("Could not load ods file", "could not load ods file", &tag)
"could not load ods file", })?;
&tag))?;
let mut dict = TaggedDictBuilder::new(&tag); let mut dict = TaggedDictBuilder::new(&tag);
@ -81,17 +87,15 @@ fn from_ods(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
dict.insert_untagged(sheet_name, sheet_output.into_untagged_value()); dict.insert_untagged(sheet_name, sheet_output.into_untagged_value());
} else { } else {
yield Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Could not load sheet", "Could not load sheet",
"could not load sheet", "could not load sheet",
&tag)); &tag,
));
} }
} }
yield ReturnSuccess::value(dict.into_value()); Ok(OutputStream::one(ReturnSuccess::value(dict.into_value())))
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -51,7 +51,7 @@ impl WholeStreamCommand for FromSSV {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
from_ssv(args, registry) from_ssv(args, registry).await
} }
} }
@ -251,37 +251,53 @@ fn from_ssv_string_to_value(
Some(UntaggedValue::Table(rows).into_value(&tag)) Some(UntaggedValue::Table(rows).into_value(&tag))
} }
fn from_ssv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn from_ssv(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (
let (FromSSVArgs { headerless, aligned_columns, minimum_spaces }, mut input) = args.process(&registry).await?; FromSSVArgs {
headerless,
aligned_columns,
minimum_spaces,
},
input,
) = args.process(&registry).await?;
let concat_string = input.collect_string(name.clone()).await?; let concat_string = input.collect_string(name.clone()).await?;
let split_at = match minimum_spaces { let split_at = match minimum_spaces {
Some(number) => number.item, Some(number) => number.item,
None => DEFAULT_MINIMUM_SPACES None => DEFAULT_MINIMUM_SPACES,
}; };
match from_ssv_string_to_value(&concat_string.item, headerless, aligned_columns, split_at, name.clone()) { Ok(
match from_ssv_string_to_value(
&concat_string.item,
headerless,
aligned_columns,
split_at,
name.clone(),
) {
Some(x) => match x { Some(x) => match x {
Value { value: UntaggedValue::Table(list), ..} => { Value {
for l in list { yield ReturnSuccess::value(l) } value: UntaggedValue::Table(list),
} ..
x => yield ReturnSuccess::value(x) } => futures::stream::iter(list.into_iter().map(ReturnSuccess::value))
.to_output_stream(),
x => OutputStream::one(ReturnSuccess::value(x)),
}, },
None => { None => {
yield Err(ShellError::labeled_error_with_secondary( return Err(ShellError::labeled_error_with_secondary(
"Could not parse as SSV", "Could not parse as SSV",
"input cannot be parsed ssv", "input cannot be parsed ssv",
&name, &name,
"value originates from here", "value originates from here",
&concat_string.tag, &concat_string.tag,
)) ));
},
} }
}; },
)
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -24,7 +24,7 @@ impl WholeStreamCommand for FromTOML {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
from_toml(args, registry) from_toml(args, registry).await
} }
} }
@ -64,28 +64,28 @@ pub fn from_toml_string_to_value(s: String, tag: impl Into<Tag>) -> Result<Value
Ok(convert_toml_value_to_nu_value(&v, tag)) Ok(convert_toml_value_to_nu_value(&v, tag))
} }
pub fn from_toml( pub async fn from_toml(
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let args = args.evaluate_once(&registry).await?; let args = args.evaluate_once(&registry).await?;
let tag = args.name_tag(); let tag = args.name_tag();
let input = args.input; let input = args.input;
let concat_string = input.collect_string(tag.clone()).await?; let concat_string = input.collect_string(tag.clone()).await?;
Ok(
match from_toml_string_to_value(concat_string.item, tag.clone()) { match from_toml_string_to_value(concat_string.item, tag.clone()) {
Ok(x) => match x { Ok(x) => match x {
Value { value: UntaggedValue::Table(list), .. } => { Value {
for l in list { value: UntaggedValue::Table(list),
yield ReturnSuccess::value(l); ..
} } => futures::stream::iter(list.into_iter().map(ReturnSuccess::value))
} .to_output_stream(),
x => yield ReturnSuccess::value(x), x => OutputStream::one(ReturnSuccess::value(x)),
}, },
Err(_) => { Err(_) => {
yield Err(ShellError::labeled_error_with_secondary( return Err(ShellError::labeled_error_with_secondary(
"Could not parse as TOML", "Could not parse as TOML",
"input cannot be parsed as TOML", "input cannot be parsed as TOML",
&tag, &tag,
@ -93,10 +93,8 @@ pub fn from_toml(
concat_string.tag, concat_string.tag,
)) ))
} }
} },
}; )
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -36,18 +36,25 @@ impl WholeStreamCommand for FromXLSX {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
from_xlsx(args, registry) from_xlsx(args, registry).await
} }
} }
fn from_xlsx(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn from_xlsx(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (
let (FromXLSXArgs { headerless: _headerless }, mut input) = args.process(&registry).await?; FromXLSXArgs {
headerless: _headerless,
},
input,
) = args.process(&registry).await?;
let value = input.collect_binary(tag.clone()).await?; let value = input.collect_binary(tag.clone()).await?;
let mut buf: Cursor<Vec<u8>> = Cursor::new(value.item); let buf: Cursor<Vec<u8>> = Cursor::new(value.item);
let mut xls = Xlsx::<_>::new(buf).map_err(|_| { let mut xls = Xlsx::<_>::new(buf).map_err(|_| {
ShellError::labeled_error("Could not load xlsx file", "could not load xlsx file", &tag) ShellError::labeled_error("Could not load xlsx file", "could not load xlsx file", &tag)
})?; })?;
@ -80,7 +87,7 @@ fn from_xlsx(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
dict.insert_untagged(sheet_name, sheet_output.into_untagged_value()); dict.insert_untagged(sheet_name, sheet_output.into_untagged_value());
} else { } else {
yield Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Could not load sheet", "Could not load sheet",
"could not load sheet", "could not load sheet",
&tag, &tag,
@ -88,10 +95,7 @@ fn from_xlsx(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
} }
} }
yield ReturnSuccess::value(dict.into_value()); Ok(OutputStream::one(ReturnSuccess::value(dict.into_value())))
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -24,7 +24,7 @@ impl WholeStreamCommand for FromXML {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
from_xml(args, registry) from_xml(args, registry).await
} }
} }
@ -99,37 +99,38 @@ pub fn from_xml_string_to_value(s: String, tag: impl Into<Tag>) -> Result<Value,
Ok(from_document_to_value(&parsed, tag)) Ok(from_document_to_value(&parsed, tag))
} }
fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn from_xml(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let args = args.evaluate_once(&registry).await?; let args = args.evaluate_once(&registry).await?;
let tag = args.name_tag(); let tag = args.name_tag();
let input = args.input; let input = args.input;
let concat_string = input.collect_string(tag.clone()).await?; let concat_string = input.collect_string(tag.clone()).await?;
Ok(
match from_xml_string_to_value(concat_string.item, tag.clone()) { match from_xml_string_to_value(concat_string.item, tag.clone()) {
Ok(x) => match x { Ok(x) => match x {
Value { value: UntaggedValue::Table(list), .. } => { Value {
for l in list { value: UntaggedValue::Table(list),
yield ReturnSuccess::value(l); ..
} } => futures::stream::iter(list.into_iter().map(ReturnSuccess::value))
} .to_output_stream(),
x => yield ReturnSuccess::value(x), x => OutputStream::one(ReturnSuccess::value(x)),
}, },
Err(_) => { Err(_) => {
yield Err(ShellError::labeled_error_with_secondary( return Err(ShellError::labeled_error_with_secondary(
"Could not parse as XML", "Could not parse as XML",
"input cannot be parsed as XML", "input cannot be parsed as XML",
&tag, &tag,
"value originates from here", "value originates from here",
&concat_string.tag, &concat_string.tag,
)) ))
} ,
} }
}; },
)
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -28,7 +28,7 @@ impl WholeStreamCommand for Headers {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
headers(args, registry) headers(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -40,51 +40,65 @@ impl WholeStreamCommand for Headers {
} }
} }
pub fn headers(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> { pub async fn headers(
let stream = async_stream! { args: CommandArgs,
let mut input = args.input; _registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let input = args.input;
let rows: Vec<Value> = input.collect().await; let rows: Vec<Value> = input.collect().await;
if rows.len() < 1 { if rows.is_empty() {
yield Err(ShellError::untagged_runtime_error("Couldn't find headers, was the input a properly formatted, non-empty table?")); return Err(ShellError::untagged_runtime_error(
"Couldn't find headers, was the input a properly formatted, non-empty table?",
));
} }
//the headers are the first row in the table //the headers are the first row in the table
let headers: Vec<String> = match &rows[0].value { let headers: Vec<String> = match &rows[0].value {
UntaggedValue::Row(d) => { UntaggedValue::Row(d) => {
Ok(d.entries.iter().map(|(k, v)| { Ok(d.entries
.iter()
.map(|(k, v)| {
match v.as_string() { match v.as_string() {
Ok(s) => s, Ok(s) => s,
Err(_) => { //If a cell that should contain a header name is empty, we name the column Column[index] Err(_) => {
//If a cell that should contain a header name is empty, we name the column Column[index]
match d.entries.get_full(k) { match d.entries.get_full(k) {
Some((index, _, _)) => format!("Column{}", index), Some((index, _, _)) => format!("Column{}", index),
None => "unknownColumn".to_string() None => "unknownColumn".to_string(),
} }
} }
} }
}).collect()) })
.collect())
} }
_ => Err(ShellError::unexpected_eof("Could not get headers, is the table empty?", rows[0].tag.span)) _ => Err(ShellError::unexpected_eof(
"Could not get headers, is the table empty?",
rows[0].tag.span,
)),
}?; }?;
Ok(
futures::stream::iter(rows.into_iter().skip(1).map(move |r| {
//Each row is a dictionary with the headers as keys //Each row is a dictionary with the headers as keys
for r in rows.iter().skip(1) {
match &r.value { match &r.value {
UntaggedValue::Row(d) => { UntaggedValue::Row(d) => {
let mut i = 0;
let mut entries = IndexMap::new(); let mut entries = IndexMap::new();
for (_, v) in d.entries.iter() { for (i, (_, v)) in d.entries.iter().enumerate() {
entries.insert(headers[i].clone(), v.clone()); entries.insert(headers[i].clone(), v.clone());
i += 1;
} }
yield Ok(ReturnSuccess::Value(UntaggedValue::Row(Dictionary{entries}).into_value(r.tag.clone()))) Ok(ReturnSuccess::Value(
UntaggedValue::Row(Dictionary { entries }).into_value(r.tag.clone()),
))
} }
_ => yield Err(ShellError::unexpected_eof("Couldn't iterate through rows, was the input a properly formatted table?", r.tag.span)) _ => Err(ShellError::unexpected_eof(
"Couldn't iterate through rows, was the input a properly formatted table?",
r.tag.span,
)),
} }
} }))
}; .to_output_stream(),
)
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -45,7 +45,7 @@ impl WholeStreamCommand for Histogram {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
histogram(args, registry) histogram(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -70,21 +70,20 @@ impl WholeStreamCommand for Histogram {
} }
} }
pub fn histogram( pub async fn histogram(
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let stream = async_stream! { let (HistogramArgs { column_name, rest }, input) = args.process(&registry).await?;
let (HistogramArgs { column_name, rest}, mut input) = args.process(&registry).await?;
let values: Vec<Value> = input.collect().await; let values: Vec<Value> = input.collect().await;
let Tagged { item: group_by, .. } = column_name.clone(); let Tagged { item: group_by, .. } = column_name.clone();
let groups = group(&column_name, values, &name)?; let groups = group(&column_name, values, &name)?;
let group_labels = columns_sorted(Some(group_by.clone()), &groups, &name); let group_labels = columns_sorted(Some(group_by.clone()), &groups, &name);
let sorted = t_sort(Some(group_by.clone()), None, &groups, &name)?; let sorted = t_sort(Some(group_by), None, &groups, &name)?;
let evaled = evaluate(&sorted, None, &name)?; let evaled = evaluate(&sorted, None, &name)?;
let reduced = reduce(&evaled, None, &name)?; let reduced = reduce(&evaled, None, &name)?;
let maxima = map_max(&reduced, None, &name)?; let maxima = map_max(&reduced, None, &name)?;
@ -95,7 +94,6 @@ pub fn histogram(
value: UntaggedValue::Table(datasets), value: UntaggedValue::Table(datasets),
.. ..
} => { } => {
let mut idx = 0; let mut idx = 0;
let column_names_supplied: Vec<_> = rest.iter().map(|f| f.item.clone()).collect(); let column_names_supplied: Vec<_> = rest.iter().map(|f| f.item.clone()).collect();
@ -109,7 +107,11 @@ pub fn histogram(
let column = (*column_name).clone(); let column = (*column_name).clone();
let count_column_name = "count".to_string(); let count_column_name = "count".to_string();
let count_shell_error = ShellError::labeled_error("Unable to load group count", "unabled to load group count", &name); let count_shell_error = ShellError::labeled_error(
"Unable to load group count",
"unabled to load group count",
&name,
);
let mut count_values: Vec<u64> = Vec::new(); let mut count_values: Vec<u64> = Vec::new();
for table_entry in reduced.table_entries() { for table_entry in reduced.table_entries() {
@ -122,43 +124,82 @@ pub fn histogram(
if let Ok(count) = i.value.clone().into_value(&name).as_u64() { if let Ok(count) = i.value.clone().into_value(&name).as_u64() {
count_values.push(count); count_values.push(count);
} else { } else {
yield Err(count_shell_error); return Err(count_shell_error);
return;
} }
} }
} }
_ => { _ => {
yield Err(count_shell_error); return Err(count_shell_error);
return;
} }
} }
} }
if let Value { value: UntaggedValue::Table(start), .. } = datasets.get(0).ok_or_else(|| ShellError::labeled_error("Unable to load dataset", "unabled to load dataset", &name))? { if let Value {
for percentage in start.iter() { value: UntaggedValue::Table(start),
..
} = datasets.get(0).ok_or_else(|| {
ShellError::labeled_error(
"Unable to load dataset",
"unabled to load dataset",
&name,
)
})? {
let start = start.clone();
Ok(
futures::stream::iter(start.into_iter().map(move |percentage| {
let mut fact = TaggedDictBuilder::new(&name); let mut fact = TaggedDictBuilder::new(&name);
let value: Tagged<String> = group_labels.get(idx).ok_or_else(|| ShellError::labeled_error("Unable to load group labels", "unabled to load group labels", &name))?.clone(); let value: Tagged<String> = group_labels
fact.insert_value(&column, UntaggedValue::string(value.item).into_value(value.tag)); .get(idx)
.ok_or_else(|| {
ShellError::labeled_error(
"Unable to load group labels",
"unabled to load group labels",
&name,
)
})?
.clone();
fact.insert_value(
&column,
UntaggedValue::string(value.item).into_value(value.tag),
);
fact.insert_untagged(&count_column_name, UntaggedValue::int(count_values[idx])); fact.insert_untagged(
&count_column_name,
UntaggedValue::int(count_values[idx]),
);
if let Value { value: UntaggedValue::Primitive(Primitive::Int(ref num)), ref tag } = percentage.clone() { if let Value {
let string = std::iter::repeat("*").take(num.to_i32().ok_or_else(|| ShellError::labeled_error("Expected a number", "expected a number", tag))? as usize).collect::<String>(); value: UntaggedValue::Primitive(Primitive::Int(ref num)),
fact.insert_untagged(&frequency_column_name, UntaggedValue::string(string)); ref tag,
} = percentage
{
let string = std::iter::repeat("*")
.take(num.to_i32().ok_or_else(|| {
ShellError::labeled_error(
"Expected a number",
"expected a number",
tag,
)
})? as usize)
.collect::<String>();
fact.insert_untagged(
&frequency_column_name,
UntaggedValue::string(string),
);
} }
idx += 1; idx += 1;
yield ReturnSuccess::value(fact.into_value()); ReturnSuccess::value(fact.into_value())
}))
.to_output_stream(),
)
} else {
Ok(OutputStream::empty())
} }
} }
_ => Ok(OutputStream::empty()),
} }
_ => {}
}
};
Ok(stream.to_output_stream())
} }
fn percentages(values: &Value, max: Value, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn percentages(values: &Value, max: Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -33,21 +33,25 @@ impl WholeStreamCommand for History {
fn history(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> { fn history(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag; let tag = args.call_info.name_tag;
let stream = async_stream! {
let history_path = HistoryFile::path(); let history_path = HistoryFile::path();
let file = File::open(history_path); let file = File::open(history_path);
if let Ok(file) = file { if let Ok(file) = file {
let reader = BufReader::new(file); let reader = BufReader::new(file);
for line in reader.lines() { let output = reader.lines().filter_map(move |line| match line {
if let Ok(line) = line { Ok(line) => Some(ReturnSuccess::value(
yield ReturnSuccess::value(UntaggedValue::string(line).into_value(tag.clone())); UntaggedValue::string(line).into_value(tag.clone()),
} )),
} Err(_) => None,
});
Ok(futures::stream::iter(output).to_output_stream())
} else { } else {
yield Err(ShellError::labeled_error("Could not open history", "history file could not be opened", tag.clone())); Err(ShellError::labeled_error(
"Could not open history",
"history file could not be opened",
tag,
))
} }
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -42,38 +42,32 @@ impl WholeStreamCommand for Insert {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
insert(args, registry) insert(args, registry).await
} }
} }
fn insert(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn insert(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (InsertArgs { column, value }, input) = args.process(&registry).await?;
let (InsertArgs { column, value }, mut input) = args.process(&registry).await?;
while let Some(row) = input.next().await { Ok(input
match row { .map(move |row| match row {
Value { Value {
value: UntaggedValue::Row(_), value: UntaggedValue::Row(_),
.. ..
} => match row.insert_data_at_column_path(&column, value.clone()) { } => match row.insert_data_at_column_path(&column, value.clone()) {
Ok(v) => yield Ok(ReturnSuccess::Value(v)), Ok(v) => Ok(ReturnSuccess::Value(v)),
Err(err) => yield Err(err), Err(err) => Err(err),
}, },
Value { tag, ..} => { Value { tag, .. } => Err(ShellError::labeled_error(
yield Err(ShellError::labeled_error(
"Unrecognized type in stream", "Unrecognized type in stream",
"original value", "original value",
tag, tag,
)); )),
} })
.to_output_stream())
}
};
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -2,7 +2,7 @@ use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged; use nu_source::Tagged;
pub struct Keep; pub struct Keep;
@ -35,7 +35,7 @@ impl WholeStreamCommand for Keep {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
keep(args, registry) keep(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -59,27 +59,16 @@ impl WholeStreamCommand for Keep {
} }
} }
fn keep(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn keep(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (KeepArgs { rows }, input) = args.process(&registry).await?;
let (KeepArgs { rows }, mut input) = args.process(&registry).await?; let rows_desired = if let Some(quantity) = rows {
let mut rows_desired = if let Some(quantity) = rows {
*quantity *quantity
} else { } else {
1 1
}; };
while let Some(input) = input.next().await { Ok(input.take(rows_desired).to_output_stream())
if rows_desired > 0 {
yield ReturnSuccess::value(input);
rows_desired -= 1;
} else {
break;
}
}
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -2,7 +2,7 @@ use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue}; use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged; use nu_source::Tagged;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
@ -43,7 +43,7 @@ impl WholeStreamCommand for Kill {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
kill(args, registry) kill(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -62,16 +62,18 @@ impl WholeStreamCommand for Kill {
} }
} }
fn kill(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn kill(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (
let (KillArgs { KillArgs {
pid, pid,
rest, rest,
force, force,
quiet, quiet,
}, mut input) = args.process(&registry).await?; },
..,
) = args.process(&registry).await?;
let mut cmd = if cfg!(windows) { let mut cmd = if cfg!(windows) {
let mut cmd = Command::new("taskkill"); let mut cmd = Command::new("taskkill");
@ -113,12 +115,7 @@ fn kill(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, S
cmd.status().expect("failed to execute shell command"); cmd.status().expect("failed to execute shell command");
if false { Ok(OutputStream::empty())
yield ReturnSuccess::value(UntaggedValue::nothing().into_value(Tag::unknown()));
}
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -37,7 +37,7 @@ impl WholeStreamCommand for Merge {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
merge(args, registry) merge(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -49,57 +49,61 @@ impl WholeStreamCommand for Merge {
} }
} }
fn merge(raw_args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn merge(
raw_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let scope = raw_args.call_info.scope.clone(); let scope = raw_args.call_info.scope.clone();
let stream = async_stream! {
let mut context = Context::from_raw(&raw_args, &registry); let mut context = Context::from_raw(&raw_args, &registry);
let name_tag = raw_args.call_info.name_tag.clone(); let name_tag = raw_args.call_info.name_tag.clone();
let (merge_args, mut input): (MergeArgs, _) = raw_args.process(&registry).await?; let (merge_args, input): (MergeArgs, _) = raw_args.process(&registry).await?;
let block = merge_args.block; let block = merge_args.block;
let table: Option<Vec<Value>> = match run_block(&block, let table: Option<Vec<Value>> = match run_block(
&block,
&mut context, &mut context,
InputStream::empty(), InputStream::empty(),
&scope.it, &scope.it,
&scope.vars, &scope.vars,
&scope.env).await { &scope.env,
)
.await
{
Ok(mut stream) => Some(stream.drain_vec().await), Ok(mut stream) => Some(stream.drain_vec().await),
Err(err) => { Err(err) => {
yield Err(err); return Err(err);
return;
} }
}; };
let table = table.unwrap_or_else(|| {
let table = table.unwrap_or_else(|| vec![Value { vec![Value {
value: UntaggedValue::row(IndexMap::default()), value: UntaggedValue::row(IndexMap::default()),
tag: name_tag, tag: name_tag,
}]); }]
});
let mut idx = 0; Ok(input
.enumerate()
while let Some(value) = input.next().await { .map(move |(idx, value)| {
let other = table.get(idx); let other = table.get(idx);
match other { match other {
Some(replacement) => { Some(replacement) => match merge_values(&value.value, &replacement.value) {
match merge_values(&value.value, &replacement.value) { Ok(merged_value) => ReturnSuccess::value(merged_value.into_value(&value.tag)),
Ok(merged_value) => yield ReturnSuccess::value(merged_value.into_value(&value.tag)), Err(_) => {
Err(err) => {
let message = format!("The row at {:?} types mismatch", idx); let message = format!("The row at {:?} types mismatch", idx);
yield Err(ShellError::labeled_error("Could not merge", &message, &value.tag)); Err(ShellError::labeled_error(
"Could not merge",
&message,
&value.tag,
))
} }
},
None => ReturnSuccess::value(value),
} }
} })
None => yield ReturnSuccess::value(value), .to_output_stream())
}
idx += 1;
}
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -43,7 +43,7 @@ impl WholeStreamCommand for Move {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
mv(args, registry) mv(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -67,20 +67,13 @@ impl WholeStreamCommand for Move {
} }
} }
fn mv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn mv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let shell_manager = args.shell_manager.clone(); let shell_manager = args.shell_manager.clone();
let (args, _) = args.process(&registry).await?; let (args, _) = args.process(&registry).await?;
let mut result = shell_manager.mv(args, name)?;
while let Some(item) = result.next().await { shell_manager.mv(args, name)
yield item;
}
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -38,7 +38,7 @@ impl WholeStreamCommand for Nth {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
nth(args, registry) nth(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -57,30 +57,36 @@ impl WholeStreamCommand for Nth {
} }
} }
fn nth(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn nth(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (
let (NthArgs { row_number, rest: and_rows}, input) = args.process(&registry).await?; NthArgs {
row_number,
rest: and_rows,
},
input,
) = args.process(&registry).await?;
let mut inp = input.enumerate(); let row_numbers = vec![vec![row_number], and_rows]
while let Some((idx, item)) = inp.next().await {
let row_number = vec![row_number.clone()];
let row_numbers = vec![&row_number, &and_rows]
.into_iter() .into_iter()
.flatten() .flatten()
.collect::<Vec<&Tagged<u64>>>(); .collect::<Vec<Tagged<u64>>>();
Ok(input
.enumerate()
.filter_map(move |(idx, item)| {
futures::future::ready(
if row_numbers if row_numbers
.iter() .iter()
.any(|requested| requested.item == idx as u64) .any(|requested| requested.item == idx as u64)
{ {
yield ReturnSuccess::value(item); Some(ReturnSuccess::value(item))
} } else {
} None
}; },
)
Ok(stream.to_output_stream()) })
.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -4,6 +4,12 @@ use nu_errors::ShellError;
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue}; use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_source::{AnchorLocation, Span, Tagged}; use nu_source::{AnchorLocation, Span, Tagged};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
extern crate encoding_rs;
use encoding_rs::*;
use std::fs::File;
use std::io::BufWriter;
use std::io::Read;
use std::io::Write;
pub struct Open; pub struct Open;
@ -11,6 +17,7 @@ pub struct Open;
pub struct OpenArgs { pub struct OpenArgs {
path: Tagged<PathBuf>, path: Tagged<PathBuf>,
raw: Tagged<bool>, raw: Tagged<bool>,
encoding: Option<Tagged<String>>,
} }
#[async_trait] #[async_trait]
@ -31,10 +38,23 @@ impl WholeStreamCommand for Open {
"load content as a string instead of a table", "load content as a string instead of a table",
Some('r'), Some('r'),
) )
.named(
"encoding",
SyntaxShape::String,
"encoding to use to open file",
Some('e'),
)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Load a file into a cell, convert to table if possible (avoid by appending '--raw')" r#"Load a file into a cell, convert to table if possible (avoid by appending '--raw').
Multiple encodings are supported for reading text files by using
the '--encoding <encoding>' parameter. Here is an example of a few:
big5, euc-jp, euc-kr, gbk, iso-8859-1, utf-16, cp1252, latin5
For a more complete list of encodings please refer to the encoding_rs
documentation link at https://docs.rs/encoding_rs/0.8.23/encoding_rs/#statics"#
} }
async fn run( async fn run(
@ -46,11 +66,32 @@ impl WholeStreamCommand for Open {
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![
Example {
description: "Opens \"users.csv\" and creates a table from the data", description: "Opens \"users.csv\" and creates a table from the data",
example: "open users.csv", example: "open users.csv",
result: None, result: None,
}] },
Example {
description: "Opens file with iso-8859-1 encoding",
example: "open file.csv --encoding iso-8859-1 | from csv",
result: None,
},
]
}
}
pub fn get_encoding(opt: Option<String>) -> &'static Encoding {
match opt {
None => UTF_8,
Some(label) => match Encoding::for_label((&label).as_bytes()) {
None => {
//print!("{} is not a known encoding label. Trying UTF-8.", label);
//std::process::exit(-2);
get_encoding(Some("utf-8".to_string()))
}
Some(encoding) => encoding,
},
} }
} }
@ -59,8 +100,19 @@ async fn open(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
let full_path = cwd; let full_path = cwd;
let registry = registry.clone(); let registry = registry.clone();
let (OpenArgs { path, raw }, _) = args.process(&registry).await?; let (
let result = fetch(&full_path, &path.item, path.tag.span).await; OpenArgs {
path,
raw,
encoding,
},
_,
) = args.process(&registry).await?;
let enc = match encoding {
Some(e) => e.to_string(),
_ => "".to_string(),
};
let result = fetch(&full_path, &path.item, path.tag.span, enc).await;
let (file_extension, contents, contents_tag) = result?; let (file_extension, contents, contents_tag) = result?;
@ -87,9 +139,173 @@ pub async fn fetch(
cwd: &PathBuf, cwd: &PathBuf,
location: &PathBuf, location: &PathBuf,
span: Span, span: Span,
encoding: String,
) -> Result<(Option<String>, UntaggedValue, Tag), ShellError> { ) -> Result<(Option<String>, UntaggedValue, Tag), ShellError> {
let mut cwd = cwd.clone(); let mut cwd = cwd.clone();
let output_encoding: &Encoding = get_encoding(Some("utf-8".to_string()));
let input_encoding: &Encoding = get_encoding(Some(encoding.clone()));
let mut decoder = input_encoding.new_decoder();
let mut encoder = output_encoding.new_encoder();
let mut _file: File;
let buf = Vec::new();
let mut bufwriter = BufWriter::new(buf);
cwd.push(Path::new(location));
if let Ok(cwd) = dunce::canonicalize(&cwd) {
if !encoding.is_empty() {
// use the encoding string
match File::open(&Path::new(&cwd)) {
Ok(mut _file) => {
convert_via_utf8(
&mut decoder,
&mut encoder,
&mut _file,
&mut bufwriter,
false,
);
//bufwriter.flush()?;
Ok((
cwd.extension()
.map(|name| name.to_string_lossy().to_string()),
UntaggedValue::string(String::from_utf8_lossy(&bufwriter.buffer())),
Tag {
span,
anchor: Some(AnchorLocation::File(cwd.to_string_lossy().to_string())),
},
))
}
Err(_) => Err(ShellError::labeled_error(
format!("Cannot open {:?} for reading.", &cwd),
"file not found",
span,
)),
}
} else {
// Do the old stuff
match std::fs::read(&cwd) {
Ok(bytes) => match std::str::from_utf8(&bytes) {
Ok(s) => Ok((
cwd.extension()
.map(|name| name.to_string_lossy().to_string()),
UntaggedValue::string(s),
Tag {
span,
anchor: Some(AnchorLocation::File(cwd.to_string_lossy().to_string())),
},
)),
Err(_) => {
//Non utf8 data.
match (bytes.get(0), bytes.get(1)) {
(Some(x), Some(y)) if *x == 0xff && *y == 0xfe => {
// Possibly UTF-16 little endian
let utf16 = read_le_u16(&bytes[2..]);
if let Some(utf16) = utf16 {
match std::string::String::from_utf16(&utf16) {
Ok(s) => Ok((
cwd.extension()
.map(|name| name.to_string_lossy().to_string()),
UntaggedValue::string(s),
Tag {
span,
anchor: Some(AnchorLocation::File(
cwd.to_string_lossy().to_string(),
)),
},
)),
Err(_) => Ok((
None,
UntaggedValue::binary(bytes),
Tag {
span,
anchor: Some(AnchorLocation::File(
cwd.to_string_lossy().to_string(),
)),
},
)),
}
} else {
Ok((
None,
UntaggedValue::binary(bytes),
Tag {
span,
anchor: Some(AnchorLocation::File(
cwd.to_string_lossy().to_string(),
)),
},
))
}
}
(Some(x), Some(y)) if *x == 0xfe && *y == 0xff => {
// Possibly UTF-16 big endian
let utf16 = read_be_u16(&bytes[2..]);
if let Some(utf16) = utf16 {
match std::string::String::from_utf16(&utf16) {
Ok(s) => Ok((
cwd.extension()
.map(|name| name.to_string_lossy().to_string()),
UntaggedValue::string(s),
Tag {
span,
anchor: Some(AnchorLocation::File(
cwd.to_string_lossy().to_string(),
)),
},
)),
Err(_) => Ok((
None,
UntaggedValue::binary(bytes),
Tag {
span,
anchor: Some(AnchorLocation::File(
cwd.to_string_lossy().to_string(),
)),
},
)),
}
} else {
Ok((
None,
UntaggedValue::binary(bytes),
Tag {
span,
anchor: Some(AnchorLocation::File(
cwd.to_string_lossy().to_string(),
)),
},
))
}
}
_ => Ok((
None,
UntaggedValue::binary(bytes),
Tag {
span,
anchor: Some(AnchorLocation::File(
cwd.to_string_lossy().to_string(),
)),
},
)),
}
}
},
Err(_) => Err(ShellError::labeled_error(
format!("Cannot open {:?} for reading.", &cwd),
"file not found",
span,
)),
}
}
} else {
Err(ShellError::labeled_error(
format!("Cannot open {:?} for reading.", &cwd),
"file not found",
span,
))
}
/*
cwd.push(Path::new(location)); cwd.push(Path::new(location));
if let Ok(cwd) = dunce::canonicalize(cwd) { if let Ok(cwd) = dunce::canonicalize(cwd) {
match std::fs::read(&cwd) { match std::fs::read(&cwd) {
@ -214,6 +430,103 @@ pub async fn fetch(
span, span,
)) ))
} }
*/
}
fn convert_via_utf8(
decoder: &mut Decoder,
encoder: &mut Encoder,
read: &mut dyn Read,
write: &mut dyn Write,
last: bool,
) {
let mut input_buffer = [0u8; 2048];
let mut intermediate_buffer_bytes = [0u8; 4096];
// Is there a safe way to create a stack-allocated &mut str?
let mut intermediate_buffer: &mut str =
//unsafe { std::mem::transmute(&mut intermediate_buffer_bytes[..]) };
std::str::from_utf8_mut(&mut intermediate_buffer_bytes[..]).expect("error with from_utf8_mut");
let mut output_buffer = [0u8; 4096];
let mut current_input_ended = false;
while !current_input_ended {
match read.read(&mut input_buffer) {
Err(_) => {
print!("Error reading input.");
//std::process::exit(-5);
}
Ok(decoder_input_end) => {
current_input_ended = decoder_input_end == 0;
let input_ended = last && current_input_ended;
let mut decoder_input_start = 0usize;
loop {
let (decoder_result, decoder_read, decoder_written, _) = decoder.decode_to_str(
&input_buffer[decoder_input_start..decoder_input_end],
&mut intermediate_buffer,
input_ended,
);
decoder_input_start += decoder_read;
let last_output = if input_ended {
match decoder_result {
CoderResult::InputEmpty => true,
CoderResult::OutputFull => false,
}
} else {
false
};
// Regardless of whether the intermediate buffer got full
// or the input buffer was exhausted, let's process what's
// in the intermediate buffer.
if encoder.encoding() == UTF_8 {
// If the target is UTF-8, optimize out the encoder.
if write
.write_all(&intermediate_buffer.as_bytes()[..decoder_written])
.is_err()
{
print!("Error writing output.");
//std::process::exit(-7);
}
} else {
let mut encoder_input_start = 0usize;
loop {
let (encoder_result, encoder_read, encoder_written, _) = encoder
.encode_from_utf8(
&intermediate_buffer[encoder_input_start..decoder_written],
&mut output_buffer,
last_output,
);
encoder_input_start += encoder_read;
if write.write_all(&output_buffer[..encoder_written]).is_err() {
print!("Error writing output.");
//std::process::exit(-6);
}
match encoder_result {
CoderResult::InputEmpty => {
break;
}
CoderResult::OutputFull => {
continue;
}
}
}
}
// Now let's see if we should read again or process the
// rest of the current input buffer.
match decoder_result {
CoderResult::InputEmpty => {
break;
}
CoderResult::OutputFull => {
continue;
}
}
}
}
}
}
} }
fn read_le_u16(input: &[u8]) -> Option<Vec<u16>> { fn read_le_u16(input: &[u8]) -> Option<Vec<u16>> {

View File

@ -51,24 +51,29 @@ impl WholeStreamCommand for Pivot {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
pivot(args, registry) pivot(args, registry).await
} }
} }
pub fn pivot(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { pub async fn pivot(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let stream = async_stream! { let (args, input): (PivotArgs, _) = args.process(&registry).await?;
let (args, mut input): (PivotArgs, _) = args.process(&registry).await?;
let input = input.into_vec().await; let input = input.into_vec().await;
let descs = merge_descriptors(&input); let descs = merge_descriptors(&input);
let mut headers: Vec<String> = vec![]; let mut headers: Vec<String> = vec![];
if args.rest.len() > 0 && args.header_row { if !args.rest.is_empty() && args.header_row {
yield Err(ShellError::labeled_error("Can not provide header names and use header row", "using header row", name)); return Err(ShellError::labeled_error(
return; "Can not provide header names and use header row",
"using header row",
name,
));
} }
if args.header_row { if args.header_row {
@ -79,18 +84,27 @@ pub fn pivot(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
if let Ok(s) = x.as_string() { if let Ok(s) = x.as_string() {
headers.push(s.to_string()); headers.push(s.to_string());
} else { } else {
yield Err(ShellError::labeled_error("Header row needs string headers", "used non-string headers", name)); return Err(ShellError::labeled_error(
return; "Header row needs string headers",
"used non-string headers",
name,
));
} }
} }
_ => { _ => {
yield Err(ShellError::labeled_error("Header row is incomplete and can't be used", "using incomplete header row", name)); return Err(ShellError::labeled_error(
return; "Header row is incomplete and can't be used",
"using incomplete header row",
name,
));
} }
} }
} else { } else {
yield Err(ShellError::labeled_error("Header row is incomplete and can't be used", "using incomplete header row", name)); return Err(ShellError::labeled_error(
return; "Header row is incomplete and can't be used",
"using incomplete header row",
name,
));
} }
} }
} else { } else {
@ -104,17 +118,20 @@ pub fn pivot(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
} }
let descs: Vec<_> = if args.header_row { let descs: Vec<_> = if args.header_row {
descs.iter().skip(1).collect() descs.into_iter().skip(1).collect()
} else { } else {
descs.iter().collect() descs
}; };
for desc in descs { Ok(futures::stream::iter(descs.into_iter().map(move |desc| {
let mut column_num: usize = 0; let mut column_num: usize = 0;
let mut dict = TaggedDictBuilder::new(&name); let mut dict = TaggedDictBuilder::new(&name);
if !args.ignore_titles && !args.header_row { if !args.ignore_titles && !args.header_row {
dict.insert_untagged(headers[column_num].clone(), UntaggedValue::string(desc.clone())); dict.insert_untagged(
headers[column_num].clone(),
UntaggedValue::string(desc.clone()),
);
column_num += 1 column_num += 1
} }
@ -130,13 +147,9 @@ pub fn pivot(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
column_num += 1; column_num += 1;
} }
yield ReturnSuccess::value(dict.into_value()); ReturnSuccess::value(dict.into_value())
} }))
.to_output_stream())
};
Ok(OutputStream::new(stream))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -2,7 +2,7 @@ use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
#[derive(Deserialize)] #[derive(Deserialize)]
struct PrependArgs { struct PrependArgs {
@ -34,7 +34,7 @@ impl WholeStreamCommand for Prepend {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
prepend(args, registry) prepend(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -51,19 +51,17 @@ impl WholeStreamCommand for Prepend {
} }
} }
fn prepend(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn prepend(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (PrependArgs { row }, input) = args.process(&registry).await?;
let (PrependArgs { row }, mut input) = args.process(&registry).await?;
yield ReturnSuccess::value(row); let bos = futures::stream::iter(vec![row]);
while let Some(item) = input.next().await {
yield ReturnSuccess::value(item);
}
};
Ok(stream.to_output_stream()) Ok(bos.chain(input).to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -36,14 +36,13 @@ impl WholeStreamCommand for Range {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
range(args, registry) range(args, registry).await
} }
} }
fn range(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn range(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (RangeArgs { area }, input) = args.process(&registry).await?;
let (RangeArgs { area }, mut input) = args.process(&registry).await?;
let range = area.item; let range = area.item;
let (from, _) = range.from; let (from, _) = range.from;
let (to, _) = range.to; let (to, _) = range.to;
@ -51,13 +50,11 @@ fn range(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
let from = *from as usize; let from = *from as usize;
let to = *to as usize; let to = *to as usize;
let mut inp = input.skip(from).take(to - from + 1); Ok(input
while let Some(item) = inp.next().await { .skip(from)
yield ReturnSuccess::value(item); .take(to - from + 1)
} .map(ReturnSuccess::value)
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -31,7 +31,7 @@ impl WholeStreamCommand for Reject {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
reject(args, registry) reject(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -43,28 +43,23 @@ impl WholeStreamCommand for Reject {
} }
} }
fn reject(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn reject(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let (RejectArgs { rest: fields }, mut input) = args.process(&registry).await?; let (RejectArgs { rest: fields }, input) = args.process(&registry).await?;
if fields.is_empty() { if fields.is_empty() {
yield Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Reject requires fields", "Reject requires fields",
"needs parameter", "needs parameter",
name, name,
)); ));
return;
} }
let fields: Vec<_> = fields.iter().map(|f| f.item.clone()).collect(); let fields: Vec<_> = fields.iter().map(|f| f.item.clone()).collect();
while let Some(item) = input.next().await { Ok(input
yield ReturnSuccess::value(reject_fields(&item, &fields, &item.tag)); .map(move |item| ReturnSuccess::value(reject_fields(&item, &fields, &item.tag)))
} .to_output_stream())
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -38,7 +38,7 @@ impl WholeStreamCommand for Rename {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
rename(args, registry) rename(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -57,17 +57,20 @@ impl WholeStreamCommand for Rename {
} }
} }
pub fn rename(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { pub async fn rename(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let stream = async_stream! { let (Arguments { column_name, rest }, input) = args.process(&registry).await?;
let (Arguments { column_name, rest }, mut input) = args.process(&registry).await?;
let mut new_column_names = vec![vec![column_name]]; let mut new_column_names = vec![vec![column_name]];
new_column_names.push(rest); new_column_names.push(rest);
let new_column_names = new_column_names.into_iter().flatten().collect::<Vec<_>>(); let new_column_names = new_column_names.into_iter().flatten().collect::<Vec<_>>();
while let Some(item) = input.next().await { Ok(input
.map(move |item| {
if let Value { if let Value {
value: UntaggedValue::Row(row), value: UntaggedValue::Row(row),
tag, tag,
@ -87,21 +90,19 @@ pub fn rename(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
let out = UntaggedValue::Row(renamed_row.into()).into_value(tag); let out = UntaggedValue::Row(renamed_row.into()).into_value(tag);
yield ReturnSuccess::value(out); ReturnSuccess::value(out)
} else { } else {
yield ReturnSuccess::value( ReturnSuccess::value(
UntaggedValue::Error(ShellError::labeled_error( UntaggedValue::Error(ShellError::labeled_error(
"no column names available", "no column names available",
"can't rename", "can't rename",
&name, &name,
)) ))
.into_untagged_value(), .into_untagged_value(),
); )
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -25,7 +25,7 @@ impl WholeStreamCommand for Reverse {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
reverse(args, registry) reverse(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -43,19 +43,16 @@ impl WholeStreamCommand for Reverse {
} }
} }
fn reverse(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn reverse(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let args = args.evaluate_once(&registry).await?; let args = args.evaluate_once(&registry).await?;
let (input, _args) = args.parts(); let (input, _args) = args.parts();
let input = input.collect::<Vec<_>>().await; let input = input.collect::<Vec<_>>().await;
for output in input.into_iter().rev() { Ok(futures::stream::iter(input.into_iter().rev().map(ReturnSuccess::value)).to_output_stream())
yield ReturnSuccess::value(output);
}
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -4,7 +4,7 @@ use crate::prelude::*;
use derive_new::new; use derive_new::new;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{hir::Block, ReturnSuccess, Signature, SyntaxShape}; use nu_protocol::{hir::Block, Signature, SyntaxShape};
#[derive(new, Clone)] #[derive(new, Clone)]
pub struct AliasCommand { pub struct AliasCommand {
@ -38,7 +38,6 @@ impl WholeStreamCommand for AliasCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let call_info = args.call_info.clone(); let call_info = args.call_info.clone();
let registry = registry.clone(); let registry = registry.clone();
let block = self.block.clone(); let block = self.block.clone();
@ -46,61 +45,26 @@ impl WholeStreamCommand for AliasCommand {
let mut context = Context::from_args(&args, &registry); let mut context = Context::from_args(&args, &registry);
let input = args.input; let input = args.input;
let stream = async_stream! {
let mut scope = call_info.scope.clone(); let mut scope = call_info.scope.clone();
let evaluated = call_info.evaluate(&registry).await?; let evaluated = call_info.evaluate(&registry).await?;
if let Some(positional) = &evaluated.args.positional { if let Some(positional) = &evaluated.args.positional {
for (pos, arg) in positional.iter().enumerate() { for (pos, arg) in positional.iter().enumerate() {
scope.vars.insert(alias_command.args[pos].to_string(), arg.clone()); scope
.vars
.insert(alias_command.args[pos].to_string(), arg.clone());
} }
} }
let result = run_block( // FIXME: we need to patch up the spans to point at the top-level error
Ok(run_block(
&block, &block,
&mut context, &mut context,
input, input,
&scope.it, &scope.it,
&scope.vars, &scope.vars,
&scope.env, &scope.env,
).await; )
.await?
match result { .to_output_stream())
Ok(stream) if stream.is_empty() => {
yield Err(ShellError::labeled_error(
"Expected a block",
"alias needs a block",
tag,
));
}
Ok(mut stream) => {
// We collect first to ensure errors are put into the context
while let Some(result) = stream.next().await {
yield Ok(ReturnSuccess::Value(result));
}
let errors = context.get_errors();
if let Some(x) = errors.first() {
yield Err(ShellError::labeled_error_with_secondary(
"Alias failed to run",
"alias failed to run",
tag.clone(),
x.to_string(),
tag
));
}
}
Err(e) => {
yield Err(ShellError::labeled_error_with_secondary(
"Alias failed to run",
"alias failed to run",
tag.clone(),
e.to_string(),
tag
));
}
}
};
Ok(stream.to_output_stream())
} }
} }

View File

@ -9,7 +9,7 @@ use std::path::PathBuf;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::hir::{Expression, ExternalArgs, ExternalCommand, Literal, SpannedExpression}; use nu_protocol::hir::{Expression, ExternalArgs, ExternalCommand, Literal, SpannedExpression};
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape}; use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged; use nu_source::Tagged;
#[derive(Deserialize)] #[derive(Deserialize)]
@ -99,7 +99,6 @@ impl WholeStreamCommand for RunExternalCommand {
let is_interactive = self.interactive; let is_interactive = self.interactive;
let stream = async_stream! {
let command = ExternalCommand { let command = ExternalCommand {
name, name,
name_tag: args.call_info.name_tag.clone(), name_tag: args.call_info.name_tag.clone(),
@ -117,51 +116,32 @@ impl WholeStreamCommand for RunExternalCommand {
path: Some(Tagged { path: Some(Tagged {
item: PathBuf::from(path), item: PathBuf::from(path),
tag: args.call_info.name_tag.clone(), tag: args.call_info.name_tag.clone(),
}) }),
}; };
let result = external_context.shell_manager.cd(cd_args, args.call_info.name_tag.clone()); let result = external_context
.shell_manager
.cd(cd_args, args.call_info.name_tag.clone());
match result { match result {
Ok(mut stream) => { Ok(stream) => return Ok(stream.to_output_stream()),
while let Some(value) = stream.next().await {
yield value;
}
},
Err(e) => { Err(e) => {
yield Err(e); return Err(e);
}, }
_ => {}
} }
return;
} }
} }
let scope = args.call_info.scope.clone(); let scope = args.call_info.scope.clone();
let is_last = args.call_info.args.is_last; let is_last = args.call_info.args.is_last;
let input = args.input; let input = args.input;
let result = external::run_external_command( let result =
command, external::run_external_command(command, &mut external_context, input, &scope, is_last)
&mut external_context, .await;
input,
&scope,
is_last,
).await;
match result { match result {
Ok(mut stream) => { Ok(stream) => Ok(stream.to_output_stream()),
while let Some(value) = stream.next().await { Err(e) => Err(e),
yield Ok(ReturnSuccess::Value(value));
} }
},
Err(e) => {
yield Err(e);
},
_ => {}
}
};
Ok(stream.to_output_stream())
} }
} }

View File

@ -154,11 +154,14 @@ impl WholeStreamCommand for Save {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
save(args, registry) save(args, registry).await
} }
} }
fn save(raw_args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn save(
raw_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let mut full_path = PathBuf::from(raw_args.shell_manager.path()); let mut full_path = PathBuf::from(raw_args.shell_manager.path());
let name_tag = raw_args.call_info.name_tag.clone(); let name_tag = raw_args.call_info.name_tag.clone();
let name = raw_args.call_info.name_tag.clone(); let name = raw_args.call_info.name_tag.clone();
@ -169,51 +172,45 @@ fn save(raw_args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
let current_errors = raw_args.current_errors.clone(); let current_errors = raw_args.current_errors.clone();
let shell_manager = raw_args.shell_manager.clone(); let shell_manager = raw_args.shell_manager.clone();
let stream = async_stream! {
let head = raw_args.call_info.args.head.clone(); let head = raw_args.call_info.args.head.clone();
let (SaveArgs { path, raw: save_raw }, mut input) = raw_args.process(&registry).await?; let (
SaveArgs {
path,
raw: save_raw,
},
input,
) = raw_args.process(&registry).await?;
let input: Vec<Value> = input.collect().await; let input: Vec<Value> = input.collect().await;
if path.is_none() { if path.is_none() {
let mut should_return_file_path_error = true;
// If there is no filename, check the metadata for the anchor filename // If there is no filename, check the metadata for the anchor filename
if input.len() > 0 { if !input.is_empty() {
let anchor = input[0].tag.anchor(); let anchor = input[0].tag.anchor();
match anchor {
Some(path) => match path { if let Some(path) = anchor {
AnchorLocation::File(file) => { if let AnchorLocation::File(file) = path {
should_return_file_path_error = false;
full_path.push(Path::new(&file)); full_path.push(Path::new(&file));
} }
_ => { }
yield Err(ShellError::labeled_error( }
if should_return_file_path_error {
return Err(ShellError::labeled_error(
"Save requires a filepath", "Save requires a filepath",
"needs path", "needs path",
name_tag.clone(), name_tag.clone(),
)); ));
} }
}, } else if let Some(file) = path {
None => {
yield Err(ShellError::labeled_error(
"Save requires a filepath",
"needs path",
name_tag.clone(),
));
}
}
} else {
yield Err(ShellError::labeled_error(
"Save requires a filepath",
"needs path",
name_tag.clone(),
));
}
} else {
if let Some(file) = path {
full_path.push(file.item()); full_path.push(file.item());
} }
}
// TODO use label_break_value once it is stable: // TODO use label_break_value once it is stable:
// https://github.com/rust-lang/rust/issues/48594 // https://github.com/rust-lang/rust/issues/48594
let content : Result<Vec<u8>, ShellError> = 'scope: loop { #[allow(clippy::never_loop)]
let content: Result<Vec<u8>, ShellError> = 'scope: loop {
break if !save_raw { break if !save_raw {
if let Some(extension) = full_path.extension() { if let Some(extension) = full_path.extension() {
let command_name = format!("to {}", extension.to_string_lossy()); let command_name = format!("to {}", extension.to_string_lossy());
@ -233,10 +230,11 @@ fn save(raw_args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
}, },
name_tag: name_tag.clone(), name_tag: name_tag.clone(),
scope, scope,
} },
}; };
let mut result = converter.run(new_args.with_input(input), &registry).await; let mut result = converter.run(new_args.with_input(input), &registry).await;
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await; let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
result.drain_vec().await;
if converter.is_binary() { if converter.is_binary() {
process_binary_return_success!('scope, result_vec, name_tag) process_binary_return_success!('scope, result_vec, name_tag)
} else { } else {
@ -255,15 +253,15 @@ fn save(raw_args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
match content { match content {
Ok(save_data) => match std::fs::write(full_path, save_data) { Ok(save_data) => match std::fs::write(full_path, save_data) {
Ok(o) => o, Ok(_) => Ok(OutputStream::empty()),
Err(e) => yield Err(ShellError::labeled_error(e.to_string(), "IO error while saving", name)), Err(e) => Err(ShellError::labeled_error(
e.to_string(),
"IO error while saving",
name,
)),
}, },
Err(e) => yield Err(e), Err(e) => Err(e),
} }
};
Ok(OutputStream::new(stream))
} }
fn string_from(input: &[Value]) -> String { fn string_from(input: &[Value]) -> String {

View File

@ -2,7 +2,7 @@ use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, ReturnValue, Value}; use nu_protocol::{ReturnSuccess, Value};
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rand::thread_rng; use rand::thread_rng;
@ -24,28 +24,20 @@ impl WholeStreamCommand for Shuffle {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
shuffle(args, registry) shuffle(args, registry).await
} }
} }
fn shuffle(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn shuffle(
let stream = async_stream! { args: CommandArgs,
let mut input = args.input; _registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let input = args.input;
let mut values: Vec<Value> = input.collect().await; let mut values: Vec<Value> = input.collect().await;
let out = {
values.shuffle(&mut thread_rng()); values.shuffle(&mut thread_rng());
values.clone()
};
for val in out.into_iter() { Ok(futures::stream::iter(values.into_iter().map(ReturnSuccess::value)).to_output_stream())
yield ReturnSuccess::value(val);
}
};
let stream: BoxStream<'static, ReturnValue> = stream.boxed();
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -43,16 +43,27 @@ impl WholeStreamCommand for SubCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
split_column(args, registry) split_column(args, registry).await
} }
} }
fn split_column(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn split_column(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.span; let name_span = args.call_info.name_tag.span;
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (
let (SplitColumnArgs { separator, rest, collapse_empty }, mut input) = args.process(&registry).await?; SplitColumnArgs {
while let Some(v) = input.next().await { separator,
rest,
collapse_empty,
},
input,
) = args.process(&registry).await?;
Ok(input
.map(move |v| {
if let Ok(s) = v.as_string() { if let Ok(s) = v.as_string() {
let splitter = separator.replace("\\n", "\n"); let splitter = separator.replace("\\n", "\n");
trace!("splitting with {:?}", splitter); trace!("splitting with {:?}", splitter);
@ -79,7 +90,7 @@ fn split_column(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputS
dict.insert_untagged(v.clone(), Primitive::String(k.into())); dict.insert_untagged(v.clone(), Primitive::String(k.into()));
} }
yield ReturnSuccess::value(dict.into_value()); ReturnSuccess::value(dict.into_value())
} else { } else {
let mut dict = TaggedDictBuilder::new(&v.tag); let mut dict = TaggedDictBuilder::new(&v.tag);
for (&k, v) in split_result.iter().zip(positional.iter()) { for (&k, v) in split_result.iter().zip(positional.iter()) {
@ -88,21 +99,19 @@ fn split_column(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputS
UntaggedValue::Primitive(Primitive::String(k.into())), UntaggedValue::Primitive(Primitive::String(k.into())),
); );
} }
yield ReturnSuccess::value(dict.into_value()); ReturnSuccess::value(dict.into_value())
} }
} else { } else {
yield Err(ShellError::labeled_error_with_secondary( Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
name_span, name_span,
"value originates from here", "value originates from here",
v.tag.span, v.tag.span,
)); ))
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -26,14 +26,10 @@ impl WholeStreamCommand for Command {
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { Ok(OutputStream::one(Ok(ReturnSuccess::Value(
yield Ok(ReturnSuccess::Value(
UntaggedValue::string(crate::commands::help::get_help(&Command, &registry)) UntaggedValue::string(crate::commands::help::get_help(&Command, &registry))
.into_value(Tag::unknown()), .into_value(Tag::unknown()),
)); ))))
};
Ok(stream.to_output_stream())
} }
} }

View File

@ -35,41 +35,52 @@ impl WholeStreamCommand for SubCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
split_row(args, registry) split_row(args, registry).await
} }
} }
fn split_row(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn split_row(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let (SplitRowArgs { separator }, mut input) = args.process(&registry).await?; let (SplitRowArgs { separator }, input) = args.process(&registry).await?;
while let Some(v) = input.next().await { Ok(input
.flat_map(move |v| {
if let Ok(s) = v.as_string() { if let Ok(s) = v.as_string() {
let splitter = separator.item.replace("\\n", "\n"); let splitter = separator.item.replace("\\n", "\n");
trace!("splitting with {:?}", splitter); trace!("splitting with {:?}", splitter);
let split_result: Vec<_> = s.split(&splitter).filter(|s| s.trim() != "").collect(); let split_result: Vec<String> = s
.split(&splitter)
.filter_map(|s| {
if s.trim() != "" {
Some(s.to_string())
} else {
None
}
})
.collect();
trace!("split result = {:?}", split_result); trace!("split result = {:?}", split_result);
for s in split_result { futures::stream::iter(split_result.into_iter().map(move |s| {
yield ReturnSuccess::value( ReturnSuccess::value(
UntaggedValue::Primitive(Primitive::String(s.into())).into_value(&v.tag), UntaggedValue::Primitive(Primitive::String(s)).into_value(&v.tag),
); )
} }))
.to_output_stream()
} else { } else {
yield Err(ShellError::labeled_error_with_secondary( OutputStream::one(Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
name.span, name.span,
"value originates from here", "value originates from here",
v.tag.span, v.tag.span,
)); )))
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -37,7 +37,7 @@ impl WholeStreamCommand for SubCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
operate(args, registry) operate(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -49,47 +49,44 @@ impl WholeStreamCommand for SubCommand {
} }
} }
fn operate(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn operate(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (Arguments { rest }, input) = args.process(&registry).await?;
let (Arguments { rest }, mut input) = args.process(&registry).await?;
let column_paths: Vec<_> = rest.iter().map(|x| x.clone()).collect(); let column_paths: Vec<_> = rest;
while let Some(v) = input.next().await { Ok(input
.map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
match action(&v, v.tag()) { match action(&v, v.tag()) {
Ok(out) => yield ReturnSuccess::value(out), Ok(out) => ReturnSuccess::value(out),
Err(err) => { Err(err) => Err(err),
yield Err(err);
return;
}
} }
} else { } else {
let mut ret = v;
let mut ret = v.clone();
for path in &column_paths { for path in &column_paths {
let swapping = ret.swap_data_by_column_path(path, Box::new(move |old| action(old, old.tag()))); let swapping = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
);
match swapping { match swapping {
Ok(new_value) => { Ok(new_value) => {
ret = new_value; ret = new_value;
} }
Err(err) => { Err(err) => return Err(err),
yield Err(err);
return;
}
} }
} }
yield ReturnSuccess::value(ret); ReturnSuccess::value(ret)
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -28,14 +28,11 @@ impl WholeStreamCommand for Command {
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
yield Ok(ReturnSuccess::Value( Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(crate::commands::help::get_help(&Command, &registry)) UntaggedValue::string(crate::commands::help::get_help(&Command, &registry))
.into_value(Tag::unknown()), .into_value(Tag::unknown()),
)); )))
};
Ok(stream.to_output_stream())
} }
} }

View File

@ -37,7 +37,7 @@ impl WholeStreamCommand for SubCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
operate(args, registry) operate(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -49,47 +49,46 @@ impl WholeStreamCommand for SubCommand {
} }
} }
fn operate(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn operate(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (Arguments { rest }, input) = args.process(&registry).await?;
let (Arguments { rest }, mut input) = args.process(&registry).await?;
let column_paths: Vec<_> = rest.iter().map(|x| x.clone()).collect(); let column_paths: Vec<_> = rest;
while let Some(v) = input.next().await { Ok(input
.map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
match action(&v, v.tag()) { match action(&v, v.tag()) {
Ok(out) => yield ReturnSuccess::value(out), Ok(out) => ReturnSuccess::value(out),
Err(err) => { Err(err) => Err(err),
yield Err(err);
return;
}
} }
} else { } else {
let mut ret = v;
let mut ret = v.clone();
for path in &column_paths { for path in &column_paths {
let swapping = ret.swap_data_by_column_path(path, Box::new(move |old| action(old, old.tag()))); let swapping = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
);
match swapping { match swapping {
Ok(new_value) => { Ok(new_value) => {
ret = new_value; ret = new_value;
} }
Err(err) => { Err(err) => {
yield Err(err); return Err(err);
return;
} }
} }
} }
yield ReturnSuccess::value(ret); ReturnSuccess::value(ret)
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -44,7 +44,7 @@ impl WholeStreamCommand for SubCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
operate(args, registry) operate(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -59,52 +59,56 @@ impl WholeStreamCommand for SubCommand {
#[derive(Clone)] #[derive(Clone)]
struct FindReplace(String, String); struct FindReplace(String, String);
fn operate(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn operate(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (
let (Arguments { find, replace, rest }, mut input) = args.process(&registry).await?; Arguments {
find,
replace,
rest,
},
input,
) = args.process(&registry).await?;
let options = FindReplace(find.item, replace.item); let options = FindReplace(find.item, replace.item);
let column_paths: Vec<_> = rest.iter().map(|x| x.clone()).collect(); let column_paths: Vec<_> = rest;
while let Some(v) = input.next().await { Ok(input
.map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
match action(&v, &options, v.tag()) { match action(&v, &options, v.tag()) {
Ok(out) => yield ReturnSuccess::value(out), Ok(out) => ReturnSuccess::value(out),
Err(err) => { Err(err) => Err(err),
yield Err(err);
return;
}
} }
} else { } else {
let mut ret = v;
let mut ret = v.clone();
for path in &column_paths { for path in &column_paths {
let options = options.clone(); let options = options.clone();
let swapping = ret.swap_data_by_column_path(path, Box::new(move |old| { let swapping = ret.swap_data_by_column_path(
action(old, &options, old.tag()) path,
})); Box::new(move |old| action(old, &options, old.tag())),
);
match swapping { match swapping {
Ok(new_value) => { Ok(new_value) => {
ret = new_value; ret = new_value;
} }
Err(err) => { Err(err) => {
yield Err(err); return Err(err);
return;
} }
} }
} }
yield ReturnSuccess::value(ret); ReturnSuccess::value(ret)
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
fn action(input: &Value, options: &FindReplace, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn action(input: &Value, options: &FindReplace, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -37,7 +37,7 @@ impl WholeStreamCommand for SubCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
operate(args, registry) operate(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -59,50 +59,49 @@ impl WholeStreamCommand for SubCommand {
#[derive(Clone)] #[derive(Clone)]
struct Replace(String); struct Replace(String);
fn operate(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn operate(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (Arguments { replace, rest }, input) = args.process(&registry).await?;
let (Arguments { replace, rest }, mut input) = args.process(&registry).await?;
let options = Replace(replace.item); let options = Replace(replace.item);
let column_paths: Vec<_> = rest.iter().map(|x| x.clone()).collect(); let column_paths: Vec<_> = rest;
while let Some(v) = input.next().await { Ok(input
.map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
match action(&v, &options, v.tag()) { match action(&v, &options, v.tag()) {
Ok(out) => yield ReturnSuccess::value(out), Ok(out) => ReturnSuccess::value(out),
Err(err) => { Err(err) => Err(err),
yield Err(err);
return;
}
} }
} else { } else {
let mut ret = v;
let mut ret = v.clone();
for path in &column_paths { for path in &column_paths {
let options = options.clone(); let options = options.clone();
let swapping = ret.swap_data_by_column_path(path, Box::new(move |old| action(old, &options, old.tag()))); let swapping = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, &options, old.tag())),
);
match swapping { match swapping {
Ok(new_value) => { Ok(new_value) => {
ret = new_value; ret = new_value;
} }
Err(err) => { Err(err) => {
yield Err(err); return Err(err);
return;
} }
} }
} }
yield ReturnSuccess::value(ret); ReturnSuccess::value(ret)
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
fn action(_input: &Value, options: &Replace, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn action(_input: &Value, options: &Replace, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -46,7 +46,7 @@ impl WholeStreamCommand for SubCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
operate(args, registry) operate(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -73,91 +73,83 @@ impl WholeStreamCommand for SubCommand {
#[derive(Clone)] #[derive(Clone)]
struct Substring(usize, usize); struct Substring(usize, usize);
fn operate(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn operate(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (Arguments { range, rest }, input) = args.process(&registry).await?;
let (Arguments { range, rest }, mut input) = args.process(&registry).await?;
let v: Vec<&str> = range.item.split(',').collect(); let v: Vec<&str> = range.item.split(',').collect();
let start = match v[0] { let start = match v[0] {
"" => 0, "" => 0,
_ => v[0] _ => v[0].trim().parse().map_err(|_| {
.trim()
.parse()
.map_err(|_| {
ShellError::labeled_error( ShellError::labeled_error(
"could not perform substring", "could not perform substring",
"could not perform substring", "could not perform substring",
name.span, name.span,
) )
})? })?,
}; };
let end = match v[1] { let end = match v[1] {
"" => usize::max_value(), "" => usize::max_value(),
_ => v[1] _ => v[1].trim().parse().map_err(|_| {
.trim()
.parse()
.map_err(|_| {
ShellError::labeled_error( ShellError::labeled_error(
"could not perform substring", "could not perform substring",
"could not perform substring", "could not perform substring",
name.span, name.span,
) )
})? })?,
}; };
if start > end { if start > end {
yield Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"End must be greater than or equal to Start", "End must be greater than or equal to Start",
"End must be greater than or equal to Start", "End must be greater than or equal to Start",
name.span, name.span,
)); ));
return;
} }
let options = Substring(start, end); let options = Substring(start, end);
let column_paths: Vec<_> = rest.iter().map(|x| x.clone()).collect(); let column_paths: Vec<_> = rest;
while let Some(v) = input.next().await { Ok(input
.map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
match action(&v, &options, v.tag()) { match action(&v, &options, v.tag()) {
Ok(out) => yield ReturnSuccess::value(out), Ok(out) => ReturnSuccess::value(out),
Err(err) => { Err(err) => Err(err),
yield Err(err);
return;
}
} }
} else { } else {
let mut ret = v;
let mut ret = v.clone();
for path in &column_paths { for path in &column_paths {
let options = options.clone(); let options = options.clone();
let swapping = ret.swap_data_by_column_path(path, Box::new(move |old| action(old, &options, old.tag()))); let swapping = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, &options, old.tag())),
);
match swapping { match swapping {
Ok(new_value) => { Ok(new_value) => {
ret = new_value; ret = new_value;
} }
Err(err) => { Err(err) => {
yield Err(err); return Err(err);
return;
} }
} }
} }
yield ReturnSuccess::value(ret); ReturnSuccess::value(ret)
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
fn action(input: &Value, options: &Substring, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn action(input: &Value, options: &Substring, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -47,7 +47,7 @@ impl WholeStreamCommand for SubCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
operate(args, registry) operate(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -62,54 +62,53 @@ impl WholeStreamCommand for SubCommand {
#[derive(Clone)] #[derive(Clone)]
struct DatetimeFormat(String); struct DatetimeFormat(String);
fn operate(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn operate(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (Arguments { format, rest }, input) = args.process(&registry).await?;
let (Arguments { format, rest }, mut input) = args.process(&registry).await?;
let column_paths: Vec<_> = rest.iter().map(|x| x.clone()).collect(); let column_paths: Vec<_> = rest;
let options = if let Some(Tagged { item: fmt, tag }) = format { let options = if let Some(Tagged { item: fmt, .. }) = format {
DatetimeFormat(fmt) DatetimeFormat(fmt)
} else { } else {
DatetimeFormat(String::from("%d.%m.%Y %H:%M %P %z")) DatetimeFormat(String::from("%d.%m.%Y %H:%M %P %z"))
}; };
while let Some(v) = input.next().await { Ok(input
.map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
match action(&v, &options, v.tag()) { match action(&v, &options, v.tag()) {
Ok(out) => yield ReturnSuccess::value(out), Ok(out) => ReturnSuccess::value(out),
Err(err) => { Err(err) => Err(err),
yield Err(err);
return;
}
} }
} else { } else {
let mut ret = v;
let mut ret = v.clone();
for path in &column_paths { for path in &column_paths {
let options = options.clone(); let options = options.clone();
let swapping = ret.swap_data_by_column_path(path, Box::new(move |old| action(old, &options, old.tag()))); let swapping = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, &options, old.tag())),
);
match swapping { match swapping {
Ok(new_value) => { Ok(new_value) => {
ret = new_value; ret = new_value;
} }
Err(err) => { Err(err) => {
yield Err(err); return Err(err);
return;
} }
} }
} }
yield ReturnSuccess::value(ret); ReturnSuccess::value(ret)
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
fn action( fn action(

View File

@ -40,7 +40,7 @@ impl WholeStreamCommand for SubCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
operate(args, registry) operate(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -52,47 +52,46 @@ impl WholeStreamCommand for SubCommand {
} }
} }
fn operate(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn operate(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (Arguments { rest }, input) = args.process(&registry).await?;
let (Arguments { rest }, mut input) = args.process(&registry).await?;
let column_paths: Vec<_> = rest.iter().map(|x| x.clone()).collect(); let column_paths: Vec<_> = rest;
while let Some(v) = input.next().await { Ok(input
.map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
match action(&v, v.tag()) { match action(&v, v.tag()) {
Ok(out) => yield ReturnSuccess::value(out), Ok(out) => ReturnSuccess::value(out),
Err(err) => { Err(err) => Err(err),
yield Err(err);
return;
}
} }
} else { } else {
let mut ret = v;
let mut ret = v.clone();
for path in &column_paths { for path in &column_paths {
let swapping = ret.swap_data_by_column_path(path, Box::new(move |old| action(old, old.tag()))); let swapping = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
);
match swapping { match swapping {
Ok(new_value) => { Ok(new_value) => {
ret = new_value; ret = new_value;
} }
Err(err) => { Err(err) => {
yield Err(err); return Err(err);
return;
} }
} }
} }
yield ReturnSuccess::value(ret); ReturnSuccess::value(ret)
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -40,7 +40,7 @@ impl WholeStreamCommand for SubCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
operate(args, registry) operate(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -52,47 +52,46 @@ impl WholeStreamCommand for SubCommand {
} }
} }
fn operate(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn operate(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (Arguments { rest }, input) = args.process(&registry).await?;
let (Arguments { rest }, mut input) = args.process(&registry).await?;
let column_paths: Vec<_> = rest.iter().map(|x| x.clone()).collect(); let column_paths: Vec<_> = rest;
while let Some(v) = input.next().await { Ok(input
.map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
match action(&v, v.tag()) { match action(&v, v.tag()) {
Ok(out) => yield ReturnSuccess::value(out), Ok(out) => ReturnSuccess::value(out),
Err(err) => { Err(err) => Err(err),
yield Err(err);
return;
}
} }
} else { } else {
let mut ret = v;
let mut ret = v.clone();
for path in &column_paths { for path in &column_paths {
let swapping = ret.swap_data_by_column_path(path, Box::new(move |old| action(old, old.tag()))); let swapping = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
);
match swapping { match swapping {
Ok(new_value) => { Ok(new_value) => {
ret = new_value; ret = new_value;
} }
Err(err) => { Err(err) => {
yield Err(err); return Err(err);
return;
} }
} }
} }
yield ReturnSuccess::value(ret); ReturnSuccess::value(ret)
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -37,7 +37,7 @@ impl WholeStreamCommand for SubCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
operate(args, registry) operate(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -49,47 +49,46 @@ impl WholeStreamCommand for SubCommand {
} }
} }
fn operate(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn operate(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (Arguments { rest }, input) = args.process(&registry).await?;
let (Arguments { rest }, mut input) = args.process(&registry).await?;
let column_paths: Vec<_> = rest.iter().map(|x| x.clone()).collect(); let column_paths: Vec<_> = rest;
while let Some(v) = input.next().await { Ok(input
.map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
match action(&v, v.tag()) { match action(&v, v.tag()) {
Ok(out) => yield ReturnSuccess::value(out), Ok(out) => ReturnSuccess::value(out),
Err(err) => { Err(err) => Err(err),
yield Err(err);
return;
}
} }
} else { } else {
let mut ret = v;
let mut ret = v.clone();
for path in &column_paths { for path in &column_paths {
let swapping = ret.swap_data_by_column_path(path, Box::new(move |old| action(old, old.tag()))); let swapping = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
);
match swapping { match swapping {
Ok(new_value) => { Ok(new_value) => {
ret = new_value; ret = new_value;
} }
Err(err) => { Err(err) => {
yield Err(err); return Err(err);
return;
} }
} }
} }
yield ReturnSuccess::value(ret); ReturnSuccess::value(ret)
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -37,7 +37,7 @@ impl WholeStreamCommand for SubCommand {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
operate(args, registry) operate(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -49,47 +49,46 @@ impl WholeStreamCommand for SubCommand {
} }
} }
fn operate(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn operate(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { let (Arguments { rest }, input) = args.process(&registry).await?;
let (Arguments { rest }, mut input) = args.process(&registry).await?;
let column_paths: Vec<_> = rest.iter().map(|x| x.clone()).collect(); let column_paths: Vec<_> = rest;
while let Some(v) = input.next().await { Ok(input
.map(move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
match action(&v, v.tag()) { match action(&v, v.tag()) {
Ok(out) => yield ReturnSuccess::value(out), Ok(out) => ReturnSuccess::value(out),
Err(err) => { Err(err) => Err(err),
yield Err(err);
return;
}
} }
} else { } else {
let mut ret = v;
let mut ret = v.clone();
for path in &column_paths { for path in &column_paths {
let swapping = ret.swap_data_by_column_path(path, Box::new(move |old| action(old, old.tag()))); let swapping = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
);
match swapping { match swapping {
Ok(new_value) => { Ok(new_value) => {
ret = new_value; ret = new_value;
} }
Err(err) => { Err(err) => {
yield Err(err); return Err(err);
return;
} }
} }
} }
yield ReturnSuccess::value(ret); ReturnSuccess::value(ret)
} }
} })
}; .to_output_stream())
Ok(stream.to_output_stream())
} }
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> { fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {

View File

@ -2,7 +2,7 @@ use crate::commands::WholeStreamCommand;
use crate::prelude::*; use crate::prelude::*;
use crate::utils::data_processing::{reducer_for, Reduce}; use crate::utils::data_processing::{reducer_for, Reduce};
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Dictionary, ReturnSuccess, ReturnValue, Signature, UntaggedValue, Value}; use nu_protocol::{Dictionary, ReturnSuccess, Signature, UntaggedValue, Value};
use num_traits::identities::Zero; use num_traits::identities::Zero;
use indexmap::map::IndexMap; use indexmap::map::IndexMap;
@ -38,6 +38,7 @@ impl WholeStreamCommand for Sum {
name: args.call_info.name_tag, name: args.call_info.name_tag,
raw_input: args.raw_input, raw_input: args.raw_input,
}) })
.await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -56,27 +57,25 @@ impl WholeStreamCommand for Sum {
} }
} }
fn sum(RunnableContext { mut input, .. }: RunnableContext) -> Result<OutputStream, ShellError> { async fn sum(
let stream = async_stream! { RunnableContext { mut input, .. }: RunnableContext,
let mut values: Vec<Value> = input.drain_vec().await; ) -> Result<OutputStream, ShellError> {
let values: Vec<Value> = input.drain_vec().await;
let action = reducer_for(Reduce::Sum); let action = reducer_for(Reduce::Sum);
if values.iter().all(|v| if let UntaggedValue::Primitive(_) = v.value {true} else {false}) { if values.iter().all(|v| v.is_primitive()) {
let total = action(Value::zero(), values)?; let total = action(Value::zero(), values)?;
yield ReturnSuccess::value(total) Ok(OutputStream::one(ReturnSuccess::value(total)))
} else { } else {
let mut column_values = IndexMap::new(); let mut column_values = IndexMap::new();
for value in values { for value in values {
match value.value { if let UntaggedValue::Row(row_dict) = value.value {
UntaggedValue::Row(row_dict) => {
for (key, value) in row_dict.entries.iter() { for (key, value) in row_dict.entries.iter() {
column_values column_values
.entry(key.clone()) .entry(key.clone())
.and_modify(|v: &mut Vec<Value>| v.push(value.clone())) .and_modify(|v: &mut Vec<Value>| v.push(value.clone()))
.or_insert(vec![value.clone()]); .or_insert(vec![value.clone()]);
} }
},
table => {},
}; };
} }
@ -86,18 +85,17 @@ fn sum(RunnableContext { mut input, .. }: RunnableContext) -> Result<OutputStrea
match sum { match sum {
Ok(value) => { Ok(value) => {
column_totals.insert(col_name, value); column_totals.insert(col_name, value);
}, }
Err(err) => yield Err(err), Err(err) => return Err(err),
}; };
} }
yield ReturnSuccess::value( Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(Dictionary {entries: column_totals}).into_untagged_value()) UntaggedValue::Row(Dictionary {
entries: column_totals,
})
.into_untagged_value(),
)))
} }
};
let stream: BoxStream<'static, ReturnValue> = stream.boxed();
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -57,15 +57,24 @@ impl WholeStreamCommand for TSortBy {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
t_sort_by(args, registry) t_sort_by(args, registry).await
} }
} }
fn t_sort_by(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn t_sort_by(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let (TSortByArgs { show_columns, group_by, ..}, mut input) = args.process(&registry).await?; let (
TSortByArgs {
show_columns,
group_by,
..
},
mut input,
) = args.process(&registry).await?;
let values: Vec<Value> = input.collect().await; let values: Vec<Value> = input.collect().await;
let column_grouped_by_name = if let Some(grouped_by) = group_by { let column_grouped_by_name = if let Some(grouped_by) = group_by {
@ -75,18 +84,20 @@ fn t_sort_by(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
}; };
if show_columns { if show_columns {
for label in columns_sorted(column_grouped_by_name, &values[0], &name).into_iter() { Ok(futures::stream::iter(
yield ReturnSuccess::value(UntaggedValue::string(label.item).into_value(label.tag)); columns_sorted(column_grouped_by_name, &values[0], &name)
} .into_iter()
.map(move |label| {
ReturnSuccess::value(UntaggedValue::string(label.item).into_value(label.tag))
}),
)
.to_output_stream())
} else { } else {
match t_sort(column_grouped_by_name, None, &values[0], name) { match t_sort(column_grouped_by_name, None, &values[0], name) {
Ok(sorted) => yield ReturnSuccess::value(sorted), Ok(sorted) => Ok(OutputStream::one(ReturnSuccess::value(sorted))),
Err(err) => yield Err(err) Err(err) => Ok(OutputStream::one(Err(err))),
} }
} }
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -2,7 +2,7 @@ use crate::commands::WholeStreamCommand;
use crate::format::TableView; use crate::format::TableView;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
use std::time::Instant; use std::time::Instant;
const STREAM_PAGE_SIZE: usize = 1000; const STREAM_PAGE_SIZE: usize = 1000;
@ -34,29 +34,32 @@ impl WholeStreamCommand for Table {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
table(args, registry) table(args, registry).await
} }
} }
fn table(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn table(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let mut args = args.evaluate_once(&registry).await?; let mut args = args.evaluate_once(&registry).await?;
let mut finished = false; let mut finished = false;
let host = args.host.clone(); let host = args.host.clone();
let mut start_number = match args.get("start_number") { let mut start_number = match args.get("start_number") {
Some(Value { value: UntaggedValue::Primitive(Primitive::Int(i)), .. }) => { Some(Value {
value: UntaggedValue::Primitive(Primitive::Int(i)),
..
}) => {
if let Some(num) = i.to_usize() { if let Some(num) = i.to_usize() {
num num
} else { } else {
yield Err(ShellError::labeled_error("Expected a row number", "expected a row number", &args.args.call_info.name_tag)); return Err(ShellError::labeled_error(
0 "Expected a row number",
"expected a row number",
&args.args.call_info.name_tag,
));
} }
} }
_ => { _ => 0,
0
}
}; };
let mut delay_slot = None; let mut delay_slot = None;
@ -109,7 +112,7 @@ fn table(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
let input: Vec<Value> = new_input.into(); let input: Vec<Value> = new_input.into();
if input.len() > 0 { if !input.is_empty() {
let mut host = host.lock(); let mut host = host.lock();
let view = TableView::from_list(&input, start_number); let view = TableView::from_list(&input, start_number);
@ -121,13 +124,7 @@ fn table(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
start_number += input.len(); start_number += input.len();
} }
// Needed for async_stream to type check Ok(OutputStream::empty())
if false {
yield ReturnSuccess::value(UntaggedValue::nothing().into_value(Tag::unknown()));
}
};
Ok(OutputStream::new(stream))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -26,14 +26,10 @@ impl WholeStreamCommand for To {
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! { Ok(OutputStream::one(ReturnSuccess::value(
yield Ok(ReturnSuccess::Value(
UntaggedValue::string(crate::commands::help::get_help(&To, &registry)) UntaggedValue::string(crate::commands::help::get_help(&To, &registry))
.into_value(Tag::unknown()), .into_value(Tag::unknown()),
)); )))
};
Ok(stream.to_output_stream())
} }
} }

View File

@ -29,7 +29,7 @@ impl WholeStreamCommand for ToBSON {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
to_bson(args, registry) to_bson(args, registry).await
} }
fn is_binary(&self) -> bool { fn is_binary(&self) -> bool {
@ -261,34 +261,37 @@ fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result<Vec<u8>, ShellError> {
Ok(out) Ok(out)
} }
fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn to_bson(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let args = args.evaluate_once(&registry).await?; let args = args.evaluate_once(&registry).await?;
let name_tag = args.name_tag(); let name_tag = args.name_tag();
let name_span = name_tag.span; let name_span = name_tag.span;
let input: Vec<Value> = args.input.collect().await; let input: Vec<Value> = args.input.collect().await;
let to_process_input = if input.len() > 1 { let to_process_input = match input.len() {
x if x > 1 => {
let tag = input[0].tag.clone(); let tag = input[0].tag.clone();
vec![Value { value: UntaggedValue::Table(input), tag } ] vec![Value {
} else if input.len() == 1 { value: UntaggedValue::Table(input),
input tag,
} else { }]
vec![] }
1 => input,
_ => vec![],
}; };
for value in to_process_input { Ok(futures::stream::iter(to_process_input.into_iter().map(
match value_to_bson_value(&value) { move |value| match value_to_bson_value(&value) {
Ok(bson_value) => { Ok(bson_value) => {
let value_span = value.tag.span; let value_span = value.tag.span;
match bson_value_to_bytes(bson_value, name_tag.clone()) { match bson_value_to_bytes(bson_value, name_tag.clone()) {
Ok(x) => yield ReturnSuccess::value( Ok(x) => ReturnSuccess::value(UntaggedValue::binary(x).into_value(&name_tag)),
UntaggedValue::binary(x).into_value(&name_tag), _ => Err(ShellError::labeled_error_with_secondary(
),
_ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a table with BSON-compatible structure from pipeline", "Expected a table with BSON-compatible structure from pipeline",
"requires BSON-compatible input", "requires BSON-compatible input",
name_span, name_span,
@ -297,15 +300,14 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
)), )),
} }
} }
_ => yield Err(ShellError::labeled_error( _ => Err(ShellError::labeled_error(
"Expected a table with BSON-compatible structure from pipeline", "Expected a table with BSON-compatible structure from pipeline",
"requires BSON-compatible input", "requires BSON-compatible input",
&name_tag)) &name_tag,
} )),
} },
}; ))
.to_output_stream())
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -42,15 +42,20 @@ impl WholeStreamCommand for ToCSV {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
to_csv(args, registry) to_csv(args, registry).await
} }
} }
fn to_csv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn to_csv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let (ToCSVArgs { separator, headerless }, mut input) = args.process(&registry).await?; let (
ToCSVArgs {
separator,
headerless,
},
input,
) = args.process(&registry).await?;
let sep = match separator { let sep = match separator {
Some(Value { Some(Value {
value: UntaggedValue::Primitive(Primitive::String(s)), value: UntaggedValue::Primitive(Primitive::String(s)),
@ -62,12 +67,11 @@ fn to_csv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
} else { } else {
let vec_s: Vec<char> = s.chars().collect(); let vec_s: Vec<char> = s.chars().collect();
if vec_s.len() != 1 { if vec_s.len() != 1 {
yield Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a single separator char from --separator", "Expected a single separator char from --separator",
"requires a single character string input", "requires a single character string input",
tag, tag,
)); ));
return;
}; };
vec_s[0] vec_s[0]
} }
@ -75,14 +79,7 @@ fn to_csv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
_ => ',', _ => ',',
}; };
let mut result = to_delimited_data(headerless, sep, "CSV", input, name)?; to_delimited_data(headerless, sep, "CSV", input, name).await
while let Some(item) = result.next().await {
yield item;
}
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -165,7 +165,7 @@ fn merge_descriptors(values: &[Value]) -> Vec<Spanned<String>> {
ret ret
} }
pub fn to_delimited_data( pub async fn to_delimited_data(
headerless: bool, headerless: bool,
sep: char, sep: char,
format_name: &'static str, format_name: &'static str,
@ -175,33 +175,41 @@ pub fn to_delimited_data(
let name_tag = name; let name_tag = name;
let name_span = name_tag.span; let name_span = name_tag.span;
let stream = async_stream! {
let input: Vec<Value> = input.collect().await; let input: Vec<Value> = input.collect().await;
let to_process_input = if input.len() > 1 { let to_process_input = match input.len() {
x if x > 1 => {
let tag = input[0].tag.clone(); let tag = input[0].tag.clone();
vec![Value { value: UntaggedValue::Table(input), tag } ] vec![Value {
} else if input.len() == 1 { value: UntaggedValue::Table(input),
input tag,
} else { }]
vec![] }
1 => input,
_ => vec![],
}; };
for value in to_process_input { Ok(
futures::stream::iter(to_process_input.into_iter().map(move |value| {
match from_value_to_delimited_string(&clone_tagged_value(&value), sep) { match from_value_to_delimited_string(&clone_tagged_value(&value), sep) {
Ok(mut x) => { Ok(mut x) => {
if headerless { if headerless {
x.find('\n').map(|second_line|{ if let Some(second_line) = x.find('\n') {
let start = second_line + 1; let start = second_line + 1;
x.replace_range(0..start, ""); x.replace_range(0..start, "");
});
} }
yield ReturnSuccess::value(UntaggedValue::Primitive(Primitive::String(x)).into_value(&name_tag))
} }
Err(x) => { ReturnSuccess::value(
let expected = format!("Expected a table with {}-compatible structure from pipeline", format_name); UntaggedValue::Primitive(Primitive::String(x)).into_value(&name_tag),
)
}
Err(_) => {
let expected = format!(
"Expected a table with {}-compatible structure from pipeline",
format_name
);
let requires = format!("requires {}-compatible input", format_name); let requires = format!("requires {}-compatible input", format_name);
yield Err(ShellError::labeled_error_with_secondary( Err(ShellError::labeled_error_with_secondary(
expected, expected,
requires, requires,
name_span, name_span,
@ -210,8 +218,7 @@ pub fn to_delimited_data(
)) ))
} }
} }
} }))
}; .to_output_stream(),
)
Ok(stream.to_output_stream())
} }

View File

@ -38,7 +38,7 @@ impl WholeStreamCommand for ToJSON {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
to_json(args, registry) to_json(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -163,25 +163,30 @@ fn json_list(input: &[Value]) -> Result<Vec<serde_json::Value>, ShellError> {
Ok(out) Ok(out)
} }
fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn to_json(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let name_tag = args.call_info.name_tag.clone(); let name_tag = args.call_info.name_tag.clone();
let (ToJSONArgs { pretty }, mut input) = args.process(&registry).await?; let (ToJSONArgs { pretty }, input) = args.process(&registry).await?;
let name_span = name_tag.span; let name_span = name_tag.span;
let input: Vec<Value> = input.collect().await; let input: Vec<Value> = input.collect().await;
let to_process_input = if input.len() > 1 { let to_process_input = match input.len() {
x if x > 1 => {
let tag = input[0].tag.clone(); let tag = input[0].tag.clone();
vec![Value { value: UntaggedValue::Table(input), tag } ] vec![Value {
} else if input.len() == 1 { value: UntaggedValue::Table(input),
input tag,
} else { }]
vec![] }
1 => input,
_ => vec![],
}; };
for value in to_process_input { Ok(futures::stream::iter(to_process_input.into_iter().map(
match value_to_json_value(&value) { move |value| match value_to_json_value(&value) {
Ok(json_value) => { Ok(json_value) => {
let value_span = value.tag.span; let value_span = value.tag.span;
@ -191,15 +196,32 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
let mut pretty_format_failed = true; let mut pretty_format_failed = true;
if let Ok(pretty_u64) = pretty_value.as_u64() { if let Ok(pretty_u64) = pretty_value.as_u64() {
if let Ok(serde_json_value) = serde_json::from_str::<serde_json::Value>(serde_json_string.as_str()) { if let Ok(serde_json_value) =
let indentation_string = std::iter::repeat(" ").take(pretty_u64 as usize).collect::<String>(); serde_json::from_str::<serde_json::Value>(
let serde_formatter = serde_json::ser::PrettyFormatter::with_indent(indentation_string.as_bytes()); serde_json_string.as_str(),
)
{
let indentation_string = std::iter::repeat(" ")
.take(pretty_u64 as usize)
.collect::<String>();
let serde_formatter =
serde_json::ser::PrettyFormatter::with_indent(
indentation_string.as_bytes(),
);
let serde_buffer = Vec::new(); let serde_buffer = Vec::new();
let mut serde_serializer = serde_json::Serializer::with_formatter(serde_buffer, serde_formatter); let mut serde_serializer =
serde_json::Serializer::with_formatter(
serde_buffer,
serde_formatter,
);
let serde_json_object = json!(serde_json_value); let serde_json_object = json!(serde_json_value);
if let Ok(()) = serde_json_object.serialize(&mut serde_serializer) { if let Ok(()) =
if let Ok(ser_json_string) = String::from_utf8(serde_serializer.into_inner()) { serde_json_object.serialize(&mut serde_serializer)
{
if let Ok(ser_json_string) =
String::from_utf8(serde_serializer.into_inner())
{
pretty_format_failed = false; pretty_format_failed = false;
serde_json_string = ser_json_string serde_json_string = ser_json_string
} }
@ -208,16 +230,20 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
} }
if pretty_format_failed { if pretty_format_failed {
yield Err(ShellError::labeled_error("Pretty formatting failed", "failed", pretty_value.tag())); return Err(ShellError::labeled_error(
return; "Pretty formatting failed",
"failed",
pretty_value.tag(),
));
} }
} }
yield ReturnSuccess::value( ReturnSuccess::value(
UntaggedValue::Primitive(Primitive::String(serde_json_string)).into_value(&name_tag), UntaggedValue::Primitive(Primitive::String(serde_json_string))
.into_value(&name_tag),
) )
}, }
_ => yield Err(ShellError::labeled_error_with_secondary( _ => Err(ShellError::labeled_error_with_secondary(
"Expected a table with JSON-compatible structure.tag() from pipeline", "Expected a table with JSON-compatible structure.tag() from pipeline",
"requires JSON-compatible input", "requires JSON-compatible input",
name_span, name_span,
@ -226,15 +252,14 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
)), )),
} }
} }
_ => yield Err(ShellError::labeled_error( _ => Err(ShellError::labeled_error(
"Expected a table with JSON-compatible structure from pipeline", "Expected a table with JSON-compatible structure from pipeline",
"requires JSON-compatible input", "requires JSON-compatible input",
&name_tag)) &name_tag,
} )),
} },
}; ))
.to_output_stream())
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -24,7 +24,7 @@ impl WholeStreamCommand for ToTOML {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
to_toml(args, registry) to_toml(args, registry).await
} }
// TODO: add an example here. What commands to run to get a Row(Dictionary)? // TODO: add an example here. What commands to run to get a Row(Dictionary)?
// fn examples(&self) -> Vec<Example> { // fn examples(&self) -> Vec<Example> {
@ -135,49 +135,53 @@ fn collect_values(input: &[Value]) -> Result<Vec<toml::Value>, ShellError> {
Ok(out) Ok(out)
} }
fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn to_toml(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let args = args.evaluate_once(&registry).await?; let args = args.evaluate_once(&registry).await?;
let name_tag = args.name_tag(); let name_tag = args.name_tag();
let name_span = name_tag.span; let name_span = name_tag.span;
let input: Vec<Value> = args.input.collect().await; let input: Vec<Value> = args.input.collect().await;
let to_process_input = if input.len() > 1 { let to_process_input = match input.len() {
x if x > 1 => {
let tag = input[0].tag.clone(); let tag = input[0].tag.clone();
vec![Value { value: UntaggedValue::Table(input), tag } ] vec![Value {
} else if input.len() == 1 { value: UntaggedValue::Table(input),
input tag,
} else { }]
vec![] }
1 => input,
_ => vec![],
}; };
for value in to_process_input { Ok(
futures::stream::iter(to_process_input.into_iter().map(move |value| {
let value_span = value.tag.span; let value_span = value.tag.span;
match value_to_toml_value(&value) { match value_to_toml_value(&value) {
Ok(toml_value) => { Ok(toml_value) => match toml::to_string(&toml_value) {
match toml::to_string(&toml_value) { Ok(x) => ReturnSuccess::value(
Ok(x) => yield ReturnSuccess::value(
UntaggedValue::Primitive(Primitive::String(x)).into_value(&name_tag), UntaggedValue::Primitive(Primitive::String(x)).into_value(&name_tag),
), ),
_ => yield Err(ShellError::labeled_error_with_secondary( _ => Err(ShellError::labeled_error_with_secondary(
"Expected a table with TOML-compatible structure.tag() from pipeline", "Expected a table with TOML-compatible structure.tag() from pipeline",
"requires TOML-compatible input", "requires TOML-compatible input",
name_span, name_span,
"originates from here".to_string(), "originates from here".to_string(),
value_span, value_span,
)), )),
} },
} _ => Err(ShellError::labeled_error(
_ => yield Err(ShellError::labeled_error(
"Expected a table with TOML-compatible structure from pipeline", "Expected a table with TOML-compatible structure from pipeline",
"requires TOML-compatible input", "requires TOML-compatible input",
&name_tag)) &name_tag,
)),
} }
} }))
}; .to_output_stream(),
)
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -34,29 +34,16 @@ impl WholeStreamCommand for ToTSV {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
to_tsv(args, registry) to_tsv(args, registry).await
} }
} }
fn to_tsv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn to_tsv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let name = args.call_info.name_tag.clone(); let name = args.call_info.name_tag.clone();
let (ToTSVArgs { headerless }, mut input) = args.process(&registry).await?; let (ToTSVArgs { headerless }, input) = args.process(&registry).await?;
let mut result = to_delimited_data(
headerless,
'\t',
"TSV",
input,
name,
)?;
while let Some(item) = result.next().await { to_delimited_data(headerless, '\t', "TSV", input, name).await
yield item;
}
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -24,67 +24,58 @@ impl WholeStreamCommand for ToURL {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
to_url(args, registry) to_url(args, registry).await
} }
} }
fn to_url(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn to_url(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let args = args.evaluate_once(&registry).await?; let args = args.evaluate_once(&registry).await?;
let tag = args.name_tag(); let tag = args.name_tag();
let input = args.input; let input = args.input;
let input: Vec<Value> = input.collect().await; Ok(input
.map(move |value| match value {
for value in input { Value {
match value { value: UntaggedValue::Row(row),
Value { value: UntaggedValue::Row(row), .. } => { ..
} => {
let mut row_vec = vec![]; let mut row_vec = vec![];
for (k,v) in row.entries { for (k, v) in row.entries {
match v.as_string() { match v.as_string() {
Ok(s) => { Ok(s) => {
row_vec.push((k.clone(), s.to_string())); row_vec.push((k.clone(), s.to_string()));
} }
_ => { _ => {
yield Err(ShellError::labeled_error_with_secondary( return Err(ShellError::labeled_error_with_secondary(
"Expected table with string values", "Expected table with string values",
"requires table with strings", "requires table with strings",
&tag, &tag,
"value originates from here", "value originates from here",
v.tag, v.tag,
)) ));
} }
} }
} }
match serde_urlencoded::to_string(row_vec) { match serde_urlencoded::to_string(row_vec) {
Ok(s) => { Ok(s) => ReturnSuccess::value(UntaggedValue::string(s).into_value(&tag)),
yield ReturnSuccess::value(UntaggedValue::string(s).into_value(&tag)); _ => Err(ShellError::labeled_error(
}
_ => {
yield Err(ShellError::labeled_error(
"Failed to convert to url-encoded", "Failed to convert to url-encoded",
"cannot url-encode", "cannot url-encode",
&tag, &tag,
)) )),
} }
} }
} Value { tag: value_tag, .. } => Err(ShellError::labeled_error_with_secondary(
Value { tag: value_tag, .. } => {
yield Err(ShellError::labeled_error_with_secondary(
"Expected a table from pipeline", "Expected a table from pipeline",
"requires table input", "requires table input",
&tag, &tag,
"value originates from here", "value originates from here",
value_tag.span, value_tag.span,
)) )),
} })
} .to_output_stream())
}
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -24,7 +24,7 @@ impl WholeStreamCommand for ToYAML {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
to_yaml(args, registry) to_yaml(args, registry).await
} }
} }
@ -125,51 +125,55 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
}) })
} }
fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn to_yaml(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let args = args.evaluate_once(&registry).await?; let args = args.evaluate_once(&registry).await?;
let name_tag = args.name_tag(); let name_tag = args.name_tag();
let name_span = name_tag.span; let name_span = name_tag.span;
let input: Vec<Value> = args.input.collect().await; let input: Vec<Value> = args.input.collect().await;
let to_process_input = if input.len() > 1 { let to_process_input = match input.len() {
x if x > 1 => {
let tag = input[0].tag.clone(); let tag = input[0].tag.clone();
vec![Value { value: UntaggedValue::Table(input), tag } ] vec![Value {
} else if input.len() == 1 { value: UntaggedValue::Table(input),
input tag,
} else { }]
vec![] }
1 => input,
_ => vec![],
}; };
for value in to_process_input { Ok(
futures::stream::iter(to_process_input.into_iter().map(move |value| {
let value_span = value.tag.span; let value_span = value.tag.span;
match value_to_yaml_value(&value) { match value_to_yaml_value(&value) {
Ok(yaml_value) => { Ok(yaml_value) => match serde_yaml::to_string(&yaml_value) {
match serde_yaml::to_string(&yaml_value) { Ok(x) => ReturnSuccess::value(
Ok(x) => yield ReturnSuccess::value(
UntaggedValue::Primitive(Primitive::String(x)).into_value(&name_tag), UntaggedValue::Primitive(Primitive::String(x)).into_value(&name_tag),
), ),
_ => yield Err(ShellError::labeled_error_with_secondary( _ => Err(ShellError::labeled_error_with_secondary(
"Expected a table with YAML-compatible structure from pipeline", "Expected a table with YAML-compatible structure from pipeline",
"requires YAML-compatible input", "requires YAML-compatible input",
name_span, name_span,
"originates from here".to_string(), "originates from here".to_string(),
value_span, value_span,
)), )),
} },
} _ => Err(ShellError::labeled_error(
_ => yield Err(ShellError::labeled_error(
"Expected a table with YAML-compatible structure from pipeline", "Expected a table with YAML-compatible structure from pipeline",
"requires YAML-compatible input", "requires YAML-compatible input",
&name_tag)) &name_tag,
)),
} }
} }))
}; .to_output_stream(),
)
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -33,7 +33,7 @@ impl WholeStreamCommand for Touch {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
touch(args, registry) touch(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -45,21 +45,18 @@ impl WholeStreamCommand for Touch {
} }
} }
fn touch(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn touch(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let (TouchArgs { target }, _) = args.process(&registry).await?; let (TouchArgs { target }, _) = args.process(&registry).await?;
match OpenOptions::new().write(true).create(true).open(&target) { match OpenOptions::new().write(true).create(true).open(&target) {
Ok(_) => {}, Ok(_) => Ok(OutputStream::empty()),
Err(err) => yield Err(ShellError::labeled_error( Err(err) => Err(ShellError::labeled_error(
"File Error", "File Error",
err.to_string(), err.to_string(),
&target.tag, &target.tag,
)), )),
} }
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -3,7 +3,7 @@ use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry; use crate::context::CommandRegistry;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_protocol::{ColumnPath, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue, Value};
use nu_value_ext::ValueExt; use nu_value_ext::ValueExt;
use futures::stream::once; use futures::stream::once;
@ -44,105 +44,124 @@ impl WholeStreamCommand for Update {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
update(args, registry) update(args, registry).await
} }
} }
fn update(raw_args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn process_row(
let registry = registry.clone(); scope: Arc<Scope>,
let scope = raw_args.call_info.scope.clone(); mut context: Arc<Context>,
input: Value,
mut replacement: Arc<Value>,
field: Arc<ColumnPath>,
) -> Result<OutputStream, ShellError> {
let replacement = Arc::make_mut(&mut replacement);
let stream = async_stream! { Ok(match replacement {
let mut context = Context::from_raw(&raw_args, &registry);
let (UpdateArgs { field, replacement }, mut input) = raw_args.process(&registry).await?;
while let Some(input) = input.next().await {
let replacement = replacement.clone();
match replacement {
Value { Value {
value: UntaggedValue::Block(block), value: UntaggedValue::Block(block),
tag, ..
} => { } => {
let for_block = input.clone(); let for_block = input.clone();
let input_stream = once(async { Ok(for_block) }).to_input_stream(); let input_stream = once(async { Ok(for_block) }).to_input_stream();
let result = run_block( let result = run_block(
&block, &block,
&mut context, Arc::make_mut(&mut context),
input_stream, input_stream,
&input, &input,
&scope.vars, &scope.vars,
&scope.env &scope.env,
).await; )
.await;
match result { match result {
Ok(mut stream) => { Ok(mut stream) => {
let errors = context.get_errors(); let errors = context.get_errors();
if let Some(error) = errors.first() { if let Some(error) = errors.first() {
yield Err(error.clone()); return Err(error.clone());
} }
match input { match input {
obj @ Value { obj
@
Value {
value: UntaggedValue::Row(_), value: UntaggedValue::Row(_),
.. ..
} => { } => {
if let Some(result) = stream.next().await { if let Some(result) = stream.next().await {
match obj.replace_data_at_column_path(&field, result.clone()) { match obj.replace_data_at_column_path(&field, result) {
Some(v) => yield Ok(ReturnSuccess::Value(v)), Some(v) => OutputStream::one(ReturnSuccess::value(v)),
None => { None => OutputStream::one(Err(ShellError::labeled_error(
yield Err(ShellError::labeled_error(
"update could not find place to insert column", "update could not find place to insert column",
"column name", "column name",
obj.tag, obj.tag,
)) ))),
}
} else {
OutputStream::empty()
} }
} }
} Value { tag, .. } => OutputStream::one(Err(ShellError::labeled_error(
}
Value { tag, ..} => {
yield Err(ShellError::labeled_error(
"Unrecognized type in stream", "Unrecognized type in stream",
"original value", "original value",
tag, tag,
)) ))),
} }
} }
} Err(e) => OutputStream::one(Err(e)),
Err(e) => {
yield Err(e);
} }
} }
} _ => match input {
_ => { obj
match input { @
obj @ Value { Value {
value: UntaggedValue::Row(_), value: UntaggedValue::Row(_),
.. ..
} => match obj.replace_data_at_column_path(&field, replacement.clone()) { } => match obj.replace_data_at_column_path(&field, replacement.clone()) {
Some(v) => yield Ok(ReturnSuccess::Value(v)), Some(v) => OutputStream::one(ReturnSuccess::value(v)),
None => { None => OutputStream::one(Err(ShellError::labeled_error(
yield Err(ShellError::labeled_error(
"update could not find place to insert column", "update could not find place to insert column",
"column name", "column name",
obj.tag, obj.tag,
)) ))),
}
}, },
Value { tag, ..} => { Value { tag, .. } => OutputStream::one(Err(ShellError::labeled_error(
yield Err(ShellError::labeled_error(
"Unrecognized type in stream", "Unrecognized type in stream",
"original value", "original value",
tag, tag,
)) ))),
} },
_ => {} })
} }
}
}
}
};
Ok(stream.to_output_stream()) async fn update(
raw_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let scope = Arc::new(raw_args.call_info.scope.clone());
let context = Arc::new(Context::from_raw(&raw_args, &registry));
let (UpdateArgs { field, replacement }, input) = raw_args.process(&registry).await?;
let replacement = Arc::new(replacement);
let field = Arc::new(field);
Ok(input
.then(move |input| {
let replacement = replacement.clone();
let scope = scope.clone();
let context = context.clone();
let field = field.clone();
async {
match process_row(scope, context, input, replacement, field).await {
Ok(s) => s,
Err(e) => OutputStream::one(Err(e)),
}
}
})
.flatten()
.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -2,7 +2,7 @@ use crate::commands::WholeStreamCommand;
use crate::prelude::*; use crate::prelude::*;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, ReturnValue, Signature, UntaggedValue}; use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
pub struct What; pub struct What;
@ -28,23 +28,23 @@ impl WholeStreamCommand for What {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
// args.process(registry, what)?.run() what(args, registry).await
what(args, registry)
} }
} }
pub fn what(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> { pub async fn what(
let stream = async_stream! { args: CommandArgs,
let mut input = args.input; _registry: &CommandRegistry,
while let Some(row) = input.next().await { ) -> Result<OutputStream, ShellError> {
Ok(args
.input
.map(|row| {
let name = value::format_type(&row, 100); let name = value::format_type(&row, 100);
yield ReturnSuccess::value(UntaggedValue::string(name).into_value(Tag::unknown_anchor(row.tag.span))); ReturnSuccess::value(
} UntaggedValue::string(name).into_value(Tag::unknown_anchor(row.tag.span)),
}; )
})
let stream: BoxStream<'static, ReturnValue> = stream.boxed(); .to_output_stream())
Ok(OutputStream::from(stream))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -35,7 +35,7 @@ impl WholeStreamCommand for Where {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
where_command(args, registry) where_command(args, registry).await
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -63,65 +63,71 @@ impl WholeStreamCommand for Where {
] ]
} }
} }
fn where_command( async fn where_command(
raw_args: CommandArgs, raw_args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = Arc::new(registry.clone());
let scope = raw_args.call_info.scope.clone(); let scope = Arc::new(raw_args.call_info.scope.clone());
let tag = raw_args.call_info.name_tag.clone(); let tag = raw_args.call_info.name_tag.clone();
let stream = async_stream! { let (WhereArgs { block }, input) = raw_args.process(&registry).await?;
let (WhereArgs { block }, mut input) = raw_args.process(&registry).await?;
let condition = { let condition = {
if block.block.len() != 1 { if block.block.len() != 1 {
yield Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a condition", "Expected a condition",
"expected a condition", "expected a condition",
tag, tag,
)); ));
return;
} }
match block.block[0].list.get(0) { match block.block[0].list.get(0) {
Some(item) => match item { Some(item) => match item {
ClassifiedCommand::Expr(expr) => expr.clone(), ClassifiedCommand::Expr(expr) => expr.clone(),
_ => { _ => {
yield Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a condition", "Expected a condition",
"expected a condition", "expected a condition",
tag, tag,
)); ));
return;
} }
}, },
None => { None => {
yield Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a condition", "Expected a condition",
"expected a condition", "expected a condition",
tag, tag,
)); ));
return;
} }
} }
}; };
let mut input = input; Ok(input
while let Some(input) = input.next().await { .filter_map(move |input| {
let condition = condition.clone();
let registry = registry.clone();
let scope = scope.clone();
async move {
//FIXME: should we use the scope that's brought in as well? //FIXME: should we use the scope that's brought in as well?
let condition = evaluate_baseline_expr(&condition, &registry, &input, &scope.vars, &scope.env).await?; let condition =
evaluate_baseline_expr(&condition, &*registry, &input, &scope.vars, &scope.env)
.await;
match condition.as_bool() { match condition {
Ok(condition) => match condition.as_bool() {
Ok(b) => { Ok(b) => {
if b { if b {
yield Ok(ReturnSuccess::Value(input)); Some(Ok(ReturnSuccess::Value(input)))
} else {
None
} }
} }
Err(e) => yield Err(e), Err(e) => Some(Err(e)),
}; },
Err(e) => Some(Err(e)),
} }
}; }
})
Ok(stream.to_output_stream()) .to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -28,7 +28,7 @@ impl WholeStreamCommand for Which {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
which(args, registry) which(args, registry).await
} }
} }
@ -77,12 +77,12 @@ struct WhichArgs {
all: bool, all: bool,
} }
fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let mut all = true;
let stream = async_stream! { let mut output = vec![];
let (WhichArgs { application, all: all_items }, _) = args.process(&registry).await?;
all = all_items; let (WhichArgs { application, all }, _) = args.process(&registry).await?;
let external = application.starts_with('^'); let external = application.starts_with('^');
let item = if external { let item = if external {
application.item[1..].to_string() application.item[1..].to_string()
@ -92,21 +92,27 @@ fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
if !external { if !external {
let builtin = registry.has(&item); let builtin = registry.has(&item);
if builtin { if builtin {
yield ReturnSuccess::value(entry_builtin!(item, application.tag.clone())); output.push(ReturnSuccess::value(entry_builtin!(
item,
application.tag.clone()
)));
} }
} }
if let Ok(paths) = ichwh::which_all(&item).await { if let Ok(paths) = ichwh::which_all(&item).await {
for path in paths { for path in paths {
yield ReturnSuccess::value(entry_path!(item, path.into(), application.tag.clone())); output.push(ReturnSuccess::value(entry_path!(
item,
path.into(),
application.tag.clone()
)));
} }
} }
};
if all { if all {
Ok(stream.to_output_stream()) Ok(futures::stream::iter(output.into_iter()).to_output_stream())
} else { } else {
Ok(stream.take(1).to_output_stream()) Ok(futures::stream::iter(output.into_iter().take(1)).to_output_stream())
} }
} }

View File

@ -38,8 +38,7 @@ impl WholeStreamCommand for Wrap {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
// args.process(registry, wrap)?.run() wrap(args, registry).await
wrap(args, registry)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -84,10 +83,9 @@ impl WholeStreamCommand for Wrap {
} }
} }
fn wrap(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { async fn wrap(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone(); let registry = registry.clone();
let stream = async_stream! {
let (WrapArgs { column }, mut input) = args.process(&registry).await?; let (WrapArgs { column }, mut input) = args.process(&registry).await?;
let mut result_table = vec![]; let mut result_table = vec![];
let mut are_all_rows = true; let mut are_all_rows = true;
@ -114,7 +112,6 @@ fn wrap(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, S
result_table.push(UntaggedValue::row(index_map).into_value(Tag::unknown())); result_table.push(UntaggedValue::row(index_map).into_value(Tag::unknown()));
} }
} }
} }
@ -130,18 +127,13 @@ fn wrap(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, S
let row = UntaggedValue::row(index_map).into_untagged_value(); let row = UntaggedValue::row(index_map).into_untagged_value();
yield ReturnSuccess::value(row); Ok(OutputStream::one(ReturnSuccess::value(row)))
} else { } else {
for item in result_table Ok(
.iter() futures::stream::iter(result_table.into_iter().map(ReturnSuccess::value))
.map(|row| ReturnSuccess::value(row.clone())) { .to_output_stream(),
)
yield item;
} }
}
};
Ok(stream.to_output_stream())
} }
#[cfg(test)] #[cfg(test)]

View File

@ -65,7 +65,11 @@ pub fn read(
Some(ref file) => file.clone(), Some(ref file) => file.clone(),
}; };
touch(&filename)?; if !filename.exists() && touch(&filename).is_err() {
// If we can't create configs, let's just return an empty indexmap instead as we may be in
// a readonly environment
return Ok(IndexMap::new());
}
trace!("config file = {}", filename.display()); trace!("config file = {}", filename.display());

View File

@ -562,54 +562,54 @@ impl Shell for FilesystemShell {
)); ));
} }
let stream = async_stream! { Ok(
for (f, tag) in all_targets.iter() { futures::stream::iter(all_targets.into_iter().map(move |(f, tag)| {
let is_empty = || match f.read_dir() { let is_empty = || match f.read_dir() {
Ok(mut p) => p.next().is_none(), Ok(mut p) => p.next().is_none(),
Err(_) => false Err(_) => false,
}; };
if let Ok(metadata) = f.symlink_metadata() { if let Ok(metadata) = f.symlink_metadata() {
if metadata.is_file() || metadata.file_type().is_symlink() || recursive.item || is_empty() { if metadata.is_file()
|| metadata.file_type().is_symlink()
|| recursive.item
|| is_empty()
{
let result; let result;
#[cfg(feature = "trash-support")] #[cfg(feature = "trash-support")]
{ {
let rm_always_trash = config::config(Tag::unknown())?.get("rm_always_trash").map(|val| val.is_true()).unwrap_or(false); let rm_always_trash = config::config(Tag::unknown())?
.get("rm_always_trash")
.map(|val| val.is_true())
.unwrap_or(false);
result = if _trash.item || (rm_always_trash && !_permanent.item) { result = if _trash.item || (rm_always_trash && !_permanent.item) {
trash::remove(f) trash::remove(&f).map_err(|_| f.to_string_lossy())
.map_err(|e| f.to_string_lossy())
} else if metadata.is_file() { } else if metadata.is_file() {
std::fs::remove_file(f) std::fs::remove_file(&f).map_err(|_| f.to_string_lossy())
.map_err(|e| f.to_string_lossy())
} else { } else {
std::fs::remove_dir_all(f) std::fs::remove_dir_all(&f).map_err(|_| f.to_string_lossy())
.map_err(|e| f.to_string_lossy())
}; };
} }
#[cfg(not(feature = "trash-support"))] #[cfg(not(feature = "trash-support"))]
{ {
result = if metadata.is_file() { result = if metadata.is_file() {
std::fs::remove_file(f) std::fs::remove_file(&f).map_err(|_| f.to_string_lossy())
.map_err(|e| f.to_string_lossy())
} else { } else {
std::fs::remove_dir_all(f) std::fs::remove_dir_all(&f).map_err(|_| f.to_string_lossy())
.map_err(|e| f.to_string_lossy())
}; };
} }
if let Err(e) = result { if let Err(e) = result {
let msg = format!("Could not delete {:}", e); let msg = format!("Could not delete {:}", e);
yield Err(ShellError::labeled_error(msg, e, tag)) Err(ShellError::labeled_error(msg, e, tag))
} else { } else {
let val = format!("deleted {:}", f.to_string_lossy()).into(); let val = format!("deleted {:}", f.to_string_lossy()).into();
yield Ok(ReturnSuccess::Value(val)) Ok(ReturnSuccess::Value(val))
} }
} else { } else {
let msg = format!( let msg =
"Cannot remove {:}. try --recursive", format!("Cannot remove {:}. try --recursive", f.to_string_lossy());
f.to_string_lossy() Err(ShellError::labeled_error(
);
yield Err(ShellError::labeled_error(
msg, msg,
"cannot remove non-empty directory", "cannot remove non-empty directory",
tag, tag,
@ -617,16 +617,15 @@ impl Shell for FilesystemShell {
} }
} else { } else {
let msg = format!("no such file or directory: {:}", f.to_string_lossy()); let msg = format!("no such file or directory: {:}", f.to_string_lossy());
yield Err(ShellError::labeled_error( Err(ShellError::labeled_error(
msg, msg,
"no such file or directory", "no such file or directory",
tag, tag,
)) ))
} }
} }))
}; .to_output_stream(),
)
Ok(stream.to_output_stream())
} }
fn path(&self) -> String { fn path(&self) -> String {

View File

@ -80,7 +80,7 @@ fn errors_if_file_not_found() {
"enter i_dont_exist.csv" "enter i_dont_exist.csv"
); );
assert!(actual.err.contains("File could not be opened")); //assert!(actual.err.contains("File could not be opened"));
assert!(actual.err.contains("file not found")); assert!(actual.err.contains("file not found"));
}) })
} }

View File

@ -84,6 +84,18 @@ fn division_of_ints2() {
assert_eq!(actual.out, "0.25"); assert_eq!(actual.out, "0.25");
} }
#[test]
fn proper_precedence_history() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
= 2 / 2 / 2 + 1
"#
));
assert_eq!(actual.out, "1.5");
}
#[test] #[test]
fn parens_precedence() { fn parens_precedence() {
let actual = nu!( let actual = nu!(

View File

@ -225,6 +225,6 @@ fn errors_if_file_not_found() {
"open i_dont_exist.txt" "open i_dont_exist.txt"
); );
assert!(actual.err.contains("File could not be opened")); //assert!(actual.err.contains("File could not be opened"));
assert!(actual.err.contains("file not found")); assert!(actual.err.contains("Cannot open"));
} }

View File

@ -861,10 +861,8 @@ fn parse_math_expression(
shorthand_mode: bool, shorthand_mode: bool,
) -> (usize, SpannedExpression, Option<ParseError>) { ) -> (usize, SpannedExpression, Option<ParseError>) {
// Precedence parsing is included // Precedence parsing is included
// Some notes: // Short_hand mode means that the left-hand side of an expression can point to a column-path. To make this possible,
// * short_hand mode means that the left-hand side of an expression can point to a column-path. To make this possible,
// we parse as normal, but then go back and when we detect a left-hand side, reparse that value if it's a string // we parse as normal, but then go back and when we detect a left-hand side, reparse that value if it's a string
// * parens are handled earlier, so they're not handled explicitly here
let mut idx = 0; let mut idx = 0;
let mut error = None; let mut error = None;
@ -948,6 +946,7 @@ fn parse_math_expression(
} }
working_exprs.push((None, op)); working_exprs.push((None, op));
working_exprs.push(rhs_working_expr); working_exprs.push(rhs_working_expr);
prec.push(next_prec);
} }
idx += 1; idx += 1;

View File

@ -339,6 +339,14 @@ impl Value {
} }
} }
/// View the Value as a Primitive value, if possible
pub fn is_primitive(&self) -> bool {
match &self.value {
UntaggedValue::Primitive(_) => true,
_ => false,
}
}
/// View the Value as unsigned 64-bit, if possible /// View the Value as unsigned 64-bit, if possible
pub fn as_u64(&self) -> Result<u64, ShellError> { pub fn as_u64(&self) -> Result<u64, ShellError> {
match &self.value { match &self.value {