grule_demo/
grule_demo.rs

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, Operator, Value};
6use std::collections::HashMap;
7
8fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
9    println!("šŸš€ Grule-Style Rule Engine Demo");
10    println!("================================\n");
11
12    // Demo 1: Create Knowledge Base and load rules
13    demo_knowledge_base()?;
14
15    // Demo 2: Facts manipulation
16    demo_facts_manipulation()?;
17
18    // Demo 3: Engine execution
19    demo_engine_execution()?;
20
21    // Demo 4: E-commerce scenario
22    demo_ecommerce_scenario()?;
23
24    Ok(())
25}
26
27fn demo_knowledge_base() -> std::result::Result<(), Box<dyn std::error::Error>> {
28    println!("šŸ“š Demo 1: Knowledge Base with Code-based Rules");
29    println!("-----------------------------------------------");
30
31    // Create knowledge base
32    let kb = KnowledgeBase::new("UserRules");
33
34    println!("āœ… Knowledge Base created: {}", kb.get_statistics().name);
35    println!("   Total rules: {}", kb.get_statistics().total_rules);
36    println!("   Version: {}\n", kb.get_statistics().version);
37
38    Ok(())
39}
40
41fn demo_facts_manipulation() -> std::result::Result<(), Box<dyn std::error::Error>> {
42    println!("šŸ—‚ļø Demo 2: Facts Manipulation");
43    println!("-----------------------------");
44
45    // Create facts
46    let facts = Facts::new();
47
48    // Add user data
49    let mut user_props = HashMap::new();
50    user_props.insert("Name".to_string(), Value::String("John Doe".to_string()));
51    user_props.insert("Age".to_string(), Value::Integer(25));
52    user_props.insert("Country".to_string(), Value::String("US".to_string()));
53    user_props.insert("SpendingTotal".to_string(), Value::Number(1500.0));
54    user_props.insert("IsAdult".to_string(), Value::Boolean(false));
55    user_props.insert("IsVIP".to_string(), Value::Boolean(false));
56
57    facts.add_value("User", Value::Object(user_props))?;
58
59    println!("āœ… Facts created and populated:");
60    if let Some(user) = facts.get("User") {
61        println!("   User = {user:?}");
62    }
63    println!();
64
65    Ok(())
66}
67
68fn demo_engine_execution() -> std::result::Result<(), Box<dyn std::error::Error>> {
69    println!("šŸš€ Demo 3: Engine Execution");
70    println!("---------------------------");
71
72    // Create facts
73    let facts = Facts::new();
74    let mut user_props = HashMap::new();
75    user_props.insert("Age".to_string(), Value::Integer(25));
76    user_props.insert("Country".to_string(), Value::String("US".to_string()));
77    user_props.insert("SpendingTotal".to_string(), Value::Number(1500.0));
78    user_props.insert("IsAdult".to_string(), Value::Boolean(false));
79    user_props.insert("IsVIP".to_string(), Value::Boolean(false));
80    user_props.insert("Category".to_string(), Value::String("unknown".to_string()));
81    user_props.insert("DiscountRate".to_string(), Value::Number(0.0));
82
83    facts.add_value("User", Value::Object(user_props))?;
84
85    // Create knowledge base
86    let mut kb = KnowledgeBase::new("UserRules");
87
88    // Rule 1: Adult Check (salience 10)
89    let adult_rule = Rule::new(
90        "AdultCheck".to_string(),
91        ConditionGroup::and(
92            ConditionGroup::single(Condition::new(
93                "User.Age".to_string(),
94                Operator::GreaterThanOrEqual,
95                Value::Integer(18),
96            )),
97            ConditionGroup::single(Condition::new(
98                "User.Country".to_string(),
99                Operator::Equal,
100                Value::String("US".to_string()),
101            )),
102        ),
103        vec![
104            ActionType::MethodCall {
105                object: "User".to_string(),
106                method: "setIsAdult".to_string(),
107                args: vec![Value::Boolean(true)],
108            },
109            ActionType::MethodCall {
110                object: "User".to_string(),
111                method: "setCategory".to_string(),
112                args: vec![Value::String("Adult".to_string())],
113            },
114            ActionType::Call {
115                function: "log".to_string(),
116                args: vec![Value::String("User qualified as adult".to_string())],
117            },
118        ],
119    )
120    .with_salience(10);
121
122    // Rule 2: VIP Check (salience 20)
123    let vip_rule = Rule::new(
124        "VIPCheck".to_string(),
125        ConditionGroup::and(
126            ConditionGroup::and(
127                ConditionGroup::single(Condition::new(
128                    "User.Age".to_string(),
129                    Operator::GreaterThanOrEqual,
130                    Value::Integer(21),
131                )),
132                ConditionGroup::single(Condition::new(
133                    "User.IsAdult".to_string(),
134                    Operator::Equal,
135                    Value::Boolean(true),
136                )),
137            ),
138            ConditionGroup::single(Condition::new(
139                "User.SpendingTotal".to_string(),
140                Operator::GreaterThan,
141                Value::Number(1000.0),
142            )),
143        ),
144        vec![
145            ActionType::MethodCall {
146                object: "User".to_string(),
147                method: "setIsVIP".to_string(),
148                args: vec![Value::Boolean(true)],
149            },
150            ActionType::MethodCall {
151                object: "User".to_string(),
152                method: "setDiscountRate".to_string(),
153                args: vec![Value::Number(0.15)],
154            },
155            ActionType::Call {
156                function: "log".to_string(),
157                args: vec![Value::String("User upgraded to VIP".to_string())],
158            },
159        ],
160    )
161    .with_salience(20);
162
163    // Rule 3: Senior Discount (salience 15)
164    let senior_rule = Rule::new(
165        "SeniorDiscount".to_string(),
166        ConditionGroup::single(Condition::new(
167            "User.Age".to_string(),
168            Operator::GreaterThanOrEqual,
169            Value::Integer(65),
170        )),
171        vec![
172            ActionType::MethodCall {
173                object: "User".to_string(),
174                method: "setDiscountRate".to_string(),
175                args: vec![Value::Number(0.20)],
176            },
177            ActionType::MethodCall {
178                object: "User".to_string(),
179                method: "setCategory".to_string(),
180                args: vec![Value::String("Senior".to_string())],
181            },
182            ActionType::Call {
183                function: "log".to_string(),
184                args: vec![Value::String("Senior discount applied".to_string())],
185            },
186        ],
187    )
188    .with_salience(15);
189
190    // Add rules to knowledge base
191    let _ = kb.add_rule(adult_rule);
192    let _ = kb.add_rule(vip_rule);
193    let _ = kb.add_rule(senior_rule);
194
195    // Create engine
196    let config = EngineConfig {
197        debug_mode: true,
198        max_cycles: 3,
199        ..Default::default()
200    };
201    let engine = RustRuleEngine::with_config(kb, config);
202
203    println!("šŸ Initial state:");
204    if let Some(user) = facts.get("User") {
205        println!("   User = {user:?}");
206    }
207    println!();
208
209    // Execute rules
210    println!("šŸš€ Executing rules...");
211    let result = engine.execute(&facts)?;
212
213    println!("\nšŸ“Š Execution Results:");
214    println!("   Cycles: {}", result.cycle_count);
215    println!("   Rules evaluated: {}", result.rules_evaluated);
216    println!("   Rules fired: {}", result.rules_fired);
217    println!("   Execution time: {:?}", result.execution_time);
218
219    println!("\nšŸ Final state:");
220    if let Some(user) = facts.get("User") {
221        println!("   User = {user:?}");
222    }
223    println!();
224
225    Ok(())
226}
227
228fn demo_ecommerce_scenario() -> std::result::Result<(), Box<dyn std::error::Error>> {
229    println!("šŸ›’ Demo 4: E-commerce Scenario");
230    println!("------------------------------");
231
232    // Create facts
233    let facts = Facts::new();
234
235    // Customer data
236    let mut customer_props = HashMap::new();
237    customer_props.insert(
238        "Email".to_string(),
239        Value::String("customer@example.com".to_string()),
240    );
241    customer_props.insert("Age".to_string(), Value::Integer(28));
242    customer_props.insert("IsNew".to_string(), Value::Boolean(true));
243    customer_props.insert("LoyaltyPoints".to_string(), Value::Integer(0));
244    customer_props.insert("TotalSpent".to_string(), Value::Number(0.0));
245
246    // Order data
247    let mut order_props = HashMap::new();
248    order_props.insert("Id".to_string(), Value::String("ORD-12345".to_string()));
249    order_props.insert("Amount".to_string(), Value::Number(150.0));
250    order_props.insert(
251        "Category".to_string(),
252        Value::String("electronics".to_string()),
253    );
254    order_props.insert("DiscountPercent".to_string(), Value::Number(0.0));
255    order_props.insert("FinalAmount".to_string(), Value::Number(150.0));
256
257    facts.add_value("Customer", Value::Object(customer_props))?;
258    facts.add_value("Order", Value::Object(order_props))?;
259
260    // Create knowledge base
261    let mut kb = KnowledgeBase::new("EcommerceRules");
262
263    // Rule 1: New Customer Discount
264    let new_customer_rule = Rule::new(
265        "NewCustomerDiscount".to_string(),
266        ConditionGroup::and(
267            ConditionGroup::single(Condition::new(
268                "Customer.IsNew".to_string(),
269                Operator::Equal,
270                Value::Boolean(true),
271            )),
272            ConditionGroup::single(Condition::new(
273                "Order.Amount".to_string(),
274                Operator::GreaterThan,
275                Value::Number(100.0),
276            )),
277        ),
278        vec![
279            ActionType::MethodCall {
280                object: "Order".to_string(),
281                method: "setDiscountPercent".to_string(),
282                args: vec![Value::Number(10.0)],
283            },
284            ActionType::MethodCall {
285                object: "Customer".to_string(),
286                method: "setLoyaltyPoints".to_string(),
287                args: vec![Value::Integer(100)],
288            },
289            ActionType::Call {
290                function: "log".to_string(),
291                args: vec![Value::String("New customer discount applied".to_string())],
292            },
293        ],
294    )
295    .with_salience(10);
296
297    // Rule 2: Calculate Final Amount
298    let calculate_final_rule = Rule::new(
299        "CalculateFinalAmount".to_string(),
300        ConditionGroup::single(Condition::new(
301            "Order.DiscountPercent".to_string(),
302            Operator::GreaterThan,
303            Value::Number(0.0),
304        )),
305        vec![ActionType::Call {
306            function: "log".to_string(),
307            args: vec![Value::String(
308                "Calculating final amount with discount".to_string(),
309            )],
310        }],
311    )
312    .with_salience(5);
313
314    // Add rules
315    let _ = kb.add_rule(new_customer_rule);
316    let _ = kb.add_rule(calculate_final_rule);
317
318    // Create engine
319    let config = EngineConfig {
320        debug_mode: true,
321        max_cycles: 3,
322        ..Default::default()
323    };
324    let engine = RustRuleEngine::with_config(kb, config);
325
326    println!("šŸ Initial e-commerce state:");
327    if let Some(customer) = facts.get("Customer") {
328        println!("   Customer = {customer:?}");
329    }
330    if let Some(order) = facts.get("Order") {
331        println!("   Order = {order:?}");
332    }
333    println!();
334
335    // Execute rules
336    println!("šŸš€ Executing e-commerce rules...");
337    let result = engine.execute(&facts)?;
338
339    println!("\nšŸ“Š E-commerce Results:");
340    println!("   Cycles: {}", result.cycle_count);
341    println!("   Rules evaluated: {}", result.rules_evaluated);
342    println!("   Rules fired: {}", result.rules_fired);
343    println!("   Execution time: {:?}", result.execution_time);
344
345    println!("\nšŸ Final e-commerce state:");
346    if let Some(customer) = facts.get("Customer") {
347        println!("   Customer = {customer:?}");
348    }
349    if let Some(order) = facts.get("Order") {
350        println!("   Order = {order:?}");
351    }
352
353    println!("\nšŸŽÆ Demo Completed Successfully!");
354    println!("   āœ… Knowledge Base management");
355    println!("   āœ… Facts manipulation");
356    println!("   āœ… Rule execution engine");
357    println!("   āœ… E-commerce scenario");
358    println!("   āœ… Method calls and function calls");
359    println!("   āœ… Salience-based rule ordering");
360
361    Ok(())
362}