diff --git a/rhai_engine/src/terra_integration/scripts/math_utils.rhai b/examples/loadscripts/scripts/math_utils.rhai similarity index 92% rename from rhai_engine/src/terra_integration/scripts/math_utils.rhai rename to examples/loadscripts/scripts/math_utils.rhai index 572f075..8f06f43 100644 --- a/rhai_engine/src/terra_integration/scripts/math_utils.rhai +++ b/examples/loadscripts/scripts/math_utils.rhai @@ -1,4 +1,4 @@ -// Math utility functions for Terra templates +// Math utility functions for Tera templates // Format a number with commas as thousands separators fn format_number(num) { diff --git a/rhai_engine/src/terra_integration/scripts/string_utils.rhai b/examples/loadscripts/scripts/string_utils.rhai similarity index 92% rename from rhai_engine/src/terra_integration/scripts/string_utils.rhai rename to examples/loadscripts/scripts/string_utils.rhai index 0b41b84..5614274 100644 --- a/rhai_engine/src/terra_integration/scripts/string_utils.rhai +++ b/examples/loadscripts/scripts/string_utils.rhai @@ -1,4 +1,4 @@ -// String manipulation functions that will be exposed to Terra templates +// String manipulation functions that will be exposed to Tera templates // Capitalize the first letter of each word fn capitalize(text) { diff --git a/rhai_engine/src/terra_integration/scripts/test_utils.rhai b/examples/loadscripts/scripts/test_utils.rhai similarity index 100% rename from rhai_engine/src/terra_integration/scripts/test_utils.rhai rename to examples/loadscripts/scripts/test_utils.rhai diff --git a/rhai_engine/src/test_dynamic_loading.rs b/examples/loadscripts/test_dynamic_loading.rs similarity index 83% rename from rhai_engine/src/test_dynamic_loading.rs rename to examples/loadscripts/test_dynamic_loading.rs index 3bb825d..30199b9 100644 --- a/rhai_engine/src/test_dynamic_loading.rs +++ b/examples/loadscripts/test_dynamic_loading.rs @@ -1,4 +1,4 @@ -use crate::terra_integration::RhaiTerraIntegration; +use crate::tera_integration::RhaiTeraIntegration; use std::collections::HashMap; use rhai::Dynamic; @@ -6,17 +6,17 @@ use rhai::Dynamic; pub fn test_dynamic_loading() -> Result<(), String> { println!("\n=== TESTING DYNAMIC FUNCTION LOADING ===\n"); - // Create a new RhaiTerraIntegration - let mut integration = RhaiTerraIntegration::new(); + // Create a new RhaiTeraIntegration + let mut integration = RhaiTeraIntegration::new(); println!("Loading Rhai scripts..."); // Load the original scripts - integration.load_script("string_utils", "src/terra_integration/scripts/string_utils.rhai")?; - integration.load_script("math_utils", "src/terra_integration/scripts/math_utils.rhai")?; + integration.load_script("string_utils", "src/tera_integration/scripts/string_utils.rhai")?; + integration.load_script("math_utils", "src/tera_integration/scripts/math_utils.rhai")?; // Load the new test script (which wasn't in the original hardcoded lists) - integration.load_script("test_utils", "src/terra_integration/scripts/test_utils.rhai")?; + integration.load_script("test_utils", "src/tera_integration/scripts/test_utils.rhai")?; // Get function names let function_names = integration.get_function_names(); @@ -86,7 +86,7 @@ fn display_functions_by_script(scripts: &HashMap>) { } // Helper function to test a function and display its result -fn test_function(integration: &RhaiTerraIntegration, name: &str, args: Vec) -> Result<(), String> { +fn test_function(integration: &RhaiTeraIntegration, name: &str, args: Vec) -> Result<(), String> { let result = integration.call_function(name, args)?; println!(" {}() => {}", name, result); Ok(()) diff --git a/rhai_engine/src/terra_integration/scripts/data_objects.rhai b/examples/simple/scripts/data_objects.rhai similarity index 97% rename from rhai_engine/src/terra_integration/scripts/data_objects.rhai rename to examples/simple/scripts/data_objects.rhai index cd10860..d98a65d 100644 --- a/rhai_engine/src/terra_integration/scripts/data_objects.rhai +++ b/examples/simple/scripts/data_objects.rhai @@ -1,5 +1,5 @@ -// Data objects for Terra template integration -// This script defines complex objects that can be accessed from Terra templates +// Data objects for Tera template integration +// This script defines complex objects that can be accessed from Tera templates // Create a products catalog object fn create_products() { diff --git a/rhai_engine/src/terra_integration/templates/product_catalog.terra b/examples/simple/templates/product_catalog.tera similarity index 100% rename from rhai_engine/src/terra_integration/templates/product_catalog.terra rename to examples/simple/templates/product_catalog.tera diff --git a/rhai_engine/src/lib.rs b/rhai_engine/src/lib.rs index a3ba742..e69de29 100644 --- a/rhai_engine/src/lib.rs +++ b/rhai_engine/src/lib.rs @@ -1,6 +0,0 @@ -//! Rhai Engine Library -//! -//! This library provides utilities for working with Rhai scripts. - -// Simple example of exposing Rhai functions to a hashmap -pub mod simple_example; diff --git a/rhai_engine/src/main.rs b/rhai_engine/src/main.rs deleted file mode 100644 index 35ec8bd..0000000 --- a/rhai_engine/src/main.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Rhai Engine Example -//! -//! This example demonstrates how to expose Rhai scripts to Rust -//! and make the functions available in a hashmap for a rendering engine. - -// mod simple_example; -mod terra_integration; -mod test_dynamic_loading; - -fn main() { - // println!("Running simple example of exposing Rhai functions to a hashmap..."); - // simple_example::main(); - - // println!("\n-----------------------------------------------------------\n"); - - // Run the dynamic loading test to demonstrate loading functions dynamically - println!("Running test for dynamic Rhai function loading..."); - if let Err(e) = test_dynamic_loading::test_dynamic_loading() { - eprintln!("Error in dynamic loading test: {}", e); - } -} \ No newline at end of file diff --git a/rhai_engine/src/terra_integration/README.md b/rhai_engine/src/rhailoader/README.md similarity index 81% rename from rhai_engine/src/terra_integration/README.md rename to rhai_engine/src/rhailoader/README.md index 23cdfc2..7c9e377 100644 --- a/rhai_engine/src/terra_integration/README.md +++ b/rhai_engine/src/rhailoader/README.md @@ -1,46 +1,46 @@ -# Rhai Terra Integration +# Rhai Tera Integration -This module provides integration between Rhai scripts and Terra templates, allowing you to expose Rhai functions and filters to Terra templates dynamically. +This module provides integration between Rhai scripts and Tera templates, allowing you to expose Rhai functions and filters to Tera templates dynamically. ## Overview -The Rhai Terra Integration module allows you to: +The Rhai Tera Integration module allows you to: 1. Load and compile Rhai scripts dynamically 2. Extract functions from these scripts and expose them as callable functions in Rust -3. Register these functions as filters in Terra templates -4. Render Terra templates with these filters +3. Register these functions as filters in Tera templates +4. Render Tera templates with these filters 5. Directly call Rhai functions from Rust code ## Key Components - **ScriptManager**: Handles loading, compiling, and exposing Rhai scripts -- **TerraRenderer**: Integrates with the Terra template engine -- **RhaiTerraIntegration**: Provides a simplified interface for the integration +- **TeraRenderer**: Integrates with the Tera template engine +- **RhaiTeraIntegration**: Provides a simplified interface for the integration ## Example Usage ### Loading Scripts and Templates ```rust -use rhai_engine::terra_integration::RhaiTerraIntegration; +use rhai_engine::tera_integration::RhaiTeraIntegration; use std::collections::HashMap; -// Create a new RhaiTerraIntegration -let mut integration = RhaiTerraIntegration::new(); +// Create a new RhaiTeraIntegration +let mut integration = RhaiTeraIntegration::new(); // Load Rhai scripts integration.load_script("string_utils", "scripts/string_utils.rhai")?; integration.load_script("math_utils", "scripts/math_utils.rhai")?; -// Load Terra templates -integration.load_template("example", "templates/example.terra")?; +// Load Tera templates +integration.load_template("example", "templates/example.tera")?; ``` ### Rendering Templates ```rust -use rhai_engine::terra_integration::{string_value, number_value, object_value}; +use rhai_engine::tera_integration::{string_value, number_value, object_value}; // Create context data for the template let mut context = HashMap::new(); @@ -102,7 +102,7 @@ if let Some(capitalize_fn) = functions.get("string_utils:capitalize") { ## Writing Rhai Scripts -Rhai scripts can define functions that will be automatically exposed to Terra templates as filters. For example: +Rhai scripts can define functions that will be automatically exposed to Tera templates as filters. For example: ```js // string_utils.rhai @@ -128,9 +128,9 @@ fn capitalize(text) { } ``` -## Using Filters in Terra Templates +## Using Filters in Tera Templates -Once functions are registered, they can be used as filters in Terra templates: +Once functions are registered, they can be used as filters in Tera templates: ```html diff --git a/rhai_engine/src/rhailoader/mod.rs b/rhai_engine/src/rhailoader/mod.rs new file mode 100644 index 0000000..5998dcb --- /dev/null +++ b/rhai_engine/src/rhailoader/mod.rs @@ -0,0 +1,6 @@ + +mod script_manager; + + +pub use script_manager::ScriptManager; + diff --git a/rhai_engine/src/terra_integration/script_manager.rs b/rhai_engine/src/rhailoader/script_manager.rs similarity index 100% rename from rhai_engine/src/terra_integration/script_manager.rs rename to rhai_engine/src/rhailoader/script_manager.rs diff --git a/rhai_engine/src/simple_example.rs b/rhai_engine/src/simple_example.rs deleted file mode 100644 index 3e1ebe9..0000000 --- a/rhai_engine/src/simple_example.rs +++ /dev/null @@ -1,149 +0,0 @@ -//! Simple Example of Exposing Rhai Functions to a Hashmap -//! -//! This example demonstrates how to expose Rhai scripts dynamically to Rust -//! and make the functions available in a hashmap for a rendering engine. - -use rhai::{Engine, Scope, Dynamic}; -use std::collections::HashMap; -use std::fs; - -/// A simple function type that can be stored in a hashmap -type RhaiFunction = Box) -> Dynamic>; - -/// Load a Rhai script and expose its functions -pub fn main() { - // Create a new Rhai engine - let mut engine = Engine::new(); - - // Register string functions that our scripts need - register_string_functions(&mut engine); - - // Load and compile the string_utils.rhai script - let script_path = "src/terra_integration/scripts/string_utils.rhai"; - let script_content = fs::read_to_string(script_path).expect("Failed to read script file"); - let ast = engine.compile(&script_content).expect("Failed to compile script"); - - // Create a hashmap to store the functions - let mut functions: HashMap = HashMap::new(); - - // Create a clone of the AST for each function - let ast_clone1 = ast.clone(); - let ast_clone2 = ast.clone(); - let ast_clone3 = ast.clone(); - - // Register the capitalize function - functions.insert("capitalize".to_string(), Box::new(move |args| { - let mut engine = Engine::new(); - register_string_functions(&mut engine); - let mut scope = Scope::new(); - let result = engine.call_fn::(&mut scope, &ast_clone1, "capitalize", args) - .unwrap_or_else(|e| Dynamic::from(format!("Error: {}", e))); - result - })); - - // Register the truncate function - functions.insert("truncate".to_string(), Box::new(move |args| { - let mut engine = Engine::new(); - register_string_functions(&mut engine); - let mut scope = Scope::new(); - let result = engine.call_fn::(&mut scope, &ast_clone2, "truncate", args) - .unwrap_or_else(|e| Dynamic::from(format!("Error: {}", e))); - result - })); - - // Register the slugify function - functions.insert("slugify".to_string(), Box::new(move |args| { - let mut engine = Engine::new(); - register_string_functions(&mut engine); - let mut scope = Scope::new(); - let result = engine.call_fn::(&mut scope, &ast_clone3, "slugify", args) - .unwrap_or_else(|e| Dynamic::from(format!("Error: {}", e))); - result - })); - - // Now we can use these functions from the hashmap - println!("Functions available:"); - for name in functions.keys() { - println!("- {}", name); - } - - // Example of calling a function from the hashmap - let capitalize_fn = functions.get("capitalize").expect("Function not found"); - let result = capitalize_fn(vec![Dynamic::from("hello world")]); - println!("capitalize('hello world') => {}", result); - - let truncate_fn = functions.get("truncate").expect("Function not found"); - let result = truncate_fn(vec![Dynamic::from("This is a long text that will be truncated"), Dynamic::from(20)]); - println!("truncate('This is a long text that will be truncated', 20) => {}", result); - - let slugify_fn = functions.get("slugify").expect("Function not found"); - let result = slugify_fn(vec![Dynamic::from("Hello, World!")]); - println!("slugify('Hello, World!') => {}", result); - - // This is how a rendering engine like Terra could use these functions - println!("\nExample of how a rendering engine might use these functions:"); - - // Simulate a template rendering with a filter - let template_var = "hello world"; - println!("Original: {}", template_var); - println!("After capitalize filter: {}", capitalize_fn(vec![Dynamic::from(template_var)])); - - let template_var = "This is a description that is too long for a meta tag"; - println!("Original: {}", template_var); - println!("After truncate filter: {}", truncate_fn(vec![Dynamic::from(template_var), Dynamic::from(30)])); - - let template_var = "Hello, World! This is a Title"; - println!("Original: {}", template_var); - println!("After slugify filter: {}", slugify_fn(vec![Dynamic::from(template_var)])); -} - -/// Register string functions that our scripts need -fn register_string_functions(engine: &mut Engine) { - // Register string functions - engine.register_fn("substr", |s: &str, start: i64, len: i64| { - let start = start as usize; - let len = len as usize; - if start >= s.len() { - return String::new(); - } - let end = std::cmp::min(start + len, s.len()); - s[start..end].to_string() - }); - - engine.register_fn("to_upper", |s: &str| s.to_uppercase()); - engine.register_fn("to_lower", |s: &str| s.to_lowercase()); - - // Register split function that returns an array - engine.register_fn("split", |s: &str, delimiter: &str| { - let parts: Vec = s.split(delimiter) - .map(|part| Dynamic::from(part.to_string())) - .collect(); - Dynamic::from_array(parts) - }); - - // Register join function for arrays - engine.register_fn("join", |arr: rhai::Array, delimiter: &str| { - let strings: Vec = arr.iter() - .map(|item| item.to_string()) - .collect(); - strings.join(delimiter) - }); - - // Register push function for arrays - engine.register_fn("push", |arr: &mut rhai::Array, item: Dynamic| { - arr.push(item); - }); - - // Register replace function - engine.register_fn("replace", |s: &str, from: &str, to: &str| { - s.replace(from, to) - }); - - // Register comparison operators for different integer types - engine.register_fn("<=", |a: i64, b: i32| a <= b as i64); - engine.register_fn("<=", |a: i32, b: i64| (a as i64) <= b); - - // Register arithmetic operators for different integer types - engine.register_fn("-", |a: i64, b: i32| a - (b as i64)); - engine.register_fn("-", |a: i32, b: i64| (a as i64) - b); -} \ No newline at end of file diff --git a/rhai_engine/src/terra_integration/example.rs b/rhai_engine/src/terra_integration/example.rs deleted file mode 100644 index 4dce7af..0000000 --- a/rhai_engine/src/terra_integration/example.rs +++ /dev/null @@ -1,161 +0,0 @@ -//! Example usage of the Terra Integration Module -//! -//! This example demonstrates how to use the RhaiTerraIntegration to: -//! 1. Load Rhai scripts with functions and filters -//! 2. Load Terra templates that use these functions and filters -//! 3. Render templates with dynamic data -//! 4. Access Rhai functions directly from Rust - -use crate::terra_integration::{ - RhaiTerraIntegration, - string_value, - number_value, - boolean_value, - object_value, - array_value, - Value, -}; -use std::collections::HashMap; -use std::path::Path; -use rhai::Dynamic; - -/// Run the example -pub fn run_example() -> Result<(), String> { - // Create a new RhaiTerraIntegration - let mut integration = RhaiTerraIntegration::new(); - - // Load Rhai scripts - integration.load_script("string_utils", "src/terra_integration/scripts/string_utils.rhai")?; - integration.load_script("math_utils", "src/terra_integration/scripts/math_utils.rhai")?; - - // Load Terra templates - integration.load_template("example", "src/terra_integration/templates/example.terra")?; - - // Create context data for the template - let mut context = HashMap::new(); - context.insert("title".to_string(), string_value("welcome to our website")); - context.insert("description".to_string(), string_value("This is a demonstration of integrating Rhai scripts with Terra templates. We can use Rhai functions as filters and directly call them from templates.")); - context.insert("content".to_string(), string_value("

