1use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6#[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#[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#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct RuleCondition {
31 pub field: String,
32 pub operator: RuleOperator,
33 pub value: String,
34}
35
36#[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#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct RuleAction {
51 pub action_type: ActionType,
52 pub target: String,
53 pub value: String,
54}
55
56#[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
67pub 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#[derive(Debug, Clone)]
89pub struct RuleContext {
90 pub data: std::collections::HashMap<String, String>,
91}
92
93#[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}