#[cfg(test)]
use std::fmt;
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Expression {
Const(bool),
Check(LeftHandSide, String),
And(Box<Expression>, Box<Expression>),
Or(Box<Expression>, Box<Expression>),
Not(Box<Expression>),
}
#[cfg(test)]
impl fmt::Display for Expression {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Expression::*;
match self {
Const(true) => f.write_str("@"),
Const(false) => f.write_str("!"),
Check(lhs, rhs) => write!(f, "{lhs}:{rhs}"),
Not(e) => write!(f, "not {e}"),
Or(lhs, rhs) => write!(f, "{lhs} or {rhs}"),
And(lhs, rhs) => match (&**lhs, &**rhs) {
(Or(_, _), Or(_, _)) => write!(f, "({lhs}) and ({rhs})"),
(Or(_, _), _) => write!(f, "({lhs}) and {rhs}"),
(_, Or(_, _)) => write!(f, "{lhs} and ({rhs})"),
(_, _) => write!(f, "{lhs} and {rhs}"),
},
}
}
}
#[cfg(test)]
impl From<bool> for Expression {
fn from(x: bool) -> Expression {
Expression::Const(x)
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum LeftHandSide {
Literal(String),
Identifier(String),
}
#[cfg(test)]
impl fmt::Display for LeftHandSide {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use LeftHandSide::*;
match self {
Literal(s) => write!(f, "'{s}'"),
Identifier(s) => f.write_str(s),
}
}
}
#[cfg(test)]
pub mod build {
use super::{Expression, LeftHandSide};
pub fn make_check(l: impl Into<String>, r: impl Into<String>) -> Expression {
Expression::Check(LeftHandSide::Identifier(l.into()), r.into())
}
pub fn make_literal_check(l: impl Into<String>, r: impl Into<String>) -> Expression {
Expression::Check(LeftHandSide::Literal(l.into()), r.into())
}
pub fn make_and(l: impl Into<Expression>, r: impl Into<Expression>) -> Expression {
Expression::And(Box::new(l.into()), Box::new(r.into()))
}
pub fn make_or(l: impl Into<Expression>, r: impl Into<Expression>) -> Expression {
Expression::Or(Box::new(l.into()), Box::new(r.into()))
}
pub fn make_not(e: impl Into<Expression>) -> Expression {
Expression::Not(Box::new(e.into()))
}
}
#[cfg(test)]
mod tests {
use super::build::*;
#[test]
fn test_serialization() {
let expr = make_and(true, false);
assert_eq!(expr.to_string(), "@ and !");
let expr = make_or(true, false);
assert_eq!(expr.to_string(), "@ or !");
let expr = make_or(true, make_or(false, make_not(true)));
assert_eq!(expr.to_string(), "@ or ! or not @");
let expr = make_or(make_or(true, false), make_not(true));
assert_eq!(expr.to_string(), "@ or ! or not @");
let expr = make_or(true, make_and(false, make_not(true)));
assert_eq!(expr.to_string(), "@ or ! and not @");
let expr = make_or(make_and(true, false), make_not(true));
assert_eq!(expr.to_string(), "@ and ! or not @");
let expr = make_and(true, make_and(false, make_not(true)));
assert_eq!(expr.to_string(), "@ and ! and not @");
let expr = make_and(make_and(true, false), make_not(true));
assert_eq!(expr.to_string(), "@ and ! and not @");
let expr = make_and(true, make_or(false, make_not(true)));
assert_eq!(expr.to_string(), "@ and (! or not @)");
let expr = make_and(make_or(true, false), make_not(true));
assert_eq!(expr.to_string(), "(@ or !) and not @");
}
}