From 4e2a4236f8700a8ddbe17bb2d21b7a274bb42889 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sat, 27 Jun 2020 17:38:19 +1200 Subject: [PATCH] Fix it expansion and add collect (#2065) --- crates/nu-cli/src/cli.rs | 5 +- crates/nu-cli/src/commands.rs | 4 +- crates/nu-cli/src/commands/str_/collect.rs | 56 ++++++++++++++++ crates/nu-cli/src/commands/str_/mod.rs | 2 + crates/nu-cli/src/stream/input.rs | 12 +++- crates/nu-protocol/src/hir.rs | 76 ++++++++++++++-------- tests/shell/pipeline/commands/internal.rs | 12 ++++ 7 files changed, 133 insertions(+), 34 deletions(-) create mode 100644 crates/nu-cli/src/commands/str_/collect.rs diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index 18a8b0d3e..59e040de7 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -303,6 +303,7 @@ pub fn create_default_context( whole_stream_command(StrSet), whole_stream_command(StrToDatetime), whole_stream_command(StrTrim), + whole_stream_command(StrCollect), whole_stream_command(BuildString), whole_stream_command(Ansi), // Column manipulation @@ -657,10 +658,12 @@ pub async fn cli( if let Ok(result) = nu_parser::lite_parse(&prompt_line, 0).map_err(ShellError::from) { - let prompt_block = nu_parser::classify_block(&result, context.registry()); + let mut prompt_block = nu_parser::classify_block(&result, context.registry()); let env = context.get_env(); + prompt_block.block.expand_it_usage(); + match run_block( &prompt_block.block, &mut context, diff --git a/crates/nu-cli/src/commands.rs b/crates/nu-cli/src/commands.rs index 16162c1fe..6cc89c0ff 100644 --- a/crates/nu-cli/src/commands.rs +++ b/crates/nu-cli/src/commands.rs @@ -240,8 +240,8 @@ pub(crate) use split::SplitColumn; pub(crate) use split::SplitRow; pub(crate) use split_by::SplitBy; pub(crate) use str_::{ - Str, StrCapitalize, StrDowncase, StrFindReplace, StrSet, StrSubstring, StrToDatetime, - StrToDecimal, StrToInteger, StrTrim, StrUpcase, + Str, StrCapitalize, StrCollect, StrDowncase, StrFindReplace, StrSet, StrSubstring, + StrToDatetime, StrToDecimal, StrToInteger, StrTrim, StrUpcase, }; #[allow(unused_imports)] pub(crate) use t_sort_by::TSortBy; diff --git a/crates/nu-cli/src/commands/str_/collect.rs b/crates/nu-cli/src/commands/str_/collect.rs new file mode 100644 index 000000000..091d55260 --- /dev/null +++ b/crates/nu-cli/src/commands/str_/collect.rs @@ -0,0 +1,56 @@ +use crate::commands::WholeStreamCommand; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{ReturnSuccess, Signature, UntaggedValue, Value}; + +pub struct SubCommand; + +#[async_trait] +impl WholeStreamCommand for SubCommand { + fn name(&self) -> &str { + "str collect" + } + + fn signature(&self) -> Signature { + Signature::build("str collect") + } + + fn usage(&self) -> &str { + "collects a list of strings into a string" + } + + async fn run( + &self, + args: CommandArgs, + _registry: &CommandRegistry, + ) -> Result { + let output = args + .input + .collect_string(args.call_info.name_tag.clone()) + .await?; + + Ok(OutputStream::one(ReturnSuccess::value( + UntaggedValue::string(output.item).into_value(output.tag), + ))) + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Collect a list of string", + example: "echo ['a' 'b' 'c'] | str collect", + result: Some(vec![Value::from("abc")]), + }] + } +} + +#[cfg(test)] +mod tests { + use super::SubCommand; + + #[test] + fn examples_work_as_expected() { + use crate::examples::test as test_examples; + + test_examples(SubCommand {}) + } +} diff --git a/crates/nu-cli/src/commands/str_/mod.rs b/crates/nu-cli/src/commands/str_/mod.rs index 66f7c61db..3c1212e62 100644 --- a/crates/nu-cli/src/commands/str_/mod.rs +++ b/crates/nu-cli/src/commands/str_/mod.rs @@ -1,4 +1,5 @@ mod capitalize; +mod collect; mod command; mod downcase; mod find_replace; @@ -11,6 +12,7 @@ mod trim; mod upcase; pub use capitalize::SubCommand as StrCapitalize; +pub use collect::SubCommand as StrCollect; pub use command::Command as Str; pub use downcase::SubCommand as StrDowncase; pub use find_replace::SubCommand as StrFindReplace; diff --git a/crates/nu-cli/src/stream/input.rs b/crates/nu-cli/src/stream/input.rs index ade98828f..385992ac0 100644 --- a/crates/nu-cli/src/stream/input.rs +++ b/crates/nu-cli/src/stream/input.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use futures::stream::{iter, once}; use nu_errors::ShellError; -use nu_protocol::{Primitive, UntaggedValue, Value}; +use nu_protocol::{Primitive, Type, UntaggedValue, Value}; use nu_source::{Tagged, TaggedItem}; pub struct InputStream { @@ -74,12 +74,18 @@ impl InputStream { value_tag = value_t; bytes.extend_from_slice(&b); } - Some(Value { tag: value_tag, .. }) => { + Some(Value { + tag: value_tag, + value, + }) => { return Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", tag, - "value originates from here", + format!( + "{} originates from here", + Type::from_value(&value).plain_string(100000) + ), value_tag, )) } diff --git a/crates/nu-protocol/src/hir.rs b/crates/nu-protocol/src/hir.rs index f831f23d9..404c7b018 100644 --- a/crates/nu-protocol/src/hir.rs +++ b/crates/nu-protocol/src/hir.rs @@ -44,6 +44,14 @@ impl InternalCommand { ), } } + + pub fn expand_it_usage(&mut self) { + if let Some(positionals) = &mut self.args.positional { + for arg in positionals { + arg.expand_it_usage(); + } + } + } } #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] @@ -109,34 +117,8 @@ impl ClassifiedCommand { pub fn expand_it_usage(&mut self) { match self { - ClassifiedCommand::Internal(command) => { - if let Some(positionals) = &mut command.args.positional { - for arg in positionals { - if let SpannedExpression { - expr: Expression::Block(block), - .. - } = arg - { - block.expand_it_usage(); - } - } - } - } - ClassifiedCommand::Expr(expr) => { - if let SpannedExpression { - expr: Expression::Block(ref block), - span, - } = **expr - { - let mut block = block.clone(); - block.expand_it_usage(); - *expr = Box::new(SpannedExpression { - expr: Expression::Block(block), - span, - }); - } - } - + ClassifiedCommand::Internal(command) => command.expand_it_usage(), + ClassifiedCommand::Expr(expr) => expr.expand_it_usage(), _ => {} } } @@ -588,6 +570,44 @@ impl SpannedExpression { _ => false, } } + + pub fn expand_it_usage(&mut self) { + match self { + SpannedExpression { + expr: Expression::Block(block), + .. + } => { + block.expand_it_usage(); + } + SpannedExpression { + expr: Expression::Invocation(block), + .. + } => { + block.expand_it_usage(); + } + SpannedExpression { + expr: Expression::List(list), + .. + } => { + for item in list.iter_mut() { + item.expand_it_usage(); + } + } + SpannedExpression { + expr: Expression::Path(path), + .. + } => { + if let SpannedExpression { + expr: Expression::Invocation(block), + .. + } = &mut path.head + { + block.expand_it_usage(); + } + } + _ => {} + } + } } impl std::ops::Deref for SpannedExpression { diff --git a/tests/shell/pipeline/commands/internal.rs b/tests/shell/pipeline/commands/internal.rs index d30718cf6..69e58caeb 100644 --- a/tests/shell/pipeline/commands/internal.rs +++ b/tests/shell/pipeline/commands/internal.rs @@ -72,6 +72,18 @@ fn it_expansion_of_list() { assert_eq!(actual.out, "[\"bar\",\"foo\"]"); } +#[test] +fn it_expansion_of_invocation() { + let actual = nu!( + cwd: ".", + r#" + echo $(echo "4" | echo $it | str to-int ) + "# + ); + + assert_eq!(actual.out, "4"); +} + #[test] fn argument_invocation() { let actual = nu!(