[!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.
if / else if / else conditionals, for loops over arrays, break, continue, and return statements.user.name) and array indexing (items[0]).context) allows adding structs to context that implement serde::Serialize.Add message-template to your Cargo.toml:
message-template = { git = "https://github.com/banocean/message-template.git" }
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()
);
}
Code blocks are enclosed in double curly braces ``. Plain text outside of code blocks is emitted as template content directly.
Expressions placed inside `` are evaluated and converted to strings in the output:
Hello , you have unread messages!
let)Declare local variables inside a template scope using let:
Double ping:
if / else if / else)Branch execution based on boolean conditions. End conditional blocks with ``:
Grade: A
Grade: B
Grade: F
for)Iterate over array structures using for ... in .... Use to exit early or to skip to the next iteration:
Users list:
-
return)Interrupt template evaluation and return a specific value or short-circuit rendering:
| Operator Category | Operators | Examples |
| — | — | — |
| Arithmetic | +, -, *, /, %, ** | , `2`, `10` |
| **Comparison** | `==`, `!=`, `>`, `<`, `>=`, `<=` |, |
| **Logical** | `&&`, `||`, `!`, `and`, `or`, `not` | |
| Unary | -, ! | , |
| String Concatenation | + | `` |
Access nested fields using dot notation (.):
User: (Role: )
Access elements by zero-based index using [index]:
First item:
Second item:
Nested lookup:
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()
);
}
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()
);
}
context)Enable the context feature in your Cargo.toml:
message-template = { git = "https://github.com/banocean/message-template.git", features = ["context"] }
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! MacroConstruct 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
}
Context::insert: (feature context) Inserts any type implementing serde::Serialize.For information on the test suite and contributing guidelines, please see CONTRIBUTING.md.