mystical-runic 0.2.0

๐Ÿ”ฎ Ancient symbols for modern web magic - A mystical templating engine that weaves HTML from runic incantations
Documentation

๐Ÿ”ฎ Mystical-Runic - Ancient Symbols for Modern Web Magic

"In the beginning was the Word, and the Word was {{mustache}}..."

Crates.io Documentation License: MIT Build Status

Welcome, brave developer, to the mystical realm of Mystical-Runic - where ancient Nordic symbols meet modern HTML templating in a beautiful dance of curly braces and digital sorcery!

โœจ Features

๐Ÿ”’ Security First: XSS-safe by default with comprehensive HTML escaping
โšก High Performance: Template caching, bytecode compilation, parallel processing
๐ŸŽฏ Simple API: Clean, intuitive interface for Rust developers
๐Ÿงช Well Tested: 127+ tests with extensive security and performance tests
๐Ÿ—๏ธ Template Inheritance: Advanced layout system with nested inheritance and {{super}}
๐Ÿ”ง Powerful Filters: Built-in filters like upper, lower, currency, truncate with chaining support
๐Ÿ“ฆ Reusable Macros: Define and reuse template components with parameters
๐ŸŒŠ Deep Object Navigation: Unlimited depth dot notation (e.g., {{user.profile.stats.level}})
๐ŸŒ Zero Dependencies: Pure Rust implementation with no external dependencies

โšก The Sacred Incantations

Core Magic

  • Whisper Variables: {{name}} - Speak a name and it shall manifest (safely escaped from evil XSS spirits)
  • Summon Raw Power: {{& html}} - Unleash unescaped HTML with great responsibility and greater danger
  • Divine Conditionals: {{if chosen_one}}...{{/if}} - The HTML appears only for the worthy
  • Mystical Loops: {{for spell in grimoire}}...{{/for}} - Repeat incantations until magic happens
  • Ancient Includes: {{include "scrolls/wisdom.html"}} - Import wisdom from other sacred texts
  • Silent Whispers: {{! This is but a comment, invisible to mortals }} - Notes for future wizards
  • Deep Path Traversal: {{user.profile.stats.level}} - Navigate through nested object realms with unlimited depth

Advanced Sorcery (v0.2.0)

  • Sacred Inheritance: {{extends "base.html"}} - Inherit the power of ancestral templates
  • Mystical Blocks: {{block content}}...{{/block}} - Define regions of power in your layouts
  • Ancestral Wisdom: {{super}} - Channel the content of parent templates
  • Transformation Filters: {{name|upper|truncate:10}} - Transform values with ancient filters
  • Reusable Spells (Macros): {{macro spell(power)}}...{{/macro}} - Create reusable incantations
  • Spell Invocation: {{spell("lightning")}} - Call upon your defined macros

๐Ÿš€ Quick Start

Installation

[dependencies]
mystical-runic = "0.2.0"

Basic Usage

use mystical_runic::{RuneEngine, RuneScroll, RuneSymbol};

// Summon the ancient engine from the template realm
let mut engine = RuneEngine::new("templates");
let mut scroll = RuneScroll::new();

// Inscribe your desires upon the scroll
scroll.set_string("hero", "Rust Developer");
scroll.set_string("quest", "Debug Production Issues");
scroll.set_number("level", 99);
scroll.set_bool("has_coffee", true);

// Speak the incantation and witness the transformation
let result = engine.render_string(
    "Behold! {{hero}} of level {{level}} embarks upon {{quest}}! {{if has_coffee}}โ˜•{{/if}}", 
    &scroll
).unwrap();

assert_eq!(result, "Behold! Rust Developer of level 99 embarks upon Debug Production Issues! โ˜•");

Advanced Example: Character Sheet Generator

use mystical_runic::{RuneEngine, RuneScroll, RuneSymbol};
use std::collections::HashMap;

let mut engine = RuneEngine::new(".");
let mut scroll = RuneScroll::new();

// Create a magical character
let mut character = HashMap::new();
character.insert("name".to_string(), RuneSymbol::String("Gandalf the Grey".to_string()));
character.insert("class".to_string(), RuneSymbol::String("Wizard".to_string()));
character.insert("level".to_string(), RuneSymbol::Number(85));
character.insert("mana".to_string(), RuneSymbol::Number(9999));
character.insert("has_staff".to_string(), RuneSymbol::Bool(true));

scroll.set("character", RuneSymbol::Object(character));

// โš”๏ธ Create spell list
let spells = vec![
    create_spell("Fireball", 50, "๐Ÿ”ฅ"),
    create_spell("Lightning Bolt", 75, "โšก"),
    create_spell("Ice Shard", 40, "โ„๏ธ"),
];
scroll.set("spells", RuneSymbol::Array(spells));

