]> git.proxmox.com Git - rustc.git/blob - src/vendor/mdbook/src/renderer/html_handlebars/helpers/navigation.rs
New upstream version 1.31.0+dfsg1
[rustc.git] / src / vendor / mdbook / src / renderer / html_handlebars / helpers / navigation.rs
1 use std::path::Path;
2 use std::collections::BTreeMap;
3
4 use serde_json;
5 use handlebars::{Context, Handlebars, Helper, RenderContext, RenderError, Renderable};
6
7 type StringMap = BTreeMap<String, String>;
8
9 /// Target for `find_chapter`.
10 enum Target {
11 Previous,
12 Next,
13 }
14
15 impl Target {
16 /// Returns target if found.
17 fn find(
18 &self,
19 base_path: &String,
20 current_path: &String,
21 current_item: &StringMap,
22 previous_item: &StringMap,
23 ) -> Result<Option<StringMap>, RenderError> {
24 match self {
25 &Target::Next => {
26 let previous_path = previous_item
27 .get("path")
28 .ok_or_else(|| RenderError::new("No path found for chapter in JSON data"))?;
29
30 if previous_path == base_path {
31 return Ok(Some(current_item.clone()));
32 }
33 }
34
35 &Target::Previous => {
36 if current_path == base_path {
37 return Ok(Some(previous_item.clone()));
38 }
39 }
40 }
41
42 Ok(None)
43 }
44 }
45
46 fn find_chapter(rc: &mut RenderContext, target: Target) -> Result<Option<StringMap>, RenderError> {
47 debug!("Get data from context");
48
49 let chapters = rc.evaluate_absolute("chapters", true).and_then(|c| {
50 serde_json::value::from_value::<Vec<StringMap>>(c.clone())
51 .map_err(|_| RenderError::new("Could not decode the JSON data"))
52 })?;
53
54 let base_path = rc.evaluate_absolute("path", true)?
55 .as_str()
56 .ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
57 .replace("\"", "");
58
59 let mut previous: Option<StringMap> = None;
60
61 debug!("Search for chapter");
62
63 for item in chapters {
64 match item.get("path") {
65 Some(path) if !path.is_empty() => {
66 if let Some(previous) = previous {
67 if let Some(item) = target.find(&base_path, &path, &item, &previous)? {
68 return Ok(Some(item));
69 }
70 }
71
72 previous = Some(item.clone());
73 }
74 _ => continue,
75 }
76 }
77
78 Ok(None)
79 }
80
81 fn render(
82 _h: &Helper,
83 r: &Handlebars,
84 rc: &mut RenderContext,
85 chapter: &StringMap,
86 ) -> Result<(), RenderError> {
87 trace!("Creating BTreeMap to inject in context");
88
89 let mut context = BTreeMap::new();
90
91 chapter
92 .get("name")
93 .ok_or_else(|| RenderError::new("No title found for chapter in JSON data"))
94 .map(|name| context.insert("title".to_owned(), json!(name)))?;
95
96 chapter
97 .get("path")
98 .ok_or_else(|| RenderError::new("No path found for chapter in JSON data"))
99 .and_then(|p| {
100 Path::new(p)
101 .with_extension("html")
102 .to_str()
103 .ok_or_else(|| RenderError::new("Link could not be converted to str"))
104 .map(|p| context.insert("link".to_owned(), json!(p.replace("\\", "/"))))
105 })?;
106
107 trace!("Render template");
108
109 _h.template()
110 .ok_or_else(|| RenderError::new("Error with the handlebars template"))
111 .and_then(|t| {
112 let mut local_rc = rc.with_context(Context::wraps(&context)?);
113 t.render(r, &mut local_rc)
114 })?;
115
116 Ok(())
117 }
118
119 pub fn previous(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
120 trace!("previous (handlebars helper)");
121
122 if let Some(previous) = find_chapter(rc, Target::Previous)? {
123 render(_h, r, rc, &previous)?;
124 }
125
126 Ok(())
127 }
128
129 pub fn next(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
130 trace!("next (handlebars helper)");
131
132 if let Some(next) = find_chapter(rc, Target::Next)? {
133 render(_h, r, rc, &next)?;
134 }
135
136 Ok(())
137 }
138
139 #[cfg(test)]
140 mod tests {
141 use super::*;
142
143 static TEMPLATE: &'static str =
144 "{{#previous}}{{title}}: {{link}}{{/previous}}|{{#next}}{{title}}: {{link}}{{/next}}";
145
146 #[test]
147 fn test_next_previous() {
148 let data = json!({
149 "name": "two",
150 "path": "two.path",
151 "chapters": [
152 {
153 "name": "one",
154 "path": "one.path"
155 },
156 {
157 "name": "two",
158 "path": "two.path",
159 },
160 {
161 "name": "three",
162 "path": "three.path"
163 }
164 ]
165 });
166
167 let mut h = Handlebars::new();
168 h.register_helper("previous", Box::new(previous));
169 h.register_helper("next", Box::new(next));
170
171 assert_eq!(
172 h.render_template(TEMPLATE, &data).unwrap(),
173 "one: one.html|three: three.html"
174 );
175 }
176
177 #[test]
178 fn test_first() {
179 let data = json!({
180 "name": "one",
181 "path": "one.path",
182 "chapters": [
183 {
184 "name": "one",
185 "path": "one.path"
186 },
187 {
188 "name": "two",
189 "path": "two.path",
190 },
191 {
192 "name": "three",
193 "path": "three.path"
194 }
195 ]
196 });
197
198 let mut h = Handlebars::new();
199 h.register_helper("previous", Box::new(previous));
200 h.register_helper("next", Box::new(next));
201
202 assert_eq!(
203 h.render_template(TEMPLATE, &data).unwrap(),
204 "|two: two.html"
205 );
206 }
207 #[test]
208 fn test_last() {
209 let data = json!({
210 "name": "three",
211 "path": "three.path",
212 "chapters": [
213 {
214 "name": "one",
215 "path": "one.path"
216 },
217 {
218 "name": "two",
219 "path": "two.path",
220 },
221 {
222 "name": "three",
223 "path": "three.path"
224 }
225 ]
226 });
227
228 let mut h = Handlebars::new();
229 h.register_helper("previous", Box::new(previous));
230 h.register_helper("next", Box::new(next));
231
232 assert_eq!(
233 h.render_template(TEMPLATE, &data).unwrap(),
234 "two: two.html|"
235 );
236 }
237 }