...
This commit is contained in:
parent
a9a44f96cf
commit
868c870bf0
@ -1,4 +1,4 @@
|
|||||||
// Math utility functions for Terra templates
|
// Math utility functions for Tera templates
|
||||||
|
|
||||||
// Format a number with commas as thousands separators
|
// Format a number with commas as thousands separators
|
||||||
fn format_number(num) {
|
fn format_number(num) {
|
@ -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
|
// Capitalize the first letter of each word
|
||||||
fn capitalize(text) {
|
fn capitalize(text) {
|
@ -1,4 +1,4 @@
|
|||||||
use crate::terra_integration::RhaiTerraIntegration;
|
use crate::tera_integration::RhaiTeraIntegration;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use rhai::Dynamic;
|
use rhai::Dynamic;
|
||||||
|
|
||||||
@ -6,17 +6,17 @@ use rhai::Dynamic;
|
|||||||
pub fn test_dynamic_loading() -> Result<(), String> {
|
pub fn test_dynamic_loading() -> Result<(), String> {
|
||||||
println!("\n=== TESTING DYNAMIC FUNCTION LOADING ===\n");
|
println!("\n=== TESTING DYNAMIC FUNCTION LOADING ===\n");
|
||||||
|
|
||||||
// Create a new RhaiTerraIntegration
|
// Create a new RhaiTeraIntegration
|
||||||
let mut integration = RhaiTerraIntegration::new();
|
let mut integration = RhaiTeraIntegration::new();
|
||||||
|
|
||||||
println!("Loading Rhai scripts...");
|
println!("Loading Rhai scripts...");
|
||||||
|
|
||||||
// Load the original scripts
|
// Load the original scripts
|
||||||
integration.load_script("string_utils", "src/terra_integration/scripts/string_utils.rhai")?;
|
integration.load_script("string_utils", "src/tera_integration/scripts/string_utils.rhai")?;
|
||||||
integration.load_script("math_utils", "src/terra_integration/scripts/math_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)
|
// 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
|
// Get function names
|
||||||
let function_names = integration.get_function_names();
|
let function_names = integration.get_function_names();
|
||||||
@ -86,7 +86,7 @@ fn display_functions_by_script(scripts: &HashMap<String, Vec<String>>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to test a function and display its result
|
// Helper function to test a function and display its result
|
||||||
fn test_function(integration: &RhaiTerraIntegration, name: &str, args: Vec<Dynamic>) -> Result<(), String> {
|
fn test_function(integration: &RhaiTeraIntegration, name: &str, args: Vec<Dynamic>) -> Result<(), String> {
|
||||||
let result = integration.call_function(name, args)?;
|
let result = integration.call_function(name, args)?;
|
||||||
println!(" {}() => {}", name, result);
|
println!(" {}() => {}", name, result);
|
||||||
Ok(())
|
Ok(())
|
@ -1,5 +1,5 @@
|
|||||||
// Data objects for Terra template integration
|
// Data objects for Tera template integration
|
||||||
// This script defines complex objects that can be accessed from Terra templates
|
// This script defines complex objects that can be accessed from Tera templates
|
||||||
|
|
||||||
// Create a products catalog object
|
// Create a products catalog object
|
||||||
fn create_products() {
|
fn create_products() {
|
@ -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;
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
## Overview
|
||||||
|
|
||||||
The Rhai Terra Integration module allows you to:
|
The Rhai Tera Integration module allows you to:
|
||||||
|
|
||||||
1. Load and compile Rhai scripts dynamically
|
1. Load and compile Rhai scripts dynamically
|
||||||
2. Extract functions from these scripts and expose them as callable functions in Rust
|
2. Extract functions from these scripts and expose them as callable functions in Rust
|
||||||
3. Register these functions as filters in Terra templates
|
3. Register these functions as filters in Tera templates
|
||||||
4. Render Terra templates with these filters
|
4. Render Tera templates with these filters
|
||||||
5. Directly call Rhai functions from Rust code
|
5. Directly call Rhai functions from Rust code
|
||||||
|
|
||||||
## Key Components
|
## Key Components
|
||||||
|
|
||||||
- **ScriptManager**: Handles loading, compiling, and exposing Rhai scripts
|
- **ScriptManager**: Handles loading, compiling, and exposing Rhai scripts
|
||||||
- **TerraRenderer**: Integrates with the Terra template engine
|
- **TeraRenderer**: Integrates with the Tera template engine
|
||||||
- **RhaiTerraIntegration**: Provides a simplified interface for the integration
|
- **RhaiTeraIntegration**: Provides a simplified interface for the integration
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
### Loading Scripts and Templates
|
### Loading Scripts and Templates
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai_engine::terra_integration::RhaiTerraIntegration;
|
use rhai_engine::tera_integration::RhaiTeraIntegration;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// Create a new RhaiTerraIntegration
|
// Create a new RhaiTeraIntegration
|
||||||
let mut integration = RhaiTerraIntegration::new();
|
let mut integration = RhaiTeraIntegration::new();
|
||||||
|
|
||||||
// Load Rhai scripts
|
// Load Rhai scripts
|
||||||
integration.load_script("string_utils", "scripts/string_utils.rhai")?;
|
integration.load_script("string_utils", "scripts/string_utils.rhai")?;
|
||||||
integration.load_script("math_utils", "scripts/math_utils.rhai")?;
|
integration.load_script("math_utils", "scripts/math_utils.rhai")?;
|
||||||
|
|
||||||
// Load Terra templates
|
// Load Tera templates
|
||||||
integration.load_template("example", "templates/example.terra")?;
|
integration.load_template("example", "templates/example.tera")?;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Rendering Templates
|
### Rendering Templates
|
||||||
|
|
||||||
```rust
|
```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
|
// Create context data for the template
|
||||||
let mut context = HashMap::new();
|
let mut context = HashMap::new();
|
||||||
@ -102,7 +102,7 @@ if let Some(capitalize_fn) = functions.get("string_utils:capitalize") {
|
|||||||
|
|
||||||
## Writing Rhai Scripts
|
## 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
|
```js
|
||||||
// string_utils.rhai
|
// 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
|
```html
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
6
rhai_engine/src/rhailoader/mod.rs
Normal file
6
rhai_engine/src/rhailoader/mod.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
mod script_manager;
|
||||||
|
|
||||||
|
|
||||||
|
pub use script_manager::ScriptManager;
|
||||||
|
|
@ -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<dyn Fn(Vec<Dynamic>) -> 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<String, RhaiFunction> = 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::<Dynamic>(&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::<Dynamic>(&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::<Dynamic>(&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<Dynamic> = 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<String> = 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);
|
|
||||||
}
|
|
@ -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("<p>This is the main content of the page.</p>"));
|
|
||||||
|
|
||||||
// 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<Dynamic>) {
|
|
||||||
println!("Calling function: {}", name);
|
|
||||||
|
|
||||||
match integration.call_function(name, args) {
|
|
||||||
Ok(result) => println!(" Result: {}", result),
|
|
||||||
Err(e) => println!(" Error: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<RwLock<ScriptManager>>,
|
|
||||||
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<Path>) -> 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<Path>) -> Result<(), String> {
|
|
||||||
self.renderer.load_template(name, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render a template with the given context
|
|
||||||
pub fn render(&self, template_name: &str, context: HashMap<String, Value>) -> Result<String, String> {
|
|
||||||
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<rhai::Dynamic>) -> Result<rhai::Dynamic, String> {
|
|
||||||
self.script_manager.read().unwrap().call_function(name, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get all available Rhai function names
|
|
||||||
pub fn get_function_names(&self) -> Vec<String> {
|
|
||||||
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<String>) -> 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<String, Value>) -> Value {
|
|
||||||
Value::Object(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create an array value for Terra templates
|
|
||||||
pub fn array_value(arr: Vec<Value>) -> Value {
|
|
||||||
Value::Array(arr)
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>{{ title | capitalize }}</title>
|
|
||||||
<meta name="description" content="{{ description | truncate(160) }}">
|
|
||||||
<link rel="canonical" href="/{{ title | slugify }}">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<h1>{{ title | capitalize }}</h1>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div class="content">
|
|
||||||
{{ content }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stats">
|
|
||||||
<div class="stat">
|
|
||||||
<span class="label">Total Users</span>
|
|
||||||
<span class="value">{{ stats.users | format_number }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="stat">
|
|
||||||
<span class="label">Conversion Rate</span>
|
|
||||||
<span class="value">{{ percentage(stats.conversions, stats.visitors) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="stat">
|
|
||||||
<span class="label">Revenue</span>
|
|
||||||
<span class="value">{{ currency(stats.revenue, "$") }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<p>© {{ current_year }} Example Company</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -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<Value>),
|
|
||||||
Object(HashMap<String, Value>),
|
|
||||||
Null,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Value {
|
|
||||||
pub fn from_string(s: impl Into<String>) -> 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<f64> {
|
|
||||||
if let Value::Number(n) = self {
|
|
||||||
Some(*n)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A mock Terra Context type for demonstration purposes
|
|
||||||
pub type Context = HashMap<String, Value>;
|
|
||||||
|
|
||||||
/// 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<String, Box<dyn Fn(Vec<Value>) -> Value>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TemplateEngine {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
filters: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_filter<F>(&mut self, name: &str, filter: F)
|
|
||||||
where
|
|
||||||
F: Fn(Vec<Value>) -> Value + 'static, // No Send + Sync
|
|
||||||
{
|
|
||||||
self.filters.insert(name.to_string(), Box::new(filter));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compile(&self, template_content: &str) -> Result<Template, String> {
|
|
||||||
// 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<String, String> {
|
|
||||||
// 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<RwLock<ScriptManager>>,
|
|
||||||
template_engine: TemplateEngine,
|
|
||||||
templates: HashMap<String, Template>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TerraRenderer {
|
|
||||||
pub fn new(script_manager: Arc<RwLock<ScriptManager>>) -> 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::<Vec<String>>()
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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<Value>| {
|
|
||||||
// Convert Terra Values to Rhai Dynamic values
|
|
||||||
let rhai_args = args.into_iter()
|
|
||||||
.map(|v| Self::terra_value_to_rhai_dynamic(v))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// 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<Path>) -> 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<String, String> {
|
|
||||||
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::<Vec<_>>();
|
|
||||||
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::<String>() {
|
|
||||||
Value::String(value.into_string().unwrap())
|
|
||||||
} else if value.is::<i64>() {
|
|
||||||
Value::Number(value.as_int().unwrap() as f64)
|
|
||||||
} else if value.is::<f64>() {
|
|
||||||
Value::Number(value.as_float().unwrap())
|
|
||||||
} else if value.is::<bool>() {
|
|
||||||
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::<rhai::Map>() {
|
|
||||||
// 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::<rhai::Map>() {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user