use std::collections::HashMap; use crate::error::{WorkflowError, WorkflowResult}; /// 简单表达式求值器。 /// /// 支持的比较运算符:>, >=, <, <=, ==, != /// 支持 && 和 || 逻辑运算。 /// 操作数可以是变量名(从 variables map 查找)或字面量(数字、字符串)。 /// /// 示例: /// - `amount > 1000` /// - `status == "approved"` /// - `score >= 60 && attendance > 80` pub struct ExpressionEvaluator; impl ExpressionEvaluator { /// 求值单个条件表达式。 /// /// 表达式格式: `{left} {op} {right}` 或复合表达式 `{expr1} && {expr2}` pub fn eval( expr: &str, variables: &HashMap, ) -> WorkflowResult { let expr = expr.trim(); // 处理逻辑 OR if let Some(idx) = Self::find_logical_op(expr, "||") { let left = &expr[..idx]; let right = &expr[idx + 2..]; return Ok(Self::eval(left, variables)? || Self::eval(right, variables)?); } // 处理逻辑 AND if let Some(idx) = Self::find_logical_op(expr, "&&") { let left = &expr[..idx]; let right = &expr[idx + 2..]; return Ok(Self::eval(left, variables)? && Self::eval(right, variables)?); } // 处理单个比较表达式 Self::eval_comparison(expr, variables) } /// 查找逻辑运算符位置,跳过引号内的内容。 fn find_logical_op(expr: &str, op: &str) -> Option { let mut in_string = false; let mut string_char = ' '; let chars: Vec = expr.chars().collect(); let op_chars: Vec = op.chars().collect(); let op_len = op_chars.len(); for i in 0..chars.len().saturating_sub(op_len - 1) { let c = chars[i]; if !in_string && (c == '"' || c == '\'') { in_string = true; string_char = c; continue; } if in_string && c == string_char { in_string = false; continue; } if in_string { continue; } if chars[i..].starts_with(&op_chars) { return Some(i); } } None } /// 求值单个比较表达式。 fn eval_comparison( expr: &str, variables: &HashMap, ) -> WorkflowResult { let operators = [">=", "<=", "!=", "==", ">", "<"]; for op in &operators { if let Some(idx) = Self::find_comparison_op(expr, op) { let left = expr[..idx].trim(); let right = expr[idx + op.len()..].trim(); let left_val = Self::resolve_value(left, variables)?; let right_val = Self::resolve_value(right, variables)?; return Self::compare(&left_val, &right_val, op); } } Err(WorkflowError::ExpressionError(format!( "无法解析表达式: '{}'", expr ))) } /// 查找比较运算符位置,跳过引号内的内容。 fn find_comparison_op(expr: &str, op: &str) -> Option { let mut in_string = false; let mut string_char = ' '; let bytes = expr.as_bytes(); let op_bytes = op.as_bytes(); let op_len = op_bytes.len(); for i in 0..bytes.len().saturating_sub(op_len - 1) { let c = bytes[i] as char; if !in_string && (c == '"' || c == '\'') { in_string = true; string_char = c; continue; } if in_string && c == string_char { in_string = false; continue; } if in_string { continue; } if bytes[i..].starts_with(op_bytes) { // 确保不是被嵌在其他运算符里(如 != 中的 =) // 对于 > 和 < 检查后面不是 = 或 > if op == ">" || op == "<" { if i + op_len < bytes.len() { let next = bytes[i + op_len] as char; if next == '=' || (op == ">" && next == '>') { continue; } } // 也检查前面不是 ! 或 = 或 < 或 > if i > 0 { let prev = bytes[i - 1] as char; if prev == '!' || prev == '=' || prev == '<' || prev == '>' { continue; } } } // 对于 ==, >=, <=, != 确保前面不是 ! 或 = (避免匹配到 == 中的第二个 =) // 这已经通过从长到短匹配处理了 return Some(i); } } None } /// 解析值:字符串字面量、数字字面量或变量引用。 fn resolve_value( token: &str, variables: &HashMap, ) -> WorkflowResult { let token = token.trim(); // 字符串字面量 if (token.starts_with('"') && token.ends_with('"')) || (token.starts_with('\'') && token.ends_with('\'')) { return Ok(serde_json::Value::String( token[1..token.len() - 1].to_string(), )); } // 数字字面量 if let Ok(n) = token.parse::() { return Ok(serde_json::Value::Number(n.into())); } if let Ok(f) = token.parse::() && let Some(n) = serde_json::Number::from_f64(f) { return Ok(serde_json::Value::Number(n)); } // 布尔字面量 if token == "true" { return Ok(serde_json::Value::Bool(true)); } if token == "false" { return Ok(serde_json::Value::Bool(false)); } // 变量引用 if let Some(val) = variables.get(token) { return Ok(val.clone()); } Err(WorkflowError::ExpressionError(format!( "未知的变量或值: '{}'", token ))) } /// 比较两个 JSON 值。 fn compare( left: &serde_json::Value, right: &serde_json::Value, op: &str, ) -> WorkflowResult { match op { "==" => Ok(Self::values_equal(left, right)), "!=" => Ok(!Self::values_equal(left, right)), ">" => Ok(Self::values_compare(left, right)? == std::cmp::Ordering::Greater), ">=" => Ok(Self::values_compare(left, right)? != std::cmp::Ordering::Less), "<" => Ok(Self::values_compare(left, right)? == std::cmp::Ordering::Less), "<=" => Ok(Self::values_compare(left, right)? != std::cmp::Ordering::Greater), _ => Err(WorkflowError::ExpressionError(format!( "不支持的比较运算符: '{}'", op ))), } } fn values_equal(left: &serde_json::Value, right: &serde_json::Value) -> bool { // 数值比较:允许整数和浮点数互比 if left.is_number() && right.is_number() { return left.as_f64() == right.as_f64(); } left == right } fn values_compare( left: &serde_json::Value, right: &serde_json::Value, ) -> WorkflowResult { if left.is_number() && right.is_number() { let l = left.as_f64().unwrap_or(0.0); let r = right.as_f64().unwrap_or(0.0); return Ok(l.partial_cmp(&r).unwrap_or(std::cmp::Ordering::Equal)); } if let (Some(l), Some(r)) = (left.as_str(), right.as_str()) { return Ok(l.cmp(r)); } Err(WorkflowError::ExpressionError(format!( "无法比较 {:?} 和 {:?}", left, right ))) } } #[cfg(test)] mod tests { use super::*; use serde_json::json; fn make_vars() -> HashMap { let mut m = HashMap::new(); m.insert("amount".to_string(), json!(1500)); m.insert("status".to_string(), json!("approved")); m.insert("score".to_string(), json!(85)); m.insert("name".to_string(), json!("Alice")); m.insert("active".to_string(), json!(true)); m } #[test] fn test_number_greater_than() { let vars = make_vars(); assert!(ExpressionEvaluator::eval("amount > 1000", &vars).unwrap()); assert!(!ExpressionEvaluator::eval("amount > 2000", &vars).unwrap()); } #[test] fn test_number_less_than() { let vars = make_vars(); assert!(ExpressionEvaluator::eval("amount < 2000", &vars).unwrap()); assert!(!ExpressionEvaluator::eval("amount < 1000", &vars).unwrap()); } #[test] fn test_number_equals() { let vars = make_vars(); assert!(ExpressionEvaluator::eval("amount == 1500", &vars).unwrap()); assert!(!ExpressionEvaluator::eval("amount == 1000", &vars).unwrap()); } #[test] fn test_string_equals() { let vars = make_vars(); assert!(ExpressionEvaluator::eval("status == \"approved\"", &vars).unwrap()); assert!(!ExpressionEvaluator::eval("status == \"rejected\"", &vars).unwrap()); } #[test] fn test_string_not_equals() { let vars = make_vars(); assert!(ExpressionEvaluator::eval("status != \"rejected\"", &vars).unwrap()); } #[test] fn test_greater_or_equal() { let vars = make_vars(); assert!(ExpressionEvaluator::eval("amount >= 1500", &vars).unwrap()); assert!(ExpressionEvaluator::eval("amount >= 1000", &vars).unwrap()); assert!(!ExpressionEvaluator::eval("amount >= 2000", &vars).unwrap()); } #[test] fn test_logical_and() { let vars = make_vars(); assert!(ExpressionEvaluator::eval("amount > 1000 && score > 80", &vars).unwrap()); assert!(!ExpressionEvaluator::eval("amount > 2000 && score > 80", &vars).unwrap()); } #[test] fn test_logical_or() { let vars = make_vars(); assert!(ExpressionEvaluator::eval("amount > 2000 || score > 80", &vars).unwrap()); assert!(!ExpressionEvaluator::eval("amount > 2000 || score > 90", &vars).unwrap()); } #[test] fn test_unknown_variable() { let vars = make_vars(); let result = ExpressionEvaluator::eval("unknown > 0", &vars); assert!(result.is_err()); } #[test] fn test_invalid_expression() { let vars = make_vars(); let result = ExpressionEvaluator::eval("justavariable", &vars); assert!(result.is_err()); } }