1 use std
::collections
::BTreeMap
;
6 use handlebars
::{Context, Handlebars, Helper, HelperDef, Output, RenderContext, RenderError}
;
7 use pulldown_cmark
::{html, Event, Parser, Tag}
;
10 // Handlebars helper to construct TOC
11 #[derive(Clone, Copy)]
12 pub struct RenderToc
{
13 pub no_section_label
: bool
,
16 impl HelperDef
for RenderToc
{
17 fn call
<'reg
: 'rc
, 'rc
>(
22 rc
: &mut RenderContext
,
24 ) -> Result
<(), RenderError
> {
25 // get value from context data
26 // rc.get_path() is current json parent path, you should always use it like this
27 // param is the key of value you want to display
28 let chapters
= rc
.evaluate_absolute(ctx
, "chapters", true).and_then(|c
| {
29 serde_json
::value
::from_value
::<Vec
<BTreeMap
<String
, String
>>>(c
.clone())
30 .map_err(|_
| RenderError
::new("Could not decode the JSON data"))
33 .evaluate_absolute(ctx
, "path", true)?
35 .ok_or_else(|| RenderError
::new("Type error for `path`, string expected"))?
38 out
.write("<ol class=\"chapter\">")?
;
40 let mut current_level
= 1;
42 for item
in chapters
{
44 if item
.get("spacer").is_some() {
45 out
.write("<li class=\"spacer\"></li>")?
;
49 let level
= if let Some(s
) = item
.get("section") {
50 s
.matches('
.'
).count()
55 if level
> current_level
{
56 while level
> current_level
{
58 out
.write("<ol class=\"section\">")?
;
62 } else if level
< current_level
{
63 while level
< current_level
{
71 if item
.get("section").is_none() {
72 out
.write(" class=\"affix\"")?
;
78 let path_exists
= if let Some(path
) = item
.get("path") {
80 out
.write("<a href=\"")?
;
82 let tmp
= Path
::new(item
.get("path").expect("Error: path should be Some(_)"))
83 .with_extension("html")
86 // Hack for windows who tends to use `\` as separator instead of `/`
90 out
.write(&utils
::fs
::path_to_root(¤t
))?
;
95 out
.write(" class=\"active\"")?
;
107 if !self.no_section_label
{
108 // Section does not necessarily exist
109 if let Some(section
) = item
.get("section") {
110 out
.write("<strong aria-hidden=\"true\">")?
;
111 out
.write(§ion
)?
;
112 out
.write("</strong> ")?
;
116 if let Some(name
) = item
.get("name") {
117 // Render only inline code blocks
119 // filter all events that are not inline code blocks
120 let parser
= Parser
::new(name
).filter(|event
| match *event
{
121 Event
::Start(Tag
::Code
)
122 | Event
::End(Tag
::Code
)
123 | Event
::InlineHtml(_
)
124 | Event
::Text(_
) => true,
128 // render markdown to html
129 let mut markdown_parsed_name
= String
::with_capacity(name
.len() * 3 / 2);
130 html
::push_html(&mut markdown_parsed_name
, parser
);
132 // write to the handlebars template
133 out
.write(&markdown_parsed_name
)?
;
142 while current_level
> 1 {