use serde_json::Value;
pub struct ConditionEvaluator;
impl ConditionEvaluator {
pub fn evaluate_condition(&self, condition: &crate::core::watchdog::TriggerCondition, result: &Value) -> bool {
match condition {
crate::core::watchdog::TriggerCondition::NumericRange { min, max } => {
self.evaluate_numeric_range(result, *min, *max)
}
crate::core::watchdog::TriggerCondition::StringContains { content } => {
self.evaluate_string_contains(result, content)
}
crate::core::watchdog::TriggerCondition::StatusMatches { expected_status } => {
self.evaluate_status_match(result, expected_status)
}
crate::core::watchdog::TriggerCondition::CustomExpression { expression } => {
self.evaluate_custom_expression(result, expression)
}
}
}
fn evaluate_numeric_range(&self, result: &Value, min: f64, max: f64) -> bool {
if let Some(num_val) = result.as_f64() {
return num_val >= min && num_val <= max;
}
if let Some(obj) = result.as_object() {
for field_name in ["value", "result", "data", "score", "count"] {
if let Some(field_value) = obj.get(field_name) {
if let Some(num_val) = field_value.as_f64() {
if num_val >= min && num_val <= max {
return true;
}
}
}
}
return obj.values()
.any(|val| val.as_f64().map(|v| v >= min && v <= max).unwrap_or(false));
}
if let Some(arr) = result.as_array() {
return arr
.iter()
.any(|val| val.as_f64().map(|v| v >= min && v <= max).unwrap_or(false));
}
false
}
fn evaluate_string_contains(&self, result: &Value, content: &str) -> bool {
if let Some(str_val) = result.as_str() {
return str_val.contains(content);
}
let result_str = result.to_string();
result_str.contains(content)
}
fn evaluate_status_match(&self, result: &Value, expected_status: &str) -> bool {
if let Some(str_val) = result.as_str() {
if str_val == expected_status {
return true;
}
}
if let Some(obj) = result.as_object() {
for field_name in ["status", "state", "result", "type"] {
if let Some(field_value) = obj.get(field_name) {
if let Some(status_val) = field_value.as_str() {
if status_val == expected_status {
return true;
}
}
}
}
}
result.to_string() == *expected_status
}
fn evaluate_custom_expression(&self, result: &Value, expression: &str) -> bool {
let expr_lower = expression.to_lowercase();
if expr_lower.contains(">=") {
self.evaluate_comparison(result, ">=", &expr_lower)
} else if expr_lower.contains("<=") {
self.evaluate_comparison(result, "<=", &expr_lower)
} else if expr_lower.contains('>') {
self.evaluate_comparison(result, ">", &expr_lower)
} else if expr_lower.contains('<') {
self.evaluate_comparison(result, "<", &expr_lower)
} else if expr_lower.contains("==") || expr_lower.contains('=') {
self.evaluate_equality(result, &expr_lower)
} else {
self.evaluate_string_contains(result, expression)
}
}
fn evaluate_comparison(&self, result: &Value, op: &str, expression: &str) -> bool {
let parts: Vec<&str> = match op {
">=" => expression.splitn(2, ">=").collect(),
"<=" => expression.splitn(2, "<=").collect(),
">" => expression.splitn(2, '>').collect(),
"<" => expression.splitn(2, '<').collect(),
_ => return false,
};
if parts.len() != 2 {
return false;
}
let _field_name = parts[0].trim();
let threshold_str = parts[1].trim();
if let Ok(threshold) = threshold_str.parse::<f64>() {
if let Some(result_num) = self.extract_number_from_value(result) {
return match op {
">=" => result_num >= threshold,
"<=" => result_num <= threshold,
">" => result_num > threshold,
"<" => result_num < threshold,
_ => false,
};
}
}
false
}
fn evaluate_equality(&self, result: &Value, expression: &str) -> bool {
let parts: Vec<&str> = if expression.contains("==") {
expression.splitn(2, "==").collect()
} else {
expression.splitn(2, '=').collect()
};
if parts.len() != 2 {
return false;
}
let _field_name = parts[0].trim();
let expected_value = parts[1].trim();
if let Some(str_val) = result.as_str() {
return str_val == expected_value;
}
if let Ok(expected_num) = expected_value.parse::<f64>() {
if let Some(result_num) = self.extract_number_from_value(result) {
return (result_num - expected_num).abs() < f64::EPSILON;
}
}
false
}
fn extract_number_from_value(&self, value: &Value) -> Option<f64> {
if let Some(num) = value.as_f64() {
return Some(num);
}
if let Some(str_val) = value.as_str() {
if let Ok(num) = str_val.parse::<f64>() {
return Some(num);
}
}
if let Some(obj) = value.as_object() {
for field_name in ["value", "result", "data", "score", "count"] {
if let Some(field_value) = obj.get(field_name) {
if let Some(num) = field_value.as_f64() {
return Some(num);
}
}
}
}
if let Some(arr) = value.as_array() {
for item in arr {
if let Some(num) = item.as_f64() {
return Some(num);
}
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_numeric_range_evaluation() {
let evaluator = ConditionEvaluator;
assert!(evaluator.evaluate_condition(
&crate::core::watchdog::TriggerCondition::NumericRange { min: 10.0, max: 20.0 },
&json!(15.0)
));
assert!(!evaluator.evaluate_condition(
&crate::core::watchdog::TriggerCondition::NumericRange { min: 10.0, max: 20.0 },
&json!(25.0)
));
assert!(evaluator.evaluate_condition(
&crate::core::watchdog::TriggerCondition::NumericRange { min: 10.0, max: 20.0 },
&json!({"value": 15.0})
));
}
#[test]
fn test_string_contains_evaluation() {
let evaluator = ConditionEvaluator;
assert!(evaluator.evaluate_condition(
&crate::core::watchdog::TriggerCondition::StringContains { content: "success".to_string() },
&json!("operation was successful")
));
assert!(!evaluator.evaluate_condition(
&crate::core::watchdog::TriggerCondition::StringContains { content: "success".to_string() },
&json!("operation failed")
));
}
#[test]
fn test_status_match_evaluation() {
let evaluator = ConditionEvaluator;
assert!(evaluator.evaluate_condition(
&crate::core::watchdog::TriggerCondition::StatusMatches { expected_status: "success".to_string() },
&json!("success")
));
assert!(evaluator.evaluate_condition(
&crate::core::watchdog::TriggerCondition::StatusMatches { expected_status: "success".to_string() },
&json!({"status": "success"})
));
}
#[test]
fn test_custom_expression_evaluation() {
let evaluator = ConditionEvaluator;
assert!(evaluator.evaluate_condition(
&crate::core::watchdog::TriggerCondition::CustomExpression { expression: "value > 10".to_string() },
&json!({"value": 15.0})
));
assert!(evaluator.evaluate_condition(
&crate::core::watchdog::TriggerCondition::CustomExpression { expression: "status = success".to_string() },
&json!("success")
));
}
}