use std::path::Path; use std::collections::BTreeMap; use utils; use serde_json; use handlebars::{Handlebars, Helper, HelperDef, RenderContext, RenderError}; use pulldown_cmark::{html, Event, Parser, Tag}; // Handlebars helper to construct TOC #[derive(Clone, Copy)] pub struct RenderToc { pub no_section_label: bool, } impl HelperDef for RenderToc { fn call(&self, _h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> { // 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.evaluate_absolute("chapters", true).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", true)? .as_str() .ok_or_else(|| RenderError::new("Type error for `path`, string expected"))? .replace("\"", ""); rc.writer.write_all(b"
    ")?; let mut current_level = 1; for item in chapters { // Spacer if item.get("spacer").is_some() { rc.writer.write_all(b"
  1. ")?; continue; } let level = if let Some(s) = item.get("section") { s.matches('.').count() } else { 1 }; if level > current_level { while level > current_level { rc.writer.write_all(b"
  2. ")?; rc.writer.write_all(b"
      ")?; current_level += 1; } rc.writer.write_all(b"
    1. ")?; } else if level < current_level { while level < current_level { rc.writer.write_all(b"
    ")?; rc.writer.write_all(b"
  3. ")?; current_level -= 1; } rc.writer.write_all(b"
  4. ")?; } else { rc.writer.write_all(b"")?; } // Link let path_exists = if let Some(path) = item.get("path") { if !path.is_empty() { rc.writer.write_all(b"")?; true } else { false } } else { false }; if !self.no_section_label { // Section does not necessarily exist if let Some(section) = item.get("section") { rc.writer.write_all(b"")?; rc.writer.write_all(section.as_bytes())?; rc.writer.write_all(b" ")?; } } if let Some(name) = item.get("name") { // Render only inline code blocks // filter all events that are not inline code blocks let parser = Parser::new(name).filter(|event| match *event { Event::Start(Tag::Code) | Event::End(Tag::Code) | Event::InlineHtml(_) | Event::Text(_) => true, _ => false, }); // render markdown to html let mut markdown_parsed_name = String::with_capacity(name.len() * 3 / 2); html::push_html(&mut markdown_parsed_name, parser); // write to the handlebars template rc.writer.write_all(markdown_parsed_name.as_bytes())?; } if path_exists { rc.writer.write_all(b"")?; } rc.writer.write_all(b"
  5. ")?; } while current_level > 1 { rc.writer.write_all(b"
")?; rc.writer.write_all(b"")?; current_level -= 1; } rc.writer.write_all(b"")?; Ok(()) } }