mirror of
https://github.com/nushell/nushell
synced 2024-12-26 13:03:07 +00:00
Add sinks
This commit is contained in:
parent
4582822d62
commit
090ec031a9
7 changed files with 182 additions and 102 deletions
168
src/cli.rs
168
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::prelude::*;
|
||||||
|
|
||||||
use crate::commands::classified::{
|
use crate::commands::classified::{
|
||||||
|
@ -7,17 +11,16 @@ use crate::commands::classified::{
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
crate use crate::errors::ShellError;
|
crate use crate::errors::ShellError;
|
||||||
use crate::evaluate::Scope;
|
use crate::evaluate::Scope;
|
||||||
crate use crate::format::{EntriesListView, GenericView};
|
|
||||||
use crate::git::current_branch;
|
use crate::git::current_branch;
|
||||||
use crate::object::Value;
|
use crate::object::Value;
|
||||||
use crate::parser::ast::{Expression, Leaf, RawExpression};
|
use crate::parser::ast::{Expression, Leaf, RawExpression};
|
||||||
use crate::parser::{Args, Pipeline};
|
use crate::parser::{Args, Pipeline};
|
||||||
use crate::stream::empty_stream;
|
|
||||||
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use rustyline::{self, ColorMode, Config, Editor};
|
use rustyline::{self, ColorMode, Config, Editor};
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
@ -44,7 +47,6 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
use crate::commands::*;
|
use crate::commands::*;
|
||||||
|
|
||||||
context.add_commands(vec![
|
context.add_commands(vec![
|
||||||
command("format-list", format_list),
|
|
||||||
command("ps", ps::ps),
|
command("ps", ps::ps),
|
||||||
command("ls", ls::ls),
|
command("ls", ls::ls),
|
||||||
command("cd", cd::cd),
|
command("cd", cd::cd),
|
||||||
|
@ -69,6 +71,8 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
Arc::new(Config),
|
Arc::new(Config),
|
||||||
command("sort-by", sort_by::sort_by),
|
command("sort-by", sort_by::sort_by),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
context.add_sinks(vec![sink("autoview", autoview::autoview)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = Config::builder().color_mode(ColorMode::Forced).build();
|
let config = Config::builder().color_mode(ColorMode::Forced).build();
|
||||||
|
@ -212,7 +216,18 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
||||||
debug!("=== Parsed ===");
|
debug!("=== Parsed ===");
|
||||||
debug!("{:#?}", result);
|
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();
|
let mut input = ClassifiedInputStream::new();
|
||||||
|
|
||||||
|
@ -237,9 +252,30 @@ async fn process_line(readline: Result<String, ReadlineError>, 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<Value> = input.objects.collect().await;
|
||||||
|
left.run(
|
||||||
|
ctx,
|
||||||
|
input_vec,
|
||||||
|
)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
Some(ClassifiedCommand::Internal(left)),
|
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 {
|
) => match left.run(ctx, input).await {
|
||||||
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
||||||
Err(err) => return LineResult::Error(err),
|
Err(err) => return LineResult::Error(err),
|
||||||
|
@ -260,17 +296,9 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
||||||
Err(err) => return LineResult::Error(err),
|
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::External(left)),
|
||||||
Some(ClassifiedCommand::Internal(_)),
|
Some(_),
|
||||||
) => match left.run(ctx, input, StreamNext::Internal).await {
|
) => match left.run(ctx, input, StreamNext::Internal).await {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(err) => return LineResult::Error(err),
|
Err(err) => return LineResult::Error(err),
|
||||||
|
@ -285,21 +313,6 @@ async fn process_line(readline: Result<String, ReadlineError>, 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::<Vec<_>>().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LineResult::Success(line.to_string())
|
LineResult::Success(line.to_string())
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Interrupted) => LineResult::Error(ShellError::string("CTRL-C")),
|
Err(ReadlineError::Interrupted) => LineResult::Error(ShellError::string("CTRL-C")),
|
||||||
|
@ -365,17 +378,31 @@ fn classify_command(
|
||||||
args,
|
args,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
false => {
|
false => match context.has_sink(&name.to_string()) {
|
||||||
let arg_list_strings: Vec<String> = match args {
|
true => {
|
||||||
Some(args) => args.iter().map(|i| i.as_external_arg()).collect(),
|
let command = context.get_sink(&name.to_string());
|
||||||
None => vec![],
|
let config = command.config();
|
||||||
};
|
let scope = Scope::empty();
|
||||||
|
|
||||||
Ok(ClassifiedCommand::External(ExternalCommand {
|
let args = match args {
|
||||||
name: name.to_string(),
|
Some(args) => config.evaluate_args(args.iter(), &scope)?,
|
||||||
args: arg_list_strings,
|
None => Args::default(),
|
||||||
}))
|
};
|
||||||
}
|
|
||||||
|
Ok(ClassifiedCommand::Sink(SinkCommand { command, args }))
|
||||||
|
}
|
||||||
|
false => {
|
||||||
|
let arg_list_strings: Vec<String> = 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(
|
(_, 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::<Vec<_>>();
|
|
||||||
|
|
||||||
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<OutputStream, ShellError> {
|
|
||||||
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<Value>) -> 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
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
crate mod args;
|
crate mod args;
|
||||||
|
crate mod autoview;
|
||||||
crate mod cd;
|
crate mod cd;
|
||||||
crate mod classified;
|
crate mod classified;
|
||||||
crate mod command;
|
crate mod command;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::commands::command::Sink;
|
||||||
use crate::parser::ast::Expression;
|
use crate::parser::ast::Expression;
|
||||||
use crate::parser::registry::Args;
|
use crate::parser::registry::Args;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
@ -79,9 +80,21 @@ crate enum ClassifiedCommand {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
Expr(Expression),
|
Expr(Expression),
|
||||||
Internal(InternalCommand),
|
Internal(InternalCommand),
|
||||||
|
Sink(SinkCommand),
|
||||||
External(ExternalCommand),
|
External(ExternalCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crate struct SinkCommand {
|
||||||
|
crate command: Arc<dyn Sink>,
|
||||||
|
crate args: Args,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SinkCommand {
|
||||||
|
crate fn run(self, context: &mut Context, input: Vec<Value>) -> Result<(), ShellError> {
|
||||||
|
context.run_sink(self.command, self.args, input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
crate struct InternalCommand {
|
crate struct InternalCommand {
|
||||||
crate command: Arc<dyn Command>,
|
crate command: Arc<dyn Command>,
|
||||||
crate args: Args,
|
crate args: Args,
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::errors::ShellError;
|
||||||
use crate::object::Value;
|
use crate::object::Value;
|
||||||
use crate::parser::CommandConfig;
|
use crate::parser::CommandConfig;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use core::future::Future;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub struct CommandArgs {
|
pub struct CommandArgs {
|
||||||
|
@ -28,6 +29,28 @@ impl CommandArgs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SinkCommandArgs {
|
||||||
|
pub ctx: Context,
|
||||||
|
pub positional: Vec<Value>,
|
||||||
|
pub named: indexmap::IndexMap<String, Value>,
|
||||||
|
pub input: Vec<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SinkCommandArgs {
|
||||||
|
crate fn from_context(
|
||||||
|
ctx: &'caller mut Context,
|
||||||
|
positional: Vec<Value>,
|
||||||
|
input: Vec<Value>,
|
||||||
|
) -> SinkCommandArgs {
|
||||||
|
SinkCommandArgs {
|
||||||
|
ctx: ctx.clone(),
|
||||||
|
positional,
|
||||||
|
named: indexmap::IndexMap::default(),
|
||||||
|
input,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum CommandAction {
|
pub enum CommandAction {
|
||||||
ChangeCwd(PathBuf),
|
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 {
|
pub struct FnCommand {
|
||||||
name: String,
|
name: String,
|
||||||
func: fn(CommandArgs) -> Result<OutputStream, ShellError>,
|
func: fn(CommandArgs) -> Result<OutputStream, ShellError>,
|
||||||
|
@ -84,3 +122,25 @@ pub fn command(
|
||||||
func,
|
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<dyn Sink> {
|
||||||
|
Arc::new(FnSink {
|
||||||
|
name: name.to_string(),
|
||||||
|
func,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::commands::command::Sink;
|
||||||
|
use crate::commands::command::SinkCommandArgs;
|
||||||
use crate::parser::Args;
|
use crate::parser::Args;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
@ -5,8 +7,10 @@ use indexmap::IndexMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
commands: IndexMap<String, Arc<dyn Command>>,
|
commands: IndexMap<String, Arc<dyn Command>>,
|
||||||
|
sinks: IndexMap<String, Arc<dyn Sink>>,
|
||||||
crate host: Arc<Mutex<dyn Host + Send>>,
|
crate host: Arc<Mutex<dyn Host + Send>>,
|
||||||
crate env: Arc<Mutex<Environment>>,
|
crate env: Arc<Mutex<Environment>>,
|
||||||
}
|
}
|
||||||
|
@ -15,6 +19,7 @@ impl Context {
|
||||||
crate fn basic() -> Result<Context, Box<dyn Error>> {
|
crate fn basic() -> Result<Context, Box<dyn Error>> {
|
||||||
Ok(Context {
|
Ok(Context {
|
||||||
commands: indexmap::IndexMap::new(),
|
commands: indexmap::IndexMap::new(),
|
||||||
|
sinks: indexmap::IndexMap::new(),
|
||||||
host: Arc::new(Mutex::new(crate::env::host::BasicHost)),
|
host: Arc::new(Mutex::new(crate::env::host::BasicHost)),
|
||||||
env: Arc::new(Mutex::new(Environment::basic()?)),
|
env: Arc::new(Mutex::new(Environment::basic()?)),
|
||||||
})
|
})
|
||||||
|
@ -26,6 +31,40 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_sinks(&mut self, sinks: Vec<Arc<dyn Sink>>) {
|
||||||
|
for sink in sinks {
|
||||||
|
self.sinks.insert(sink.name().to_string(), sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clone_sinks(&self) -> indexmap::IndexMap<String, Arc<dyn Sink>> {
|
||||||
|
self.sinks.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn has_sink(&self, name: &str) -> bool {
|
||||||
|
self.sinks.contains_key(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn get_sink(&self, name: &str) -> Arc<dyn Sink> {
|
||||||
|
self.sinks.get(name).unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn run_sink(
|
||||||
|
&mut self,
|
||||||
|
command: Arc<dyn Sink>,
|
||||||
|
args: Args,
|
||||||
|
input: Vec<Value>,
|
||||||
|
) -> 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<String, Arc<dyn Command>> {
|
pub fn clone_commands(&self) -> indexmap::IndexMap<String, Arc<dyn Command>> {
|
||||||
self.commands.clone()
|
self.commands.clone()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::prelude::*;
|
||||||
|
|
||||||
crate use entries::{EntriesListView, EntriesView};
|
crate use entries::{EntriesListView, EntriesView};
|
||||||
crate use generic::GenericView;
|
crate use generic::GenericView;
|
||||||
|
crate use list::ListView;
|
||||||
crate use table::TableView;
|
crate use table::TableView;
|
||||||
|
|
||||||
crate trait RenderView {
|
crate trait RenderView {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::format::{EntriesView, RenderView, TableView};
|
use crate::format::{EntriesView, ListView, RenderView, TableView};
|
||||||
use crate::object::Value;
|
use crate::object::Value;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
|
|
Loading…
Reference in a new issue