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(),
                })
            }
        })
    }
}