From 8bd3cedce1ce82d67dc7c88feb859e508334a3d2 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 27 Apr 2020 14:04:54 +1200 Subject: [PATCH] It expansion (#1668) * First step in it-expansion * Fix tests * fix clippy warnings --- crates/nu-cli/src/cli.rs | 48 ++-- crates/nu-cli/src/commands.rs | 5 +- crates/nu-cli/src/commands/alias.rs | 76 +++--- crates/nu-cli/src/commands/calc.rs | 62 +++-- .../nu-cli/src/commands/classified/block.rs | 1 - .../src/commands/classified/internal.rs | 4 +- crates/nu-cli/src/commands/command.rs | 52 ---- crates/nu-cli/src/commands/cp.rs | 20 +- crates/nu-cli/src/commands/du.rs | 22 +- crates/nu-cli/src/commands/each.rs | 106 ++++---- crates/nu-cli/src/commands/echo.rs | 62 +++-- crates/nu-cli/src/commands/edit.rs | 64 +++-- crates/nu-cli/src/commands/enter.rs | 250 +++++++++--------- crates/nu-cli/src/commands/format.rs | 111 ++++---- crates/nu-cli/src/commands/help.rs | 139 +++++----- crates/nu-cli/src/commands/history.rs | 54 ++-- crates/nu-cli/src/commands/insert.rs | 77 +++--- crates/nu-cli/src/commands/is_empty.rs | 249 +++++++++-------- crates/nu-cli/src/commands/kill.rs | 18 +- crates/nu-cli/src/commands/ls.rs | 20 +- crates/nu-cli/src/commands/mkdir.rs | 20 +- crates/nu-cli/src/commands/mv.rs | 20 +- crates/nu-cli/src/commands/open.rs | 50 ++-- crates/nu-cli/src/commands/parse.rs | 101 ++++--- crates/nu-cli/src/commands/rm.rs | 20 +- crates/nu-cli/src/commands/run_alias.rs | 112 ++++---- crates/nu-cli/src/commands/touch.rs | 25 +- crates/nu-cli/src/commands/where_.rs | 104 ++++---- crates/nu-cli/src/context.rs | 24 ++ crates/nu-cli/src/deserializer.rs | 2 +- crates/nu-cli/src/prelude.rs | 5 +- crates/nu-cli/src/shell/filesystem_shell.rs | 2 +- crates/nu-cli/src/shell/help_shell.rs | 6 +- crates/nu-cli/src/shell/shell.rs | 6 +- crates/nu-cli/src/shell/shell_manager.rs | 16 +- crates/nu-cli/src/shell/value_shell.rs | 2 +- crates/nu-protocol/src/hir.rs | 87 +++++- 37 files changed, 1040 insertions(+), 1002 deletions(-) diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index d02a7b94d4..752dff030c 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -238,28 +238,28 @@ pub fn create_default_context( context.add_commands(vec![ // System/file operations whole_stream_command(Pwd), - per_item_command(Ls), - per_item_command(Du), + whole_stream_command(Ls), + whole_stream_command(Du), whole_stream_command(Cd), - per_item_command(Remove), - per_item_command(Open), + whole_stream_command(Remove), + whole_stream_command(Open), whole_stream_command(Config), - per_item_command(Help), - per_item_command(History), + whole_stream_command(Help), + whole_stream_command(History), whole_stream_command(Save), - per_item_command(Touch), - per_item_command(Cpy), + whole_stream_command(Touch), + whole_stream_command(Cpy), whole_stream_command(Date), - per_item_command(Calc), - per_item_command(Mkdir), - per_item_command(Move), - per_item_command(Kill), + whole_stream_command(Calc), + whole_stream_command(Mkdir), + whole_stream_command(Move), + whole_stream_command(Kill), whole_stream_command(Version), whole_stream_command(Clear), whole_stream_command(What), whole_stream_command(Which), whole_stream_command(Debug), - per_item_command(Alias), + whole_stream_command(Alias), // Statistics whole_stream_command(Size), whole_stream_command(Count), @@ -269,7 +269,7 @@ pub fn create_default_context( whole_stream_command(Next), whole_stream_command(Previous), whole_stream_command(Shells), - per_item_command(Enter), + whole_stream_command(Enter), whole_stream_command(Exit), // Viewers whole_stream_command(Autoview), @@ -279,14 +279,14 @@ pub fn create_default_context( whole_stream_command(SplitRow), whole_stream_command(Lines), whole_stream_command(Trim), - per_item_command(Echo), - per_item_command(Parse), + whole_stream_command(Echo), + whole_stream_command(Parse), // Column manipulation whole_stream_command(Reject), whole_stream_command(Pick), whole_stream_command(Get), - per_item_command(Edit), - per_item_command(Insert), + whole_stream_command(Edit), + whole_stream_command(Insert), whole_stream_command(SplitBy), // Row manipulation whole_stream_command(Reverse), @@ -299,16 +299,16 @@ pub fn create_default_context( whole_stream_command(Skip), whole_stream_command(Nth), whole_stream_command(Drop), - per_item_command(Format), - per_item_command(Where), + whole_stream_command(Format), + whole_stream_command(Where), whole_stream_command(Compact), whole_stream_command(Default), whole_stream_command(SkipWhile), whole_stream_command(Range), whole_stream_command(Rename), whole_stream_command(Uniq), - per_item_command(Each), - per_item_command(IsEmpty), + whole_stream_command(Each), + whole_stream_command(IsEmpty), // Table manipulation whole_stream_command(Shuffle), whole_stream_command(Wrap), @@ -726,7 +726,7 @@ async fn process_line( debug!("=== Parsed ==="); debug!("{:#?}", result); - let classified_block = nu_parser::classify_block(&result, ctx.registry()); + let mut classified_block = nu_parser::classify_block(&result, ctx.registry()); debug!("{:#?}", classified_block); //println!("{:#?}", pipeline); @@ -845,6 +845,8 @@ async fn process_line( InputStream::empty() }; + classified_block.block.expand_it_usage(); + match run_block(&classified_block.block, ctx, input_stream, &Scope::empty()).await { Ok(input) => { // Running a pipeline gives us back a stream that we can then diff --git a/crates/nu-cli/src/commands.rs b/crates/nu-cli/src/commands.rs index 1e54e58e2a..6083ab7ed2 100644 --- a/crates/nu-cli/src/commands.rs +++ b/crates/nu-cli/src/commands.rs @@ -116,10 +116,7 @@ pub(crate) mod wrap; pub(crate) use autoview::Autoview; pub(crate) use cd::Cd; -pub(crate) use command::{ - per_item_command, whole_stream_command, Command, PerItemCommand, UnevaluatedCallInfo, - WholeStreamCommand, -}; +pub(crate) use command::{whole_stream_command, Command, UnevaluatedCallInfo, WholeStreamCommand}; pub(crate) use alias::Alias; pub(crate) use append::Append; diff --git a/crates/nu-cli/src/commands/alias.rs b/crates/nu-cli/src/commands/alias.rs index bfaa1467b0..0bf797f75f 100644 --- a/crates/nu-cli/src/commands/alias.rs +++ b/crates/nu-cli/src/commands/alias.rs @@ -1,14 +1,20 @@ -use crate::commands::PerItemCommand; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{ - CallInfo, CommandAction, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, -}; +use nu_protocol::{hir::Block, CommandAction, ReturnSuccess, Signature, SyntaxShape, Value}; +use nu_source::Tagged; pub struct Alias; -impl PerItemCommand for Alias { +#[derive(Deserialize)] +pub struct AliasArgs { + pub name: Tagged, + pub args: Vec, + pub block: Block, +} + +impl WholeStreamCommand for Alias { fn name(&self) -> &str { "alias" } @@ -26,40 +32,32 @@ impl PerItemCommand for Alias { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - _raw_args: &RawCommandArgs, - _input: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - let call_info = call_info.clone(); - let stream = async_stream! { - match (call_info.args.expect_nth(0)?, call_info.args.expect_nth(1)?, call_info.args.expect_nth(2)?) { - (Value {value: UntaggedValue::Primitive(Primitive::String(name)), .. }, - Value { value: UntaggedValue::Table(list), .. }, - Value { - value: UntaggedValue::Block(block), - tag - }) => { - let mut args: Vec = vec![]; - for item in list.iter() { - if let Ok(string) = item.as_string() { - args.push(format!("${}", string)); - } else { - yield Err(ShellError::labeled_error("Expected a string", "expected a string", item.tag())); - } - } - yield ReturnSuccess::action(CommandAction::AddAlias(name.to_string(), args, block.clone())) - } - _ => { - yield Err(ShellError::labeled_error( - "Expected `name [args] {block}", - "needs a name, args, and a block", - call_info.name_tag, - )) - } - }; - }; - - Ok(stream.to_output_stream()) + args.process(registry, alias)?.run() } } + +pub fn alias( + AliasArgs { + name, + args: list, + block, + }: AliasArgs, + _: RunnableContext, +) -> Result { + let stream = async_stream! { + let mut args: Vec = vec![]; + for item in list.iter() { + if let Ok(string) = item.as_string() { + args.push(format!("${}", string)); + } else { + yield Err(ShellError::labeled_error("Expected a string", "expected a string", item.tag())); + } + } + yield ReturnSuccess::action(CommandAction::AddAlias(name.to_string(), args, block.clone())) + }; + + Ok(stream.to_output_stream()) +} diff --git a/crates/nu-cli/src/commands/calc.rs b/crates/nu-cli/src/commands/calc.rs index cae4a1c87d..2785d10ccf 100644 --- a/crates/nu-cli/src/commands/calc.rs +++ b/crates/nu-cli/src/commands/calc.rs @@ -1,11 +1,14 @@ -use crate::commands::PerItemCommand; +use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, Primitive, ReturnSuccess, UntaggedValue, Value}; +use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value}; pub struct Calc; -impl PerItemCommand for Calc { +#[derive(Deserialize)] +pub struct CalcArgs {} + +impl WholeStreamCommand for Calc { fn name(&self) -> &str { "calc" } @@ -16,36 +19,37 @@ impl PerItemCommand for Calc { fn run( &self, - _call_info: &CallInfo, - _registry: &CommandRegistry, - raw_args: &RawCommandArgs, - input: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - calc(input, raw_args) + args.process(registry, calc)?.run() } } -fn calc(input: Value, args: &RawCommandArgs) -> Result { - let name_span = &args.call_info.name_tag.span; - - let output = if let Ok(string) = input.as_string() { - match parse(&string, &input.tag) { - Ok(value) => ReturnSuccess::value(value), - Err(err) => Err(ShellError::labeled_error( - "Calculation error", - err, - &input.tag.span, - )), - } - } else { - Err(ShellError::labeled_error( - "Expected a string from pipeline", - "requires string input", - name_span, - )) - }; - - Ok(vec![output].into()) +pub fn calc( + _: CalcArgs, + RunnableContext { input, name, .. }: RunnableContext, +) -> Result { + Ok(input + .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( + "Calculation error", + err, + &input.tag.span, + )), + } + } else { + Err(ShellError::labeled_error( + "Expected a string from pipeline", + "requires string input", + name.clone(), + )) + } + }) + .to_output_stream()) } pub fn parse(math_expression: &str, tag: impl Into) -> Result { diff --git a/crates/nu-cli/src/commands/classified/block.rs b/crates/nu-cli/src/commands/classified/block.rs index 8694a7a50d..79a631f444 100644 --- a/crates/nu-cli/src/commands/classified/block.rs +++ b/crates/nu-cli/src/commands/classified/block.rs @@ -1,5 +1,4 @@ use crate::commands::classified::expr::run_expression_block; -//use crate::commands::classified::external::run_external_command; use crate::commands::classified::internal::run_internal_command; use crate::context::Context; use crate::prelude::*; diff --git a/crates/nu-cli/src/commands/classified/internal.rs b/crates/nu-cli/src/commands/classified/internal.rs index cbe8d1ec28..13c302ab81 100644 --- a/crates/nu-cli/src/commands/classified/internal.rs +++ b/crates/nu-cli/src/commands/classified/internal.rs @@ -1,4 +1,4 @@ -use crate::commands::command::per_item_command; +use crate::commands::command::whole_stream_command; use crate::commands::run_alias::AliasCommand; use crate::commands::UnevaluatedCallInfo; use crate::prelude::*; @@ -122,7 +122,7 @@ pub(crate) fn run_internal_command( } CommandAction::AddAlias(name, args, block) => { context.add_commands(vec![ - per_item_command(AliasCommand::new( + whole_stream_command(AliasCommand::new( name, args, block, diff --git a/crates/nu-cli/src/commands/command.rs b/crates/nu-cli/src/commands/command.rs index 4d39db5230..5e0297ea8a 100644 --- a/crates/nu-cli/src/commands/command.rs +++ b/crates/nu-cli/src/commands/command.rs @@ -49,36 +49,6 @@ impl UnevaluatedCallInfo { } } -pub trait CallInfoExt { - fn process<'de, T: Deserialize<'de>>( - &self, - shell_manager: &ShellManager, - ctrl_c: Arc, - callback: fn(T, &RunnablePerItemContext) -> Result, - ) -> Result, ShellError>; -} - -impl CallInfoExt for CallInfo { - fn process<'de, T: Deserialize<'de>>( - &self, - shell_manager: &ShellManager, - ctrl_c: Arc, - callback: fn(T, &RunnablePerItemContext) -> Result, - ) -> Result, ShellError> { - let mut deserializer = ConfigDeserializer::from_call_info(self.clone()); - - Ok(RunnablePerItemArgs { - args: T::deserialize(&mut deserializer)?, - context: RunnablePerItemContext { - shell_manager: shell_manager.clone(), - name: self.name_tag.clone(), - ctrl_c, - }, - callback, - }) - } -} - #[derive(Getters)] #[get = "pub(crate)"] pub struct CommandArgs { @@ -227,12 +197,6 @@ impl CommandArgs { } } -pub struct RunnablePerItemContext { - pub shell_manager: ShellManager, - pub name: Tag, - pub ctrl_c: Arc, -} - pub struct RunnableContext { pub input: InputStream, pub shell_manager: ShellManager, @@ -248,18 +212,6 @@ impl RunnableContext { } } -pub struct RunnablePerItemArgs { - args: T, - context: RunnablePerItemContext, - callback: fn(T, &RunnablePerItemContext) -> Result, -} - -impl RunnablePerItemArgs { - pub fn run(self) -> Result { - (self.callback)(self.args, &self.context) - } -} - pub struct RunnableArgs { args: T, context: RunnableContext, @@ -626,7 +578,3 @@ impl WholeStreamCommand for FnFilterCommand { pub fn whole_stream_command(command: impl WholeStreamCommand + 'static) -> Arc { Arc::new(Command::WholeStream(Arc::new(command))) } - -pub fn per_item_command(command: impl PerItemCommand + 'static) -> Arc { - Arc::new(Command::PerItem(Arc::new(command))) -} diff --git a/crates/nu-cli/src/commands/cp.rs b/crates/nu-cli/src/commands/cp.rs index 1bd04040d4..8524ce6bc5 100644 --- a/crates/nu-cli/src/commands/cp.rs +++ b/crates/nu-cli/src/commands/cp.rs @@ -1,8 +1,8 @@ -use crate::commands::command::RunnablePerItemContext; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; +use nu_protocol::{Signature, SyntaxShape}; use nu_source::Tagged; use std::path::PathBuf; @@ -15,7 +15,7 @@ pub struct CopyArgs { pub recursive: Tagged, } -impl PerItemCommand for Cpy { +impl WholeStreamCommand for Cpy { fn name(&self) -> &str { "cp" } @@ -37,18 +37,14 @@ impl PerItemCommand for Cpy { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - raw_args: &RawCommandArgs, - _input: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - call_info - .process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), cp)? - .run() + args.process(registry, cp)?.run() } } -fn cp(args: CopyArgs, context: &RunnablePerItemContext) -> Result { +pub fn cp(args: CopyArgs, context: RunnableContext) -> Result { let shell_manager = context.shell_manager.clone(); - shell_manager.cp(args, context) + shell_manager.cp(args, &context) } diff --git a/crates/nu-cli/src/commands/du.rs b/crates/nu-cli/src/commands/du.rs index c7ea0d1daf..3a1e254d31 100644 --- a/crates/nu-cli/src/commands/du.rs +++ b/crates/nu-cli/src/commands/du.rs @@ -1,12 +1,10 @@ -extern crate filesize; - -use crate::commands::command::RunnablePerItemContext; +use crate::commands::WholeStreamCommand; use crate::prelude::*; use filesize::file_real_size_fast; use glob::*; use indexmap::map::IndexMap; use nu_errors::ShellError; -use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_source::Tagged; use std::path::PathBuf; @@ -31,7 +29,7 @@ pub struct DuArgs { min_size: Option>, } -impl PerItemCommand for Du { +impl WholeStreamCommand for Du { fn name(&self) -> &str { NAME } @@ -75,18 +73,14 @@ impl PerItemCommand for Du { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - raw_args: &RawCommandArgs, - _input: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - call_info - .process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), du)? - .run() + args.process(registry, du)?.run() } } -fn du(args: DuArgs, ctx: &RunnablePerItemContext) -> Result { +fn du(args: DuArgs, ctx: RunnableContext) -> Result { let tag = ctx.name.clone(); let exclude = args.exclude.map_or(Ok(None), move |x| { @@ -117,7 +111,7 @@ fn du(args: DuArgs, ctx: &RunnablePerItemContext) -> Result &str { "each" } @@ -29,58 +34,51 @@ impl PerItemCommand for Each { fn run( &self, - call_info: &CallInfo, + args: CommandArgs, registry: &CommandRegistry, - raw_args: &RawCommandArgs, - input: Value, ) -> Result { - let call_info = call_info.clone(); - let registry = registry.clone(); - let raw_args = raw_args.clone(); - let stream = async_stream! { - match call_info.args.expect_nth(0)? { - Value { - value: UntaggedValue::Block(block), - tag - } => { - let mut context = Context::from_raw(&raw_args, ®istry); - let input_clone = input.clone(); - let input_stream = once(async { Ok(input) }).to_input_stream(); - - let result = run_block( - block, - &mut context, - input_stream, - &Scope::new(input_clone), - ).await; - - match result { - Ok(mut stream) => { - let errors = context.get_errors(); - if let Some(error) = errors.first() { - yield Err(error.clone()); - return; - } - - while let Some(result) = stream.next().await { - yield Ok(ReturnSuccess::Value(result)); - } - } - Err(e) => { - yield Err(e); - } - } - } - Value { tag, .. } => { - yield Err(ShellError::labeled_error( - "Expected a block", - "each needs a block", - tag, - )) - } - }; - }; - - Ok(stream.to_output_stream()) + Ok(args.process_raw(registry, each)?.run()) } } + +fn each( + each_args: EachArgs, + context: RunnableContext, + raw_args: RawCommandArgs, +) -> Result { + let block = each_args.block; + let registry = context.registry.clone(); + let mut input_stream = context.input; + let stream = async_stream! { + while let Some(input) = input_stream.next().await { + let mut context = Context::from_raw(&raw_args, ®istry); + let input_clone = input.clone(); + let input_stream = once(async { Ok(input) }).to_input_stream(); + + let result = run_block( + &block, + &mut context, + input_stream, + &Scope::new(input_clone), + ).await; + + match result { + Ok(mut stream) => { + let errors = context.get_errors(); + if let Some(error) = errors.first() { + yield Err(error.clone()); + } + + while let Some(result) = stream.next().await { + yield Ok(ReturnSuccess::Value(result)); + } + } + Err(e) => { + yield Err(e); + } + } + } + }; + + Ok(stream.to_output_stream()) +} diff --git a/crates/nu-cli/src/commands/echo.rs b/crates/nu-cli/src/commands/echo.rs index 655439846c..85d5008008 100644 --- a/crates/nu-cli/src/commands/echo.rs +++ b/crates/nu-cli/src/commands/echo.rs @@ -1,10 +1,16 @@ +use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; pub struct Echo; -impl PerItemCommand for Echo { +#[derive(Deserialize)] +pub struct EchoArgs { + pub rest: Vec, +} + +impl WholeStreamCommand for Echo { fn name(&self) -> &str { "echo" } @@ -19,44 +25,36 @@ impl PerItemCommand for Echo { fn run( &self, - call_info: &CallInfo, + args: CommandArgs, registry: &CommandRegistry, - raw_args: &RawCommandArgs, - _input: Value, ) -> Result { - run(call_info, registry, raw_args) + args.process(registry, echo)?.run() } } -fn run( - call_info: &CallInfo, - _registry: &CommandRegistry, - _raw_args: &RawCommandArgs, -) -> Result { +fn echo(args: EchoArgs, _: RunnableContext) -> Result { let mut output = vec![]; - if let Some(ref positional) = call_info.args.positional { - for i in positional { - match i.as_string() { - Ok(s) => { - output.push(Ok(ReturnSuccess::Value( - UntaggedValue::string(s).into_value(i.tag.clone()), - ))); - } - _ => match i { - Value { - value: UntaggedValue::Table(table), - .. - } => { - for value in table { - output.push(Ok(ReturnSuccess::Value(value.clone()))); - } - } - _ => { - output.push(Ok(ReturnSuccess::Value(i.clone()))); - } - }, + for i in args.rest { + match i.as_string() { + Ok(s) => { + output.push(Ok(ReturnSuccess::Value( + UntaggedValue::string(s).into_value(i.tag.clone()), + ))); } + _ => match i { + Value { + value: UntaggedValue::Table(table), + .. + } => { + for value in table { + output.push(Ok(ReturnSuccess::Value(value.clone()))); + } + } + _ => { + output.push(Ok(ReturnSuccess::Value(i.clone()))); + } + }, } } diff --git a/crates/nu-cli/src/commands/edit.rs b/crates/nu-cli/src/commands/edit.rs index 0698ee407c..3533159f22 100644 --- a/crates/nu-cli/src/commands/edit.rs +++ b/crates/nu-cli/src/commands/edit.rs @@ -1,13 +1,19 @@ -use crate::commands::PerItemCommand; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_value_ext::ValueExt; pub struct Edit; -impl PerItemCommand for Edit { +#[derive(Deserialize)] +pub struct EditArgs { + field: ColumnPath, + replacement: Value, +} + +impl WholeStreamCommand for Edit { fn name(&self) -> &str { "edit" } @@ -15,13 +21,13 @@ impl PerItemCommand for Edit { fn signature(&self) -> Signature { Signature::build("edit") .required( - "Field", + "field", SyntaxShape::ColumnPath, "the name of the column to edit", ) .required( - "Value", - SyntaxShape::String, + "replacement value", + SyntaxShape::Any, "the new value to give the cell(s)", ) } @@ -32,41 +38,45 @@ impl PerItemCommand for Edit { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - _raw_args: &RawCommandArgs, - value: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - let value_tag = value.tag(); - let field = call_info.args.expect_nth(0)?.as_column_path()?; - let replacement = call_info.args.expect_nth(1)?.tagged_unknown(); + args.process(registry, edit)?.run() + } +} - let stream = match value { - obj - @ - Value { +fn edit( + EditArgs { field, replacement }: EditArgs, + RunnableContext { input, .. }: RunnableContext, +) -> Result { + let mut input = input; + + let stream = async_stream! { + match input.next().await { + Some(obj @ Value { value: UntaggedValue::Row(_), .. - } => match obj.replace_data_at_column_path(&field, replacement.item.clone()) { - Some(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]), + }) => match obj.replace_data_at_column_path(&field, replacement.clone()) { + Some(v) => yield Ok(ReturnSuccess::Value(v)), None => { - return Err(ShellError::labeled_error( + yield Err(ShellError::labeled_error( "edit could not find place to insert column", "column name", - &field.tag, + obj.tag, )) } }, - _ => { - return Err(ShellError::labeled_error( + Some(Value { tag, ..}) => { + yield Err(ShellError::labeled_error( "Unrecognized type in stream", "original value", - value_tag, + tag, )) } - }; + _ => {} + } + }; - Ok(stream.to_output_stream()) - } + Ok(stream.to_output_stream()) } diff --git a/crates/nu-cli/src/commands/enter.rs b/crates/nu-cli/src/commands/enter.rs index c52ed8ab31..d704ef2bc0 100644 --- a/crates/nu-cli/src/commands/enter.rs +++ b/crates/nu-cli/src/commands/enter.rs @@ -1,15 +1,22 @@ -use crate::commands::PerItemCommand; use crate::commands::UnevaluatedCallInfo; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{ - CallInfo, CommandAction, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, + CommandAction, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, }; +use nu_source::Tagged; +use std::path::PathBuf; pub struct Enter; -impl PerItemCommand for Enter { +#[derive(Deserialize)] +pub struct EnterArgs { + location: Tagged, +} + +impl WholeStreamCommand for Enter { fn name(&self) -> &str { "enter" } @@ -28,130 +35,121 @@ impl PerItemCommand for Enter { fn run( &self, - call_info: &CallInfo, + args: CommandArgs, registry: &CommandRegistry, - raw_args: &RawCommandArgs, - _input: Value, ) -> Result { - let registry = registry.clone(); - let raw_args = raw_args.clone(); - match call_info.args.expect_nth(0)? { - Value { - value: UntaggedValue::Primitive(Primitive::Path(location)), - tag, - .. - } => { - let location_string = location.display().to_string(); - let location_clone = location_string.clone(); - let tag_clone = tag.clone(); - - if location_string.starts_with("help") { - let spec = location_string.split(':').collect::>(); - - if spec.len() == 2 { - let (_, command) = (spec[0], spec[1]); - - if registry.has(command) { - return Ok(vec![Ok(ReturnSuccess::Action( - CommandAction::EnterHelpShell( - UntaggedValue::string(command).into_value(Tag::unknown()), - ), - ))] - .into()); - } - } - Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( - UntaggedValue::nothing().into_value(Tag::unknown()), - )))] - .into()) - } else if location.is_dir() { - Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell( - location_clone, - )))] - .into()) - } else { - let stream = async_stream! { - // If it's a file, attempt to open the file as a value and enter it - let cwd = raw_args.shell_manager.path(); - - let full_path = std::path::PathBuf::from(cwd); - - let (file_extension, contents, contents_tag) = - crate::commands::open::fetch( - &full_path, - &location_clone, - tag_clone.span, - ).await?; - - match contents { - UntaggedValue::Primitive(Primitive::String(_)) => { - let tagged_contents = contents.into_value(&contents_tag); - - if let Some(extension) = file_extension { - let command_name = format!("from-{}", extension); - if let Some(converter) = - registry.get_command(&command_name) - { - let new_args = RawCommandArgs { - host: raw_args.host, - ctrl_c: raw_args.ctrl_c, - shell_manager: raw_args.shell_manager, - call_info: UnevaluatedCallInfo { - args: nu_protocol::hir::Call { - head: raw_args.call_info.args.head, - positional: None, - named: None, - span: Span::unknown(), - is_last: false, - }, - name_tag: raw_args.call_info.name_tag, - scope: raw_args.call_info.scope.clone() - }, - }; - let mut result = converter.run( - new_args.with_input(vec![tagged_contents]), - ®istry, - ); - let result_vec: Vec> = - result.drain_vec().await; - for res in result_vec { - match res { - Ok(ReturnSuccess::Value(Value { - value, - .. - })) => { - yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell( - Value { - value, - tag: contents_tag.clone(), - }))); - } - x => yield x, - } - } - } else { - yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents))); - } - } else { - yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents))); - } - } - _ => { - let tagged_contents = contents.into_value(contents_tag); - - yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents))); - } - } - }; - Ok(stream.to_output_stream()) - } - } - x => Ok( - vec![Ok(ReturnSuccess::Action(CommandAction::EnterValueShell( - x.clone(), - )))] - .into(), - ), - } + Ok(args.process_raw(registry, enter)?.run()) + } +} + +fn enter( + EnterArgs { location }: EnterArgs, + RunnableContext { + registry, + name: tag, + .. + }: RunnableContext, + raw_args: RawCommandArgs, +) -> Result { + let location_string = location.display().to_string(); + let location_clone = location_string.clone(); + + if location_string.starts_with("help") { + let spec = location_string.split(':').collect::>(); + + if spec.len() == 2 { + let (_, command) = (spec[0], spec[1]); + + if registry.has(command) { + return Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( + UntaggedValue::string(command).into_value(Tag::unknown()), + )))] + .into()); + } + } + Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( + UntaggedValue::nothing().into_value(Tag::unknown()), + )))] + .into()) + } else if location.is_dir() { + Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell( + location_clone, + )))] + .into()) + } else { + let stream = async_stream! { + // If it's a file, attempt to open the file as a value and enter it + let cwd = raw_args.shell_manager.path(); + + let full_path = std::path::PathBuf::from(cwd); + + let (file_extension, contents, contents_tag) = + crate::commands::open::fetch( + &full_path, + &PathBuf::from(location_clone), + tag.span, + ).await?; + + match contents { + UntaggedValue::Primitive(Primitive::String(_)) => { + let tagged_contents = contents.into_value(&contents_tag); + + if let Some(extension) = file_extension { + let command_name = format!("from-{}", extension); + if let Some(converter) = + registry.get_command(&command_name) + { + let new_args = RawCommandArgs { + host: raw_args.host, + ctrl_c: raw_args.ctrl_c, + shell_manager: raw_args.shell_manager, + call_info: UnevaluatedCallInfo { + args: nu_protocol::hir::Call { + head: raw_args.call_info.args.head, + positional: None, + named: None, + span: Span::unknown(), + is_last: false, + }, + name_tag: raw_args.call_info.name_tag, + scope: raw_args.call_info.scope.clone() + }, + }; + let mut result = converter.run( + new_args.with_input(vec![tagged_contents]), + ®istry, + ); + let result_vec: Vec> = + result.drain_vec().await; + for res in result_vec { + match res { + Ok(ReturnSuccess::Value(Value { + value, + .. + })) => { + yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell( + Value { + value, + tag: contents_tag.clone(), + }))); + } + x => yield x, + } + } + } else { + yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents))); + } + } else { + yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents))); + } + } + _ => { + let tagged_contents = contents.into_value(contents_tag); + + yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell(tagged_contents))); + } + } + }; + Ok(stream.to_output_stream()) } } diff --git a/crates/nu-cli/src/commands/format.rs b/crates/nu-cli/src/commands/format.rs index 682a3a66cd..1b714de586 100644 --- a/crates/nu-cli/src/commands/format.rs +++ b/crates/nu-cli/src/commands/format.rs @@ -1,17 +1,20 @@ -use crate::commands::PerItemCommand; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{ - CallInfo, ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, -}; +use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_source::Tagged; use nu_value_ext::{as_column_path, get_data_by_column_path}; use std::borrow::Borrow; pub struct Format; -impl PerItemCommand for Format { +#[derive(Deserialize)] +pub struct FormatArgs { + pattern: Tagged, +} + +impl WholeStreamCommand for Format { fn name(&self) -> &str { "format" } @@ -19,7 +22,7 @@ impl PerItemCommand for Format { fn signature(&self) -> Signature { Signature::build("format").required( "pattern", - SyntaxShape::Any, + SyntaxShape::String, "the pattern to output. Eg) \"{foo}: {bar}\"", ) } @@ -30,61 +33,67 @@ impl PerItemCommand for Format { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - _raw_args: &RawCommandArgs, - value: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - //let value_tag = value.tag(); - let pattern = call_info.args.expect_nth(0)?; - let pattern_tag = pattern.tag.clone(); - let pattern = pattern.as_string()?; + args.process(registry, format_command)?.run() + } +} - let format_pattern = format(&pattern); - let commands = format_pattern; +fn format_command( + FormatArgs { pattern }: FormatArgs, + RunnableContext { input, .. }: RunnableContext, +) -> Result { + let pattern_tag = pattern.tag.clone(); - let output = match value { - value - @ - Value { - value: UntaggedValue::Row(_), - .. - } => { - let mut output = String::new(); + let format_pattern = format(&pattern); + let commands = format_pattern; + let mut input = input; - for command in &commands { - match command { - FormatCommand::Text(s) => { - output.push_str(&s); - } - FormatCommand::Column(c) => { - let key = to_column_path(&c, &pattern_tag)?; + let stream = async_stream! { + while let Some(value) = input.next().await { + match value { + value + @ + Value { + value: UntaggedValue::Row(_), + .. + } => { + let mut output = String::new(); - let fetcher = get_data_by_column_path( - &value, - &key, - Box::new(move |(_, _, error)| error), - ); - - if let Ok(c) = fetcher { - output - .push_str(&value::format_leaf(c.borrow()).plain_string(100_000)) + for command in &commands { + match command { + FormatCommand::Text(s) => { + output.push_str(&s); + } + FormatCommand::Column(c) => { + let key = to_column_path(&c, &pattern_tag)?; + + let fetcher = get_data_by_column_path( + &value, + &key, + Box::new(move |(_, _, error)| error), + ); + + if let Ok(c) = fetcher { + output + .push_str(&value::format_leaf(c.borrow()).plain_string(100_000)) + } + // That column doesn't match, so don't emit anything } - // That column doesn't match, so don't emit anything } } + + yield ReturnSuccess::value( + UntaggedValue::string(output).into_untagged_value()) } + _ => yield ReturnSuccess::value( + UntaggedValue::string(String::new()).into_untagged_value()), + }; + } + }; - output - } - _ => String::new(), - }; - - Ok(futures::stream::iter(vec![ReturnSuccess::value( - UntaggedValue::string(output).into_untagged_value(), - )]) - .to_output_stream()) - } + Ok(stream.to_output_stream()) } #[derive(Debug)] diff --git a/crates/nu-cli/src/commands/help.rs b/crates/nu-cli/src/commands/help.rs index 1401aa6fca..c16f8f4df7 100644 --- a/crates/nu-cli/src/commands/help.rs +++ b/crates/nu-cli/src/commands/help.rs @@ -1,24 +1,33 @@ -use crate::commands::PerItemCommand; +use crate::commands::WholeStreamCommand; use crate::data::command_dict; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{ - CallInfo, NamedType, PositionalType, Primitive, ReturnSuccess, Signature, SyntaxShape, - TaggedDictBuilder, UntaggedValue, Value, + NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, + UntaggedValue, }; -use nu_source::SpannedItem; +use nu_source::{SpannedItem, Tagged}; use nu_value_ext::get_data_by_key; pub struct Help; -impl PerItemCommand for Help { +#[derive(Deserialize)] +pub struct HelpArgs { + command: Option>, +} + +impl WholeStreamCommand for Help { fn name(&self) -> &str { "help" } fn signature(&self) -> Signature { - Signature::build("help").rest(SyntaxShape::Any, "the name of command(s) to get help on") + Signature::build("help").optional( + "command", + SyntaxShape::String, + "the name of command(s) to get help on", + ) } fn usage(&self) -> &str { @@ -27,67 +36,65 @@ impl PerItemCommand for Help { fn run( &self, - call_info: &CallInfo, + args: CommandArgs, registry: &CommandRegistry, - _raw_args: &RawCommandArgs, - _input: Value, ) -> Result { - let tag = &call_info.name_tag; + args.process(registry, help)?.run() + } +} - match call_info.args.nth(0) { - Some(Value { - value: UntaggedValue::Primitive(Primitive::String(document)), - tag, - }) => { - let mut help = VecDeque::new(); - if document == "commands" { - let mut sorted_names = registry.names(); - sorted_names.sort(); - for cmd in sorted_names { - let mut short_desc = TaggedDictBuilder::new(tag.clone()); - let value = command_dict( - registry.get_command(&cmd).ok_or_else(|| { - ShellError::labeled_error( - format!("Could not load {}", cmd), - "could not load command", - tag, - ) - })?, - tag.clone(), - ); +fn help( + HelpArgs { command }: HelpArgs, + RunnableContext { registry, name, .. }: RunnableContext, +) -> Result { + if let Some(document) = command { + let mut help = VecDeque::new(); + if document.item == "commands" { + let mut sorted_names = registry.names(); + sorted_names.sort(); + for cmd in sorted_names { + let mut short_desc = TaggedDictBuilder::new(name.clone()); + let document_tag = document.tag.clone(); + let value = command_dict( + registry.get_command(&cmd).ok_or_else(|| { + ShellError::labeled_error( + format!("Could not load {}", cmd), + "could not load command", + document_tag, + ) + })?, + name.clone(), + ); - short_desc.insert_untagged("name", cmd); - short_desc.insert_untagged( - "description", - get_data_by_key(&value, "usage".spanned_unknown()) - .ok_or_else(|| { - ShellError::labeled_error( - "Expected a usage key", - "expected a 'usage' key", - &value.tag, - ) - })? - .as_string()?, - ); + short_desc.insert_untagged("name", cmd); + short_desc.insert_untagged( + "description", + get_data_by_key(&value, "usage".spanned_unknown()) + .ok_or_else(|| { + ShellError::labeled_error( + "Expected a usage key", + "expected a 'usage' key", + &value.tag, + ) + })? + .as_string()?, + ); - help.push_back(ReturnSuccess::value(short_desc.into_value())); - } - } else if let Some(command) = registry.get_command(document) { - return Ok( - get_help(&command.name(), &command.usage(), command.signature()).into(), - ); - } else { - return Err(ShellError::labeled_error( - "Can't find command (use 'help commands' for full list)", - "can't find command", - tag, - )); - } - let help = futures::stream::iter(help); - Ok(help.to_output_stream()) + help.push_back(ReturnSuccess::value(short_desc.into_value())); } - _ => { - let msg = r#"Welcome to Nushell. + } else if let Some(command) = registry.get_command(&document.item) { + return Ok(get_help(&command.name(), &command.usage(), command.signature()).into()); + } else { + return Err(ShellError::labeled_error( + "Can't find command (use 'help commands' for full list)", + "can't find command", + document.tag, + )); + } + let help = futures::stream::iter(help); + Ok(help.to_output_stream()) + } else { + let msg = r#"Welcome to Nushell. Here are some tips to help you get started. * help commands - list all available commands @@ -109,13 +116,11 @@ Get the processes on your system actively using CPU: You can also learn more at https://www.nushell.sh/book/"#; - let output_stream = futures::stream::iter(vec![ReturnSuccess::value( - UntaggedValue::string(msg).into_value(tag), - )]); + let output_stream = futures::stream::iter(vec![ReturnSuccess::value( + UntaggedValue::string(msg).into_value(name), + )]); - Ok(output_stream.to_output_stream()) - } - } + Ok(output_stream.to_output_stream()) } } diff --git a/crates/nu-cli/src/commands/history.rs b/crates/nu-cli/src/commands/history.rs index c029ac16b8..2c33d670a3 100644 --- a/crates/nu-cli/src/commands/history.rs +++ b/crates/nu-cli/src/commands/history.rs @@ -1,14 +1,17 @@ use crate::cli::History as HistoryFile; -use crate::commands::PerItemCommand; +use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, ReturnSuccess, Signature, UntaggedValue, Value}; +use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; use std::fs::File; use std::io::{BufRead, BufReader}; pub struct History; -impl PerItemCommand for History { +#[derive(Deserialize)] +pub struct HistoryArgs; + +impl WholeStreamCommand for History { fn name(&self) -> &str { "history" } @@ -23,27 +26,30 @@ impl PerItemCommand for History { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - _raw_args: &RawCommandArgs, - _input: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - let tag = call_info.name_tag.clone(); - - let stream = async_stream! { - let history_path = HistoryFile::path(); - let file = File::open(history_path); - if let Ok(file) = file { - let reader = BufReader::new(file); - for line in reader.lines() { - if let Ok(line) = line { - yield ReturnSuccess::value(UntaggedValue::string(line).into_value(tag.clone())); - } - } - } else { - yield Err(ShellError::labeled_error("Could not open history", "history file could not be opened", tag.clone())); - } - }; - Ok(stream.to_output_stream()) + args.process(registry, history)?.run() } } + +fn history( + _: HistoryArgs, + RunnableContext { name: tag, .. }: RunnableContext, +) -> Result { + let stream = async_stream! { + let history_path = HistoryFile::path(); + let file = File::open(history_path); + if let Ok(file) = file { + let reader = BufReader::new(file); + for line in reader.lines() { + if let Ok(line) = line { + yield ReturnSuccess::value(UntaggedValue::string(line).into_value(tag.clone())); + } + } + } else { + yield Err(ShellError::labeled_error("Could not open history", "history file could not be opened", tag.clone())); + } + }; + Ok(stream.to_output_stream()) +} diff --git a/crates/nu-cli/src/commands/insert.rs b/crates/nu-cli/src/commands/insert.rs index b2f69115ad..7fd5869e18 100644 --- a/crates/nu-cli/src/commands/insert.rs +++ b/crates/nu-cli/src/commands/insert.rs @@ -1,13 +1,19 @@ -use crate::commands::PerItemCommand; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_value_ext::ValueExt; pub struct Insert; -impl PerItemCommand for Insert { +#[derive(Deserialize)] +pub struct InsertArgs { + column: ColumnPath, + value: Value, +} + +impl WholeStreamCommand for Insert { fn name(&self) -> &str { "insert" } @@ -27,40 +33,45 @@ impl PerItemCommand for Insert { } fn usage(&self) -> &str { - "Edit an existing column to have a new value." + "Insert a new column with a given value." } fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - _raw_args: &RawCommandArgs, - value: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - let value_tag = value.tag(); - let field = call_info.args.expect_nth(0)?.as_column_path()?; - let replacement = call_info.args.expect_nth(1)?.tagged_unknown(); - - let stream = match value { - obj - @ - Value { - value: UntaggedValue::Row(_), - .. - } => match obj.insert_data_at_column_path(&field, replacement.item.clone()) { - Ok(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]), - Err(err) => return Err(err), - }, - - _ => { - return Err(ShellError::labeled_error( - "Unrecognized type in stream", - "original value", - value_tag, - )) - } - }; - - Ok(stream.to_output_stream()) + args.process(registry, insert)?.run() } } + +fn insert( + InsertArgs { column, value }: InsertArgs, + RunnableContext { input, .. }: RunnableContext, +) -> Result { + let mut input = input; + + let stream = async_stream! { + match input.next().await { + Some(obj @ Value { + value: UntaggedValue::Row(_), + .. + }) => match obj.insert_data_at_column_path(&column, value.clone()) { + Ok(v) => yield Ok(ReturnSuccess::Value(v)), + Err(err) => yield Err(err), + }, + + Some(Value { tag, ..}) => { + yield Err(ShellError::labeled_error( + "Unrecognized type in stream", + "original value", + tag, + )); + } + + None => {} + }; + + }; + Ok(stream.to_output_stream()) +} diff --git a/crates/nu-cli/src/commands/is_empty.rs b/crates/nu-cli/src/commands/is_empty.rs index db7f2d3080..d387404497 100644 --- a/crates/nu-cli/src/commands/is_empty.rs +++ b/crates/nu-cli/src/commands/is_empty.rs @@ -1,10 +1,8 @@ -use crate::commands::PerItemCommand; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{ - CallInfo, ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, -}; +use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_source::Tagged; use nu_value_ext::ValueExt; @@ -17,7 +15,12 @@ enum IsEmptyFor { pub struct IsEmpty; -impl PerItemCommand for IsEmpty { +#[derive(Deserialize)] +pub struct IsEmptyArgs { + rest: Vec, +} + +impl WholeStreamCommand for IsEmpty { fn name(&self) -> &str { "empty?" } @@ -35,61 +38,100 @@ impl PerItemCommand for IsEmpty { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - _raw_args: &RawCommandArgs, - value: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - let value_tag = value.tag(); + args.process(registry, is_empty)?.run() + } +} - let action = if call_info.args.len() <= 2 { - let field = call_info.args.expect_nth(0); - let replacement_if_true = call_info.args.expect_nth(1); +fn is_empty( + IsEmptyArgs { rest }: IsEmptyArgs, + RunnableContext { input, .. }: RunnableContext, +) -> Result { + Ok(input + .map(move |value| { + let value_tag = value.tag(); - match (field, replacement_if_true) { - (Ok(field), Ok(replacement_if_true)) => IsEmptyFor::RowWithFieldAndFallback( - Box::new(field.as_column_path()?), - replacement_if_true.clone(), - ), - (Ok(field), Err(_)) => IsEmptyFor::RowWithField(field.as_column_path()?), - (_, _) => IsEmptyFor::Value, - } - } else { - let no_args = vec![]; - let mut arguments = call_info - .args - .positional - .as_ref() - .unwrap_or_else(|| &no_args) - .iter() - .rev(); - let replacement_if_true = match arguments.next() { - Some(arg) => arg.clone(), - None => UntaggedValue::boolean(value.is_empty()).into_value(&value_tag), + let action = if rest.len() <= 2 { + let field = rest.get(0); + let replacement_if_true = rest.get(1); + + match (field, replacement_if_true) { + (Some(field), Some(replacement_if_true)) => { + IsEmptyFor::RowWithFieldAndFallback( + Box::new(field.as_column_path()?), + replacement_if_true.clone(), + ) + } + (Some(field), None) => IsEmptyFor::RowWithField(field.as_column_path()?), + (_, _) => IsEmptyFor::Value, + } + } else { + // let no_args = vec![]; + let mut arguments = rest.iter().rev(); + let replacement_if_true = match arguments.next() { + Some(arg) => arg.clone(), + None => UntaggedValue::boolean(value.is_empty()).into_value(&value_tag), + }; + + IsEmptyFor::RowWithFieldsAndFallback( + arguments + .map(|a| a.as_column_path()) + .filter_map(Result::ok) + .collect(), + replacement_if_true, + ) }; - IsEmptyFor::RowWithFieldsAndFallback( - arguments - .map(|a| a.as_column_path()) - .filter_map(Result::ok) - .collect(), - replacement_if_true, - ) - }; + match action { + IsEmptyFor::Value => Ok(ReturnSuccess::Value( + UntaggedValue::boolean(value.is_empty()).into_value(value_tag), + )), + IsEmptyFor::RowWithFieldsAndFallback(fields, default) => { + let mut out = value; - match action { - IsEmptyFor::Value => Ok(futures::stream::iter(vec![Ok(ReturnSuccess::Value( - UntaggedValue::boolean(value.is_empty()).into_value(value_tag), - ))]) - .to_output_stream()), - IsEmptyFor::RowWithFieldsAndFallback(fields, default) => { - let mut out = value; + for field in fields.iter() { + let val = + out.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?; - for field in fields.iter() { + let emptiness_value = match out { + obj + @ + Value { + value: UntaggedValue::Row(_), + .. + } => { + if val.is_empty() { + match obj.replace_data_at_column_path(&field, default.clone()) { + Some(v) => Ok(v), + None => Err(ShellError::labeled_error( + "empty? could not find place to check emptiness", + "column name", + &field.tag, + )), + } + } else { + Ok(obj) + } + } + _ => Err(ShellError::labeled_error( + "Unrecognized type in stream", + "original value", + &value_tag, + )), + }; + + out = emptiness_value?; + } + + Ok(ReturnSuccess::Value(out)) + } + IsEmptyFor::RowWithField(field) => { let val = - out.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?; + value.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?; - let emptiness_value = match out { + match &value { obj @ Value { @@ -97,8 +139,11 @@ impl PerItemCommand for IsEmpty { .. } => { if val.is_empty() { - match obj.replace_data_at_column_path(&field, default.clone()) { - Some(v) => Ok(v), + match obj.replace_data_at_column_path( + &field, + UntaggedValue::boolean(true).into_value(&value_tag), + ) { + Some(v) => Ok(ReturnSuccess::Value(v)), None => Err(ShellError::labeled_error( "empty? could not find place to check emptiness", "column name", @@ -106,7 +151,7 @@ impl PerItemCommand for IsEmpty { )), } } else { - Ok(obj) + Ok(ReturnSuccess::Value(value)) } } _ => Err(ShellError::labeled_error( @@ -114,90 +159,40 @@ impl PerItemCommand for IsEmpty { "original value", &value_tag, )), - }; - - out = emptiness_value?; + } } + IsEmptyFor::RowWithFieldAndFallback(field, default) => { + let val = + value.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?; - Ok(futures::stream::iter(vec![Ok(ReturnSuccess::Value(out))]).to_output_stream()) - } - IsEmptyFor::RowWithField(field) => { - let val = - value.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?; - - let stream = match &value { - obj - @ - Value { - value: UntaggedValue::Row(_), - .. - } => { - if val.is_empty() { - match obj.replace_data_at_column_path( - &field, - UntaggedValue::boolean(true).into_value(&value_tag), - ) { - Some(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]), - None => { - return Err(ShellError::labeled_error( + match &value { + obj + @ + Value { + value: UntaggedValue::Row(_), + .. + } => { + if val.is_empty() { + match obj.replace_data_at_column_path(&field, default) { + Some(v) => Ok(ReturnSuccess::Value(v)), + None => Err(ShellError::labeled_error( "empty? could not find place to check emptiness", "column name", &field.tag, - )) + )), } + } else { + Ok(ReturnSuccess::Value(value)) } - } else { - futures::stream::iter(vec![Ok(ReturnSuccess::Value(value))]) } - } - _ => { - return Err(ShellError::labeled_error( + _ => Err(ShellError::labeled_error( "Unrecognized type in stream", "original value", &value_tag, - )) + )), } - }; - - Ok(stream.to_output_stream()) + } } - IsEmptyFor::RowWithFieldAndFallback(field, default) => { - let val = - value.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?; - - let stream = match &value { - obj - @ - Value { - value: UntaggedValue::Row(_), - .. - } => { - if val.is_empty() { - match obj.replace_data_at_column_path(&field, default) { - Some(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]), - None => { - return Err(ShellError::labeled_error( - "empty? could not find place to check emptiness", - "column name", - &field.tag, - )) - } - } - } else { - futures::stream::iter(vec![Ok(ReturnSuccess::Value(value))]) - } - } - _ => { - return Err(ShellError::labeled_error( - "Unrecognized type in stream", - "original value", - &value_tag, - )) - } - }; - - Ok(stream.to_output_stream()) - } - } - } + }) + .to_output_stream()) } diff --git a/crates/nu-cli/src/commands/kill.rs b/crates/nu-cli/src/commands/kill.rs index cc11d60a16..bb66dcbd25 100644 --- a/crates/nu-cli/src/commands/kill.rs +++ b/crates/nu-cli/src/commands/kill.rs @@ -1,8 +1,8 @@ -use crate::commands::command::RunnablePerItemContext; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; +use nu_protocol::{Signature, SyntaxShape}; use nu_source::Tagged; use std::process::{Command, Stdio}; @@ -16,7 +16,7 @@ pub struct KillArgs { pub quiet: Tagged, } -impl PerItemCommand for Kill { +impl WholeStreamCommand for Kill { fn name(&self) -> &str { "kill" } @@ -39,14 +39,10 @@ impl PerItemCommand for Kill { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - raw_args: &RawCommandArgs, - _input: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - call_info - .process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), kill)? - .run() + args.process(registry, kill)?.run() } } @@ -57,7 +53,7 @@ fn kill( force, quiet, }: KillArgs, - _context: &RunnablePerItemContext, + _context: RunnableContext, ) -> Result { let mut cmd = if cfg!(windows) { let mut cmd = Command::new("taskkill"); diff --git a/crates/nu-cli/src/commands/ls.rs b/crates/nu-cli/src/commands/ls.rs index e8ded5962e..5095240eaa 100644 --- a/crates/nu-cli/src/commands/ls.rs +++ b/crates/nu-cli/src/commands/ls.rs @@ -1,7 +1,7 @@ -use crate::commands::command::RunnablePerItemContext; +use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; +use nu_protocol::{Signature, SyntaxShape}; use nu_source::Tagged; use std::path::PathBuf; @@ -18,7 +18,7 @@ pub struct LsArgs { pub with_symlink_targets: bool, } -impl PerItemCommand for Ls { +impl WholeStreamCommand for Ls { fn name(&self) -> &str { "ls" } @@ -54,17 +54,13 @@ impl PerItemCommand for Ls { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - raw_args: &RawCommandArgs, - _input: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - call_info - .process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), ls)? - .run() + args.process(registry, ls)?.run() } } -fn ls(args: LsArgs, context: &RunnablePerItemContext) -> Result { - context.shell_manager.ls(args, context) +fn ls(args: LsArgs, context: RunnableContext) -> Result { + context.shell_manager.ls(args, &context) } diff --git a/crates/nu-cli/src/commands/mkdir.rs b/crates/nu-cli/src/commands/mkdir.rs index f94f6eb016..acf7bb61f7 100644 --- a/crates/nu-cli/src/commands/mkdir.rs +++ b/crates/nu-cli/src/commands/mkdir.rs @@ -1,8 +1,8 @@ -use crate::commands::command::RunnablePerItemContext; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; +use nu_protocol::{Signature, SyntaxShape}; use nu_source::Tagged; use std::path::PathBuf; @@ -13,7 +13,7 @@ pub struct MkdirArgs { pub rest: Vec>, } -impl PerItemCommand for Mkdir { +impl WholeStreamCommand for Mkdir { fn name(&self) -> &str { "mkdir" } @@ -28,18 +28,14 @@ impl PerItemCommand for Mkdir { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - raw_args: &RawCommandArgs, - _input: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - call_info - .process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), mkdir)? - .run() + args.process(registry, mkdir)?.run() } } -fn mkdir(args: MkdirArgs, context: &RunnablePerItemContext) -> Result { +fn mkdir(args: MkdirArgs, context: RunnableContext) -> Result { let shell_manager = context.shell_manager.clone(); - shell_manager.mkdir(args, context) + shell_manager.mkdir(args, &context) } diff --git a/crates/nu-cli/src/commands/mv.rs b/crates/nu-cli/src/commands/mv.rs index 8118777cf4..238d45f2c7 100644 --- a/crates/nu-cli/src/commands/mv.rs +++ b/crates/nu-cli/src/commands/mv.rs @@ -1,8 +1,8 @@ -use crate::commands::command::RunnablePerItemContext; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; +use nu_protocol::{Signature, SyntaxShape}; use nu_source::Tagged; use std::path::PathBuf; @@ -14,7 +14,7 @@ pub struct MoveArgs { pub dst: Tagged, } -impl PerItemCommand for Move { +impl WholeStreamCommand for Move { fn name(&self) -> &str { "mv" } @@ -39,18 +39,14 @@ impl PerItemCommand for Move { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - raw_args: &RawCommandArgs, - _input: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - call_info - .process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), mv)? - .run() + args.process(registry, mv)?.run() } } -fn mv(args: MoveArgs, context: &RunnablePerItemContext) -> Result { +fn mv(args: MoveArgs, context: RunnableContext) -> Result { let shell_manager = context.shell_manager.clone(); - shell_manager.mv(args, context) + shell_manager.mv(args, &context) } diff --git a/crates/nu-cli/src/commands/open.rs b/crates/nu-cli/src/commands/open.rs index d416a9c2ac..1ad09eff67 100644 --- a/crates/nu-cli/src/commands/open.rs +++ b/crates/nu-cli/src/commands/open.rs @@ -1,14 +1,19 @@ +use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{ - CallInfo, CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, -}; -use nu_source::{AnchorLocation, Span}; +use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue}; +use nu_source::{AnchorLocation, Span, Tagged}; use std::path::{Path, PathBuf}; pub struct Open; -impl PerItemCommand for Open { +#[derive(Deserialize)] +pub struct OpenArgs { + path: Tagged, + raw: Tagged, +} + +impl WholeStreamCommand for Open { fn name(&self) -> &str { "open" } @@ -33,36 +38,23 @@ impl PerItemCommand for Open { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - raw_args: &RawCommandArgs, - _input: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - run(call_info, raw_args) + args.process(registry, open)?.run() } } -fn run(call_info: &CallInfo, raw_args: &RawCommandArgs) -> Result { - let shell_manager = &raw_args.shell_manager; +fn open( + OpenArgs { path, raw }: OpenArgs, + RunnableContext { shell_manager, .. }: RunnableContext, +) -> Result { let cwd = PathBuf::from(shell_manager.path()); let full_path = cwd; - let path = call_info.args.nth(0).ok_or_else(|| { - ShellError::labeled_error( - "No file or directory specified", - "for command", - &call_info.name_tag, - ) - })?; - - let path_buf = path.as_path()?; - let path_str = path_buf.display().to_string(); - let path_span = path.tag.span; - let has_raw = call_info.args.has("raw"); - let stream = async_stream! { - let result = fetch(&full_path, &path_str, path_span).await; + let result = fetch(&full_path, &path.item, path.tag.span).await; if let Err(e) = result { yield Err(e); @@ -70,12 +62,12 @@ fn run(call_info: &CallInfo, raw_args: &RawCommandArgs) -> Result Result Result<(Option, UntaggedValue, Tag), ShellError> { let mut cwd = cwd.clone(); diff --git a/crates/nu-cli/src/commands/parse.rs b/crates/nu-cli/src/commands/parse.rs index 2733fdf8b0..5eb4e9404f 100644 --- a/crates/nu-cli/src/commands/parse.rs +++ b/crates/nu-cli/src/commands/parse.rs @@ -1,11 +1,9 @@ -use crate::commands::PerItemCommand; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{ - CallInfo, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value, -}; - +use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue}; +use nu_source::Tagged; use regex::Regex; #[derive(Debug)] @@ -84,7 +82,12 @@ fn build_regex(commands: &[ParseCommand]) -> String { } pub struct Parse; -impl PerItemCommand for Parse { +#[derive(Deserialize)] +pub struct ParseArgs { + pattern: Tagged, +} + +impl WholeStreamCommand for Parse { fn name(&self) -> &str { "parse" } @@ -92,7 +95,7 @@ impl PerItemCommand for Parse { fn signature(&self) -> Signature { Signature::build("parse").required( "pattern", - SyntaxShape::Any, + SyntaxShape::String, "the pattern to match. Eg) \"{foo}: {bar}\"", ) } @@ -103,41 +106,55 @@ impl PerItemCommand for Parse { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - _raw_args: &RawCommandArgs, - value: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - //let value_tag = value.tag(); - let pattern = call_info.args.expect_nth(0)?.as_string()?; - - let parse_pattern = parse(&pattern); - let parse_regex = build_regex(&parse_pattern); - - let column_names = column_names(&parse_pattern); - let regex = Regex::new(&parse_regex).map_err(|_| { - ShellError::labeled_error("Could not parse regex", "could not parse regex", &value.tag) - })?; - - let output = if let Ok(s) = value.as_string() { - let mut results = vec![]; - for cap in regex.captures_iter(&s) { - let mut dict = TaggedDictBuilder::new(value.tag()); - - for (idx, column_name) in column_names.iter().enumerate() { - dict.insert_untagged( - column_name, - UntaggedValue::string(&cap[idx + 1].to_string()), - ); - } - - results.push(ReturnSuccess::value(dict.into_value())); - } - - VecDeque::from(results) - } else { - VecDeque::new() - }; - Ok(output.into()) + args.process(registry, parse_command)?.run() } } + +fn parse_command( + ParseArgs { pattern }: ParseArgs, + RunnableContext { name, input, .. }: RunnableContext, +) -> Result { + let parse_pattern = parse(&pattern.item); + let parse_regex = build_regex(&parse_pattern); + let column_names = column_names(&parse_pattern); + let name = name.span; + let regex = Regex::new(&parse_regex).map_err(|_| { + ShellError::labeled_error( + "Could not parse regex", + "could not parse regex", + &pattern.tag, + ) + })?; + + Ok(input + .map(move |value| { + if let Ok(s) = value.as_string() { + let mut output = vec![]; + for cap in regex.captures_iter(&s) { + let mut dict = TaggedDictBuilder::new(value.tag()); + for (idx, column_name) in column_names.iter().enumerate() { + dict.insert_untagged( + column_name, + UntaggedValue::string(cap[idx + 1].to_string()), + ); + } + output.push(Ok(ReturnSuccess::Value(dict.into_value()))); + } + output + } else { + vec![Err(ShellError::labeled_error_with_secondary( + "Expected string input", + "expected string input", + name, + "value originated here", + value.tag, + ))] + } + }) + .map(futures::stream::iter) + .flatten() + .to_output_stream()) +} diff --git a/crates/nu-cli/src/commands/rm.rs b/crates/nu-cli/src/commands/rm.rs index af94614086..e9a0e8e038 100644 --- a/crates/nu-cli/src/commands/rm.rs +++ b/crates/nu-cli/src/commands/rm.rs @@ -1,8 +1,8 @@ -use crate::commands::command::RunnablePerItemContext; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; +use nu_protocol::{Signature, SyntaxShape}; use nu_source::Tagged; use std::path::PathBuf; @@ -16,7 +16,7 @@ pub struct RemoveArgs { pub trash: Tagged, } -impl PerItemCommand for Remove { +impl WholeStreamCommand for Remove { fn name(&self) -> &str { "rm" } @@ -38,18 +38,14 @@ impl PerItemCommand for Remove { fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - raw_args: &RawCommandArgs, - _input: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - call_info - .process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), rm)? - .run() + args.process(registry, rm)?.run() } } -fn rm(args: RemoveArgs, context: &RunnablePerItemContext) -> Result { +fn rm(args: RemoveArgs, context: RunnableContext) -> Result { let shell_manager = context.shell_manager.clone(); - shell_manager.rm(args, context) + shell_manager.rm(args, &context) } diff --git a/crates/nu-cli/src/commands/run_alias.rs b/crates/nu-cli/src/commands/run_alias.rs index 9338ec3a03..bb33a098d1 100644 --- a/crates/nu-cli/src/commands/run_alias.rs +++ b/crates/nu-cli/src/commands/run_alias.rs @@ -1,18 +1,19 @@ use crate::commands::classified::block::run_block; +use crate::commands::WholeStreamCommand; use crate::prelude::*; use derive_new::new; use nu_errors::ShellError; -use nu_protocol::{hir::Block, CallInfo, ReturnSuccess, Scope, Signature, SyntaxShape, Value}; +use nu_protocol::{hir::Block, ReturnSuccess, Scope, Signature, SyntaxShape}; -#[derive(new)] +#[derive(new, Clone)] pub struct AliasCommand { name: String, args: Vec, block: Block, } -impl PerItemCommand for AliasCommand { +impl WholeStreamCommand for AliasCommand { fn name(&self) -> &str { &self.name } @@ -33,73 +34,76 @@ impl PerItemCommand for AliasCommand { fn run( &self, - call_info: &CallInfo, + args: CommandArgs, registry: &CommandRegistry, - raw_args: &RawCommandArgs, - input: Value, ) -> Result { - let tag = call_info.name_tag.clone(); - let call_info = call_info.clone(); + let tag = args.call_info.name_tag.clone(); + let call_info = args.call_info.clone(); let registry = registry.clone(); - let raw_args = raw_args.clone(); let block = self.block.clone(); - - let mut scope = Scope::it_value(input.clone()); - if let Some(positional) = &call_info.args.positional { - for (pos, arg) in positional.iter().enumerate() { - scope = scope.set_var(self.args[pos].to_string(), arg.clone()); - } - } + let alias_command = self.clone(); + let mut context = Context::from_args(&args, ®istry); + let mut input = args.input; let stream = async_stream! { - let mut context = Context::from_raw(&raw_args, ®istry); - - let input_clone = Ok(input.clone()); - let input_stream = futures::stream::once(async { input_clone }).boxed().to_input_stream(); - - let result = run_block( - &block, - &mut context, - input_stream, - &scope - ).await; - - match result { - 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)); + while let Some(input) = input.next().await { + let call_info = call_info.clone(); + let tag = tag.clone(); + let evaluated = call_info.evaluate_with_new_it(®istry, &input)?; + let mut scope = Scope::it_value(input.clone()); + if let Some(positional) = &evaluated.args.positional { + for (pos, arg) in positional.iter().enumerate() { + scope = scope.set_var(alias_command.args[pos].to_string(), arg.clone()); } + } - let errors = context.get_errors(); - if let Some(x) = errors.first() { - //yield Err(error.clone()); + let input_clone = Ok(input.clone()); + let input_stream = futures::stream::once(async { input_clone }).boxed().to_input_stream(); + + let result = run_block( + &block, + &mut context, + input_stream, + &scope + ).await; + + match result { + 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(error.clone()); + 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(e); yield Err(ShellError::labeled_error_with_secondary( "Alias failed to run", "alias failed to run", tag.clone(), - x.to_string(), + e.to_string(), tag )); } } - Err(e) => { - //yield Err(e); - yield Err(ShellError::labeled_error_with_secondary( - "Alias failed to run", - "alias failed to run", - tag.clone(), - e.to_string(), - tag - )); - } } }; diff --git a/crates/nu-cli/src/commands/touch.rs b/crates/nu-cli/src/commands/touch.rs index 3fa2c0c7b9..6f1076b0b6 100644 --- a/crates/nu-cli/src/commands/touch.rs +++ b/crates/nu-cli/src/commands/touch.rs @@ -1,6 +1,7 @@ +use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; +use nu_protocol::{Signature, SyntaxShape}; use nu_source::Tagged; use std::fs::OpenOptions; use std::path::PathBuf; @@ -12,7 +13,7 @@ pub struct TouchArgs { pub target: Tagged, } -impl PerItemCommand for Touch { +impl WholeStreamCommand for Touch { fn name(&self) -> &str { "touch" } @@ -28,27 +29,19 @@ impl PerItemCommand for Touch { } fn run( &self, - call_info: &CallInfo, - _registry: &CommandRegistry, - raw_args: &RawCommandArgs, - _input: Value, + args: CommandArgs, + registry: &CommandRegistry, ) -> Result { - call_info - .process(&raw_args.shell_manager, raw_args.ctrl_c.clone(), touch)? - .run() + args.process(registry, touch)?.run() } } -fn touch(args: TouchArgs, _context: &RunnablePerItemContext) -> Result { - match OpenOptions::new() - .write(true) - .create(true) - .open(&args.target) - { +fn touch(TouchArgs { target }: TouchArgs, _: RunnableContext) -> Result { + match OpenOptions::new().write(true).create(true).open(&target) { Ok(_) => Ok(OutputStream::empty()), Err(err) => Err(ShellError::labeled_error( "File Error", err.to_string(), - &args.target.tag, + &target.tag, )), } } diff --git a/crates/nu-cli/src/commands/where_.rs b/crates/nu-cli/src/commands/where_.rs index 84d6bf39f4..9a87b3d5fa 100644 --- a/crates/nu-cli/src/commands/where_.rs +++ b/crates/nu-cli/src/commands/where_.rs @@ -1,16 +1,20 @@ -use crate::commands::PerItemCommand; +use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::evaluate::evaluate_baseline_expr; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{ - hir::ClassifiedCommand, CallInfo, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue, - Value, + hir::Block, hir::ClassifiedCommand, ReturnSuccess, Scope, Signature, SyntaxShape, }; pub struct Where; -impl PerItemCommand for Where { +#[derive(Deserialize)] +pub struct WhereArgs { + block: Block, +} + +impl WholeStreamCommand for Where { fn name(&self) -> &str { "where" } @@ -29,68 +33,68 @@ impl PerItemCommand for Where { fn run( &self, - call_info: &CallInfo, + args: CommandArgs, registry: &CommandRegistry, - _raw_args: &RawCommandArgs, - input: Value, ) -> Result { - let block = call_info.args.expect_nth(0)?.clone(); - - let condition = match block { - Value { - value: UntaggedValue::Block(block), + args.process(registry, where_command)?.run() + } +} +fn where_command( + WhereArgs { block }: WhereArgs, + RunnableContext { + name: tag, + registry, + input, + .. + }: RunnableContext, +) -> Result { + let condition = { + if block.block.len() != 1 { + return Err(ShellError::labeled_error( + "Expected a condition", + "expected a condition", tag, - } => { - if block.block.len() != 1 { + )); + } + match block.block[0].list.get(0) { + Some(item) => match item { + ClassifiedCommand::Expr(expr) => expr.clone(), + _ => { return Err(ShellError::labeled_error( "Expected a condition", "expected a condition", tag, - )); + )) } - match block.block[0].list.get(0) { - Some(item) => match item { - ClassifiedCommand::Expr(expr) => expr.clone(), - _ => { - return Err(ShellError::labeled_error( - "Expected a condition", - "expected a condition", - tag, - )) - } - }, - None => { - return Err(ShellError::labeled_error( - "Expected a condition", - "expected a condition", - tag, - )); - } - } - } - Value { tag, .. } => { + }, + None => { return Err(ShellError::labeled_error( "Expected a condition", "expected a condition", tag, )); } - }; + } + }; - //FIXME: should we use the scope that's brought in as well? - let condition = evaluate_baseline_expr(&condition, registry, &Scope::new(input.clone()))?; + let mut input = input; - let stream = match condition.as_bool() { - Ok(b) => { - if b { - VecDeque::from(vec![Ok(ReturnSuccess::Value(input))]) - } else { - VecDeque::new() + let stream = async_stream! { + while let Some(input) = input.next().await { + + //FIXME: should we use the scope that's brought in as well? + let condition = evaluate_baseline_expr(&condition, ®istry, &Scope::new(input.clone()))?; + + match condition.as_bool() { + Ok(b) => { + if b { + yield Ok(ReturnSuccess::Value(input)); + } } - } - Err(e) => return Err(e), - }; + Err(e) => yield Err(e), + }; + } + }; - Ok(stream.into()) - } + Ok(stream.to_output_stream()) } diff --git a/crates/nu-cli/src/context.rs b/crates/nu-cli/src/context.rs index ed038b93a1..7a04885926 100644 --- a/crates/nu-cli/src/context.rs +++ b/crates/nu-cli/src/context.rs @@ -112,6 +112,30 @@ impl Context { } } + pub(crate) fn from_args(args: &CommandArgs, registry: &CommandRegistry) -> Context { + #[cfg(windows)] + { + Context { + registry: registry.clone(), + host: args.host.clone(), + current_errors: Arc::new(Mutex::new(vec![])), + ctrl_c: args.ctrl_c.clone(), + shell_manager: args.shell_manager.clone(), + windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())), + } + } + #[cfg(not(windows))] + { + Context { + registry: registry.clone(), + host: args.host.clone(), + current_errors: Arc::new(Mutex::new(vec![])), + ctrl_c: args.ctrl_c.clone(), + shell_manager: args.shell_manager.clone(), + } + } + } + pub(crate) fn basic() -> Result> { let registry = CommandRegistry::new(); diff --git a/crates/nu-cli/src/deserializer.rs b/crates/nu-cli/src/deserializer.rs index 911f4bbfb9..67ce808f68 100644 --- a/crates/nu-cli/src/deserializer.rs +++ b/crates/nu-cli/src/deserializer.rs @@ -356,7 +356,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> { return visit::(value.val, name, fields, visitor); } - if name == "Evaluate" { + if name == "Block" { let block = match value.val { Value { value: UntaggedValue::Block(block), diff --git a/crates/nu-cli/src/prelude.rs b/crates/nu-cli/src/prelude.rs index 4ce9d287e2..cba240853d 100644 --- a/crates/nu-cli/src/prelude.rs +++ b/crates/nu-cli/src/prelude.rs @@ -71,10 +71,7 @@ macro_rules! trace_out_stream { pub(crate) use nu_protocol::{errln, out, outln}; use nu_source::HasFallibleSpan; -pub(crate) use crate::commands::command::{ - CallInfoExt, CommandArgs, PerItemCommand, RawCommandArgs, RunnableContext, - RunnablePerItemContext, -}; +pub(crate) use crate::commands::command::{CommandArgs, RawCommandArgs, RunnableContext}; pub(crate) use crate::context::CommandRegistry; pub(crate) use crate::context::Context; pub(crate) use crate::data::config; diff --git a/crates/nu-cli/src/shell/filesystem_shell.rs b/crates/nu-cli/src/shell/filesystem_shell.rs index f88269b2b5..cc575a4c0a 100644 --- a/crates/nu-cli/src/shell/filesystem_shell.rs +++ b/crates/nu-cli/src/shell/filesystem_shell.rs @@ -102,7 +102,7 @@ impl Shell for FilesystemShell { short_names, with_symlink_targets, }: LsArgs, - context: &RunnablePerItemContext, + context: &RunnableContext, ) -> Result { let ctrl_c = context.ctrl_c.clone(); let name_tag = context.name.clone(); diff --git a/crates/nu-cli/src/shell/help_shell.rs b/crates/nu-cli/src/shell/help_shell.rs index 6b5e333c85..fe89a8a7cb 100644 --- a/crates/nu-cli/src/shell/help_shell.rs +++ b/crates/nu-cli/src/shell/help_shell.rs @@ -140,11 +140,7 @@ impl Shell for HelpShell { self.path = path; } - fn ls( - &self, - _args: LsArgs, - _context: &RunnablePerItemContext, - ) -> Result { + fn ls(&self, _args: LsArgs, _context: &RunnableContext) -> Result { let output = self .commands() .into_iter() diff --git a/crates/nu-cli/src/shell/shell.rs b/crates/nu-cli/src/shell/shell.rs index da0432a1f5..c38d9b4b5b 100644 --- a/crates/nu-cli/src/shell/shell.rs +++ b/crates/nu-cli/src/shell/shell.rs @@ -14,11 +14,7 @@ pub trait Shell: std::fmt::Debug { fn name(&self) -> String; fn homedir(&self) -> Option; - fn ls( - &self, - args: LsArgs, - context: &RunnablePerItemContext, - ) -> Result; + fn ls(&self, args: LsArgs, context: &RunnableContext) -> Result; fn cd(&self, args: CdArgs, name: Tag) -> Result; fn cp(&self, args: CopyArgs, name: Tag, path: &str) -> Result; fn mkdir(&self, args: MkdirArgs, name: Tag, path: &str) -> Result; diff --git a/crates/nu-cli/src/shell/shell_manager.rs b/crates/nu-cli/src/shell/shell_manager.rs index 8ad03c1f91..8c635f1488 100644 --- a/crates/nu-cli/src/shell/shell_manager.rs +++ b/crates/nu-cli/src/shell/shell_manager.rs @@ -1,5 +1,5 @@ use crate::commands::cd::CdArgs; -use crate::commands::command::{EvaluatedWholeStreamCommandArgs, RunnablePerItemContext}; +use crate::commands::command::EvaluatedWholeStreamCommandArgs; use crate::commands::cp::CopyArgs; use crate::commands::ls::LsArgs; use crate::commands::mkdir::MkdirArgs; @@ -132,11 +132,7 @@ impl ShellManager { env[self.current_shell()].homedir() } - pub fn ls( - &self, - args: LsArgs, - context: &RunnablePerItemContext, - ) -> Result { + pub fn ls(&self, args: LsArgs, context: &RunnableContext) -> Result { let env = self.shells.lock(); env[self.current_shell()].ls(args, context) @@ -151,7 +147,7 @@ impl ShellManager { pub fn cp( &self, args: CopyArgs, - context: &RunnablePerItemContext, + context: &RunnableContext, ) -> Result { let shells = self.shells.lock(); @@ -162,7 +158,7 @@ impl ShellManager { pub fn rm( &self, args: RemoveArgs, - context: &RunnablePerItemContext, + context: &RunnableContext, ) -> Result { let shells = self.shells.lock(); @@ -173,7 +169,7 @@ impl ShellManager { pub fn mkdir( &self, args: MkdirArgs, - context: &RunnablePerItemContext, + context: &RunnableContext, ) -> Result { let shells = self.shells.lock(); @@ -184,7 +180,7 @@ impl ShellManager { pub fn mv( &self, args: MoveArgs, - context: &RunnablePerItemContext, + context: &RunnableContext, ) -> Result { let shells = self.shells.lock(); diff --git a/crates/nu-cli/src/shell/value_shell.rs b/crates/nu-cli/src/shell/value_shell.rs index 8ecd6a306e..ab54e6ffa4 100644 --- a/crates/nu-cli/src/shell/value_shell.rs +++ b/crates/nu-cli/src/shell/value_shell.rs @@ -95,7 +95,7 @@ impl Shell for ValueShell { fn ls( &self, LsArgs { path, .. }: LsArgs, - context: &RunnablePerItemContext, + context: &RunnableContext, ) -> Result { let mut full_path = PathBuf::from(self.path()); let name_tag = context.name.clone(); diff --git a/crates/nu-protocol/src/hir.rs b/crates/nu-protocol/src/hir.rs index ea04e6e488..712c59fafd 100644 --- a/crates/nu-protocol/src/hir.rs +++ b/crates/nu-protocol/src/hir.rs @@ -77,6 +77,36 @@ pub enum ClassifiedCommand { Error(ParseError), } +impl ClassifiedCommand { + pub fn has_it_iteration(&self) -> bool { + match self { + ClassifiedCommand::Internal(command) => { + if let SpannedExpression { + expr: Expression::Literal(Literal::String(s)), + .. + } = &*command.args.head + { + if s == "run_external" { + // For now, don't it-expand externals + return false; + } + } + let mut result = command.args.head.has_shallow_it_usage(); + + if let Some(positionals) = &command.args.positional { + for arg in positionals { + result = result || arg.has_shallow_it_usage(); + } + } + + result + } + ClassifiedCommand::Expr(expr) => expr.has_shallow_it_usage(), + _ => false, + } + } +} + #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] pub struct Commands { pub list: Vec, @@ -91,6 +121,37 @@ impl Commands { pub fn push(&mut self, command: ClassifiedCommand) { self.list.push(command); } + + /// Convert all shallow uses of $it to `each { use of $it }`, converting each to a per-row command + pub fn expand_it_usage(&mut self) { + for idx in 0..self.list.len() { + if self.list[idx].has_it_iteration() { + self.list[idx] = ClassifiedCommand::Internal(InternalCommand { + name: "each".to_string(), + name_span: self.span, + args: hir::Call { + head: Box::new(SpannedExpression { + expr: Expression::Synthetic(Synthetic::String("each".to_string())), + span: self.span, + }), + named: None, + span: self.span, + positional: Some(vec![SpannedExpression { + expr: Expression::Block(Block { + block: vec![Commands { + list: vec![self.list[idx].clone()], + span: self.span, + }], + span: self.span, + }), + span: self.span, + }]), + is_last: false, // FIXME + }, + }) + } + } + } } #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] @@ -110,6 +171,13 @@ impl Block { pub fn push(&mut self, commands: Commands) { self.block.push(commands); } + + /// Convert all shallow uses of $it to `each { use of $it }`, converting each to a per-row command + pub fn expand_it_usage(&mut self) { + for commands in &mut self.block { + commands.expand_it_usage(); + } + } } #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)] @@ -201,14 +269,6 @@ pub enum Member { } impl Member { - // pub fn int(span: Span, source: &Text) -> Member { - // if let Ok(big_int) = BigInt::from_str(span.slice(source)) { - // Member::Int(big_int, span) - // } else { - // unreachable!("Internal error: could not convert text to BigInt as expected") - // } - // } - pub fn to_path_member(&self) -> PathMember { match self { //Member::String(outer, inner) => PathMember::string(inner.slice(source), *outer), @@ -459,6 +519,17 @@ impl SpannedExpression { _ => 0, } } + + pub fn has_shallow_it_usage(&self) -> bool { + match &self.expr { + Expression::Binary(binary) => { + binary.left.has_shallow_it_usage() || binary.right.has_shallow_it_usage() + } + Expression::Variable(Variable::It(_)) => true, + Expression::Path(path) => path.head.has_shallow_it_usage(), + _ => false, + } + } } impl std::ops::Deref for SpannedExpression {