Rust axum Teraを使いこなす

### {% %}の利用
テンプレート内での計算などができる

<body class="container">
	<h1 class="display-6 my-2">{{title}}</h1>
	<div class="border border-primary p-3 my-3">
		{% set v1 = value * 1.08 %}
		{% set v2 = value * 1.1 %}
		<p class="my-2">value: {{ value }}</p>
		<p class="my-2">value1: {{ v1 }}</p>
		<p class="my-2">value2: {{ v2 }}</p>
	</div>
</body>

main.rs

#[tokio::main]
async fn main() {
	let app = axum::Router::new()
		.route("/:value", axum::routing::get(handle_index));

    axum::Server::bind(&"192.168.56.10:8000".parse().unwrap())
    	.serve(app.into_make_service())
    	.await
    	.unwrap();
}

async fn handle_index(axum::extract::Path(value):
		axum::extract::Path<usize>
		)-> axum::response::Html<String> {
	let tera = tera::Tera::new("template/*").unwrap();

	let mut context = tera::Context::new();
	context.insert("title", "Index page");
	context.insert("value", &value);

	let output = tera.render("index.html", &context);
	axum::response::Html(output.unwrap())
}

{% raw %}で生の文を出力する
L HTMLタグはそのまま表示される

	<div class="border border-primary p-3 my-3">
		{% raw %}
		{% set v1 = value * 1.08 %}
		{% set v2 = value * 1.1 %}
		<p class="my-2">value: {{ value }}</p>
		<p class="my-2">value1: {{ v1 }}</p>
		<p class="my-2">value2: {{ v2 }}</p>
		{% endraw %}
	</div>

テキストで繋げる

	<div class="border border-primary p-3 my-3">
		{% set v1 = value * 1.08 %}
		{% set v2 = value * 1.1 %}
		<p class="my-2">value: {{ value }}</p>
		<p class="my-2">value1: {{ value ~ " * 1.08 = " ~ v1  }}</p>
		<p class="my-2">value2: {{ value ~ " * 1.1 = " ~ v2 }}</p>
	</div>

### ifを使う

	{% if value % 2 == 0 %}
	<div class="border border-primary p-3 my-3">
		<p class="my-2">value: {{ value ~ "は偶数です。" }}</p>
	</div>
	{% else %}
	<div class="border border-primary p-3 my-3">
		<p class="my-2">value: {{ value ~ "は奇数です。" }}</p>
	</div>
	{% endif %}

更に分岐したい場合

	{% if value % 3 == 0 %}
	<div class="border border-primary p-3 my-3">
		<p class="my-2">value: {{ value ~ "はグーです。" }}</p>
	</div>
	{% elif value % 3 == 1 %}
	<div class="border border-primary p-3 my-3">
		<p class="my-2">value: {{ value ~ "はチョキです。" }}</p>
	</div> 
	{% else %}
	<div class="border border-primary p-3 my-3">
		<p class="my-2">value: {{ value ~ "はパーです。" }}</p>
	</div> 
	{% endif %}

### 繰り返し表示
main.rs

async fn handle_index()-> axum::response::Html<String> {
	let data = [
		Myform {name:"taro".to_string(), mail:"taro@yamada".to_string()},
		Myform {name:"hanako".to_string(), mail:"hanako@flower".to_string()},
		Myform {name:"sachiko".to_string(), mail:"sachiko@happy".to_string()},
		Myform {name:"jiro".to_string(), mail:"jiro@change".to_string()},
	];
	let tera = tera::Tera::new("template/*").unwrap();

	let mut context = tera::Context::new();
	context.insert("title", "Index page");
	context.insert("data", &data);

	let output = tera.render("index.html", &context);
	axum::response::Html(output.unwrap())
}

view

	<ul class="list-group">
		{% for item in data %}
		<li class="list-group-item">
			{{item.name}} &lt;{{item.mail}}&gt;
		</li>
		{% endfor %}
	</ul>

