Simplify commands

This commit is contained in:
Yehuda Katz 2019-05-15 17:21:46 -07:00
parent af1963d148
commit 6b82e3a8a8
11 changed files with 90 additions and 179 deletions

View file

@ -1,42 +1,22 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
use derive_new::new; use derive_new::new;
use std::path::PathBuf;
#[derive(new)] #[derive(new)]
pub struct CdBlueprint; pub struct Cd;
impl crate::CommandBlueprint for CdBlueprint { impl crate::Command for Cd {
fn create( fn run(&self, args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> {
&self, let target = match args.args.first() {
args: Vec<Value>,
_host: &dyn Host,
env: &mut Environment,
) -> Result<Box<dyn Command>, ShellError> {
let target = match args.first() {
// TODO: This needs better infra // TODO: This needs better infra
None => return Err(ShellError::string(format!("cd must take one arg"))), None => return Err(ShellError::string(format!("cd must take one arg"))),
Some(v) => v.as_string()?.clone(), Some(v) => v.as_string()?.clone(),
}; };
Ok(Box::new(Cd { let cwd = args.env.cwd().to_path_buf();
cwd: env.cwd().to_path_buf(),
target,
}))
}
}
#[derive(new)]
pub struct Cd {
cwd: PathBuf,
target: String,
}
impl crate::Command for Cd {
fn run(&mut self, _stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> {
let mut stream = VecDeque::new(); let mut stream = VecDeque::new();
let path = dunce::canonicalize(self.cwd.join(&self.target).as_path())?; let path = dunce::canonicalize(cwd.join(&target).as_path())?;
stream.push_back(ReturnValue::change_cwd(path)); stream.push_back(ReturnValue::change_cwd(path));
Ok(stream) Ok(stream)
} }

View file

@ -3,6 +3,13 @@ use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
pub struct CommandArgs<'caller> {
pub host: &'caller dyn Host,
pub env: &'caller crate::Environment,
pub args: Vec<Value>,
pub input: VecDeque<Value>,
}
pub trait CommandBlueprint { pub trait CommandBlueprint {
fn create( fn create(
&self, &self,
@ -36,5 +43,5 @@ impl ReturnValue {
} }
pub trait Command { pub trait Command {
fn run(&mut self, stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError>; fn run(&self, args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError>;
} }

View file

@ -1,35 +1,17 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::{dir_entry_dict, Value}; use crate::object::{dir_entry_dict, Value};
use crate::prelude::*; use crate::prelude::*;
use crate::Command;
use derive_new::new; use derive_new::new;
use std::path::PathBuf;
#[derive(new)] #[derive(new)]
pub struct LsBlueprint; pub struct Ls;
impl crate::CommandBlueprint for LsBlueprint {
fn create(
&self,
_args: Vec<Value>,
_host: &dyn crate::Host,
env: &mut crate::Environment,
) -> Result<Box<dyn Command>, ShellError> {
Ok(Box::new(Ls {
cwd: env.cwd().to_path_buf(),
}))
}
}
#[derive(new)]
pub struct Ls {
cwd: PathBuf,
}
impl crate::Command for Ls { impl crate::Command for Ls {
fn run(&mut self, _stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> { fn run(&self, args: CommandArgs<'value>) -> Result<VecDeque<ReturnValue>, ShellError> {
let cwd = args.env.cwd().to_path_buf();
let entries = let entries =
std::fs::read_dir(&self.cwd).map_err(|e| ShellError::string(format!("{:?}", e)))?; std::fs::read_dir(&cwd).map_err(|e| ShellError::string(format!("{:?}", e)))?;
let mut shell_entries = VecDeque::new(); let mut shell_entries = VecDeque::new();

View file

@ -2,29 +2,14 @@ use crate::errors::ShellError;
use crate::object::process::process_dict; use crate::object::process::process_dict;
use crate::object::Value; use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
use crate::Command;
use derive_new::new; use derive_new::new;
use sysinfo::SystemExt; use sysinfo::SystemExt;
#[derive(new)]
pub struct PsBlueprint;
impl crate::CommandBlueprint for PsBlueprint {
fn create(
&self,
_args: Vec<Value>,
_host: &dyn crate::Host,
_env: &mut crate::Environment,
) -> Result<Box<dyn Command>, ShellError> {
Ok(Box::new(Ps::new()))
}
}
#[derive(new)] #[derive(new)]
pub struct Ps; pub struct Ps;
impl crate::Command for Ps { impl crate::Command for Ps {
fn run(&mut self, _stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> { fn run(&self, _args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> {
let mut system = sysinfo::System::new(); let mut system = sysinfo::System::new();
system.refresh_all(); system.refresh_all();

View file

@ -5,35 +5,21 @@ use crate::prelude::*;
use derive_new::new; use derive_new::new;
#[derive(new)] #[derive(new)]
pub struct RejectBlueprint; pub struct Reject;
impl crate::CommandBlueprint for RejectBlueprint {
fn create(
&self,
args: Vec<Value>,
_host: &dyn Host,
_env: &mut Environment,
) -> Result<Box<dyn Command>, ShellError> {
if args.is_empty() {
return Err(ShellError::string("take requires an integer"));
}
let fields: Result<_, _> = args.iter().map(|a| a.as_string()).collect();
Ok(Box::new(Reject { fields: fields? }))
}
}
#[derive(new)]
pub struct Reject {
fields: Vec<String>,
}
impl crate::Command for Reject { impl crate::Command for Reject {
fn run(&mut self, stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> { fn run(&self, args: CommandArgs<'value>) -> Result<VecDeque<ReturnValue>, ShellError> {
let objects = stream if args.args.is_empty() {
return Err(ShellError::string("select requires a field"));
}
let fields: Result<Vec<String>, _> = args.args.iter().map(|a| a.as_string()).collect();
let fields = fields?;
let objects = args
.input
.iter() .iter()
.map(|item| Value::Object(reject(item, &self.fields))) .map(|item| Value::Object(reject(item, &fields)))
.map(|item| ReturnValue::Value(item)) .map(|item| ReturnValue::Value(item))
.collect(); .collect();

View file

@ -5,35 +5,21 @@ use crate::prelude::*;
use derive_new::new; use derive_new::new;
#[derive(new)] #[derive(new)]
pub struct SelectBlueprint; pub struct Select;
impl crate::CommandBlueprint for SelectBlueprint {
fn create(
&self,
args: Vec<Value>,
_host: &dyn Host,
_env: &mut Environment,
) -> Result<Box<dyn Command>, ShellError> {
if args.is_empty() {
return Err(ShellError::string("take requires an integer"));
}
let fields: Result<_, _> = args.iter().map(|a| a.as_string()).collect();
Ok(Box::new(Select { fields: fields? }))
}
}
#[derive(new)]
pub struct Select {
fields: Vec<String>,
}
impl crate::Command for Select { impl crate::Command for Select {
fn run(&mut self, stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> { fn run(&self, args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> {
let objects = stream if args.args.is_empty() {
return Err(ShellError::string("select requires a field"));
}
let fields: Result<Vec<String>, _> = args.args.iter().map(|a| a.as_string()).collect();
let fields = fields?;
let objects = args
.input
.iter() .iter()
.map(|item| Value::Object(select(item, &self.fields))) .map(|item| Value::Object(select(item, &fields)))
.map(|item| ReturnValue::Value(item)) .map(|item| ReturnValue::Value(item))
.collect(); .collect();

View file

@ -1,42 +1,24 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
use derive_new::new; use derive_new::new;
#[derive(new)] #[derive(new)]
pub struct TakeBlueprint; pub struct Take;
impl crate::CommandBlueprint for TakeBlueprint { // TODO: "Amount remaining" wrapper
fn create(
&self,
args: Vec<Value>,
_host: &dyn Host,
_env: &mut Environment,
) -> Result<Box<dyn Command>, ShellError> {
if args.is_empty() {
return Err(ShellError::string("take requires an integer"));
}
let amount = args[0].as_int()?;
Ok(Box::new(Take { amount }))
}
}
#[derive(new)]
pub struct Take {
amount: i64,
}
impl crate::Command for Take { impl crate::Command for Take {
fn run(&mut self, stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> { fn run(&self, args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> {
let amount = if stream.len() > self.amount as usize { let amount = args.args[0].as_int()?;
self.amount as usize
let amount = if args.input.len() > amount as usize {
amount as usize
} else { } else {
stream.len() args.input.len()
}; };
let out: VecDeque<ReturnValue> = stream let out: VecDeque<ReturnValue> = args
.input
.into_iter() .into_iter()
.take(amount) .take(amount)
.map(|v| ReturnValue::Value(v)) .map(|v| ReturnValue::Value(v))

View file

@ -3,26 +3,12 @@ use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
use derive_new::new; use derive_new::new;
#[derive(new)]
pub struct ToArrayBlueprint;
impl crate::CommandBlueprint for ToArrayBlueprint {
fn create(
&self,
_args: Vec<Value>,
_host: &dyn Host,
_env: &mut Environment,
) -> Result<Box<dyn Command>, ShellError> {
Ok(Box::new(ToArray))
}
}
#[derive(new)] #[derive(new)]
pub struct ToArray; pub struct ToArray;
impl crate::Command for ToArray { impl crate::Command for ToArray {
fn run(&mut self, stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> { fn run(&self, args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> {
let out = stream.into_iter().collect(); let out = args.input.into_iter().collect();
Ok(ReturnValue::single(Value::List(out))) Ok(ReturnValue::single(Value::List(out)))
} }
} }

View file

@ -1,8 +1,9 @@
use crate::prelude::*; use crate::prelude::*;
use std::error::Error; use std::error::Error;
pub struct Context { pub struct Context {
commands: indexmap::IndexMap<String, Box<dyn crate::CommandBlueprint>>, commands: indexmap::IndexMap<String, Box<dyn crate::Command>>,
crate host: Box<dyn crate::Host>, crate host: Box<dyn crate::Host>,
crate env: Environment, crate env: Environment,
} }
@ -16,7 +17,7 @@ impl Context {
}) })
} }
pub fn add_commands(&mut self, commands: Vec<(&str, Box<dyn crate::CommandBlueprint>)>) { pub fn add_commands(&mut self, commands: Vec<(&str, Box<dyn crate::Command>)>) {
for (name, command) in commands { for (name, command) in commands {
self.commands.insert(name.to_string(), command); self.commands.insert(name.to_string(), command);
} }
@ -26,14 +27,25 @@ impl Context {
self.commands.contains_key(name) self.commands.contains_key(name)
} }
crate fn create_command( crate fn run_command(
&mut self, &self,
name: &str, name: &str,
arg_list: Vec<Value>, arg_list: Vec<Value>,
) -> Result<Box<dyn Command>, ShellError> { input: VecDeque<Value>,
) -> Result<VecDeque<ReturnValue>, ShellError> {
let command_args = CommandArgs {
host: &self.host,
env: &self.env,
args: arg_list,
input,
};
match self.commands.get(name) { match self.commands.get(name) {
Some(command) => Ok(command.create(arg_list, &self.host, &mut self.env)?), None => Err(ShellError::string(format!(
None => Err(ShellError::string(format!("Missing command {}", name))), "Command {} did not exist",
name
))),
Some(command) => command.run(command_args),
} }
} }
} }

View file

@ -1,6 +1,9 @@
#![feature(crate_visibility_modifier)] #![feature(crate_visibility_modifier)]
#![feature(in_band_lifetimes)] #![feature(in_band_lifetimes)]
#[allow(unused)]
use crate::prelude::*;
mod commands; mod commands;
mod context; mod context;
mod env; mod env;
@ -53,13 +56,13 @@ fn main() -> Result<(), Box<Error>> {
use crate::commands::*; use crate::commands::*;
context.lock().unwrap().add_commands(vec![ context.lock().unwrap().add_commands(vec![
("ps", Box::new(ps::PsBlueprint)), ("ps", Box::new(ps::Ps)),
("ls", Box::new(ls::LsBlueprint)), ("ls", Box::new(ls::Ls)),
("cd", Box::new(cd::CdBlueprint)), ("cd", Box::new(cd::Cd)),
("take", Box::new(take::TakeBlueprint)), ("take", Box::new(take::Take)),
("select", Box::new(select::SelectBlueprint)), ("select", Box::new(select::Select)),
("reject", Box::new(reject::RejectBlueprint)), ("reject", Box::new(reject::Reject)),
("to-array", Box::new(to_array::ToArrayBlueprint)), ("to-array", Box::new(to_array::ToArray)),
]); ]);
} }
@ -140,9 +143,11 @@ fn process_command(
match ctx.has_command(*command) { match ctx.has_command(*command) {
true => { true => {
let mut instance = ctx.create_command(command, arg_list)?; // let mut instance = ctx.create_command(command, arg_list)?;
let result = instance.run(input)?; let result = ctx.run_command(command, arg_list, input)?;
// let result = command.run(input_args)?;
let mut next = VecDeque::new(); let mut next = VecDeque::new();
for v in result { for v in result {

View file

@ -1,4 +1,4 @@
crate use crate::commands::command::{Command, ReturnValue}; crate use crate::commands::command::{CommandArgs, ReturnValue};
crate use crate::env::{Environment, Host}; crate use crate::env::{Environment, Host};
crate use crate::errors::ShellError; crate use crate::errors::ShellError;
crate use crate::object::{Primitive, Value}; crate use crate::object::{Primitive, Value};