]> git.proxmox.com Git - rustc.git/blame - vendor/mdbook/src/book/mod.rs
New upstream version 1.50.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
1b1a35ee 253 let mut failed = false;
9fa01778 254 for item in book.iter() {
2c00a5a8 255 if let BookItem::Chapter(ref ch) = *item {
f035d41b
XL
256 let chapter_path = match ch.path {
257 Some(ref path) if !path.as_os_str().is_empty() => path,
258 _ => continue,
259 };
260
261 let path = self.source_dir().join(&chapter_path);
262 info!("Testing file: {:?}", path);
263
264 // write preprocessed file to tempdir
265 let path = temp_dir.path().join(&chapter_path);
266 let mut tmpf = utils::fs::create_file(&path)?;
267 tmpf.write_all(ch.content.as_bytes())?;
268
269 let mut cmd = Command::new("rustdoc");
270 cmd.arg(&path).arg("--test").args(&library_args);
271
272 if let Some(edition) = self.config.rust.edition {
273 match edition {
274 RustEdition::E2015 => {
275 cmd.args(&["--edition", "2015"]);
276 }
277 RustEdition::E2018 => {
278 cmd.args(&["--edition", "2018"]);
279 }
2c00a5a8
XL
280 }
281 }
f035d41b
XL
282
283 let output = cmd.output()?;
284
285 if !output.status.success() {
1b1a35ee
XL
286 failed = true;
287 error!(
f035d41b
XL
288 "rustdoc returned an error:\n\
289 \n--- stdout\n{}\n--- stderr\n{}",
290 String::from_utf8_lossy(&output.stdout),
291 String::from_utf8_lossy(&output.stderr)
292 );
293 }
2c00a5a8 294 }
cc61c64b 295 }
1b1a35ee
XL
296 if failed {
297 bail!("One or more tests failed");
298 }
2c00a5a8 299 Ok(())
cc61c64b
XL
300 }
301
2c00a5a8
XL
302 /// The logic for determining where a backend should put its build
303 /// artefacts.
cc61c64b 304 ///
2c00a5a8
XL
305 /// If there is only 1 renderer, put it in the directory pointed to by the
306 /// `build.build_dir` key in `Config`. If there is more than one then the
307 /// renderer gets its own directory within the main build dir.
308 ///
309 /// i.e. If there were only one renderer (in this case, the HTML renderer):
310 ///
311 /// - build/
312 /// - index.html
313 /// - ...
314 ///
315 /// Otherwise if there are multiple:
316 ///
317 /// - build/
318 /// - epub/
319 /// - my_awesome_book.epub
320 /// - html/
321 /// - index.html
322 /// - ...
323 /// - latex/
324 /// - my_awesome_book.tex
325 ///
326 pub fn build_dir_for(&self, backend_name: &str) -> PathBuf {
327 let build_dir = self.root.join(&self.config.build.build_dir);
cc61c64b 328
2c00a5a8
XL
329 if self.renderers.len() <= 1 {
330 build_dir
331 } else {
332 build_dir.join(backend_name)
333 }
cc61c64b
XL
334 }
335
2c00a5a8
XL
336 /// Get the directory containing this book's source files.
337 pub fn source_dir(&self) -> PathBuf {
338 self.root.join(&self.config.book.src)
cc61c64b
XL
339 }
340
83c7162d 341 /// Get the directory containing the theme resources for the book.
2c00a5a8 342 pub fn theme_dir(&self) -> PathBuf {
83c7162d
XL
343 self.config
344 .html_config()
345 .unwrap_or_default()
346 .theme_dir(&self.root)
cc61c64b 347 }
2c00a5a8 348}
cc61c64b 349
2c00a5a8 350/// Look at the `Config` and try to figure out what renderers to use.
dc9dc135 351fn determine_renderers(config: &Config) -> Vec<Box<dyn Renderer>> {
416331ca 352 let mut renderers = Vec::new();
2c00a5a8 353
dc9dc135 354 if let Some(output_table) = config.get("output").and_then(Value::as_table) {
416331ca 355 renderers.extend(output_table.iter().map(|(key, table)| {
2c00a5a8 356 if key == "html" {
416331ca 357 Box::new(HtmlHandlebars::new()) as Box<dyn Renderer>
e74abb32
XL
358 } else if key == "markdown" {
359 Box::new(MarkdownRenderer::new()) as Box<dyn Renderer>
2c00a5a8 360 } else {
416331ca 361 interpret_custom_renderer(key, table)
2c00a5a8 362 }
416331ca 363 }));
cc61c64b
XL
364 }
365
2c00a5a8
XL
366 // if we couldn't find anything, add the HTML renderer as a default
367 if renderers.is_empty() {
368 renderers.push(Box::new(HtmlHandlebars::new()));
369 }
cc61c64b 370
2c00a5a8
XL
371 renderers
372}
cc61c64b 373
dc9dc135 374fn default_preprocessors() -> Vec<Box<dyn Preprocessor>> {
9fa01778
XL
375 vec![
376 Box::new(LinkPreprocessor::new()),
377 Box::new(IndexPreprocessor::new()),
378 ]
379}
380
dc9dc135 381fn is_default_preprocessor(pre: &dyn Preprocessor) -> bool {
9fa01778
XL
382 let name = pre.name();
383 name == LinkPreprocessor::NAME || name == IndexPreprocessor::NAME
2c00a5a8 384}
cc61c64b 385
2c00a5a8 386/// Look at the `MDBook` and try to figure out what preprocessors to run.
dc9dc135 387fn determine_preprocessors(config: &Config) -> Result<Vec<Box<dyn Preprocessor>>> {
9fa01778
XL
388 let mut preprocessors = Vec::new();
389
390 if config.build.use_default_preprocessors {
391 preprocessors.extend(default_preprocessors());
392 }
393
dc9dc135 394 if let Some(preprocessor_table) = config.get("preprocessor").and_then(Value::as_table) {
9fa01778
XL
395 for key in preprocessor_table.keys() {
396 match key.as_ref() {
397 "links" => preprocessors.push(Box::new(LinkPreprocessor::new())),
398 "index" => preprocessors.push(Box::new(IndexPreprocessor::new())),
399 name => preprocessors.push(interpret_custom_preprocessor(
400 name,
401 &preprocessor_table[name],
402 )),
403 }
ea8adc8c 404 }
cc61c64b
XL
405 }
406
2c00a5a8
XL
407 Ok(preprocessors)
408}
cc61c64b 409
9fa01778
XL
410fn interpret_custom_preprocessor(key: &str, table: &Value) -> Box<CmdPreprocessor> {
411 let command = table
412 .get("command")
dc9dc135
XL
413 .and_then(Value::as_str)
414 .map(ToString::to_string)
9fa01778
XL
415 .unwrap_or_else(|| format!("mdbook-{}", key));
416
f035d41b 417 Box::new(CmdPreprocessor::new(key.to_string(), command))
9fa01778
XL
418}
419
420fn interpret_custom_renderer(key: &str, table: &Value) -> Box<CmdRenderer> {
2c00a5a8
XL
421 // look for the `command` field, falling back to using the key
422 // prepended by "mdbook-"
423 let table_dot_command = table
424 .get("command")
dc9dc135
XL
425 .and_then(Value::as_str)
426 .map(ToString::to_string);
cc61c64b 427
2c00a5a8 428 let command = table_dot_command.unwrap_or_else(|| format!("mdbook-{}", key));
cc61c64b 429
f035d41b 430 Box::new(CmdRenderer::new(key.to_string(), command))
2c00a5a8 431}
ea8adc8c 432
9fa01778
XL
433/// Check whether we should run a particular `Preprocessor` in combination
434/// with the renderer, falling back to `Preprocessor::supports_renderer()`
435/// method if the user doesn't say anything.
436///
437/// The `build.use-default-preprocessors` config option can be used to ensure
438/// default preprocessors always run if they support the renderer.
dc9dc135
XL
439fn preprocessor_should_run(
440 preprocessor: &dyn Preprocessor,
441 renderer: &dyn Renderer,
442 cfg: &Config,
443) -> bool {
9fa01778
XL
444 // default preprocessors should be run by default (if supported)
445 if cfg.build.use_default_preprocessors && is_default_preprocessor(preprocessor) {
446 return preprocessor.supports_renderer(renderer.name());
447 }
448
449 let key = format!("preprocessor.{}.renderers", preprocessor.name());
450 let renderer_name = renderer.name();
451
452 if let Some(Value::Array(ref explicit_renderers)) = cfg.get(&key) {
453 return explicit_renderers
454 .iter()
dc9dc135 455 .filter_map(Value::as_str)
9fa01778
XL
456 .any(|name| name == renderer_name);
457 }
458
459 preprocessor.supports_renderer(renderer_name)
460}
461
2c00a5a8
XL
462#[cfg(test)]
463mod tests {
464 use super::*;
dc9dc135 465 use std::str::FromStr;
2c00a5a8 466 use toml::value::{Table, Value};
cc61c64b 467
2c00a5a8
XL
468 #[test]
469 fn config_defaults_to_html_renderer_if_empty() {
470 let cfg = Config::default();
ea8adc8c 471
2c00a5a8
XL
472 // make sure we haven't got anything in the `output` table
473 assert!(cfg.get("output").is_none());
cc61c64b 474
2c00a5a8 475 let got = determine_renderers(&cfg);
cc61c64b 476
2c00a5a8
XL
477 assert_eq!(got.len(), 1);
478 assert_eq!(got[0].name(), "html");
cc61c64b
XL
479 }
480
2c00a5a8
XL
481 #[test]
482 fn add_a_random_renderer_to_the_config() {
483 let mut cfg = Config::default();
484 cfg.set("output.random", Table::new()).unwrap();
cc61c64b 485
2c00a5a8 486 let got = determine_renderers(&cfg);
cc61c64b 487
2c00a5a8
XL
488 assert_eq!(got.len(), 1);
489 assert_eq!(got[0].name(), "random");
cc61c64b
XL
490 }
491
2c00a5a8
XL
492 #[test]
493 fn add_a_random_renderer_with_custom_command_to_the_config() {
494 let mut cfg = Config::default();
cc61c64b 495
2c00a5a8
XL
496 let mut table = Table::new();
497 table.insert("command".to_string(), Value::String("false".to_string()));
498 cfg.set("output.random", table).unwrap();
cc61c64b 499
2c00a5a8 500 let got = determine_renderers(&cfg);
cc61c64b 501
2c00a5a8
XL
502 assert_eq!(got.len(), 1);
503 assert_eq!(got[0].name(), "random");
cc61c64b
XL
504 }
505
2c00a5a8 506 #[test]
9fa01778 507 fn config_defaults_to_link_and_index_preprocessor_if_not_set() {
2c00a5a8 508 let cfg = Config::default();
cc61c64b 509
9fa01778
XL
510 // make sure we haven't got anything in the `preprocessor` table
511 assert!(cfg.get("preprocessor").is_none());
cc61c64b 512
2c00a5a8 513 let got = determine_preprocessors(&cfg);
cc61c64b 514
2c00a5a8 515 assert!(got.is_ok());
9fa01778 516 assert_eq!(got.as_ref().unwrap().len(), 2);
2c00a5a8 517 assert_eq!(got.as_ref().unwrap()[0].name(), "links");
9fa01778
XL
518 assert_eq!(got.as_ref().unwrap()[1].name(), "index");
519 }
520
521 #[test]
522 fn use_default_preprocessors_works() {
523 let mut cfg = Config::default();
524 cfg.build.use_default_preprocessors = false;
525
526 let got = determine_preprocessors(&cfg).unwrap();
527
528 assert_eq!(got.len(), 0);
cc61c64b
XL
529 }
530
2c00a5a8 531 #[test]
9fa01778 532 fn can_determine_third_party_preprocessors() {
dc9dc135 533 let cfg_str = r#"
2c00a5a8
XL
534 [book]
535 title = "Some Book"
cc61c64b 536
9fa01778
XL
537 [preprocessor.random]
538
2c00a5a8
XL
539 [build]
540 build-dir = "outputs"
541 create-missing = false
2c00a5a8 542 "#;
ea8adc8c 543
2c00a5a8 544 let cfg = Config::from_str(cfg_str).unwrap();
ea8adc8c 545
9fa01778
XL
546 // make sure the `preprocessor.random` table exists
547 assert!(cfg.get_preprocessor("random").is_some());
ea8adc8c 548
9fa01778 549 let got = determine_preprocessors(&cfg).unwrap();
ea8adc8c 550
9fa01778 551 assert!(got.into_iter().any(|p| p.name() == "random"));
ea8adc8c
XL
552 }
553
2c00a5a8 554 #[test]
9fa01778
XL
555 fn preprocessors_can_provide_their_own_commands() {
556 let cfg_str = r#"
557 [preprocessor.random]
558 command = "python random.py"
559 "#;
ea8adc8c 560
9fa01778
XL
561 let cfg = Config::from_str(cfg_str).unwrap();
562
563 // make sure the `preprocessor.random` table exists
564 let random = cfg.get_preprocessor("random").unwrap();
565 let random = interpret_custom_preprocessor("random", &Value::Table(random.clone()));
566
567 assert_eq!(random.cmd(), "python random.py");
568 }
569
570 #[test]
571 fn config_respects_preprocessor_selection() {
dc9dc135 572 let cfg_str = r#"
9fa01778
XL
573 [preprocessor.links]
574 renderers = ["html"]
2c00a5a8 575 "#;
ea8adc8c 576
2c00a5a8 577 let cfg = Config::from_str(cfg_str).unwrap();
ea8adc8c 578
9fa01778
XL
579 // double-check that we can access preprocessor.links.renderers[0]
580 let html = cfg
581 .get_preprocessor("links")
582 .and_then(|links| links.get("renderers"))
dc9dc135 583 .and_then(Value::as_array)
9fa01778 584 .and_then(|renderers| renderers.get(0))
dc9dc135 585 .and_then(Value::as_str)
9fa01778
XL
586 .unwrap();
587 assert_eq!(html, "html");
588 let html_renderer = HtmlHandlebars::default();
589 let pre = LinkPreprocessor::new();
590
591 let should_run = preprocessor_should_run(&pre, &html_renderer, &cfg);
592 assert!(should_run);
593 }
ea8adc8c 594
9fa01778
XL
595 struct BoolPreprocessor(bool);
596 impl Preprocessor for BoolPreprocessor {
597 fn name(&self) -> &str {
598 "bool-preprocessor"
599 }
600
601 fn run(&self, _ctx: &PreprocessorContext, _book: Book) -> Result<Book> {
602 unimplemented!()
603 }
604
605 fn supports_renderer(&self, _renderer: &str) -> bool {
606 self.0
607 }
608 }
609
610 #[test]
611 fn preprocessor_should_run_falls_back_to_supports_renderer_method() {
612 let cfg = Config::default();
613 let html = HtmlHandlebars::new();
614
615 let should_be = true;
616 let got = preprocessor_should_run(&BoolPreprocessor(should_be), &html, &cfg);
617 assert_eq!(got, should_be);
cc61c64b 618
9fa01778
XL
619 let should_be = false;
620 let got = preprocessor_should_run(&BoolPreprocessor(should_be), &html, &cfg);
621 assert_eq!(got, should_be);
cc61c64b
XL
622 }
623}