6 个版本

0.2.3 2024年1月24日
0.2.2 2023年12月6日
0.2.1 2023年9月28日
0.1.1 2023年7月3日

#119 in 模板引擎

MIT 许可证

21KB
275

Stilts

Documentation Package

Stilts 是一种受 Askama 启发的模板语言。这意味着它具有 Rust 编译器提供的一切类型安全。它仍然与 Jinja 有关联,但采用了更 Rust 风格的表达式语法。

这个项目还处于早期阶段,我只是因为我喜欢 Askama 而开始它,但我认为可以通过在模板表达式中允许更多 Rust 特定的语法来改进它。如果您有建议、功能、问题或任何其他事项,请随时提出。我特别需要帮助编写单元测试,并且愿意与他人合作完成这项工作。

指南

请参阅 书籍 以获取深入了解文档。

工作原理

Stilts 使用在结构体上定义的进程式 derive 宏来生成模板渲染代码,从而使模板代码由 Rust 编译器检查其正确性。

快速示例

以下是一些定义模板的 Rust 代码示例

use stilts::Template;

#[derive(Template)] // This derive macro will generate the template rendering code
#[stilts(path = "index.html")] // based on the contents of the given template path
struct IndexPage<'a> {
    some_data: &'a str, // The fields on a struct are the variables 
}                       // you want to use in your template

以下是 index.html 可能看起来像什么

<!DOCTYPE html>
<html>
    <body>
        <h1>My Index Page</h1>
        {% some_data %} <!--This will print some_data to the template here using 
                            the types implementaion of the Display trait-->
    </body>
</html

关于 Stilts 表达式

In Stilts an expression is made up of two variants single and block expressions. A single expression is anything inside the delimiters {%%}. Whereas a block expression has an opening single expression and a closing single expression with some kind of content in-between. For example

{% for i in 0..10 %}
    This will be repeated 10 times
    {% i %}
    And I can put other expressions inside
{% end %}

Something you might notice if you have used Askama or Jinja before is that normally to render the value of something you have to use a different set of delimiters namely something like {{ some_data }}. But here in Stilts there is only one set of delimiters {% some_data %}.

那么我们如何判断用户是想渲染某些内容还是仅仅编写一些代码呢?答案是,通过判断括号内的Rust代码是一个表达式还是一个语句。在Rust中,表达式总是会产生一个值,而语句则不会。

因此,如果我们作为用户只想编写代码而不将其渲染到模板中,我们只需要添加一个分号即可。现在 {% some_data; %} 就是一个语句而不是表达式,这意味着Stilts将把它作为代码插入到模板渲染代码中,而不是渲染到最终的模板中。这对许多Rust开发者来说很熟悉,因为这就是我们可以在函数内部通过仅以表达式结束来省略 return 关键字的方式。

这也意味着你可以在括号内放置任何任意的Rust表达式或语句。例如 {% let myval = some_data.split(' ').filter(|s| s != "abcd"); %}。但你不仅限于单行,你也可以将它们分成尽可能多的行。

{% fn my_useful_func() {
    // do some stuff
} %}

功能

  • 语法类似于Jinja,但也更紧密地与Rust相关
  • 利用Rust的类型系统来验证你的代码
  • 实现了与Askama相当的性能,而Askama已经非常好了
  • 良好的错误信息和格式化,特别是带有可选的复杂特性
  • 在稳定的Rust上运行

支持的模板构造

  • 继承
  • for循环
  • if/else if/else
  • match表达式
  • 包含语句
  • 变量(由于允许任意Rust表达式和语句,因此存在)
  • 导入到作用域的有用的扩展特质
  • 可配置的HTML转义选项

目标

  • 创建一个既熟悉给Jinja用户又熟悉Rust开发者的模板语言
  • 具有良好的错误报告和消息,并带有有用的错误位置。

其他东西

在工具目录中有一个正在进行的tree-sitter解析器实现。如果你知道如何更好地使其工作,那将是超级酷的。

依赖项

~2–17MB
~195K SLoC