diff --git a/yazi-shared/src/condition.rs b/yazi-shared/src/condition.rs index e348045e..9f608924 100644 --- a/yazi-shared/src/condition.rs +++ b/yazi-shared/src/condition.rs @@ -1,4 +1,4 @@ -use std::str::FromStr; +use std::{cmp::Ordering, str::FromStr}; use anyhow::bail; use serde_with::DeserializeFromStr; @@ -33,8 +33,7 @@ impl ConditionOp { } } - #[inline] - pub fn prec(&self) -> u8 { + fn prec(&self) -> u8 { match self { Self::Or => 1, Self::And => 2, @@ -44,6 +43,18 @@ impl ConditionOp { } } +impl PartialOrd for ConditionOp { + fn partial_cmp(&self, other: &Self) -> Option { + use Ordering::*; + + match self.prec().cmp(&other.prec()) { + // Keep repeated `!` right-associative by making `! >= !` false. + Equal if matches!((self, other), (Self::Not, Self::Not)) => None, + ordering => Some(ordering), + } + } +} + #[derive(Debug, DeserializeFromStr)] pub struct Condition { ops: Vec, @@ -72,7 +83,7 @@ impl Condition { let op = ConditionOp::new(token); match op { ConditionOp::Or | ConditionOp::And | ConditionOp::Not => { - while matches!(stack.last(), Some(last) if last.prec() >= op.prec()) { + while matches!(stack.last(), Some(last) if last >= &op) { output.push(stack.pop().unwrap()); } stack.push(op); @@ -129,3 +140,21 @@ impl Condition { if stack.len() == 1 { Some(stack[0]) } else { None } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_condition_not() -> anyhow::Result<()> { + let cond: Condition = "!dir".parse()?; + assert!(!cond.eval(|s| s == "dir").unwrap()); + assert!(cond.eval(|_| false).unwrap()); + + let cond: Condition = "!!dir".parse()?; + assert!(cond.eval(|s| s == "dir").unwrap()); + assert!(!cond.eval(|_| false).unwrap()); + + Ok(()) + } +}