MCPcopy Index your code
hub / github.com/KSD-CO/rust-rule-engine

github.com/KSD-CO/rust-rule-engine @v1.20.2

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.20.2 ↗ · + Follow
2,588 symbols 9,319 edges 138 files 1,307 documented · 51%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Rust Rule Engine v1.20.0 🦀⚡🚀

Crates.io Documentation License: MIT Build Status

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


🎯 What's New in v1.20.0 ⚡

⚡ Performance Optimization & Memory Efficiency

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");
}

🚀 Quick Start

Forward Chaining Example

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 ✓

Backward Chaining Example

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);
}

Stream Processing Example 🆕

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

✨ Previous Releases

v1.19.2 - Documentation Release

  • 📚 Complete API Documentation: All public APIs now have comprehensive documentation
  • 🔍 Missing Docs Lint: Enabled #![warn(missing_docs)] to ensure API documentation quality
  • 📖 Enhanced RuleEngineBuilder Docs: Detailed documentation with examples for builder pattern
  • ✨ Zero Breaking Changes: Pure documentation improvement with no API changes

v1.19.0 - Array Membership & String Methods

🎯 Array Membership Operator (in)

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

🔤 String Methods Fixed (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


✨ Previous Release: v1.17.0

🚀 Proof Graph Caching with TMS Integration

Global cache for proven facts with dependency tracking and automatic invalidation for backward chaining!

Key Features

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

Usage Example

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),
)?;

Dependency Tracking Example

// 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

Multiple Justifications Example

// 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


✨ What's New in v1.18.28 🎉

🔧 Dependency Updates & Bug Fixes

Critical Unicode Bug Fix - Upgraded to rexile 0.5.3 with complete Unicode support!

Changes

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)

Verification

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)          ✅

Performance

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!

Extension points exported contracts — how you extend this code

Aggregation (Interface)
Trait for aggregation functions [6 implementers]
src/streaming/operators.rs
RulePlugin (Interface)
Core trait that all plugins must implement [5 implementers]
src/engine/plugin.rs
AccumulateFunction (Interface)
Accumulate function trait - defines how to aggregate values [5 implementers]
src/rete/accumulate.rs
RuleGRLExport (Interface)
Extension trait to add GRL export functionality to Rule [1 implementers]
src/engine/knowledge_base.rs
AccumulateState (Interface)
State maintained during accumulation [5 implementers]
src/rete/accumulate.rs
ConditionGroupGRLExport (Interface)
Extension trait for ConditionGroup GRL export [1 implementers]
src/engine/knowledge_base.rs
OperatorGRLExport (Interface)
Extension trait for Operator GRL export [1 implementers]
src/engine/knowledge_base.rs
ValueGRLExport (Interface)
Extension trait for Value GRL export [1 implementers]
src/engine/knowledge_base.rs

Core symbols most depended-on inside this repo

to_string
called by 918
src/rete/multifield.rs
to_string
called by 905
src/types.rs
insert
called by 566
src/rete/propagation.rs
set
called by 517
src/rete/facts.rs
clone
called by 483
src/rete/accumulate.rs
get
called by 424
src/rete/facts.rs
to_string
called by 388
src/backward/expression.rs
len
called by 337
src/rete/deffacts.rs

Shape

Method 1,286
Function 981
Class 256
Enum 55
Interface 10

Languages

Rust100%

Modules by API surface

src/engine/engine.rs89 symbols
src/parser/grl.rs76 symbols
src/engine/module.rs72 symbols
src/parser/grl_no_regex.rs65 symbols
src/streaming/operators.rs63 symbols
src/backward/grl_query.rs62 symbols
src/rete/optimization.rs50 symbols
src/rete/propagation.rs46 symbols
tests/backward_comprehensive_tests.rs45 symbols
src/engine/rule.rs45 symbols
src/backward/expression.rs44 symbols
src/rete/facts.rs43 symbols

For agents

$ claude mcp add rust-rule-engine \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact