From 01df904bb385a51e166d2f79705c93d16cedd829 Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Sun, 7 Jan 2018 15:24:37 +0000 Subject: [PATCH 01/18] Initial Preprocessor trait implementation --- src/book/mod.rs | 15 ++++++++++++++- src/preprocess/mod.rs | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 4101f6a9..a50726d3 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -23,7 +23,7 @@ use toml::Value; use utils; use renderer::{CmdRenderer, HtmlHandlebars, RenderContext, Renderer}; -use preprocess; +use preprocess::{self, Preprocessor}; use errors::*; use config::Config; @@ -40,6 +40,9 @@ pub struct MDBook { /// The URL used for live reloading when serving up the book. pub livereload: Option, + + /// List of pre-processors to be run on the book + preprocessors: Vec> } impl MDBook { @@ -86,12 +89,15 @@ impl MDBook { let renderers = determine_renderers(&config); + let preprocessors = vec![]; + Ok(MDBook { root, config, book, renderers, livereload, + preprocessors, }) } @@ -192,6 +198,13 @@ impl MDBook { self } + /// You can add a new preprocessor by using this method. + /// The only requirement is for your renderer to implement the Preprocessor trait. + pub fn with_preprecessor(&mut self, preprocessor: P) -> &mut Self { + self.preprocessors.push(Box::new(preprocessor)); + self + } + /// Run `rustdoc` tests on the book, linking against the provided libraries. pub fn test(&mut self, library_paths: Vec<&str>) -> Result<()> { let library_args: Vec<&str> = (0..library_paths.len()) diff --git a/src/preprocess/mod.rs b/src/preprocess/mod.rs index 19b2f4d4..6f4fb21d 100644 --- a/src/preprocess/mod.rs +++ b/src/preprocess/mod.rs @@ -1 +1,5 @@ pub mod links; + +pub trait Preprocessor { + +} \ No newline at end of file From cad76a9f6c2aac121852b21aaa8b6d24604341a9 Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Sun, 7 Jan 2018 16:21:46 +0000 Subject: [PATCH 02/18] Factor out replace_all preprocessor --- src/book/mod.rs | 21 ++++++++++----- src/preprocess/links.rs | 28 +++++++++++++++++++- src/preprocess/mod.rs | 6 ++++- src/renderer/html_handlebars/hbs_renderer.rs | 9 ------- 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index a50726d3..10a59dfc 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -89,7 +89,7 @@ impl MDBook { let renderers = determine_renderers(&config); - let preprocessors = vec![]; + let preprocessors = determine_preprocessors(&config); Ok(MDBook { root, @@ -215,16 +215,18 @@ impl MDBook { let temp_dir = TempDir::new("mdbook")?; + let replace_all_preprocessor = preprocess::links::ReplaceAllPreprocessor { + src_dir: self.source_dir(), + }; + + replace_all_preprocessor.run(&mut self.book)?; + for item in self.iter() { if let BookItem::Chapter(ref ch) = *item { if !ch.path.as_os_str().is_empty() { let path = self.source_dir().join(&ch.path); - let base = path.parent() - .ok_or_else(|| String::from("Invalid bookitem path!"))?; - let content = utils::fs::file_to_string(&path)?; - // Parse and expand links - let content = preprocess::links::replace_all(&content, base)?; println!("[*]: Testing file: {:?}", path); + let content = utils::fs::file_to_string(&path)?; // write preprocessed file to tempdir let path = temp_dir.path().join(&ch.path); @@ -322,6 +324,13 @@ fn determine_renderers(config: &Config) -> Vec> { renderers } +/// Look at the `Config` and try to figure out what preprocessors to run. +fn determine_preprocessors(config: &Config) -> Vec> { + let mut preprocessors: Vec> = Vec::new(); + + preprocessors +} + fn interpret_custom_renderer(key: &str, table: &Value) -> Box { // look for the `command` field, falling back to using the key // prepended by "mdbook-" diff --git a/src/preprocess/links.rs b/src/preprocess/links.rs index fbc1a2f5..6a92defe 100644 --- a/src/preprocess/links.rs +++ b/src/preprocess/links.rs @@ -5,9 +5,35 @@ use utils::fs::file_to_string; use utils::take_lines; use errors::*; +use super::Preprocessor; +use book::{Book, BookItem}; + const ESCAPE_CHAR: char = '\\'; -pub fn replace_all>(s: &str, path: P) -> Result { +pub struct ReplaceAllPreprocessor { + pub src_dir: PathBuf +} + +impl Preprocessor for ReplaceAllPreprocessor { + fn run(&self, book: &mut Book) -> Result<()> { + for section in &mut book.sections { + match *section { + BookItem::Chapter(ref mut ch) => { + let content = ::std::mem::replace(&mut ch.content, String::new()); + let base = ch.path.parent() + .map(|dir| self.src_dir.join(dir)) + .ok_or_else(|| String::from("Invalid bookitem path!"))?; + ch.content = replace_all(&content, base)? + } + _ => {} + } + } + + Ok(()) + } +} + +fn replace_all>(s: &str, path: P) -> Result { // When replacing one thing in a string by something with a different length, // the indices after that will not correspond, // we therefore have to store the difference to correct this diff --git a/src/preprocess/mod.rs b/src/preprocess/mod.rs index 6f4fb21d..acc954ec 100644 --- a/src/preprocess/mod.rs +++ b/src/preprocess/mod.rs @@ -1,5 +1,9 @@ pub mod links; -pub trait Preprocessor { +use book::Book; +use errors::*; + +pub trait Preprocessor { + fn run(&self, book: &mut Book) -> Result<()>; } \ No newline at end of file diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 694186ee..d0ec14f7 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -1,5 +1,4 @@ use renderer::html_handlebars::helpers; -use preprocess; use renderer::{RenderContext, Renderer}; use book::{Book, BookItem, Chapter}; use config::{Config, HtmlConfig, Playpen}; @@ -50,12 +49,6 @@ impl HtmlHandlebars { match *item { BookItem::Chapter(ref ch) => { let content = ch.content.clone(); - let base = ch.path.parent() - .map(|dir| ctx.src_dir.join(dir)) - .expect("All chapters must have a parent directory"); - - // Parse and expand links - let content = preprocess::links::replace_all(&content, base)?; let content = utils::render_markdown(&content, ctx.html_config.curly_quotes); print_content.push_str(&content); @@ -322,7 +315,6 @@ impl Renderer for HtmlHandlebars { let ctx = RenderItemContext { handlebars: &handlebars, destination: destination.to_path_buf(), - src_dir: src_dir.clone(), data: data.clone(), is_index: i == 0, html_config: html_config.clone(), @@ -634,7 +626,6 @@ fn partition_source(s: &str) -> (String, String) { struct RenderItemContext<'a> { handlebars: &'a Handlebars, destination: PathBuf, - src_dir: PathBuf, data: serde_json::Map, is_index: bool, html_config: HtmlConfig, From 966811061b1be2679492d31da23e61b30cdf5e1a Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Sun, 7 Jan 2018 16:40:17 +0000 Subject: [PATCH 03/18] Start determining preprocessors --- src/book/mod.rs | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 10a59dfc..4c801d6e 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -89,16 +89,19 @@ impl MDBook { let renderers = determine_renderers(&config); - let preprocessors = determine_preprocessors(&config); - - Ok(MDBook { + let mut md_book = MDBook { root, config, book, renderers, livereload, - preprocessors, - }) + preprocessors: vec![], + }; + + let preprocessors = determine_preprocessors(&md_book); + md_book.preprocessors = preprocessors; + + Ok(md_book) } /// Returns a flat depth-first iterator over the elements of the book, @@ -324,10 +327,22 @@ fn determine_renderers(config: &Config) -> Vec> { renderers } -/// Look at the `Config` and try to figure out what preprocessors to run. -fn determine_preprocessors(config: &Config) -> Vec> { +/// Look at the `MDBook` and try to figure out what preprocessors to run. +fn determine_preprocessors(md_book: &MDBook) -> Vec> { let mut preprocessors: Vec> = Vec::new(); + if let Some(preprocess_array) = md_book.config.get("pre_process").and_then(|o| o.as_array()) { + for key in preprocess_array.iter() { + if key.as_str().map_or(false, |key| key == "links") { + let preprocessor = preprocess::links::ReplaceAllPreprocessor { + src_dir: md_book.source_dir(), + }; + + preprocessors.push(Box::new(preprocessor)) + } + } + } + preprocessors } From f282a553fdfa23be7410ad2fcd5115d87904a8a2 Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Sun, 7 Jan 2018 16:43:34 +0000 Subject: [PATCH 04/18] Remove unnecessary mem::replace --- src/preprocess/links.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/preprocess/links.rs b/src/preprocess/links.rs index 6a92defe..1ad63165 100644 --- a/src/preprocess/links.rs +++ b/src/preprocess/links.rs @@ -19,11 +19,11 @@ impl Preprocessor for ReplaceAllPreprocessor { for section in &mut book.sections { match *section { BookItem::Chapter(ref mut ch) => { - let content = ::std::mem::replace(&mut ch.content, String::new()); let base = ch.path.parent() .map(|dir| self.src_dir.join(dir)) .ok_or_else(|| String::from("Invalid bookitem path!"))?; - ch.content = replace_all(&content, base)? + let content = replace_all(&ch.content, base)?; + ch.content = content } _ => {} } From 12815fe399a756022213e768ec63c545a24a0495 Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Sun, 7 Jan 2018 19:08:31 +0000 Subject: [PATCH 05/18] Add pre-processing step to build method of MDBook --- src/book/mod.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 4c801d6e..9e718598 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -160,14 +160,20 @@ impl MDBook { pub fn build(&self) -> Result<()> { debug!("[fn]: build"); + let mut preprocessed_book = self.book.clone(); + + for preprocessor in &self.preprocessors { + preprocessor.run(&mut preprocessed_book)?; + } + for renderer in &self.renderers { - self.run_renderer(renderer.as_ref())?; + self.run_renderer(&preprocessed_book, renderer.as_ref())?; } Ok(()) } - fn run_renderer(&self, renderer: &Renderer) -> Result<()> { + fn run_renderer(&self, preprocessed_book: &Book, renderer: &Renderer) -> Result<()> { let name = renderer.name(); let build_dir = self.build_dir_for(name); if build_dir.exists() { @@ -183,7 +189,7 @@ impl MDBook { let render_context = RenderContext::new( self.root.clone(), - self.book.clone(), + preprocessed_book.clone(), self.config.clone(), build_dir, ); From 4cc708e00f51419e4e0e7afb2cb16418b6a70286 Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Sun, 7 Jan 2018 19:11:51 +0000 Subject: [PATCH 06/18] Preprocess links by default --- src/book/mod.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 9e718598..0dd6edda 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -340,15 +340,23 @@ fn determine_preprocessors(md_book: &MDBook) -> Vec> { if let Some(preprocess_array) = md_book.config.get("pre_process").and_then(|o| o.as_array()) { for key in preprocess_array.iter() { if key.as_str().map_or(false, |key| key == "links") { - let preprocessor = preprocess::links::ReplaceAllPreprocessor { + let replace_all_preprocessor = preprocess::links::ReplaceAllPreprocessor { src_dir: md_book.source_dir(), }; - preprocessors.push(Box::new(preprocessor)) + preprocessors.push(Box::new(replace_all_preprocessor)) } } } + if preprocessors.is_empty() { + let replace_all_preprocessor = preprocess::links::ReplaceAllPreprocessor { + src_dir: md_book.source_dir(), + }; + + preprocessors.push(Box::new(replace_all_preprocessor)) + } + preprocessors } From 144358bec69e5a6ed54b1c9d2496e0804328cf6b Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Sun, 7 Jan 2018 19:25:47 +0000 Subject: [PATCH 07/18] Change name of link preprocessor --- src/book/mod.rs | 21 ++++++++++++--------- src/preprocess/links.rs | 4 ++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 0dd6edda..eac350e3 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -224,7 +224,7 @@ impl MDBook { let temp_dir = TempDir::new("mdbook")?; - let replace_all_preprocessor = preprocess::links::ReplaceAllPreprocessor { + let replace_all_preprocessor = preprocess::links::LinkPreprocessor { src_dir: self.source_dir(), }; @@ -337,24 +337,27 @@ fn determine_renderers(config: &Config) -> Vec> { fn determine_preprocessors(md_book: &MDBook) -> Vec> { let mut preprocessors: Vec> = Vec::new(); - if let Some(preprocess_array) = md_book.config.get("pre_process").and_then(|o| o.as_array()) { + if let Some(preprocess_array) = md_book.config.get("preprocess").and_then(|o| o.as_array()) { for key in preprocess_array.iter() { - if key.as_str().map_or(false, |key| key == "links") { - let replace_all_preprocessor = preprocess::links::ReplaceAllPreprocessor { - src_dir: md_book.source_dir(), - }; + match key.as_str() { + Some(key) if key == "links" => { + let link_preprocessor = preprocess::links::LinkPreprocessor { + src_dir: md_book.source_dir(), + }; - preprocessors.push(Box::new(replace_all_preprocessor)) + preprocessors.push(Box::new(link_preprocessor )) + } + _ => {} } } } if preprocessors.is_empty() { - let replace_all_preprocessor = preprocess::links::ReplaceAllPreprocessor { + let link_preprocessor = preprocess::links::LinkPreprocessor { src_dir: md_book.source_dir(), }; - preprocessors.push(Box::new(replace_all_preprocessor)) + preprocessors.push(Box::new(link_preprocessor)) } preprocessors diff --git a/src/preprocess/links.rs b/src/preprocess/links.rs index 1ad63165..5a99f629 100644 --- a/src/preprocess/links.rs +++ b/src/preprocess/links.rs @@ -10,11 +10,11 @@ use book::{Book, BookItem}; const ESCAPE_CHAR: char = '\\'; -pub struct ReplaceAllPreprocessor { +pub struct LinkPreprocessor { pub src_dir: PathBuf } -impl Preprocessor for ReplaceAllPreprocessor { +impl Preprocessor for LinkPreprocessor { fn run(&self, book: &mut Book) -> Result<()> { for section in &mut book.sections { match *section { From 9c922cf26ba047b80bce5c192a75cc5958caf442 Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Sun, 7 Jan 2018 19:45:28 +0000 Subject: [PATCH 08/18] Add LinkPreprocessor::new constructor --- src/book/mod.rs | 19 +++++++------------ src/preprocess/links.rs | 8 +++++++- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index eac350e3..e0ab9bf7 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -224,9 +224,8 @@ impl MDBook { let temp_dir = TempDir::new("mdbook")?; - let replace_all_preprocessor = preprocess::links::LinkPreprocessor { - src_dir: self.source_dir(), - }; + let src_dir = self.source_dir(); + let replace_all_preprocessor = preprocess::links::LinkPreprocessor::new(src_dir); replace_all_preprocessor.run(&mut self.book)?; @@ -341,11 +340,9 @@ fn determine_preprocessors(md_book: &MDBook) -> Vec> { for key in preprocess_array.iter() { match key.as_str() { Some(key) if key == "links" => { - let link_preprocessor = preprocess::links::LinkPreprocessor { - src_dir: md_book.source_dir(), - }; - - preprocessors.push(Box::new(link_preprocessor )) + let src_dir = md_book.source_dir(); + let link_preprocessor = preprocess::links::LinkPreprocessor::new(src_dir); + preprocessors.push(Box::new(link_preprocessor)) } _ => {} } @@ -353,10 +350,8 @@ fn determine_preprocessors(md_book: &MDBook) -> Vec> { } if preprocessors.is_empty() { - let link_preprocessor = preprocess::links::LinkPreprocessor { - src_dir: md_book.source_dir(), - }; - + let src_dir = md_book.source_dir(); + let link_preprocessor = preprocess::links::LinkPreprocessor::new(src_dir); preprocessors.push(Box::new(link_preprocessor)) } diff --git a/src/preprocess/links.rs b/src/preprocess/links.rs index 5a99f629..7058c07d 100644 --- a/src/preprocess/links.rs +++ b/src/preprocess/links.rs @@ -11,7 +11,13 @@ use book::{Book, BookItem}; const ESCAPE_CHAR: char = '\\'; pub struct LinkPreprocessor { - pub src_dir: PathBuf + src_dir: PathBuf +} + +impl LinkPreprocessor { + pub fn new>(src_dir: P) -> Self { + LinkPreprocessor { src_dir: src_dir.into() } + } } impl Preprocessor for LinkPreprocessor { From b98ed3f79418eb355dafb714f455a3409f65499d Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Sun, 7 Jan 2018 20:05:57 +0000 Subject: [PATCH 09/18] Clean up LinkPreprocessor exports and use explicit PreprocessorContext struct --- src/book/mod.rs | 37 +++++++++++++++++-------------------- src/preprocess/links.rs | 14 ++++++-------- src/preprocess/mod.rs | 11 +++++++++-- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index e0ab9bf7..2734d296 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -23,7 +23,7 @@ use toml::Value; use utils; use renderer::{CmdRenderer, HtmlHandlebars, RenderContext, Renderer}; -use preprocess::{self, Preprocessor}; +use preprocess::{Preprocessor, LinkPreprocessor, PreprocessorContext}; use errors::*; use config::Config; @@ -88,20 +88,16 @@ impl MDBook { let livereload = None; let renderers = determine_renderers(&config); + let preprocessors = determine_preprocessors(&config); - let mut md_book = MDBook { + Ok(MDBook { root, config, book, renderers, livereload, - preprocessors: vec![], - }; - - let preprocessors = determine_preprocessors(&md_book); - md_book.preprocessors = preprocessors; - - Ok(md_book) + preprocessors, + }) } /// Returns a flat depth-first iterator over the elements of the book, @@ -161,9 +157,12 @@ impl MDBook { debug!("[fn]: build"); let mut preprocessed_book = self.book.clone(); + let preprocess_ctx = PreprocessorContext { + src_dir: self.source_dir(), + }; for preprocessor in &self.preprocessors { - preprocessor.run(&mut preprocessed_book)?; + preprocessor.run(&preprocess_ctx, &mut preprocessed_book)?; } for renderer in &self.renderers { @@ -225,9 +224,11 @@ impl MDBook { let temp_dir = TempDir::new("mdbook")?; let src_dir = self.source_dir(); - let replace_all_preprocessor = preprocess::links::LinkPreprocessor::new(src_dir); + let preprocess_context = PreprocessorContext { + src_dir + }; - replace_all_preprocessor.run(&mut self.book)?; + LinkPreprocessor::new().run(&preprocess_context, &mut self.book)?; for item in self.iter() { if let BookItem::Chapter(ref ch) = *item { @@ -333,16 +334,14 @@ fn determine_renderers(config: &Config) -> Vec> { } /// Look at the `MDBook` and try to figure out what preprocessors to run. -fn determine_preprocessors(md_book: &MDBook) -> Vec> { +fn determine_preprocessors(config: &Config) -> Vec> { let mut preprocessors: Vec> = Vec::new(); - if let Some(preprocess_array) = md_book.config.get("preprocess").and_then(|o| o.as_array()) { + if let Some(preprocess_array) = config.get("preprocess").and_then(|o| o.as_array()) { for key in preprocess_array.iter() { match key.as_str() { Some(key) if key == "links" => { - let src_dir = md_book.source_dir(); - let link_preprocessor = preprocess::links::LinkPreprocessor::new(src_dir); - preprocessors.push(Box::new(link_preprocessor)) + preprocessors.push(Box::new(LinkPreprocessor::new())) } _ => {} } @@ -350,9 +349,7 @@ fn determine_preprocessors(md_book: &MDBook) -> Vec> { } if preprocessors.is_empty() { - let src_dir = md_book.source_dir(); - let link_preprocessor = preprocess::links::LinkPreprocessor::new(src_dir); - preprocessors.push(Box::new(link_preprocessor)) + preprocessors.push(Box::new(LinkPreprocessor::new())) } preprocessors diff --git a/src/preprocess/links.rs b/src/preprocess/links.rs index 7058c07d..cbbd71f6 100644 --- a/src/preprocess/links.rs +++ b/src/preprocess/links.rs @@ -5,28 +5,26 @@ use utils::fs::file_to_string; use utils::take_lines; use errors::*; -use super::Preprocessor; +use super::{Preprocessor, PreprocessorContext}; use book::{Book, BookItem}; const ESCAPE_CHAR: char = '\\'; -pub struct LinkPreprocessor { - src_dir: PathBuf -} +pub struct LinkPreprocessor; impl LinkPreprocessor { - pub fn new>(src_dir: P) -> Self { - LinkPreprocessor { src_dir: src_dir.into() } + pub fn new() -> Self { + LinkPreprocessor } } impl Preprocessor for LinkPreprocessor { - fn run(&self, book: &mut Book) -> Result<()> { + fn run(&self, ctx: &PreprocessorContext, book: &mut Book) -> Result<()> { for section in &mut book.sections { match *section { BookItem::Chapter(ref mut ch) => { let base = ch.path.parent() - .map(|dir| self.src_dir.join(dir)) + .map(|dir| ctx.src_dir.join(dir)) .ok_or_else(|| String::from("Invalid bookitem path!"))?; let content = replace_all(&ch.content, base)?; ch.content = content diff --git a/src/preprocess/mod.rs b/src/preprocess/mod.rs index acc954ec..727fad1e 100644 --- a/src/preprocess/mod.rs +++ b/src/preprocess/mod.rs @@ -1,9 +1,16 @@ -pub mod links; +pub use self::links::LinkPreprocessor; + +mod links; use book::Book; use errors::*; +use std::path::PathBuf; + +pub struct PreprocessorContext { + pub src_dir: PathBuf +} pub trait Preprocessor { - fn run(&self, book: &mut Book) -> Result<()>; + fn run(&self, ctx: &PreprocessorContext, book: &mut Book) -> Result<()>; } \ No newline at end of file From 08027b86cc805a61580b196a57e28d5b85559622 Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Sun, 7 Jan 2018 20:10:10 +0000 Subject: [PATCH 10/18] Revert reordering --- src/book/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 2734d296..613b3306 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -234,8 +234,8 @@ impl MDBook { if let BookItem::Chapter(ref ch) = *item { if !ch.path.as_os_str().is_empty() { let path = self.source_dir().join(&ch.path); - println!("[*]: Testing file: {:?}", path); let content = utils::fs::file_to_string(&path)?; + println!("[*]: Testing file: {:?}", path); // write preprocessed file to tempdir let path = temp_dir.path().join(&ch.path); From b5999565167b4aae1e56d1f65dbf50675e5762bd Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Mon, 15 Jan 2018 22:54:14 +0000 Subject: [PATCH 11/18] Move preprocess field location and add tests --- src/book/mod.rs | 92 ++++++++++++++++++++++++++++++++++------- src/config.rs | 8 ++++ src/preprocess/links.rs | 4 ++ src/preprocess/mod.rs | 1 + 4 files changed, 90 insertions(+), 15 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 613b3306..8a9ddd01 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -88,7 +88,7 @@ impl MDBook { let livereload = None; let renderers = determine_renderers(&config); - let preprocessors = determine_preprocessors(&config); + let preprocessors = determine_preprocessors(&config)?; Ok(MDBook { root, @@ -334,25 +334,26 @@ fn determine_renderers(config: &Config) -> Vec> { } /// Look at the `MDBook` and try to figure out what preprocessors to run. -fn determine_preprocessors(config: &Config) -> Vec> { +fn determine_preprocessors(config: &Config) -> Result>> { + + let preprocess_list = match config.build.preprocess { + Some(ref p) => p, + // If no preprocessor field is set, default to the LinkPreprocessor. This allows you + // to disable the LinkPreprocessor by setting "preprocess" to an empty list. + None => return Ok(vec![Box::new(LinkPreprocessor::new())]) + }; + let mut preprocessors: Vec> = Vec::new(); - if let Some(preprocess_array) = config.get("preprocess").and_then(|o| o.as_array()) { - for key in preprocess_array.iter() { - match key.as_str() { - Some(key) if key == "links" => { - preprocessors.push(Box::new(LinkPreprocessor::new())) - } - _ => {} - } + for key in preprocess_list { + if key == "links" { + preprocessors.push(Box::new(LinkPreprocessor::new())) + } else { + bail!("{:?} is not a recognised preprocessor", key); } } - if preprocessors.is_empty() { - preprocessors.push(Box::new(LinkPreprocessor::new())) - } - - preprocessors + Ok(preprocessors) } fn interpret_custom_renderer(key: &str, table: &Value) -> Box { @@ -410,4 +411,65 @@ mod tests { assert_eq!(got.len(), 1); assert_eq!(got[0].name(), "random"); } + + #[test] + fn config_defaults_to_link_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()); + + let got = determine_preprocessors(&cfg); + + assert!(got.is_ok()); + assert_eq!(got.as_ref().unwrap().len(), 1); + assert_eq!(got.as_ref().unwrap()[0].name(), "links"); + } + + #[test] + fn config_doesnt_default_if_empty() { + let cfg_str: &'static str = r#" + [book] + title = "Some Book" + + [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()); + + let got = determine_preprocessors(&cfg); + + assert!(got.is_ok()); + assert!(got.unwrap().is_empty()); + } + + #[test] + fn config_complains_if_unimplemented_preprocessor() { + let cfg_str: &'static str = r#" + [book] + title = "Some Book" + + [build] + build-dir = "outputs" + create-missing = false + preprocess = ["random"] + "#; + + + let cfg = Config::from_str(cfg_str).unwrap(); + + // make sure we have something in the `output` table + assert!(cfg.build.preprocess.is_some()); + + let got = determine_preprocessors(&cfg); + + assert!(got.is_err()); + } } diff --git a/src/config.rs b/src/config.rs index 24c3f96e..e74f1917 100644 --- a/src/config.rs +++ b/src/config.rs @@ -329,6 +329,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>, + } impl Default for BuildConfig { @@ -336,6 +339,7 @@ impl Default for BuildConfig { BuildConfig { build_dir: PathBuf::from("book"), create_missing: true, + preprocess: None, } } } @@ -422,6 +426,7 @@ mod tests { [build] build-dir = "outputs" create-missing = false + preprocess = ["first_preprocessor", "second_preprocessor"] [output.html] theme = "./themedir" @@ -449,6 +454,8 @@ 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()]), }; let playpen_should_be = Playpen { editable: true, @@ -550,6 +557,7 @@ mod tests { let build_should_be = BuildConfig { build_dir: PathBuf::from("my-book"), create_missing: true, + preprocess: None, }; let html_should_be = HtmlConfig { diff --git a/src/preprocess/links.rs b/src/preprocess/links.rs index cbbd71f6..72bc6c8f 100644 --- a/src/preprocess/links.rs +++ b/src/preprocess/links.rs @@ -19,6 +19,10 @@ impl LinkPreprocessor { } impl Preprocessor for LinkPreprocessor { + fn name(&self) -> &str { + "links" + } + fn run(&self, ctx: &PreprocessorContext, book: &mut Book) -> Result<()> { for section in &mut book.sections { match *section { diff --git a/src/preprocess/mod.rs b/src/preprocess/mod.rs index 727fad1e..c31080a1 100644 --- a/src/preprocess/mod.rs +++ b/src/preprocess/mod.rs @@ -12,5 +12,6 @@ pub struct PreprocessorContext { } pub trait Preprocessor { + fn name(&self) -> &str; fn run(&self, ctx: &PreprocessorContext, book: &mut Book) -> Result<()>; } \ No newline at end of file From 4177288b11ba5c965a274e7377bc45c41faef713 Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Tue, 16 Jan 2018 22:13:47 +0000 Subject: [PATCH 12/18] Add test to make sure pre-processors are being run --- src/lib.rs | 2 +- tests/testing.rs | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 163f3619..08c4c37e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,7 +118,7 @@ extern crate toml_query; #[macro_use] extern crate pretty_assertions; -mod preprocess; +pub mod preprocess; pub mod book; pub mod config; pub mod renderer; diff --git a/tests/testing.rs b/tests/testing.rs index 8e060eb7..a61c8ec5 100644 --- a/tests/testing.rs +++ b/tests/testing.rs @@ -1,10 +1,18 @@ extern crate mdbook; +#[macro_use] +extern crate lazy_static; mod dummy_book; use dummy_book::DummyBook; -use mdbook::MDBook; +use mdbook::MDBook; +use mdbook::preprocess::{Preprocessor, PreprocessorContext}; +use mdbook::book::Book; +use mdbook::config::Config; +use mdbook::errors::*; + +use std::sync::Mutex; #[test] fn mdbook_can_correctly_test_a_passing_book() { @@ -21,3 +29,33 @@ fn mdbook_detects_book_with_failing_tests() { assert!(md.test(vec![]).is_err()); } + +#[test] +fn mdbook_runs_preprocessors() { + + lazy_static! { + static ref HAS_RUN: Mutex = Mutex::new(false); + } + + struct DummyPreprocessor; + + impl Preprocessor for DummyPreprocessor { + fn name(&self) -> &str { + "dummy" + } + + fn run(&self, _ctx: &PreprocessorContext, _book: &mut Book) -> Result<()> { + *HAS_RUN.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); + book.build().unwrap(); + + assert!(*HAS_RUN.lock().unwrap()) +} From f2d7b705af5a6ef93c93623327e7381deee8b232 Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Tue, 16 Jan 2018 22:25:51 +0000 Subject: [PATCH 13/18] Pull out default preprocessors into function --- src/book/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 8a9ddd01..bc366e39 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -333,6 +333,10 @@ fn determine_renderers(config: &Config) -> Vec> { renderers } +fn default_preprocessors() -> Vec> { + vec![Box::new(LinkPreprocessor::new())] +} + /// Look at the `MDBook` and try to figure out what preprocessors to run. fn determine_preprocessors(config: &Config) -> Result>> { @@ -340,7 +344,7 @@ fn determine_preprocessors(config: &Config) -> Result>> { Some(ref p) => p, // If no preprocessor field is set, default to the LinkPreprocessor. This allows you // to disable the LinkPreprocessor by setting "preprocess" to an empty list. - None => return Ok(vec![Box::new(LinkPreprocessor::new())]) + None => return Ok(default_preprocessors()) }; let mut preprocessors: Vec> = Vec::new(); From 90fa1b4909df24f2576b22245c38f4a6b7c2ea8a Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Tue, 16 Jan 2018 22:33:09 +0000 Subject: [PATCH 14/18] Turn chained if's into match --- src/book/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index bc366e39..374ab9f1 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -350,10 +350,11 @@ fn determine_preprocessors(config: &Config) -> Result>> { let mut preprocessors: Vec> = Vec::new(); for key in preprocess_list { - if key == "links" { - preprocessors.push(Box::new(LinkPreprocessor::new())) - } else { - bail!("{:?} is not a recognised preprocessor", key); + match key.as_ref() { + "links" => { + preprocessors.push(Box::new(LinkPreprocessor::new())) + } + _ => bail!("{:?} is not a recognised preprocessor", key), } } From 47cc57177d43863c452d714e1973e6efc89a89c5 Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Tue, 16 Jan 2018 23:02:50 +0000 Subject: [PATCH 15/18] Update comment with rustdoc link --- src/book/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 374ab9f1..2bf1b032 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -199,15 +199,14 @@ impl MDBook { } /// You can change the default renderer to another one by using this method. - /// The only requirement is for your renderer to implement the [Renderer - /// trait](../../renderer/renderer/trait.Renderer.html) + /// The only requirement is for your renderer to implement the [`Renderer` + /// trait](../renderer/trait.Renderer.html) pub fn with_renderer(&mut self, renderer: R) -> &mut Self { self.renderers.push(Box::new(renderer)); self } - /// You can add a new preprocessor by using this method. - /// The only requirement is for your renderer to implement the Preprocessor trait. + /// Register a [`Preprocessor`](../preprocess/trait.Preprocessor.html) to be used when rendering the book. pub fn with_preprecessor(&mut self, preprocessor: P) -> &mut Self { self.preprocessors.push(Box::new(preprocessor)); self From 0d62578c7b4fa560535e542b51d366352203fa8e Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Wed, 17 Jan 2018 09:44:52 +0000 Subject: [PATCH 16/18] Make Preprocessor context store config and root --- src/book/mod.rs | 9 ++------- src/preprocess/links.rs | 4 +++- src/preprocess/mod.rs | 10 +++++++++- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/book/mod.rs b/src/book/mod.rs index 2bf1b032..43f5932a 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -157,9 +157,7 @@ impl MDBook { debug!("[fn]: build"); let mut preprocessed_book = self.book.clone(); - let preprocess_ctx = PreprocessorContext { - src_dir: self.source_dir(), - }; + let preprocess_ctx = PreprocessorContext::new(self.root.clone(), self.config.clone()); for preprocessor in &self.preprocessors { preprocessor.run(&preprocess_ctx, &mut preprocessed_book)?; @@ -222,10 +220,7 @@ impl MDBook { let temp_dir = TempDir::new("mdbook")?; - let src_dir = self.source_dir(); - let preprocess_context = PreprocessorContext { - src_dir - }; + let preprocess_context = PreprocessorContext::new(self.root.clone(), self.config.clone()); LinkPreprocessor::new().run(&preprocess_context, &mut self.book)?; diff --git a/src/preprocess/links.rs b/src/preprocess/links.rs index 72bc6c8f..c3cab8bf 100644 --- a/src/preprocess/links.rs +++ b/src/preprocess/links.rs @@ -24,11 +24,13 @@ impl Preprocessor for LinkPreprocessor { } fn run(&self, ctx: &PreprocessorContext, book: &mut Book) -> Result<()> { + let src_dir = ctx.root.join(&ctx.config.book.src); + for section in &mut book.sections { match *section { BookItem::Chapter(ref mut ch) => { let base = ch.path.parent() - .map(|dir| ctx.src_dir.join(dir)) + .map(|dir| src_dir.join(dir)) .ok_or_else(|| String::from("Invalid bookitem path!"))?; let content = replace_all(&ch.content, base)?; ch.content = content diff --git a/src/preprocess/mod.rs b/src/preprocess/mod.rs index c31080a1..091604d6 100644 --- a/src/preprocess/mod.rs +++ b/src/preprocess/mod.rs @@ -3,12 +3,20 @@ pub use self::links::LinkPreprocessor; mod links; use book::Book; +use config::Config; use errors::*; use std::path::PathBuf; pub struct PreprocessorContext { - pub src_dir: PathBuf + pub root: PathBuf, + pub config: Config, +} + +impl PreprocessorContext { + pub fn new(root: PathBuf, config: Config) -> Self { + PreprocessorContext { root, config } + } } pub trait Preprocessor { From 80a20eb7303b5199405efb3f9ddd85b3910b4745 Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Wed, 17 Jan 2018 19:02:25 +0000 Subject: [PATCH 17/18] Use Arc instead of lazy_static for dummy preprocessor --- tests/testing.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/testing.rs b/tests/testing.rs index a61c8ec5..fb6a5e6a 100644 --- a/tests/testing.rs +++ b/tests/testing.rs @@ -1,6 +1,4 @@ extern crate mdbook; -#[macro_use] -extern crate lazy_static; mod dummy_book; @@ -12,7 +10,7 @@ use mdbook::book::Book; use mdbook::config::Config; use mdbook::errors::*; -use std::sync::Mutex; +use std::sync::{Arc, Mutex}; #[test] fn mdbook_can_correctly_test_a_passing_book() { @@ -33,11 +31,9 @@ fn mdbook_detects_book_with_failing_tests() { #[test] fn mdbook_runs_preprocessors() { - lazy_static! { - static ref HAS_RUN: Mutex = Mutex::new(false); - } + let has_run: Arc> = Arc::new(Mutex::new(false)); - struct DummyPreprocessor; + struct DummyPreprocessor(Arc>); impl Preprocessor for DummyPreprocessor { fn name(&self) -> &str { @@ -45,7 +41,7 @@ fn mdbook_runs_preprocessors() { } fn run(&self, _ctx: &PreprocessorContext, _book: &mut Book) -> Result<()> { - *HAS_RUN.lock().unwrap() = true; + *self.0.lock().unwrap() = true; Ok(()) } } @@ -54,8 +50,8 @@ fn mdbook_runs_preprocessors() { let cfg = Config::default(); let mut book = MDBook::load_with_config(temp.path(), cfg).unwrap(); - book.with_preprecessor(DummyPreprocessor); + book.with_preprecessor(DummyPreprocessor(Arc::clone(&has_run))); book.build().unwrap(); - assert!(*HAS_RUN.lock().unwrap()) + assert!(*has_run.lock().unwrap()) } From 1136f671a0295aa2715f28bdeb6e22b04bbb35a1 Mon Sep 17 00:00:00 2001 From: Jaime Valdemoros Date: Wed, 17 Jan 2018 19:05:15 +0000 Subject: [PATCH 18/18] Log as each preprocessor is run --- src/book/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/book/mod.rs b/src/book/mod.rs index 43f5932a..4666e271 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -160,6 +160,7 @@ impl MDBook { let preprocess_ctx = PreprocessorContext::new(self.root.clone(), self.config.clone()); for preprocessor in &self.preprocessors { + debug!("Running the {} preprocessor.", preprocessor.name()); preprocessor.run(&preprocess_ctx, &mut preprocessed_book)?; }