use super::Condition;
use crate::internal::query_context::flat_conditions::FlatCondition;
use crate::internal::query_context::ConditionBuilder;
#[derive(Copy, Clone, Debug)]
pub enum CollectionOperator {
And,
Or,
}
#[derive(Clone)]
pub struct DynamicCollection<T> {
pub operator: CollectionOperator,
pub vector: Vec<T>,
}
impl<'a, T> DynamicCollection<T>
where
Self: Condition<'a>,
{
pub fn and(vector: Vec<T>) -> Option<Self> {
Self::new_checked(CollectionOperator::And, vector)
}
pub fn and_unchecked(vector: Vec<T>) -> Self {
Self {
operator: CollectionOperator::And,
vector,
}
}
pub fn or(vector: Vec<T>) -> Option<Self> {
Self::new_checked(CollectionOperator::Or, vector)
}
pub fn or_unchecked(vector: Vec<T>) -> Self {
Self {
operator: CollectionOperator::Or,
vector,
}
}
fn new_checked(operator: CollectionOperator, vector: Vec<T>) -> Option<Self> {
if vector.is_empty() {
None
} else {
Some(Self { operator, vector })
}
}
}
impl<'a, T: Condition<'a>> Condition<'a> for DynamicCollection<T> {
fn build(&self, mut builder: ConditionBuilder<'_, 'a>) {
builder.push_condition(FlatCondition::StartCollection(self.operator));
for cond in self.vector.iter() {
cond.build(builder.reborrow());
}
builder.push_condition(FlatCondition::EndCollection);
}
}
impl<'a, T: Condition<'a>> Condition<'a> for DynamicCollection<Option<T>> {
fn build(&self, mut builder: ConditionBuilder<'_, 'a>) {
builder.push_condition(FlatCondition::StartCollection(self.operator));
let len = builder.len_condition();
for cond in self.vector.iter().flat_map(Option::as_ref) {
cond.build(builder.reborrow());
}
if len != builder.len_condition() {
builder.push_condition(FlatCondition::EndCollection);
} else {
builder.pop_condition();
super::Value::Bool(match self.operator {
CollectionOperator::And => true,
CollectionOperator::Or => false,
})
.build(builder.reborrow());
}
}
}
#[derive(Copy, Clone)]
pub struct StaticCollection<T> {
pub operator: CollectionOperator,
pub tuple: T,
}
impl<'a, T> StaticCollection<T>
where
Self: Condition<'a>,
{
pub fn and(tuple: T) -> Self {
Self {
operator: CollectionOperator::And,
tuple,
}
}
pub fn or(tuple: T) -> Self {
Self {
operator: CollectionOperator::Or,
tuple,
}
}
}
macro_rules! impl_static_collection {
(recu $head:ident, $($tail:ident),+) => {
impl_static_collection!(impl $head, $($tail),+);
impl_static_collection!(recu $($tail),+);
};
(recu $generic:ident) => {
impl_static_collection!(impl $generic);
};
(impl $($generic:ident),+) => {
#[allow(non_snake_case)] impl<'a, $($generic: Condition<'a>),+> Condition<'a> for StaticCollection<($($generic,)+)> {
fn build(&self, mut builder: ConditionBuilder<'_, 'a>) {
builder.push_condition(FlatCondition::StartCollection(self.operator));
let ($($generic,)+) = &self.tuple;
$($generic.build(builder.reborrow());)+
builder.push_condition(FlatCondition::EndCollection);
}
}
#[allow(non_snake_case)] impl<'a, $($generic: Condition<'a>),+> Condition<'a> for StaticCollection<($(Option<$generic>,)+)> {
fn build(&self, mut builder: ConditionBuilder<'_, 'a>) {
builder.push_condition(FlatCondition::StartCollection(self.operator));
let len = builder.len_condition();
let ($($generic,)+) = &self.tuple;
$(if let Some(cond) = $generic {
cond.build(builder.reborrow());
})+
if len != builder.len_condition() {
builder.push_condition(FlatCondition::EndCollection);
} else {
builder.pop_condition();
super::Value::Bool(match self.operator {
CollectionOperator::And => true,
CollectionOperator::Or => false,
})
.build(builder.reborrow());
}
}
}
};
($($generic:ident),+) => {
impl_static_collection!(recu $($generic),+);
}
}
impl_static_collection!(H, G, F, E, D, C, B, A);
#[doc(hidden)]
#[macro_export]
macro_rules! create_collection {
($method:ident, $H:expr, $G:expr, $F:expr, $E:expr, $D:expr, $C:expr, $B:expr, $A:expr, $($other:expr),+ $(,)?) => {
$crate::conditions::collections::DynamicCollection::ident(vec![
$crate::conditions::collections::Condition::boxed($H),
$crate::conditions::collections::Condition::boxed($G),
$crate::conditions::collections::Condition::boxed($F),
$crate::conditions::collections::Condition::boxed($E),
$crate::conditions::collections::Condition::boxed($D),
$crate::conditions::collections::Condition::boxed($C),
$crate::conditions::collections::Condition::boxed($B),
$crate::conditions::collections::Condition::boxed($A),
$(
$crate::conditions::collections::Condition::boxed($other),
)+
]).unwrap_or_else(|| unreachable!("Vector should contain at least 8 conditions"))
};
($method:ident, $($other:expr),+ $(,)?) => {
$crate::conditions::collections::StaticCollection::$method(($(
$crate::conditions::collections::ensure_condition($other),
)+))
}
}
#[macro_export]
macro_rules! or {
($($condition:expr),+ $(,)?) => {
$crate::create_collection!(or, $($condition),+)
};
}
#[macro_export]
macro_rules! and {
($($condition:expr),+ $(,)?) => {
$crate::create_collection!(and, $($condition),+)
};
}
#[doc(hidden)]
pub trait IsCondition {}
impl<'c, C: Condition<'c>> IsCondition for C {}
impl<'c, C: Condition<'c>> IsCondition for Option<C> {}
#[doc(hidden)]
pub fn ensure_condition<C: IsCondition>(value: C) -> C {
value
}