Started working on a MDBookBuilder

This commit is contained in:
Michael Bryan 2017-07-09 03:50:11 +08:00
parent ed83e0ec10
commit 92f0fa0aba
8 changed files with 135 additions and 81 deletions

View File

@ -17,7 +17,7 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
// Build command implementation // Build command implementation
pub fn execute(args: &ArgMatches) -> Result<()> { pub fn execute(args: &ArgMatches) -> Result<()> {
let book_dir = get_book_dir(args); let book_dir = get_book_dir(args);
let book = MDBook::new(&book_dir)?.read_config()?; let book = MDBook::new(&book_dir)?;
let mut book = match args.value_of("dest-dir") { let mut book = match args.value_of("dest-dir") {
Some(dest_dir) => book.with_destination(dest_dir), Some(dest_dir) => book.with_destination(dest_dir),

View File

@ -33,7 +33,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
const RELOAD_COMMAND: &'static str = "reload"; const RELOAD_COMMAND: &'static str = "reload";
let book_dir = get_book_dir(args); let book_dir = get_book_dir(args);
let book = MDBook::new(&book_dir)?.read_config()?; let book = MDBook::new(&book_dir)?;
let mut book = match args.value_of("dest-dir") { let mut book = match args.value_of("dest-dir") {
Some(dest_dir) => book.with_destination(Path::new(dest_dir)), Some(dest_dir) => book.with_destination(Path::new(dest_dir)),

View File

@ -14,7 +14,7 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
pub fn execute(args: &ArgMatches) -> Result<()> { pub fn execute(args: &ArgMatches) -> Result<()> {
let library_paths: Vec<&str> = args.values_of("library-path").map(|v| v.collect()).unwrap_or_default(); let library_paths: Vec<&str> = args.values_of("library-path").map(|v| v.collect()).unwrap_or_default();
let book_dir = get_book_dir(args); let book_dir = get_book_dir(args);
let mut book = MDBook::new(&book_dir)?.read_config()?; let mut book = MDBook::new(&book_dir)?;
book.test(library_paths)?; book.test(library_paths)?;

View File

@ -24,7 +24,7 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
// Watch command implementation // Watch command implementation
pub fn execute(args: &ArgMatches) -> Result<()> { pub fn execute(args: &ArgMatches) -> Result<()> {
let book_dir = get_book_dir(args); let book_dir = get_book_dir(args);
let book = MDBook::new(&book_dir)?.read_config()?; let book = MDBook::new(&book_dir)?;
let mut book = match args.value_of("dest-dir") { let mut book = match args.value_of("dest-dir") {
Some(dest_dir) => book.with_destination(dest_dir), Some(dest_dir) => book.with_destination(dest_dir),

56
src/book/builder.rs Normal file
View File

@ -0,0 +1,56 @@
use std::path::{Path, PathBuf};
use config::{self, BookConfig};
use renderer::{Renderer, HtmlHandlebars};
use loader;
use errors::*;
use super::MDBook;
#[derive(Default)]
pub struct Builder {
root: PathBuf,
create_missing: bool,
config: Option<BookConfig>,
renderer: Option<Box<Renderer>>,
livereload: Option<String>,
}
impl Builder {
/// Create a new builder which loads the book from an existing directory.
pub fn new<P: AsRef<Path>>(root: P) -> Builder {
let root = root.as_ref();
Builder {
root: root.to_path_buf(),
..Default::default()
}
}
/// Set the config to use.
pub fn with_config(mut self, config: BookConfig) -> Self {
self.config = Some(config);
self
}
pub fn build(self) -> Result<MDBook> {
// if no custom config provided, try to read it from disk
let cfg = match self.config {
Some(c) => c,
None => config::read_config(&self.root)?,
};
let book = loader::load_book(cfg.get_source())?;
let renderer: Box<Renderer> = self.renderer.unwrap_or_else(
|| Box::new(HtmlHandlebars::new()),
);
Ok(MDBook {
config: cfg,
book: book,
renderer: renderer,
livereload: self.livereload,
create_missing: self.create_missing,
})
}
}

View File

@ -1,4 +1,6 @@
// pub use self::bookitem::{BookItem, BookItems}; mod builder;
pub use self::builder::Builder;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::fs::{self, File}; use std::fs::{self, File};
@ -11,8 +13,6 @@ use tempdir::TempDir;
use errors::*; use errors::*;
use config::BookConfig; use config::BookConfig;
use config::tomlconfig::TomlConfig;
use config::jsonconfig::JsonConfig;
use loader::{self, Book, BookItem, BookItems, Chapter}; use loader::{self, Book, BookItem, BookItems, Chapter};
@ -58,21 +58,7 @@ impl MDBook {
/// [`set_dest()`](#method.set_dest) /// [`set_dest()`](#method.set_dest)
pub fn new<P: AsRef<Path>>(root: P) -> Result<MDBook> { pub fn new<P: AsRef<Path>>(root: P) -> Result<MDBook> {
Builder::new(root).build()
let root = root.as_ref();
if !root.exists() || !root.is_dir() {
bail!("{:?} No directory with that name", root);
}
let book = loader::load_book(root.join("src"))?;
Ok(MDBook {
config: BookConfig::new(root),
book: book,
renderer: Box::new(HtmlHandlebars::new()),
livereload: None,
create_missing: true,
})
} }
/// Returns a flat depth-first iterator over the elements of the book, /// Returns a flat depth-first iterator over the elements of the book,
@ -82,15 +68,14 @@ impl MDBook {
/// ```no_run /// ```no_run
/// # extern crate mdbook; /// # extern crate mdbook;
/// # use mdbook::MDBook; /// # use mdbook::MDBook;
/// # use mdbook::BookItem; /// # use mdbook::loader::BookItem;
/// # #[allow(unused_variables)] /// # #[allow(unused_variables)]
/// # fn main() { /// # fn run() -> ::errors::Result<()> {
/// # let book = MDBook::new("mybook"); /// # let book = MDBook::new("mybook")?;
/// for item in book.iter() { /// for item in book.iter() {
/// match item { /// match *item {
/// &BookItem::Chapter(ref section, ref chapter) => {}, /// BookItem::Chapter(ref chapter) => {},
/// &BookItem::Affix(ref chapter) => {}, /// BookItem::Separator => {},
/// &BookItem::Spacer => {},
/// } /// }
/// } /// }
/// ///
@ -101,7 +86,9 @@ impl MDBook {
/// // 2. Chapter 2 /// // 2. Chapter 2
/// // /// //
/// // etc. /// // etc.
/// # Ok(())
/// # } /// # }
/// # fn main() { run().unwrap() }
/// ``` /// ```
pub fn iter(&self) -> BookItems { pub fn iter(&self) -> BookItems {
@ -252,36 +239,6 @@ impl MDBook {
) )
} }
/// Parses the `book.json` file (if it exists) to extract
/// the configuration parameters.
/// The `book.json` file should be in the root directory of the book.
/// The root directory is the one specified when creating a new `MDBook`
pub fn read_config(mut self) -> Result<Self> {
let toml = self.get_root().join("book.toml");
let json = self.get_root().join("book.json");
if toml.exists() {
let mut file = File::open(toml)?;
let mut content = String::new();
file.read_to_string(&mut content)?;
let parsed_config = TomlConfig::from_toml(&content)?;
self.config.fill_from_tomlconfig(parsed_config);
} else if json.exists() {
warn!("The JSON configuration file is deprecated, please use the TOML configuration.");
let mut file = File::open(json)?;
let mut content = String::new();
file.read_to_string(&mut content)?;
let parsed_config = JsonConfig::from_json(&content)?;
self.config.fill_from_jsonconfig(parsed_config);
}
Ok(self)
}
/// You can change the default renderer to another one /// You can change the default renderer to another one
/// by using this method. The only requirement /// by using this method. The only requirement
/// is for your renderer to implement the /// is for your renderer to implement the
@ -293,14 +250,16 @@ impl MDBook {
/// use mdbook::renderer::HtmlHandlebars; /// use mdbook::renderer::HtmlHandlebars;
/// ///
/// # #[allow(unused_variables)] /// # #[allow(unused_variables)]
/// fn main() { /// # fn run() -> mdbook::errors::Result<()> {
/// let book = MDBook::new("mybook") /// let book = MDBook::new("mybook")?
/// .set_renderer(Box::new(HtmlHandlebars::new())); /// .set_renderer(Box::new(HtmlHandlebars::new()));
/// ///
/// // In this example we replace the default renderer /// // In this example we replace the default renderer
/// // by the default renderer... /// // by the default renderer...
/// // Don't forget to put your renderer in a Box /// // Don't forget to put your renderer in a Box
/// } /// # Ok(())
/// # }
/// # fn main() { run().unwrap() }
/// ``` /// ```
/// ///
/// **note:** Don't forget to put your renderer in a `Box` /// **note:** Don't forget to put your renderer in a `Box`
@ -343,7 +302,6 @@ impl MDBook {
self.config.get_root() self.config.get_root()
} }
pub fn with_destination<T: Into<PathBuf>>(mut self, destination: T) -> Self { pub fn with_destination<T: Into<PathBuf>>(mut self, destination: T) -> Self {
let root = self.config.get_root().to_owned(); let root = self.config.get_root().to_owned();
self.config.get_mut_html_config().set_destination( self.config.get_mut_html_config().set_destination(

View File

@ -3,7 +3,42 @@ pub mod htmlconfig;
pub mod tomlconfig; pub mod tomlconfig;
pub mod jsonconfig; pub mod jsonconfig;
use std::path::Path;
use std::fs::File;
use std::io::Read;
use errors::*;
// Re-export the config structs // Re-export the config structs
pub use self::bookconfig::BookConfig; pub use self::bookconfig::BookConfig;
pub use self::htmlconfig::HtmlConfig; pub use self::htmlconfig::HtmlConfig;
pub use self::tomlconfig::TomlConfig; pub use self::tomlconfig::TomlConfig;
/// Parses the `book.json` file (if it exists) to extract
/// the configuration parameters.
/// The `book.json` file should be in the root directory of the book.
/// The root directory is the one specified when creating a new `MDBook`
pub fn read_config<P: AsRef<Path>>(root: P) -> Result<BookConfig> {
let root = root.as_ref();
let toml = root.join("book.toml");
let json = root.join("book.json");
if toml.exists() {
let mut file = File::open(toml)?;
let mut content = String::new();
file.read_to_string(&mut content)?;
let cfg = TomlConfig::from_toml(&content)?;
Ok(BookConfig::from_tomlconfig(root, cfg))
} else if json.exists() {
warn!("The JSON configuration file is deprecated, please use the TOML configuration.");
let mut file = File::open(json)?;
let mut content = String::new();
file.read_to_string(&mut content)?;
let jason = jsonconfig::JsonConfig::from_json(&content)?;
Ok(BookConfig::from_jsonconfig(root, jason))
} else {
Err(Error::from("No config file found"))
}
}

View File

@ -11,7 +11,12 @@ use tempdir::TempDir;
// overwrite // overwrite
// values specified earlier. // values specified earlier.
#[test] #[test]
#[ignore]
fn do_not_overwrite_unspecified_config_values() { fn do_not_overwrite_unspecified_config_values() {
// FIXME: This entire test needs to be rewritten to reflect the new builder
// semantics
// This is because the `MDBook` given to you by the builder isn't mutable
// so changing config settings after it's created may or may not be desired
let dir = TempDir::new("mdbook").expect("Could not create a temp dir"); let dir = TempDir::new("mdbook").expect("Could not create a temp dir");
let book = MDBook::init(dir.path()) let book = MDBook::init(dir.path())
@ -24,26 +29,26 @@ fn do_not_overwrite_unspecified_config_values() {
assert_eq!(book.get_source(), dir.path().join("bar")); assert_eq!(book.get_source(), dir.path().join("bar"));
assert_eq!(book.get_destination(), dir.path().join("baz")); assert_eq!(book.get_destination(), dir.path().join("baz"));
// Test when trying to read a config file that does not exist // // Test when trying to read a config file that does not exist
let book = book.read_config().expect("Error reading the config file"); // let book = book.expect("Error reading the config file");
assert_eq!(book.get_root(), dir.path()); // assert_eq!(book.get_root(), dir.path());
assert_eq!(book.get_source(), dir.path().join("bar")); // assert_eq!(book.get_source(), dir.path().join("bar"));
assert_eq!(book.get_destination(), dir.path().join("baz")); // assert_eq!(book.get_destination(), dir.path().join("baz"));
assert_eq!(book.get_mathjax_support(), true); // assert_eq!(book.get_mathjax_support(), true);
// Try with a partial config file // // Try with a partial config file
let file_path = dir.path().join("book.toml"); // let file_path = dir.path().join("book.toml");
let mut f = File::create(file_path).expect("Could not create config file"); // let mut f = File::create(file_path).expect("Could not create config file");
f.write_all(br#"source = "barbaz""#).expect( // f.write_all(br#"source = "barbaz""#).expect(
"Could not write to config file", // "Could not write to config file",
); // );
f.sync_all().expect("Could not sync the file"); // f.sync_all().expect("Could not sync the file");
let book = book.read_config().expect("Error reading the config file"); // let book = book.read_config().expect("Error reading the config file");
assert_eq!(book.get_root(), dir.path()); // assert_eq!(book.get_root(), dir.path());
assert_eq!(book.get_source(), dir.path().join("barbaz")); // assert_eq!(book.get_source(), dir.path().join("barbaz"));
assert_eq!(book.get_destination(), dir.path().join("baz")); // assert_eq!(book.get_destination(), dir.path().join("baz"));
assert_eq!(book.get_mathjax_support(), true); // assert_eq!(book.get_mathjax_support(), true);
} }