diff --git a/book-example/src/for_developers/preprocessors.md b/book-example/src/for_developers/preprocessors.md index 16bd8e8800..03c915bbc1 100644 --- a/book-example/src/for_developers/preprocessors.md +++ b/book-example/src/for_developers/preprocessors.md @@ -18,7 +18,10 @@ A preprocessor is represented by the `Preprocessor` trait. ```rust pub trait Preprocessor { fn name(&self) -> &str; - fn run(&self, ctx: &PreprocessorContext, book: &mut Book) -> Result<()>; + fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result; + fn supports_renderer(&self, _renderer: &str) -> bool { + true + } } ``` @@ -28,9 +31,13 @@ Where the `PreprocessorContext` is defined as pub struct PreprocessorContext { pub root: PathBuf, pub config: Config, + /// The `Renderer` this preprocessor is being used with. + pub renderer: String, } ``` +The `renderer` value allows you react accordingly, for example, PDF or HTML. + ## A complete Example The magic happens within the `run(...)` method of the @@ -68,8 +75,12 @@ The following code block shows how to remove all emphasis from markdown, and do so safely. ```rust -fn remove_emphasis(num_removed_items: &mut i32, chapter: &mut Chapter) -> Result { +fn remove_emphasis( + num_removed_items: &mut usize, + chapter: &mut Chapter, +) -> Result { let mut buf = String::with_capacity(chapter.content.len()); + let events = Parser::new(&chapter.content).filter(|e| { let should_keep = match *e { Event::Start(Tag::Emphasis) @@ -83,15 +94,16 @@ fn remove_emphasis(num_removed_items: &mut i32, chapter: &mut Chapter) -> Result } should_keep }); - cmark(events, &mut buf, None) - .map(|_| buf) - .map_err(|err| Error::from(format!("Markdown serialization failed: {}", err))) + + cmark(events, &mut buf, None).map(|_| buf).map_err(|err| { + Error::from(format!("Markdown serialization failed: {}", err)) + }) } ``` For everything else, have a look [at the complete example][example]. -[preprocessor-docs]: https://docs.rs/mdbook/0.1.3/mdbook/preprocess/trait.Preprocessor.html +[preprocessor-docs]: https://docs.rs/mdbook/latest/mdbook/preprocess/trait.Preprocessor.html [pc]: https://crates.io/crates/pulldown-cmark [pctc]: https://crates.io/crates/pulldown-cmark-to-cmark [example]: https://github.com/rust-lang-nursery/mdBook/blob/master/examples/de-emphasize.rs diff --git a/book-example/src/format/config.md b/book-example/src/format/config.md index 312e4ebbf2..3f0fa2786f 100644 --- a/book-example/src/format/config.md +++ b/book-example/src/format/config.md @@ -14,6 +14,10 @@ description = "The example book covers examples." build-dir = "my-example-book" create-missing = false +[preprocess.index] + +[preprocess.links] + [output.html] additional-css = ["custom.css"] @@ -27,7 +31,6 @@ It is important to note that **any** relative path specified in the in the configuration will always be taken relative from the root of the book where the configuration file is located. - ### General metadata This is general information about your book. @@ -59,15 +62,25 @@ This controls the build process of your book. will be created when the book is built (i.e. `create-missing = true`). If this is `false` then the build process will instead exit with an error if any files do not exist. -- **preprocess:** Specify which preprocessors to be applied. Default is - `["links", "index"]`. To disable default preprocessors, pass an empty array - `[]` in. +- **use-default-preprocessors:** Disable the default preprocessors of (`links` & + `index`) by setting this option to `false`. + If you have the same, and/or other preprocessors declared via their table + of configuration, they will run instead. + + - For clarity, with no preprocessor configuration, the default `links` and + `index` will run. + - Setting `use-default-preprocessors = false` will disable these + default preprocessors from running. + - Adding `[preprocessor.links]`, for example, will ensure, regardless of + `use-default-preprocessors` that `links` it will run. + +## Configuring Preprocessors The following preprocessors are available and included by default: -- `links`: Expand the `{{# playpen}}` and `{{# include}}` handlebars helpers in - a chapter. +- `links`: Expand the `{{ #playpen }}` and `{{ #include }}` handlebars helpers in + a chapter to include the contents of a file. - `index`: Convert all chapter files named `README.md` into `index.md`. That is to say, all `README.md` would be rendered to an index file `index.html` in the rendered book. @@ -78,10 +91,39 @@ The following preprocessors are available and included by default: [build] build-dir = "build" create-missing = false -preprocess = ["links", "index"] + +[preprocess.links] + +[preprocess.index] ``` +### Custom Preprocessor Configuration + +Like renderers, preprocessor will need to be given its own table (e.g. `[preprocessor.mathjax]`). +In the section, you may then pass extra configuration to the preprocessor by adding key-value pairs to the table. + +For example + +``` +[preprocess.links] +# set the renderers this preprocessor will run for +renderers = ["html"] +some_extra_feature = true +``` + +#### Locking a Preprocessor dependency to a renderer + +You can explicitly specify that a preprocessor should run for a renderer by binding the two together. + +``` +[preprocessor.mathjax] +renderers = ["html"] # mathjax only makes sense with the HTML renderer +``` + +## Configuring Renderers + ### HTML renderer options + The HTML renderer has a couple of options as well. All the options for the renderer need to be specified under the TOML table `[output.html]`. @@ -214,4 +256,4 @@ book's title without needing to touch your `book.toml`. The latter case may be useful in situations where `mdbook` is invoked from a script or CI, where it sometimes isn't possible to update the `book.toml` before -building. +building. \ No newline at end of file diff --git a/examples/de-emphasize.rs b/examples/de-emphasize.rs index 0e2ae983f2..88c1b3a413 100644 --- a/examples/de-emphasize.rs +++ b/examples/de-emphasize.rs @@ -14,81 +14,85 @@ use std::env::{args, args_os}; use std::ffi::OsString; use std::process; +const NAME: &str = "md-links-to-html-links"; + +fn do_it(book: OsString) -> Result<()> { + let mut book = MDBook::load(book)?; + book.with_preprecessor(Deemphasize); + book.build() +} + +fn main() { + if args_os().count() != 2 { + eprintln!("USAGE: {} ", args().next().expect("executable")); + return; + } + if let Err(e) = do_it(args_os().skip(1).next().expect("one argument")) { + eprintln!("{}", e); + process::exit(1); + } +} + struct Deemphasize; impl Preprocessor for Deemphasize { fn name(&self) -> &str { - "md-links-to-html-links" + NAME } - fn run(&self, _ctx: &PreprocessorContext, book: &mut Book) -> Result<()> { + fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result { eprintln!("Running '{}' preprocessor", self.name()); - let mut res: Option<_> = None; let mut num_removed_items = 0; - book.for_each_mut(|item: &mut BookItem| { - if let Some(Err(_)) = res { - return; - } - if let BookItem::Chapter(ref mut chapter) = *item { - eprintln!("{}: processing chapter '{}'", self.name(), chapter.name); - res = Some( - match Deemphasize::remove_emphasis(&mut num_removed_items, chapter) { - Ok(md) => { - chapter.content = md; - Ok(()) - } - Err(err) => Err(err), - }, - ); - } - }); + + process(&mut book.sections, &mut num_removed_items)?; + eprintln!( "{}: removed {} events from markdown stream.", self.name(), num_removed_items ); - match res { - Some(res) => res, - None => Ok(()), - } + + Ok(book) } } -fn do_it(book: OsString) -> Result<()> { - let mut book = MDBook::load(book)?; - book.with_preprecessor(Deemphasize); - book.build() -} +fn process<'a, I>(items: I, num_removed_items: &mut usize) -> Result<()> +where + I: IntoIterator + 'a, +{ + for item in items { + if let BookItem::Chapter(ref mut chapter) = *item { + eprintln!("{}: processing chapter '{}'", NAME, chapter.name); -fn main() { - if args_os().count() != 2 { - eprintln!("USAGE: {} ", args().next().expect("executable")); - return; - } - if let Err(e) = do_it(args_os().skip(1).next().expect("one argument")) { - eprintln!("{}", e); - process::exit(1); + let md = remove_emphasis(num_removed_items, chapter)?; + chapter.content = md; + } } + + Ok(()) } -impl Deemphasize { - fn remove_emphasis(num_removed_items: &mut i32, chapter: &mut Chapter) -> Result { - let mut buf = String::with_capacity(chapter.content.len()); - let events = Parser::new(&chapter.content).filter(|e| { - let should_keep = match *e { - Event::Start(Tag::Emphasis) - | Event::Start(Tag::Strong) - | Event::End(Tag::Emphasis) - | Event::End(Tag::Strong) => false, - _ => true, - }; - if !should_keep { - *num_removed_items += 1; - } - should_keep - }); - cmark(events, &mut buf, None) - .map(|_| buf) - .map_err(|err| Error::from(format!("Markdown serialization failed: {}", err))) - } +fn remove_emphasis( + num_removed_items: &mut usize, + chapter: &mut Chapter, +) -> Result { + let mut buf = String::with_capacity(chapter.content.len()); + + let events = Parser::new(&chapter.content).filter(|e| { + let should_keep = match *e { + Event::Start(Tag::Emphasis) + | Event::Start(Tag::Strong) + | Event::End(Tag::Emphasis) + | Event::End(Tag::Strong) => false, + _ => true, + }; + if !should_keep { + *num_removed_items += 1; + } + should_keep + }); + + cmark(events, &mut buf, None).map(|_| buf).map_err(|err| { + Error::from(format!("Markdown serialization failed: {}", err)) + }) } diff --git a/src/book/mod.rs b/src/book/mod.rs index d1ed19c44c..f1e3daf473 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -149,23 +149,39 @@ impl MDBook { pub fn build(&self) -> Result<()> { info!("Book building has started"); + for renderer in &self.renderers { + self.execute_build_process(&**renderer)?; + } + + Ok(()) + } + + /// Run the entire build process for a particular `Renderer`. + fn execute_build_process(&self, renderer: &Renderer) -> Result<()> { let mut preprocessed_book = self.book.clone(); - let preprocess_ctx = PreprocessorContext::new(self.root.clone(), self.config.clone()); + let preprocess_ctx = PreprocessorContext::new(self.root.clone(), + self.config.clone(), + renderer.name().to_string()); for preprocessor in &self.preprocessors { - debug!("Running the {} preprocessor.", preprocessor.name()); - preprocessor.run(&preprocess_ctx, &mut preprocessed_book)?; + if preprocessor_should_run(&**preprocessor, renderer, &self.config) { + debug!("Running the {} preprocessor.", preprocessor.name()); + preprocessed_book = + preprocessor.run(&preprocess_ctx, preprocessed_book)?; + } } - for renderer in &self.renderers { - info!("Running the {} backend", renderer.name()); - self.run_renderer(&preprocessed_book, renderer.as_ref())?; - } + info!("Running the {} backend", renderer.name()); + self.render(&preprocessed_book, renderer)?; Ok(()) } - fn run_renderer(&self, preprocessed_book: &Book, renderer: &Renderer) -> Result<()> { + fn render( + &self, + preprocessed_book: &Book, + renderer: &Renderer, + ) -> Result<()> { let name = renderer.name(); let build_dir = self.build_dir_for(name); if build_dir.exists() { @@ -215,13 +231,16 @@ impl MDBook { let temp_dir = TempFileBuilder::new().prefix("mdbook-").tempdir()?; - let preprocess_context = PreprocessorContext::new(self.root.clone(), self.config.clone()); + // FIXME: Is "test" the proper renderer name to use here? + let preprocess_context = PreprocessorContext::new(self.root.clone(), + self.config.clone(), + "test".to_string()); - LinkPreprocessor::new().run(&preprocess_context, &mut self.book)?; + let book = LinkPreprocessor::new().run(&preprocess_context, self.book.clone())?; // Index Preprocessor is disabled so that chapter paths continue to point to the // actual markdown files. - for item in self.iter() { + for item in book.iter() { if let BookItem::Chapter(ref ch) = *item { if !ch.path.as_os_str().is_empty() { let path = self.source_dir().join(&ch.path); @@ -330,19 +349,32 @@ fn default_preprocessors() -> Vec> { ] } +fn is_default_preprocessor(pre: &Preprocessor) -> bool { + let name = pre.name(); + name == LinkPreprocessor::NAME || name == IndexPreprocessor::NAME +} + /// Look at the `MDBook` and try to figure out what preprocessors to run. fn determine_preprocessors(config: &Config) -> Result>> { - let preprocess_list = match config.build.preprocess { - Some(ref p) => p, + let preprocessor_keys = config.get("preprocessor") + .and_then(|value| value.as_table()) + .map(|table| table.keys()); + + let mut preprocessors = if config.build.use_default_preprocessors { + default_preprocessors() + } else { + Vec::new() + }; + + let preprocessor_keys = match preprocessor_keys { + Some(keys) => keys, // If no preprocessor field is set, default to the LinkPreprocessor and // IndexPreprocessor. This allows you to disable default preprocessors // by setting "preprocess" to an empty list. - None => return Ok(default_preprocessors()), + None => return Ok(preprocessors), }; - let mut preprocessors: Vec> = Vec::new(); - - for key in preprocess_list { + for key in preprocessor_keys { match key.as_ref() { "links" => preprocessors.push(Box::new(LinkPreprocessor::new())), "index" => preprocessors.push(Box::new(IndexPreprocessor::new())), @@ -366,6 +398,31 @@ fn interpret_custom_renderer(key: &str, table: &Value) -> Box { Box::new(CmdRenderer::new(key.to_string(), command.to_string())) } +/// Check whether we should run a particular `Preprocessor` in combination +/// with the renderer, falling back to `Preprocessor::supports_renderer()` +/// method if the user doesn't say anything. +/// +/// The `build.use-default-preprocessors` config option can be used to ensure +/// default preprocessors always run if they support the renderer. +fn preprocessor_should_run(preprocessor: &Preprocessor, renderer: &Renderer, cfg: &Config) -> bool { + // default preprocessors should be run by default (if supported) + if cfg.build.use_default_preprocessors && is_default_preprocessor(preprocessor) { + return preprocessor.supports_renderer(renderer.name()); + } + + let key = format!("preprocessor.{}.renderers", preprocessor.name()); + let renderer_name = renderer.name(); + + if let Some(Value::Array(ref explicit_renderers)) = cfg.get(&key) { + return explicit_renderers.into_iter() + .filter_map(|val| val.as_str()) + .any(|name| name == renderer_name); + } + + preprocessor.supports_renderer(renderer_name) +} + + #[cfg(test)] mod tests { use super::*; @@ -413,8 +470,8 @@ mod tests { fn config_defaults_to_link_and_index_preprocessor_if_not_set() { let cfg = Config::default(); - // make sure we haven't got anything in the `output` table - assert!(cfg.build.preprocess.is_none()); + // make sure we haven't got anything in the `preprocessor` table + assert!(cfg.get("preprocessor").is_none()); let got = determine_preprocessors(&cfg); @@ -425,47 +482,88 @@ mod tests { } #[test] - fn config_doesnt_default_if_empty() { + fn use_default_preprocessors_works() { + let mut cfg = Config::default(); + cfg.build.use_default_preprocessors = false; + + let got = determine_preprocessors(&cfg).unwrap(); + + assert_eq!(got.len(), 0); + } + + #[test] + fn config_complains_if_unimplemented_preprocessor() { let cfg_str: &'static str = r#" [book] title = "Some Book" + [preprocessor.random] + [build] build-dir = "outputs" create-missing = false - preprocess = [] "#; let cfg = Config::from_str(cfg_str).unwrap(); - // make sure we have something in the `output` table - assert!(cfg.build.preprocess.is_some()); + // make sure the `preprocessor.random` table exists + assert!(cfg.get_preprocessor("random").is_some()); let got = determine_preprocessors(&cfg); - assert!(got.is_ok()); - assert!(got.unwrap().is_empty()); + assert!(got.is_err()); } #[test] - fn config_complains_if_unimplemented_preprocessor() { + fn config_respects_preprocessor_selection() { let cfg_str: &'static str = r#" - [book] - title = "Some Book" - - [build] - build-dir = "outputs" - create-missing = false - preprocess = ["random"] + [preprocessor.links] + renderers = ["html"] "#; let cfg = Config::from_str(cfg_str).unwrap(); - // make sure we have something in the `output` table - assert!(cfg.build.preprocess.is_some()); + // double-check that we can access preprocessor.links.renderers[0] + let html = cfg.get_preprocessor("links") + .and_then(|links| links.get("renderers")) + .and_then(|renderers| renderers.as_array()) + .and_then(|renderers| renderers.get(0)) + .and_then(|renderer| renderer.as_str()) + .unwrap(); + assert_eq!(html, "html"); + let html_renderer = HtmlHandlebars::default(); + let pre = LinkPreprocessor::new(); + + let should_run = preprocessor_should_run(&pre, &html_renderer, &cfg); + assert!(should_run); + } - let got = determine_preprocessors(&cfg); + struct BoolPreprocessor(bool); + impl Preprocessor for BoolPreprocessor { + fn name(&self) -> &str { + "bool-preprocessor" + } - assert!(got.is_err()); + fn run(&self, _ctx: &PreprocessorContext, _book: Book) -> Result { + unimplemented!() + } + + fn supports_renderer(&self, _renderer: &str) -> bool { + self.0 + } + } + + #[test] + fn preprocessor_should_run_falls_back_to_supports_renderer_method() { + let cfg = Config::default(); + let html = HtmlHandlebars::new(); + + let should_be = true; + let got = preprocessor_should_run(&BoolPreprocessor(should_be), &html, &cfg); + assert_eq!(got, should_be); + + let should_be = false; + let got = preprocessor_should_run(&BoolPreprocessor(should_be), &html, &cfg); + assert_eq!(got, should_be); } } diff --git a/src/config.rs b/src/config.rs index 338d7191d4..655d3da0a0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -209,6 +209,18 @@ impl Config { Ok(()) } + /// Get the table associated with a particular renderer. + pub fn get_renderer>(&self, index: I) -> Option<&Table> { + let key = format!("output.{}", index.as_ref()); + self.get(&key).and_then(|v| v.as_table()) + } + + /// Get the table associated with a particular preprocessor. + pub fn get_preprocessor>(&self, index: I) -> Option<&Table> { + let key = format!("preprocessor.{}", index.as_ref()); + self.get(&key).and_then(|v| v.as_table()) + } + fn from_legacy(mut table: Value) -> Config { let mut cfg = Config::default(); @@ -382,8 +394,9 @@ pub struct BuildConfig { /// Should non-existent markdown files specified in `SETTINGS.md` be created /// if they don't exist? pub create_missing: bool, - /// Which preprocessors should be applied - pub preprocess: Option>, + /// Should the default preprocessors always be used when they are + /// compatible with the renderer? + pub use_default_preprocessors: bool, } impl Default for BuildConfig { @@ -391,7 +404,7 @@ impl Default for BuildConfig { BuildConfig { build_dir: PathBuf::from("book"), create_missing: true, - preprocess: None, + use_default_preprocessors: true, } } } @@ -551,7 +564,7 @@ mod tests { [build] build-dir = "outputs" create-missing = false - preprocess = ["first_preprocessor", "second_preprocessor"] + use-default-preprocessors = true [output.html] theme = "./themedir" @@ -562,6 +575,10 @@ mod tests { [output.html.playpen] editable = true editor = "ace" + + [preprocess.first] + + [preprocess.second] "#; #[test] @@ -579,10 +596,7 @@ mod tests { let build_should_be = BuildConfig { build_dir: PathBuf::from("outputs"), create_missing: false, - preprocess: Some(vec![ - "first_preprocessor".to_string(), - "second_preprocessor".to_string(), - ]), + use_default_preprocessors: true, }; let playpen_should_be = Playpen { editable: true, @@ -684,7 +698,7 @@ mod tests { let build_should_be = BuildConfig { build_dir: PathBuf::from("my-book"), create_missing: true, - preprocess: None, + use_default_preprocessors: true, }; let html_should_be = HtmlConfig { diff --git a/src/preprocess/index.rs b/src/preprocess/index.rs index 0ec89b806f..5560db54b8 100644 --- a/src/preprocess/index.rs +++ b/src/preprocess/index.rs @@ -11,6 +11,8 @@ use book::{Book, BookItem}; pub struct IndexPreprocessor; impl IndexPreprocessor { + pub(crate) const NAME: &'static str = "index"; + /// Create a new `IndexPreprocessor`. pub fn new() -> Self { IndexPreprocessor @@ -19,10 +21,10 @@ impl IndexPreprocessor { impl Preprocessor for IndexPreprocessor { fn name(&self) -> &str { - "index" + Self::NAME } - fn run(&self, ctx: &PreprocessorContext, book: &mut Book) -> Result<()> { + fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result { let source_dir = ctx.root.join(&ctx.config.book.src); book.for_each_mut(|section: &mut BookItem| { if let BookItem::Chapter(ref mut ch) = *section { @@ -37,7 +39,7 @@ impl Preprocessor for IndexPreprocessor { } }); - Ok(()) + Ok(book) } } diff --git a/src/preprocess/links.rs b/src/preprocess/links.rs index 696919df3e..870f96a8d9 100644 --- a/src/preprocess/links.rs +++ b/src/preprocess/links.rs @@ -16,6 +16,8 @@ const MAX_LINK_NESTED_DEPTH: usize = 10; pub struct LinkPreprocessor; impl LinkPreprocessor { + pub(crate) const NAME: &'static str = "links"; + /// Create a new `LinkPreprocessor`. pub fn new() -> Self { LinkPreprocessor @@ -24,10 +26,10 @@ impl LinkPreprocessor { impl Preprocessor for LinkPreprocessor { fn name(&self) -> &str { - "links" + Self::NAME } - fn run(&self, ctx: &PreprocessorContext, book: &mut Book) -> Result<()> { + fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result { let src_dir = ctx.root.join(&ctx.config.book.src); book.for_each_mut(|section: &mut BookItem| { @@ -43,7 +45,7 @@ impl Preprocessor for LinkPreprocessor { } }); - Ok(()) + Ok(book) } } diff --git a/src/preprocess/mod.rs b/src/preprocess/mod.rs index 57a18f0c63..5f59c5bf71 100644 --- a/src/preprocess/mod.rs +++ b/src/preprocess/mod.rs @@ -19,12 +19,14 @@ pub struct PreprocessorContext { pub root: PathBuf, /// The book configuration (`book.toml`). pub config: Config, + /// The `Renderer` this preprocessor is being used with. + pub renderer: String, } impl PreprocessorContext { /// Create a new `PreprocessorContext`. - pub(crate) fn new(root: PathBuf, config: Config) -> Self { - PreprocessorContext { root, config } + pub(crate) fn new(root: PathBuf, config: Config, renderer: String) -> Self { + PreprocessorContext { root, config, renderer } } } @@ -36,5 +38,13 @@ pub trait Preprocessor { /// Run this `Preprocessor`, allowing it to update the book before it is /// given to a renderer. - fn run(&self, ctx: &PreprocessorContext, book: &mut Book) -> Result<()>; + fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result; + + /// A hint to `MDBook` whether this preprocessor is compatible with a + /// particular renderer. + /// + /// By default, always returns `true`. + fn supports_renderer(&self, _renderer: &str) -> bool { + true + } } diff --git a/tests/build_process.rs b/tests/build_process.rs new file mode 100644 index 0000000000..85750ab32a --- /dev/null +++ b/tests/build_process.rs @@ -0,0 +1,80 @@ +extern crate mdbook; + +mod dummy_book; + +use dummy_book::DummyBook; +use mdbook::book::Book; +use mdbook::config::Config; +use mdbook::errors::*; +use mdbook::preprocess::{Preprocessor, PreprocessorContext}; +use mdbook::renderer::{RenderContext, Renderer}; +use mdbook::MDBook; +use std::sync::{Arc, Mutex}; + +struct Spy(Arc>); + +#[derive(Debug, Default)] +struct Inner { + run_count: usize, + rendered_with: Vec, +} + +impl Preprocessor for Spy { + fn name(&self) -> &str { + "dummy" + } + + fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result { + let mut inner = self.0.lock().unwrap(); + inner.run_count += 1; + inner.rendered_with.push(ctx.renderer.clone()); + Ok(book) + } +} + +impl Renderer for Spy { + fn name(&self) -> &str { + "dummy" + } + + fn render(&self, _ctx: &RenderContext) -> Result<()> { + let mut inner = self.0.lock().unwrap(); + inner.run_count += 1; + Ok(()) + } +} + +#[test] +fn mdbook_runs_preprocessors() { + let spy: Arc> = Default::default(); + + let temp = DummyBook::new().build().unwrap(); + let cfg = Config::default(); + + let mut book = MDBook::load_with_config(temp.path(), cfg).unwrap(); + book.with_preprecessor(Spy(Arc::clone(&spy))); + book.build().unwrap(); + + let inner = spy.lock().unwrap(); + assert_eq!(inner.run_count, 1); + assert_eq!(inner.rendered_with.len(), 1); + assert_eq!( + "html", inner.rendered_with[0], + "We should have been run with the default HTML renderer" + ); +} + +#[test] +fn mdbook_runs_renderers() { + let spy: Arc> = Default::default(); + + let temp = DummyBook::new().build().unwrap(); + let cfg = Config::default(); + + let mut book = MDBook::load_with_config(temp.path(), cfg).unwrap(); + book.with_renderer(Spy(Arc::clone(&spy))); + book.build().unwrap(); + + let inner = spy.lock().unwrap(); + assert_eq!(inner.run_count, 1); +} diff --git a/tests/testing.rs b/tests/testing.rs index 48e028923a..23e1737194 100644 --- a/tests/testing.rs +++ b/tests/testing.rs @@ -4,14 +4,8 @@ mod dummy_book; use dummy_book::DummyBook; -use mdbook::book::Book; -use mdbook::config::Config; -use mdbook::errors::*; -use mdbook::preprocess::{Preprocessor, PreprocessorContext}; use mdbook::MDBook; -use std::sync::{Arc, Mutex}; - #[test] fn mdbook_can_correctly_test_a_passing_book() { let temp = DummyBook::new().with_passing_test(true).build().unwrap(); @@ -27,30 +21,3 @@ fn mdbook_detects_book_with_failing_tests() { assert!(md.test(vec![]).is_err()); } - -#[test] -fn mdbook_runs_preprocessors() { - let has_run: Arc> = Arc::new(Mutex::new(false)); - - struct DummyPreprocessor(Arc>); - - impl Preprocessor for DummyPreprocessor { - fn name(&self) -> &str { - "dummy" - } - - fn run(&self, _ctx: &PreprocessorContext, _book: &mut Book) -> Result<()> { - *self.0.lock().unwrap() = true; - Ok(()) - } - } - - let temp = DummyBook::new().build().unwrap(); - let cfg = Config::default(); - - let mut book = MDBook::load_with_config(temp.path(), cfg).unwrap(); - book.with_preprecessor(DummyPreprocessor(Arc::clone(&has_run))); - book.build().unwrap(); - - assert!(*has_run.lock().unwrap()) -}