A blazing-fast production-ready rule engine for Rust supporting both Forward and Backward Chaining. Features RETE-UL algorithm with Alpha Memory Indexing and Beta Memory Indexing, parallel execution, goal-driven reasoning, and GRL (Grule Rule Language) syntax.
🆕 v1.20.0: Performance optimization release — eliminated unnecessary .clone() calls in hot paths for 2-683x speedup. Zero-copy string operations, optimized rule iteration, and memory-efficient fact access. Zero breaking changes, 443 tests passing.
🔗 GitHub | Documentation | Crates.io
Massive performance improvements with zero breaking changes!
Key Optimizations:
- ✅ Zero-Copy String Operations: Value::as_string_ref() eliminates cloning in Contains/StartsWith/EndsWith operators (2x faster)
- ✅ Optimized Rule Iteration: Index-based access replaces get_rules().clone() (41-683x faster)
- ✅ Memory-Efficient Facts: Facts::with_value() callback API reduces allocations by 40%
- ✅ RETE Performance: FactValue::as_str() with Cow<str> optimizes comparison and hashing (6x faster)
Real Performance Impact:
// Before v1.20.0 - Cloning overhead
let rules = kb.get_rules(); // Clones entire Vec<Rule>
let count = rules.len(); // 14.2ms for 1K calls
// After v1.20.0 - Direct access
let count = kb.rule_count(); // 20.8µs for 1K calls (683x faster!)
naive_date.and_hms_opt(0, 0, 0).unwrap() // 💥 PANIC!
// v1.19.3 - Proper error handling naive_date.and_hms_opt(0, 0, 0).ok_or_else(|| RuleEngineError::ParseError { message: format!("Invalid time for date: {}", naive_date) } )? // ✅ Returns Result
**Files improved:**
- `src/parser/grl_no_regex.rs` - 9 critical unwraps fixed
- `src/parser/grl.rs` - 7 critical unwraps fixed
**Quality metrics:**
- ✅ 436 tests passing (100% pass rate maintained)
- ✅ Zero clippy warnings
- ✅ Zero breaking changes
- ✅ Better UX with descriptive error messages
**Patterns fixed:**
1. Date parsing: `.and_hms_opt().unwrap()` → proper `Result` propagation
2. String find: `contains() + find().unwrap()` → `if let Some(pos) = find()`
3. Iterators: `.unwrap()` → `.expect()` with invariant docs
4. Char access: Safe handling of empty strings
5. Prefix stripping: Proper error on missing prefix
This release makes the parser **production-ready** for handling untrusted or malformed GRL input without panicking.
---
## 🎯 Reasoning Modes
### 🔄 Forward Chaining (Data-Driven)
**"When facts change, fire matching rules"**
- **Native Engine** - Simple pattern matching for small rule sets
- **RETE-UL** - Optimized network for 100-10,000 rules with O(1) indexing
- **Parallel Execution** - Multi-threaded rule evaluation
**Use Cases:** Business rules, validation, reactive systems, decision automation
### 🎯 Backward Chaining (Goal-Driven)
**"Given a goal, find facts/rules to prove it"**
- **Unification** - Pattern matching with variable bindings
- **Search Strategies** - DFS, BFS, Iterative Deepening
- **Aggregation** - COUNT, SUM, AVG, MIN, MAX
- **Negation** - NOT queries with closed-world assumption
- **Explanation** - Proof trees with JSON/MD/HTML export
- **Disjunction** - OR patterns for alternative paths
- **Nested Queries** - Subqueries with shared variables
- **Query Optimization** - Automatic goal reordering for 10-100x speedup
**Use Cases:** Expert systems, diagnostics, planning, decision support, AI reasoning
### 🌊 Stream Processing (Event-Driven) 🆕
**"Process real-time event streams with time-based windows"**
- **GRL Stream Syntax** - Declarative stream pattern definitions
- **StreamAlphaNode** - RETE-integrated event filtering & windowing
- **Time Windows** - Sliding (continuous), tumbling (non-overlapping), and **session (gap-based)** 🆕
- **Multi-Stream Correlation** - Join events from different streams
- **WorkingMemory Integration** - Stream events become facts for rule evaluation
**Use Cases:** Real-time fraud detection, IoT monitoring, financial analytics, security alerts, CEP
**Example:**
```grl
rule "Fraud Alert" {
when
login: LoginEvent from stream("logins") over window(10 min, sliding) &&
purchase: PurchaseEvent from stream("purchases") over window(10 min, sliding) &&
login.user_id == purchase.user_id &&
login.ip_address != purchase.ip_address
then
Alert.trigger("IP mismatch detected");
}
use rust_rule_engine::{RuleEngine, Facts, Value};
let mut engine = RuleEngine::new();
// Define rule in GRL
engine.add_rule_from_grl(r#"
rule "VIP Discount" {
when
Customer.TotalSpent > 10000
then
Customer.Discount = 0.15;
}
"#)?;
// Add facts and execute
let mut facts = Facts::new();
facts.set("Customer.TotalSpent", Value::Number(15000.0));
engine.execute(&mut facts)?;
// Result: Customer.Discount = 0.15 ✓
use rust_rule_engine::backward::BackwardEngine;
let mut engine = BackwardEngine::new(kb);
// Query: "Can this order be auto-approved?"
let result = engine.query(
"Order.AutoApproved == true",
&mut facts
)?;
if result.provable {
println!("Order can be auto-approved!");
println!("Proof: {:?}", result.proof_trace);
}
use rust_rule_engine::parser::grl::stream_syntax::parse_stream_pattern;
use rust_rule_engine::rete::stream_alpha_node::{StreamAlphaNode, WindowSpec};
use rust_rule_engine::rete::working_memory::WorkingMemory;
// Parse GRL stream pattern
let grl = r#"login: LoginEvent from stream("logins") over window(5 min, sliding)"#;
let (_, pattern) = parse_stream_pattern(grl)?;
// Create stream processor
let mut node = StreamAlphaNode::new(
&pattern.source.stream_name,
pattern.event_type,
pattern.source.window.as_ref().map(|w| WindowSpec {
duration: w.duration,
window_type: w.window_type.clone(),
}),
);
// Process events in real-time
let mut wm = WorkingMemory::new();
for event in event_stream {
if node.process_event(&event) {
// Event passed filters and is in window
wm.insert_from_stream("logins".to_string(), event);
// Now available for rule evaluation!
}
}
// Run: cargo run --example streaming_fraud_detection --features streaming
#![warn(missing_docs)] to ensure API documentation qualityin)Concise syntax for checking if a value exists in an array!
// OLD WAY - Verbose with multiple OR conditions
rule "SkipDependencies" {
when
Path.name == "node_modules" ||
Path.name == "__pycache__" ||
Path.name == ".pytest_cache"
then
Path.action = "skip";
}
// NEW WAY - Clean and maintainable ✨
rule "SkipDependencies" {
when
Path.name in ["node_modules", "__pycache__", ".pytest_cache"]
then
Path.action = "skip";
}
Features:
- ✅ Array literals: ["value1", "value2", 123, true]
- ✅ Mixed types: strings, numbers, booleans
- ✅ Works with RETE and backward chaining
- ✅ Example: cargo run --example in_operator_demo
startsWith, endsWith)Previously missing from GRL parser, now fully supported!
rule "AdminEmail" {
when
User.email startsWith "admin@"
then
User.role = "administrator";
}
rule "ImageFile" {
when
File.name endsWith ".jpg" ||
File.name endsWith ".png"
then
File.type = "image";
}
All String Operators:
- ✅ startsWith - Check prefix
- ✅ endsWith - Check suffix
- ✅ contains - Substring search
- ✅ matches - Wildcard patterns (* and ?)
- ✅ Example: cargo run --example string_methods_demo
Global cache for proven facts with dependency tracking and automatic invalidation for backward chaining!
1. Proof Caching - Cache proven facts with their justifications (rule + premises) - O(1) lookup by fact key (predicate + arguments) - Multiple justifications per fact (different ways to prove) - Thread-safe concurrent access with Arc<Mutex<>>
2. Dependency Tracking - Forward edges: Track which rules used a fact as premise - Reverse edges: Track which facts a fact depends on - Automatic dependency graph construction during proof
3. TMS-Aware Invalidation - Integrates with RETE's IncrementalEngine insert_logical - When premise retracted → cascading invalidation through dependents - Recursive propagation through entire dependency chain - Statistics tracking (hits, misses, invalidations, justifications)
4. Search Integration - Seamlessly integrated into DepthFirstSearch and BreadthFirstSearch - Cache lookup before condition evaluation (early return on hit) - Automatic cache updates via inserter closure
use rust_rule_engine::backward::{BackwardEngine, DepthFirstSearch};
use rust_rule_engine::rete::IncrementalEngine;
// Create engines
let mut rete_engine = IncrementalEngine::new();
let kb = /* load rules */;
let mut backward_engine = BackwardEngine::new(kb);
// Create search with ProofGraph enabled
let search = DepthFirstSearch::new_with_engine(
backward_engine.kb().clone(),
Arc::new(Mutex::new(rete_engine)),
);
// First query builds cache
let result1 = backward_engine.query_with_search(
"eligible(?x)",
&mut facts,
Box::new(search.clone()),
)?;
// Subsequent queries use cache
let result2 = backward_engine.query_with_search(
"eligible(?x)",
&mut facts,
Box::new(search),
)?;
// Given rules: A → B → C (chain dependency)
let result_c = engine.query("C", &mut facts)?; // Proves A, B, C
// Retract A (premise)
facts.set("A", FactValue::Bool(false));
// Automatic cascading invalidation:
// A invalidated → B invalidated → C invalidated
// Total: 3 invalidations propagated through dependency graph
// Same fact proven 3 different ways:
// Rule 1: HighSpender → eligible
// Rule 2: LoyalCustomer → eligible
// Rule 3: Subscription → eligible
let result = engine.query("eligible(?x)", &mut facts)?;
// ProofGraph stores all 3 justifications
// If one premise fails, others still valid!
Try it yourself:
# Run comprehensive demo with 5 scenarios
cargo run --example proof_graph_cache_demo --features backward-chaining
# Run integration tests
cargo test proof_graph --features backward-chaining
New Files:
- src/backward/proof_graph.rs (520 lines) - Core ProofGraph implementation
- tests/proof_graph_integration_test.rs - 6 comprehensive tests
- examples/09-backward-chaining/proof_graph_cache_demo.rs - Interactive demo
Features: - ✅ Global proof caching with O(1) lookup - ✅ Dependency tracking (forward + reverse edges) - ✅ TMS-aware cascading invalidation - ✅ Multiple justifications per fact - ✅ Thread-safe concurrent access - ✅ Statistics tracking (hits/misses/invalidations) - ✅ Zero overhead when cache miss - ✅ Automatic integration with DFS/BFS search
Critical Unicode Bug Fix - Upgraded to rexile 0.5.3 with complete Unicode support!
1. Rexile Upgrade (0.4.10 → 0.5.3) - ✅ CRITICAL FIX: Unicode char boundary panic resolved - ✅ GRL files with Unicode symbols (→, ∑, ∫, emojis, CJK) now work perfectly - ✅ No performance regression - benchmarks stable - ⚠️ Skipped 0.5.1 & 0.5.2 due to critical Unicode bugs
2. Nom Parser Upgrade (7.x → 8.0)
- ✅ Removed deprecated tuple combinator
- ✅ Updated to modern nom 8.0 API with Parser trait
- ✅ Changed from parser(input)? to parser.parse(input)?
- ✅ All stream syntax parsing updated
3. Criterion Benchmark Updates
- ✅ Replaced deprecated criterion::black_box with std::hint::black_box
- ✅ Updated all 6 benchmark files
- ✅ Modern Rust stdlib usage (no external deps for black_box)
All Systems Green: - ✅ 152/152 tests passing (100% pass rate) - ✅ All 29 examples working (including Unicode-heavy examples) - ✅ All benchmarks passing with stable performance - ✅ Zero regressions detected
Unicode Test Cases:
// These now work perfectly in v1.18.28:
// Rule: Amount < 2M + COD → Auto approve ✅
// Mathematical: ∑ ∫ ∂ → ← ↔ ✅
// Emoji: 🚀 🎉 ✅ ❌ ✅
// CJK: 规则 (Chinese characters) ✅
No regression from previous version: - Alpha Linear 1K: ~18.0µs (stable) - Alpha Indexed 1K: ~147ns (stable) - Speedup: ~122x (maintained)
Recommendation: ✅ Safe to upgrade - Critical Unicode fixes with zero breaking changes!
$ claude mcp add rust-rule-engine \
-- python -m otcore.mcp_server <graph>