This is the main content of the page.

")); - - // Create stats object - let mut stats = HashMap::new(); - stats.insert("users".to_string(), number_value(12345)); - stats.insert("visitors".to_string(), number_value(50000)); - stats.insert("conversions".to_string(), number_value(2500)); - stats.insert("revenue".to_string(), number_value(98765.43)); - - context.insert("stats".to_string(), object_value(stats)); - context.insert("current_year".to_string(), number_value(2025.0)); - - // Render the template - let rendered = integration.render("example", context)?; - println!("Rendered template:\n{}", rendered); - - // Demonstrate direct function calls - println!("\nDirect function calls:"); - - // Call string_utils:capitalize - let result = integration.call_function( - "string_utils:capitalize", - vec![Dynamic::from("hello world")] - )?; - println!("capitalize('hello world') => {}", result); - - // Call math_utils:format_number - let result = integration.call_function( - "math_utils:format_number", - vec![Dynamic::from(1234567)] - )?; - println!("format_number(1234567) => {}", result); - - // Call math_utils:percentage - let result = integration.call_function( - "math_utils:percentage", - vec![Dynamic::from(75), Dynamic::from(100)] - )?; - println!("percentage(75, 100) => {}", result); - - // Call string_utils:slugify - let result = integration.call_function( - "string_utils:slugify", - vec![Dynamic::from("Hello, World!")] - )?; - println!("slugify('Hello, World!') => {}", result); - - // Print all available functions - println!("\nAvailable functions:"); - for name in integration.get_function_names() { - println!("- {}", name); - } - - // Filters are just functions without script: prefix - println!("\nAvailable filters (same as functions without script: prefix):"); - for name in integration.get_function_names() { - println!("- {}", name); - } - - Ok(()) -} - -/// Example of how to expose Rhai functions to a rendering engine like Terra -pub fn example_expose_to_rendering_engine() { - // Create a new RhaiTerraIntegration - let mut integration = RhaiTerraIntegration::new(); - - // Load Rhai scripts - if let Err(e) = integration.load_script("string_utils", "src/terra_integration/scripts/string_utils.rhai") { - eprintln!("Error loading script: {}", e); - return; - } - - if let Err(e) = integration.load_script("math_utils", "src/terra_integration/scripts/math_utils.rhai") { - eprintln!("Error loading script: {}", e); - return; - } - - // Get all function names - let function_names = integration.get_function_names(); - - // This HashMap can now be passed to any rendering engine - println!("Functions available to rendering engine:"); - for name in &function_names { - println!("- {}", name); - } - - // Simulating how a rendering engine might call these functions - println!("\nSimulated rendering engine function calls:"); - simulate_function_call(&integration, "string_utils:capitalize", vec![Dynamic::from("hello world")]); - simulate_function_call(&integration, "math_utils:format_number", vec![Dynamic::from(1234567)]); - - // Example of how a rendering engine might use these functions - println!("\nRendering engine function calls:"); - - // Get a specific function - if let Some(capitalize_fn) = functions.get("string_utils:capitalize") { - // Call the function with arguments - match capitalize_fn(vec![Dynamic::from("hello world")]) { - Ok(result) => println!("capitalize('hello world') => {}", result), - Err(e) => println!("Error: {}", e), - } - } - - if let Some(format_number_fn) = functions.get("math_utils:format_number") { - match format_number_fn(vec![Dynamic::from(1234567)]) { - Ok(result) => println!("format_number(1234567) => {}", result), - Err(e) => println!("Error: {}", e), - } - } - - // This demonstrates how a rendering engine like Terra could access and use - // the Rhai functions through the HashMap -} - -// Helper function to simulate function calls in a rendering engine -fn simulate_function_call(integration: &RhaiTerraIntegration, name: &str, args: Vec) { - println!("Calling function: {}", name); - - match integration.call_function(name, args) { - Ok(result) => println!(" Result: {}", result), - Err(e) => println!(" Error: {}", e), - } -} \ No newline at end of file diff --git a/rhai_engine/src/terra_integration/mod.rs b/rhai_engine/src/terra_integration/mod.rs deleted file mode 100644 index ca18b15..0000000 --- a/rhai_engine/src/terra_integration/mod.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Terra Integration Module -//! -//! This module provides integration between Rhai scripts and Terra templates. -//! It allows exposing Rhai functions and filters to Terra templates dynamically. - -mod script_manager; -mod terra_renderer; - -pub use script_manager::ScriptManager; -pub use terra_renderer::{TerraRenderer, Value, Context, Template}; - -use std::path::Path; -use std::sync::{Arc, RwLock}; -use std::collections::HashMap; - -/// RhaiTerraIntegration provides a simplified interface for integrating Rhai with Terra -pub struct RhaiTerraIntegration { - script_manager: Arc>, - renderer: TerraRenderer, -} - -impl RhaiTerraIntegration { - /// Create a new RhaiTerraIntegration - pub fn new() -> Self { - let script_manager = Arc::new(RwLock::new(ScriptManager::new())); - let renderer = TerraRenderer::new(script_manager.clone()); - - Self { - script_manager, - renderer, - } - } - - /// Load a Rhai script from a file - pub fn load_script(&mut self, name: &str, path: impl AsRef) -> Result<(), String> { - self.script_manager.write().unwrap().load_script(name, path) - } - - /// Load a Terra template from a file - pub fn load_template(&mut self, name: &str, path: impl AsRef) -> Result<(), String> { - self.renderer.load_template(name, path) - } - - /// Render a template with the given context - pub fn render(&self, template_name: &str, context: HashMap) -> Result { - self.renderer.render(template_name, context) - } - - /// Call a Rhai function by its full name (script:function) - pub fn call_function(&self, name: &str, args: Vec) -> Result { - self.script_manager.read().unwrap().call_function(name, args) - } - - /// Get all available Rhai function names - pub fn get_function_names(&self) -> Vec { - let script_manager = self.script_manager.read().unwrap(); - let function_map = script_manager.get_all_functions(); - function_map.keys().cloned().collect() - } -} - -/// Create a string value for Terra templates -pub fn string_value(s: impl Into) -> Value { - Value::String(s.into()) -} - -/// Create a number value for Terra templates -pub fn number_value(n: f64) -> Value { - Value::Number(n) -} - -/// Create a boolean value for Terra templates -pub fn boolean_value(b: bool) -> Value { - Value::Boolean(b) -} - -/// Create an object value for Terra templates -pub fn object_value(map: HashMap) -> Value { - Value::Object(map) -} - -/// Create an array value for Terra templates -pub fn array_value(arr: Vec) -> Value { - Value::Array(arr) -} \ No newline at end of file diff --git a/rhai_engine/src/terra_integration/templates/example.terra b/rhai_engine/src/terra_integration/templates/example.terra deleted file mode 100644 index 92cb4ab..0000000 --- a/rhai_engine/src/terra_integration/templates/example.terra +++ /dev/null @@ -1,38 +0,0 @@ - - - - {{ title | capitalize }} - - - - -
-

