| Crates.io | figura |
| lib.rs | figura |
| version | 2.0.3 |
| created_at | 2025-06-21 13:04:47.941315+00 |
| updated_at | 2026-01-15 21:03:59.158753+00 |
| description | A flexible string template formatting crate |
| homepage | https://github.com/saverioscagnoli/figura |
| repository | https://github.com/saverioscagnoli/figura |
| max_upload_size | |
| id | 1720796 |
| size | 137,226 |
.---------------.
| J |
| . //// |
| / \ |o o| |
| (_,_) | < | |
| | |___| |
| / \ |
| | ||
'---------------'
CowAdd this to your Cargo.toml:
[dependencies]
figura = "2.0.3"
use figura::{Template, DefaultParser, Context, Value};
use std::collections::HashMap;
let mut ctx = Context::new();
ctx.insert("name", Value::static_str("Alice"));
ctx.insert("count", Value::Int(3));
let template = Template::<'{', '}'>::compile(
"Hello {name}! Stars: {'★':count}"
).unwrap();
let output = template.format(&ctx).unwrap();
assert_eq!(output, "Hello Alice! Stars: ★★★");
let template = Template::<'{', '}'>::compile(
"User: {username}, Age: {age}"
).unwrap();
ctx.insert("username", Value::static_str("Bob"));
ctx.insert("age", Value::Int(25));
// Output: "User: Bob, Age: 25"
let template = Template::<'{', '}'>::compile(
"Message: {'Hello World'}"
).unwrap();
// Output: "Message: Hello World"
let template = Template::<'{', '}'>::compile(
"{'-':50}\n{title}\n{'-':50}"
).unwrap();
ctx.insert("title", Value::static_str("HEADER"));
// Output:
// --------------------------------------------------
// HEADER
// --------------------------------------------------
Simple boolean conditions:
let template = Template::<'{', '}'>::compile(
"Status: {active ? 'Online' : 'Offline'}"
).unwrap();
ctx.insert("active", Value::Bool(true));
// Output: "Status: Online"
With comparisons:
let template = Template::<'{', '}'>::compile(
"Access: {age >= 18 ? 'Granted' : 'Denied'}"
).unwrap();
ctx.insert("age", Value::Int(21));
// Output: "Access: Granted"
Supported operators: ==, !=, >, <, >=, <=
Logical NOT:
let template = Template::<'{', '}'>::compile(
"{!enabled ? 'Disabled' : 'Enabled'}"
).unwrap();
let template = Template::<'{', '}'>::compile(
"Literal braces: {{not a variable}}"
).unwrap();
// Output: "Literal braces: {not a variable}"
Use any characters as delimiters:
// Angle brackets
let template = Template::<'<', '>'>::compile(
"Hello <name>!"
).unwrap();
// Square brackets
let template = Template::<'[', ']'>::compile(
"Value: [count]"
).unwrap();
// Same character for both
let template = Template::<'%', '%'>::compile(
"Data: %value%"
).unwrap();
Figura supports four value types:
// String (zero-copy when possible)
ctx.insert("name", Value::static_str("Alice"));
ctx.insert("name", Value::owned_str(String::from("Bob")));
// Integer
ctx.insert("count", Value::Int(42));
// Float
ctx.insert("score", Value::Float(95.5));
// Boolean
ctx.insert("active", Value::Bool(true));
Implement the Parser trait to create custom parsing logic:
use figura::{Parser, Token, Directive, Argument};
struct MathParser;
impl Parser for MathParser {
fn parse(tokens: &[Token]) -> Option<Box<dyn Directive>> {
match tokens {
[Token::Ident(left), Token::Plus, Token::Ident(right)] => {
Some(Box::new(AddDirective {
left: left.to_string(),
right: right.to_string(),
}))
}
_ => Some(Box::new(EmptyDirective)),
}
}
}
// Implement custom directive
struct AddDirective {
left: String,
right: String,
}
impl Directive for AddDirective {
fn exec(&self, ctx: &Context) -> Result<Cow<'static, str>, DirectiveError> {
// Custom execution logic
}
}
// Use custom parser
let template = Template::<'{', '}'>::compile_with_parser::<MathParser>(
"{x + y}"
).unwrap();
Template<O, C> - Compiled template with open/close delimiters
Value - Runtime values (String, Int, Float, Bool)
Context - HashMap of variable names to values
DefaultParser - Built-in parser implementation
Parser - Trait for custom parsers
Directive - Trait for executable template components
MIT License - Copyright (c) Saverio Scagnoli
Contributions are welcome! Please feel free to submit a Pull Request.