From 090ec031a971b4d1d2a98b3411a5660cef14e3d2 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 7 Jun 2019 18:34:42 +1200 Subject: [PATCH] Add sinks --- src/cli.rs | 168 +++++++++++++++---------------------- src/commands.rs | 1 + src/commands/classified.rs | 13 +++ src/commands/command.rs | 60 +++++++++++++ src/context.rs | 39 +++++++++ src/format.rs | 1 + src/format/generic.rs | 2 +- 7 files changed, 182 insertions(+), 102 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 528fd92e0f..58d8b7c7f5 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,3 +1,7 @@ +use crate::commands::autoview; +use crate::commands::classified::SinkCommand; +use crate::commands::command::sink; + use crate::prelude::*; use crate::commands::classified::{ @@ -7,17 +11,16 @@ use crate::commands::classified::{ use crate::context::Context; crate use crate::errors::ShellError; use crate::evaluate::Scope; -crate use crate::format::{EntriesListView, GenericView}; + use crate::git::current_branch; use crate::object::Value; use crate::parser::ast::{Expression, Leaf, RawExpression}; use crate::parser::{Args, Pipeline}; -use crate::stream::empty_stream; use log::debug; use rustyline::error::ReadlineError; use rustyline::{self, ColorMode, Config, Editor}; -use std::collections::VecDeque; + use std::error::Error; use std::iter::Iterator; use std::sync::atomic::{AtomicBool, Ordering}; @@ -44,7 +47,6 @@ pub async fn cli() -> Result<(), Box> { use crate::commands::*; context.add_commands(vec![ - command("format-list", format_list), command("ps", ps::ps), command("ls", ls::ls), command("cd", cd::cd), @@ -69,6 +71,8 @@ pub async fn cli() -> Result<(), Box> { Arc::new(Config), command("sort-by", sort_by::sort_by), ]); + + context.add_sinks(vec![sink("autoview", autoview::autoview)]); } let config = Config::builder().color_mode(ColorMode::Forced).build(); @@ -212,7 +216,18 @@ async fn process_line(readline: Result, ctx: &mut Context debug!("=== Parsed ==="); debug!("{:#?}", result); - let pipeline = classify_pipeline(&result, ctx)?; + let mut pipeline = classify_pipeline(&result, ctx)?; + + match pipeline.commands.last() { + Some(ClassifiedCommand::Sink(_)) => {} + _ => pipeline.commands.push(ClassifiedCommand::Sink(SinkCommand { + command: sink("autoview", autoview::autoview), + args: Args { + positional: vec![], + named: indexmap::IndexMap::new(), + }, + })), + } let mut input = ClassifiedInputStream::new(); @@ -237,9 +252,30 @@ async fn process_line(readline: Result, ctx: &mut Context )) } + (Some(ClassifiedCommand::Sink(_)), Some(_)) => { + return LineResult::Error(ShellError::string("Commands like table, save, and autoview must come last in the pipeline")) + } + + (Some(ClassifiedCommand::Sink(left)), None) => { + let input_vec: Vec = input.objects.collect().await; + left.run( + ctx, + input_vec, + )?; + break; + } + ( Some(ClassifiedCommand::Internal(left)), - Some(ClassifiedCommand::Internal(_)), + Some(ClassifiedCommand::External(_)), + ) => match left.run(ctx, input).await { + Ok(val) => ClassifiedInputStream::from_input_stream(val), + Err(err) => return LineResult::Error(err), + }, + + ( + Some(ClassifiedCommand::Internal(left)), + Some(_), ) => match left.run(ctx, input).await { Ok(val) => ClassifiedInputStream::from_input_stream(val), Err(err) => return LineResult::Error(err), @@ -260,17 +296,9 @@ async fn process_line(readline: Result, ctx: &mut Context Err(err) => return LineResult::Error(err), }, - ( - Some(ClassifiedCommand::Internal(left)), - Some(ClassifiedCommand::External(_)), - ) => match left.run(ctx, input).await { - Ok(val) => ClassifiedInputStream::from_input_stream(val), - Err(err) => return LineResult::Error(err), - }, - ( Some(ClassifiedCommand::External(left)), - Some(ClassifiedCommand::Internal(_)), + Some(_), ) => match left.run(ctx, input, StreamNext::Internal).await { Ok(val) => val, Err(err) => return LineResult::Error(err), @@ -285,21 +313,6 @@ async fn process_line(readline: Result, ctx: &mut Context } } - let input_vec: VecDeque<_> = input.objects.collect().await; - - if input_vec.len() > 0 { - if equal_shapes(&input_vec) { - let array = crate::commands::stream_to_array(input_vec.boxed()).await; - let args = CommandArgs::from_context(ctx, vec![], array); - let mut result = format(args); - let mut vec = vec![]; - vec.send_all(&mut result).await?; - } else { - let args = CommandArgs::from_context(ctx, vec![], input_vec.boxed()); - format(args).collect::>().await; - } - } - LineResult::Success(line.to_string()) } Err(ReadlineError::Interrupted) => LineResult::Error(ShellError::string("CTRL-C")), @@ -365,17 +378,31 @@ fn classify_command( args, })) } - false => { - let arg_list_strings: Vec = match args { - Some(args) => args.iter().map(|i| i.as_external_arg()).collect(), - None => vec![], - }; + false => match context.has_sink(&name.to_string()) { + true => { + let command = context.get_sink(&name.to_string()); + let config = command.config(); + let scope = Scope::empty(); - Ok(ClassifiedCommand::External(ExternalCommand { - name: name.to_string(), - args: arg_list_strings, - })) - } + let args = match args { + Some(args) => config.evaluate_args(args.iter(), &scope)?, + None => Args::default(), + }; + + Ok(ClassifiedCommand::Sink(SinkCommand { command, args })) + } + false => { + let arg_list_strings: Vec = match args { + Some(args) => args.iter().map(|i| i.as_external_arg()).collect(), + None => vec![], + }; + + Ok(ClassifiedCommand::External(ExternalCommand { + name: name.to_string(), + args: arg_list_strings, + })) + } + }, }, (_, None) => Err(ShellError::string( @@ -390,64 +417,3 @@ fn classify_command( ))) } } - -crate fn format(args: CommandArgs) -> OutputStream { - let host = args.host.clone(); - let input = args.input.map(|a| a.copy()); - let input = input.collect::>(); - - input - .then(move |input| { - let last = input.len() - 1; - let mut host = host.lock().unwrap(); - for (i, item) in input.iter().enumerate() { - let view = GenericView::new(item); - - handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host)); - - if last != i { - host.stdout(""); - } - } - - futures::future::ready(empty_stream()) - }) - .flatten_stream() - .boxed() -} - -crate fn format_list(args: CommandArgs) -> Result { - let host = args.host.clone(); - - let view = EntriesListView::from_stream(args.input); - - Ok(view - .then(move |view| { - handle_unexpected(&mut *host.lock().unwrap(), |host| { - crate::format::print_view(&view, host) - }); - - futures::future::ready(empty_stream()) - }) - .flatten_stream() - .boxed()) -} - -fn equal_shapes(input: &VecDeque) -> bool { - let mut items = input.iter(); - - let item = match items.next() { - Some(item) => item, - None => return false, - }; - - let desc = item.data_descriptors(); - - for item in items { - if desc != item.data_descriptors() { - return false; - } - } - - true -} diff --git a/src/commands.rs b/src/commands.rs index b0b8a55c06..02c4b98cca 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,4 +1,5 @@ crate mod args; +crate mod autoview; crate mod cd; crate mod classified; crate mod command; diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 44452635be..e10496f6ad 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -1,3 +1,4 @@ +use crate::commands::command::Sink; use crate::parser::ast::Expression; use crate::parser::registry::Args; use crate::prelude::*; @@ -79,9 +80,21 @@ crate enum ClassifiedCommand { #[allow(unused)] Expr(Expression), Internal(InternalCommand), + Sink(SinkCommand), External(ExternalCommand), } +crate struct SinkCommand { + crate command: Arc, + crate args: Args, +} + +impl SinkCommand { + crate fn run(self, context: &mut Context, input: Vec) -> Result<(), ShellError> { + context.run_sink(self.command, self.args, input) + } +} + crate struct InternalCommand { crate command: Arc, crate args: Args, diff --git a/src/commands/command.rs b/src/commands/command.rs index 8ac5ee0618..cf98bb5ac7 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -2,6 +2,7 @@ use crate::errors::ShellError; use crate::object::Value; use crate::parser::CommandConfig; use crate::prelude::*; +use core::future::Future; use std::path::PathBuf; pub struct CommandArgs { @@ -28,6 +29,28 @@ impl CommandArgs { } } +pub struct SinkCommandArgs { + pub ctx: Context, + pub positional: Vec, + pub named: indexmap::IndexMap, + pub input: Vec, +} + +impl SinkCommandArgs { + crate fn from_context( + ctx: &'caller mut Context, + positional: Vec, + input: Vec, + ) -> SinkCommandArgs { + SinkCommandArgs { + ctx: ctx.clone(), + positional, + named: indexmap::IndexMap::default(), + input, + } + } +} + #[derive(Debug)] pub enum CommandAction { ChangeCwd(PathBuf), @@ -60,6 +83,21 @@ pub trait Command { } } +pub trait Sink { + fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError>; + fn name(&self) -> &str; + + fn config(&self) -> CommandConfig { + CommandConfig { + name: self.name().to_string(), + mandatory_positional: vec![], + optional_positional: vec![], + rest_positional: true, + named: indexmap::IndexMap::new(), + } + } +} + pub struct FnCommand { name: String, func: fn(CommandArgs) -> Result, @@ -84,3 +122,25 @@ pub fn command( func, }) } + +pub struct FnSink { + name: String, + func: fn(SinkCommandArgs) -> Result<(), ShellError>, +} + +impl Sink for FnSink { + fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError> { + (self.func)(args) + } + + fn name(&self) -> &str { + &self.name + } +} + +pub fn sink(name: &str, func: fn(SinkCommandArgs) -> Result<(), ShellError>) -> Arc { + Arc::new(FnSink { + name: name.to_string(), + func, + }) +} diff --git a/src/context.rs b/src/context.rs index 5c5bb4a8f2..b63252f767 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,3 +1,5 @@ +use crate::commands::command::Sink; +use crate::commands::command::SinkCommandArgs; use crate::parser::Args; use crate::prelude::*; @@ -5,8 +7,10 @@ use indexmap::IndexMap; use std::error::Error; use std::sync::Arc; +#[derive(Clone)] pub struct Context { commands: IndexMap>, + sinks: IndexMap>, crate host: Arc>, crate env: Arc>, } @@ -15,6 +19,7 @@ impl Context { crate fn basic() -> Result> { Ok(Context { commands: indexmap::IndexMap::new(), + sinks: indexmap::IndexMap::new(), host: Arc::new(Mutex::new(crate::env::host::BasicHost)), env: Arc::new(Mutex::new(Environment::basic()?)), }) @@ -26,6 +31,40 @@ impl Context { } } + pub fn add_sinks(&mut self, sinks: Vec>) { + for sink in sinks { + self.sinks.insert(sink.name().to_string(), sink); + } + } + + pub fn clone_sinks(&self) -> indexmap::IndexMap> { + self.sinks.clone() + } + + crate fn has_sink(&self, name: &str) -> bool { + self.sinks.contains_key(name) + } + + crate fn get_sink(&self, name: &str) -> Arc { + self.sinks.get(name).unwrap().clone() + } + + crate fn run_sink( + &mut self, + command: Arc, + args: Args, + input: Vec, + ) -> Result<(), ShellError> { + let command_args = SinkCommandArgs { + ctx: self.clone(), + positional: args.positional, + named: args.named, + input, + }; + + command.run(command_args) + } + pub fn clone_commands(&self) -> indexmap::IndexMap> { self.commands.clone() } diff --git a/src/format.rs b/src/format.rs index 8494697e64..7d3a522544 100644 --- a/src/format.rs +++ b/src/format.rs @@ -7,6 +7,7 @@ use crate::prelude::*; crate use entries::{EntriesListView, EntriesView}; crate use generic::GenericView; +crate use list::ListView; crate use table::TableView; crate trait RenderView { diff --git a/src/format/generic.rs b/src/format/generic.rs index da5954398f..5e4e430ecc 100644 --- a/src/format/generic.rs +++ b/src/format/generic.rs @@ -1,4 +1,4 @@ -use crate::format::{EntriesView, RenderView, TableView}; +use crate::format::{EntriesView, ListView, RenderView, TableView}; use crate::object::Value; use crate::prelude::*; use derive_new::new;