LoginSignup
1
1

テンプレートエンジン Handlebars を Rust で扱う

Last updated at Posted at 2024-05-18

はじめに

Tauri のインストーラースクリプト作成に Hnadlebars が使われているのを見かけた。
今後 Rust でのテンプレートエンジンはどれにしようかなとも思っていたのと、Hnadlebars 自体は初めてなので、挙動や書き方をメモろうと思ったのでこのポストを書く。

確認環境

  • handlebars 5.1.2
  • Windows 11 Pro 23H2
  • Rust 1.77.2

導入

cargo add handlebars

使い方

基本的な使い方

基本的には

  1. テンプレートの作成・取得
  2. テンプレートの登録
  3. レンダリングの実行

という手順をとる。

use handlebars::Handlebars;
use serde::Serialize;

#[derive(Serialize)]
struct Data {
    world: String,
}

fn main() {
    // オブジェクトを作成
    let mut handlebars = Handlebars::new();

    // 代入するための構造体を作成
    let data = Data {
        world: "世界!".to_string(),
    };

    // テンプレートを定義
    let template = "hello, {{world}}";
    // テンプレートを `t1` という名前で登録
    handlebars.register_template_string("t1", template).unwrap();

    // レンダリング (値の代入と結果の出力) を実行
    println!("{}", handlebars.render("t1", &data).unwrap());
}

実行すると以下の結果を得られる。

hello, 世界!

公式ドキュメントでは、 BTreeMap のサンプルも記載がある。

let mut data = BTreeMap::new();
data.insert("world".to_string(), "世界!".to_string());
assert_eq!(handlebars.render("t1", &data).unwrap(), "hello 世界!");

これは serde 自体が BTreeMap のシリアライズを実装しているためそのまま使えるという事。
ドキュメントには serde::Serialize 可能なデータタイプでないとだめとしか書かれていないので混乱するが、つまりそういう事。

配列を処理する

基本は {{#each <key>}} {{/each}} で囲む。

use handlebars::Handlebars;
use serde::Serialize;

#[derive(Serialize)]
struct Goods {
    name: String,
    price: u32,
}

#[derive(Serialize)]
struct Basket {
    contents: Vec<Goods>,
}

fn main() {
    let mut handlebars = Handlebars::new();

    let data = Basket {
        contents: vec![
            Goods {
                name: "ほうれん草".to_string(),
                price: 99,
            },
            Goods {
                name: "新玉ねぎ".to_string(),
                price: 280,
            },
            Goods {
                name: "レタス".to_string(),
                price: 158,
            },
        ],
    };

    let template = "{{#each contents}}\n{{name}}  {{price}}円\n{{/each}}\n";
    handlebars.register_template_string("t1", template).unwrap();

    println!("{}", handlebars.render("t1", &data).unwrap());
}

実行すると次の結果を得られる。

ほうれん草  99円
新玉ねぎ  280円
レタス  158円

テンプレートファイルをレンダリングする

template.txt
今日の買い物リスト {{date}}

バスケット:
{{#each contents}}
    - {{name}} {{price}}円
{{/each}}
use handlebars::Handlebars;
use serde::Serialize;
use std::path::PathBuf;

#[derive(Serialize)]
struct Goods {
    name: String,
    price: u32,
}

#[derive(Serialize)]
struct Basket {
    date: String,
    contents: Vec<Goods>,
}

fn main() {
    let mut handlebars = Handlebars::new();

    let data = Basket {
        date: chrono::Local::now().to_string(),
        contents: vec![
            Goods {
                name: "ほうれん草".to_string(),
                price: 99,
            },
            Goods {
                name: "新玉ねぎ".to_string(),
                price: 280,
            },
            Goods {
                name: "レタス".to_string(),
                price: 158,
            },
        ],
    };

    // let template = "{{#each contents}}\n{{name}}  {{price}}円\n{{/each}}\n";
    handlebars
        .register_template_file(
            "t1",
            PathBuf::from(r"F:\Develop\Rust\_learn\handlebars_\src\template.txt"),
        )
        .unwrap();

    println!("{}", handlebars.render("t1", &data).unwrap());
}

実行すると次の結果が得られる。

今日の買い物リスト 2024-05-18 20:33:49.547171800 +09:00

バスケット:
    - ほうれん草 99円
    - 新玉ねぎ 280円
    - レタス 158円

さいごに

Handlebars 自体は色んな言語環境で使われている様子だし、そんなに書き方も違和感を感じない。
Rust でのどの serde::Serialize タイプを前提にというのもあるので、そこだけ意識しておけばよさそう。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1