#template #compile #evaluate #following #name #consider

已删除 rubble-rs

一个用于从模板文件编译文本和评估代码的模板引擎

0.1.0 2021年4月23日

#5 in #consider

自定义许可证

36KB
553 代码行

rubble-rs

Rust中的模板引擎。

它做什么?

它允许使用包含将被替换占位符的模板。考虑以下示例

Hello there, {{ name }}!

根据为 name 提供的值,输出可能会有所不同。例如,如果使用 name 并将其值设置为 Joe,则将生成以下输出

Hello there, Joe!

该库评估此类模板并将它们编译成如上例所示的输出文本。

用法

要简单地编译模板,您可以使用 compile_template_from_file(file: PathBuf, variables: HashMap<String, String>, functions: HashMap<String, Box<dyn Function>>)

let file = PathBuf::from("template-file.txt");
let functions: HashMap<String, Box<dyn Function>> = HashMap::new();

let mut variables: HashMap<String, String> = HashMap::new();
variables.insert("name".to_string(), "Joe".to_string());

let result = compile_template_from_file(file, variables, functions);

assert_eq!(result.ok(), Some("Hello there, Joe!".to_string()));

还有一些其他实用函数

  • compile_template_from_string(template: String, variables: HashMap<String, String>, functions: HashMap<String, Box<dyn Function>>) - 可以用于将模板文本作为原始文本编译。
  • compile_template_fromcompile_template_from(template: Template, variables: HashMap<String, String>, functions: HashMap<String, Box<dyn Function>>) - 可以用于从模板实例编译文本。

这些是编译模板的方便函数,但对于某些特殊案例,您可能需要直接使用 EvaluatorCompiler 来编译模板。

语法

默认情况下,rubble-rs 在解析模板时会寻找所有以 {{ 开始并以 }} 结束的块,这些块被标记为评估点,其中的代码可以由 Evaluator 评估。

以下是一个示例

Hello there, {{ name }}!

这个模板包含三个部分

  • Hello there, - 原始文本
  • {{ name }} - 代码
  • ! - 原始文本

第二部分将通过 Compiler 传递给指定的 Evaluator,以便获得字符串输出。代码片段将被替换为输出。

rubble-rs 库还可以评估更复杂的代码。您可以通过传递自己的函数来丰富模板。

在评估过程中,该库使用类似 Lisp 的语法。所有函数调用看起来都像以下这样

function_name arg0 arg1 arg2

要调用 plus 函数(可以假设实现)来计算 2 + 2 的结果,您需要编写

The result is: {{ plus 2 2 }}

参数也可以用括号分组。这在某些情况下可能会有所帮助。例如,对于 plusmultiply 函数,您需要使用以下代码来计算 (1 + 2) * 3

The result is: {{ multiply (plus 1 2) 3 }}

自定义

编译包含三个阶段

  • parsing - 由模板迭代器执行,例如 EvaluableMixedContentIterator'a, T>,它可以提取模板部分,
  • evaluation - 由 Evaluator 执行,它评估迭代器找到的所有代码,
  • compiling - 由 Compiler 执行,它使用迭代器解析内容,向 Evaluator 提供内容,然后将所有内容连接成输出文本。

您可以实现自己的迭代器、评估器或编译器。要修改编译过程,您只需使用自己的 trait 实现,而不是默认实现。

为了说明它是如何工作的,以下是 compile_template_from_string 实际上执行的操作

let raw_input = "Hello there, {{ name }}!".to_string();
let variables: HashMap<String, String> = HashMap::new();
let functions: HashMap<String, Box<dyn Function>> = HashMap::new();

// compilation below
let template = Template::from(raw_input);
let engine = SimpleEvaluationEngine::from(functions);
let compiler = TemplateCompiler::new(engine);

compiler.compile(&template, &variables)

自定义函数

评估器可以通过自定义 Function 特性实现来扩展函数。为了使这个过程更容易,存在默认实现为 F where F: Fn(&dyn Evaluator, &Vec<SyntaxNode>, &HashMap<String, String>) -> Result<String, EvaluationError>,这意味着您可以使用lambda函数或Rust函数。

带有函数的示例

#[test]
fn should_compile_template() {
    let text = Template::from("{{hello}}\n2 + 2 = {{ plus 2 2 }}".to_string());
    
    let mut functions: HashMap<String, Box<dyn Function>> = HashMap::new();
    functions.insert("plus".to_string(), Box::new(plus_function));

    let mut variables: HashMap<String, String> = HashMap::new();
    variables.insert("hello".to_string(), "Hello world!".to_string());

    let result = compile_template_from_string(text, variables, functions);

    assert_eq!(result.ok(), Some("Hello world!.\n2 + 2 = 4".to_string()));
}

fn plus_function(evaluator: &dyn Evaluator, parameters: &Vec<SyntaxNode>, variables: &HashMap<String, String>) -> Result<String, EvaluationError> {
    Ok(
        parameters.iter()
            .map(|node|
                evaluator.evaluate(node.clone(), variables).unwrap().parse::<i32>().unwrap()
            )
            .sum::<i32>()
            .to_string()
    )
}

无运行时依赖