tytanic_filter/eval/set.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
use std::fmt::Debug;
use std::sync::Arc;
use ecow::eco_vec;
use super::{Context, Error, Test, TryFromValue, Type, Value};
use crate::ast::Pat;
/// The backing implementation for a [`Set`].
type SetImpl<T> = Arc<dyn Fn(&Context<T>, &T) -> Result<bool, Error> + 'static>;
/// A test set, this can be used to check if a test is contained in it and is
/// expected to be the top level value in an [`ExpressionFilter`][filter].
///
/// [filter]: crate::ExpressionFilter
#[derive(Clone)]
pub struct Set<T>(SetImpl<T>);
impl<T> Set<T> {
/// Create a new set with the given implementation.
pub fn new<F>(f: F) -> Self
where
F: Fn(&Context<T>, &T) -> Result<bool, Error> + 'static,
{
Self(Arc::new(f) as _)
}
/// Whether the given test is contained within this set.
pub fn contains(&self, ctx: &Context<T>, test: &T) -> Result<bool, Error> {
(self.0)(ctx, test)
}
}
impl<T> Debug for Set<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Set").field(&..).finish()
}
}
impl<T: Test> Set<T> {
/// Construct a test set which contains all tests matching the given pattern.
///
/// This is the test set created from pattern literals like `r:'foot-(\w-)+'`.
pub fn coerce_pat(pat: Pat) -> Set<T> {
Set::new(move |_, test: &T| Ok(pat.is_match(test.id())))
}
}
impl<T: 'static> Set<T> {
/// Construct a set which contains all tests _not_ contained in the given
/// set.
///
/// This is the test set created by `!set`.
pub fn expr_comp(set: Set<T>) -> Self {
Self::new(move |ctx, test| Ok(!set.contains(ctx, test)?))
}
/// Construct a set which contains all tests which are contained in any of
/// the given sets.
///
/// This is the test set created by `a | b`.
pub fn expr_union<I>(a: Set<T>, b: Set<T>, rest: I) -> Self
where
I: IntoIterator<Item = Set<T>>,
{
let sets: Vec<_> = [a, b].into_iter().chain(rest).collect();
Self::new(move |ctx, test| {
for set in &sets {
if set.contains(ctx, test)? {
return Ok(true);
}
}
Ok(false)
})
}
/// Construct a set which contains all tests which are contained in all of
/// the given sets.
///
/// This is the test set created by `a & b`.
pub fn expr_inter<I>(a: Set<T>, b: Set<T>, rest: I) -> Self
where
I: IntoIterator<Item = Set<T>>,
{
let sets: Vec<_> = [a, b].into_iter().chain(rest).collect();
Self::new(move |ctx, test| {
for set in &sets {
if !set.contains(ctx, test)? {
return Ok(false);
}
}
Ok(true)
})
}
/// Construct a set which contains all tests which are contained in the
/// first but not the second set.
///
/// This is the test set created by `a ~ b` and is equivalent to `a & !b`.
pub fn expr_diff(a: Set<T>, b: Set<T>) -> Self {
Self::new(move |ctx, test| Ok(a.contains(ctx, test)? && !b.contains(ctx, test)?))
}
/// Construct a set which contains all tests which are contained in the
/// either the first or the second, but not both sets.
///
/// This is the test set created by `a ^ b`.
pub fn expr_sym_diff(a: Set<T>, b: Set<T>) -> Self {
Self::new(move |ctx, test| Ok(a.contains(ctx, test)? ^ b.contains(ctx, test)?))
}
}
impl<T> TryFromValue<T> for Set<T> {
fn try_from_value(value: Value<T>) -> Result<Self, Error> {
Ok(match value {
Value::Set(set) => set,
_ => {
return Err(Error::TypeMismatch {
expected: eco_vec![Type::Set],
found: value.as_type(),
})
}
})
}
}