diff --git a/Cargo.toml b/Cargo.toml index 2fbe38a5..24b20596 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ description = "create books from markdown files (like Gitbook)" documentation = "http://azerupi.github.io/mdBook/index.html" repository = "https://github.com/azerupi/mdBook" keywords = ["book", "gitbook", "rustbook", "markdown"] -license = "MPL" +license = "MPL-2.0" readme = "README.md" exclude = [ "book-example/*", diff --git a/src/bin/mdbook.rs b/src/bin/mdbook.rs index cd508173..6057b214 100644 --- a/src/bin/mdbook.rs +++ b/src/bin/mdbook.rs @@ -24,9 +24,9 @@ fn main() { .after_help("For more information about a specific command, try `mdbook --help`") .subcommand(SubCommand::with_name("init") .about("Create boilerplate structure and files in the directory") - // the {n} denotes a newline which will properly aligned in all help - // messages - .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'")) + // the {n} denotes a newline which will properly aligned in all help messages + .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'") + .arg_from_usage("--theme 'Copies the default theme into your source folder'")) .subcommand(SubCommand::with_name("build") .about("Build the book from the markdown files") .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'")) @@ -43,22 +43,47 @@ fn main() { }; if let Err(e) = res { - writeln!(&mut io::stderr(), "Error: {}", e).ok(); + writeln!(&mut io::stderr(), "An error occured:\n{}", e).ok(); } } fn init(args: &ArgMatches) -> Result<(), Box> { + let book_dir = get_book_dir(args); let book = MDBook::new(&book_dir); - book.init() + // Call the function that does the initialization + try!(book.init()); + + // If flag `--theme` is present, copy theme to src + if args.is_present("theme") { + + // Print warning + print!("\nCopying the default theme to {:?}", book.get_src()); + println!("could potentially overwrite files already present in that directory."); + print!("\nAre you sure you want to continue? (y/n) "); + + // Read answer from user + + // Joke while I don't read user response, has to be deleted when merged into master !!! + println!("\n\nI am doing it anyways... (at the moment)"); + + // Call the function that copies the theme + try!(book.copy_theme()); + + println!(""); + } + + Ok(()) } fn build(args: &ArgMatches) -> Result<(), Box> { let book_dir = get_book_dir(args); let mut book = MDBook::new(&book_dir).read_config(); - book.build() + try!(book.build()); + + Ok(()) } fn get_book_dir(args: &ArgMatches) -> PathBuf { diff --git a/src/book/bookconfig.rs b/src/book/bookconfig.rs index 5dace266..73fa35b7 100644 --- a/src/book/bookconfig.rs +++ b/src/book/bookconfig.rs @@ -22,7 +22,7 @@ impl BookConfig { author: String::new(), dest: PathBuf::from("book"), src: PathBuf::from("src"), - indent_spaces: 4, + indent_spaces: 4, // indentation used for SUMMARY.md multilingual: false, } } @@ -60,7 +60,7 @@ impl BookConfig { // If path is relative make it absolute from the parent directory of src if dest.is_relative() { - let dest = &self.src().parent().unwrap().join(&dest); + let dest = &self.get_src().parent().unwrap().join(&dest); self.set_dest(dest); } } @@ -68,7 +68,7 @@ impl BookConfig { self } - pub fn dest(&self) -> &Path { + pub fn get_dest(&self) -> &Path { &self.dest } @@ -77,7 +77,7 @@ impl BookConfig { self } - pub fn src(&self) -> &Path { + pub fn get_src(&self) -> &Path { &self.src } diff --git a/src/book/mdbook.rs b/src/book/mdbook.rs index 4bccc3c3..e32d8481 100644 --- a/src/book/mdbook.rs +++ b/src/book/mdbook.rs @@ -6,6 +6,7 @@ use std::error::Error; use {BookConfig, BookItem}; use book::BookItems; use parse; +use theme; use renderer::Renderer; use renderer::HtmlHandlebars; @@ -96,8 +97,8 @@ impl MDBook { debug!("[fn]: init"); - let dest = self.config.dest(); - let src = self.config.src(); + let dest = self.config.get_dest(); + let src = self.config.get_src(); // Hacky way to check if the directory exists... Until PathExt moves to stable match metadata(&dest) { @@ -169,6 +170,45 @@ impl MDBook { } + pub fn copy_theme(&self) -> Result<(), Box> { + debug!("[fn]: copy_theme"); + + let theme_dir = self.config.get_src().join("theme"); + + // Hacky way to check if the directory exists... Until PathExt moves to stable + match metadata(&theme_dir) { + Err(_) => { + // There is a very high chance that the error is due to the fact that + // the directory / file does not exist + debug!("[*]: {:?} does not exist, trying to create directory", theme_dir); + fs::create_dir(&theme_dir).unwrap(); + }, + Ok(_) => { /* If there is no error, the directory / file does exist */ } + } + + // index.hbs + let mut index = try!(File::create(&theme_dir.join("index.hbs"))); + try!(index.write_all(theme::INDEX)); + + // book.css + let mut css = try!(File::create(&theme_dir.join("book.css"))); + try!(css.write_all(theme::CSS)); + + // book.js + let mut js = try!(File::create(&theme_dir.join("book.js"))); + try!(js.write_all(theme::JS)); + + // highlight.css + let mut highlight_css = try!(File::create(&theme_dir.join("highlight.css"))); + try!(highlight_css.write_all(theme::HIGHLIGHT_CSS)); + + // highlight.js + let mut highlight_js = try!(File::create(&theme_dir.join("highlight.js"))); + try!(highlight_js.write_all(theme::HIGHLIGHT_JS)); + + Ok(()) + } + /// 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` @@ -220,11 +260,19 @@ impl MDBook { self } + pub fn get_dest(&self) -> &Path { + self.config.get_dest() + } + pub fn set_src(mut self, src: &Path) -> Self { self.config.set_src(&self.root.join(src)); self } + pub fn get_src(&self) -> &Path { + self.config.get_src() + } + pub fn set_title(mut self, title: &str) -> Self { self.config.title = title.to_owned(); self @@ -240,7 +288,7 @@ impl MDBook { fn parse_summary(&mut self) -> Result<(), Box> { // When append becomes stable, use self.content.append() ... - let book_items = try!(parse::construct_bookitems(&self.config.src().join("SUMMARY.md"))); + let book_items = try!(parse::construct_bookitems(&self.config.get_src().join("SUMMARY.md"))); for item in book_items { self.content.push(item) diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 106b6066..ef9e01f5 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -31,11 +31,11 @@ impl Renderer for HtmlHandlebars { let mut handlebars = Handlebars::new(); // Load theme - let theme = theme::Theme::new(&config.src()); + let theme = theme::Theme::new(&config.get_src()); // Register template debug!("[*]: Register handlebars template"); - try!(handlebars.register_template_string("index", theme.index.to_owned())); + try!(handlebars.register_template_string("index", try!(String::from_utf8(theme.index)))); // Register helpers debug!("[*]: Register handlebars helpers"); @@ -47,7 +47,7 @@ impl Renderer for HtmlHandlebars { // Check if dest directory exists debug!("[*]: Check if destination directory exists"); - match utils::create_path(config.dest()) { + match utils::create_path(config.get_dest()) { Err(_) => return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Unexcpected error when constructing destination path"))), _ => {}, }; @@ -58,7 +58,7 @@ impl Renderer for HtmlHandlebars { if item.path != PathBuf::new() { - let path = config.src().join(&item.path); + let path = config.get_src().join(&item.path); debug!("[*]: Opening file: {:?}", path); let mut f = try!(File::open(&path)); @@ -86,10 +86,10 @@ impl Renderer for HtmlHandlebars { debug!("[*]: Render template"); let rendered = try!(handlebars.render("index", &data)); - debug!("[*]: Create file {:?}", &config.dest().join(&item.path).with_extension("html")); + debug!("[*]: Create file {:?}", &config.get_dest().join(&item.path).with_extension("html")); // Write to file - let mut file = try!(utils::create_file(&config.dest().join(&item.path).with_extension("html"))); - output!("[*] Creating {:?} ✓", &config.dest().join(&item.path).with_extension("html")); + let mut file = try!(utils::create_file(&config.get_dest().join(&item.path).with_extension("html"))); + output!("[*] Creating {:?} ✓", &config.get_dest().join(&item.path).with_extension("html")); try!(file.write_all(&rendered.into_bytes())); @@ -97,13 +97,13 @@ impl Renderer for HtmlHandlebars { if index { debug!("[*]: index.html"); try!(fs::copy( - config.dest().join(&item.path.with_extension("html")), - config.dest().join("index.html") + config.get_dest().join(&item.path.with_extension("html")), + config.get_dest().join("index.html") )); output!( "[*] Creating index.html from {:?} ✓", - config.dest().join(&item.path.with_extension("html")) + config.get_dest().join(&item.path.with_extension("html")) ); index = false; } @@ -114,17 +114,17 @@ impl Renderer for HtmlHandlebars { debug!("[*] Copy static files"); // JavaScript - let mut js_file = try!(File::create(config.dest().join("book.js"))); + let mut js_file = try!(File::create(config.get_dest().join("book.js"))); try!(js_file.write_all(&theme.js)); // Css - let mut css_file = try!(File::create(config.dest().join("book.css"))); + let mut css_file = try!(File::create(config.get_dest().join("book.css"))); try!(css_file.write_all(&theme.css)); // syntax highlighting - let mut highlight_css = try!(File::create(config.dest().join("highlight.css"))); + let mut highlight_css = try!(File::create(config.get_dest().join("highlight.css"))); try!(highlight_css.write_all(&theme.highlight_css)); - let mut highlight_js = try!(File::create(config.dest().join("highlight.js"))); + let mut highlight_js = try!(File::create(config.get_dest().join("highlight.js"))); try!(highlight_js.write_all(&theme.highlight_js)); Ok(()) diff --git a/src/theme/mod.rs b/src/theme/mod.rs index 0a3d0f85..34041510 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -2,14 +2,20 @@ use std::path::Path; use std::fs::{File, metadata}; use std::io::Read; -static INDEX: &'static str = include_str!("index.hbs"); -static CSS: &'static [u8] = include_bytes!("book.css"); -static JS: &'static [u8] = include_bytes!("book.js"); -static HIGHLIGHT_JS: &'static [u8] = include_bytes!("highlight.js"); -static HIGHLIGHT_CSS: &'static [u8] = include_bytes!("highlight.css"); +pub static INDEX: &'static [u8] = include_bytes!("index.hbs"); +pub static CSS: &'static [u8] = include_bytes!("book.css"); +pub static JS: &'static [u8] = include_bytes!("book.js"); +pub static HIGHLIGHT_JS: &'static [u8] = include_bytes!("highlight.js"); +pub static HIGHLIGHT_CSS: &'static [u8] = include_bytes!("highlight.css"); +/// The `Theme` struct should be used instead of the static variables because the `new()` method +/// will look if the user has a theme directory in his source folder and use the users theme instead +/// of the default. +/// +/// You should exceptionnaly use the static variables only if you need the default theme even if the +/// user has specified another theme. pub struct Theme { - pub index: String, + pub index: Vec, pub css: Vec, pub js: Vec, pub highlight_css: Vec, @@ -56,8 +62,8 @@ impl Theme { // index.hbs match File::open(&src.join("index.hbs")) { Ok(mut f) => { - theme.index = String::new(); // Reset the value, because read_to_string appends... - f.read_to_string(&mut theme.index).unwrap(); + theme.index.clear(); // Reset the value, because read_to_string appends... + f.read_to_end(&mut theme.index).unwrap(); }, _ => {}, }