feat(tera_factory): Implement hot reload example for Tera templates with Rhai
This commit adds a comprehensive hot reload example that demonstrates how to use the rhai_system for dynamic template rendering with Tera. Key improvements include: - Refactor the example to use external script files instead of hardcoded Rhai code - Implement proper module imports using the BasePathModuleResolver approach - Fix template rendering by using keyword arguments in Tera function calls - Add support for hot reloading both main and utility scripts - Remove unnecessary output file generation to keep the example clean - Fix compatibility issues with Rhai functions (avoiding to_string with parameters) This example showcases how changes to Rhai scripts are automatically detected and applied to rendered templates without restarting the application, providing a smooth development experience.
This commit is contained in:
		
							
								
								
									
										1608
									
								
								tera_factory/examples/basic/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1608
									
								
								tera_factory/examples/basic/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										11
									
								
								tera_factory/examples/basic/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tera_factory/examples/basic/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "tera-factory-example"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
tera_factory = { path = "../.." }
 | 
			
		||||
rhai_factory = { path = "../../../rhai_factory" }
 | 
			
		||||
tera = "1.19.0"
 | 
			
		||||
rhai = { version = "1.15.1", features = ["sync"] }
 | 
			
		||||
env_logger = "0.11"
 | 
			
		||||
							
								
								
									
										21
									
								
								tera_factory/examples/basic/scripts/math.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tera_factory/examples/basic/scripts/math.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
// Basic math functions for Tera templates
 | 
			
		||||
 | 
			
		||||
// Add two numbers together
 | 
			
		||||
