tron 2.1.0

A rust based template system built for speed and simplicity.
Documentation
//! Compile-time Template Macros Example
//!
//! This example demonstrates how to use Tron's compile-time macros for
//! efficient template generation with zero runtime overhead.

use tron::{template, template_ref, assemble_templates, generate_code, template_builder};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("=== Tron Compile-time Template Macros Example ===\n");

    // 1. Basic template macro
    demonstrate_template_macro()?;
    
    // 2. Template reference with dependencies
    demonstrate_template_ref_macro()?;
    
    // 3. Template assembly macro
    demonstrate_assemble_templates_macro()?;
    
    // 4. Code generation macro
    demonstrate_generate_code_macro();
    
    // 5. Template builder macro
    demonstrate_template_builder_macro()?;
    
    // 6. Performance comparison
    demonstrate_compile_time_benefits()?;

    Ok(())
}

fn demonstrate_template_macro() -> Result<(), Box<dyn std::error::Error>> {
    println!("1. Basic Template Macro:");
    
    // Create template from string literal at compile time
    let mut function_template = template!("fn @[name]@(@[params]@) -> @[return_type]@ {\n    @[body]@\n}");
    
    println!("   - Template created at compile time");
    println!("   - Placeholders: {:?}", function_template.placeholder_names());
    
    // Fill in the template
    function_template.set("name", "calculate_area")?;
    function_template.set("params", "width: f64, height: f64")?;
    function_template.set("return_type", "f64")?;
    function_template.set("body", "width * height")?;
    
    let result = function_template.render()?;
    println!("   - Generated function:");
    for (i, line) in result.lines().enumerate() {
        println!("     {:2}: {}", i + 1, line);
    }
    
    println!();
    Ok(())
}

