1use rust_rule_engine::engine::facts::Facts;
2use rust_rule_engine::engine::knowledge_base::KnowledgeBase;
3use rust_rule_engine::engine::rule::{Condition, ConditionGroup, Rule};
4use rust_rule_engine::engine::{EngineConfig, RustRuleEngine};
5use rust_rule_engine::types::{ActionType, LogicalOperator, Operator, Value};
6use std::collections::HashMap;
7
8fn main() -> Result<(), Box<dyn std::error::Error>> {
9 println!("=== Demo: Fraud Detection System ===\n");
10
11 let mut transaction_props = HashMap::new();
13 transaction_props.insert("Amount".to_string(), Value::Number(5000.0));
14 transaction_props.insert("Location".to_string(), Value::String("FOREIGN".to_string()));
15 transaction_props.insert("Time".to_string(), Value::String("02:30".to_string()));
16 transaction_props.insert("CardType".to_string(), Value::String("CREDIT".to_string()));
17 transaction_props.insert(
18 "MerchantCategory".to_string(),
19 Value::String("CASINO".to_string()),
20 );
21
22 let mut account_props = HashMap::new();
23 account_props.insert("Balance".to_string(), Value::Number(2000.0));
24 account_props.insert("DailyLimit".to_string(), Value::Number(3000.0));
25 account_props.insert("RiskLevel".to_string(), Value::String("LOW".to_string()));
26 account_props.insert("IsActive".to_string(), Value::Boolean(true));
27 account_props.insert(
28 "LastLoginLocation".to_string(),
29 Value::String("DOMESTIC".to_string()),
30 );
31
32 let mut alert_props = HashMap::new();
33 alert_props.insert("FraudScore".to_string(), Value::Number(0.0));
34 alert_props.insert("Status".to_string(), Value::String("PENDING".to_string()));
35 alert_props.insert("Alerts".to_string(), Value::Array(vec![]));
36
37 let facts = Facts::new();
39 facts.add_value("Transaction", Value::Object(transaction_props))?;
40 facts.add_value("Account", Value::Object(account_props))?;
41 facts.add_value("Alert", Value::Object(alert_props))?;
42
43 println!("š Initial transaction data:");
44 if let Some(transaction) = facts.get("Transaction") {
45 println!(" Transaction = {transaction:?}");
46 }
47 if let Some(account) = facts.get("Account") {
48 println!(" Account = {account:?}");
49 }
50 if let Some(alert) = facts.get("Alert") {
51 println!(" Alert = {alert:?}");
52 }
53 println!();
54
55 let mut kb = KnowledgeBase::new("FraudDetectionSystem");
57
58 let high_amount_rule = Rule::new(
60 "HighAmountAlert".to_string(),
61 ConditionGroup::and(
62 ConditionGroup::single(Condition::new(
63 "Transaction.Amount".to_string(),
64 Operator::GreaterThan,
65 Value::Number(3000.0),
66 )),
67 ConditionGroup::single(Condition::new(
68 "Account.DailyLimit".to_string(),
69 Operator::LessThan,
70 Value::String("Transaction.Amount".to_string()),
71 )),
72 ),
73 vec![
74 ActionType::Call {
75 function: "log".to_string(),
76 args: vec![Value::String(
77 "HIGH AMOUNT ALERT: Transaction exceeds daily limit".to_string(),
78 )],
79 },
80 ActionType::MethodCall {
81 object: "Alert".to_string(),
82 method: "setFraudScore".to_string(),
83 args: vec![Value::Number(50.0)],
84 },
85 ActionType::Set {
86 field: "Alert.Status".to_string(),
87 value: Value::String("FLAGGED".to_string()),
88 },
89 ],
90 )
91 .with_salience(10);
92
93 let foreign_location_rule = Rule::new(
95 "ForeignLocationAlert".to_string(),
96 ConditionGroup::and(
97 ConditionGroup::single(Condition::new(
98 "Transaction.Location".to_string(),
99 Operator::Equal,
100 Value::String("FOREIGN".to_string()),
101 )),
102 ConditionGroup::single(Condition::new(
103 "Account.LastLoginLocation".to_string(),
104 Operator::Equal,
105 Value::String("DOMESTIC".to_string()),
106 )),
107 ),
108 vec![
109 ActionType::Call {
110 function: "log".to_string(),
111 args: vec![Value::String(
112 "LOCATION ALERT: Foreign transaction detected".to_string(),
113 )],
114 },
115 ActionType::Call {
116 function: "sum".to_string(),
117 args: vec![
118 Value::String("Alert.FraudScore".to_string()),
119 Value::Number(30.0),
120 ],
121 },
122 ],
123 )
124 .with_salience(8);
125
126 let late_night_rule = Rule::new(
128 "LateNightAlert".to_string(),
129 ConditionGroup::single(Condition::new(
130 "Transaction.Time".to_string(),
131 Operator::Contains,
132 Value::String("02:".to_string()),
133 )),
134 vec![
135 ActionType::Call {
136 function: "log".to_string(),
137 args: vec![Value::String(
138 "TIME ALERT: Late night transaction".to_string(),
139 )],
140 },
141 ActionType::MethodCall {
142 object: "Alert".to_string(),
143 method: "setFraudScore".to_string(),
144 args: vec![Value::Number(20.0)],
145 },
146 ],
147 )
148 .with_salience(6);
149
150 let high_risk_merchant_rule = Rule::new(
152 "HighRiskMerchantAlert".to_string(),
153 ConditionGroup::single(Condition::new(
154 "Transaction.MerchantCategory".to_string(),
155 Operator::Equal,
156 Value::String("CASINO".to_string()),
157 )),
158 vec![
159 ActionType::Call {
160 function: "log".to_string(),
161 args: vec![Value::String(
162 "MERCHANT ALERT: High-risk merchant category".to_string(),
163 )],
164 },
165 ActionType::MethodCall {
166 object: "Alert".to_string(),
167 method: "setFraudScore".to_string(),
168 args: vec![Value::Number(40.0)],
169 },
170 ],
171 )
172 .with_salience(7);
173
174 let critical_fraud_rule = Rule::new(
176 "CriticalFraudAlert".to_string(),
177 ConditionGroup::single(Condition::new(
178 "Alert.FraudScore".to_string(),
179 Operator::GreaterThanOrEqual,
180 Value::Number(70.0),
181 )),
182 vec![
183 ActionType::Call {
184 function: "log".to_string(),
185 args: vec![Value::String(
186 "šØ CRITICAL FRAUD ALERT! Blocking transaction!".to_string(),
187 )],
188 },
189 ActionType::Set {
190 field: "Alert.Status".to_string(),
191 value: Value::String("BLOCKED".to_string()),
192 },
193 ActionType::Call {
194 function: "format".to_string(),
195 args: vec![
196 Value::String("Transaction #{0} BLOCKED - Fraud Score: {1}".to_string()),
197 Value::String("TX123456".to_string()),
198 Value::String("Alert.FraudScore".to_string()),
199 ],
200 },
201 ],
202 )
203 .with_salience(15);
204
205 let insufficient_funds_rule = Rule::new(
207 "InsufficientFundsAlert".to_string(),
208 ConditionGroup::single(Condition::new(
209 "Account.Balance".to_string(),
210 Operator::LessThan,
211 Value::String("Transaction.Amount".to_string()),
212 )),
213 vec![
214 ActionType::Call {
215 function: "log".to_string(),
216 args: vec![Value::String(
217 "š° INSUFFICIENT FUNDS: Transaction declined".to_string(),
218 )],
219 },
220 ActionType::Set {
221 field: "Alert.Status".to_string(),
222 value: Value::String("DECLINED".to_string()),
223 },
224 ],
225 )
226 .with_salience(20); let _ = kb.add_rule(high_amount_rule);
230 let _ = kb.add_rule(foreign_location_rule);
231 let _ = kb.add_rule(late_night_rule);
232 let _ = kb.add_rule(high_risk_merchant_rule);
233 let _ = kb.add_rule(critical_fraud_rule);
234 let _ = kb.add_rule(insufficient_funds_rule);
235
236 let config = EngineConfig {
238 debug_mode: true,
239 max_cycles: 10,
240 ..Default::default()
241 };
242 let engine = RustRuleEngine::with_config(kb, config);
243
244 println!("š Running fraud detection analysis...");
245 let result = engine.execute(&facts)?;
246
247 println!("\nš Fraud Detection Results:");
248 println!(" Cycles: {}", result.cycle_count);
249 println!(" Rules evaluated: {}", result.rules_evaluated);
250 println!(" Rules fired: {}", result.rules_fired);
251 println!(" Execution time: {:?}", result.execution_time);
252
253 println!("\nš Final analysis:");
254 if let Some(transaction) = facts.get("Transaction") {
255 println!(" Transaction = {transaction:?}");
256 }
257 if let Some(account) = facts.get("Account") {
258 println!(" Account = {account:?}");
259 }
260 if let Some(alert) = facts.get("Alert") {
261 println!(" Alert = {alert:?}");
262 }
263
264 println!("\nšÆ Fraud Detection Rules Demonstrated:");
265 println!(" šØ High Amount Transactions");
266 println!(" š Foreign Location Detection");
267 println!(" š Late Night Activity");
268 println!(" š° High-Risk Merchant Categories");
269 println!(" ā ļø Critical Fraud Score Alerts");
270 println!(" š° Insufficient Funds Checks");
271
272 Ok(())
273}