### キー&バリューのコレクション
マップにHashMapインスタンスを代入。このHashMapをテンプレート側で処理する

async fn handle_index()-> axum::response::Html<String> {
	let mut map = std::collections::HashMap::new();
	map.insert("taro", ("taro@yamada", 39));
	map.insert("hanako", ("hanako@flower", 28));
	map.insert("sachiko", ("sachiko@happy", 17));

	let tera = tera::Tera::new("template/*").unwrap();

	let mut context = tera::Context::new();
	context.insert("title", "Index page");
	context.insert("data", &map);

	let output = tera.render("index.html", &context);
	axum::response::Html(output.unwrap())
}
	<ul class="list-group">
		{% for key, value in data %}
			<li class="list-group-item">
				[{{loop.index}}] {{ key }}({{value.1}}) &lt;{{value.0}}&gt;
			</li>
		{% endfor %}
	</ul>

loop.indexは繰り返し情報がまとめられたオブジェクト

### フィルターの利用
フィルターは、あらかじめRust側で定義した関数を使ってテンプレートの表示を変換する機能
フィルター用の関数は形が決まっている
fn 関数(引数1: &value, 引数2: &HashMap)-> Result{…}

作成したフィルター関数はTeraのインスタンスに登録する

main.rs

async fn handle_index()-> axum::response::Html<String> {

	let mut tera = tera::Tera::new("template/*").unwrap();
	tera.register_filter("hello", hello_filter);

	let mut context = tera::Context::new();
	context.insert("title", "Index page");
	context.insert("name", "山田タロー");

	let output = tera.render("index.html", &context);
	axum::response::Html(output.unwrap())
}

fn hello_filter(value: &tera::Value,
	_: &std::collections::HashMap<String, tera::Value>)
	-> tera::Result<tera::Value> {
		let s = tera::try_get_value!("hello_filter", "value", String, value);
		Ok(tera::Value::String(format!("こんにちは、{}さん!", s)))
	}

テンプレート

	<div class="alert alert-primary">
		<p class="my-2">{{ name | hello}}</p>
	</div>

### フィルターでオブジェクトを扱う場合

async fn handle_index()-> axum::response::Html<String> {

	let mut tera = tera::Tera::new("template/*").unwrap();
	tera.register_filter("sample", sample_filter);

	let mut context = tera::Context::new();
	context.insert("title", "Index page");
	context.insert("id", &1);

	let output = tera.render("index.html", &context);
	axum::response::Html(output.unwrap())
}

fn sample_filter(value: &tera::Value,
	_: &std::collections::HashMap<String, tera::Value>)
	-> tera::Result<tera::Value> {
		let data = [
			("taro", "taro@yamada", 39, "male"),
			("hanako", "hanako@flower", 28, "female"),
			("sachiko", "sachiko@happy", 17, "female"),
			("jiro", "jiro@change", 6, "male")
		];
		let n = tera::try_get_value!("sample_filter", "value", usize, value);
		let item = data[n];
		Ok(tera::Value::String(format!("{}({},{})<{}>", item.0, item.3, item.2, item.1)))
	}

### フィルターの属性を利用する

async fn handle_index()-> axum::response::Html<String> {

	let mut tera = tera::Tera::new("template/*").unwrap();
	tera.register_filter("calc", calc_filter);

	let mut context = tera::Context::new();
	context.insert("title", "Index page");

	let output = tera.render("index.html", &context);
	axum::response::Html(output.unwrap())
}

fn calc_filter(_: &tera::Value,
	map: &std::collections::HashMap<String, tera::Value>)
	-> tera::Result<tera::Value> {
		let price = map.get("price").unwrap().as_f64().unwrap();
		let tax = map.get("tax").unwrap().as_f64().unwrap();
		let res = price * tax;
		Ok(tera::Value::String(format!("price:{} * tax:{} = {}", price, tax, res)))
}
	<div class="alert alert-primary">
		<p class="my-2">{{ false | calc(price=1234, tax=1.1) }}</p>
	</div>