This commit is contained in:
kristof 2025-04-03 12:24:59 +02:00
parent a9a44f96cf
commit 868c870bf0
16 changed files with 33 additions and 713 deletions

View File

@ -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) {

View File

@ -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) {

View File

@ -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(())

View File

@ -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() {

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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>

View File

@ -0,0 +1,6 @@
mod script_manager;
pub use script_manager::ScriptManager;

View File

@ -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);
}

View File

@ -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),
}
}

View File

@ -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)
}

View File

@ -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>

View File

@ -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
}
}
}