Rust のテンプレートエンジン askama を使ってみた
背景、お久しぶりでございます
Rust でテンプレートエンジンを使ってコードの自動生成をしてみることにしました。Rust なのはただの趣味です。意味はない。
で、Rust テンプレートエンジンって調べると、なんだかいっぱいあるっぽい。何を使うのが良いのだろうか。
Rust template engine benchmarks をみると Handlebars, Liquid, Sailfish, Markup というのが早いっぽい。
これを眺めてて気になったのが、 Askama と Sailfish 。これらのテンプレートエンジンは、どうやらコンパイル時にテンプレートをプログラムに埋め込むことが出来るっぽい。1つのデータから、複数のテンプレートを使って生成するような場合には向かないけど、今回私は単一のデータから単一のファイルを出力するだけなので、コンパイル時に分かるほうが色々とはかどります。テンプレートファイルを読み込む処理が要らないし、楽じゃん、コレ、というわけで今回はひとまずスターが多い Askama を選んでみました。別に私は速度はそんなに重要でもないしね。
サンプルプロジェクト
サンプルプロジェクトは askama_sample に置いています。
プロジェクトの初期化
cargo init
cargo add askama
mkdir templates
touch askama.toml
- Cargo で初期化
- askama を入れる
- テンプレートを入れるディレクトリを作成
- askama の設定ファイルを作成
設定ファイル askama.toml
今回は C++ のヘッダをテンプレートエンジンから作成します。
デフォルトだと拡張子に使えるのが html とか txt だけっぽいので、 hpp を使えるように askama.toml で指定します。私が最大の嵌って調べるのに時間がかかったポイントです。
[[escaper]]
path = "::askama::Text"
extensions = ["hpp"]
::askama::Text
は Escaper を実装している構造体名らしい。 Text だと何もエスケープしない。 HTML だと HTML の特殊文字をエスケープ処理してくれるっぽい。
C++ ヘッダなので特にエスケープする必要もないので、 ::askama::Text
を選びました。
テンプレートの作成
とりあえず、おもむろにテンプレートファイル templates/foobar.hpp を作成します。文法はしっかりとは理解してませんが、この資料 を適当に眺めながら書いたら、なんかかけました。
{{-
のように {{
や {%
などの後に -
を書いてやると直前の空白を無効化できます。逆に -}}
だと直後の空白を無効化します。これは正直生成された結果を見ながらええ感じに調整したので、どうやるのがベストなのかはよくわかりません。
プログラムの作成
あとは、テンプレートで作ったデータを埋めるようにプログラム側で構造体を用意してやるだけです。
use askama::Template;
#[derive(Template)]
#[template(path = "foobar.hpp")]
struct Foobar<'a> {
namespace: &'a str,
structures: Vec<Structure<'a>>,
}
これだけで Foobar
が templates/foobar.hpp をテンプレートにした出力を作る render
が作られます。
println!("{}", foobar.render().unwrap());
コンパイル時に足りないフィールドやメソッド等も分かるので、実行して確認しなくても安心です。まぁ、もうちょっとわかりやすいエラーメッセージだとありがたいけど。
実行結果
良い感じに C++ のヘッダが出力されましたね。
出力
#pragma once
#include <cstdint>
namespace fooooobaaaaaa {
/// @brief foooooo
struct Foo {
/// @brief HOGE
const char* hoge;
/// @brief PIYO
uint8_t piyo;
/// @brief TARO
uint32_t taro[3][2][10];
};
/// @brief baaaaaa
struct Bar {
/// @brief HOGE
uint32_t x;
/// @brief PIYO
uint8_t y;
};
} // namespace fooooobaaaaaa
感想
askama.toml の書き方が分かれば非常に簡単に使うことが出来ました。
ファイルの読み込み処理が要らないので使いやすいですね。テンプレートファイルを指定する場合は、自分の実行バイナリの場所からフォルダを見つけるとか、面倒くさい処理しないといけないですからね。
ただ、1つの構造体につき1つのテンプレートになるので、やや使いまわしがしにくいのかな?
トップが Rc
を持ってるだけとか一つ階層を挟む必要があるかもしれませんね。
個人的にはとても便利だと思います。
Discussion