Add first per-item commands

This commit is contained in:
Jonathan Turner 2019-08-15 05:02:39 +12:00
parent 79fd635b5f
commit 99b881e42f
7 changed files with 137 additions and 57 deletions

View file

@ -158,7 +158,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
//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),

View file

@ -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;

View file

@ -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);

View file

@ -28,7 +28,7 @@ impl ToDebug for UnevaluatedCallInfo {
}
impl UnevaluatedCallInfo {
fn evaluate(
pub fn evaluate(
self,
registry: &registry::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<Mutex<dyn Host>>,
@ -346,6 +346,7 @@ pub enum CommandAction {
AddSpanSource(Uuid, SpanSource),
Exit,
EnterShell(String),
EnterValueShell(Tagged<Value>),
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: &registry::CommandRegistry,
input: Tagged<Value>,
) -> Result<VecDeque<ReturnValue>, 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<dyn StaticCommand>),
PerItem(Arc<dyn PerItemCommand>),
}
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<OutputStream, ShellError> {
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<dyn PerItemCommand>,
args: CommandArgs,
registry: CommandRegistry,
) -> Result<OutputStream, ShellError> {
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(), &registry, x).unwrap())
.flatten();
Ok(out.to_output_stream())
}
}
#[allow(unused)]
@ -528,6 +574,10 @@ pub fn static_command(command: impl StaticCommand + 'static) -> Arc<Command> {
Arc::new(Command::Static(Arc::new(command)))
}
pub fn per_item_command(command: impl PerItemCommand + 'static) -> Arc<Command> {
Arc::new(Command::PerItem(Arc::new(command)))
}
#[allow(unused)]
pub fn filter(
name: &str,

View file

@ -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<OutputStream, ShellError> {
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: &registry::CommandRegistry,
input: Tagged<Value>,
) -> Result<VecDeque<ReturnValue>, 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(),
),
}
}
}

View file

@ -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: &registry::CommandRegistry,
) -> Result<OutputStream, ShellError> {
args.process(registry, run)?.run()
input: Tagged<Value>,
) -> Result<VecDeque<ReturnValue>, 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<OutputStream, ShellError> {
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())
}

View file

@ -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<Command>,
name_span: Span,