The bomboni_template crate provides a comprehensive collection of custom Handlebars helpers for template rendering. These helpers enable mathematical operations, string transformations, value manipulation, conditional logic, and dynamic template rendering.
Quick Start
Register all helpers with your Handlebars instance:
use handlebars :: Handlebars ;
use bomboni_template :: helpers :: register_helpers;
let mut hbs = Handlebars :: new ();
register_helpers ( & mut hbs );
Math Operations
Perform mathematical calculations directly in templates:
use handlebars :: Handlebars ;
use serde_json :: Value ;
use bomboni_template :: helpers :: register_helpers;
let mut hbs = Handlebars :: new ();
register_helpers ( & mut hbs );
// Basic arithmetic
assert_eq! ( hbs . render_template ( "{{add 1 2}}" , & Value :: Null ) . unwrap (), "3.0" );
assert_eq! ( hbs . render_template ( "{{multiply 5 3}}" , & Value :: Null ) . unwrap (), "15.0" );
assert_eq! ( hbs . render_template ( "{{subtract 10 4}}" , & Value :: Null ) . unwrap (), "6.0" );
assert_eq! ( hbs . render_template ( "{{divide 20 4}}" , & Value :: Null ) . unwrap (), "5.0" );
// Advanced operations
assert_eq! ( hbs . render_template ( "{{sqrt 2}}" , & Value :: Null ) . unwrap (), "1.4142135623730951" );
assert_eq! ( hbs . render_template ( "{{pow 2 8}}" , & Value :: Null ) . unwrap (), "256.0" );
assert_eq! ( hbs . render_template ( "{{clamp 5 1 3}}" , & Value :: Null ) . unwrap (), "3.0" );
assert_eq! ( hbs . render_template ( "{{absolute -42}}" , & Value :: Null ) . unwrap (), "42.0" );
Available Math Helpers
Helper Description Example addAddition {{add 1 2}} → 3.0subtractSubtraction {{subtract 10 4}} → 6.0multiplyMultiplication {{multiply 5 3}} → 15.0divideDivision {{divide 20 4}} → 5.0moduloModulo operation {{modulo 10 3}} → 1.0negateNegate value {{negate 5}} → -5.0absoluteAbsolute value {{absolute -42}} → 42.0roundRound to nearest {{round 3.7}} → 4.0ceilCeiling {{ceil 3.2}} → 4.0floorFloor {{floor 3.8}} → 3.0sqrtSquare root {{sqrt 16}} → 4.0signSign of number {{sign -5}} → -1.0powPower {{pow 2 8}} → 256.0clampClamp between min/max {{clamp 5 1 3}} → 3.0
String Helpers
Case Conversion
Transform strings between different case conventions:
use handlebars :: Handlebars ;
use serde_json :: Value ;
use bomboni_template :: helpers :: register_helpers;
let mut hbs = Handlebars :: new ();
register_helpers ( & mut hbs );
assert_eq! (
hbs . render_template ( r#"{{upperCase "hello world"}}"# , & Value :: Null ) . unwrap (),
"HELLO WORLD"
);
assert_eq! (
hbs . render_template ( r#"{{pascalCase "hello world"}}"# , & Value :: Null ) . unwrap (),
"HelloWorld"
);
assert_eq! (
hbs . render_template ( r#"{{snakeCase "hello world"}}"# , & Value :: Null ) . unwrap (),
"hello_world"
);
assert_eq! (
hbs . render_template ( r#"{{screamingSnakeCase "hello world"}}"# , & Value :: Null ) . unwrap (),
"HELLO_WORLD"
);
assert_eq! (
hbs . render_template ( r#"{{camelCase "hello world"}}"# , & Value :: Null ) . unwrap (),
"helloWorld"
);
assert_eq! (
hbs . render_template ( r#"{{kebabCase "hello world"}}"# , & Value :: Null ) . unwrap (),
"hello-world"
);
Available Case Helpers
upperCase - UPPER CASE
lowerCase - lower case
titleCase - Title Case
camelCase - camelCase
pascalCase - PascalCase
snakeCase - snake_case
screamingSnakeCase - SCREAMING_SNAKE_CASE
kebabCase - kebab-case
trainCase - Train-Case
cobolCase - COBOL-CASE
flatCase - flatcase
toggleCase - tOGGLE cASE
alternatingCase - aLtErNaTiNg CaSe
use handlebars :: Handlebars ;
use serde_json :: { Value , json};
use bomboni_template :: helpers :: register_helpers;
let mut hbs = Handlebars :: new ();
register_helpers ( & mut hbs );
// Convert to integer string
assert_eq! (
hbs . render_template ( r"{{toIntegerString 3.14}}" , & Value :: Null ) . unwrap (),
"3"
);
// Concatenate values
assert_eq! (
hbs . render_template ( r#"{{concat "Hello" " " "World"}}"# , & Value :: Null ) . unwrap (),
"Hello World"
);
// Convert to JSON
let data = json! ({ "name" : "Alice" , "age" : 30 });
assert_eq! (
hbs . render_template ( r"{{toJson this}}" , & data ) . unwrap (),
r#"{"age":30,"name":"Alice"}"#
);
Value Helpers
Object and Array Operations
use handlebars :: Handlebars ;
use serde_json :: Value ;
use bomboni_template :: helpers :: register_helpers;
let mut hbs = Handlebars :: new ();
register_helpers ( & mut hbs );
// Create objects
assert_eq! (
hbs . render_template ( r#"{{objectHasKey (object x=42) "x"}}"# , & Value :: Null ) . unwrap (),
"true"
);
// Create arrays
assert_eq! (
hbs . render_template ( r"{{contains (array 1 2 3 4) 2}}" , & Value :: Null ) . unwrap (),
"true"
);
// Check truthiness
assert_eq! (
hbs . render_template ( r"{{none false 0}}" , & Value :: Null ) . unwrap (),
"true"
);
assert_eq! (
hbs . render_template ( r"{{all true 1 \" text \ "}}" , & Value :: Null ) . unwrap (),
"true"
);
assert_eq! (
hbs . render_template ( r"{{some false 0 true}}" , & Value :: Null ) . unwrap (),
"true"
);
Available Value Helpers
Helper Description objectCreate object from named parameters objectHasKeyCheck if object has key arrayCreate array from parameters groupByGroup array by key containsCheck if haystack contains needle noneCheck if all values are falsy allCheck if all values are truthy someCheck if some value is truthy filterFilter array by predicate template orElseReturn item if truthy, else fallback andThenReturn fallback if item truthy, else item eitherOrConditional value selection
Group By Example
use handlebars :: Handlebars ;
use serde_json :: Value ;
use bomboni_template :: helpers :: register_helpers;
let mut hbs = Handlebars :: new ();
register_helpers ( & mut hbs );
// Group objects by a key
hbs . render_template (
r#"{{groupBy (array (object x=1 y=2) (object x=2 y=4)) "x"}}"# ,
& Value :: Null
) . unwrap ();
Switch/Case Pattern Matching
Implement pattern matching in templates:
use handlebars :: Handlebars ;
use serde_json :: json;
use bomboni_template :: helpers :: register_helpers;
let mut hbs = Handlebars :: new ();
register_helpers ( & mut hbs );
let template = r#"
{{#switch x}}
{{#case 42}}life{{/case}}
{{#case 2 4 8}}power of two{{/case}}
{{#default}}other{{/default}}
{{/switch}}
"# ;
assert_eq! ( hbs . render_template ( template , & json! ({ "x" : 42 })) . unwrap () . trim (), "life" );
assert_eq! ( hbs . render_template ( template , & json! ({ "x" : 2 })) . unwrap () . trim (), "power of two" );
assert_eq! ( hbs . render_template ( template , & json! ({ "x" : 5 })) . unwrap () . trim (), "other" );
Switch Statement Structure
{{ #switch value }}
{{ #case "option1" }} Handle option 1 {{ /case }}
{{ #case "option2" "option3" }} Handle options 2 or 3 {{ /case }}
{{ #default }} Handle all other cases {{ /default }}
{{ /switch }}
The case helper supports multiple values, matching if any value equals the switch value.
Dynamic Template Rendering
Render templates dynamically at runtime:
use handlebars :: Handlebars ;
use serde_json :: json;
use bomboni_template :: helpers :: register_helpers;
let mut hbs = Handlebars :: new ();
register_helpers ( & mut hbs );
// Render a template string with current context
assert_eq! (
hbs . render_template ( r#"{{render "x = {{x}}"}}"# , & json! ({ "x" : 42 })) . unwrap (),
"x = 42"
);
// Use with nested data
let template = r#"{{render "User: {{user.name}}, Age: {{user.age}}"}}"}"# ;
assert_eq! (
hbs . render_template ( template , & json! ({
"user" : { "name" : "Alice" , "age" : 30 }
})) . unwrap (),
"User: Alice, Age: 30"
);
This is particularly useful for:
Code generation with dynamic templates
Configuration-driven template composition
Building templates from database content
Local Variables
Set and use local variables within templates:
use handlebars :: Handlebars ;
use bomboni_template :: helpers :: register_helpers;
let mut hbs = Handlebars :: new ();
register_helpers ( & mut hbs );
let template = r#"
{{*set name = "John" age = 14000}}
{{@name}} is {{@age}} years old.
"# ;
let rendered = hbs . render_template ( template , & serde_json :: Value :: Null ) . unwrap ();
assert_eq! ( rendered . trim (), "John is 14000 years old." );
Variable Scope
Local variables set with the set decorator:
Use the * prefix for decorators: {{*set ...}}
Access with @ prefix: {{@variableName}}
Are scoped to the current template block
Can be used in nested contexts
Use Cases
Code Generation
{{ * set structName = ( pascalCase name ) }}
struct {{ @structName }} {
{{ #each fields }}
pub {{ snakeCase this.name }} : {{ this.type }} ,
{{ /each }}
}
{{ #switch status }}
{{ #case 200 201}} Success: {{ message }}{{ /case }}
{{ #case 400 404}} Client Error: {{ error }}{{ /case }}
{{ #default }} Server Error {{ /default }}
{{ /switch }}
Configuration Templates
{{ * set port = ( orElse config.port 8080 ) }}
server:
host: {{ config.host }}
port: {{ @port }}
workers: {{ multiply cpus 2 }}
Best Practices
Register helpers once at application startup for optimal performance.
Avoid complex logic in templates. Use helpers for simple transformations and move business logic to your Rust code.
Compile templates ahead of time with hbs.register_template_string()
Reuse Handlebars instances across requests
Keep templates focused on presentation logic
Use render helper sparingly as it has runtime overhead
Request Parsing Parse and validate API requests
Protocol Buffers Work with protobuf messages