use {
crate::{
col::{ColProxy,Col},
rename_col::RenameCol,
header::{Header},
record::Record,
},
tylisp::{
HCons, HNil, sexpr, defun_nocalc,
non_calc_literal,
engine::Eval,
ops::{
list::{Union, SupersetP, EmptyP, Tail},
},
marker_traits::List
},
};
use std::ops::{Bound,RangeBounds};
use std::any::Any;
pub trait QueryFilter: Clone {
type ReqCols: Header;
fn test_record(&self, _:&impl Record)->bool;
fn bounds_of<C:Col+Ord>(&self)->(Bound<&'_ C>, Bound<&'_ C>) {
(Bound::Unbounded, Bound::Unbounded)
}
}
pub trait IntoFilter {
type Result:QueryFilter + List;
fn into_filter(self)->Self::Result;
}
impl IntoFilter for HNil {
type Result = Self;
fn into_filter(self)->Self { self }
}
impl<H:ColProxy+PartialEq, T:IntoFilter> IntoFilter for HCons<H,T>
where HCons<Exact<H>, T::Result>: QueryFilter {
type Result = HCons<Exact<H>, T::Result>;
fn into_filter(self)->Self::Result {
HCons { head: Exact(self.head), tail: self.tail.into_filter() }
}
}
#[derive(Copy,Clone,Default)]
pub struct FilterForHeader;
defun_nocalc!{() FilterForHeader {
(H: Header, F:QueryFilter) { _:H, _:F } =>
{SupersetP, @H, @F::ReqCols};
}}
#[derive(Copy,Clone,Default)]
pub struct SingleColFilter;
defun_nocalc!{() SingleColFilter {
(F:QueryFilter) {_:F} => {EmptyP, {Tail, @F::ReqCols}};
}}
#[test] fn test_filter_for_header() {
col!{A: usize}
col!{B: usize}
col!{C: usize}
use tylisp::{typenum as tn, eval, ops::list::BuildList};
assert_type_eq! { tn::True : eval!{FilterForHeader, @{A,B}, @Exact<A>}}
assert_type_eq! { tn::True : eval!{FilterForHeader, @{A,B}, @Exact<B>}}
assert_type_eq! { tn::False: eval!{FilterForHeader, @{A,B}, @Exact<C>}}
assert_type_eq! { tn::True : eval!{FilterForHeader, @{A,B}, @HNil}}
assert_type_eq! { tn::True : eval!{FilterForHeader, @{A,B}, {BuildList, @Exact<A>, @Exact<B>}}}
assert_type_eq! { tn::True : eval!{FilterForHeader, @{A,B}, {BuildList, @Exact<B>, @Exact<A>}}}
assert_type_eq! { tn::False: eval!{FilterForHeader, @{A,B}, {BuildList, @Exact<C>, @Exact<A>}}}
assert_type_eq! { tn::False: eval!{FilterForHeader, @{A,B}, {BuildList, @Exact<A>, @Exact<C>}}}
}
#[derive(Copy, Clone, Debug)]
pub struct Exact<T>(pub T);
non_calc_literal!({T} Exact<T>);
impl<A,B,T> RenameCol<A,B> for Exact<T>
where A: Col, B:Col<Inner = A::Inner>,
T: RenameCol<A,B>,
Exact<T::Renamed>: QueryFilter {
type Renamed = Exact<T::Renamed>;
fn rename_col(self)->Exact<T::Renamed> { Exact(self.0.rename_col()) }
}
impl<T:ColProxy+Clone> QueryFilter for Exact<T>
where T::For: PartialEq {
type ReqCols = sexpr!{T::For};
fn test_record(&self, rec:&impl Record)->bool {
match rec.col_opt::<T::For>() {
Some(c) => *c == *self.0.col_ref(),
None => true
}
}
fn bounds_of<C:Col+Ord>(&self)->(Bound<&'_ C>, Bound<&'_ C>) {
let c: Option<&C> = (self.0.col_ref() as &dyn Any).downcast_ref();
match c {
Some(c) => (Bound::Included(c), Bound::Included(c)),
None => (Bound::Unbounded, Bound::Unbounded)
}
}
}
impl QueryFilter for HNil {
type ReqCols = HNil;
fn test_record(&self, _:&impl Record)->bool { true }
fn bounds_of<C:Col+Ord>(&self)->(Bound<&'_ C>, Bound<&'_ C>) {
(Bound::Unbounded, Bound::Unbounded)
}
}
impl<H,T, UnionCols> QueryFilter for HCons<H,T> where
H:QueryFilter,
T:QueryFilter,
sexpr!{Union, @H::ReqCols, @T::ReqCols}: Eval<Result=UnionCols>,
UnionCols: Header,
{
type ReqCols = UnionCols;
#[inline(always)]
fn test_record(&self, rec:&impl Record)->bool {
self.head.test_record(rec) && self.tail.test_record(rec)
}
fn bounds_of<C:Col+Ord>(&self)->(Bound<&'_ C>, Bound<&'_ C>) {
let (lo_1, hi_1) = self.head.bounds_of::<C>();
let (lo_2, hi_2) = self.tail.bounds_of::<C>();
use Bound::*;
let lo = match (lo_1, lo_2) {
(Unbounded, _) => lo_2,
(_, Unbounded) => lo_1,
(Included(a), Included(b)) => Included(a.max(b)),
(Excluded(a), Excluded(b)) => Excluded(a.max(b)),
(Included(a), Excluded(b)) => if a > b { Included(a) }
else { Excluded(b) },
(Excluded(a), Included(b)) => if b > a { Included(b) }
else { Excluded(a) },
};
let hi = match (hi_1, hi_2) {
(Unbounded, _) => hi_2,
(_, Unbounded) => hi_1,
(Included(a), Included(b)) => Included(a.min(b)),
(Excluded(a), Excluded(b)) => Excluded(a.min(b)),
(Included(a), Excluded(b)) => if a < b { Included(a) }
else { Excluded(b) },
(Excluded(a), Included(b)) => if b < a { Included(b) }
else { Excluded(a) },
};
(lo, hi)
}
}