dysk_cli/
filter.rs

1use {
2    crate::col_expr::*,
3    bet::*,
4    lfs_core::*,
5    std::str::FromStr,
6};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9enum BoolOperator {
10    And,
11    Or,
12    Not,
13}
14
15#[derive(Debug, Default, Clone)]
16pub struct Filter {
17    expr: BeTree<BoolOperator, ColExpr>,
18}
19
20impl Filter {
21    #[allow(clippy::match_like_matches_macro)]
22    pub fn eval(
23        &self,
24        mount: &Mount,
25    ) -> Result<bool, EvalExprError> {
26        self.expr
27            .eval_faillible(
28                // leaf evaluation
29                |col_expr| col_expr.eval(mount),
30                // bool operation
31                |op, a, b| match (op, b) {
32                    (BoolOperator::And, Some(b)) => Ok(a & b),
33                    (BoolOperator::Or, Some(b)) => Ok(a | b),
34                    (BoolOperator::Not, None) => Ok(!a),
35                    _ => {
36                        unreachable!()
37                    }
38                },
39                // when to short-circuit
40                |op, a| match (op, a) {
41                    (BoolOperator::And, false) => true,
42                    (BoolOperator::Or, true) => true,
43                    _ => false,
44                },
45            )
46            .map(|b| b.unwrap_or(true))
47    }
48    pub fn filter<'m>(
49        &self,
50        mounts: &'m [Mount],
51    ) -> Result<Vec<&'m Mount>, EvalExprError> {
52        let mut filtered = Vec::new();
53        for mount in mounts {
54            if self.eval(mount)? {
55                filtered.push(mount);
56            }
57        }
58        Ok(filtered)
59    }
60}
61
62impl FromStr for Filter {
63    type Err = ParseExprError;
64    fn from_str(input: &str) -> Result<Self, ParseExprError> {
65        // we start by reading the global structure
66        let mut expr: BeTree<BoolOperator, String> = BeTree::new();
67        for c in input.chars() {
68            match c {
69                '&' => expr.push_operator(BoolOperator::And),
70                '|' => expr.push_operator(BoolOperator::Or),
71                '!' => expr.push_operator(BoolOperator::Not),
72                ' ' => {}
73                '(' => expr.open_par(),
74                ')' => expr.close_par(),
75                _ => expr.mutate_or_create_atom(String::new).push(c),
76            }
77        }
78
79        // then we parse each leaf
80        let expr = expr.try_map_atoms(|raw| raw.parse())?;
81
82        Ok(Self { expr })
83    }
84}