...
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
|
||||
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
|
||||
fn capitalize(text) {
|
@ -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<String, Vec<String>>) {
|
||||
}
|
||||
|
||||
// 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)?;
|
||||
println!(" {}() => {}", name, result);
|
||||
Ok(())
|
@ -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() {
|
@ -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
|
||||
|
||||
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
|
||||
<!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