From 6ee6da074ec2a60b76ba5cacd42ff416762fa46b Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Sat, 3 Jun 2017 14:06:09 +0800 Subject: [PATCH] (refactor) rework helpers based on new handlebars api Signed-off-by: Ning Sun --- .../html_handlebars/helpers/navigation.rs | 183 +++++++----------- src/renderer/html_handlebars/helpers/toc.rs | 20 +- 2 files changed, 83 insertions(+), 120 deletions(-) diff --git a/src/renderer/html_handlebars/helpers/navigation.rs b/src/renderer/html_handlebars/helpers/navigation.rs index e90254b5..a49db8b5 100644 --- a/src/renderer/html_handlebars/helpers/navigation.rs +++ b/src/renderer/html_handlebars/helpers/navigation.rs @@ -1,8 +1,8 @@ use std::path::Path; -use std::collections::{VecDeque, BTreeMap}; +use std::collections::BTreeMap; use serde_json; -use handlebars::{Handlebars, RenderError, RenderContext, Helper, Renderable}; +use handlebars::{Handlebars, RenderError, RenderContext, Helper, Renderable, Context}; // 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!("[*]: Get data from context"); - // get value from context data - // 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 - let chapters = rc.context() - .navigate(rc.get_path(), &VecDeque::new(), "chapters")? - .to_owned(); + let chapters = rc.evaluate_absolute("chapters") + .and_then(|c| { + serde_json::value::from_value::>>(c.clone()) + .map_err(|_| RenderError::new("Could not decode the JSON data")) + })?; - let current = rc.context() - .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("\"", ""); - - debug!("[*]: Decode chapters from JSON"); - // Decode json format - let decoded: Vec> = 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> = None; - debug!("[*]: Search for current Chapter"); // Search for current chapter and return previous entry - for item in decoded { + for item in chapters { match item.get("path") { 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(); // Chapter title - match previous.get("name") { - Some(n) => { - debug!("[*]: Inserting title: {}", n); - previous_chapter.insert("title".to_owned(), json!(n)) - }, - None => { - debug!("[*]: No title found for chapter"); - return Err(RenderError::new("No title found for chapter in JSON data")); - }, - }; + previous + .get("name") + .ok_or(RenderError::new("No title found for chapter in JSON data")) + .and_then(|n| { + previous_chapter.insert("title".to_owned(), json!(n)); + Ok(()) + })?; + // 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"); // Render template - match _h.template() { - Some(t) => { - *rc.context_mut() = updated_context; - t.render(r, rc)?; - }, - None => return Err(RenderError::new("Error with the handlebars template")), - } - + _h.template() + .ok_or(RenderError::new("Error with the handlebars template")) + .and_then(|t| { + let mut local_rc = rc.with_context(Context::wraps(&previous_chapter)); + t.render(r, &mut local_rc) + })?; } - break; } else { previous = Some(item.clone()); @@ -102,7 +83,6 @@ pub fn previous(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<( _ => continue, } - } Ok(()) @@ -115,29 +95,21 @@ pub fn next(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), R debug!("[fn]: next (handlebars helper)"); debug!("[*]: Get data from context"); - // get value from context data - // 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 - let chapters = rc.context() - .navigate(rc.get_path(), &VecDeque::new(), "chapters")? - .to_owned(); - - let current = rc.context() - .navigate(rc.get_path(), &VecDeque::new(), "path")? - .to_string() + let chapters = rc.evaluate_absolute("chapters") + .and_then(|c| { + serde_json::value::from_value::>>(c.clone()) + .map_err(|_| RenderError::new("Could not decode the JSON data")) + })?; + let current = rc.evaluate_absolute("path")? + .as_str() + .ok_or(RenderError::new("Type error for `path`, string expected"))? .replace("\"", ""); - debug!("[*]: Decode chapters from JSON"); - // Decode json format - let decoded: Vec> = 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> = None; debug!("[*]: Search for current Chapter"); // Search for current chapter and return previous entry - for item in decoded { + for item in chapters { 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 { - let previous_path = match previous.get("path") { - Some(p) => p, - None => return Err(RenderError::new("No path found for chapter in JSON data")), - }; + let previous_path = previous + .get("path") + .ok_or(RenderError::new("No path found for chapter in JSON data"))?; if previous_path == ¤t { @@ -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' let mut next_chapter = BTreeMap::new(); - match item.get("name") { - Some(n) => { - debug!("[*]: Inserting title: {}", n); - next_chapter.insert("title".to_owned(), json!(n)); - }, - None => return Err(RenderError::new("No title found for chapter in JSON data")), - } + item.get("name") + .ok_or(RenderError::new("No title found for chapter in JSON data")) + .and_then(|n| { + next_chapter.insert("title".to_owned(), json!(n)); + Ok(()) + })?; - - let link = Path::new(path).with_extension("html"); - debug!("[*]: Inserting link: {:?}", link); - - match link.to_str() { - Some(l) => { - // Hack for windows who tends to use `\` as separator instead of `/` - next_chapter.insert("link".to_owned(), json!(l.replace("\\", "/"))); - }, - 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); + Path::new(path) + .with_extension("html") + .to_str() + .ok_or(RenderError::new("Link could not converted to str")) + .and_then(|l| { + debug!("[*]: Inserting link: {:?}", l); + // Hack for windows who tends to use `\` as separator instead of `/` + next_chapter.insert("link".to_owned(), json!(l.replace("\\", "/"))); + Ok(()) + })?; debug!("[*]: Render template"); // Render template - match _h.template() { - Some(t) => { - *rc.context_mut() = updated_context; - t.render(r, rc)?; - }, - None => return Err(RenderError::new("Error with the handlebars template")), - } - + _h.template() + .ok_or(RenderError::new("Error with the handlebars template")) + .and_then(|t| { + let mut local_rc = rc.with_context(Context::wraps(&next_chapter)); + t.render(r, &mut local_rc) + })?; break; } } diff --git a/src/renderer/html_handlebars/helpers/toc.rs b/src/renderer/html_handlebars/helpers/toc.rs index 047c120b..2bed6681 100644 --- a/src/renderer/html_handlebars/helpers/toc.rs +++ b/src/renderer/html_handlebars/helpers/toc.rs @@ -15,21 +15,21 @@ impl HelperDef for RenderToc { // get value from context data // 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 - let chapters = rc.context() - .navigate(rc.get_path(), &VecDeque::new(), "chapters")? - .to_owned(); - let current = rc.context() - .navigate(rc.get_path(), &VecDeque::new(), "path") - .to_string() + let chapters = rc.evaluate_absolute("chapters") + .and_then(|c| { + serde_json::value::from_value::>>(c.clone()) + .map_err(|_| RenderError::new("Could not decode the JSON data")) + })?; + let current = rc.evaluate_absolute("path")? + .as_str() + .ok_or(RenderError::new("Type error for `path`, string expected"))? .replace("\"", ""); - rc.writer.write_all("
    ".as_bytes())?; - // Decode json format - let decoded: Vec> = serde_json::from_str(&chapters.to_string()).unwrap(); + rc.writer.write_all("
      ".as_bytes())?; let mut current_level = 1; - for item in decoded { + for item in chatpers { // Spacer if item.get("spacer").is_some() {