fn sum(a, b) {
 | 
			
		||||
    a + b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Multiply two numbers
 | 
			
		||||
fn multiply(a, b) {
 | 
			
		||||
    a * b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a number with a prefix and suffix
 | 
			
		||||
fn format_number(number, prefix, suffix) {
 | 
			
		||||
    prefix + number.to_string() + suffix
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a greeting with a name
 | 
			
		||||
fn greet(name) {
 | 
			
		||||
    "Hey, " + name + "!"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										98
									
								
								tera_factory/examples/basic/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								tera_factory/examples/basic/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::sync::{Arc, RwLock};
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use std::thread;
 | 
			
		||||
 | 
			
		||||
use rhai_factory::RhaiFactory;
 | 
			
		||||
use tera::Context;
 | 
			
		||||
use tera_factory::TeraFactory;
 | 
			
		||||
use env_logger;
 | 
			
		||||
 | 
			
		||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    env_logger::init();
 | 
			
		||||
    println!("Starting Tera with Rhai example...");
 | 
			
		||||
    
 | 
			
		||||
    // Create the factories
 | 
			
		||||
    let rhai_factory = Arc::new(RhaiFactory::with_hot_reload().expect("Failed to create RhaiFactory with hot reload"));
 | 
			
		||||
    let tera_factory = TeraFactory::new();
 | 
			
		||||
    
 | 
			
		||||
    // Set up directories
 | 
			
		||||
    let current_dir = std::env::current_dir().expect("Failed to get current directory");
 | 
			
		||||
    let scripts_dir = current_dir.join("scripts");
 | 
			
		||||
    let templates_dir = current_dir.join("templates");
 | 
			
		||||
    
 | 
			
		||||
    // Compile the initial script
 | 
			
		||||
    let script_path = scripts_dir.join("math.rhai");
 | 
			
		||||
    println!("Compiling Rhai script: {:?}", script_path);
 | 
			
		||||
    
 | 
			
		||||
    let ast = rhai_factory.compile_modules(&[&script_path], None)?;
 | 
			
		||||
    let hot_ast = Arc::new(RwLock::new(ast));
 | 
			
		||||
    
 | 
			
		||||
    // Enable hot reloading
 | 
			
		||||
    println!("Enabling hot reloading for Rhai scripts...");
 | 
			
		||||
    let _handle = rhai_factory.enable_hot_reload(
 | 
			
		||||
        hot_ast.clone(),
 | 
			
		||||
        &[&script_path],
 | 
			
		||||
        None,
 | 
			
		||||
        Some(Box::new(|| println!("Script reloaded! Functions have been updated.")))
 | 
			
		||||
    )?;
 | 
			
		||||
    
 | 
			
		||||
    // Create a Tera engine with Rhai integration
 | 
			
		||||
    println!("Creating Tera engine with Rhai integration...");
 | 
			
		||||
    println!("Template directory: {:?}", templates_dir);
 | 
			
		||||
    if templates_dir.exists() {
 | 
			
		||||
        println!("Template directory contents:");
 | 
			
		||||
        for entry in std::fs::read_dir(&templates_dir).unwrap() {
 | 
			
		||||
            println!("  {:?}", entry.unwrap().path());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    let tera = tera_factory.create_tera_with_rhai(&[&templates_dir], hot_ast.clone())?;
 | 
			
		||||
    
 | 
			
		||||
    println!("\nInitial template rendering:");
 | 
			
		||||
    println!("==========================");
 | 
			
		||||
    
 | 
			
		||||
    // Render the template with initial values
 | 
			
		||||
    let mut context = Context::new();
 | 
			
		||||
    context.insert("name", "World");
 | 
			
		||||
    context.insert("a", &10);
 | 
			
		||||
    context.insert("b", &5);
 | 
			
		||||
    
 | 
			
		||||
    let rendered = tera.render("index.html", &context)?;
 | 
			
		||||
    println!("{}", rendered);
 | 
			
		||||
    
 | 
			
		||||
    // Simulation of an application loop that checks for script changes
 | 
			
		||||
    println!("\nWaiting for script changes. You can modify examples/basic/scripts/math.rhai to see hot reloading in action.");
 | 
			
		||||
    println!("Press Ctrl+C to exit.\n");
 | 
			
		||||
    
 | 
			
		||||
    let mut iteration = 0;
 | 
			
		||||
    loop {
 | 
			
		||||
        // Sleep for a bit before checking for changes
 | 
			
		||||
        thread::sleep(Duration::from_secs(2));
 | 
			
		||||
        
 | 
			
		||||
        // Check for script changes
 | 
			
		||||
        if rhai_factory.check_for_changes()? {
 | 
			
		||||
            println!("\nScript changes detected and applied!");
 | 
			
		||||
            
 | 
			
		||||
            // Re-render the template with the updated functions
 | 
			
		||||
            let rendered = tera.render("index.html", &context)?;
 | 
			
		||||
            println!("{}", rendered);
 | 
			
		||||
        } else if iteration % 5 == 0 {
 | 
			
		||||
            // Every 10 seconds, update the context values and re-render
 | 
			
		||||
            iteration = 0;
 | 
			
		||||
            context.insert("a", &(10 + (iteration % 10)));
 | 
			
		||||
            context.insert("b", &(5 + (iteration % 5)));
 | 
			
		||||
            
 | 
			
		||||
            println!("\nUpdating context values:");
 | 
			
		||||
            println!("a = {}, b = {}", 10 + (iteration % 10), 5 + (iteration % 5));
 | 
			
		||||
            
 | 
			
		||||
            let rendered = tera.render("index.html", &context)?;
 | 
			
		||||
            println!("{}", rendered);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        iteration += 1;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // This code is unreachable due to the infinite loop, but included for completeness
 | 
			
		||||
    // rhai_factory.disable_hot_reload(handle);
 | 
			
		||||
    // Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								tera_factory/examples/basic/templates/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								tera_factory/examples/basic/templates/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
    <title>Tera with Rhai Example</title>
 | 
			
		||||
    <style>
 | 
			
		||||
        body {
 | 
			
		||||
            font-family: Arial, sans-serif;
 | 
			
		||||
            margin: 40px;
 | 
			
		||||
            line-height: 1.6;
 | 
			
		||||
        }
 | 
			
		||||
        .result {
 | 
			
		||||
            background-color: #f5f5f5;
 | 
			
		||||
            padding: 15px;
 | 
			
		||||
            border-radius: 5px;
 | 
			
		||||
            margin-bottom: 20px;
 | 
			
		||||
        }
 | 
			
		||||
        h1 {
 | 
			
		||||
            color: #333;
 | 
			
		||||
        }
 | 
			
		||||
        .highlight {
 | 
			
		||||
            color: #0066cc;
 | 
			
		||||
            font-weight: bold;
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <h1>{{ greet(name="World") }}</h1>
 | 
			
		||||
    
 | 
			
		||||
    <div class="result">
 | 
			
		||||
        <p>The sum of {{ a }} and {{ b }} is: <span class="highlight">{{ sum(a=a, b=b) }}</span></p>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <div class="result">
 | 
			
		||||
        <p>{{ a }} multiplied by {{ b }} is: <span class="highlight">{{ multiply(a=a, b=b) }}</span></p>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <div class="result">
 | 
			
		||||
        <p>Formatted number: {{ format_number(number=sum(a=a, b=b), prefix="Result: ", suffix=" units") }}</p>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <p><em>This template is using Rhai functions that can be hot-reloaded!</em></p>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										96
									
								
								tera_factory/examples/hot_reload/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								tera_factory/examples/hot_reload/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
# Tera + Rhai Hot Reload Example
 | 
			
		||||
 | 
			
		||||
This example demonstrates how to use the `tera_factory` and `rhai_system` crates together to create a Tera templating engine with hot-reloadable Rhai functions.
 | 
			
		||||
 | 
			
		||||
## Overview
 | 
			
		||||
 | 
			
		||||
The example shows how to:
 | 
			
		||||
 | 
			
		||||
1. Create a hot-reloadable Rhai system with multiple script files
 | 
			
		||||
2. Integrate the Rhai system with Tera templates
 | 
			
		||||
3. Automatically reload Rhai functions when script files change
 | 
			
		||||
4. Use Rhai functions within Tera templates
 | 
			
		||||
 | 
			
		||||
## How It Works
 | 
			
		||||
 | 
			
		||||
The example uses two main components:
 | 
			
		||||
 | 
			
		||||
1. **rhai_system**: Provides hot-reloadable Rhai scripting with support for multiple script files
 | 
			
		||||
2. **tera_factory**: Creates a Tera engine with Rhai functions registered as Tera functions
 | 
			
		||||
 | 
			
		||||
When a Rhai script file changes, the system automatically:
 | 
			
		||||
1. Detects the change
 | 
			
		||||
2. Recompiles the script
 | 
			
		||||
3. Updates the Tera functions with the new implementations
 | 
			
		||||
 | 
			
		||||
This allows you to modify your Rhai functions at runtime and see the changes immediately in your rendered templates.
 | 
			
		||||
 | 
			
		||||
## Files
 | 
			
		||||
 | 
			
		||||
- **main.rs**: The main application that sets up the hot-reloadable system and renders templates
 | 
			
		||||
- **scripts/main.rhai**: Contains the main Rhai functions used in templates
 | 
			
		||||
- **scripts/utils.rhai**: Contains utility Rhai functions
 | 
			
		||||
- **templates/index.html.tera**: A Tera template that uses the Rhai functions
 | 
			
		||||
 | 
			
		||||
## Running the Example
 | 
			
		||||
 | 
			
		||||
To run this example, navigate to the root of the tera_factory project and execute:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
cargo run --example hot_reload
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## What to Expect
 | 
			
		||||
 | 
			
		||||
1. The application will set up a hot-reloadable Rhai system with two script files
 | 
			
		||||
2. It will create a Tera engine with the Rhai functions registered
 | 
			
		||||
3. It will render the template every second, showing the output in the console
 | 
			
		||||
4. After 5 seconds, it will modify the main.rhai script with updated functions
 | 
			
		||||
5. The next render will automatically use the updated functions
 | 
			
		||||
 | 
			
		||||
## Key Implementation Details
 | 
			
		||||
 | 
			
		||||
### Creating a Hot-Reloadable Rhai System
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Create a hot reloadable Rhai system
 | 
			
		||||
let script_paths = vec![main_script_path.clone(), utils_script_path.clone()];
 | 
			
		||||
let system = Arc::new(create_hot_reloadable_system(&script_paths, None)?);
 | 
			
		||||
 | 
			
		||||
// Start watching for changes to the script files
 | 
			
		||||
system.watch();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Creating a Tera Engine with Rhai Functions
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Create a TeraFactory instance
 | 
			
		||||
let factory = TeraFactory::new();
 | 
			
		||||
 | 
			
		||||
// Create a Tera instance with the hot reloadable Rhai system
 | 
			
		||||
let tera = factory.create_tera_with_hot_rhai(
 | 
			
		||||
    &[template_dir.to_str().unwrap()], 
 | 
			
		||||
    Arc::clone(&system)
 | 
			
		||||
)?;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Using Rhai Functions in Tera Templates
 | 
			
		||||
 | 
			
		||||
```html
 | 
			
		||||
<div class="greeting">{{ greet(name) }}</div>
 | 
			
		||||
<p>Today's date: <span class="date">{{ format_date(current_date) }}</span></p>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Modifying Scripts at Runtime
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Modify the main script after 5 seconds
 | 
			
		||||
modify_script(
 | 
			
		||||
    &main_script_path_clone,
 | 
			
		||||
    modified_main_content,
 | 
			
		||||
    5,
 | 
			
		||||
    "Modifying the main script with updated functions..."
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This example demonstrates a powerful pattern for dynamic template rendering with hot-reloadable business logic, allowing for rapid development and testing of template functionality without restarting your application.
 | 
			
		||||
							
								
								
									
										31
									
								
								tera_factory/examples/hot_reload/initial_main.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								tera_factory/examples/hot_reload/initial_main.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
// Main functions for Tera templates - INITIAL VERSION
 | 
			
		||||
 | 
			
		||||
// Format a greeting with the current version
 | 
			
		||||
fn greet(name) {
 | 
			
		||||
    return `Hello, ${name}! Welcome to our website.`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a product display with basic formatting
 | 
			
		||||
fn format_product(name, price, discount) {
 | 
			
		||||
    // Use the calculate_price function from utils
 | 
			
		||||
    let final_price = calculate_price(price, discount);
 | 
			
		||||
    return `${name} - Price: $${price}, Discounted: $${final_price}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a list of items as HTML
 | 
			
		||||
fn format_list_items(items) {
 | 
			
		||||
    let result = "<ul>";
 | 
			
		||||
    for item in items {
 | 
			
		||||
        result += `<li>${item}</li>`;
 | 
			
		||||
    }
 | 
			
		||||
    result += "</ul>";
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Import utility functions
 | 
			
		||||
import "utils" as utils;
 | 
			
		||||
 | 
			
		||||
// Helper function that calls the utils function
 | 
			
		||||
fn calculate_price(price, discount_percent) {
 | 
			
		||||
    return utils::calculate_price(price, discount_percent);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								tera_factory/examples/hot_reload/initial_utils.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tera_factory/examples/hot_reload/initial_utils.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
// Utility functions for Tera templates - INITIAL VERSION
 | 
			
		||||
 | 
			
		||||
// Format a date string
 | 
			
		||||
fn format_date(date_str) {
 | 
			
		||||
    let parts = date_str.split("-");
 | 
			
		||||
    let year = parts[0];
 | 
			
		||||
    let month = parts[1];
 | 
			
		||||
    let day = parts[2];
 | 
			
		||||
    
 | 
			
		||||
    return `${month}/${day}/${year}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Calculate a price with discount
 | 
			
		||||
fn calculate_price(price, discount_percent) {
 | 
			
		||||
    let discount = price * (discount_percent / 100.0);
 | 
			
		||||
    return price - discount;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a currency value
 | 
			
		||||
fn format_currency(amount) {
 | 
			
		||||
    return "$" + amount;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										194
									
								
								tera_factory/examples/hot_reload/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								tera_factory/examples/hot_reload/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,194 @@
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::fs::{self, File};
 | 
			
		||||
use std::io::Write;
 | 
			
		||||
use std::thread;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use std::error::Error;
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use tera::{Context};
 | 
			
		||||
use rhai_system::{create_hot_reloadable_system};
 | 
			
		||||
use tera_factory::TeraFactory;
 | 
			
		||||
 | 
			
		||||
/// Function to modify a script file with content from another file
 | 
			
		||||
fn modify_script(target_path: &PathBuf, source_path: &PathBuf, delay_secs: u64, message: &str) {
 | 
			
		||||
    println!("\n🔄 {}", message);
 | 
			
		||||
    
 | 
			
		||||
    // Read the source script content
 | 
			
		||||
    let source_content = fs::read_to_string(&source_path)
 | 
			
		||||
        .expect(&format!("Failed to read source script file: {}", source_path.display()));
 | 
			
		||||
    
 | 
			
		||||
    // Write the content to the target file
 | 
			
		||||
    let mut file = File::create(target_path)
 | 
			
		||||
        .expect("Failed to open target script file for writing");
 | 
			
		||||
    file.write_all(source_content.as_bytes())
 | 
			
		||||
        .expect("Failed to write to target script file");
 | 
			
		||||
    
 | 
			
		||||
    println!("✅ Script modified successfully!");
 | 
			
		||||
    
 | 
			
		||||
    // Wait before the next modification if delay is specified
 | 
			
		||||
    if delay_secs > 0 {
 | 
			
		||||
        thread::sleep(Duration::from_secs(delay_secs));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    // Set up the script paths
 | 
			
		||||
    let script_dir = PathBuf::from("examples/hot_reload/scripts");
 | 
			
		||||
    
 | 
			
		||||
    // Main script paths
 | 
			
		||||
    let main_script_path = script_dir.join("main.rhai");
 | 
			
		||||
    let utils_script_path = script_dir.join("utils.rhai");
 | 
			
		||||
    
 | 
			
		||||
    // Initial script paths
 | 
			
		||||
    let initial_main_path = script_dir.join("initial_main.rhai");
 | 
			
		||||
    let initial_utils_path = script_dir.join("initial_utils.rhai");
 | 
			
		||||
    
 | 
			
		||||
    // Modified script paths
 | 
			
		||||
    let modified_main_path = script_dir.join("modified_main.rhai");
 | 
			
		||||
    let modified_utils_path = script_dir.join("modified_utils.rhai");
 | 
			
		||||
    
 | 
			
		||||
    println!("Main script path: {:?}", main_script_path);
 | 
			
		||||
    println!("Utils script path: {:?}", utils_script_path);
 | 
			
		||||
    
 | 
			
		||||
    // Initialize main.rhai with the content from initial_main.rhai
 | 
			
		||||
    let initial_main_content = fs::read_to_string(&initial_main_path)
 | 
			
		||||
        .expect("Failed to read initial main script file");
 | 
			
		||||
    
 | 
			
		||||
    let mut file = File::create(&main_script_path)
 | 
			
		||||
        .expect("Failed to open main script file for writing");
 | 
			
		||||
    file.write_all(initial_main_content.as_bytes())
 | 
			
		||||
        .expect("Failed to write to main script file");
 | 
			
		||||
    
 | 
			
		||||
    // Initialize utils.rhai with the content from initial_utils.rhai
 | 
			
		||||
    let initial_utils_content = fs::read_to_string(&initial_utils_path)
 | 
			
		||||
        .expect("Failed to read initial utils script file");
 | 
			
		||||
    
 | 
			
		||||
    let mut file = File::create(&utils_script_path)
 | 
			
		||||
        .expect("Failed to open utils script file for writing");
 | 
			
		||||
    file.write_all(initial_utils_content.as_bytes())
 | 
			
		||||
        .expect("Failed to write to utils script file");
 | 
			
		||||
    
 | 
			
		||||
    // Set up the template path
 | 
			
		||||
    let template_dir = PathBuf::from("examples/hot_reload/templates");
 | 
			
		||||
    let template_path = template_dir.join("index.html.tera");
 | 
			
		||||
    
 | 
			
		||||
    println!("Template path: {:?}", template_path);
 | 
			
		||||
    println!("Template exists: {}", template_path.exists());
 | 
			
		||||
    
 | 
			
		||||
    // Create a hot reloadable Rhai system
 | 
			
		||||
    let script_paths = vec![main_script_path.clone(), utils_script_path.clone()];
 | 
			
		||||
    
 | 
			
		||||
    // Use the first script as the main script
 | 
			
		||||
    let main_script_index = Some(0);
 | 
			
		||||
    println!("Using main script index: {}", main_script_index.unwrap());
 | 
			
		||||
    
 | 
			
		||||
    let system = Arc::new(create_hot_reloadable_system(&script_paths, main_script_index)?);
 | 
			
		||||
 | 
			
		||||
    // Create a TeraFactory instance
 | 
			
		||||
    let factory = TeraFactory::new();
 | 
			
		||||
    
 | 
			
		||||
    // Create a Tera instance with the hot reloadable Rhai system
 | 
			
		||||
    println!("Creating Tera with template directory: {}", template_dir.display());
 | 
			
		||||
    let mut tera = factory.create_tera_with_hot_rhai(
 | 
			
		||||
        &[template_dir.to_str().unwrap()], 
 | 
			
		||||
        Arc::clone(&system)
 | 
			
		||||
    )?;
 | 
			
		||||
    
 | 
			
		||||
    // Manually add the template to Tera
 | 
			
		||||
    println!("Manually adding template: {}", template_path.display());
 | 
			
		||||
    let template_content = fs::read_to_string(&template_path)?;
 | 
			
		||||
    tera.add_raw_template("index.html.tera", &template_content)?;
 | 
			
		||||
    
 | 
			
		||||
    // List available templates
 | 
			
		||||
    println!("Available templates: {:?}", tera.get_template_names().collect::<Vec<_>>());
 | 
			
		||||
    
 | 
			
		||||
    // Create a thread to modify the scripts after a delay
 | 
			
		||||
    let main_script_path_clone = main_script_path.clone();
 | 
			
		||||
    let utils_script_path_clone = utils_script_path.clone();
 | 
			
		||||
    let modified_main_path_clone = modified_main_path.clone();
 | 
			
		||||
    let modified_utils_path_clone = modified_utils_path.clone();
 | 
			
		||||
    
 | 
			
		||||
    let _modification_thread = thread::spawn(move || {
 | 
			
		||||
        // Modify the main script after 5 seconds
 | 
			
		||||
        modify_script(
 | 
			
		||||
            &main_script_path_clone,
 | 
			
		||||
            &modified_main_path_clone,
 | 
			
		||||
            5,
 | 
			
		||||
            "Modifying the main script with updated functions..."
 | 
			
		||||
        );
 | 
			
		||||
        
 | 
			
		||||
        // Modify the utils script after another 5 seconds
 | 
			
		||||
        modify_script(
 | 
			
		||||
            &utils_script_path_clone,
 | 
			
		||||
            &modified_utils_path_clone,
 | 
			
		||||
            5,
 | 
			
		||||
            "Modifying the utils script with updated functions..."
 | 
			
		||||
        );
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    // Main rendering loop
 | 
			
		||||
    for i in 1..30 {
 | 
			
		||||
        // Create a context with some data
 | 
			
		||||
        let mut context = Context::new();
 | 
			
		||||
        context.insert("name", "User");
 | 
			
		||||
        context.insert("current_date", "2025-05-02");
 | 
			
		||||
        context.insert("current_time", &format!("{:02}:{:02}:{:02}", 
 | 
			
		||||
            (12 + i % 12) % 12, i % 60, i % 60));
 | 
			
		||||
        
 | 
			
		||||
        // Add some products
 | 
			
		||||
        let products = vec![
 | 
			
		||||
            tera::to_value(serde_json::json!({
 | 
			
		||||
                "name": "Deluxe Widget",
 | 
			
		||||
                "price": 99.99,
 | 
			
		||||
                "discount": 15.0
 | 
			
		||||
            })).unwrap(),
 | 
			
		||||
            tera::to_value(serde_json::json!({
 | 
			
		||||
                "name": "Premium Gadget",
 | 
			
		||||
                "price": 149.95,
 | 
			
		||||
                "discount": 20.0
 | 
			
		||||
            })).unwrap(),
 | 
			
		||||
            tera::to_value(serde_json::json!({
 | 
			
		||||
                "name": "Basic Tool",
 | 
			
		||||
                "price": 29.99,
 | 
			
		||||
                "discount": 5.0
 | 
			
		||||
            })).unwrap(),
 | 
			
		||||
        ];
 | 
			
		||||
        context.insert("products", &products);
 | 
			
		||||
        
 | 
			
		||||
        // Add some categories
 | 
			
		||||
        let categories = vec![
 | 
			
		||||
            "Electronics", "Home & Garden", "Clothing", "Sports"
 | 
			
		||||
        ];
 | 
			
		||||
        context.insert("categories", &categories);
 | 
			
		||||
        
 | 
			
		||||
        // Render the template
 | 
			
		||||
        match tera.render("index.html.tera", &context) {
 | 
			
		||||
            Ok(result) => {
 | 
			
		||||
                // Print the first 500 characters of the result to avoid flooding the console
 | 
			
		||||
                let preview = if result.len() > 500 {
 | 
			
		||||
                    format!("{}... (truncated)", &result[..500])
 | 
			
		||||
                } else {
 | 
			
		||||
                    result.clone()
 | 
			
		||||
                };
 | 
			
		||||
                
 | 
			
		||||
                println!("\n--- Render #{} ---\n{}", i, preview);
 | 
			
		||||
            },
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                println!("Error rendering template: {}", e);
 | 
			
		||||
                // Print more detailed error information
 | 
			
		||||
                if let Some(source) = e.source() {
 | 
			
		||||
                    println!("Error source: {}", source);
 | 
			
		||||
                    if let Some(next_source) = source.source() {
 | 
			
		||||
                        println!("Next error source: {}", next_source);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Wait for a second before rendering again
 | 
			
		||||
        thread::sleep(Duration::from_secs(1));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								tera_factory/examples/hot_reload/scripts/initial_main.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								tera_factory/examples/hot_reload/scripts/initial_main.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
// Main functions for Tera templates - INITIAL VERSION
 | 
			
		||||
 | 
			
		||||
// Format a greeting with the current version
 | 
			
		||||
fn greet(name) {
 | 
			
		||||
    return `Hello, ${name}! Welcome to our website.`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a product display with basic formatting
 | 
			
		||||
fn format_product(name, price, discount) {
 | 
			
		||||
    // Use the calculate_price function from utils
 | 
			
		||||
    let final_price = calculate_price(price, discount);
 | 
			
		||||
    return `${name} - Price: $${price}, Discounted: $${final_price}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a list of items as HTML
 | 
			
		||||
fn format_list_items(items) {
 | 
			
		||||
    let result = "<ul>";
 | 
			
		||||
    for item in items {
 | 
			
		||||
        result += `<li>${item}</li>`;
 | 
			
		||||
    }
 | 
			
		||||
    result += "</ul>";
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Import utility functions
 | 
			
		||||
import "utils" as utils;
 | 
			
		||||
 | 
			
		||||
// Helper function that calls the utils function
 | 
			
		||||
fn calculate_price(price, discount_percent) {
 | 
			
		||||
    return utils::calculate_price(price, discount_percent);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								tera_factory/examples/hot_reload/scripts/initial_utils.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tera_factory/examples/hot_reload/scripts/initial_utils.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
// Utility functions for Tera templates - INITIAL VERSION
 | 
			
		||||
 | 
			
		||||
// Format a date string
 | 
			
		||||
fn format_date(date_str) {
 | 
			
		||||
    let parts = date_str.split("-");
 | 
			
		||||
    let year = parts[0];
 | 
			
		||||
    let month = parts[1];
 | 
			
		||||
    let day = parts[2];
 | 
			
		||||
    
 | 
			
		||||
    return `${month}/${day}/${year}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Calculate a price with discount
 | 
			
		||||
fn calculate_price(price, discount_percent) {
 | 
			
		||||
    let discount = price * (discount_percent / 100.0);
 | 
			
		||||
    return price - discount;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a currency value
 | 
			
		||||
fn format_currency(amount) {
 | 
			
		||||
    return "$" + amount;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								tera_factory/examples/hot_reload/scripts/main.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								tera_factory/examples/hot_reload/scripts/main.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
// Main functions for Tera templates - UPDATED VERSION
 | 
			
		||||
 | 
			
		||||
// Format a greeting with the current version
 | 
			
		||||
fn greet(name) {
 | 
			
		||||
    return `Hello, ${name}! Welcome to our UPDATED website. (Version 2.0)`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a product display with enhanced formatting
 | 
			
		||||
fn format_product(name, price, discount) {
 | 
			
		||||
    // Use the calculate_price function from utils
 | 
			
		||||
    let final_price = calculate_price(price, discount);
 | 
			
		||||
    let savings = price - final_price;
 | 
			
		||||
    return `${name} - Original: $${price}, Now: $${final_price} (Save $${savings})`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a list of items as HTML with CSS classes
 | 
			
		||||
fn format_list_items(items) {
 | 
			
		||||
    let result = "<ul class='category-list'>";
 | 
			
		||||
    for item in items {
 | 
			
		||||
        result += `<li class='category-item'>${item}</li>`;
 | 
			
		||||
    }
 | 
			
		||||
    result += "</ul>";
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Import utility functions
 | 
			
		||||
import "utils" as utils;
 | 
			
		||||
 | 
			
		||||
// Helper function that calls the utils function
 | 
			
		||||
fn calculate_price(price, discount_percent) {
 | 
			
		||||
    return utils::calculate_price(price, discount_percent);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								tera_factory/examples/hot_reload/scripts/modified_main.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								tera_factory/examples/hot_reload/scripts/modified_main.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
// Main functions for Tera templates - UPDATED VERSION
 | 
			
		||||
 | 
			
		||||
// Format a greeting with the current version
 | 
			
		||||
fn greet(name) {
 | 
			
		||||
    return `Hello, ${name}! Welcome to our UPDATED website. (Version 2.0)`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a product display with enhanced formatting
 | 
			
		||||
fn format_product(name, price, discount) {
 | 
			
		||||
    // Use the calculate_price function from utils
 | 
			
		||||
    let final_price = calculate_price(price, discount);
 | 
			
		||||
    let savings = price - final_price;
 | 
			
		||||
    return `${name} - Original: $${price}, Now: $${final_price} (Save $${savings})`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a list of items as HTML with CSS classes
 | 
			
		||||
fn format_list_items(items) {
 | 
			
		||||
    let result = "<ul class='category-list'>";
 | 
			
		||||
    for item in items {
 | 
			
		||||
        result += `<li class='category-item'>${item}</li>`;
 | 
			
		||||
    }
 | 
			
		||||
    result += "</ul>";
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Import utility functions
 | 
			
		||||
import "utils" as utils;
 | 
			
		||||
 | 
			
		||||
// Helper function that calls the utils function
 | 
			
		||||
fn calculate_price(price, discount_percent) {
 | 
			
		||||
    return utils::calculate_price(price, discount_percent);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								tera_factory/examples/hot_reload/scripts/modified_utils.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								tera_factory/examples/hot_reload/scripts/modified_utils.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
// Utility functions for Tera templates - UPDATED VERSION
 | 
			
		||||
 | 
			
		||||
// Format a date string with improved formatting
 | 
			
		||||
fn format_date(date_str) {
 | 
			
		||||
    let parts = date_str.split("-");
 | 
			
		||||
    let year = parts[0];
 | 
			
		||||
    let month = parts[1];
 | 
			
		||||
    let day = parts[2];
 | 
			
		||||
    
 | 
			
		||||
    // Add month names for better readability
 | 
			
		||||
    let month_names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
 | 
			
		||||
    let month_idx = month.parse_int() - 1;
 | 
			
		||||
    let month_name = month_names[month_idx];
 | 
			
		||||
    
 | 
			
		||||
    return `${month_name} ${day}, ${year}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Calculate a price with discount - enhanced with minimum discount
 | 
			
		||||
fn calculate_price(price, discount_percent) {
 | 
			
		||||
    let discount = price * (discount_percent / 100.0);
 | 
			
		||||
    return price - discount;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a currency value with improved formatting
 | 
			
		||||
fn format_currency(amount) {
 | 
			
		||||
    return "$" + amount;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								tera_factory/examples/hot_reload/scripts/utils.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								tera_factory/examples/hot_reload/scripts/utils.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
// Utility functions for Tera templates - UPDATED VERSION
 | 
			
		||||
 | 
			
		||||
// Format a date string with improved formatting
 | 
			
		||||
fn format_date(date_str) {
 | 
			
		||||
    let parts = date_str.split("-");
 | 
			
		||||
    let year = parts[0];
 | 
			
		||||
    let month = parts[1];
 | 
			
		||||
    let day = parts[2];
 | 
			
		||||
    
 | 
			
		||||
    // Add month names for better readability
 | 
			
		||||
    let month_names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
 | 
			
		||||
    let month_idx = month.parse_int() - 1;
 | 
			
		||||
    let month_name = month_names[month_idx];
 | 
			
		||||
    
 | 
			
		||||
    return `${month_name} ${day}, ${year}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Calculate a price with discount - enhanced with minimum discount
 | 
			
		||||
fn calculate_price(price, discount_percent) {
 | 
			
		||||
    let discount = price * (discount_percent / 100.0);
 | 
			
		||||
    return price - discount;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format a currency value with improved formatting
 | 
			
		||||
fn format_currency(amount) {
 | 
			
		||||
    return "$" + amount;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								tera_factory/examples/hot_reload/templates/index.html.tera
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								tera_factory/examples/hot_reload/templates/index.html.tera
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>Rhai + Tera Hot Reload Example</title>
 | 
			
		||||
    <style>
 | 
			
		||||
        body {
 | 
			
		||||
            font-family: Arial, sans-serif;
 | 
			
		||||
            max-width: 800px;
 | 
			
		||||
            margin: 0 auto;
 | 
			
		||||
            padding: 20px;
 | 
			
		||||
            line-height: 1.6;
 | 
			
		||||
        }
 | 
			
		||||
        .product {
 | 
			
		||||
            border: 1px solid #ddd;
 | 
			
		||||
            padding: 15px;
 | 
			
		||||
            margin-bottom: 15px;
 | 
			
		||||
            border-radius: 5px;
 | 
			
		||||
        }
 | 
			
		||||
        .date {
 | 
			
		||||
            color: #666;
 | 
			
		||||
            font-style: italic;
 | 
			
		||||
        }
 | 
			
		||||
        .greeting {
 | 
			
		||||
            font-size: 24px;
 | 
			
		||||
            margin-bottom: 20px;
 | 
			
		||||
            color: #333;
 | 
			
		||||
        }
 | 
			
		||||
        .reload-time {
 | 
			
		||||
            background-color: #f8f8f8;
 | 
			
		||||
            padding: 10px;
 | 
			
		||||
            border-radius: 5px;
 | 
			
		||||
            margin-top: 30px;
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <div class="greeting">{{ greet(name=name) }}</div>
 | 
			
		||||
    
 | 
			
		||||
    <p>Today's date: <span class="date">{{ format_date(date_str=current_date) }}</span></p>
 | 
			
		||||
    
 | 
			
		||||
    <h2>Featured Products</h2>
 | 
			
		||||
    {% for product in products %}
 | 
			
		||||
        <div class="product">
 | 
			
		||||
            {{ format_product(name=product.name, price=product.price, discount=product.discount) }}
 | 
			
		||||
        </div>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
    
 | 
			
		||||
    <h2>Categories</h2>
 | 
			
		||||
    {{ format_list_items(items=categories) | safe }}
 | 
			
		||||
    
 | 
			
		||||
    <div class="reload-time">
 | 
			
		||||
        Page generated at: {{ current_time }}
 | 
			
		||||
    </div>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
		Reference in New Issue
	
	Block a user