use crate::behavioral_economics::actions::BehaviorAction;
use crate::behavioral_economics::conditions::BehaviorCondition;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub enum RuleType {
Declarative,
Scriptable,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct BehaviorRule {
pub name: String,
pub rule_type: RuleType,
pub condition: BehaviorCondition,
pub action: BehaviorAction,
pub priority: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub script: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub script_language: Option<String>,
#[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")]
pub metadata: std::collections::HashMap<String, Value>,
}
impl BehaviorRule {
pub fn declarative(
name: String,
condition: BehaviorCondition,
action: BehaviorAction,
priority: u32,
) -> Self {
Self {
name,
rule_type: RuleType::Declarative,
condition,
action,
priority,
script: None,
script_language: None,
metadata: std::collections::HashMap::new(),
}
}
pub fn scriptable(
name: String,
condition: BehaviorCondition,
action: BehaviorAction,
priority: u32,
script: String,
script_language: String,
) -> Self {
Self {
name,
rule_type: RuleType::Scriptable,
condition,
action,
priority,
script: Some(script),
script_language: Some(script_language),
metadata: std::collections::HashMap::new(),
}
}
pub fn validate(&self) -> crate::Result<()> {
if self.name.trim().is_empty() {
return Err(crate::Error::internal("Rule name cannot be empty"));
}
if matches!(self.rule_type, RuleType::Scriptable) {
if self.script.is_none() {
return Err(crate::Error::internal("Scriptable rules must have a script"));
}
if self.script_language.is_none() {
return Err(crate::Error::internal("Scriptable rules must have a script_language"));
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::behavioral_economics::actions::BehaviorAction;
use crate::behavioral_economics::conditions::BehaviorCondition;
#[test]
fn test_declarative_rule_creation() {
let rule = BehaviorRule::declarative(
"test-rule".to_string(),
BehaviorCondition::Always,
BehaviorAction::NoOp,
100,
);
assert_eq!(rule.rule_type, RuleType::Declarative);
assert!(rule.script.is_none());
}
#[test]
fn test_scriptable_rule_creation() {
let rule = BehaviorRule::scriptable(
"test-rule".to_string(),
BehaviorCondition::Always,
BehaviorAction::NoOp,
100,
"console.log('test')".to_string(),
"javascript".to_string(),
);
assert_eq!(rule.rule_type, RuleType::Scriptable);
assert!(rule.script.is_some());
}
#[test]
fn test_rule_validation() {
let mut rule = BehaviorRule::declarative(
"test-rule".to_string(),
BehaviorCondition::Always,
BehaviorAction::NoOp,
100,
);
assert!(rule.validate().is_ok());
rule.name = "".to_string();
assert!(rule.validate().is_err());
}
}