jeeves/
predicate.rs

1use peg::parser;
2
3use serde::de::{Deserialize, Deserializer, Visitor};
4use std::str;
5use std::time::Duration;
6
7#[derive(Debug, PartialEq)]
8pub enum Scalar {
9    Size(u64),
10    Time(Duration),
11}
12
13impl Scalar {
14    pub fn is_size(&self) -> bool {
15        match self {
16            Scalar::Size(_) => true,
17            _ => false,
18        }
19    }
20
21    pub fn is_time(&self) -> bool {
22        match self {
23            Scalar::Time(_) => true,
24            _ => false,
25        }
26    }
27}
28
29#[derive(Debug, PartialEq)]
30pub enum Token {
31    Size,
32    Age,
33}
34
35#[derive(Debug, PartialEq)]
36pub enum Operator {
37    Greater,
38    GreaterEqual,
39    Lesser,
40    LesserEqual,
41    Equal,
42    NotEqual,
43}
44
45impl Operator {
46    pub fn compare<T>(&self, lhs: &T, rhs: &T) -> bool
47    where
48        T: PartialOrd<T>,
49    {
50        match self {
51            Operator::Greater => lhs > rhs,
52            Operator::GreaterEqual => lhs >= rhs,
53            Operator::Equal => lhs == rhs,
54            Operator::Lesser => lhs < rhs,
55            Operator::LesserEqual => lhs <= rhs,
56            Operator::NotEqual => lhs != rhs,
57        }
58    }
59}
60
61#[derive(Debug, PartialEq)]
62pub enum Predicate {
63    Comparison {
64        token: Token,
65        operator: Operator,
66        value: Scalar,
67    },
68}
69
70impl str::FromStr for Predicate {
71    type Err = ();
72    fn from_str(s: &str) -> Result<Self, Self::Err> {
73        Ok(predicates::predicate(s).unwrap())
74    }
75}
76
77struct PredicateVisitor;
78impl<'de> Visitor<'de> for PredicateVisitor {
79    type Value = Predicate;
80
81    fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
82        write!(f, "AAAAAAAAAAAAAA")
83    }
84
85    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
86    where
87        E: serde::de::Error,
88    {
89        Ok(s.parse::<Predicate>().unwrap())
90    }
91}
92
93impl<'de> Deserialize<'de> for Predicate {
94    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
95    where
96        D: Deserializer<'de>,
97    {
98        deserializer.deserialize_str(PredicateVisitor)
99    }
100}
101
102parser! {
103  grammar predicates() for str {
104    rule token() -> Token
105        = "age" { Token::Age }
106        / "size" { Token::Size }
107
108    rule number() -> u64
109        = n:$(['0'..='9']) { n.parse().unwrap() }
110
111    rule scalar_size() -> Scalar
112        = n:number() " "? q:scalar_size_quantif() { Scalar::Size(n * q) }
113
114    rule scalar_size_quantif() -> u64
115        = "B" { 1 }
116        / "KB" { 2u64.pow(10) }
117        / "MB" { 2u64.pow(10 * 2) }
118        / "GB" { 2u64.pow(10 * 3) }
119
120    rule duration_quantif() -> u64
121        = ("seconds" / "second" / "secs" / "sec" / "s") { 1 }
122        / ("minutes" / "minute" / "min" / "m") { 60 }
123        / ("hours" / "hour" / "h") { 60 * 60 }
124        / ("days" / "day" / "d") { 24 * 60 * 60 }
125        / ("weeks" / "week" / "w") { 7 * 24 * 60 * 60 }
126
127    rule scalar_age() -> Scalar
128        = n:number() " " q:duration_quantif() { Scalar::Time(Duration::from_secs(n as u64 * q)) }
129
130    rule scalar() -> Scalar
131        = scalar_size() / scalar_age()
132
133    rule operator() -> Operator
134        = ">"  { Operator::Greater }
135        / "<"  { Operator::Lesser }
136        / ">=" { Operator::GreaterEqual }
137        / "<=" { Operator::LesserEqual }
138        / "==" { Operator::Equal }
139        / "!=" { Operator::NotEqual }
140
141    pub rule predicate() -> Predicate
142      = t:token() " " operator() " " s:scalar() { Predicate::Comparison { token: t, operator: Operator::Greater, value: s } }
143  }
144}
145
146#[test]
147fn can_parse_size_predicate() {
148    assert_eq!(
149        predicates::predicate("size > 1 MB").unwrap(),
150        Predicate::Comparison {
151            token: Token::Size,
152            operator: Operator::Greater,
153            value: Scalar::Size(1024 * 1024)
154        },
155    )
156}
157
158#[test]
159fn can_parse_time_predicates() {
160    assert_eq!(
161        predicates::predicate("age > 1 h").unwrap(),
162        Predicate::Comparison {
163            token: Token::Age,
164            operator: Operator::Greater,
165            value: Scalar::Time(Duration::from_secs(60 * 60))
166        },
167    )
168}