use std::fmt::{self, Write};
use crate::formatter::{FormatTable, Render, template::TemplateItem};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(super) struct Condition<'a> {
pub tokens: Vec<ConditionalToken<'a>>,
}
impl Condition<'_> {
pub(super) fn solve(&self, format_table: &FormatTable) -> bool {
enum Operator {
Or(bool),
And(bool),
Nor(bool),
Nand(bool),
}
let mut operators = Vec::new();
let mut condition = false;
let mut inverted = false;
let mut can_invert = true;
let mut iter = self.tokens.iter().peekable();
while let Some(token) = iter.next() {
match token {
ConditionalToken::Arg(arg) => {
let mut state = !arg.render(format_table).is_empty();
while let Some(ConditionalToken::Arg(peek_arg)) = iter.peek() {
if !state {
state = !peek_arg.render(format_table).is_empty();
}
iter.next();
}
condition = conditional_invert(state, inverted);
inverted = false;
can_invert = false;
}
ConditionalToken::Or => {
if let Some(ConditionalToken::Or) = iter.peek() {
operators.push(Operator::Or(condition));
iter.next();
can_invert = true;
} else {
condition = true;
can_invert = false;
}
}
ConditionalToken::And => {
if let Some(ConditionalToken::And) = iter.peek() {
operators.push(Operator::And(condition));
iter.next();
can_invert = true;
} else {
condition = true;
can_invert = false;
}
}
ConditionalToken::Not => match iter.peek() {
Some(ConditionalToken::Or) => {
operators.push(Operator::Nor(condition));
iter.next();
can_invert = true;
}
Some(ConditionalToken::And) => {
operators.push(Operator::Nand(condition));
iter.next();
can_invert = true;
}
_ if can_invert => inverted = !inverted,
_ => {
condition = true;
can_invert = false;
}
},
}
}
condition = conditional_invert(condition, inverted);
operators.reverse();
for operator in operators {
condition = match operator {
Operator::Or(lhs) => lhs || condition,
Operator::And(lhs) => lhs && condition,
Operator::Nor(lhs) => !(lhs || condition),
Operator::Nand(lhs) => !(lhs && condition),
}
}
condition
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum ConditionalToken<'a> {
Arg(TemplateItem<'a>),
Or,
And,
Not,
}
impl ConditionalToken<'_> {
pub fn to_str(&self) -> &'static str {
match self {
ConditionalToken::Or => "|",
ConditionalToken::And => "&",
ConditionalToken::Not => "!",
ConditionalToken::Arg(_) => unreachable!(
"Attempted to turn ConditionalToken::Arg into a &str. This should never happen"
),
}
}
}
fn conditional_invert(condition: bool, invert: bool) -> bool {
if invert { !condition } else { condition }
}
impl fmt::Display for ConditionalToken<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConditionalToken::Or => f.write_char('|'),
ConditionalToken::And => f.write_char('&'),
ConditionalToken::Not => f.write_char('!'),
ConditionalToken::Arg(arg) => arg.fmt(f),
}
}
}
impl fmt::Display for Condition<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for token in &self.tokens {
token.fmt(f)?;
}
Ok(())
}
}