use std::fs::{self, File}; use std::path::PathBuf; use std::io::Write; use toml; use config::Config; use super::MDBook; use theme; use errors::*; /// A helper for setting up a new book and its directory structure. #[derive(Debug, Clone, PartialEq)] pub struct BookBuilder { root: PathBuf, create_gitignore: bool, config: Config, copy_theme: bool, } impl BookBuilder { pub fn new>(root: P) -> BookBuilder { BookBuilder { root: root.into(), create_gitignore: false, config: Config::default(), copy_theme: false, } } /// Set the `Config` to be used. pub fn with_config(&mut self, cfg: Config) -> &mut BookBuilder { self.config = cfg; self } pub fn copy_theme(&mut self, copy: bool) -> &mut BookBuilder { self.copy_theme = copy; self } pub fn create_gitignore(&mut self, create: bool) -> &mut BookBuilder { self.create_gitignore = create; self } pub fn config(&self) -> &Config { &self.config } pub fn build(&self) -> Result { info!("Creating a new book with stub content"); self.create_directory_structure() .chain_err(|| "Unable to create directory structure")?; self.create_stub_files() .chain_err(|| "Unable to create stub files")?; if self.create_gitignore { self.build_gitignore() .chain_err(|| "Unable to create .gitignore")?; } if self.copy_theme { self.copy_across_theme() .chain_err(|| "Unable to copy across the theme")?; } self.write_book_toml()?; let book = MDBook::load(&self.root) .expect("The BookBuilder should always create a valid book. \ If you are seeing this it is a bug and should be reported."); Ok(book) } fn write_book_toml(&self) -> Result<()> { debug!("[*] Writing book.toml"); let book_toml = self.root.join("book.toml"); let cfg = toml::to_vec(&self.config) .chain_err(|| "Unable to serialize the config")?; File::create(book_toml) .chain_err(|| "Couldn't create book.toml")? .write_all(&cfg) .chain_err(|| "Unable to write config to book.toml")?; Ok(()) } fn copy_across_theme(&self) -> Result<()> { debug!("[*] Copying theme"); let themedir = self.config.html_config() .and_then(|html| html.theme) .unwrap_or_else(|| self.config.book.src.join("theme")); let themedir = self.root.join(themedir); if !themedir.exists() { debug!("[*]: {:?} does not exist, creating the directory", themedir); fs::create_dir(&themedir)?; } let mut index = File::create(themedir.join("index.hbs"))?; index.write_all(theme::INDEX)?; let mut css = File::create(themedir.join("book.css"))?; css.write_all(theme::CSS)?; let mut favicon = File::create(themedir.join("favicon.png"))?; favicon.write_all(theme::FAVICON)?; let mut js = File::create(themedir.join("book.js"))?; js.write_all(theme::JS)?; let mut highlight_css = File::create(themedir.join("highlight.css"))?; highlight_css.write_all(theme::HIGHLIGHT_CSS)?; let mut highlight_js = File::create(themedir.join("highlight.js"))?; highlight_js.write_all(theme::HIGHLIGHT_JS)?; Ok(()) } fn build_gitignore(&self) -> Result<()> { debug!("[*]: Creating .gitignore"); Ok(()) } fn create_stub_files(&self) -> Result<()> { debug!("[*] Creating example book contents"); let src_dir = self.root.join(&self.config.book.src); let summary = src_dir.join("SUMMARY.md"); let mut f = File::create(&summary).chain_err(|| "Unable to create SUMMARY.md")?; writeln!(f, "# Summary")?; writeln!(f, "")?; writeln!(f, "- [Chapter 1](./chapter_1.md)")?; let chapter_1 = src_dir.join("chapter_1.md"); let mut f = File::create(&chapter_1).chain_err(|| "Unable to create chapter_1.md")?; writeln!(f, "# Chapter 1")?; Ok(()) } fn create_directory_structure(&self) -> Result<()> { debug!("[*]: Creating directory tree"); fs::create_dir_all(&self.root)?; let src = self.root.join(&self.config.book.src); fs::create_dir_all(&src)?; let build = self.root.join(&self.config.build.build_dir); fs::create_dir_all(&build)?; Ok(()) } }