(refactor) rework helpers based on new handlebars api

Signed-off-by: Ning Sun <sunng@about.me>
This commit is contained in:
Ning Sun 2017-06-03 14:06:09 +08:00
parent 2bb274d424
commit 6ee6da074e
2 changed files with 83 additions and 120 deletions

View File

@ -1,8 +1,8 @@
use std::path::Path; use std::path::Path;
use std::collections::{VecDeque, BTreeMap}; use std::collections::BTreeMap;
use serde_json; use serde_json;
use handlebars::{Handlebars, RenderError, RenderContext, Helper, Renderable}; use handlebars::{Handlebars, RenderError, RenderContext, Helper, Renderable, Context};
// Handlebars helper for navigation // Handlebars helper for navigation
@ -11,31 +11,22 @@ pub fn previous(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(
debug!("[fn]: previous (handlebars helper)"); debug!("[fn]: previous (handlebars helper)");
debug!("[*]: Get data from context"); debug!("[*]: Get data from context");
// get value from context data let chapters = rc.evaluate_absolute("chapters")
// rc.get_path() is current json parent path, you should always use it like this .and_then(|c| {
// param is the key of value you want to display serde_json::value::from_value::<Vec<BTreeMap<String, String>>>(c.clone())
let chapters = rc.context() .map_err(|_| RenderError::new("Could not decode the JSON data"))
.navigate(rc.get_path(), &VecDeque::new(), "chapters")? })?;
.to_owned();
let current = rc.context() let current = rc.evaluate_absolute("path")?
.navigate(rc.get_path(), &VecDeque::new(), "path")? .as_str()
.to_string() .ok_or(RenderError::new("Type error for `path`, string expected"))?
.replace("\"", ""); .replace("\"", "");
debug!("[*]: Decode chapters from JSON");
// Decode json format
let decoded: Vec<BTreeMap<String, String>> = match serde_json::from_str(&chapters.to_string()) {
Ok(data) => data,
Err(_) => return Err(RenderError::new("Could not decode the JSON data")),
};
let mut previous: Option<BTreeMap<String, String>> = None; let mut previous: Option<BTreeMap<String, String>> = None;
debug!("[*]: Search for current Chapter"); debug!("[*]: Search for current Chapter");
// Search for current chapter and return previous entry // Search for current chapter and return previous entry
for item in decoded { for item in chapters {
match item.get("path") { match item.get("path") {
Some(path) if !path.is_empty() => { Some(path) if !path.is_empty() => {
@ -49,51 +40,41 @@ pub fn previous(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(
let mut previous_chapter = BTreeMap::new(); let mut previous_chapter = BTreeMap::new();
// Chapter title // Chapter title
match previous.get("name") { previous
Some(n) => { .get("name")
debug!("[*]: Inserting title: {}", n); .ok_or(RenderError::new("No title found for chapter in JSON data"))
previous_chapter.insert("title".to_owned(), json!(n)) .and_then(|n| {
}, previous_chapter.insert("title".to_owned(), json!(n));
None => { Ok(())
debug!("[*]: No title found for chapter"); })?;
return Err(RenderError::new("No title found for chapter in JSON data"));
},
};
// Chapter link // Chapter link
previous
.get("path")
.ok_or(RenderError::new("No path found for chapter in JSON data"))
.and_then(|p| {
Path::new(p)
.with_extension("html")
.to_str()
.ok_or(RenderError::new("Link could not be converted to str"))
.and_then(|p| {
previous_chapter
.insert("link".to_owned(), json!(p.replace("\\", "/")));
Ok(())
})
})?;
match previous.get("path") {
Some(p) => {
// Hack for windows who tends to use `\` as separator instead of `/`
let path = Path::new(p).with_extension("html");
debug!("[*]: Inserting link: {:?}", path);
match path.to_str() {
Some(p) => {
previous_chapter.insert("link".to_owned(), json!(p.replace("\\", "/")));
},
None => return Err(RenderError::new("Link could not be converted to str")),
}
},
None => return Err(RenderError::new("No path found for chapter in JSON data")),
}
debug!("[*]: Inject in context");
// Inject in current context
let updated_context = rc.context().extend(&previous_chapter);
debug!("[*]: Render template"); debug!("[*]: Render template");
// Render template // Render template
match _h.template() { _h.template()
Some(t) => { .ok_or(RenderError::new("Error with the handlebars template"))
*rc.context_mut() = updated_context; .and_then(|t| {
t.render(r, rc)?; let mut local_rc = rc.with_context(Context::wraps(&previous_chapter));
}, t.render(r, &mut local_rc)
None => return Err(RenderError::new("Error with the handlebars template")), })?;
}
} }
break; break;
} else { } else {
previous = Some(item.clone()); previous = Some(item.clone());
@ -102,7 +83,6 @@ pub fn previous(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(
_ => continue, _ => continue,
} }
} }
Ok(()) Ok(())
@ -115,29 +95,21 @@ pub fn next(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), R
debug!("[fn]: next (handlebars helper)"); debug!("[fn]: next (handlebars helper)");
debug!("[*]: Get data from context"); debug!("[*]: Get data from context");
// get value from context data let chapters = rc.evaluate_absolute("chapters")
// rc.get_path() is current json parent path, you should always use it like this .and_then(|c| {
// param is the key of value you want to display serde_json::value::from_value::<Vec<BTreeMap<String, String>>>(c.clone())
let chapters = rc.context() .map_err(|_| RenderError::new("Could not decode the JSON data"))
.navigate(rc.get_path(), &VecDeque::new(), "chapters")? })?;
.to_owned(); let current = rc.evaluate_absolute("path")?
.as_str()
let current = rc.context() .ok_or(RenderError::new("Type error for `path`, string expected"))?
.navigate(rc.get_path(), &VecDeque::new(), "path")?
.to_string()
.replace("\"", ""); .replace("\"", "");
debug!("[*]: Decode chapters from JSON");
// Decode json format
let decoded: Vec<BTreeMap<String, String>> = match serde_json::from_str(&chapters.to_string()) {
Ok(data) => data,
Err(_) => return Err(RenderError::new("Could not decode the JSON data")),
};
let mut previous: Option<BTreeMap<String, String>> = None; let mut previous: Option<BTreeMap<String, String>> = None;
debug!("[*]: Search for current Chapter"); debug!("[*]: Search for current Chapter");
// Search for current chapter and return previous entry // Search for current chapter and return previous entry
for item in decoded { for item in chapters {
match item.get("path") { match item.get("path") {
@ -145,10 +117,9 @@ pub fn next(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), R
if let Some(previous) = previous { if let Some(previous) = previous {
let previous_path = match previous.get("path") { let previous_path = previous
Some(p) => p, .get("path")
None => return Err(RenderError::new("No path found for chapter in JSON data")), .ok_or(RenderError::new("No path found for chapter in JSON data"))?;
};
if previous_path == &current { if previous_path == &current {
@ -157,41 +128,33 @@ pub fn next(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), R
// Create new BTreeMap to extend the context: 'title' and 'link' // Create new BTreeMap to extend the context: 'title' and 'link'
let mut next_chapter = BTreeMap::new(); let mut next_chapter = BTreeMap::new();
match item.get("name") { item.get("name")
Some(n) => { .ok_or(RenderError::new("No title found for chapter in JSON data"))
debug!("[*]: Inserting title: {}", n); .and_then(|n| {
next_chapter.insert("title".to_owned(), json!(n)); next_chapter.insert("title".to_owned(), json!(n));
}, Ok(())
None => return Err(RenderError::new("No title found for chapter in JSON data")), })?;
}
Path::new(path)
let link = Path::new(path).with_extension("html"); .with_extension("html")
debug!("[*]: Inserting link: {:?}", link); .to_str()
.ok_or(RenderError::new("Link could not converted to str"))
match link.to_str() { .and_then(|l| {
Some(l) => { debug!("[*]: Inserting link: {:?}", l);
// Hack for windows who tends to use `\` as separator instead of `/` // Hack for windows who tends to use `\` as separator instead of `/`
next_chapter.insert("link".to_owned(), json!(l.replace("\\", "/"))); next_chapter.insert("link".to_owned(), json!(l.replace("\\", "/")));
}, Ok(())
None => return Err(RenderError::new("Link could not converted to str")), })?;
}
debug!("[*]: Inject in context");
// Inject in current context
let updated_context = rc.context().extend(&next_chapter);
debug!("[*]: Render template"); debug!("[*]: Render template");
// Render template // Render template
match _h.template() { _h.template()
Some(t) => { .ok_or(RenderError::new("Error with the handlebars template"))
*rc.context_mut() = updated_context; .and_then(|t| {
t.render(r, rc)?; let mut local_rc = rc.with_context(Context::wraps(&next_chapter));
}, t.render(r, &mut local_rc)
None => return Err(RenderError::new("Error with the handlebars template")), })?;
}
break; break;
} }
} }

View File

@ -15,21 +15,21 @@ impl HelperDef for RenderToc {
// get value from context data // get value from context data
// rc.get_path() is current json parent path, you should always use it like this // rc.get_path() is current json parent path, you should always use it like this
// param is the key of value you want to display // param is the key of value you want to display
let chapters = rc.context() let chapters = rc.evaluate_absolute("chapters")
.navigate(rc.get_path(), &VecDeque::new(), "chapters")? .and_then(|c| {
.to_owned(); serde_json::value::from_value::<Vec<BTreeMap<String, String>>>(c.clone())
let current = rc.context() .map_err(|_| RenderError::new("Could not decode the JSON data"))
.navigate(rc.get_path(), &VecDeque::new(), "path") })?;
.to_string() let current = rc.evaluate_absolute("path")?
.as_str()
.ok_or(RenderError::new("Type error for `path`, string expected"))?
.replace("\"", ""); .replace("\"", "");
rc.writer.write_all("<ul class=\"chapter\">".as_bytes())?;
// Decode json format rc.writer.write_all("<ul class=\"chapter\">".as_bytes())?;
let decoded: Vec<BTreeMap<String, String>> = serde_json::from_str(&chapters.to_string()).unwrap();
let mut current_level = 1; let mut current_level = 1;
for item in decoded { for item in chatpers {
// Spacer // Spacer
if item.get("spacer").is_some() { if item.get("spacer").is_some() {