Add support for skip and where

This commit is contained in:
Jonathan Turner 2019-05-15 19:42:44 -07:00
parent 638093aad6
commit cbb86b0cab
6 changed files with 174 additions and 2 deletions

View file

@ -5,7 +5,9 @@ crate mod ls;
crate mod ps;
crate mod reject;
crate mod select;
crate mod skip;
crate mod take;
crate mod to_array;
crate mod where_;
crate use to_array::to_array;

29
src/commands/skip.rs Normal file
View file

@ -0,0 +1,29 @@
use crate::errors::ShellError;
use crate::prelude::*;
use derive_new::new;
#[derive(new)]
pub struct Skip;
// TODO: "Amount remaining" wrapper
impl crate::Command for Skip {
fn run(&self, args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> {
let amount = args.args[0].as_int()?;
let amount = if args.input.len() > amount as usize {
amount as usize
} else {
args.input.len()
};
let out: VecDeque<ReturnValue> = args
.input
.into_iter()
.skip(amount)
.map(|v| ReturnValue::Value(v))
.collect();
Ok(out)
}
}

30
src/commands/where_.rs Normal file
View file

@ -0,0 +1,30 @@
use crate::errors::ShellError;
use crate::object::base::find;
use crate::prelude::*;
use derive_new::new;
#[derive(new)]
pub struct Where;
impl crate::Command for Where {
fn run(&self, args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> {
if args.args.is_empty() {
return Err(ShellError::string("select requires a field"));
}
let field: Result<String, _> = args.args[0].as_string();
let field = field?;
let op: Result<String, _> = args.args[1].as_string();
let op = op?;
let objects = args
.input
.iter()
.filter(|item| find(&item, &field, &op, &args.args[2]))
.map(|item| ReturnValue::Value(item.copy()))
.collect();
Ok(objects)
}
}

View file

@ -59,10 +59,12 @@ fn main() -> Result<(), Box<Error>> {
("ps", Box::new(ps::Ps)),
("ls", Box::new(ls::Ls)),
("cd", Box::new(cd::Cd)),
("skip", Box::new(skip::Skip)),
("take", Box::new(take::Take)),
("select", Box::new(select::Select)),
("reject", Box::new(reject::Reject)),
("to-array", Box::new(to_array::ToArray)),
("where", Box::new(where_::Where))
]);
}
@ -73,6 +75,9 @@ fn main() -> Result<(), Box<Error>> {
));
match readline {
Ok(ref line) if line.trim() == "exit" => {
break;
}
Ok(line) => {
let result = crate::parser::shell_parser(&line)
.map_err(|e| ShellError::string(format!("{:?}", e)))?;

View file

@ -120,6 +120,17 @@ impl Value {
}
}
crate fn as_bool(&self) -> Result<bool, ShellError> {
match self {
Value::Primitive(Primitive::Boolean(b)) => Ok(*b),
// TODO: this should definitely be more general with better errors
other => Err(ShellError::string(format!(
"Expected integer, got {:?}",
other
))),
}
}
crate fn string(s: impl Into<String>) -> Value {
Value::Primitive(Primitive::String(s.into()))
}
@ -132,6 +143,10 @@ impl Value {
Value::Primitive(Primitive::Int(s.into()))
}
crate fn bool(s: impl Into<bool>) -> Value {
Value::Primitive(Primitive::Boolean(s.into()))
}
#[allow(unused)]
crate fn system_date(s: SystemTime) -> Value {
Value::Primitive(Primitive::Date(s.into()))
@ -188,3 +203,86 @@ crate fn reject(obj: &Value, fields: &[String]) -> crate::object::Dictionary {
out
}
crate fn find(obj: &Value, field: &str, op: &str, rhs: &Value) -> bool {
let descs = obj.data_descriptors();
match descs.iter().find(|d| d.name == *field) {
None => false,
Some(desc) => {
let v = obj.get_data(desc).borrow().copy();
//println!("'{:?}' '{}' '{:?}'", v, op, rhs);
match v {
Value::Primitive(Primitive::Boolean(b)) => {
match (op, rhs) {
("-eq", Value::Primitive(Primitive::Boolean(b2))) => {
b == *b2
}
("-ne", Value::Primitive(Primitive::Boolean(b2))) => {
b != *b2
}
_ => false
}
}
Value::Primitive(Primitive::Bytes(i)) => {
match (op, rhs) {
("-lt", Value::Primitive(Primitive::Int(i2))) => {
i < (*i2 as u128)
}
("-gt", Value::Primitive(Primitive::Int(i2))) => {
i > (*i2 as u128)
}
("-le", Value::Primitive(Primitive::Int(i2))) => {
i <= (*i2 as u128)
}
("-ge", Value::Primitive(Primitive::Int(i2))) => {
i >= (*i2 as u128)
}
("-eq", Value::Primitive(Primitive::Int(i2))) => {
i == (*i2 as u128)
}
("-ne", Value::Primitive(Primitive::Int(i2))) => {
i != (*i2 as u128)
}
_ => false
}
}
Value::Primitive(Primitive::Int(i)) => {
match (op, rhs) {
("-lt", Value::Primitive(Primitive::Int(i2))) => {
i < *i2
}
("-gt", Value::Primitive(Primitive::Int(i2))) => {
i > *i2
}
("-le", Value::Primitive(Primitive::Int(i2))) => {
i <= *i2
}
("-ge", Value::Primitive(Primitive::Int(i2))) => {
i >= *i2
}
("-eq", Value::Primitive(Primitive::Int(i2))) => {
i == *i2
}
("-ne", Value::Primitive(Primitive::Int(i2))) => {
i != *i2
}
_ => false
}
}
Value::Primitive(Primitive::String(s)) => {
match (op, rhs) {
("-eq", Value::Primitive(Primitive::String(s2))) => {
s == *s2
}
("-ne", Value::Primitive(Primitive::String(s2))) => {
s != *s2
}
_ => false
}
}
_ => false,
}
}
}
}

View file

@ -13,6 +13,7 @@ pub enum Item {
Quoted(String),
Bare(String),
Int(i64),
Boolean(bool),
}
impl Item {
@ -21,6 +22,7 @@ impl Item {
Item::Quoted(s) => Value::Primitive(Primitive::String(s.clone())),
Item::Bare(s) => Value::Primitive(Primitive::String(s.clone())),
Item::Int(i) => Value::Primitive(Primitive::Int(*i)),
Item::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)),
}
}
}
@ -30,6 +32,7 @@ crate fn print_items(items: &[Item]) -> String {
Item::Bare(s) => format!("{}", s),
Item::Quoted(s) => format!("{:?}", s),
Item::Int(i) => format!("{:?}", i),
Item::Boolean(b) => format!("{:?}", b),
});
itertools::join(formatted, " ")
@ -40,7 +43,7 @@ impl Item {
match self {
Item::Quoted(s) => s,
Item::Bare(s) => s,
Item::Int(_) => unimplemented!(),
Item::Boolean(_) | Item::Int(_) => unimplemented!(),
}
}
}
@ -62,8 +65,13 @@ fn int(s: &str) -> IResult<&str, Item> {
is_a("1234567890")(s).map(|(a, b)| (a, Item::Int(FromStr::from_str(b).unwrap())))
}
fn boolean(s: &str) -> IResult<&str, Item> {
alt((tag("true"), tag("false")))(s)
.map(|(a, b)| (a, Item::Boolean(FromStr::from_str(b).unwrap())))
}
fn command_token(s: &str) -> IResult<&str, Item> {
alt((int, quoted, unquoted))(s)
alt((boolean, int, quoted, unquoted))(s)
}
fn command_args(s: &str) -> IResult<&str, Vec<Item>> {