let character_sheet = r#"
๐Ÿง™โ€โ™‚๏ธ Name: {{character.name}}
๐ŸŽ“ Class: {{character.class}} (Level {{character.level}})
๐Ÿ”ฎ Mana: {{character.mana}}

{{if character.has_staff}}
๐Ÿช„ Equipment: Magical Staff of Power
{{/if}}

โšก KNOWN SPELLS:
{{for spell in spells}}
  {{spell.icon}} {{spell.name}} - Power: {{spell.damage}}
{{/for}}
"#;

let result = engine.render_string(character_sheet, &scroll).unwrap();
println!("{}", result);

fn create_spell(name: &str, damage: i64, icon: &str) -> RuneSymbol {
    let mut spell = HashMap::new();
    spell.insert("name".to_string(), RuneSymbol::String(name.to_string()));
    spell.insert("damage".to_string(), RuneSymbol::Number(damage));
    spell.insert("icon".to_string(), RuneSymbol::String(icon.to_string()));
    RuneSymbol::Object(spell)
}

Deep Dot Notation Example

use mystical_runic::{RuneEngine, RuneScroll, RuneSymbol};
use std::collections::HashMap;

let mut engine = RuneEngine::new(".");
let mut scroll = RuneScroll::new();

// Create deeply nested game data structure
let mut stats = HashMap::new();
stats.insert("level".to_string(), RuneSymbol::Number(42));
stats.insert("health".to_string(), RuneSymbol::Number(100));
stats.insert("mana".to_string(), RuneSymbol::Number(75));

let mut equipment = HashMap::new();
equipment.insert("weapon".to_string(), RuneSymbol::String("Mystical Sword".to_string()));
equipment.insert("armor".to_string(), RuneSymbol::String("Dragon Scale".to_string()));

let mut character = HashMap::new();
character.insert("name".to_string(), RuneSymbol::String("Aragorn".to_string()));
character.insert("class".to_string(), RuneSymbol::String("Ranger".to_string()));
character.insert("stats".to_string(), RuneSymbol::Object(stats));
character.insert("equipment".to_string(), RuneSymbol::Object(equipment));

let mut game_data = HashMap::new();
game_data.insert("character".to_string(), RuneSymbol::Object(character));

scroll.set("game", RuneSymbol::Object(game_data));

// Use deep dot notation to access nested values
let template = r#"
๐ŸŽฎ GAME CHARACTER SHEET ๐ŸŽฎ

๐Ÿ‘ค Name: {{game.character.name}}
๐ŸŽญ Class: {{game.character.class}}

๐Ÿ“Š STATS:
โค๏ธ Health: {{game.character.stats.health}}
๐Ÿ’™ Mana: {{game.character.stats.mana}}
โญ Level: {{game.character.stats.level}}

โš”๏ธ EQUIPMENT:
{{if game.character.equipment.weapon}}
๐Ÿ—ก๏ธ Weapon: {{game.character.equipment.weapon}}
{{/if}}
{{if game.character.equipment.armor}}
๐Ÿ›ก๏ธ Armor: {{game.character.equipment.armor}}
{{/if}}

{{if game.character.stats.level}}
๐Ÿ† Status: {{if game.character.stats.health}}Combat Ready{{/if}}
{{/if}}
"#;

let result = engine.render_string(template, &scroll).unwrap();
println!("{}", result);

๐Ÿฐ Template Inheritance (v0.2.0)

Create sophisticated layouts with template inheritance:

<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{{block title}}My Site{{/block}}</title>
</head>
<body>
    <header>{{block header}}Default Header{{/block}}</header>
    <main>{{block content}}{{/block}}</main>
    <footer>{{block footer}}ยฉ 2024 My Site{{/block}}</footer>
</body>
</html>
<!-- admin.html -->
{{extends "base.html"}}

{{block title}}Admin Panel - {{block page_title}}{{/block}}{{/block}}

{{block content}}
<div class="admin-layout">
    <nav>{{block sidebar}}Default Sidebar{{/block}}</nav>
    <main>{{block admin_content}}{{/block}}</main>
</div>
{{/block}}
<!-- admin_users.html -->
{{extends "admin.html"}}

{{block page_title}}User Management{{/block}}

{{block admin_content}}
<h1>Users</h1>
{{for user in users}}
    <div class="user-card">{{user.name}} - {{user.role}}</div>
{{/for}}
{{/block}}

๐Ÿ”ง Powerful Filters (v0.2.0)

Transform your data with built-in filters:

