use std::fmt::Debug;
use dyn_clone::DynClone;
use crate::environment::Environment;
use crate::ir::NumVal::{Number, Unsure};
use crate::ir::*;
use crate::resolver::{resolve_date, resolve_range};
pub trait Filter<T>: Debug + DynClone {
fn check(&self, value: &T, env: Option<&Environment>) -> bool;
fn filter(&self, values: Vec<T>, env: Option<&Environment>) -> Vec<T> {
let mut res = vec![];
for value in values {
if self.check(&value, env) {
res.push(value);
}
}
res
}
}
dyn_clone::clone_trait_object!(<T> Filter<T>);
#[derive(Debug, Clone)]
pub enum Op {
OR,
And,
}
pub type BDF<T> = Box<dyn Filter<T>>;
#[derive(Debug, Clone)]
pub struct BinFilt<T: Debug> {
pub lhs: BDF<T>,
pub rhs: BDF<T>,
pub op: Op,
}
impl<T: Debug + Clone> Filter<T> for BinFilt<T> {
fn check(&self, value: &T, env: Option<&Environment>) -> bool {
match self.op {
Op::OR => self.lhs.check(value, env) || self.rhs.check(value, env),
Op::And => self.lhs.check(value, env) && self.rhs.check(value, env),
}
}
}
#[derive(Debug, Clone)]
pub struct ExcludeFilt<T> {
pub target: BDF<T>,
}
impl<T: Debug + Clone> ExcludeFilt<T> {
pub fn new(target: BDF<T>) -> Self {
ExcludeFilt { target }
}
}
impl<T: Debug + Clone> Filter<T> for ExcludeFilt<T> {
fn check(&self, value: &T, env: Option<&Environment>) -> bool {
!self.target.check(value, env)
}
}
impl Filter<NumVal> for NumRange {
fn check(&self, value: &NumVal, _env: Option<&Environment>) -> bool {
match *value {
Number(target) => match (&self.start, &self.end) {
(Unsure, Unsure) => true,
(Unsure, Number(nd)) => target <= *nd,
(Number(st), Unsure) => target >= *st,
(Number(st), Number(nd)) => target >= *st && target <= *nd,
},
_ => true,
}
}
}
impl Filter<NumVal> for NumVal {
fn check(&self, value: &NumVal, _env: Option<&Environment>) -> bool {
if matches!(self, Unsure) {
true
} else {
self == value
}
}
}
impl Filter<ExactDate> for ExactRange {
fn check(&self, value: &ExactDate, _env: Option<&Environment>) -> bool {
match self {
ExactRange::TimeRange(tr) => {
let start = tr.start.date.to_chrono().unwrap();
let end = tr.end.date.to_chrono().unwrap();
let target = value.to_chrono().unwrap();
target >= start && target <= end
}
ExactRange::AllDay(date) => date == value,
}
}
}
impl Filter<Date> for Range {
fn check(&self, value: &Date, env: Option<&Environment>) -> bool {
let exact_range = resolve_range(self, env.unwrap()).unwrap();
let exact_date = resolve_date(value, env.unwrap()).unwrap();
exact_range.check(&exact_date, env)
}
}
impl Filter<NumVal> for FlexField {
fn check(&self, value: &NumVal, env: Option<&Environment>) -> bool {
match self {
FlexField::NumRange(nr) => nr.check(value, env),
FlexField::NumVal(nv) => nv.check(value, env),
}
}
}
impl Filter<ExactDate> for FlexDate {
fn check(&self, value: &ExactDate, env: Option<&Environment>) -> bool {
self.year.check(&Number(value.year as i64), env)
&& self.month.check(&Number(value.month as i64), env)
&& self.day.check(&Number(value.day as i64), env)
}
}
impl Filter<Date> for FlexDate {
fn check(&self, value: &Date, env: Option<&Environment>) -> bool {
let exact_date = resolve_date(value, env.unwrap()).unwrap();
self.year.check(&Number(exact_date.year as i64), env)
&& self.month.check(&Number(exact_date.month as i64), env)
&& self.day.check(&Number(exact_date.day as i64), env)
}
}
mod tests {
use super::*;
use crate::ir::Range;
use crate::ir::TimeRange;
#[test]
fn test_flex_date() {
let fd = FlexDate {
year: Box::new(Number(2023)),
month: Box::new(NumRange {
start: Number(6),
end: Number(10),
}),
day: Box::new(NumRange {
start: Number(8),
end: Number(15),
}),
};
assert!(fd.check(
&ExactDate {
year: 2023,
month: 6,
day: 8
},
None
));
assert!(!fd.check(
&ExactDate {
year: 2023,
month: 6,
day: 7
},
None
));
assert!(!fd.check(
&ExactDate {
year: 2023,
month: 5,
day: 8
},
None
));
assert!(!fd.check(
&ExactDate {
year: 2022,
month: 6,
day: 8
},
None
));
}
#[test]
fn test_time_range() {
let trange = TimeRange {
start: DateTime::from_ymd(2020, 1, 3),
end: DateTime::from_ymd(2020, 2, 1),
};
eprintln!("{:?}", trange);
let range = Range::Time(trange);
let env = Environment::from_exact(ExactDateTime::from_ymd_hms(2020, 1, 1, 1, 1, 1));
assert!(range.check(&Date::from_ymd(2020, 1, 3), Some(&env)));
assert!(!range.check(&Date::from_ymd(2020, 1, 2), Some(&env)));
assert!(range.check(&Date::from_ymd(2020, 2, 1), Some(&env)));
}
#[test]
fn test_tree() {
let orfilt = BinFilt {
lhs: Box::new(NumRange {
start: Number(1),
end: Number(5),
}),
rhs: Box::new(NumRange {
start: Number(10),
end: Number(15),
}),
op: Op::OR,
};
assert!(orfilt.check(&Number(1), None));
assert!(!orfilt.check(&Number(8), None));
assert!(orfilt.check(&Number(13), None));
let andfilt = BinFilt {
lhs: Box::new(NumRange {
start: Number(1),
end: Number(8),
}),
rhs: Box::new(NumRange {
start: Number(3),
end: Unsure,
}),
op: Op::And,
};
assert!(!andfilt.check(&Number(1), None));
assert!(andfilt.check(&Number(8), None));
assert!(!andfilt.check(&Number(13), None));
let combfilt = BinFilt {
lhs: Box::new(ExcludeFilt {
target: Box::new(orfilt),
}),
rhs: Box::new(andfilt),
op: Op::OR,
};
assert!(combfilt.check(&Number(3), None));
assert!(combfilt.check(&Number(9), None));
assert!(!combfilt.check(&Number(2), None));
}
}