mage 0.2.0

An intuitive and powerful template engine.
Documentation

use {Context, Contexts, Function, Functions, utils};
use {Value, to_value};
use serde::Serialize;
use std::path::{Path, PathBuf};
use error::Error;
use tree::Tree;
use Compiled;
use ExpressionError;


/// Template builder
#[derive(Default)]
pub struct Template {
    template: Option<String>,
    root: Option<PathBuf>,
    extension: Option<String>,
    context: Option<Context>,
    compiled: Option<Compiled>,
    contexts: Contexts,
    functions: Functions,
}

impl Template {
    /// Create a template builder.
    pub fn new() -> Template {
        Template { contexts: create_empty_contexts(), ..Default::default() }
    }

    /// Set temlate from string.
    pub fn template<T: Into<String>>(mut self, template: T) -> Template {
        self.template = Some(template.into());
        self
    }

    /// Open a template file.
    pub fn open<P: AsRef<Path>>(mut self, path: P) -> Result<Template, Error> {
        self.template = Some(utils::read(self.resolve_view_path(path.as_ref()))?);
        Ok(self)
    }

    /// Set context.
    pub fn context(mut self, context: Context) -> Template {
        self.context = Some(context);
        self
    }

    /// Set template's root path.
    pub fn root<P: Into<PathBuf>>(mut self, root: P) -> Template {
        self.root = Some(root.into());
        self
    }

    /// Set template's extension.
    pub fn extension<T: Into<String>>(mut self, extension: T) -> Template {
        self.extension = Some(extension.into());
        self
    }

    /// Add value to context.
    pub fn value<T, V>(mut self, name: T, value: V) -> Template
        where T: Into<String>,
              V: Serialize
    {
        self.contexts.first_mut().unwrap().insert(name.into(), to_value(&value));
        self
    }

    /// Add function to context.
    pub fn function<T, F>(mut self, name: T, function: F) -> Template
        where T: Into<String>,
              F: 'static + Fn(Vec<Value>) -> Result<Value, ExpressionError> + Sync + Send
    {
        self.functions.insert(name.into(), Function::new(function));
        self
    }

    /// Compile an template.
    /// An template can be compiled only once and then render multiple times with different context and function.
    /// You can also render a template without compile.
    pub fn compile(mut self) -> Result<Template, Error> {
        self.compiled = Some(Tree::new(self.template.clone().unwrap(),
                                       self.root.clone(),
                                       self.extension.clone()).compile()?);
        Ok(self)
    }

    /// Render.
    pub fn render(&self) -> Result<String, Error> {
        let mut contexts = self.contexts.clone();
        if self.context.is_some() {
            contexts.push(self.context.clone().unwrap());
        }

        if self.compiled.is_none() {
            Tree::new(self.template.clone().unwrap(),
                      self.root.clone(),
                      self.extension.clone()).compile()?(contexts, &self.functions)
        } else {
            (self.compiled.as_ref().unwrap())(contexts, &self.functions)
        }
    }

    fn resolve_view_path(&self, view: &Path) -> PathBuf {
        let mut absolute_path = if self.root.is_some() {
            utils::resolve(self.root.as_ref().unwrap(), view)
        } else {
            PathBuf::from(view)
        };

        if self.extension.is_some() {
            absolute_path.set_extension(self.extension.as_ref().unwrap());
        }

        absolute_path
    }

    fn get_compiled(&self) -> Option<&Compiled> {
        self.compiled.as_ref()
    }
}

/// Render options
pub struct RenderOptions<'a> {
    template: &'a Template,
    context: Option<Context>,
    functions: Option<&'a Functions>,
}

impl<'a> RenderOptions<'a> {
    /// Create new render options.
    pub fn new(template: &'a Template) -> RenderOptions<'a> {
        RenderOptions {
            template: template,
            context: None,
            functions: None,
        }
    }

    /// Set context.
    pub fn context(&mut self, context: Context) -> &mut RenderOptions<'a> {
        self.context = Some(context);
        self
    }

    /// Set functions.
    pub fn functions(&mut self, functions: &'a Functions) -> &mut RenderOptions<'a> {
        self.functions = Some(functions);
        self
    }

    /// Render.
    pub fn render(&mut self) -> Result<String, Error> {
        let contexts = if self.context.is_some() {
            vec![self.context.take().unwrap()]
        } else {
            create_empty_contexts()
        };

        let empty_functions = Functions::new();
        let functions = if self.functions.is_some() {
            self.functions.unwrap()
        } else {
            &empty_functions
        };

        self.template.get_compiled().unwrap()(contexts, functions)
    }
}


fn create_empty_contexts() -> Contexts {
    let mut contexts = Contexts::new();
    contexts.push(Context::new());
    contexts
}