tomplate 0.1.0

TOML-based compile-time template composition for Rust
Documentation
  • Coverage
  • 100%
    1 out of 1 items documented0 out of 0 items with examples
  • Size
  • Source code size: 39.59 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 786.12 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 23s Average build duration of successful builds.
  • all releases: 23s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Cyanistic/tomplate
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • Cyanistic

Tomplate: TOML-Based Compile-Time Template Composition

Tomplate is a powerful compile-time template engine for Rust that processes templates at compile time, resulting in zero runtime overhead. Templates are defined in TOML files and can use various template engines including Handlebars, Tera, and MiniJinja.

Features

  • Zero Runtime Overhead: All template processing happens at compile time
  • Multiple Template Engines: Choose from Simple, Handlebars, Tera, or MiniJinja
  • Composition Blocks: Build complex templates from reusable parts
  • Inline Templates: Use template strings directly without registry
  • Eager Evaluation: Solve macro expansion order issues with tomplate_eager!
  • File-Based Organization: Store templates in .tomplate.toml files

Getting Started

Step 1: Add Dependencies

Add to your Cargo.toml:

[dependencies]
tomplate = "0.1"

[build-dependencies]
tomplate-build = "0.1"

# Optional: Enable additional template engines
# [dependencies.tomplate]
# version = "0.1"
# features = ["handlebars", "tera"]  # Add the engines you need

Step 2: Create a Build Script

Create build.rs in your project root:

fn main() {
    tomplate_build::Builder::new()
        .add_patterns([
            "**/*.tomplate.toml",  // Recursively find .tomplate.toml files
            "templates/*.toml",     // Also check templates directory
        ])
        .build()
        .expect("Failed to build templates");
}

Step 3: Create Template Files

Create a file like templates/queries.tomplate.toml:

# Simple variable substitution (default engine)
[user_query]
template = "SELECT {fields} FROM users WHERE {condition}"

# Using Handlebars for logic
[conditional_query]
template = """
SELECT * FROM users
{{#if status}}
WHERE status = '{{status}}'
{{/if}}
"""
engine = "handlebars"

# Template with default values
[paginated_query]
template = "SELECT * FROM {table} LIMIT {limit} OFFSET {offset}"

Step 4: Use Templates in Your Code

# use tomplate::tomplate;
# fn main() {
// Using templates from files
const USER_QUERY: &str = tomplate!("user_query",
    fields = "id, name, email",
    condition = "active = true"
);
// Result: "SELECT id, name, email FROM users WHERE active = true"

// Using inline templates (when not found in registry)
const GREETING: &str = tomplate!(
    "Hello {name}, welcome to {place}!",
    name = "Alice",
    place = "Wonderland"
);
// Result: "Hello Alice, welcome to Wonderland!"
# }

Major Features Examples

1. File-Based vs Inline Templates

use tomplate::tomplate;

// File-based: Looks for "user_query" in your .tomplate.toml files
const FROM_FILE: &str = tomplate!("user_query", 
    fields = "id, name",
    condition = "active = true"
);

// Inline: If "Hello {name}!" isn't found in files, treats it as template
const INLINE: &str = tomplate!(
    "Hello {name}!",
    name = "World"
);

// How it works:
// 1. First checks if the string matches a template name in registry
// 2. If not found, uses the string itself as an inline template

2. Nested Template Composition

# use tomplate::tomplate;
# fn main() {
// Templates can use other templates as parameters
const NESTED: &str = tomplate!("wrapper_template",
    header = tomplate!("header_template", title = "My App"),
    body = tomplate!("SELECT * FROM {table}", table = "users"),
    footer = tomplate!("footer_template", year = "2024")
);

// This enables building complex templates from simple parts
# }

3. Composition Blocks with Scoped Variables

use tomplate::tomplate;

tomplate! {
    // Local variables - reusable within the block
    let base_fields = tomplate!("id, name, email");
    let active_condition = tomplate!("status = 'active'");
    let pagination = tomplate!("LIMIT {limit} OFFSET {offset}",
        limit = "10",
        offset = "0"
    );
    
    // Export constants - available outside the block
    const GET_ACTIVE_USERS = tomplate!(
        "SELECT {fields} FROM users WHERE {condition} {page}",
        fields = base_fields,
        condition = active_condition,
        page = pagination
    );
    
    const COUNT_ACTIVE = tomplate!(
        "SELECT COUNT(*) FROM users WHERE {condition}",
        condition = active_condition
    );
    
    // Can use both file templates and inline templates
    const MIXED = tomplate!("wrapper_template",
        content = tomplate!("Inline: {value}", value = base_fields)
    );
}

// The constants are now available for use
fn main(){
    println!("{}", GET_ACTIVE_USERS);
}
// Output: "SELECT id, name, email FROM users WHERE status = 'active' LIMIT 10 OFFSET 0"

4. Multiple Template Engines

// In your .tomplate.toml file:
// [simple_template]
// template = "Hello {name}"
// engine = "simple"  # Default - basic {var} substitution
//
// [handlebars_template]
// template = "{{#if logged_in}}Welcome {{user}}{{else}}Please login{{/if}}"
// engine = "handlebars"
//
// [tera_template]
// template = "{% for item in items %}{{ item|upper }}{% endfor %}"
// engine = "tera"

// Use them the same way
const SIMPLE: &str = tomplate!("simple_template", name = "Alice");
const LOGIC: &str = tomplate!("handlebars_template", 
    logged_in = "true",
    user = "Bob"
);

5. Eager Evaluation for Nested Macros

use tomplate::{tomplate, tomplate_eager};

// Problem: This won't work with macros that expect string literals
// sqlx::query!(tomplate!("select_user", id = "5"))  // ❌ Fails

// Solution: Use tomplate_eager! to expand inner macros first
tomplate_eager! {
    sqlx::query!(tomplate!("select_user", id = "5"))  // ✅ Works
        .fetch_one(&pool)
        .await?
}

Build Configuration

In your build.rs:

fn main() {
    tomplate::Builder::new()
        .add_patterns([
            "**/*.tomplate.toml",
            "templates/*.toml"
        ])
        .default_engine(tomplate::Engine::Simple)
        .build()
        .expect("Failed to build templates");
}

Template Files

Create .tomplate.toml files in your project:

[user_query]
template = "SELECT {fields} FROM {table} WHERE {condition}"
engine = "simple"  # Optional, defaults to "simple"

[complex_template]
template = "{{#if condition}}{{value}}{{else}}default{{/if}}"
engine = "handlebars"

Feature Flags

  • build: Enables the build-time template discovery (enabled by default)
  • handlebars: Enables Handlebars template engine
  • tera: Enables Tera template engine
  • minijinja: Enables MiniJinja template engine