8.8 KiB
Architecture of the rhailib_engine
Crate
The rhailib_engine
crate serves as the central Rhai scripting engine for the heromodels ecosystem. It provides a unified interface for creating, configuring, and executing Rhai scripts with access to all business domain modules through a feature-based architecture.
Core Architecture
The engine acts as an orchestration layer that brings together the DSL modules and provides execution utilities:
graph TD
A[rhailib_engine] --> B[Engine Creation]
A --> C[Script Execution]
A --> D[Mock Database]
A --> E[Feature Management]
B --> B1[create_heromodels_engine]
B --> B2[Engine Configuration]
B --> B3[DSL Registration]
C --> C1[eval_script]
C --> C2[eval_file]
C --> C3[compile_script]
C --> C4[run_ast]
D --> D1[create_mock_db]
D --> D2[seed_mock_db]
D --> D3[Domain Data Seeding]
E --> E1[calendar]
E --> E2[finance]
E --> E3[flow]
E --> E4[legal]
E --> E5[projects]
E --> E6[biz]
B3 --> F[rhailib_dsl]
F --> G[All Domain Modules]
Core Components
1. Engine Factory (create_heromodels_engine
)
The primary entry point for creating a fully configured Rhai engine:
pub fn create_heromodels_engine() -> Engine
Responsibilities:
- Creates a new Rhai engine instance
- Configures engine limits and settings
- Registers all available DSL modules
- Returns a ready-to-use engine
Configuration Settings:
- Expression Depth: 128 levels for both expressions and functions
- String Size Limit: 10 MB maximum string size
- Array Size Limit: 10,000 elements maximum
- Map Size Limit: 10,000 key-value pairs maximum
2. Script Execution Utilities
Direct Script Evaluation
pub fn eval_script(engine: &Engine, script: &str) -> Result<Dynamic, Box<EvalAltResult>>
Executes Rhai script strings directly with immediate results.
File-Based Script Execution
pub fn eval_file(engine: &Engine, file_path: &Path) -> Result<Dynamic, Box<EvalAltResult>>
Loads and executes Rhai scripts from filesystem with proper error handling.
Compiled Script Execution
pub fn compile_script(engine: &Engine, script: &str) -> Result<AST, Box<EvalAltResult>>
pub fn run_ast(engine: &Engine, ast: &AST, scope: &mut Scope) -> Result<Dynamic, Box<EvalAltResult>>
Provides compilation and execution of scripts for performance optimization.
3. Mock Database System
Database Creation
pub fn create_mock_db() -> Arc<OurDB>
Creates an in-memory database instance for testing and examples.
Data Seeding
pub fn seed_mock_db(db: Arc<OurDB>)
Populates the mock database with representative data across all domains.
Feature-Based Architecture
The engine uses Cargo features to control which domain modules are included:
Available Features
calendar
(default): Calendar and event managementfinance
(default): Financial accounts, assets, and marketplaceflow
: Workflow and approval processeslegal
: Contract and legal document managementprojects
: Project and task managementbiz
: Business operations and entities
Feature Integration Pattern
#[cfg(feature = "calendar")]
use heromodels::models::calendar::*;
#[cfg(feature = "finance")]
use heromodels::models::finance::*;
This allows for:
- Selective Compilation: Only include needed functionality
- Reduced Binary Size: Exclude unused domain modules
- Modular Deployment: Different configurations for different use cases
Mock Database Architecture
Database Structure
The mock database provides a complete testing environment:
graph LR
A[Mock Database] --> B[Calendar Data]
A --> C[Finance Data]
A --> D[Flow Data]
A --> E[Legal Data]
A --> F[Projects Data]
B --> B1[Calendars]
B --> B2[Events]
B --> B3[Attendees]
C --> C1[Accounts]
C --> C2[Assets - ERC20/ERC721]
C --> C3[Marketplace Listings]
D --> D1[Flows]
D --> D2[Flow Steps]
D --> D3[Signature Requirements]
E --> E1[Contracts]
E --> E2[Contract Revisions]
E --> E3[Contract Signers]
F --> F1[Projects]
F --> F2[Project Members]
F --> F3[Project Tags]
Seeding Strategy
Each domain has its own seeding function that creates realistic test data:
Calendar Seeding
- Creates work calendars with descriptions
- Adds team meetings with attendees
- Sets up recurring events
Finance Seeding
- Creates demo trading accounts
- Generates ERC20 tokens and ERC721 NFTs
- Sets up marketplace listings with metadata
Flow Seeding (Feature-Gated)
- Creates document approval workflows
- Defines multi-step approval processes
- Sets up signature requirements
Legal Seeding (Feature-Gated)
- Creates service agreements
- Adds contract revisions and versions
- Defines contract signers and roles
Projects Seeding (Feature-Gated)
- Creates project instances with status tracking
- Assigns team members and priorities
- Adds project tags and categorization
Error Handling Architecture
Comprehensive Error Propagation
Result<Dynamic, Box<EvalAltResult>>
All functions return proper Rhai error types that include:
- Script Compilation Errors: Syntax and parsing issues
- Runtime Errors: Execution failures and exceptions
- File System Errors: File reading and path resolution issues
- Database Errors: Mock database operation failures
Error Context Enhancement
File operations include enhanced error context:
Err(Box::new(EvalAltResult::ErrorSystem(
format!("Failed to read script file: {}", file_path.display()),
Box::new(io_err),
)))
Performance Considerations
Engine Configuration
Optimized settings for production use:
- Memory Limits: Prevent runaway script execution
- Depth Limits: Avoid stack overflow from deep recursion
- Size Limits: Control memory usage for large data structures
Compilation Strategy
- AST Caching: Compile once, execute multiple times
- Scope Management: Efficient variable scope handling
- Module Registration: One-time registration at engine creation
Mock Database Performance
- In-Memory Storage: Fast access for testing scenarios
- Temporary Directories: Automatic cleanup after use
- Lazy Loading: Data seeded only when needed
Integration Patterns
Script Development Workflow
// 1. Create engine with all modules
let engine = create_heromodels_engine();
// 2. Execute business logic scripts
let result = eval_script(&engine, r#"
let company = new_company()
.name("Tech Startup")
.business_type("startup");
save_company(company)
"#)?;
// 3. Handle results and errors
match result {
Ok(value) => println!("Success: {:?}", value),
Err(error) => eprintln!("Error: {}", error),
}
Testing Integration
// 1. Create mock database
let db = create_mock_db();
seed_mock_db(db.clone());
// 2. Create engine
let engine = create_heromodels_engine();
// 3. Test scripts against seeded data
let script = r#"
let calendars = list_calendars();
calendars.len()
"#;
let count = eval_script(&engine, script)?;
File-Based Script Execution
// Execute scripts from files
let result = eval_file(&engine, Path::new("scripts/business_logic.rhai"))?;
Deployment Configurations
Minimal Configuration
[dependencies]
rhailib_engine = { version = "0.1.0", default-features = false, features = ["calendar"] }
Full Configuration
[dependencies]
rhailib_engine = { version = "0.1.0", features = ["calendar", "finance", "flow", "legal", "projects", "biz"] }
Custom Configuration
[dependencies]
rhailib_engine = { version = "0.1.0", default-features = false, features = ["finance", "biz"] }
Security Considerations
Script Execution Limits
- Resource Limits: Prevent resource exhaustion attacks
- Execution Time: Configurable timeouts for long-running scripts
- Memory Bounds: Controlled memory allocation
Database Access
- Mock Environment: Safe testing without production data exposure
- Temporary Storage: Automatic cleanup prevents data persistence
- Isolated Execution: Each test run gets fresh database state
Extensibility
Adding New Domains
- Create new feature flag in
Cargo.toml
- Add conditional imports for new models
- Implement seeding function for test data
- Register with DSL module system
Custom Engine Configuration
let mut engine = Engine::new();
// Custom configuration
engine.set_max_expr_depths(256, 256);
// Register specific modules
rhailib_dsl::register_dsl_modules(&mut engine);
This architecture provides a robust, feature-rich foundation for Rhai script execution while maintaining flexibility, performance, and security.