<h1>{{title|upper}}</h1>                    <!-- HELLO WORLD -->
<p>{{description|lower}}</p>                <!-- hello world -->
<span>${{price|currency}}</span>            <!-- $12.99 -->
<div>{{content|truncate:50}}</div>          <!-- Truncated text... -->
<time>{{date|date:"Y-m-d"}}</time>          <!-- 2024-01-15 -->

<!-- Chain multiple filters -->
<p>{{name|lower|capitalize}}</p>            <!-- John Doe -->
<span>{{text|strip|truncate:20|upper}}</span>   <!-- TRIMMED TEXT... -->

๐Ÿ“ฆ Reusable Macros (v0.2.0)

Create reusable template components:

<!-- Define macros -->
{{macro button(text, type="button", class="btn")}}
<button type="{{type}}" class="{{class}}">{{text}}</button>
{{/macro}}

{{macro card(title, content, class="card")}}
<div class="{{class}}">
    <h3 class="card-title">{{title}}</h3>
    <div class="card-body">{{content}}</div>
</div>
{{/macro}}

{{macro user_card(user)}}
<div class="user-card">
    <h4>{{user.name}}</h4>
    <p>{{user.email}}</p>
    {{if user.active}}
        <span class="status active">Online</span>
    {{/if}}
</div>
{{/macro}}

<!-- Use macros -->
{{button("Save", type="submit", class="btn btn-primary")}}
{{card("Welcome", "This is a welcome message", class="card highlight")}}

{{for user in users}}
    {{user_card(user)}}
{{/for}}

๐Ÿ“– Template Syntax Guide

Variables

<!-- Safe HTML escaping (default) -->
<p>{{user_input}}</p>

<!-- Raw HTML output (use with caution) -->
<div>{{& trusted_html}}</div>

<!-- Object properties -->
<span>{{user.name}} ({{user.email}})</span>

<!-- Deep nested properties -->
<div>Level: {{player.character.stats.level}}</div>
<p>{{config.database.connection.host}}:{{config.database.connection.port}}</p>

Conditionals

{{if user.is_admin}}
  <button class="admin-panel">Admin Controls</button>
{{/if}}

{{if items}}
  <ul class="item-list">
    <!-- items exist -->
  </ul>
{{/if}}

<!-- Deep conditionals -->
{{if user.settings.notifications.email.enabled}}
  <p>Email notifications are on</p>
{{/if}}

{{if config.features.advanced.enabled}}
  <div class="advanced-features">Advanced mode active</div>
{{/if}}

Truthiness Rules:

  • Strings: non-empty = true, empty = false
  • Numbers: non-zero = true, zero = false
  • Booleans: as expected
  • Arrays: non-empty = true, empty = false
  • Objects: non-empty = true, empty = false

Loops

{{for product in products}}
  <div class="product">
    <h3>{{product.name}}</h3>
    <p>Price: ${{product.price}}</p>
    {{if product.on_sale}}
      <span class="sale-badge">ON SALE!</span>
    {{/if}}
  </div>
{{/for}}

Template Includes

<!-- main.html -->
<!DOCTYPE html>
<html>
<head>
  {{include "partials/head.html"}}
</head>
<body>
  {{include "partials/header.html"}}
  <main>{{content}}</main>
  {{include "partials/footer.html"}}
</body>
</html>

Comments

{{! This comment will not appear in the output }}
<div>
  {{! 
    Multi-line comments
    are also supported
  }}
  <p>Visible content</p>
</div>

๐Ÿ”’ Security Features

Mystical-Runic takes security seriously and provides multiple layers of protection:

XSS Prevention

let mut context = RuneScroll::new();
context.set_string("user_input", "<script>alert('xss')</script>");

let result = engine.render_string("{{user_input}}", &context).unwrap();
// Output: &lt;script&gt;alert(&#x27;xss&#x27;)&lt;/script&gt;

Path Traversal Protection

// These will safely fail:
engine.render("../../../etc/passwd", &context);  // โŒ Blocked
engine.render("..\\windows\\system32", &context);  // โŒ Blocked

Template Injection Prevention

context.set_string("malicious", "{{admin_password}}");
let result = engine.render_string("{{malicious}}", &context).unwrap();
// Output: {{admin_password}} (literal text, not executed)

๐ŸŽจ API Reference

RuneEngine (TemplateEngine)

let mut engine = RuneEngine::new("path/to/templates");

// Render from file
let result = engine.render("template.html", &context)?;

// Render from string
let result = engine.render_string("Hello {{name}}!", &context)?;

// Load template (with caching)
let template_content = engine.load_template("header.html")?;

RuneScroll (TemplateContext)

let mut scroll = RuneScroll::new();

// Set different value types
scroll.set_string("name", "value");
scroll.set_number("count", 42);
scroll.set_bool("active", true);

// Set complex values
scroll.set("array", RuneSymbol::Array(vec![...]));
scroll.set("object", RuneSymbol::Object(hashmap));

