mirror of
https://github.com/nushell/nushell
synced 2025-01-14 14:14:13 +00:00
Tighten how input streams handle nothing, and related (#2847)
This commit is contained in:
parent
a5f7600f6f
commit
77f915befe
8 changed files with 99 additions and 63 deletions
|
@ -2,9 +2,7 @@ use crate::commands::classified::block::run_block;
|
|||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, hir::ExternalRedirection, ReturnSuccess, Signature, SyntaxShape, Value,
|
||||
};
|
||||
use nu_protocol::{hir::CapturedBlock, hir::ExternalRedirection, Signature, SyntaxShape, Value};
|
||||
|
||||
pub struct Do;
|
||||
|
||||
|
@ -48,7 +46,7 @@ impl WholeStreamCommand for Do {
|
|||
Example {
|
||||
description: "Run the block and ignore errors",
|
||||
example: r#"do -i { thisisnotarealcommand }"#,
|
||||
result: Some(vec![Value::nothing()]),
|
||||
result: Some(vec![]),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -98,7 +96,7 @@ async fn do_(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
|||
context.clear_errors();
|
||||
Ok(futures::stream::iter(output).to_output_stream())
|
||||
}
|
||||
Err(_) => Ok(OutputStream::one(ReturnSuccess::value(Value::nothing()))),
|
||||
Err(_) => Ok(OutputStream::empty()),
|
||||
}
|
||||
} else {
|
||||
result.map(|x| x.to_output_stream())
|
||||
|
|
|
@ -50,37 +50,48 @@ pub async fn eval(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
|||
let name = args.call_info.name_tag.span;
|
||||
let (SubCommandArgs { expression }, input) = args.process().await?;
|
||||
|
||||
Ok(input
|
||||
.map(move |x| {
|
||||
if let Some(Tagged {
|
||||
tag,
|
||||
item: expression,
|
||||
}) = &expression
|
||||
{
|
||||
UntaggedValue::string(expression).into_value(tag)
|
||||
} else {
|
||||
x
|
||||
}
|
||||
})
|
||||
.map(move |input| {
|
||||
if let Ok(string) = input.as_string() {
|
||||
match parse(&string, &input.tag) {
|
||||
Ok(value) => ReturnSuccess::value(value),
|
||||
Err(err) => Err(ShellError::labeled_error(
|
||||
"Math evaluation error",
|
||||
err,
|
||||
&input.tag.span,
|
||||
)),
|
||||
if let Some(string) = expression {
|
||||
match parse(&string, &string.tag) {
|
||||
Ok(value) => Ok(OutputStream::one(ReturnSuccess::value(value))),
|
||||
Err(err) => Err(ShellError::labeled_error(
|
||||
"Math evaluation error",
|
||||
err,
|
||||
&string.tag.span,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Ok(input
|
||||
.map(move |x| {
|
||||
if let Some(Tagged {
|
||||
tag,
|
||||
item: expression,
|
||||
}) = &expression
|
||||
{
|
||||
UntaggedValue::string(expression).into_value(tag)
|
||||
} else {
|
||||
x
|
||||
}
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
name,
|
||||
))
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
})
|
||||
.map(move |input| {
|
||||
if let Ok(string) = input.as_string() {
|
||||
match parse(&string, &input.tag) {
|
||||
Ok(value) => ReturnSuccess::value(value),
|
||||
Err(err) => Err(ShellError::labeled_error(
|
||||
"Math evaluation error",
|
||||
err,
|
||||
&input.tag.span,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
name,
|
||||
))
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse<T: Into<Tag>>(math_expression: &str, tag: T) -> Result<Value, String> {
|
||||
|
|
|
@ -73,7 +73,11 @@ pub fn summation(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
|
|||
let sum = reducer_for(Reduce::Summation);
|
||||
|
||||
let first = values.get(0).ok_or_else(|| {
|
||||
ShellError::unexpected("Cannot perform aggregate math operation on empty data")
|
||||
ShellError::labeled_error(
|
||||
"Cannot perform aggregate math operation on empty data",
|
||||
"expected input",
|
||||
name.span,
|
||||
)
|
||||
})?;
|
||||
|
||||
match first {
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{CommandArgs, Example, OutputStream};
|
|||
use futures::stream::once;
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::ParserScope;
|
||||
use nu_protocol::{hir::CapturedBlock, Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{hir::CapturedBlock, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Reduce;
|
||||
|
@ -93,22 +93,25 @@ async fn process_row(
|
|||
}
|
||||
|
||||
async fn reduce(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let span = raw_args.call_info.name_tag.span;
|
||||
let context = Arc::new(EvaluationContext::from_raw(&raw_args));
|
||||
let (reduce_args, mut input): (ReduceArgs, _) = raw_args.process().await?;
|
||||
let block = Arc::new(reduce_args.block);
|
||||
let (ioffset, start) = match reduce_args.fold {
|
||||
None => {
|
||||
let first = input
|
||||
.next()
|
||||
.await
|
||||
.expect("empty stream expected to contain Primitive::Nothing");
|
||||
if let UntaggedValue::Primitive(Primitive::Nothing) = first.value {
|
||||
return Err(ShellError::missing_value(None, "empty input"));
|
||||
}
|
||||
let (ioffset, start) = if !input.is_empty() {
|
||||
match reduce_args.fold {
|
||||
None => {
|
||||
let first = input.next().await.expect("non-empty stream");
|
||||
|
||||
(1, first)
|
||||
(1, first)
|
||||
}
|
||||
Some(acc) => (0, acc),
|
||||
}
|
||||
Some(acc) => (0, acc),
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected input",
|
||||
"needs input",
|
||||
span,
|
||||
));
|
||||
};
|
||||
|
||||
if reduce_args.numbered.item {
|
||||
|
|
|
@ -49,12 +49,39 @@ pub async fn collect(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
|||
|
||||
let strings: Vec<Result<String, ShellError>> =
|
||||
input.map(|value| value.as_string()).collect().await;
|
||||
let strings: Vec<String> = strings.into_iter().collect::<Result<_, _>>()?;
|
||||
let output = strings.join(&separator);
|
||||
let strings: Result<Vec<_>, _> = strings.into_iter().collect::<Result<_, _>>();
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(tag),
|
||||
)))
|
||||
match strings {
|
||||
Ok(strings) => {
|
||||
let output = strings.join(&separator);
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(tag),
|
||||
)))
|
||||
}
|
||||
Err(err) => match err.error {
|
||||
nu_errors::ProximateShellError::TypeError { actual, .. } => {
|
||||
if let Some(item) = actual.item {
|
||||
Err(ShellError::labeled_error_with_secondary(
|
||||
"could not convert to string",
|
||||
format!("tried to convert '{}' in input to a string", item),
|
||||
tag.span,
|
||||
format!("'{}' value originated here", item),
|
||||
actual.span,
|
||||
))
|
||||
} else {
|
||||
Err(ShellError::labeled_error_with_secondary(
|
||||
"could not convert to string",
|
||||
"failed to convert input to strings",
|
||||
tag.span,
|
||||
"non-string found here",
|
||||
actual.span,
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(err),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -33,13 +33,6 @@ fn all() {
|
|||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn outputs_zero_with_no_input() {
|
||||
let actual = nu!(cwd: ".", "math sum");
|
||||
|
||||
assert_eq!(actual.out, "0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
#[allow(clippy::float_cmp)]
|
||||
|
|
|
@ -103,5 +103,5 @@ fn error_reduce_empty() {
|
|||
)
|
||||
);
|
||||
|
||||
assert!(actual.err.contains("empty input"));
|
||||
assert!(actual.err.contains("needs input"));
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::prelude::*;
|
||||
use futures::stream::{iter, once};
|
||||
use futures::stream::iter;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Type, UntaggedValue, Value};
|
||||
use nu_source::{PrettyDebug, Tag, Tagged, TaggedItem};
|
||||
|
@ -14,7 +14,7 @@ pub struct InputStream {
|
|||
impl InputStream {
|
||||
pub fn empty() -> InputStream {
|
||||
InputStream {
|
||||
values: once(async { UntaggedValue::nothing().into_untagged_value() }).boxed(),
|
||||
values: futures::stream::empty().boxed(),
|
||||
empty: true,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue