Skip to content

Commit

Permalink
Merge pull request sunng87#19 from sunng87/feature/sources-api
Browse files Browse the repository at this point in the history
Source API
  • Loading branch information
sunng87 committed Dec 8, 2015
2 parents fadb9ca + 40b2ae2 commit 849ff15
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 84 deletions.
12 changes: 10 additions & 2 deletions examples/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ extern crate rustc_serialize;
use std::error::Error;
use iron::prelude::*;
use iron::{status, AfterMiddleware};
use hbs::{Template, HandlebarsEngine};
use hbs::{Template, HandlebarsEngine, DirectorySource};

/// the handler
fn hello_world(_: &mut Request) -> IronResult<Response> {
Expand All @@ -27,7 +27,15 @@ impl AfterMiddleware for ErrorReporter {

fn main() {
let mut chain = Chain::new(hello_world);
chain.link_after(HandlebarsEngine::new("./examples/templates/", ".hbs"));
let mut hbse = HandlebarsEngine::new2();
hbse.add(Box::new(DirectorySource::new("./examples/templates/", ".hbs")));
// success of panic
if let Err(r) = hbse.reload() {
panic!("{}", r.description());
}


chain.link_after(hbse);
chain.link_after(ErrorReporter);
println!("Server running at http://localhost:3000/");
Iron::new(chain).http("localhost:3000").unwrap();
Expand Down
14 changes: 11 additions & 3 deletions examples/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ extern crate env_logger;
extern crate handlebars_iron as hbs;
extern crate rustc_serialize;

use std::error::Error;
use std::collections::BTreeMap;

use iron::prelude::*;
use iron::{status};
use hbs::{Template, HandlebarsEngine};
use hbs::{Template, HandlebarsEngine, DirectorySource};
use rustc_serialize::json::{ToJson, Json};
use std::collections::BTreeMap;

struct Team {
name: String,
Expand Down Expand Up @@ -54,7 +56,13 @@ fn main() {
env_logger::init().unwrap();

let mut chain = Chain::new(hello_world);
chain.link_after(HandlebarsEngine::new("./examples/templates/", ".hbs"));
let mut hbse = HandlebarsEngine::new2();
hbse.add(Box::new(DirectorySource::new("./examples/templates/", ".hbs")));
if let Err(r) = hbse.reload() {
panic!("{}", r.description());
}

chain.link_after(hbse);
println!("Server running at http://localhost:3000/");
Iron::new(chain).http("localhost:3000").unwrap();
}
18 changes: 14 additions & 4 deletions examples/watch_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ extern crate env_logger;

use iron::prelude::*;
use iron::{status};
use hbs::{Template, HandlebarsEngine};
use hbs::{Template, HandlebarsEngine, DirectorySource};
#[cfg(feature = "watch")]
use hbs::Watchable;
use rustc_serialize::json::{ToJson, Json};

use std::collections::BTreeMap;
use std::sync::Arc;
use std::error::Error;


struct Team {
Expand Down Expand Up @@ -60,10 +62,18 @@ fn main() {
env_logger::init().unwrap();

let mut chain = Chain::new(hello_world);
let template_engine_ref = Arc::new(HandlebarsEngine::new("./examples/templates/", ".hbs"));
template_engine_ref.watch();

chain.link_after(template_engine_ref);
let mut hbse = HandlebarsEngine::new2();
let source = Box::new(DirectorySource::new("./examples/templates/", ".hbs"));
hbse.add(source);
if let Err(r) = hbse.reload() {
panic!("{}", r.description());
}

let hbse_ref = Arc::new(hbse);
hbse_ref.watch("./examples/templates/");

chain.link_after(hbse_ref);

println!("Server running at http://localhost:3000/");
Iron::new(chain).http("localhost:3000").unwrap();
Expand Down
6 changes: 5 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ extern crate notify;
#[macro_use]
extern crate log;


pub use self::middleware::Template;
pub use self::middleware::HandlebarsEngine;
pub use self::source::{Source, SourceError};
pub use self::sources::directory::DirectorySource;
pub use self::sources::memory::MemorySource;
#[cfg(feature = "watch")]
pub use self::watch::Watchable;

mod middleware;
#[cfg(feature = "watch")]
mod watch;
mod source;
mod sources;
112 changes: 46 additions & 66 deletions src/middleware.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
use std::fs::File;
use std::env::current_dir;
use std::path::{Path, PathBuf};
use std::io::prelude::*;
use std::sync::RwLock;
use std::error::Error;

Expand All @@ -11,11 +7,13 @@ use iron::{AfterMiddleware, typemap};
use iron::modifier::Modifier;
use plugin::Plugin as PluginFor;
use iron::headers::ContentType;
use walkdir::{WalkDir, DirEntry};

use handlebars::Handlebars;
use serialize::json::{ToJson, Json};

use ::source::{Source, SourceError};
use ::sources::directory::{DirectorySource};

#[derive(Clone)]
pub struct Template {
name: String,
Expand All @@ -32,8 +30,7 @@ impl Template {
}

pub struct HandlebarsEngine {
pub prefix: String,
pub suffix: String,
pub sources: Vec<Box<Source + Send + Sync>>,
pub registry: RwLock<Box<Handlebars>>
}

Expand All @@ -58,74 +55,57 @@ impl PluginFor<Response> for HandlebarsEngine {
}
}

fn read_file(path: &Path) -> Option<String> {
if let Ok(mut file) = File::open(path) {
let mut buf = String::new();
if file.read_to_string(&mut buf).is_ok() {
Some(buf)
} else {
info!("Failed to read file {}, skipped", path.display());
None
}
} else {
info!("Failed to open file {}, skipped.", path.display());
None
}
}

fn filter_file(entry: &DirEntry, suffix: &str) -> bool {
entry.file_name().to_str()
.and_then(|s| Some(s.starts_with(".") || s.starts_with("#") || !s.ends_with(suffix)))
.unwrap_or(false)
}

impl HandlebarsEngine {
pub fn reload(&self) {
let mut hbs = self.registry.write().unwrap();
match current_dir() {
Ok(current_path) => {
let mut prefix_path = PathBuf::from(current_path);
prefix_path.push(self.prefix.clone());
let template_path = prefix_path.as_path();

info!("Loading templates from path {}", template_path.display());
let walker = WalkDir::new(template_path);

hbs.clear_templates();
let suffix = &self.suffix;
let prefix_len = template_path.as_os_str().to_str().unwrap().len();
for p in walker.min_depth(1).into_iter().filter(|e| e.is_ok() && !filter_file(e.as_ref().unwrap(), suffix)).map(|e| e.unwrap()) {
let path = p.path();
let disp = path.to_str().unwrap();
debug!("getting file {}", disp);
let tpl_name = &disp[prefix_len .. disp.len()-suffix.len()];
if let Some(tpl) = read_file(&path) {
if let Err(e) = hbs.register_template_string(tpl_name, tpl){
warn!("Failed to parse template {}, {}", tpl_name, e);
} else {
info!("Added template {}", tpl_name);
}
}
}
},
/// #[Deprecated], for backward compaitibility only
pub fn new(prefix: &str, suffix: &str) -> HandlebarsEngine {
let mut hbs = HandlebarsEngine::new2();
hbs.add(Box::new(DirectorySource::new(prefix, suffix)));
match hbs.reload() {
Ok(_) => hbs,
Err(e) => {
error!("Failed to get current directory due to {}", e);
panic!("Failed to init from directory: {}", e.description())
}
}
}

/// #[Deprecated], for backward compaitibility only
pub fn from(prefix: &str, suffix: &str, custom: Handlebars) -> HandlebarsEngine {
let eng = HandlebarsEngine {
prefix: prefix.to_string(),
suffix: suffix.to_string(),
registry: RwLock::new(Box::new(custom))
};
eng.reload();
eng
let mut hbs = HandlebarsEngine::from2(custom);
hbs.add(Box::new(DirectorySource::new(prefix, suffix)));
match hbs.reload() {
Ok(_) => hbs,
Err(e) => {
panic!("Failed to init from directory: {}", e.description())
}
}
}

pub fn new(prefix: &str, suffix: &str) -> HandlebarsEngine {
HandlebarsEngine::from(prefix, suffix, Handlebars::new())
pub fn new2() -> HandlebarsEngine {
HandlebarsEngine {
sources: Vec::new(),
registry: RwLock::new(Box::new(Handlebars::new()))
}
}

pub fn from2(reg: Handlebars) -> HandlebarsEngine {
HandlebarsEngine {
sources: Vec::new(),
registry: RwLock::new(Box::new(reg))
}
}

pub fn add(&mut self, source: Box<Source + Send + Sync>) {
self.sources.push(source);
}

pub fn reload(&self) -> Result<(), SourceError> {
let mut hbs = self.registry.write().unwrap();
hbs.clear_templates();
for s in self.sources.iter() {
try!(s.load(&mut hbs))
}
Ok(())
}
}

Expand Down Expand Up @@ -197,7 +177,7 @@ mod test {

#[test]
fn test_register_helper() {
let hbs = HandlebarsEngine::new("./examples/templates", ".hbs");
let hbs = HandlebarsEngine::new2();
let mut reg = hbs.registry.write().unwrap();
reg.register_helper("ignore", Box::new(|_: &Context, _: &Helper, _: &Handlebars, _: &mut RenderContext| -> Result<(), RenderError> {
Ok(())
Expand Down
28 changes: 28 additions & 0 deletions src/source.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use handlebars::Handlebars;
use std::error::Error;
use std::fmt;

#[derive(Debug)]
pub struct SourceError {
pub cause: Box<Error>
}

impl fmt::Display for SourceError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt::Display::fmt(&*self.cause, f)
}
}

impl Error for SourceError {
fn description(&self) -> &str {
self.cause.description()
}

fn cause(&self) -> Option<&Error> {
self.cause.cause()
}
}

pub trait Source {
fn load(&self, reg: &mut Handlebars) -> Result<(), SourceError>;
}
79 changes: 79 additions & 0 deletions src/sources/directory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use ::source::{Source, SourceError};

use std::io::prelude::*;
use std::fs::File;
use std::env::current_dir;
use std::path::{Path, PathBuf};

use handlebars::Handlebars;
use walkdir::{WalkDir, DirEntry};

pub struct DirectorySource {
pub prefix: String,
pub suffix: String
}

impl DirectorySource {
pub fn new(prefix: &str, suffix: &str) -> DirectorySource {
DirectorySource {
prefix: prefix.to_owned(),
suffix: suffix.to_owned()
}
}
}

fn read_file(path: &Path) -> Option<String> {
if let Ok(mut file) = File::open(path) {
let mut buf = String::new();
if file.read_to_string(&mut buf).is_ok() {
Some(buf)
} else {
info!("Failed to read file {}, skipped", path.display());
None
}
} else {
info!("Failed to open file {}, skipped.", path.display());
None
}
}

fn filter_file(entry: &DirEntry, suffix: &str) -> bool {
entry.file_name().to_str()
.and_then(|s| Some(s.starts_with(".") || s.starts_with("#") || !s.ends_with(suffix)))
.unwrap_or(false)
}

impl Source for DirectorySource {
fn load(&self, reg: &mut Handlebars) -> Result<(), SourceError> {
match current_dir() {
Ok(current_path) => {
let mut prefix_path = PathBuf::from(current_path);
prefix_path.push(self.prefix.clone());
let template_path = prefix_path.as_path();

info!("Loading templates from path {}", template_path.display());
let walker = WalkDir::new(template_path);

let suffix = &self.suffix;
let prefix_len = template_path.as_os_str().to_str().unwrap().len();
for p in walker.min_depth(1).into_iter().filter(|e| e.is_ok() && !filter_file(e.as_ref().unwrap(), suffix)).map(|e| e.unwrap()) {
let path = p.path();
let disp = path.to_str().unwrap();
debug!("getting file {}", disp);
let tpl_name = &disp[prefix_len .. disp.len()-suffix.len()];
if let Some(tpl) = read_file(&path) {
if let Err(e) = reg.register_template_string(tpl_name, tpl){
warn!("Failed to parse template {}, {}", tpl_name, e);
} else {
info!("Added template {}", tpl_name);
}
}
}
Ok(())
},
Err(e) => {
Err(SourceError{ cause: Box::new(e) })
}
}
}
}
18 changes: 18 additions & 0 deletions src/sources/memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use ::source::{Source, SourceError};
use std::collections::BTreeMap;
use handlebars::Handlebars;

pub struct MemorySource(BTreeMap<String, String>);

impl Source for MemorySource {
fn load(&self, reg: &mut Handlebars) -> Result<(), SourceError> {
for (name, tpl) in self.0.iter() {
if let Err(e) = reg.register_template_string(name, tpl.clone()){
warn!("Failed to parse template {}, {}", name, e);
} else {
info!("Added template {}", name);
}
}
Ok(())
}
}
Loading

0 comments on commit 849ff15

Please sign in to comment.