15 个版本 (9 个重大变更)

0.60.1 2023 年 9 月 27 日
0.52.0 2023 年 9 月 4 日
0.40.1 2023 年 7 月 29 日

146模板引擎 中排名

Download history 9/week @ 2024-03-09 1/week @ 2024-03-16 2/week @ 2024-03-30 1/week @ 2024-04-06 1/week @ 2024-04-20 2/week @ 2024-05-18

每月 139 次下载

MIT 许可证

185KB
3.5K SLoC

一个最小化和快速的模板引擎。

ban 是一个在运行时编译模板的模板引擎。它支持您可能期望的基本功能,易于使用,并尝试提供良好的错误信息。

这是一个正在进行中的项目,请查看文档以获取更多详细信息。


lib.rs:

一个最小化和快速的模板引擎。

ban 是一个在运行时编译模板的模板引擎。它支持您可能期望的基本功能,易于使用,并尝试提供良好的错误信息。

特性

  • 常见的逻辑结构(如 ifletfor)。
  • 用户自定义过滤器以转换内容。
    • 一个可选的标准库,提供 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 块调用的其他模板的地方。

编译

使用 Engine 编译一个 Template

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