jeeves 0.1.0

WIP: Automatically move or delete files
Documentation
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))
        },
    )
}