use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ContextExpr<P> {
Pattern(P),
WordBoundary,
And(Box<ContextExpr<P>>, Box<ContextExpr<P>>),
Or(Box<ContextExpr<P>>, Box<ContextExpr<P>>),
Not(Box<ContextExpr<P>>),
}
impl<P> ContextExpr<P> {
pub fn pattern(pattern: P) -> Self {
ContextExpr::Pattern(pattern)
}
pub fn word_boundary() -> Self {
ContextExpr::WordBoundary
}
pub fn and(left: ContextExpr<P>, right: ContextExpr<P>) -> Self {
ContextExpr::And(Box::new(left), Box::new(right))
}
pub fn or(left: ContextExpr<P>, right: ContextExpr<P>) -> Self {
ContextExpr::Or(Box::new(left), Box::new(right))
}
pub fn not(inner: ContextExpr<P>) -> Self {
ContextExpr::Not(Box::new(inner))
}
}
impl<P: fmt::Display> fmt::Display for ContextExpr<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ContextExpr::Pattern(p) => write!(f, "{}", p),
ContextExpr::WordBoundary => write!(f, "#"),
ContextExpr::And(left, right) => write!(f, "({} & {})", left, right),
ContextExpr::Or(left, right) => write!(f, "({} | {})", left, right),
ContextExpr::Not(inner) => write!(f, "!{}", inner),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq, Eq)]
struct TestPattern(String);
impl fmt::Display for TestPattern {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[test]
fn test_context_expr_pattern() {
let expr: ContextExpr<TestPattern> = ContextExpr::pattern(TestPattern("abc".to_string()));
assert_eq!(expr.to_string(), "abc");
}
#[test]
fn test_context_expr_word_boundary() {
let expr: ContextExpr<TestPattern> = ContextExpr::word_boundary();
assert_eq!(expr.to_string(), "#");
}
#[test]
fn test_context_expr_and() {
let left = ContextExpr::pattern(TestPattern("a".to_string()));
let right = ContextExpr::pattern(TestPattern("b".to_string()));
let expr = ContextExpr::and(left, right);
assert_eq!(expr.to_string(), "(a & b)");
}
#[test]
fn test_context_expr_or() {
let left = ContextExpr::pattern(TestPattern("a".to_string()));
let right = ContextExpr::pattern(TestPattern("b".to_string()));
let expr = ContextExpr::or(left, right);
assert_eq!(expr.to_string(), "(a | b)");
}
#[test]
fn test_context_expr_not() {
let inner = ContextExpr::pattern(TestPattern("a".to_string()));
let expr = ContextExpr::not(inner);
assert_eq!(expr.to_string(), "!a");
}
#[test]
fn test_context_expr_complex() {
let a = ContextExpr::pattern(TestPattern("a".to_string()));
let boundary: ContextExpr<TestPattern> = ContextExpr::word_boundary();
let left = ContextExpr::or(a, boundary);
let b = ContextExpr::pattern(TestPattern("b".to_string()));
let right = ContextExpr::not(b);
let expr = ContextExpr::and(left, right);
assert_eq!(expr.to_string(), "((a | #) & !b)");
}
#[test]
fn test_context_expr_equality() {
let expr1: ContextExpr<TestPattern> = ContextExpr::word_boundary();
let expr2: ContextExpr<TestPattern> = ContextExpr::word_boundary();
assert_eq!(expr1, expr2);
let expr3 = ContextExpr::pattern(TestPattern("a".to_string()));
let expr4 = ContextExpr::pattern(TestPattern("a".to_string()));
assert_eq!(expr3, expr4);
}
}