fn demonstrate_template_ref_macro() -> Result<(), Box<dyn std::error::Error>> {
    println!("2. Template Reference with Dependencies:");
    
    // Create template reference with dependencies at compile time
    let mut api_template = template_ref!(
        r#"use serde::{Deserialize, Serialize};
use axum::Json;

#[derive(Debug, Serialize, Deserialize)]
pub struct @[request_type]@ {
    @[request_fields]@
}

#[derive(Debug, Serialize, Deserialize)]
pub struct @[response_type]@ {
    @[response_fields]@
}

pub async fn @[handler_name]@(Json(req): Json<@[request_type]@>) -> Json<@[response_type]@> {
    @[handler_body]@
}"#,
        dependencies = [
            "serde = { version = \"1.0\", features = [\"derive\"] }",
            "axum = \"0.7\"",
            "tokio = { version = \"1.0\", features = [\"full\"] }"
        ]
    );
    
    println!("   - Template reference created with {} dependencies", api_template.dependencies().len());
    
    // Fill in the template
    api_template.set("request_type", "CreateUserRequest")?;
    api_template.set("request_fields", "pub name: String,\n    pub email: String")?;
    api_template.set("response_type", "CreateUserResponse")?;
    api_template.set("response_fields", "pub id: u64,\n    pub message: String")?;
    api_template.set("handler_name", "create_user")?;
    api_template.set("handler_body", r#"// Implementation logic here
    Json(CreateUserResponse {
        id: 1,
        message: "User created successfully".to_string(),
    })"#)?;
    
    let result = api_template.render()?;
    println!("   - Generated API handler (first 10 lines):");
    for (i, line) in result.lines().take(10).enumerate() {
        println!("     {:2}: {}", i + 1, line);
    }
    println!("     ... (truncated)");
    
    println!();
    Ok(())
}

fn demonstrate_assemble_templates_macro() -> Result<(), Box<dyn std::error::Error>> {
    println!("3. Template Assembly Macro:");
    
    // Create assembler with multiple templates at compile time
    let mut assembler = assemble_templates![
        separator = "\n\n",
        "//! @[module_title]@\n//!\n//! @[module_description]@\n",
        "use std::collections::HashMap;\nuse serde::{Deserialize, Serialize};\n",
        "#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct @[struct_name]@ {\n    @[struct_fields]@\n}",
        "impl @[struct_name]@ {\n    pub fn new(@[constructor_params]@) -> Self {\n        @[constructor_body]@\n    }\n\n    @[additional_methods]@\n}"
    ];
    
    println!("   - Assembler created with {} templates", assembler.len());
    println!("   - Custom separator: \"\\n\\n\"");
    
    // Set global placeholders
    assembler.set_global("module_title", "User Management Module")?;
    assembler.set_global("module_description", "Handles user creation, updates, and queries.")?;
    assembler.set_global("struct_name", "User")?;
    assembler.set_global("struct_fields", "pub id: u64,\n    pub name: String,\n    pub email: String,\n    pub created_at: String")?;
    assembler.set_global("constructor_params", "name: String, email: String")?;
    assembler.set_global("constructor_body", "Self {\n            id: 0,\n            name,\n            email,\n            created_at: chrono::Utc::now().to_rfc3339(),\n        }")?;
    assembler.set_global("additional_methods", "pub fn update_email(&mut self, email: String) {\n        self.email = email;\n    }")?;
    
    let result = assembler.render_all()?;
    println!("   - Generated module (first 15 lines):");
    for (i, line) in result.lines().take(15).enumerate() {
        println!("     {:2}: {}", i + 1, line);
    }
    println!("     ... (truncated)");
    
    println!();
    Ok(())
}

fn demonstrate_generate_code_macro() {
    println!("4. Code Generation Macro:");
    
    // Generate code directly at compile time
    let constant_code = generate_code!(
        template = "pub const @[name]@: @[type]@ = @[value]@;",
        placeholders = {
            "name" => "MAX_CONNECTIONS",
            "type" => "usize",
            "value" => "100"
        }
    );
    
    let enum_code = generate_code!(
        template = r#"#[derive(@[derives]@)]
pub enum @[enum_name]@ {
    @[variants]@
}"#,
        placeholders = {
            "derives" => "Debug, Clone, PartialEq",
            "enum_name" => "ConnectionStatus",
            "variants" => "Connected,\n    Disconnected,\n    Reconnecting,\n    Failed(String)"
        }
    );
    
    println!("   - Generated constant:");
    println!("     {}", constant_code);
    
    println!("   - Generated enum:");
    for (i, line) in enum_code.lines().enumerate() {
        println!("     {:2}: {}", i + 1, line);
    }
    
    println!();
}

fn demonstrate_template_builder_macro() -> Result<(), Box<dyn std::error::Error>> {
    println!("5. Template Builder Macro:");
    
    // Create template builder with predefined values
    let builder = template_builder!(
        template = r#"#[derive(Debug)]
pub struct @[name]@ {
    @[fields]@
}

impl @[name]@ {
    pub fn @[getter_name]@(&self) -> &@[field_type]@ {
        &self.@[field_name]@
    }
}"#,
        values = {
            "name" => "Config",
            "fields" => "pub database_url: String,\n    pub port: u16,\n    pub debug: bool"
        }
    );
    
    let template = builder
        .set("getter_name", "database_url")
        .set("field_type", "str")
        .set("field_name", "database_url")
        .build()?;
    
    let result = template.render()?;
    println!("   - Generated struct with getter:");
    for (i, line) in result.lines().enumerate() {
        println!("     {:2}: {}", i + 1, line);
    }
    
    println!();
    Ok(())
}

fn demonstrate_compile_time_benefits() -> Result<(), Box<dyn std::error::Error>> {
    println!("6. Compile-time Benefits:");
    
    // Show that templates are parsed and validated at compile time
    println!("   - All template syntax validated at compile time ✓");
    println!("   - Zero runtime parsing overhead ✓");
    println!("   - Template errors caught during compilation ✓");
    println!("   - Optimal performance for frequently used templates ✓");
    
    // Demonstrate that compile-time templates behave identically to runtime ones
    let compile_time = template!("Hello @[name]@!");
    let mut runtime = tron::TronTemplate::new("Hello @[name]@!")?;
    
    let mut compile_time_clone = compile_time.clone();
    compile_time_clone.set("name", "Compile-time")?;
    runtime.set("name", "Runtime")?;
    
    println!("   - Compile-time result: {}", compile_time_clone.render()?);
    println!("   - Runtime result: {}", runtime.render()?);
    println!("   - Same API, different performance characteristics!");
    
    println!();
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use tron::TronTemplate;

    #[test]
    fn test_compile_time_macros_functionality() -> Result<(), Box<dyn std::error::Error>> {
        // Test that compile-time templates work identically to runtime ones
        let compile_time = template!("Test @[value]@");
        let runtime = TronTemplate::new("Test @[value]@")?;
        
        assert_eq!(compile_time.placeholder_names(), runtime.placeholder_names());
        assert_eq!(compile_time.content(), runtime.content());
        
        Ok(())
    }
    
    #[test]
    fn test_template_ref_macro_with_deps() {
        let template_ref = template_ref!(
            "use @[crate]@; fn @[name]@() {}",
            dependencies = ["tokio = \"1.0\""]
        );
        
        assert_eq!(template_ref.dependencies().len(), 1);
        assert!(template_ref.dependencies()[0].contains("tokio"));
    }
    
    #[test]
    fn test_code_generation_macro() {
        let code = generate_code!(
            template = "const @[name]@: @[type]@ = @[value]@;",
            placeholders = {
                "name" => "TEST_CONST",
                "type" => "i32", 
                "value" => "42"
            }
        );
        
        assert_eq!(code, "const TEST_CONST: i32 = 42;");
    }
}