use eav::eavi::{Attribute, Entity, EntityAttributeValueIndex, Value};
use std::collections::BTreeSet;
pub struct EaviQuery<'a, A: Attribute> {
entity: EntityFilter<'a>,
attribute: AttributeFilter<'a, A>,
value: ValueFilter<'a>,
index: IndexFilter,
}
type EntityFilter<'a> = EavFilter<'a, Entity>;
type AttributeFilter<'a, A> = EavFilter<'a, A>;
type ValueFilter<'a> = EavFilter<'a, Value>;
impl<'a, A: Attribute> Default for EaviQuery<'a, A> {
fn default() -> EaviQuery<'a, A> {
EaviQuery::new(
Default::default(),
Default::default(),
Default::default(),
IndexFilter::LatestByAttribute,
)
}
}
impl<'a, A: Attribute> EaviQuery<'a, A> {
pub fn new(
entity: EntityFilter<'a>,
attribute: AttributeFilter<'a, A>,
value: ValueFilter<'a>,
index: IndexFilter,
) -> Self {
Self {
entity,
attribute,
value,
index,
}
}
pub fn run<I>(&self, iter: I) -> BTreeSet<EntityAttributeValueIndex<A>>
where
I: Clone + Iterator<Item = EntityAttributeValueIndex<A>> + 'a,
{
let iter2 = iter.clone();
let filtered = iter
.clone()
.filter(|eavi| EaviQuery::eav_check(&eavi, &self.entity, &self.attribute, &self.value));
match self.index {
IndexFilter::LatestByAttribute => filtered
.filter(|eavi| {
iter2
.clone()
.filter(|eavi_inner| {
EaviQuery::eav_check(
&eavi_inner,
&Some(eavi.entity()).into(),
&self.attribute,
&Some(eavi.value()).into(),
)
})
.last()
.map(|latest| latest.index() == eavi.index())
.unwrap_or(false)
})
.collect(),
IndexFilter::Range(start, end) => filtered
.filter(|eavi| {
start.map(|lo| lo <= eavi.index()).unwrap_or(true)
&& end.map(|hi| eavi.index() <= hi).unwrap_or(true)
})
.collect(),
}
}
fn eav_check(
eavi: &EntityAttributeValueIndex<A>,
e: &EntityFilter<'a>,
a: &AttributeFilter<'a, A>,
v: &ValueFilter<'a>,
) -> bool {
e.check(eavi.entity()) && a.check(eavi.attribute()) && v.check(eavi.value())
}
pub fn entity(&self) -> &EntityFilter<'a> {
&self.entity
}
pub fn attribute(&self) -> &AttributeFilter<'a, A> {
&self.attribute
}
pub fn value(&self) -> &ValueFilter<'a> {
&self.value
}
pub fn index(&self) -> &IndexFilter {
&self.index
}
}
pub struct EavFilter<'a, T: 'a + Eq>(Box<dyn Fn(T) -> bool + 'a>);
impl<'a, T: 'a + Eq> EavFilter<'a, T> {
pub fn single(val: T) -> Self {
Self(Box::new(move |v| v == val))
}
pub fn multiple(vals: Vec<T>) -> Self {
Self(Box::new(move |val| vals.iter().any(|v| *v == val)))
}
pub fn predicate<F>(predicate: F) -> Self
where
F: Fn(T) -> bool + 'a,
{
Self(Box::new(predicate))
}
pub fn check(&self, val: T) -> bool {
self.0(val)
}
}
impl<'a, T: Eq> Default for EavFilter<'a, T> {
fn default() -> EavFilter<'a, T> {
Self(Box::new(|_| true))
}
}
impl<'a, T: Eq> From<Option<T>> for EavFilter<'a, T> {
fn from(val: Option<T>) -> EavFilter<'a, T> {
val.map(EavFilter::single).unwrap_or_default()
}
}
impl<'a, T: Eq> From<Vec<T>> for EavFilter<'a, T> {
fn from(vals: Vec<T>) -> EavFilter<'a, T> {
EavFilter::multiple(vals)
}
}
#[derive(Clone, Debug)]
pub enum IndexFilter {
LatestByAttribute,
Range(Option<i64>, Option<i64>),
}