tron 2.1.0

A rust based template system built for speed and simplicity.
Documentation
//! Advanced features example showing complex real-world scenarios.
//!
//! This example demonstrates advanced usage patterns including file loading,
//! complex composition, error handling, and integration with build systems.

use tron::{TronTemplate, TronRef, TronAssembler, TronTemplateBuilder, TronRefBuilder, Result};
use tron::validation::TemplateValidator;
use std::collections::HashMap;

fn main() -> Result<()> {
    println!("=== Tron Advanced Features Example ===\n");

    // Example 1: REST API code generation
    example_rest_api_generation()?;
    
    // Example 2: Database model generation
    example_database_models()?;
    
    // Example 3: Configuration-driven generation
    example_configuration_driven()?;
    
    // Example 4: Template validation in build process
    example_build_integration()?;

    Ok(())
}

fn example_rest_api_generation() -> Result<()> {
    println!("1. REST API Code Generation:");
    
    // Define API endpoint data
    struct ApiEndpoint {
        name: String,
        method: String,
        path: String,
        request_type: Option<String>,
        response_type: String,
        description: String,
    }
    
    let endpoints = vec![
        ApiEndpoint {
            name: "get_users".to_string(),
            method: "GET".to_string(),
            path: "/api/users".to_string(),
            request_type: None,
            response_type: "Vec<User>".to_string(),
            description: "Retrieve all users".to_string(),
        },
        ApiEndpoint {
            name: "create_user".to_string(),
            method: "POST".to_string(),
            path: "/api/users".to_string(),
            request_type: Some("CreateUserRequest".to_string()),
            response_type: "User".to_string(),
            description: "Create a new user".to_string(),
        },
        ApiEndpoint {
            name: "get_user".to_string(),
            method: "GET".to_string(),
            path: "/api/users/{id}".to_string(),
            request_type: None,
            response_type: "User".to_string(),
            description: "Retrieve a specific user".to_string(),
        },
    ];
    
    let mut assembler = TronAssembler::new();
    
    // Add module header
    let header = TronTemplate::new(r#"//! Auto-generated REST API handlers
//! 
//! This module contains handlers for all REST API endpoints.
//! Generated by Tron Template Engine.

use axum::{
    extract::{Path, Json},
    response::Json as ResponseJson,
    http::StatusCode,
};
use serde::{Deserialize, Serialize};

"#)?;
    assembler.add_template(TronRef::new(header));
    
    // Generate handlers for each endpoint
    for endpoint in endpoints {
        let handler_template = if endpoint.request_type.is_some() {
            TronTemplate::new(r#"/// @[description]@
pub async fn @[handler_name]@(Json(payload): Json<@[request_type]@>) -> Result<ResponseJson<@[response_type]@>, StatusCode> {
    // TODO: Implement @[method]@ @[path]@
    todo!("Implement handler for @[handler_name]@")
}
"#)?
        } else {
            TronTemplate::new(r#"/// @[description]@
pub async fn @[handler_name]@() -> Result<ResponseJson<@[response_type]@>, StatusCode> {
    // TODO: Implement @[method]@ @[path]@
    todo!("Implement handler for @[handler_name]@")
}
"#)?
        };
        
        let mut handler_ref = TronRef::new(handler_template);
        handler_ref.set("description", &endpoint.description)?;
        handler_ref.set("handler_name", &endpoint.name)?;
        handler_ref.set("method", &endpoint.method)?;
        handler_ref.set("path", &endpoint.path)?;
        handler_ref.set("response_type", &endpoint.response_type)?;
        
        if let Some(ref req_type) = endpoint.request_type {
            handler_ref.set("request_type", req_type)?;
        }
        
        assembler.add_template(handler_ref);
    }
    
    // Add routing setup
    let router_template = TronTemplate::new(r#"/// Setup API routes
pub fn create_router() -> axum::Router {
    axum::Router::new()
        .route("/api/users", axum::routing::get(get_users).post(create_user))
        .route("/api/users/:id", axum::routing::get(get_user))
}
"#)?;
    assembler.add_template(TronRef::new(router_template));
    
    let result = assembler.render_all()?;
    println!("{}", result);
    
    Ok(())
}

fn example_database_models() -> Result<()> {
    println!("2. Database Model Generation:");
    
    // Define table schema
    struct Column {
        name: String,
        rust_type: String,
        sql_type: String,
        nullable: bool,
        primary_key: bool,
    }
    
    struct Table {
        name: String,
        columns: Vec<Column>,
    }
    
    let table = Table {
        name: "users".to_string(),
        columns: vec![
            Column {
                name: "id".to_string(),
                rust_type: "i32".to_string(),
                sql_type: "INTEGER".to_string(),
                nullable: false,
                primary_key: true,
            },
            Column {
                name: "email".to_string(),
                rust_type: "String".to_string(),
                sql_type: "VARCHAR(255)".to_string(),
                nullable: false,
                primary_key: false,
            },
            Column {
                name: "name".to_string(),
                rust_type: "String".to_string(),
                sql_type: "VARCHAR(100)".to_string(),
                nullable: false,
                primary_key: false,
            },
            Column {
                name: "created_at".to_string(),
                rust_type: "chrono::DateTime<chrono::Utc>".to_string(),
                sql_type: "TIMESTAMP".to_string(),
                nullable: false,
                primary_key: false,
            },
        ],
    };
    
    // Generate Rust struct
    let struct_fields = table.columns
        .iter()
        .map(|col| {
            let field_type = if col.nullable && !col.primary_key {
                format!("Option<{}>", col.rust_type)
            } else {
                col.rust_type.clone()
            };
            format!("    pub {}: {},", col.name, field_type)
        })
        .collect::<Vec<_>>()
        .join("\n");
    
    let model = TronRefBuilder::new()
        .content(r#"use serde::{Deserialize, Serialize};
use sqlx::FromRow;

#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct @[struct_name]@ {
@[fields]@
}

impl @[struct_name]@ {
    pub async fn find_by_id(pool: &sqlx::PgPool, id: i32) -> Result<Option<Self>, sqlx::Error> {
        sqlx::query_as!(
            Self,
            "SELECT * FROM @[table_name]@ WHERE id = $1",
            id
        )
        .fetch_optional(pool)
        .await
    }
    
    pub async fn create(pool: &sqlx::PgPool, @[create_params]@) -> Result<Self, sqlx::Error> {
        sqlx::query_as!(
            Self,
            "@[insert_query]@",
            @[insert_params]@
        )
        .fetch_one(pool)
        .await
    }
}
"#)
        .dependencies(&[
            "serde = { version = \"1.0\", features = [\"derive\"] }",
            "sqlx = { version = \"0.7\", features = [\"postgres\", \"chrono\", \"uuid\"] }",
            "chrono = { version = \"0.4\", features = [\"serde\"] }"
        ])
        .set("struct_name", "User")
        .set("table_name", &table.name)
        .set("fields", &struct_fields)
        .set("create_params", "email: String, name: String")
        .set("insert_query", "INSERT INTO users (email, name, created_at) VALUES ($1, $2, NOW()) RETURNING *")
        .set("insert_params", "email, name")
        .build()?;
    
    let result = model.render()?;
    println!("{}", result);
    println!("Dependencies: {:?}", model.dependencies());
    
    Ok(())
}

fn example_configuration_driven() -> Result<()> {
    println!("3. Configuration-Driven Generation:");
    
    // Simulate loading configuration (would typically come from JSON/YAML)
    let config = HashMap::from([
        ("project_name", "MyAwesomeApp"),
        ("version", "1.0.0"),
        ("author", "Tron User"),
        ("description", "An awesome application built with Tron templates"),
        ("license", "MIT"),
        ("rust_version", "1.70"),
    ]);
    
    // Generate Cargo.toml
    let cargo_toml = TronTemplateBuilder::new()
        .content(r#"[package]
name = "@[project_name]@"
version = "@[version]@"
edition = "2021"
rust-version = "@[rust_version]@"
authors = ["@[author]@"]
description = "@[description]@"
license = "@[license]@"
repository = "https://github.com/@[author]@/@[project_name]@"

[dependencies]
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
clap = { version = "4.0", features = ["derive"] }

[dev-dependencies]
tokio-test = "0.4"
"#)
        .set_many(config.clone())
        .build()?;
    
    println!("Generated Cargo.toml:");
    println!("{}", cargo_toml.render()?);
    
    // Generate README.md
    let readme = TronTemplateBuilder::new()
        .content(r#"# @[project_name]@

@[description]@

## Installation

```bash
cargo install @[project_name]@
```

## Usage

```rust
use @[project_name]@::*;

fn main() {
    println!("Hello from @[project_name]@ v@[version]@!");
}
```

## License

This project is licensed under the @[license]@ License - see the [LICENSE](LICENSE) file for details.

## Author

@[author]@
"#)
        .set_many(config)
        .build()?;
    
    println!("Generated README.md:");
    println!("{}", readme.render()?);
    
    Ok(())
}

fn example_build_integration() -> Result<()> {
    println!("4. Template Validation in Build Process:");
    
    // Simulate templates that might be used in a build script
    let templates = vec![
        ("config.tpl", "const @[constant_name]@: &str = \"@[value]@\";"),
        ("api.tpl", "fn @[function_name]@(@[params]@) -> @[return_type]@ { @[body]@ }"),
        ("bad.tpl", "fn @[x]@() { @[a]@; }"), // This will have validation issues
    ];
    
    let validator = TemplateValidator::new();
    let mut all_valid = true;
    
    for (name, content) in templates {
        match TronTemplate::new(content) {
            Ok(template) => {
                let report = validator.validate(&template)?;
                
                if report.has_errors() {
                    println!("{} failed validation:", name);
                    for issue in report.issues() {
                        println!("   - {}", issue);
                    }
                    all_valid = false;
                } else if report.has_issues() {
                    println!("⚠️  {} has warnings:", name);
                    for issue in report.issues() {
                        println!("   - {}", issue);
                    }
                } else {
                    println!("{} passed validation", name);
                }
            }
            Err(e) => {
                println!("{} failed to parse: {}", name, e);
                all_valid = false;
            }
        }
    }
    
    if all_valid {
        println!("\n🎉 All critical validations passed! Build can continue.");
    } else {
        println!("\n💥 Some templates failed validation. Build should fail.");
    }
    
    // Example of template caching simulation
    println!("\n📦 Template Caching Simulation:");
    let cache_key = "user_struct_v1";
    println!("   Cache key: {}", cache_key);
    
    let template = TronTemplate::new(r#"#[derive(Debug)]
pub struct User {
    pub id: @[id_type]@,
    pub name: String,
}
"#)?;
    
    // In a real scenario, you'd serialize and store this
    println!("   Template cached: {} bytes", template.content().len());
    println!("   Placeholders: {:?}", template.placeholder_names());
    
    Ok(())
}