message-template

[!WARNING] This project is maintained for usage in custom-fail/custom. Contributions outside of that scope won’t be merged.

A simple templating language for rust.

Features

Quickstart

Add message-template to your Cargo.toml:

message-template = { git = "https://github.com/banocean/message-template.git" }

Basic Example

use message_template::*;

#[tokio::main]
async fn main() {
    let mut context = Context::new();
    context.insert_value("ping", Value::Integer(42));

    let template = "Current ping is `ms`";

    assert_eq!(
        run_as_text(template, Some(&context)).await,
        "Current ping is `42ms`".to_string()
    );
}

Syntax Guide

Code blocks are enclosed in double curly braces ``. Plain text outside of code blocks is emitted as template content directly.

Variable Interpolation & Display

Expressions placed inside `` are evaluated and converted to strings in the output:

Hello , you have  unread messages!

Local Variables (let)

Declare local variables inside a template scope using let:



Double ping: 

Conditionals (if / else if / else)

Branch execution based on boolean conditions. End conditional blocks with ``:


    Grade: A

    Grade: B

    Grade: F

Loops (for)

Iterate over array structures using for ... in .... Use to exit early or to skip to the next iteration:

Users list:

    
        
    
    - 

Return Statement (return)

Interrupt template evaluation and return a specific value or short-circuit rendering:


    

Operators & Expressions

| Operator Category | Operators | Examples | | — | — | — | | Arithmetic | +, -, *, /, %, ** | , `2`, `10` | | **Comparison** | `==`, `!=`, `>`, `<`, `>=`, `<=` |, | | **Logical** | `&&`, `||`, `!`, `and`, `or`, `not` | | | Unary | -, ! | , | | String Concatenation | + | `` |

Data Structures

Object Property Access

Access nested fields using dot notation (.):

User:  (Role: )

Array Indexing

Access elements by zero-based index using [index]:

First item: 
Second item: 
Nested lookup: 

Registering Custom Functions

Synchronous Functions

Register synchronous functions into Context using Context::register_function:

use message_template::*;

#[tokio::main]
async fn main() {
    let mut context = Context::new();
    context.register_function("square", |args| {
        if let Some(Value::Integer(n)) = args.first() {
            Ok(Value::Integer(n * n))
        } else {
            Err("Expected integer".to_string())
        }
    });
    
    assert_eq!(
        run_as_text("5 squared is ", Some(&context)).await,
        "5 squared is 25".to_string()
    );
}

Asynchronous Functions

Register asynchronous functions using Context::register_async_function:

use message_template::*;

#[tokio::main]
async fn main() {
    let mut context = Context::new();
    context.register_async_function("fetchUser", |args| async move {
        let name = match args.first() {
            Some(Value::String(s)) => s.as_str(),
            _ => "Guest"
        };
        Ok(Value::String(format!("User: {}", name)))
    });

    assert_eq!(
        run_as_text("", Some(&context)).await,
        "User: Alice".to_string()
    );
    
    assert_eq!(
        run_as_text("", Some(&context)).await,
        "User: Guest".to_string()
    );
}

Serde & Context Feature (context)

Enable the context feature in your Cargo.toml:

message-template = { git = "https://github.com/banocean/message-template.git", features = ["context"] }

Inserting Serde Structs

Directly insert any serde::Serialize struct into the context using Context::insert:

use message_template::*;

#[cfg(feature = "context")]
async fn execute() {
    #[derive(serde::Serialize)]
    struct User {
        name: String,
        age: u32,
    }

    let mut context = Context::new();
    context.insert("user", User { name: "Alice".to_string(), age: 30 });

    assert_eq!(
        run_as_text("", Some(&context)).await,
        "30".to_string()
    );
}

#[tokio::main]
async fn main() {
    #[cfg(feature = "context")]
    execute().await
}

context! Macro

Construct contexts using a JSON-like syntax with the context! macro:

use message_template::*;

#[cfg(feature = "context")]
async fn execute() {
    let context = context! {
        "siteName": "My Store",
        "item": {
            "name": "Laptop",
            "price": 999.99
        }
    };
    
    assert_eq!(
        run_as_text("", Some(&context)).await, 
        "Laptop".to_string()
    );
}

#[tokio::main]
async fn main() {
    #[cfg(feature = "context")]
    execute().await
}

API Overview

Contributing & Testing

For information on the test suite and contributing guidelines, please see CONTRIBUTING.md.