#![cfg_attr(all(feature = "unstable", test), feature(test))]
#![deny(missing_docs)]
#[macro_use]
extern crate quick_error;
extern crate serde;
extern crate serde_json;
extern crate eval;
extern crate glob;
mod template;
mod render;
mod utils;
mod error;
mod node;
mod tree;
mod keyword;
mod cache;
pub use cache::Cache;
pub use template::RenderOptions;
pub use template::Template;
pub use error::Error;
pub use eval::Error as ExpressionError;
pub use eval::{Context, Contexts, Value, Function, Functions, to_value};
type Compiled = Box<Fn(Contexts, &Functions) -> Result<String, Error>>;
#[cfg(test)]
mod tests {
use Template;
use std::collections::HashMap;
#[test]
fn test_if() {
let source = "{{if true}}Hello world!{{if}}";
assert_eq!(Template::new()
.template(source)
.render()
.unwrap(),
"Hello world!");
}
#[test]
fn test_if_value() {
let source = "{{if boolean}}Hello world!{{if}}";
assert_eq!(Template::new()
.template(source)
.value("boolean", true)
.render()
.unwrap(),
"Hello world!");
assert!(Template::new()
.template(source)
.value("boolean", false)
.render()
.unwrap()
.is_empty());
}
#[test]
fn test_if_expression() {
let source = "{{if 1 < 2}}Hello world!{{if}}";
assert_eq!(Template::new()
.template(source)
.render()
.unwrap(),
"Hello world!");
}
#[test]
fn test_if_complex_expression() {
let source = "{{if (2 + 3) * 5 == 25}}Hello world!{{if}}";
assert_eq!(Template::new()
.template(source)
.render()
.unwrap(),
"Hello world!");
}
#[test]
fn test_if_is_empty() {
let source = "{{if is_empty('')}}Hello world!{{if}}";
assert_eq!(Template::new()
.template(source)
.render()
.unwrap(),
"Hello world!");
}
#[test]
fn test_if_is_not_empty() {
let source = "{{if !is_empty('Not Empty')}}Hello world!{{if}}";
assert_eq!(Template::new()
.template(source)
.render()
.unwrap(),
"Hello world!");
}
#[test]
fn test_else_if() {
let source = "{{if false}}{{elseif true}}Hello world!{{if}}";
assert_eq!(Template::new()
.template(source)
.render()
.unwrap(),
"Hello world!");
}
#[test]
fn test_else() {
let source = "{{if false}}{{else}}Hello world!{{if}}";
assert_eq!(Template::new()
.template(source)
.render()
.unwrap(),
"Hello world!");
}
#[test]
fn test_set_boolean() {
let source = "{{set boolean=true}}{{if boolean}}Hello world!{{if}}";
assert_eq!(Template::new()
.template(source)
.render()
.unwrap(),
"Hello world!");
}
#[test]
fn test_set_string() {
let source = "{{set value='Hello world!'}}{{value}}";
assert_eq!(Template::new()
.template(source)
.render()
.unwrap(),
"Hello world!");
}
#[test]
fn test_set_number() {
let source = "{{set number=123}}{{number == 123}}";
assert_eq!(Template::new()
.template(source)
.render()
.unwrap(),
"true");
}
#[test]
fn test_for() {
let source = "{{for value, index in array}}[{{index}}]{{value}}{{for}}";
assert_eq!(Template::new()
.template(source)
.value("array", vec!["Hello", " ", "world", "!"])
.render()
.unwrap(),
"[0]Hello[1] [2]world[3]!");
}
#[test]
fn test_example() {
Template::new()
.root("examples")
.extension("html")
.open("main")
.unwrap()
.render()
.unwrap();
}
#[test]
fn test_string() {
let source = "Hello world!";
assert_eq!(Template::new()
.template(source)
.render()
.unwrap(),
"Hello world!");
}
#[test]
fn test_expression() {
let source = "{{ 3 + 5 * 4 + 3 - 5 }}";
assert_eq!(Template::new()
.template(source)
.render()
.unwrap(),
"21");
}
#[test]
fn test_object_access() {
let mut object = HashMap::new();
object.insert("foo", "Foo, hello world!");
object.insert("bar", "Bar, hello world!");
let source = "{{if object.foo == 'Foo, hello world!'}}{{object.bar}}{{if}}";
assert_eq!(Template::new()
.template(source)
.value("object", object)
.render()
.unwrap(),
"Bar, hello world!");
}
#[test]
fn test_array_access() {
let array = vec!["hello", "world", "!"];
let source = "{{if array[0] == 'hello'}}{{array[1]}}{{array[2]}}{{if}}";
assert_eq!(Template::new()
.template(source)
.value("array", array)
.render()
.unwrap(),
"world!");
}
}
#[cfg(all(feature = "unstable", test))]
mod benches {
extern crate test;
use {Value, Template, to_value};
use std::collections::HashMap;
static SOURCE: &'static str = r#"<html>
<head>
<title>{{year}}</title>
</head>
<body>
<h1>{{year}}</h1>
<ul>
{{for item, index in items}}
<li class="{{if index == 0}}active{{if}}"> <b>{{item.name}}</b>
: {{item.score}}
</li>
{{for}}
</ul>
</body>
</html>"#;
fn create_items() -> Vec<HashMap<String, Value>> {
let mut items = Vec::new();
for tuple in &vec![("Abc", 100), ("Def", 105), ("Ghi", 150), ("Jkl", 510)] {
let (name, score) = *tuple;
let mut item = HashMap::new();
item.insert("name".to_owned(), to_value(name));
item.insert("score".to_owned(), to_value(score));
items.push(item);
}
items
}
#[bench]
fn bench_compile(b: &mut test::Bencher) {
b.iter(|| {
Template::new()
.template(SOURCE)
.compile()
.unwrap();
})
}
#[bench]
fn bench_render(b: &mut test::Bencher) {
let template = Template::new()
.template(SOURCE)
.value("year", 2016)
.value("items", create_items())
.compile()
.unwrap();
b.iter(|| template.render().unwrap())
}
#[bench]
fn bench_render_without_compile(b: &mut test::Bencher) {
let items = create_items();
b.iter(|| {
Template::new()
.template(SOURCE)
.value("year", 2016)
.value("items", items.clone())
.render()
.unwrap();
})
}
}