// Retrieve values
let value = scroll.get_string("name");

RuneSymbol (TemplateValue)

// Create different value types
let string_val = RuneSymbol::String("text".to_string());
let number_val = RuneSymbol::Number(42);
let bool_val = RuneSymbol::Bool(true);
let array_val = RuneSymbol::Array(vec![...]);
let object_val = RuneSymbol::Object(hashmap);

๐Ÿงช Testing

Run the comprehensive test suite:

# Run all tests
cargo test

# Run specific test categories
cargo test integration_tests
cargo test unit_tests
cargo test security_tests

# Run with output
cargo test -- --nocapture

๐Ÿ”ฎ Examples

Check out the examples/ directory for more magical demonstrations:

  • spell_casting.rs - Fantasy RPG character sheet generator
  • More examples coming soon!

Development Setup

git clone https://github.com/yrbane/mystical-runic.git
cd mystical-runic
cargo build
cargo test

๐Ÿงช Test-Driven Development (TDD) Methodology

Mystical-Runic follows strict Test-Driven Development practices. When contributing, please observe the sacred TDD ritual:

๐Ÿ”ด Red โ†’ ๐ŸŸข Green โ†’ ๐Ÿ”ต Refactor Cycle

  1. ๐Ÿ”ด RED - Write a Failing Test First

    # Write your test before any implementation
    cargo test your_new_feature_test
    # โŒ Should fail - good!
    
  2. ๐ŸŸข GREEN - Write Minimal Code to Pass

    # Write just enough code to make the test pass
    cargo test your_new_feature_test
    # โœ… Should pass - excellent!
    
  3. ๐Ÿ”ต REFACTOR - Improve Without Breaking

    # Clean up code while keeping tests green
    cargo test  # All tests should still pass
    

"Tests are the safety net that lets you refactor fearlessly." โ€” TDD Proverb

๐Ÿ“œ Changelog

v0.2.0 (Latest Release) - The Advanced Sorcery Edition

  • ๐Ÿฐ Template Inheritance: Advanced layout system with nested inheritance support
  • ๐Ÿ”ง Powerful Filters: Built-in filters (upper, lower, currency, truncate, date) with chaining
  • ๐Ÿ“ฆ Reusable Macros: Define and invoke template components with parameters
  • ๐ŸŒŠ Enhanced Deep Navigation: Unlimited depth dot notation ({{game.player.stats.level}})
  • โšก Performance Boost: Bytecode compilation, parallel processing, memory mapping
  • ๐Ÿงช 127+ Tests: Comprehensive test coverage including v0.2.0 features
  • ๐Ÿ”ง Bug Fixes: Fixed nested layout inheritance and function call error handling
  • ๐ŸŒ Zero Dependencies: Pure Rust implementation

v0.1.4 (Stability Release)

  • ๐Ÿ”ง Fixed nested layout inheritance block replacement boundary calculation
  • ๐Ÿ›ก๏ธ Enhanced loop error handling for unsupported function calls
  • ๐Ÿ”„ Maintained backward compatibility for missing variables in loops
  • โœ… All 127 tests passing with comprehensive coverage

v0.1.1 (Security & Testing Release)

  • ๐Ÿ›ก๏ธ Comprehensive security testing suite
  • ๐Ÿงช 85+ tests with 100% coverage following TDD methodology
  • ๐Ÿ“š Complete documentation with TDD development guidelines
  • ๐Ÿ”’ Advanced XSS and injection protection
  • โšก Performance optimizations and stress testing
  • ๐Ÿ”ด๐ŸŸข๐Ÿ”ต Strict Test-Driven Development practices implemented

v0.1.0 (Initial Release)

  • โœจ Core template engine with Mustache-inspired syntax
  • ๐Ÿ”’ XSS-safe HTML escaping by default
  • โšก Template caching for performance
  • ๐ŸŽฏ Support for variables, conditionals, loops, includes, and comments
  • ๐Ÿงช Comprehensive test suite with high coverage
  • ๐Ÿ“š Complete documentation and examples

๐ŸŒŸ Why "Mystical-Runic"?

Because templating is basically ancient magic:

  • You write mysterious symbols ({{}}) that transform into reality
  • Variables appear and disappear like spirits
  • One wrong bracket and your entire spell explodes
  • Senior developers guard the template secrets like ancient druids
  • Documentation is written in a language only the initiated understand
  • And just like real magic, it works perfectly until production ๐Ÿ”ฅ

๐Ÿ”— Links

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


"May your templates be bug-free and your variables always defined."
โ€” Ancient DevOps Proverb

๐Ÿ”ฎโœจ Happy templating! โœจ๐Ÿ”ฎ