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
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
//! This module implements the bridge to handlebars.
use std::convert;
use std::io::Read;
use std::io::Result as IOResult;
use std::fs::File;
use std::path::PathBuf;
use std::error::Error;

use rustc_serialize::json::ToJson;
use handlebars::{RenderError, TemplateRenderError};

use app::Pencil;
use types::{PencilResult, PenUserError, UserError, PencilError};
use wrappers::Response;

impl convert::From<RenderError> for PencilError {
    fn from(err: RenderError) -> PencilError {
        PenUserError(UserError::new(err.description()))
    }
}

impl convert::From<TemplateRenderError> for PencilError {
    fn from(err: TemplateRenderError) -> PencilError {
        PenUserError(UserError::new(err.description()))
    }
}

pub fn render_template<T: ToJson>(app: &Pencil, template_name: &str, context: &T) -> PencilResult {
    let registry_read_rv = app.handlebars_registry.read();
    if registry_read_rv.is_err() {
        return Err(PenUserError(UserError::new("Can't acquire handlebars registry")));
    }
    let registry = registry_read_rv.unwrap();
    let rv = try!(registry.render(template_name, context));
    Ok(Response::from(rv))
}

pub fn render_template_string<T: ToJson>(app: &Pencil, source: &str, context: &T) -> PencilResult {
    let registry_read_rv = app.handlebars_registry.read();
    if registry_read_rv.is_err() {
        return Err(PenUserError(UserError::new("Can't acquire handlebars registry")));
    }
    let registry = registry_read_rv.unwrap();
    let rv = try!(registry.template_render(source, context));
    Ok(Response::from(rv))
}

/// The template loader trait allows for loading template source.
trait TemplateLoader {
    /// Get the template source for a template name.
    fn get_source(&self, template_name: &str) -> Option<IOResult<String>>;
}

/// A template loader that loads templates from the file system.
pub struct FileSystemLoader {
    search_path: String,
}

impl FileSystemLoader {
    /// Create one file system loader.
    /// This loader can find templates in folders on the file system.
    ///
    /// The loader takes the path to the templates:
    ///
    /// ```ignore
    /// let loader = FileSystemLoader::new("/path/to/templates");
    /// let source = loader.get_source("index.html");
    /// ```
    pub fn new(search_path: &str) -> FileSystemLoader {
        FileSystemLoader {
            search_path: search_path.to_string(),
        }
    }
}

impl TemplateLoader for FileSystemLoader {
    fn get_source(&self, template_name: &str) -> Option<IOResult<String>> {
        let mut pathbuf = PathBuf::from(&self.search_path);
        pathbuf.push(template_name);
        match File::open(&pathbuf.as_path()) {
            Ok(mut file) => {
                let mut s = String::new();
                match file.read_to_string(&mut s) {
                    Ok(_) => Some(Ok(s)),
                    Err(err) => Some(Err(err)),
                }
            },
            Err(_) => None
        }
    }
}

pub fn load_template(app: &Pencil, template_name: &str) -> Option<IOResult<String>> {
    let mut template_path = PathBuf::from(&app.root_path);
    template_path.push(&app.template_folder);
    let template_loader = FileSystemLoader::new(template_path.to_str().unwrap());
    if let Some(source) = template_loader.get_source(template_name) {
        return Some(source);
    }
    for module in app.modules.values() {
        if let Some(ref module_template_folder) = module.template_folder {
            let mut template_path = PathBuf::from(&module.root_path);
            template_path.push(module_template_folder);
            let template_loader = FileSystemLoader::new(template_path.to_str().unwrap());
            if let Some(source) = template_loader.get_source(template_name) {
                return Some(source);
            }
        }
    }
    None
}