15 个版本 (9 个重大变更)
0.60.1 | 2023 年 9 月 27 日 |
---|---|
0.52.0 | 2023 年 9 月 4 日 |
0.40.1 | 2023 年 7 月 29 日 |
146 在 模板引擎 中排名
每月 139 次下载
185KB
3.5K SLoC
一个最小化和快速的模板引擎。
ban 是一个在运行时编译模板的模板引擎。它支持您可能期望的基本功能,易于使用,并尝试提供良好的错误信息。
这是一个正在进行中的项目,请查看文档以获取更多详细信息。
lib.rs
:
一个最小化和快速的模板引擎。
ban 是一个在运行时编译模板的模板引擎。它支持您可能期望的基本功能,易于使用,并尝试提供良好的错误信息。
特性
- 常见的逻辑结构(如
if
、let
和for
)。 - 用户自定义过滤器以转换内容。
- 一个可选的标准库,提供 HTML 转义等常见功能的过滤器。
- 多种模板继承策略。
- 块/扩展 - 将模板分解为可以被子模板覆盖的块。
- 包含 - 在指定位置渲染另一个模板。
- 自定义定界符。
🦊 Note
Ban is still in development, and is not ready to be used.
I only document features here that are actually implemented, but even then,
information may be incomplete until v1.
使用方法
使用 ban::default
创建一个新的 Engine
,或者如果您想使用自定义定界符,请使用 Builder
类型及其 ban::new
方法。
let engine = ban::default();
Engine
类型提供了一个注册过滤器并存储您可以使用 include
块调用的其他模板的地方。
编译
let engine = ban::default();
let template = engine.compile("hello (( name ))!").is_ok();
创建存储库
我们刚刚编译的 Template
有一个名为 "name" 的表达式,想要渲染某些内容。
为了渲染它,我们需要提供一个包含该标识符值的 Store
实例。
use ban::Store;
let mut store = Store::new();
store.insert_must("name", "taylor");
渲染
现在我们有了包含我们的 Template
想要使用的数据的 Store
,我们可以使用 Engine
来渲染它。
use ban::Store;
let engine = ban::default();
let template = engine.compile("hello, (( name ))!").unwrap();
let mut store = Store::new();
store.insert_must("name", "taylor");
let result = engine.render(&template, &store).unwrap();
assert_eq!(result, "hello, taylor!");
语法
本节提供了对表达式以及您可以使用的不同块的概述。
如果您使用过其他模板引擎,Ban 应该很熟悉。
表达式
表达式允许您从 Store
渲染内容,或使用字符串和数字等字面值。它们看起来像这样
(( name ))
或者,如果您想使用过滤器修改 "name" 变量
(( name | to_lowercase | left 3 ))
过滤器
Filters
可以在表达式中用来转换数据。
第一个过滤器的输入来自最左边的值。它从左到右经过每个过滤器,并渲染链中最后一个过滤器的输出。
过滤器可以接受任何数量的参数,也可以不接受。它们可以是命名的或匿名的。
命名参数需要在名称和值之间用冒号分隔
(( name | tag name: "taylor", age: 25 ))
匿名参数的工作方式相同,但没有显式的名称
(( name | tag "taylor", 25 ))
两种变体都需要用逗号分隔参数。
有关更多信息,请参阅 filter
模块。
如果
如果块允许根据一系列表达式进行条件渲染。
(* if true *)
hello
(* else if false *)
goodbye
(* end *)
您可以将两个值进行比较,或者只提供一个。如果值为真,则块将执行。
(* if 100 *)
hello
(* end *)
以下是真值表的简要说明
类型 | 何时为真值 |
---|---|
字符串 | 字符串不为空。 |
数字 | 数字大于零。 |
数组 | 数组不为空。 |
对象 | 对象不为空。 |
布尔值 | 布尔值为真。 |
您可以使用 not
关键字进行否定
(* if not false && 500 > 10 || true *)
hello
(* end *)
示例
use ban::Store;
let mut engine = ban::default();
let template = engine
.compile("(* if first > second *)hello(* else *)goodbye(* end *)")
.unwrap();
let store = Store::new()
.with_must("first", 100)
.with_must("second", 10);
let result = engine.render(&template, &store).unwrap();
assert_eq!(result, "hello");
对于
对于块允许对值进行迭代。
(* for item in inventory *)
Name: (( item.name ))
(* end *)
您可以提供如上所示的单个标识符,也可以提供两个
(* for i, item in inventory *)
Item number: (( i | add 1 )) // <-- Zero indexed, so add one!
Name: (( item.name ))
(* end *)
标识符持有的值取决于您正在迭代的类型
类型 | i 的值 |
item 的值 |
---|---|---|
字符串 | 字符的索引。 | 字符串中的字符。 |
数组 | 元素的索引。 | 数组中的元素。 |
对象 | 对象的键。 | 对象的值。 |
示例
use ban::{filter::serde::json, Store};
let mut engine = ban::default();
let template = engine
.compile("(* for item in inventory *)(( item )), (* end *)")
.unwrap();
let store = Store::new()
.with_must("inventory", json!(["sword", "shield"]));
let result = engine.render(&template, &store).unwrap();
assert_eq!(result, "sword, shield, ");
令
令表达式允许将值赋给一个标识符。
(* let name = "taylor" *)
表达式的左侧必须是标识符,即未引用的字符串,但右侧可以是标识符或字面值。
您也可以在令表达式中使用过滤器!
(* if is_admin *)
(* let name = "admin" *)
(* else *)
(* let name = user.name | to_lowercase *)
(* end *)
Hello, (( name )).
在 for 块内进行的赋值仅限于该块的范围
(* for item in inventory *)
(* let name = item.name *)
Name: (( name ))
(* end *)
Last item name: (( name )). // <-- Error, "name" is not in scope!
示例
use ban::Store;
let mut engine = ban::default();
let template = engine
.compile("hello, (* let name = \"taylor\" -*) (( name ))!")
.unwrap();
let store = Store::new();
let result = engine.render(&template, &store).unwrap();
assert_eq!(result, "hello, taylor!");
包含
包含表达式允许渲染其他模板。
(* include header *)
如果您以这种方式调用另一个模板,它将访问与调用它的模板相同的存储。
您可以传递参数,类似于过滤器
(* include header name: data.name, age: data.age *)
当您向包含的模板传递参数时,它将访问这些值以及没有其他东西。
示例
use ban::{filter::serde::json, Store};
let mut engine = ban::default();
engine
.insert_template_must("header", "hello, (( name ))! - (( age ))")
.unwrap();
let template = engine
.compile(r#"(* include header name: data.name, age: data.age *)"#)
.unwrap();
let store = Store::new()
.with_must("data", json!({"name": "taylor", "age": 25}));
let result = engine.render(&template, &store);
assert_eq!(result.unwrap(), "hello, taylor! - 25");
扩展
扩展表达式允许模板相互扩展。
(* extends parent *)
当模板源中首先找到 "extends" 表达式时,模板会扩展另一个模板。
当 Ban 渲染扩展的模板时,源中找到的所有块都会收集并携带到父模板。假设父模板本身没有被扩展,则这些块将在那里渲染。
当在父模板(非扩展)中找到块表达式时,Ban 将渲染匹配的块,如果有的话。
如果没有找到匹配的块,则将块内的任何数据作为默认值渲染。
use ban::{Engine, Store};
let mut engine = Engine::default();
engine
.insert_template_must(
"first",
r#"hello (* block "🦊" *)(* end *), (* block greeting *)doing well?(* end *)"#,
)
.unwrap();
engine
.insert_template_must(
"second",
r#"(* extends first *)(* block "🦊" *)(( name ))(* end *)"#,
)
.unwrap();
let store = Store::new().with_must("name", "taylor");
let template = engine.get_template("second").unwrap();
assert_eq!(
engine.render(&template, &store).unwrap(),
"hello taylor, doing well?"
);
请参阅 examples/inheritance 目录以获得完整的说明。
定界符
使用 Builder
类型创建一个识别不同分隔符集的 Engine
。
use ban::{Engine, Builder, Store};
let engine = Engine::new(
Builder::new()
.with_expression(">>", "<<")
.with_block("{@", "@}")
.with_whitespace(&'~')
.to_syntax(),
);
let template = engine
.compile("{@ if true ~@} Hello, >> name <<!{@ end @}")
.unwrap();
let store = Store::new().with_must("name", "taylor");
let result = engine.render(&template, &store).unwrap();
assert_eq!(result, "Hello, taylor!")
依赖项
~0.7–1.2MB
~18K SLoC