use std::ops::{Add, Sub, Mul, Div, Rem};
use std::collections::HashMap;
use lazy_static::lazy_static;
use crate::arg::Arg;
pub type Func = fn(Vec<Arg>) -> Arg;
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Op {
pub name: String,
pub func: Func,
}
impl Op {
pub fn new(name: &str, func: Func) -> Op {
Op {
name: name.to_owned(),
func: func,
}
}
pub fn get(name: &str) -> Option<&Op> {
OPS.get(name)
}
}
macro_rules! register_builtin {
( $($alias:tt => $func:tt),* $(,)? ) => {
lazy_static! {
static ref OPS: HashMap<&'static str, Op> = {
let mut map = HashMap::new();
$(
map.insert($alias, Op::new($alias, $func as Func));
map.insert(stringify!($func), Op::new(stringify!($func), $func as Func));
)*
map
};
}
};
}
register_builtin!(
"var" => var,
"=" => eq,
"<" => lt,
"<=" => le,
"!=" => ne,
">=" => ge,
">=" => ge,
">" => gt,
"&" => and,
"&&" => and,
"all" => and,
"|" => or,
"||" => or,
"any" => or,
"!" => not,
"+" => add,
"sum" => add,
"-" => sub,
"minus" => sub,
"neg" => neg,
"*" => mul,
"/" => div,
"%" => rem,
"mod" => rem,
"abs" => abs,
"in" => r#in,
"startswith" => startswith,
"endswith" => endswith,
"split" => split,
"join" => join,
"lower" => lower,
"upper" => upper,
"match" => r#match,
"regex" => regex,
"num" => num,
"string" => string,
);
pub fn var(args: Vec<Arg>) -> Arg {
args[0].clone()
}
pub fn eq(args: Vec<Arg>) -> Arg {
Arg::Bool(args.windows(2).all(|w| w[0] == w[1]))
}
pub fn lt(args: Vec<Arg>) -> Arg {
Arg::Bool(args.windows(2).all(|w| w[0] < w[1]))
}
pub fn le(args: Vec<Arg>) -> Arg {
Arg::Bool(args.windows(2).all(|w| w[0] <= w[1]))
}
pub fn ne(args: Vec<Arg>) -> Arg {
Arg::Bool(args.windows(2).all(|w| w[0] != w[1]))
}
pub fn ge(args: Vec<Arg>) -> Arg {
Arg::Bool(args.windows(2).all(|w| w[0] >= w[1]))
}
pub fn gt(args: Vec<Arg>) -> Arg {
Arg::Bool(args.windows(2).all(|w| w[0] > w[1]))
}
pub fn and(args: Vec<Arg>) -> Arg {
Arg::Bool(args.iter().all(|v| v.into()))
}
pub fn or(args: Vec<Arg>) -> Arg {
Arg::Bool(args.iter().any(|v| v.into()))
}
pub fn not(args: Vec<Arg>) -> Arg {
let b: bool = args.get(0).unwrap_or(&Arg::Null).into();
Arg::Bool(!b)
}
pub fn add(args: Vec<Arg>) -> Arg {
let mut it = args.into_iter();
it.next().map(|first| it.fold(first, Add::add)).unwrap_or(Arg::Null)
}
pub fn sub(args: Vec<Arg>) -> Arg {
let mut it = args.into_iter();
it.next().map(|first| it.fold(first, Sub::sub)).unwrap_or(Arg::Null)
}
pub fn neg(args: Vec<Arg>) -> Arg {
-args.get(0).unwrap_or(&Arg::Null)
}
pub fn mul(args: Vec<Arg>) -> Arg {
let mut it = args.into_iter();
it.next().map(|first| it.fold(first, Mul::mul)).unwrap_or(Arg::Null)
}
pub fn div(args: Vec<Arg>) -> Arg {
let mut it = args.into_iter();
it.next().map(|first| it.fold(first, Div::div)).unwrap_or(Arg::Null)
}
pub fn rem(args: Vec<Arg>) -> Arg {
let mut it = args.into_iter();
it.next().map(|first| it.fold(first, Rem::rem)).unwrap_or(Arg::Null)
}
pub fn abs(args: Vec<Arg>) -> Arg {
let int: i64 = args.get(0).unwrap_or(&Arg::Null).into();
Arg::Int(int.abs())
}
pub fn r#in(args: Vec<Arg>) -> Arg {
Arg::Bool(args[1..].contains(&args[0]))
}
pub fn startswith(args: Vec<Arg>) -> Arg {
let ret = match &args[0] {
Arg::String(s) => Arg::Bool((&s).starts_with(&args[1].to_string())),
Arg::Array(a) => Arg::Bool(a.starts_with(&args[1..])),
_ => Arg::Bool(false),
};
ret
}
pub fn endswith(args: Vec<Arg>) -> Arg {
let ret = match &args[0] {
Arg::String(s) => Arg::Bool((&s).ends_with(&args[1].to_string())),
Arg::Array(a) => Arg::Bool(a.ends_with(&args[1..])),
_ => Arg::Bool(false),
};
ret
}
pub fn lower(args: Vec<Arg>) -> Arg {
Arg::String(String::from(&args[0]).to_lowercase())
}
pub fn upper(args: Vec<Arg>) -> Arg {
Arg::String(String::from(&args[0]).to_uppercase())
}
pub fn split(args: Vec<Arg>) -> Arg {
let s = &String::from(&args[0]);
let sep = &String::from(&args[1]);
Arg::Array(s.split(sep).map(|x| Arg::String(x.to_owned())).collect())
}
pub fn join(args: Vec<Arg>) -> Arg {
let mut it = args.into_iter();
let sep = &String::from(&it.nth(0).unwrap_or(Arg::String("".to_owned())));
Arg::String(it.map(|x| String::from(&x)).collect::<Vec<String>>().join(sep))
}
pub fn r#match(args: Vec<Arg>) -> Arg {
match glob::Pattern::new(&String::from(&args[1])) {
Ok(patt) => {
Arg::Bool(patt.matches(&String::from(&args[0])))
},
Err(_) => Arg::Bool(false),
}
}
pub fn regex(args: Vec<Arg>) -> Arg {
match regex::Regex::new(&String::from(&args[1])) {
Ok(re) => {
Arg::Bool(re.is_match(&String::from(&args[0])))
},
Err(_) => Arg::Bool(false),
}
}
pub fn num(args: Vec<Arg>) -> Arg {
let a = args[0].clone();
match a {
Arg::Int(_) => a,
Arg::Float(_) => a,
Arg::String(v) => {
match v.parse::<i64>() {
Ok(i) => Arg::Int(i),
Err(_) => match v.parse::<f64>() {
Ok(f) => Arg::Float(f),
Err(_) => Arg::Int(0i64),
}
}
},
_ => Arg::Int(Into::<i64>::into(a)),
}
}
pub fn string(args: Vec<Arg>) -> Arg {
Arg::String(String::from(&args[0]))
}