]> git.proxmox.com Git - rustc.git/blame - vendor/mdbook/src/book/mod.rs
New upstream version 1.47.0+dfsg1
[rustc.git] / vendor / mdbook / src / book / mod.rs
CommitLineData
2c00a5a8
XL
1//! The internal representation of a book and infrastructure for loading it from
2//! disk and building it.
3//!
4//! For examples on using `MDBook`, consult the [top-level documentation][1].
5//!
6//! [1]: ../index.html
7
f035d41b 8#[allow(clippy::module_inception)]
2c00a5a8
XL
9mod book;
10mod init;
9fa01778 11mod summary;
2c00a5a8
XL
12
13pub use self::book::{load_book, Book, BookItem, BookItems, Chapter};
2c00a5a8 14pub use self::init::BookBuilder;
9fa01778 15pub use self::summary::{parse_summary, Link, SectionNumber, Summary, SummaryItem};
2c00a5a8 16
2c00a5a8 17use std::io::Write;
9fa01778 18use std::path::PathBuf;
cc61c64b 19use std::process::Command;
dc9dc135 20use std::string::ToString;
83c7162d 21use tempfile::Builder as TempFileBuilder;
2c00a5a8 22use toml::Value;
cc61c64b 23
dc9dc135
XL
24use crate::errors::*;
25use crate::preprocess::{
9fa01778
XL
26 CmdPreprocessor, IndexPreprocessor, LinkPreprocessor, Preprocessor, PreprocessorContext,
27};
e74abb32 28use crate::renderer::{CmdRenderer, HtmlHandlebars, MarkdownRenderer, RenderContext, Renderer};
dc9dc135 29use crate::utils;
cc61c64b 30
f035d41b 31use crate::config::{Config, RustEdition};
cc61c64b 32
2c00a5a8 33/// The object used to manage and build a book.
cc61c64b 34pub struct MDBook {
2c00a5a8
XL
35 /// The book's root directory.
36 pub root: PathBuf,
37 /// The configuration used to tweak now a book is built.
38 pub config: Config,
39 /// A representation of the book's contents in memory.
40 pub book: Book,
dc9dc135 41 renderers: Vec<Box<dyn Renderer>>,
2c00a5a8
XL
42
43 /// List of pre-processors to be run on the book
dc9dc135 44 preprocessors: Vec<Box<dyn Preprocessor>>,
cc61c64b
XL
45}
46
47impl MDBook {
2c00a5a8
XL
48 /// Load a book from its root directory on disk.
49 pub fn load<P: Into<PathBuf>>(book_root: P) -> Result<MDBook> {
50 let book_root = book_root.into();
51 let config_location = book_root.join("book.toml");
52
53 // the book.json file is no longer used, so we should emit a warning to
54 // let people know to migrate to book.toml
55 if book_root.join("book.json").exists() {
56 warn!("It appears you are still using book.json for configuration.");
57 warn!("This format is no longer used, so you should migrate to the");
58 warn!("book.toml format.");
59 warn!("Check the user guide for migration information:");
e74abb32 60 warn!("\thttps://rust-lang.github.io/mdBook/format/config.html");
2c00a5a8 61 }
cc61c64b 62
2c00a5a8
XL
63 let mut config = if config_location.exists() {
64 debug!("Loading config from {}", config_location.display());
65 Config::from_disk(&config_location)?
66 } else {
67 Config::default()
68 };
cc61c64b 69
2c00a5a8
XL
70 config.update_from_env();
71
dc9dc135 72 if log_enabled!(log::Level::Trace) {
2c00a5a8
XL
73 for line in format!("Config: {:#?}", config).lines() {
74 trace!("{}", line);
75 }
cc61c64b
XL
76 }
77
2c00a5a8
XL
78 MDBook::load_with_config(book_root, config)
79 }
cc61c64b 80
2c00a5a8
XL
81 /// Load a book from its root directory using a custom config.
82 pub fn load_with_config<P: Into<PathBuf>>(book_root: P, config: Config) -> Result<MDBook> {
83 let root = book_root.into();
cc61c64b 84
2c00a5a8
XL
85 let src_dir = root.join(&config.book.src);
86 let book = book::load_book(&src_dir, &config.build)?;
87
88 let renderers = determine_renderers(&config);
89 let preprocessors = determine_preprocessors(&config)?;
90
91 Ok(MDBook {
92 root,
93 config,
94 book,
95 renderers,
96 preprocessors,
97 })
cc61c64b
XL
98 }
99
dc9dc135
XL
100 /// Load a book from its root directory using a custom config and a custom summary.
101 pub fn load_with_config_and_summary<P: Into<PathBuf>>(
102 book_root: P,
103 config: Config,
104 summary: Summary,
105 ) -> Result<MDBook> {
106 let root = book_root.into();
107
108 let src_dir = root.join(&config.book.src);
109 let book = book::load_book_from_disk(&summary, &src_dir)?;
110
111 let renderers = determine_renderers(&config);
112 let preprocessors = determine_preprocessors(&config)?;
113
114 Ok(MDBook {
115 root,
116 config,
117 book,
118 renderers,
119 preprocessors,
120 })
121 }
122
041b39d2
XL
123 /// Returns a flat depth-first iterator over the elements of the book,
124 /// it returns an [BookItem enum](bookitem.html):
cc61c64b
XL
125 /// `(section: String, bookitem: &BookItem)`
126 ///
127 /// ```no_run
cc61c64b 128 /// # use mdbook::MDBook;
2c00a5a8 129 /// # use mdbook::book::BookItem;
2c00a5a8 130 /// # let book = MDBook::load("mybook").unwrap();
cc61c64b 131 /// for item in book.iter() {
2c00a5a8
XL
132 /// match *item {
133 /// BookItem::Chapter(ref chapter) => {},
134 /// BookItem::Separator => {},
f035d41b 135 /// BookItem::PartTitle(ref title) => {}
cc61c64b
XL
136 /// }
137 /// }
138 ///
139 /// // would print something like this:
140 /// // 1. Chapter 1
141 /// // 1.1 Sub Chapter
142 /// // 1.2 Sub Chapter
143 /// // 2. Chapter 2
144 /// //
145 /// // etc.
cc61c64b 146 /// ```
dc9dc135 147 pub fn iter(&self) -> BookItems<'_> {
2c00a5a8 148 self.book.iter()
cc61c64b
XL
149 }
150
2c00a5a8
XL
151 /// `init()` gives you a `BookBuilder` which you can use to setup a new book
152 /// and its accompanying directory structure.
153 ///
154 /// The `BookBuilder` creates some boilerplate files and directories to get
155 /// you started with your book.
cc61c64b
XL
156 ///
157 /// ```text
158 /// book-test/
159 /// ├── book
160 /// └── src
161 /// ├── chapter_1.md
162 /// └── SUMMARY.md
163 /// ```
164 ///
2c00a5a8
XL
165 /// It uses the path provided as the root directory for your book, then adds
166 /// in a `src/` directory containing a `SUMMARY.md` and `chapter_1.md` file
167 /// to get you started.
168 pub fn init<P: Into<PathBuf>>(book_root: P) -> BookBuilder {
169 BookBuilder::new(book_root)
170 }
cc61c64b 171
2c00a5a8
XL
172 /// Tells the renderer to build our book and put it in the build directory.
173 pub fn build(&self) -> Result<()> {
174 info!("Book building has started");
cc61c64b 175
9fa01778
XL
176 for renderer in &self.renderers {
177 self.execute_build_process(&**renderer)?;
178 }
179
180 Ok(())
181 }
182
183 /// Run the entire build process for a particular `Renderer`.
f9f354fc 184 pub fn execute_build_process(&self, renderer: &dyn Renderer) -> Result<()> {
2c00a5a8 185 let mut preprocessed_book = self.book.clone();
9fa01778
XL
186 let preprocess_ctx = PreprocessorContext::new(
187 self.root.clone(),
188 self.config.clone(),
189 renderer.name().to_string(),
190 );
cc61c64b 191
2c00a5a8 192 for preprocessor in &self.preprocessors {
9fa01778
XL
193 if preprocessor_should_run(&**preprocessor, renderer, &self.config) {
194 debug!("Running the {} preprocessor.", preprocessor.name());
195 preprocessed_book = preprocessor.run(&preprocess_ctx, preprocessed_book)?;
196 }
cc61c64b
XL
197 }
198
9fa01778
XL
199 info!("Running the {} backend", renderer.name());
200 self.render(&preprocessed_book, renderer)?;
cc61c64b 201
2c00a5a8
XL
202 Ok(())
203 }
cc61c64b 204
dc9dc135 205 fn render(&self, preprocessed_book: &Book, renderer: &dyn Renderer) -> Result<()> {
2c00a5a8
XL
206 let name = renderer.name();
207 let build_dir = self.build_dir_for(name);
cc61c64b 208
2c00a5a8
XL
209 let render_context = RenderContext::new(
210 self.root.clone(),
211 preprocessed_book.clone(),
212 self.config.clone(),
213 build_dir,
214 );
cc61c64b 215
2c00a5a8
XL
216 renderer
217 .render(&render_context)
f035d41b 218 .with_context(|| "Rendering failed")
2c00a5a8 219 }
7cac9316 220
2c00a5a8
XL
221 /// You can change the default renderer to another one by using this method.
222 /// The only requirement is for your renderer to implement the [`Renderer`
223 /// trait](../renderer/trait.Renderer.html)
224 pub fn with_renderer<R: Renderer + 'static>(&mut self, renderer: R) -> &mut Self {
225 self.renderers.push(Box::new(renderer));
226 self
227 }
cc61c64b 228
2c00a5a8 229 /// Register a [`Preprocessor`](../preprocess/trait.Preprocessor.html) to be used when rendering the book.
dc9dc135 230 pub fn with_preprocessor<P: Preprocessor + 'static>(&mut self, preprocessor: P) -> &mut Self {
2c00a5a8
XL
231 self.preprocessors.push(Box::new(preprocessor));
232 self
cc61c64b
XL
233 }
234
2c00a5a8
XL
235 /// Run `rustdoc` tests on the book, linking against the provided libraries.
236 pub fn test(&mut self, library_paths: Vec<&str>) -> Result<()> {
237 let library_args: Vec<&str> = (0..library_paths.len())
238 .map(|_| "-L")
239 .zip(library_paths.into_iter())
240 .flat_map(|x| vec![x.0, x.1])
241 .collect();
cc61c64b 242
9fa01778 243 let temp_dir = TempFileBuilder::new().prefix("mdbook-").tempdir()?;
cc61c64b 244
9fa01778
XL
245 // FIXME: Is "test" the proper renderer name to use here?
246 let preprocess_context =
247 PreprocessorContext::new(self.root.clone(), self.config.clone(), "test".to_string());
cc61c64b 248
9fa01778
XL
249 let book = LinkPreprocessor::new().run(&preprocess_context, self.book.clone())?;
250 // Index Preprocessor is disabled so that chapter paths continue to point to the
251 // actual markdown files.
cc61c64b 252
9fa01778 253 for item in book.iter() {
2c00a5a8 254 if let BookItem::Chapter(ref ch) = *item {
f035d41b
XL
255 let chapter_path = match ch.path {
256 Some(ref path) if !path.as_os_str().is_empty() => path,
257 _ => continue,
258 };
259
260 let path = self.source_dir().join(&chapter_path);
261 info!("Testing file: {:?}", path);
262
263 // write preprocessed file to tempdir
264 let path = temp_dir.path().join(&chapter_path);
265 let mut tmpf = utils::fs::create_file(&path)?;
266 tmpf.write_all(ch.content.as_bytes())?;
267
268 let mut cmd = Command::new("rustdoc");
269 cmd.arg(&path).arg("--test").args(&library_args);
270
271 if let Some(edition) = self.config.rust.edition {
272 match edition {
273 RustEdition::E2015 => {
274 cmd.args(&["--edition", "2015"]);
275 }
276 RustEdition::E2018 => {
277 cmd.args(&["--edition", "2018"]);
278 }
2c00a5a8
XL
279 }
280 }
f035d41b
XL
281
282 let output = cmd.output()?;
283
284 if !output.status.success() {
285 bail!(
286 "rustdoc returned an error:\n\
287 \n--- stdout\n{}\n--- stderr\n{}",
288 String::from_utf8_lossy(&output.stdout),
289 String::from_utf8_lossy(&output.stderr)
290 );
291 }
2c00a5a8 292 }
cc61c64b 293 }
2c00a5a8 294 Ok(())
cc61c64b
XL
295 }
296
2c00a5a8
XL
297 /// The logic for determining where a backend should put its build
298 /// artefacts.
cc61c64b 299 ///
2c00a5a8
XL
300 /// If there is only 1 renderer, put it in the directory pointed to by the
301 /// `build.build_dir` key in `Config`. If there is more than one then the
302 /// renderer gets its own directory within the main build dir.
303 ///
304 /// i.e. If there were only one renderer (in this case, the HTML renderer):
305 ///
306 /// - build/
307 /// - index.html
308 /// - ...
309 ///
310 /// Otherwise if there are multiple:
311 ///
312 /// - build/
313 /// - epub/
314 /// - my_awesome_book.epub
315 /// - html/
316 /// - index.html
317 /// - ...
318 /// - latex/
319 /// - my_awesome_book.tex
320 ///
321 pub fn build_dir_for(&self, backend_name: &str) -> PathBuf {
322 let build_dir = self.root.join(&self.config.build.build_dir);
cc61c64b 323
2c00a5a8
XL
324 if self.renderers.len() <= 1 {
325 build_dir
326 } else {
327 build_dir.join(backend_name)
328 }
cc61c64b
XL
329 }
330
2c00a5a8
XL
331 /// Get the directory containing this book's source files.
332 pub fn source_dir(&self) -> PathBuf {
333 self.root.join(&self.config.book.src)
cc61c64b
XL
334 }
335
83c7162d 336 /// Get the directory containing the theme resources for the book.
2c00a5a8 337 pub fn theme_dir(&self) -> PathBuf {
83c7162d
XL
338 self.config
339 .html_config()
340 .unwrap_or_default()
341 .theme_dir(&self.root)
cc61c64b 342 }
2c00a5a8 343}
cc61c64b 344
2c00a5a8 345/// Look at the `Config` and try to figure out what renderers to use.
dc9dc135 346fn determine_renderers(config: &Config) -> Vec<Box<dyn Renderer>> {
416331ca 347 let mut renderers = Vec::new();
2c00a5a8 348
dc9dc135 349 if let Some(output_table) = config.get("output").and_then(Value::as_table) {
416331ca 350 renderers.extend(output_table.iter().map(|(key, table)| {
2c00a5a8 351 if key == "html" {
416331ca 352 Box::new(HtmlHandlebars::new()) as Box<dyn Renderer>
e74abb32
XL
353 } else if key == "markdown" {
354 Box::new(MarkdownRenderer::new()) as Box<dyn Renderer>
2c00a5a8 355 } else {
416331ca 356 interpret_custom_renderer(key, table)
2c00a5a8 357 }
416331ca 358 }));
cc61c64b
XL
359 }
360
2c00a5a8
XL
361 // if we couldn't find anything, add the HTML renderer as a default
362 if renderers.is_empty() {
363 renderers.push(Box::new(HtmlHandlebars::new()));
364 }
cc61c64b 365
2c00a5a8
XL
366 renderers
367}
cc61c64b 368
dc9dc135 369fn default_preprocessors() -> Vec<Box<dyn Preprocessor>> {
9fa01778
XL
370 vec![
371 Box::new(LinkPreprocessor::new()),
372 Box::new(IndexPreprocessor::new()),
373 ]
374}
375
dc9dc135 376fn is_default_preprocessor(pre: &dyn Preprocessor) -> bool {
9fa01778
XL
377 let name = pre.name();
378 name == LinkPreprocessor::NAME || name == IndexPreprocessor::NAME
2c00a5a8 379}
cc61c64b 380
2c00a5a8 381/// Look at the `MDBook` and try to figure out what preprocessors to run.
dc9dc135 382fn determine_preprocessors(config: &Config) -> Result<Vec<Box<dyn Preprocessor>>> {
9fa01778
XL
383 let mut preprocessors = Vec::new();
384
385 if config.build.use_default_preprocessors {
386 preprocessors.extend(default_preprocessors());
387 }
388
dc9dc135 389 if let Some(preprocessor_table) = config.get("preprocessor").and_then(Value::as_table) {
9fa01778
XL
390 for key in preprocessor_table.keys() {
391 match key.as_ref() {
392 "links" => preprocessors.push(Box::new(LinkPreprocessor::new())),
393 "index" => preprocessors.push(Box::new(IndexPreprocessor::new())),
394 name => preprocessors.push(interpret_custom_preprocessor(
395 name,
396 &preprocessor_table[name],
397 )),
398 }
ea8adc8c 399 }
cc61c64b
XL
400 }
401
2c00a5a8
XL
402 Ok(preprocessors)
403}
cc61c64b 404
9fa01778
XL
405fn interpret_custom_preprocessor(key: &str, table: &Value) -> Box<CmdPreprocessor> {
406 let command = table
407 .get("command")
dc9dc135
XL
408 .and_then(Value::as_str)
409 .map(ToString::to_string)
9fa01778
XL
410 .unwrap_or_else(|| format!("mdbook-{}", key));
411
f035d41b 412 Box::new(CmdPreprocessor::new(key.to_string(), command))
9fa01778
XL
413}
414
415fn interpret_custom_renderer(key: &str, table: &Value) -> Box<CmdRenderer> {
2c00a5a8
XL
416 // look for the `command` field, falling back to using the key
417 // prepended by "mdbook-"
418 let table_dot_command = table
419 .get("command")
dc9dc135
XL
420 .and_then(Value::as_str)
421 .map(ToString::to_string);
cc61c64b 422
2c00a5a8 423 let command = table_dot_command.unwrap_or_else(|| format!("mdbook-{}", key));
cc61c64b 424
f035d41b 425 Box::new(CmdRenderer::new(key.to_string(), command))
2c00a5a8 426}
ea8adc8c 427
9fa01778
XL
428/// Check whether we should run a particular `Preprocessor` in combination
429/// with the renderer, falling back to `Preprocessor::supports_renderer()`
430/// method if the user doesn't say anything.
431///
432/// The `build.use-default-preprocessors` config option can be used to ensure
433/// default preprocessors always run if they support the renderer.
dc9dc135
XL
434fn preprocessor_should_run(
435 preprocessor: &dyn Preprocessor,
436 renderer: &dyn Renderer,
437 cfg: &Config,
438) -> bool {
9fa01778
XL
439 // default preprocessors should be run by default (if supported)
440 if cfg.build.use_default_preprocessors && is_default_preprocessor(preprocessor) {
441 return preprocessor.supports_renderer(renderer.name());
442 }
443
444 let key = format!("preprocessor.{}.renderers", preprocessor.name());
445 let renderer_name = renderer.name();
446
447 if let Some(Value::Array(ref explicit_renderers)) = cfg.get(&key) {
448 return explicit_renderers
449 .iter()
dc9dc135 450 .filter_map(Value::as_str)
9fa01778
XL
451 .any(|name| name == renderer_name);
452 }
453
454 preprocessor.supports_renderer(renderer_name)
455}
456
2c00a5a8
XL
457#[cfg(test)]
458mod tests {
459 use super::*;
dc9dc135 460 use std::str::FromStr;
2c00a5a8 461 use toml::value::{Table, Value};
cc61c64b 462
2c00a5a8
XL
463 #[test]
464 fn config_defaults_to_html_renderer_if_empty() {
465 let cfg = Config::default();
ea8adc8c 466
2c00a5a8
XL
467 // make sure we haven't got anything in the `output` table
468 assert!(cfg.get("output").is_none());
cc61c64b 469
2c00a5a8 470 let got = determine_renderers(&cfg);
cc61c64b 471
2c00a5a8
XL
472 assert_eq!(got.len(), 1);
473 assert_eq!(got[0].name(), "html");
cc61c64b
XL
474 }
475
2c00a5a8
XL
476 #[test]
477 fn add_a_random_renderer_to_the_config() {
478 let mut cfg = Config::default();
479 cfg.set("output.random", Table::new()).unwrap();
cc61c64b 480
2c00a5a8 481 let got = determine_renderers(&cfg);
cc61c64b 482
2c00a5a8
XL
483 assert_eq!(got.len(), 1);
484 assert_eq!(got[0].name(), "random");
cc61c64b
XL
485 }
486
2c00a5a8
XL
487 #[test]
488 fn add_a_random_renderer_with_custom_command_to_the_config() {
489 let mut cfg = Config::default();
cc61c64b 490
2c00a5a8
XL
491 let mut table = Table::new();
492 table.insert("command".to_string(), Value::String("false".to_string()));
493 cfg.set("output.random", table).unwrap();
cc61c64b 494
2c00a5a8 495 let got = determine_renderers(&cfg);
cc61c64b 496
2c00a5a8
XL
497 assert_eq!(got.len(), 1);
498 assert_eq!(got[0].name(), "random");
cc61c64b
XL
499 }
500
2c00a5a8 501 #[test]
9fa01778 502 fn config_defaults_to_link_and_index_preprocessor_if_not_set() {
2c00a5a8 503 let cfg = Config::default();
cc61c64b 504
9fa01778
XL
505 // make sure we haven't got anything in the `preprocessor` table
506 assert!(cfg.get("preprocessor").is_none());
cc61c64b 507
2c00a5a8 508 let got = determine_preprocessors(&cfg);
cc61c64b 509
2c00a5a8 510 assert!(got.is_ok());
9fa01778 511 assert_eq!(got.as_ref().unwrap().len(), 2);
2c00a5a8 512 assert_eq!(got.as_ref().unwrap()[0].name(), "links");
9fa01778
XL
513 assert_eq!(got.as_ref().unwrap()[1].name(), "index");
514 }
515
516 #[test]
517 fn use_default_preprocessors_works() {
518 let mut cfg = Config::default();
519 cfg.build.use_default_preprocessors = false;
520
521 let got = determine_preprocessors(&cfg).unwrap();
522
523 assert_eq!(got.len(), 0);
cc61c64b
XL
524 }
525
2c00a5a8 526 #[test]
9fa01778 527 fn can_determine_third_party_preprocessors() {
dc9dc135 528 let cfg_str = r#"
2c00a5a8
XL
529 [book]
530 title = "Some Book"
cc61c64b 531
9fa01778
XL
532 [preprocessor.random]
533
2c00a5a8
XL
534 [build]
535 build-dir = "outputs"
536 create-missing = false
2c00a5a8 537 "#;
ea8adc8c 538
2c00a5a8 539 let cfg = Config::from_str(cfg_str).unwrap();
ea8adc8c 540
9fa01778
XL
541 // make sure the `preprocessor.random` table exists
542 assert!(cfg.get_preprocessor("random").is_some());
ea8adc8c 543
9fa01778 544 let got = determine_preprocessors(&cfg).unwrap();
ea8adc8c 545
9fa01778 546 assert!(got.into_iter().any(|p| p.name() == "random"));
ea8adc8c
XL
547 }
548
2c00a5a8 549 #[test]
9fa01778
XL
550 fn preprocessors_can_provide_their_own_commands() {
551 let cfg_str = r#"
552 [preprocessor.random]
553 command = "python random.py"
554 "#;
ea8adc8c 555
9fa01778
XL
556 let cfg = Config::from_str(cfg_str).unwrap();
557
558 // make sure the `preprocessor.random` table exists
559 let random = cfg.get_preprocessor("random").unwrap();
560 let random = interpret_custom_preprocessor("random", &Value::Table(random.clone()));
561
562 assert_eq!(random.cmd(), "python random.py");
563 }
564
565 #[test]
566 fn config_respects_preprocessor_selection() {
dc9dc135 567 let cfg_str = r#"
9fa01778
XL
568 [preprocessor.links]
569 renderers = ["html"]
2c00a5a8 570 "#;
ea8adc8c 571
2c00a5a8 572 let cfg = Config::from_str(cfg_str).unwrap();
ea8adc8c 573
9fa01778
XL
574 // double-check that we can access preprocessor.links.renderers[0]
575 let html = cfg
576 .get_preprocessor("links")
577 .and_then(|links| links.get("renderers"))
dc9dc135 578 .and_then(Value::as_array)
9fa01778 579 .and_then(|renderers| renderers.get(0))
dc9dc135 580 .and_then(Value::as_str)
9fa01778
XL
581 .unwrap();
582 assert_eq!(html, "html");
583 let html_renderer = HtmlHandlebars::default();
584 let pre = LinkPreprocessor::new();
585
586 let should_run = preprocessor_should_run(&pre, &html_renderer, &cfg);
587 assert!(should_run);
588 }
ea8adc8c 589
9fa01778
XL
590 struct BoolPreprocessor(bool);
591 impl Preprocessor for BoolPreprocessor {
592 fn name(&self) -> &str {
593 "bool-preprocessor"
594 }
595
596 fn run(&self, _ctx: &PreprocessorContext, _book: Book) -> Result<Book> {
597 unimplemented!()
598 }
599
600 fn supports_renderer(&self, _renderer: &str) -> bool {
601 self.0
602 }
603 }
604
605 #[test]
606 fn preprocessor_should_run_falls_back_to_supports_renderer_method() {
607 let cfg = Config::default();
608 let html = HtmlHandlebars::new();
609
610 let should_be = true;
611 let got = preprocessor_should_run(&BoolPreprocessor(should_be), &html, &cfg);
612 assert_eq!(got, should_be);
cc61c64b 613
9fa01778
XL
614 let should_be = false;
615 let got = preprocessor_should_run(&BoolPreprocessor(should_be), &html, &cfg);
616 assert_eq!(got, should_be);
cc61c64b
XL
617 }
618}