use crate::context::StatContext;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "operator")]
pub enum ConditionDef {
Equals {
key: String,
value: serde_json::Value,
},
NotEquals {
key: String,
value: serde_json::Value,
},
GreaterThan { key: String, value: f64 },
LessThan { key: String, value: f64 },
And { conditions: Vec<ConditionDef> },
Or { conditions: Vec<ConditionDef> },
Not { condition: Box<ConditionDef> },
}
impl ConditionDef {
pub fn build_closure(&self) -> Box<dyn Fn(&StatContext) -> bool + Send + Sync> {
let def = self.clone();
Box::new(move |ctx| def.evaluate(ctx))
}
pub fn evaluate(&self, ctx: &StatContext) -> bool {
match self {
ConditionDef::Equals { key, value } => {
if let Some(ctx_val) = ctx.get::<serde_json::Value>(key) {
ctx_val == *value
} else {
false
}
}
ConditionDef::NotEquals { key, value } => {
if let Some(ctx_val) = ctx.get::<serde_json::Value>(key) {
ctx_val != *value
} else {
true
}
}
ConditionDef::GreaterThan { key, value } => {
if let Some(ctx_val) = ctx.get::<f64>(key) {
ctx_val > *value
} else {
false
}
}
ConditionDef::LessThan { key, value } => {
if let Some(ctx_val) = ctx.get::<f64>(key) {
ctx_val < *value
} else {
false
}
}
ConditionDef::And { conditions } => conditions.iter().all(|c| c.evaluate(ctx)),
ConditionDef::Or { conditions } => conditions.iter().any(|c| c.evaluate(ctx)),
ConditionDef::Not { condition } => !condition.evaluate(ctx),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_condition_evaluation() {
let mut ctx = StatContext::new();
ctx.set("is_undead", true);
ctx.set("enemy_level", 50);
let equals = ConditionDef::Equals {
key: "is_undead".to_string(),
value: serde_json::json!(true),
};
assert!(equals.evaluate(&ctx));
let not_equals = ConditionDef::NotEquals {
key: "is_undead".to_string(),
value: serde_json::json!(false),
};
assert!(not_equals.evaluate(&ctx));
let greater = ConditionDef::GreaterThan {
key: "enemy_level".to_string(),
value: 40.0,
};
assert!(greater.evaluate(&ctx));
let less = ConditionDef::LessThan {
key: "enemy_level".to_string(),
value: 40.0,
};
assert!(!less.evaluate(&ctx));
let and_cond = ConditionDef::And {
conditions: vec![equals.clone(), greater.clone()],
};
assert!(and_cond.evaluate(&ctx));
}
}