use crate::{
db::{
predicate::{CoercionId, CompareOp, ComparePredicate},
query::construction::ConstructionBudget,
},
error::InternalError,
value::{Value, canonicalize_value_set},
};
pub(in crate::db) struct MembershipCompareLeaf<'a> {
field: &'a str,
value: Value,
coercion: CoercionId,
}
impl<'a> MembershipCompareLeaf<'a> {
#[must_use]
pub(in crate::db) const fn new(field: &'a str, value: Value, coercion: CoercionId) -> Self {
Self {
field,
value,
coercion,
}
}
}
pub(in crate::db) fn collapse_membership_compare_leaves(
leaves: Vec<MembershipCompareLeaf<'_>>,
target_op: CompareOp,
budget: &dyn ConstructionBudget,
) -> Result<Option<ComparePredicate>, InternalError> {
if leaves.len() < 2 {
return Ok(None);
}
let Some((field, coercion)) =
membership_compare_domain(leaves.iter().map(|leaf| Some((leaf.field, leaf.coercion))))
else {
return Ok(None);
};
let field = budget.copy_text(field)?;
let mut values = budget.vec_with_capacity(leaves.len())?;
values.extend(leaves.into_iter().map(|leaf| leaf.value));
Ok(Some(membership_compare_from_values(
field, target_op, values, coercion,
)))
}
pub(in crate::db::predicate) fn membership_compare_domain<'a>(
leaves: impl IntoIterator<Item = Option<(&'a str, CoercionId)>>,
) -> Option<(&'a str, CoercionId)> {
let mut leaves = leaves.into_iter();
let domain = leaves.next()??;
for leaf in leaves {
if leaf? != domain {
return None;
}
}
Some(domain)
}
pub(in crate::db::predicate) fn membership_compare_from_values(
field: impl Into<String>,
target_op: CompareOp,
values: Vec<Value>,
coercion: CoercionId,
) -> ComparePredicate {
let value = canonical_membership_value_list(values);
ComparePredicate::with_coercion(field, target_op, value, coercion)
}
pub(in crate::db) fn canonical_membership_value_list(mut values: Vec<Value>) -> Value {
canonicalize_value_set(&mut values);
Value::List(values)
}