notmuch-tagrewriter 0.1.0

Retag notmuch mails
Documentation
use crate::{
    common::Tag,
    dnf::{DisjunctiveNormalForm, Formula, Litteral, LitteralTrait},
};
use std::{fmt::Display, str::FromStr};

peg::parser! {
    grammar query_parser() for str {

        rule and() = ( _ ("and" / "AND" / "&" / "&&") _) / _
        rule or() = _ ("or" / "OR" / "|" / "||") _
        rule not() = ("not" / "NOT" / "!" / "-") _

        use BooleanFormula::{Or, And, Not};

        rule expression() -> BooleanQuery = precedence! {
            l:@ or() r:(@) { Or(Box::new(l), Box::new(r)) }
            --
            l:@ and() r:(@) { And(Box::new(l), Box::new(r)) }
            --
            not() e:@ { Not(Box::new(e)) }
            --
            "(" _opt() e:expression() _opt() ")" { e }
            "tag:" t:$([^ ' ' | '\t' | '(' | ')']+) { BooleanQuery::Litteral(Tag::from(t.to_string())) }
        }

        rule _() = quiet!{[' ' | '\t']+}
        rule _opt() = quiet!{[' ' | '\t']*}

        pub rule expr() -> BooleanQuery = _opt() e:expression() _opt() { e }
    }
}

/// A type for boolean formulae
#[derive(Debug, Clone)]
pub enum BooleanFormula<T> {
    And(Box<Self>, Box<Self>),
    Or(Box<Self>, Box<Self>),
    Not(Box<Self>),
    Litteral(T),
}

/// Type for queries passed to notmuch
pub type BooleanQuery = BooleanFormula<Tag>;
impl BooleanQuery {
    /// A unicode representation of a query
    fn symbolic_repr(&self) -> String {
        use BooleanFormula::*;
        match self {
            And(l, r) => format!("({}) ∧ ({})", l.symbolic_repr(), r.symbolic_repr()),
            Or(l, r) => format!("({}) ∨ ({})", l.symbolic_repr(), r.symbolic_repr()),
            Litteral(tag) => format!("#{}", tag.as_str()),
            Not(p) => format!("¬({})", p.symbolic_repr()),
        }
    }
}

impl<T: LitteralTrait> From<BooleanFormula<T>> for DisjunctiveNormalForm<T> {
    fn from(value: BooleanFormula<T>) -> Self {
        match value {
            BooleanFormula::Litteral(t) => Litteral::Val(t).into(),
            BooleanFormula::Or(l, r) => DisjunctiveNormalForm::from(*l).or(r.as_ref()),
            BooleanFormula::And(l, r) => DisjunctiveNormalForm::from(*l).and(r.as_ref()),
            BooleanFormula::Not(p) => DisjunctiveNormalForm::from(*p).not(),
        }
    }
}

impl Display for BooleanQuery {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.symbolic_repr())
    }
}

impl FromStr for BooleanQuery {
    type Err = peg::error::ParseError<<str as peg::Parse>::PositionRepr>;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        query_parser::expr(s)
    }
}