1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use std::path::PathBuf;
use std::collections::HashMap;
use crate::evaluator::{Function, Context};
use crate::template::Template;
use crate::evaluator::engine::SimpleEvaluationEngine;
use crate::compiler::{TemplateCompiler, Compiler, CompilationError};
use std::error::Error;

pub mod template;
pub mod evaluator;
pub mod compiler;
pub mod functions;

/// Compiles template from file.
///
/// This function reads a file and uses supplied variables and functions to compile a template.
/// It is a quick way to get a compiled template, as it initializes Engine and Compiler with each invocation.
///
/// For some special cases consider using [SimpleEvaluationEngine], [TemplateCompiler] or other specific
/// [Evaluator](evaluator::Evaluator) and [Compiler] traits implementations.
///
/// Template can look like the following: `Some template {{ variable }} - or something`.
/// Code that will be evaluated should be put between `{{` and `}}`.
pub fn compile_template_from_file(file: PathBuf, variables: HashMap<String, String>, functions: HashMap<String, Box<dyn Function>>) -> Result<String, Box<dyn Error>> {
    let template = Template::read_from(&file)?;

    compile_template_from(template, variables, functions)
        .map_err(|error| Box::new(error) as Box<dyn Error>)
}

/// Compiles template from String.
///
/// It creates a [Template] instance on the fly and then compiles it.
///
/// For some special cases consider using [SimpleEvaluationEngine], [TemplateCompiler] or other specific
/// [Evaluator](evaluator::Evaluator) and [Compiler] traits implementations.
///
/// Template can look like the following: `Some template {{ variable }} - or something`.
/// Code that will be evaluated should be put between `{{` and `}}`.
pub fn compile_template_from_string(template: String, variables: HashMap<String, String>, functions: HashMap<String, Box<dyn Function>>) -> Result<String, CompilationError> {
    compile_template_from(Template::from(template), variables, functions)
}

/// Compiles template from [Template].
///
/// For some special cases consider using [SimpleEvaluationEngine], [TemplateCompiler] or other specific
/// [Evaluator](evaluator::Evaluator) and [Compiler] traits implementations.
///
/// Template can look like the following: `Some template {{ variable }} - or something`.
/// Code that will be evaluated should be put between `{{` and `}}`.
pub fn compile_template_from(template: Template, variables: HashMap<String, String>, functions: HashMap<String, Box<dyn Function>>) -> Result<String, CompilationError> {
    let engine = SimpleEvaluationEngine::from(functions);
    let compiler = TemplateCompiler::new(engine);

    compiler.compile(&template, Context::with_variables(variables))
}

#[cfg(test)]
mod tests {
    use crate::compile_template_from_file;
    use std::path::PathBuf;
    use std::collections::HashMap;
    use crate::evaluator::Function;
    use crate::evaluator::functions::SimpleFunction;

    #[test]
    fn should_compile_template() {
        let file = PathBuf::from("test-assets/complex-template");
        let mut functions: HashMap<String, Box<dyn Function>> = HashMap::new();
        functions.insert("plus".to_string(), SimpleFunction::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_file(file, variables, functions);

        assert_eq!(result.ok(), Some("Some template. Hello world!.\n\nThis shows a function evaluation usage example:\n2 + 2 = 4".to_string()));
    }

    fn plus_function(parameters: &[String]) -> String {
        parameters.iter()
                .map(|param|
                    param.parse::<i32>().unwrap()
                )
                .sum::<i32>()
                .to_string()
    }
}