{{ title | capitalize }}

-
- -
-
- {{ content }} -
- -
-
- Total Users - {{ stats.users | format_number }} -
-
- Conversion Rate - {{ percentage(stats.conversions, stats.visitors) }} -
-
- Revenue - {{ currency(stats.revenue, "$") }} -
-
-
- -
-

© {{ current_year }} Example Company

-
- - \ No newline at end of file diff --git a/rhai_engine/src/terra_integration/terra_renderer.rs b/rhai_engine/src/terra_integration/terra_renderer.rs deleted file mode 100644 index d68826a..0000000 --- a/rhai_engine/src/terra_integration/terra_renderer.rs +++ /dev/null @@ -1,226 +0,0 @@ -use crate::terra_integration::script_manager::ScriptManager; -use std::collections::HashMap; -use std::path::Path; -use std::fs; -use std::sync::{Arc, RwLock}; - -/// A mock Terra Value type for demonstration purposes -/// In a real implementation, this would be the actual Terra Value type -#[derive(Debug, Clone)] -pub enum Value { - String(String), - Number(f64), - Boolean(bool), - Array(Vec), - Object(HashMap), - Null, -} - -impl Value { - pub fn from_string(s: impl Into) -> Self { - Value::String(s.into()) - } - - pub fn from_number(n: f64) -> Self { - Value::Number(n) - } - - pub fn from_bool(b: bool) -> Self { - Value::Boolean(b) - } - - pub fn as_string(&self) -> Option<&String> { - if let Value::String(s) = self { - Some(s) - } else { - None - } - } - - pub fn as_number(&self) -> Option { - if let Value::Number(n) = self { - Some(*n) - } else { - None - } - } -} - -/// A mock Terra Context type for demonstration purposes -pub type Context = HashMap; - -/// A mock Terra Template type for demonstration purposes -#[derive(Debug, Clone)] -pub struct Template { - content: String, -} - -/// A mock Terra TemplateEngine type for demonstration purposes -pub struct TemplateEngine { - // Using a simple closure that doesn't need Send + Sync - filters: HashMap) -> Value>>, -} - -impl TemplateEngine { - pub fn new() -> Self { - Self { - filters: HashMap::new(), - } - } - - pub fn register_filter(&mut self, name: &str, filter: F) - where - F: Fn(Vec) -> Value + 'static, // No Send + Sync - { - self.filters.insert(name.to_string(), Box::new(filter)); - } - - pub fn compile(&self, template_content: &str) -> Result { - // In a real implementation, this would compile the template - // For this example, we just store the content - Ok(Template { - content: template_content.to_string(), - }) - } - - pub fn render(&self, _template: &Template, context: &Context) -> Result { - // In a real implementation, this would render the template with the context - // For this example, we just return a placeholder - Ok(format!("Rendered template with {} filters and {} context variables", - self.filters.len(), - context.len())) - } -} - -/// TerraRenderer integrates Rhai scripts with Terra templates -pub struct TerraRenderer { - script_manager: Arc>, - template_engine: TemplateEngine, - templates: HashMap, -} - -impl TerraRenderer { - pub fn new(script_manager: Arc>) -> Self { - let mut template_engine = TemplateEngine::new(); - - // Register all Rhai filters with Terra - // Create a new local scope to limit the lifetime of the lock - let filters = { - // Get a read lock on the script manager - let manager = script_manager.read().unwrap(); - // Get all the filters (returns a reference) - let filters_ref = manager.get_all_filters(); - // Create a copy of the filter names for use outside the lock - filters_ref.keys().cloned().collect::>() - }; - - // Register each filter - for name in filters { - let script_manager_clone = script_manager.clone(); - let name_clone = name.clone(); - - template_engine.register_filter(&name, move |args: Vec| { - // Convert Terra Values to Rhai Dynamic values - let rhai_args = args.into_iter() - .map(|v| Self::terra_value_to_rhai_dynamic(v)) - .collect::>(); - - // Call the filter by name from the script manager - match script_manager_clone.read().unwrap().call_filter(&name_clone, rhai_args) { - Ok(result) => Self::rhai_dynamic_to_terra_value(result), - Err(e) => Value::String(format!("Error: {}", e)), - } - }); - } - - Self { - script_manager, - template_engine, - templates: HashMap::new(), - } - } - - /// Load a template from a file - pub fn load_template(&mut self, name: &str, path: impl AsRef) -> Result<(), String> { - let path = path.as_ref(); - - // Read the template file - let template_content = fs::read_to_string(path) - .map_err(|e| format!("Failed to read template file {}: {}", path.display(), e))?; - - // Compile the template - let template = self.template_engine.compile(&template_content)?; - - // Store the compiled template - self.templates.insert(name.to_string(), template); - - Ok(()) - } - - /// Render a template with the given context - pub fn render(&self, template_name: &str, context: Context) -> Result { - let template = self.templates.get(template_name) - .ok_or_else(|| format!("Template not found: {}", template_name))?; - - // Render the template - self.template_engine.render(template, &context) - } - - /// Convert a Terra Value to a Rhai Dynamic - fn terra_value_to_rhai_dynamic(value: Value) -> rhai::Dynamic { - use rhai::Dynamic; - - match value { - Value::String(s) => Dynamic::from(s), - Value::Number(n) => Dynamic::from(n), - Value::Boolean(b) => Dynamic::from(b), - Value::Array(arr) => { - let rhai_arr = arr.into_iter() - .map(Self::terra_value_to_rhai_dynamic) - .collect::>(); - Dynamic::from(rhai_arr) - }, - Value::Object(obj) => { - let mut rhai_map = rhai::Map::new(); - for (k, v) in obj { - rhai_map.insert(k.into(), Self::terra_value_to_rhai_dynamic(v)); - } - Dynamic::from(rhai_map) - }, - Value::Null => Dynamic::UNIT, - } - } - - /// Convert a Rhai Dynamic to a Terra Value - fn rhai_dynamic_to_terra_value(value: rhai::Dynamic) -> Value { - if value.is::() { - Value::String(value.into_string().unwrap()) - } else if value.is::() { - Value::Number(value.as_int().unwrap() as f64) - } else if value.is::() { - Value::Number(value.as_float().unwrap()) - } else if value.is::() { - Value::Boolean(value.as_bool().unwrap()) - } else if value.is_array() { - let arr = value.into_array().unwrap(); - let terra_arr = arr.into_iter() - .map(Self::rhai_dynamic_to_terra_value) - .collect(); - Value::Array(terra_arr) - } else if value.is::() { - // Use a different approach to handle maps - // Create a new HashMap for Terra - let mut terra_map = HashMap::new(); - - // Get the Map as a reference and convert each key-value pair - if let Some(map) = value.try_cast::() { - for (k, v) in map.iter() { - terra_map.insert(k.to_string(), Self::rhai_dynamic_to_terra_value(v.clone())); - } - } - Value::Object(terra_map) - } else { - Value::Null - } - } -} \ No newline at end of file