Crates.io | struct-string-template |
lib.rs | struct-string-template |
version | 0.1.0 |
source | src |
created_at | 2023-05-16 05:50:34.75606 |
updated_at | 2023-05-16 05:50:34.75606 |
description | Simple string templating language on struct fields |
homepage | |
repository | https://github.com/ryanYtan/templater |
max_upload_size | |
id | 865802 |
size | 26,868 |
This crate defines a simple templating language in the format %(SELECTOR)
that
can operate on a structure. A given SELECTOR
accesses a given structure of
type T
via a closure Fn(&T) -> Option<String>
. Using a closure allows
complex logic to accessing the structure e.g. "if field x does not exist,
then try field y, otherwise return a default value or None
".
(This library is most definitely not production-ready)
%(X)
where X
is a selector)
%(id) - %(title)
In Cargo.toml
:
[dependencies]
struct_string_template = "0.1.0"
Say we have a struct
definition, and an instance of the struct
:
struct Book {
id: i64,
title: String,
author: String,
contributors: Option<String>
}
let my_book = Book {
id: 9784832275492,
title: "Hidamari Sketch".to_owned(),
author: "蒼樹うめ".to_owned(),
contributors: None,
};
Define a format string:
let format_string = "[%(id)] %(title) %(所有作者)";
Build a Templater<Book>
by doing:
use struct_string_template::TemplaterBuilder;
let templater = TemplaterBuilder::<Book>::new()
.with_selector("id", |book| Some(book.id.to_string()))
.with_selector("title", |book| Some(book.title.clone()))
.with_selector("所有作者", |book| {
Some(format!("(By: {}{})",
&book.author,
&book.contributors.clone().map(|x| format!(", {}", x)).or(Some("".to_owned())).unwrap())
)
})
.build();
Render it using Templater
's render
function
let result = templater.render(&my_book, format_string).ok().unwrap();
println!("{}", &result);
Templater
without TemplateBuilder
If the Templater
needs to be built iteratively instead of using the builder
class, use Templater::new()
, then add closures using the insert
or extend
methods.
let mut templater = Templater::<Book>::new();
templater.insert("id", |book| Some(book.id.to_string()));
templater.insert("title", |book| Some(book.title.clone()));
templater.insert("所有作者", |book| {
Some(format!("(By: {}{})",
&book.author,
&book.contributors.clone().map(|x| format!(", {}", x)).or(Some("".to_owned())).unwrap())
)
});
If you plan to use a format string many times, you can "precompile" it similar
to a regex for better performance using the Formatter
class (render
turns
the format string into a Formatter
internally), then pass the Formatter
variable into the renderf
function:
use struct_string_template::Formatter;
let formatter = Formatter::build(format_string).ok().unwrap();
let result = templater.renderf(&my_book, &formatter);
println!("{}", &result);
See src/err.rs
for the errors thrown.
In this section, X
is any selector
%%
is treated as a literal %
%
followed by a character (or end of string) that is not (
is invalid%(X)
is always valid%(X)A
where A
is any valid string is valid%(X
(template is not terminated) is invalidX
is invalidX
on the structure is None
, NA
is printed. This is currently unconfigurable except by modifying the closure itself.Currently the Templater
object cannot be copied around due to the closure
types. I've not found a work-around for this yet.
I use this library in a few personal applications of mine, and I've found it annoying to keep changes to the library in sync between them.