pcm_engine/
rules.rs

1//! Rule engine for catalog management
2
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6/// Catalog rule
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct CatalogRule {
9    pub id: Uuid,
10    pub name: String,
11    pub rule_type: RuleType,
12    pub conditions: Vec<RuleCondition>,
13    pub actions: Vec<RuleAction>,
14    pub priority: u32,
15    pub is_active: bool,
16}
17
18/// Rule type
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
20#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
21pub enum RuleType {
22    Validation,
23    Transformation,
24    Pricing,
25    Eligibility,
26}
27
28/// Rule condition
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct RuleCondition {
31    pub field: String,
32    pub operator: RuleOperator,
33    pub value: String,
34}
35
36/// Rule operator
37#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
38#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
39pub enum RuleOperator {
40    Equals,
41    NotEquals,
42    GreaterThan,
43    LessThan,
44    Contains,
45    Regex,
46}
47
48/// Rule action
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct RuleAction {
51    pub action_type: ActionType,
52    pub target: String,
53    pub value: String,
54}
55
56/// Action type
57#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
58#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
59pub enum ActionType {
60    Set,
61    Add,
62    Remove,
63    Validate,
64    Transform,
65}
66
67/// Evaluate a rule
68pub fn evaluate_rule(rule: &CatalogRule, context: &RuleContext) -> RuleResult {
69    if !rule.is_active {
70        return RuleResult::Skipped;
71    }
72
73    let conditions_met = rule
74        .conditions
75        .iter()
76        .all(|condition| evaluate_condition(condition, context));
77
78    if conditions_met {
79        RuleResult::Matched {
80            actions: rule.actions.clone(),
81        }
82    } else {
83        RuleResult::NotMatched
84    }
85}
86
87/// Rule context
88#[derive(Debug, Clone)]
89pub struct RuleContext {
90    pub data: std::collections::HashMap<String, String>,
91}
92
93/// Rule evaluation result
94#[derive(Debug, Clone)]
95pub enum RuleResult {
96    Matched { actions: Vec<RuleAction> },
97    NotMatched,
98    Skipped,
99}
100
101fn evaluate_condition(condition: &RuleCondition, context: &RuleContext) -> bool {
102    let field_value = context
103        .data
104        .get(&condition.field)
105        .cloned()
106        .unwrap_or_default();
107
108    match condition.operator {
109        RuleOperator::Equals => field_value == condition.value,
110        RuleOperator::NotEquals => field_value != condition.value,
111        RuleOperator::GreaterThan => {
112            if let (Ok(field_num), Ok(cond_num)) =
113                (field_value.parse::<f64>(), condition.value.parse::<f64>())
114            {
115                field_num > cond_num
116            } else {
117                false
118            }
119        }
120        RuleOperator::LessThan => {
121            if let (Ok(field_num), Ok(cond_num)) =
122                (field_value.parse::<f64>(), condition.value.parse::<f64>())
123            {
124                field_num < cond_num
125            } else {
126                false
127            }
128        }
129        RuleOperator::Contains => field_value.contains(&condition.value),
130        RuleOperator::Regex => regex::Regex::new(&condition.value)
131            .map(|re| re.is_match(&field_value))
132            .unwrap_or(false),
133    }
134}