From 99b881e42f199b063e0915ca16eb9f2b5e6a3df7 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 15 Aug 2019 05:02:39 +1200 Subject: [PATCH] Add first per-item commands --- src/cli.rs | 5 +-- src/commands.rs | 5 +-- src/commands/classified.rs | 3 ++ src/commands/command.rs | 54 ++++++++++++++++++++++++++-- src/commands/enter.rs | 51 ++++++++++++++++++-------- src/commands/where_.rs | 74 +++++++++++++++++++------------------- src/context.rs | 2 +- 7 files changed, 137 insertions(+), 57 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index e09c3e92fe..93fccbf185 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -158,7 +158,7 @@ pub async fn cli() -> Result<(), Box> { command("cd", Box::new(cd::cd)), command("size", Box::new(size::size)), command("from-yaml", Box::new(from_yaml::from_yaml)), - command("enter", Box::new(enter::enter)), + //command("enter", Box::new(enter::enter)), command("nth", Box::new(nth::nth)), command("n", Box::new(next::next)), command("p", Box::new(prev::prev)), @@ -182,9 +182,10 @@ pub async fn cli() -> Result<(), Box> { //static_command(Cd), static_command(Remove), static_command(Open), - static_command(Where), + per_item_command(Where), static_command(Config), static_command(SkipWhile), + per_item_command(Enter), static_command(Exit), static_command(Clip), static_command(Autoview), diff --git a/src/commands.rs b/src/commands.rs index f9b0aab82f..e2c3aebd22 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -57,12 +57,13 @@ crate use autoview::Autoview; //crate use cd::Cd; crate use clip::Clip; crate use command::{ - command, static_command, Command, CommandArgs, RawCommandArgs, StaticCommand, - UnevaluatedCallInfo, + command, per_item_command, static_command, Command, CommandArgs, PerItemCommand, + RawCommandArgs, StaticCommand, UnevaluatedCallInfo, }; crate use config::Config; crate use cp::Copycp; crate use date::Date; +crate use enter::Enter; crate use exit::Exit; crate use get::Get; crate use mkdir::Mkdir; diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 53ce51733b..ef9bbd8835 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -141,6 +141,9 @@ impl InternalCommand { context.add_span_source(uuid, span_source); } CommandAction::Exit => std::process::exit(0), + CommandAction::EnterValueShell(value) => { + context.shell_manager.push(Box::new(ValueShell::new(value))); + } CommandAction::EnterShell(location) => { let path = std::path::Path::new(&location); diff --git a/src/commands/command.rs b/src/commands/command.rs index 8a871bea40..e90cd05cb8 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -28,7 +28,7 @@ impl ToDebug for UnevaluatedCallInfo { } impl UnevaluatedCallInfo { - fn evaluate( + pub fn evaluate( self, registry: ®istry::CommandRegistry, scope: &Scope, @@ -62,7 +62,7 @@ pub struct CommandArgs { pub input: InputStream, } -#[derive(Getters)] +#[derive(Getters, Clone)] #[get = "crate"] pub struct RawCommandArgs { pub host: Arc>, @@ -346,6 +346,7 @@ pub enum CommandAction { AddSpanSource(Uuid, SpanSource), Exit, EnterShell(String), + EnterValueShell(Tagged), PreviousShell, NextShell, LeaveShell, @@ -405,20 +406,44 @@ pub trait StaticCommand: Send + Sync { } } +pub trait PerItemCommand: Send + Sync { + fn name(&self) -> &str; + + fn run( + &self, + args: RawCommandArgs, + registry: ®istry::CommandRegistry, + input: Tagged, + ) -> Result, ShellError>; + + fn signature(&self) -> Signature { + Signature { + name: self.name().to_string(), + positional: vec![], + rest_positional: true, + named: indexmap::IndexMap::new(), + is_filter: true, + } + } +} + pub enum Command { Static(Arc), + PerItem(Arc), } impl Command { pub fn name(&self) -> &str { match self { Command::Static(command) => command.name(), + Command::PerItem(command) => command.name(), } } pub fn signature(&self) -> Signature { match self { Command::Static(command) => command.signature(), + Command::PerItem(command) => command.signature(), } } @@ -429,8 +454,29 @@ impl Command { ) -> Result { match self { Command::Static(command) => command.run(args, registry), + Command::PerItem(command) => self.run_helper(command.clone(), args, registry.clone()), } } + + fn run_helper( + &self, + command: Arc, + args: CommandArgs, + registry: CommandRegistry, + ) -> Result { + let raw_args = RawCommandArgs { + host: args.host, + shell_manager: args.shell_manager, + call_info: args.call_info, + }; + let out = args + .input + .values + .map(move |x| command.run(raw_args.clone(), ®istry, x).unwrap()) + .flatten(); + + Ok(out.to_output_stream()) + } } #[allow(unused)] @@ -528,6 +574,10 @@ pub fn static_command(command: impl StaticCommand + 'static) -> Arc { Arc::new(Command::Static(Arc::new(command))) } +pub fn per_item_command(command: impl PerItemCommand + 'static) -> Arc { + Arc::new(Command::PerItem(Arc::new(command))) +} + #[allow(unused)] pub fn filter( name: &str, diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 344134ffc4..f0d0160d3a 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -1,23 +1,46 @@ use crate::commands::command::CommandAction; +use crate::commands::{PerItemCommand, RawCommandArgs}; use crate::errors::ShellError; +use crate::evaluate::Scope; +use crate::parser::registry; use crate::prelude::*; -pub fn enter(args: CommandArgs, registry: &CommandRegistry) -> Result { - let args = args.evaluate_once(registry)?; +pub struct Enter; - //TODO: We could also enter a value in the stream - if args.len() == 0 { - return Err(ShellError::labeled_error( - "Enter requires a path", - "needs parameter", - args.call_info.name_span, - )); +impl PerItemCommand for Enter { + fn name(&self) -> &str { + "enter" } - let location = args.expect_nth(0)?.as_string()?; + fn signature(&self) -> registry::Signature { + Signature::build("enter").required("location", SyntaxType::Block) + } - Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell( - location, - )))] - .into()) + fn run( + &self, + args: RawCommandArgs, + registry: ®istry::CommandRegistry, + input: Tagged, + ) -> Result, ShellError> { + let call_info = args + .call_info + .evaluate(registry, &Scope::it_value(input)) + .unwrap(); + + match call_info.args.expect_nth(0)? { + Tagged { + item: Value::Primitive(Primitive::String(location)), + .. + } => Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell( + location.to_string(), + )))] + .into()), + x => Ok( + vec![Ok(ReturnSuccess::Action(CommandAction::EnterValueShell( + x.clone(), + )))] + .into(), + ), + } + } } diff --git a/src/commands/where_.rs b/src/commands/where_.rs index b5f5aaa5ee..ff16001c44 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -1,21 +1,12 @@ -use crate::commands::StaticCommand; +use crate::commands::{PerItemCommand, RawCommandArgs}; use crate::errors::ShellError; -use crate::object::base as value; use crate::parser::hir::SyntaxType; use crate::parser::registry; use crate::prelude::*; -use futures::future::ready; -use serde::Deserialize; - pub struct Where; -#[derive(Deserialize)] -struct WhereArgs { - condition: value::Block, -} - -impl StaticCommand for Where { +impl PerItemCommand for Where { fn name(&self) -> &str { "where" } @@ -26,31 +17,42 @@ impl StaticCommand for Where { fn run( &self, - args: CommandArgs, + args: RawCommandArgs, registry: ®istry::CommandRegistry, - ) -> Result { - args.process(registry, run)?.run() + input: Tagged, + ) -> Result, ShellError> { + let input_clone = input.clone(); + let call_info = args + .with_input(vec![input]) + .evaluate_once(registry) + .unwrap(); + let condition = &call_info.args.call_info.args.positional.unwrap()[0]; + match condition { + Tagged { + item: Value::Block(block), + tag, + } => { + let result = block.invoke(&input_clone); + match result { + Ok(v) => { + if v.is_true() { + Ok(VecDeque::from(vec![Ok(ReturnSuccess::Value(input_clone))])) + } else { + Ok(VecDeque::new()) + } + } + Err(e) => Err(ShellError::labeled_error( + format!("Could not evaluate ({})", e.to_string()), + "could not evaluate", + tag.span, + )), + } + } + Tagged { tag, .. } => Err(ShellError::labeled_error( + "Expected a condition", + "where needs a condition", + tag.span, + )), + } } } - -fn run( - WhereArgs { condition }: WhereArgs, - context: RunnableContext, -) -> Result { - Ok(context - .input - .values - .filter_map(move |item| { - let result = condition.invoke(&item); - - let return_value = match result { - Err(err) => Some(Err(err)), - Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))), - _ => None, - }; - - ready(return_value) - }) - .boxed() - .to_output_stream()) -} diff --git a/src/context.rs b/src/context.rs index f707a625f9..93516510dd 100644 --- a/src/context.rs +++ b/src/context.rs @@ -115,7 +115,7 @@ impl Context { self.registry.get_command(name).unwrap() } - crate async fn run_command( + crate async fn run_command<'a>( &mut self, command: Arc, name_span: Span,