use peg::parser;
use serde::de::{Deserialize, Deserializer, Visitor};
use std::str;
use std::time::Duration;
#[derive(Debug, PartialEq)]
pub enum Scalar {
Size(u64),
Time(Duration),
}
impl Scalar {
pub fn is_size(&self) -> bool {
match self {
Scalar::Size(_) => true,
_ => false,
}
}
pub fn is_time(&self) -> bool {
match self {
Scalar::Time(_) => true,
_ => false,
}
}
}
#[derive(Debug, PartialEq)]
pub enum Token {
Size,
Age,
}
#[derive(Debug, PartialEq)]
pub enum Operator {
Greater,
GreaterEqual,
Lesser,
LesserEqual,
Equal,
NotEqual,
}
impl Operator {
pub fn compare<T>(&self, lhs: &T, rhs: &T) -> bool
where
T: PartialOrd<T>,
{
match self {
Operator::Greater => lhs > rhs,
Operator::GreaterEqual => lhs >= rhs,
Operator::Equal => lhs == rhs,
Operator::Lesser => lhs < rhs,
Operator::LesserEqual => lhs <= rhs,
Operator::NotEqual => lhs != rhs,
}
}
}
#[derive(Debug, PartialEq)]
pub enum Predicate {
Comparison {
token: Token,
operator: Operator,
value: Scalar,
},
}
impl str::FromStr for Predicate {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(predicates::predicate(s).unwrap())
}
}
struct PredicateVisitor;
impl<'de> Visitor<'de> for PredicateVisitor {
type Value = Predicate;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "AAAAAAAAAAAAAA")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(s.parse::<Predicate>().unwrap())
}
}
impl<'de> Deserialize<'de> for Predicate {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(PredicateVisitor)
}
}
parser! {
grammar predicates() for str {
rule token() -> Token
= "age" { Token::Age }
/ "size" { Token::Size }
rule number() -> u64
= n:$(['0'..='9']) { n.parse().unwrap() }
rule scalar_size() -> Scalar
= n:number() " "? q:scalar_size_quantif() { Scalar::Size(n * q) }
rule scalar_size_quantif() -> u64
= "B" { 1 }
/ "KB" { 2u64.pow(10) }
/ "MB" { 2u64.pow(10 * 2) }
/ "GB" { 2u64.pow(10 * 3) }
rule duration_quantif() -> u64
= ("seconds" / "second" / "secs" / "sec" / "s") { 1 }
/ ("minutes" / "minute" / "min" / "m") { 60 }
/ ("hours" / "hour" / "h") { 60 * 60 }
/ ("days" / "day" / "d") { 24 * 60 * 60 }
/ ("weeks" / "week" / "w") { 7 * 24 * 60 * 60 }
rule scalar_age() -> Scalar
= n:number() " " q:duration_quantif() { Scalar::Time(Duration::from_secs(n as u64 * q)) }
rule scalar() -> Scalar
= scalar_size() / scalar_age()
rule operator() -> Operator
= ">" { Operator::Greater }
/ "<" { Operator::Lesser }
/ ">=" { Operator::GreaterEqual }
/ "<=" { Operator::LesserEqual }
/ "==" { Operator::Equal }
/ "!=" { Operator::NotEqual }
pub rule predicate() -> Predicate
= t:token() " " operator() " " s:scalar() { Predicate::Comparison { token: t, operator: Operator::Greater, value: s } }
}
}
#[test]
fn can_parse_size_predicate() {
assert_eq!(
predicates::predicate("size > 1 MB").unwrap(),
Predicate::Comparison {
token: Token::Size,
operator: Operator::Greater,
value: Scalar::Size(1024 * 1024)
},
)
}
#[test]
fn can_parse_time_predicates() {
assert_eq!(
predicates::predicate("age > 1 h").unwrap(),
Predicate::Comparison {
token: Token::Age,
operator: Operator::Greater,
value: Scalar::Time(Duration::from_secs(60 * 60))
},
)
}