This library is great! I really like the simplicity and power of directly using Rust syntax for control flow.
I would like to use the templating with an inline string, and without needing to define a custom struct. Using the MyTemplateTxt
example from your README, it would become something like this:
fn my_template(foo: bool, bar: Result<String, Box<dyn std::error::Error>>) -> String {
boilerplate!(r#"
%% if foo {
Foo was true!
%% }
%% match bar {
%% Ok(ok) => {
Pretty good: {{ ok }}
%% }
%% Err(err) => {
Not so great: {{ err }}
%% }
%% }
"#)
}
In fact, I independently developed a similar library to boilerplate, which uses exactly this approach, see example: https://github.com/reinerp/utemplate/blob/main/utemplate/examples/simple.rs. But I think I prefer your template syntax to mine!
There's a few advantages to the functionlike macro approach:
-
The user doesn't need to define a struct
for each template they care about. Instead, variable reference from templates are picked up from context. This allows things like global constants const SOME_CONSTANT: &str
as well.
-
The template is more powerful, allowing other aspects of control flow such as early returns. For example:
fn format_all(foos: &[Result<usize, Box<dyn std::error::Error>>]) -> Result<String, Box<dyn std::error::Error>> {
boilerplate!(r#"
%% for foo in foos {
%% let f = foo?; // Note early return here.
The next value is {{f}}.
%% }
"#)
}
-
The procmacro implementation is simpler. In particular, you don't need to parse the struct
type (so you don't need syn
as a dependency), and you don't need to handle generics (which are often a pain).