306 lines
10 KiB
Rust
306 lines
10 KiB
Rust
//! # Rhailib Engine
|
|
//!
|
|
//! The central Rhai scripting engine for the heromodels ecosystem. This crate provides
|
|
//! a unified interface for creating, configuring, and executing Rhai scripts with access
|
|
//! to all business domain modules.
|
|
//!
|
|
//! ## Features
|
|
//!
|
|
//! - **Unified Engine Creation**: Pre-configured Rhai engine with all DSL modules
|
|
//! - **Script Execution Utilities**: Direct evaluation, file-based execution, and AST compilation
|
|
//! - **Mock Database System**: Complete testing environment with seeded data
|
|
//! - **Feature-Based Architecture**: Modular compilation based on required domains
|
|
//!
|
|
//! ## Quick Start
|
|
//!
|
|
//! ```rust
|
|
//! use rhailib_engine::{create_heromodels_engine, eval_script};
|
|
//!
|
|
//! // Create a fully configured engine
|
|
//! let engine = create_heromodels_engine();
|
|
//!
|
|
//! // Execute a business logic script
|
|
//! let result = eval_script(&engine, r#"
|
|
//! let company = new_company()
|
|
//! .name("Acme Corp")
|
|
//! .business_type("global");
|
|
//! company.name
|
|
//! "#)?;
|
|
//!
|
|
//! println!("Company name: {}", result.as_string().unwrap());
|
|
//! ```
|
|
//!
|
|
//! ## Available Features
|
|
//!
|
|
//! - `calendar` (default): Calendar and event management
|
|
//! - `finance` (default): Financial accounts, assets, and marketplace
|
|
//! - `flow`: Workflow and approval processes
|
|
//! - `legal`: Contract and legal document management
|
|
//! - `projects`: Project and task management
|
|
//! - `biz`: Business operations and entities
|
|
|
|
use rhai::{Engine, EvalAltResult, Scope, AST};
|
|
use rhailib_dsl;
|
|
use std::fs;
|
|
use std::path::Path;
|
|
|
|
/// Mock database module for testing and examples
|
|
pub mod mock_db;
|
|
|
|
/// Creates a fully configured Rhai engine with all available DSL modules.
|
|
///
|
|
/// This function creates a new Rhai engine instance, configures it with appropriate
|
|
/// limits and settings, and registers all available business domain modules based
|
|
/// on enabled features.
|
|
///
|
|
/// # Engine Configuration
|
|
///
|
|
/// The engine is configured with the following limits:
|
|
/// - **Expression Depth**: 128 levels for both expressions and functions
|
|
/// - **String Size**: 10 MB maximum
|
|
/// - **Array Size**: 10,000 elements maximum
|
|
/// - **Map Size**: 10,000 key-value pairs maximum
|
|
///
|
|
/// # Registered Modules
|
|
///
|
|
/// All enabled DSL modules are automatically registered, including:
|
|
/// - Business operations (companies, products, sales, shareholders)
|
|
/// - Financial models (accounts, assets, marketplace)
|
|
/// - Content management (collections, images, PDFs, books)
|
|
/// - Workflow management (flows, steps, signatures)
|
|
/// - And more based on enabled features
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// A fully configured `Engine` instance ready for script execution.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use rhailib_engine::create_heromodels_engine;
|
|
///
|
|
/// let engine = create_heromodels_engine();
|
|
///
|
|
/// // Engine is now ready to execute scripts with access to all DSL functions
|
|
/// let result = engine.eval::<String>(r#"
|
|
/// let company = new_company().name("Test Corp");
|
|
/// company.name
|
|
/// "#).unwrap();
|
|
/// assert_eq!(result, "Test Corp");
|
|
/// ```
|
|
pub fn create_heromodels_engine() -> Engine {
|
|
let mut engine = Engine::new();
|
|
|
|
// Configure engine settings
|
|
engine.set_max_expr_depths(128, 128);
|
|
engine.set_max_string_size(10 * 1024 * 1024); // 10 MB
|
|
engine.set_max_array_size(10 * 1024); // 10K elements
|
|
engine.set_max_map_size(10 * 1024); // 10K elements
|
|
|
|
// Register all heromodels Rhai modules
|
|
rhailib_dsl::register_dsl_modules(&mut engine);
|
|
|
|
engine
|
|
}
|
|
|
|
// /// Register all heromodels Rhai modules with the engine
|
|
// pub fn register_all_modules(engine: &mut Engine, db: Arc<OurDB>) {
|
|
// // Register the calendar module if the feature is enabled
|
|
// heromodels::models::access::register_access_rhai_module(engine, db.clone());
|
|
// #[cfg(feature = "calendar")]
|
|
// heromodels::models::calendar::register_calendar_rhai_module(engine, db.clone());
|
|
// heromodels::models::contact::register_contact_rhai_module(engine, db.clone());
|
|
// heromodels::models::library::register_library_rhai_module(engine, db.clone());
|
|
// heromodels::models::circle::register_circle_rhai_module(engine, db.clone());
|
|
|
|
// // Register the flow module if the feature is enabled
|
|
// #[cfg(feature = "flow")]
|
|
// heromodels::models::flow::register_flow_rhai_module(engine, db.clone());
|
|
|
|
// // // Register the finance module if the feature is enabled
|
|
// // #[cfg(feature = "finance")]
|
|
// // heromodels::models::finance::register_finance_rhai_module(engine, db.clone());
|
|
|
|
// // Register the legal module if the feature is enabled
|
|
// #[cfg(feature = "legal")]
|
|
// heromodels::models::legal::register_legal_rhai_module(engine, db.clone());
|
|
|
|
// // Register the projects module if the feature is enabled
|
|
// #[cfg(feature = "projects")]
|
|
// heromodels::models::projects::register_projects_rhai_module(engine, db.clone());
|
|
|
|
// // Register the biz module if the feature is enabled
|
|
// #[cfg(feature = "biz")]
|
|
// heromodels::models::biz::register_biz_rhai_module(engine, db.clone());
|
|
|
|
// println!("Heromodels Rhai modules registered successfully.");
|
|
// }
|
|
|
|
/// Evaluates a Rhai script string and returns the result.
|
|
///
|
|
/// This function provides a convenient way to execute Rhai script strings directly
|
|
/// using the provided engine. It's suitable for one-off script execution or when
|
|
/// the script content is dynamically generated.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `engine` - The Rhai engine to use for script execution
|
|
/// * `script` - The Rhai script content as a string
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `Ok(Dynamic)` - The result of script execution
|
|
/// * `Err(Box<EvalAltResult>)` - Script compilation or execution error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use rhailib_engine::{create_heromodels_engine, eval_script};
|
|
///
|
|
/// let engine = create_heromodels_engine();
|
|
/// let result = eval_script(&engine, r#"
|
|
/// let x = 42;
|
|
/// let y = 8;
|
|
/// x + y
|
|
/// "#)?;
|
|
/// assert_eq!(result.as_int().unwrap(), 50);
|
|
/// ```
|
|
pub fn eval_script(
|
|
engine: &Engine,
|
|
script: &str,
|
|
) -> Result<rhai::Dynamic, Box<rhai::EvalAltResult>> {
|
|
engine.eval::<rhai::Dynamic>(script)
|
|
}
|
|
|
|
/// Evaluates a Rhai script from a file and returns the result.
|
|
///
|
|
/// This function reads a Rhai script from the filesystem and executes it using
|
|
/// the provided engine. It handles file reading errors gracefully and provides
|
|
/// meaningful error messages.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `engine` - The Rhai engine to use for script execution
|
|
/// * `file_path` - Path to the Rhai script file
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `Ok(Dynamic)` - The result of script execution
|
|
/// * `Err(Box<EvalAltResult>)` - File reading, compilation, or execution error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use rhailib_engine::{create_heromodels_engine, eval_file};
|
|
/// use std::path::Path;
|
|
///
|
|
/// let engine = create_heromodels_engine();
|
|
/// let result = eval_file(&engine, Path::new("scripts/business_logic.rhai"))?;
|
|
/// println!("Script result: {:?}", result);
|
|
/// ```
|
|
///
|
|
/// # Error Handling
|
|
///
|
|
/// File reading errors are converted to Rhai `ErrorSystem` variants with
|
|
/// descriptive messages including the file path that failed to load.
|
|
pub fn eval_file(
|
|
engine: &Engine,
|
|
file_path: &Path,
|
|
) -> Result<rhai::Dynamic, Box<rhai::EvalAltResult>> {
|
|
match fs::read_to_string(file_path) {
|
|
Ok(script_content) => engine.eval::<rhai::Dynamic>(&script_content),
|
|
Err(io_err) => Err(Box::new(EvalAltResult::ErrorSystem(
|
|
format!("Failed to read script file: {}", file_path.display()),
|
|
Box::new(io_err),
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Compiles a Rhai script string into an Abstract Syntax Tree (AST).
|
|
///
|
|
/// This function compiles a Rhai script into an AST that can be executed multiple
|
|
/// times with different scopes. This is more efficient than re-parsing the script
|
|
/// for each execution when the same script needs to be run repeatedly.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `engine` - The Rhai engine to use for compilation
|
|
/// * `script` - The Rhai script content as a string
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `Ok(AST)` - The compiled Abstract Syntax Tree
|
|
/// * `Err(Box<EvalAltResult>)` - Script compilation error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use rhailib_engine::{create_heromodels_engine, compile_script, run_ast};
|
|
/// use rhai::Scope;
|
|
///
|
|
/// let engine = create_heromodels_engine();
|
|
/// let ast = compile_script(&engine, r#"
|
|
/// let company = new_company().name(company_name);
|
|
/// save_company(company)
|
|
/// "#)?;
|
|
///
|
|
/// // Execute the compiled script multiple times with different variables
|
|
/// let mut scope1 = Scope::new();
|
|
/// scope1.push("company_name", "Acme Corp");
|
|
/// let result1 = run_ast(&engine, &ast, &mut scope1)?;
|
|
///
|
|
/// let mut scope2 = Scope::new();
|
|
/// scope2.push("company_name", "Tech Startup");
|
|
/// let result2 = run_ast(&engine, &ast, &mut scope2)?;
|
|
/// ```
|
|
pub fn compile_script(engine: &Engine, script: &str) -> Result<AST, Box<rhai::EvalAltResult>> {
|
|
Ok(engine.compile(script)?)
|
|
}
|
|
|
|
/// Executes a compiled Rhai script AST with the provided scope.
|
|
///
|
|
/// This function runs a pre-compiled AST using the provided engine and scope.
|
|
/// The scope can contain variables and functions that will be available to
|
|
/// the script during execution.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `engine` - The Rhai engine to use for execution
|
|
/// * `ast` - The compiled Abstract Syntax Tree to execute
|
|
/// * `scope` - Mutable scope containing variables and functions for the script
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `Ok(Dynamic)` - The result of script execution
|
|
/// * `Err(Box<EvalAltResult>)` - Script execution error
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use rhailib_engine::{create_heromodels_engine, compile_script, run_ast};
|
|
/// use rhai::Scope;
|
|
///
|
|
/// let engine = create_heromodels_engine();
|
|
/// let ast = compile_script(&engine, "x + y")?;
|
|
///
|
|
/// let mut scope = Scope::new();
|
|
/// scope.push("x", 10_i64);
|
|
/// scope.push("y", 32_i64);
|
|
///
|
|
/// let result = run_ast(&engine, &ast, &mut scope)?;
|
|
/// assert_eq!(result.as_int().unwrap(), 42);
|
|
/// ```
|
|
///
|
|
/// # Performance Notes
|
|
///
|
|
/// Using compiled ASTs is significantly more efficient than re-parsing scripts
|
|
/// for repeated execution, especially for complex scripts or when executing
|
|
/// the same logic with different input parameters.
|
|
pub fn run_ast(
|
|
engine: &Engine,
|
|
ast: &AST,
|
|
scope: &mut Scope,
|
|
) -> Result<rhai::Dynamic, Box<rhai::EvalAltResult>> {
|
|
engine.eval_ast_with_scope(scope, ast)
|
|
}
|