mirror of
https://github.com/nushell/nushell.git
synced 2025-01-23 06:39:17 +01:00
Tighten how input streams handle nothing, and related (#2847)
This commit is contained in:
parent
a5f7600f6f
commit
77f915befe
@ -2,9 +2,7 @@ use crate::commands::classified::block::run_block;
|
|||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{
|
use nu_protocol::{hir::CapturedBlock, hir::ExternalRedirection, Signature, SyntaxShape, Value};
|
||||||
hir::CapturedBlock, hir::ExternalRedirection, ReturnSuccess, Signature, SyntaxShape, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Do;
|
pub struct Do;
|
||||||
|
|
||||||
@ -48,7 +46,7 @@ impl WholeStreamCommand for Do {
|
|||||||
Example {
|
Example {
|
||||||
description: "Run the block and ignore errors",
|
description: "Run the block and ignore errors",
|
||||||
example: r#"do -i { thisisnotarealcommand }"#,
|
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();
|
context.clear_errors();
|
||||||
Ok(futures::stream::iter(output).to_output_stream())
|
Ok(futures::stream::iter(output).to_output_stream())
|
||||||
}
|
}
|
||||||
Err(_) => Ok(OutputStream::one(ReturnSuccess::value(Value::nothing()))),
|
Err(_) => Ok(OutputStream::empty()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result.map(|x| x.to_output_stream())
|
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 name = args.call_info.name_tag.span;
|
||||||
let (SubCommandArgs { expression }, input) = args.process().await?;
|
let (SubCommandArgs { expression }, input) = args.process().await?;
|
||||||
|
|
||||||
Ok(input
|
if let Some(string) = expression {
|
||||||
.map(move |x| {
|
match parse(&string, &string.tag) {
|
||||||
if let Some(Tagged {
|
Ok(value) => Ok(OutputStream::one(ReturnSuccess::value(value))),
|
||||||
tag,
|
Err(err) => Err(ShellError::labeled_error(
|
||||||
item: expression,
|
"Math evaluation error",
|
||||||
}) = &expression
|
err,
|
||||||
{
|
&string.tag.span,
|
||||||
UntaggedValue::string(expression).into_value(tag)
|
)),
|
||||||
} else {
|
}
|
||||||
x
|
} else {
|
||||||
}
|
Ok(input
|
||||||
})
|
.map(move |x| {
|
||||||
.map(move |input| {
|
if let Some(Tagged {
|
||||||
if let Ok(string) = input.as_string() {
|
tag,
|
||||||
match parse(&string, &input.tag) {
|
item: expression,
|
||||||
Ok(value) => ReturnSuccess::value(value),
|
}) = &expression
|
||||||
Err(err) => Err(ShellError::labeled_error(
|
{
|
||||||
"Math evaluation error",
|
UntaggedValue::string(expression).into_value(tag)
|
||||||
err,
|
} else {
|
||||||
&input.tag.span,
|
x
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
} else {
|
})
|
||||||
Err(ShellError::labeled_error(
|
.map(move |input| {
|
||||||
"Expected a string from pipeline",
|
if let Ok(string) = input.as_string() {
|
||||||
"requires string input",
|
match parse(&string, &input.tag) {
|
||||||
name,
|
Ok(value) => ReturnSuccess::value(value),
|
||||||
))
|
Err(err) => Err(ShellError::labeled_error(
|
||||||
}
|
"Math evaluation error",
|
||||||
})
|
err,
|
||||||
.to_output_stream())
|
&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> {
|
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 sum = reducer_for(Reduce::Summation);
|
||||||
|
|
||||||
let first = values.get(0).ok_or_else(|| {
|
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 {
|
match first {
|
||||||
|
@ -6,7 +6,7 @@ use crate::{CommandArgs, Example, OutputStream};
|
|||||||
use futures::stream::once;
|
use futures::stream::once;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_parser::ParserScope;
|
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;
|
use nu_source::Tagged;
|
||||||
|
|
||||||
pub struct Reduce;
|
pub struct Reduce;
|
||||||
@ -93,22 +93,25 @@ async fn process_row(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn reduce(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
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 context = Arc::new(EvaluationContext::from_raw(&raw_args));
|
||||||
let (reduce_args, mut input): (ReduceArgs, _) = raw_args.process().await?;
|
let (reduce_args, mut input): (ReduceArgs, _) = raw_args.process().await?;
|
||||||
let block = Arc::new(reduce_args.block);
|
let block = Arc::new(reduce_args.block);
|
||||||
let (ioffset, start) = match reduce_args.fold {
|
let (ioffset, start) = if !input.is_empty() {
|
||||||
None => {
|
match reduce_args.fold {
|
||||||
let first = input
|
None => {
|
||||||
.next()
|
let first = input.next().await.expect("non-empty stream");
|
||||||
.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"));
|
|
||||||
}
|
|
||||||
|
|
||||||
(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 {
|
if reduce_args.numbered.item {
|
||||||
|
@ -49,12 +49,39 @@ pub async fn collect(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
|||||||
|
|
||||||
let strings: Vec<Result<String, ShellError>> =
|
let strings: Vec<Result<String, ShellError>> =
|
||||||
input.map(|value| value.as_string()).collect().await;
|
input.map(|value| value.as_string()).collect().await;
|
||||||
let strings: Vec<String> = strings.into_iter().collect::<Result<_, _>>()?;
|
let strings: Result<Vec<_>, _> = strings.into_iter().collect::<Result<_, _>>();
|
||||||
let output = strings.join(&separator);
|
|
||||||
|
|
||||||
Ok(OutputStream::one(ReturnSuccess::value(
|
match strings {
|
||||||
UntaggedValue::string(output).into_value(tag),
|
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)]
|
#[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]
|
#[test]
|
||||||
#[allow(clippy::unreadable_literal)]
|
#[allow(clippy::unreadable_literal)]
|
||||||
#[allow(clippy::float_cmp)]
|
#[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 crate::prelude::*;
|
||||||
use futures::stream::{iter, once};
|
use futures::stream::iter;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Primitive, Type, UntaggedValue, Value};
|
use nu_protocol::{Primitive, Type, UntaggedValue, Value};
|
||||||
use nu_source::{PrettyDebug, Tag, Tagged, TaggedItem};
|
use nu_source::{PrettyDebug, Tag, Tagged, TaggedItem};
|
||||||
@ -14,7 +14,7 @@ pub struct InputStream {
|
|||||||
impl InputStream {
|
impl InputStream {
|
||||||
pub fn empty() -> InputStream {
|
pub fn empty() -> InputStream {
|
||||||
InputStream {
|
InputStream {
|
||||||
values: once(async { UntaggedValue::nothing().into_untagged_value() }).boxed(),
|
values: futures::stream::empty().boxed(),
|
||||||
empty: true,
|
empty: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user