Some clean-up + initial implementation of '--theme' flag for init. Still needs some work

This commit is contained in:
Mathieu David 2015-08-11 16:13:41 +02:00
parent 909953d877
commit 835c61c7f3
6 changed files with 115 additions and 36 deletions

View File

@ -6,7 +6,7 @@ description = "create books from markdown files (like Gitbook)"
documentation = "http://azerupi.github.io/mdBook/index.html" documentation = "http://azerupi.github.io/mdBook/index.html"
repository = "https://github.com/azerupi/mdBook" repository = "https://github.com/azerupi/mdBook"
keywords = ["book", "gitbook", "rustbook", "markdown"] keywords = ["book", "gitbook", "rustbook", "markdown"]
license = "MPL" license = "MPL-2.0"
readme = "README.md" readme = "README.md"
exclude = [ exclude = [
"book-example/*", "book-example/*",

View File

@ -24,9 +24,9 @@ fn main() {
.after_help("For more information about a specific command, try `mdbook <command> --help`") .after_help("For more information about a specific command, try `mdbook <command> --help`")
.subcommand(SubCommand::with_name("init") .subcommand(SubCommand::with_name("init")
.about("Create boilerplate structure and files in the directory") .about("Create boilerplate structure and files in the directory")
// the {n} denotes a newline which will properly aligned in all help // the {n} denotes a newline which will properly aligned in all help messages
// messages .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'")
.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") .subcommand(SubCommand::with_name("build")
.about("Build the book from the markdown files") .about("Build the book from the markdown files")
.arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'")) .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 { 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<Error>> { fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
let book_dir = get_book_dir(args); let book_dir = get_book_dir(args);
let book = MDBook::new(&book_dir); 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<Error>> { fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
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).read_config();
book.build() try!(book.build());
Ok(())
} }
fn get_book_dir(args: &ArgMatches) -> PathBuf { fn get_book_dir(args: &ArgMatches) -> PathBuf {

View File

@ -22,7 +22,7 @@ impl BookConfig {
author: String::new(), author: String::new(),
dest: PathBuf::from("book"), dest: PathBuf::from("book"),
src: PathBuf::from("src"), src: PathBuf::from("src"),
indent_spaces: 4, indent_spaces: 4, // indentation used for SUMMARY.md
multilingual: false, multilingual: false,
} }
} }
@ -60,7 +60,7 @@ impl BookConfig {
// If path is relative make it absolute from the parent directory of src // If path is relative make it absolute from the parent directory of src
if dest.is_relative() { 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); self.set_dest(dest);
} }
} }
@ -68,7 +68,7 @@ impl BookConfig {
self self
} }
pub fn dest(&self) -> &Path { pub fn get_dest(&self) -> &Path {
&self.dest &self.dest
} }
@ -77,7 +77,7 @@ impl BookConfig {
self self
} }
pub fn src(&self) -> &Path { pub fn get_src(&self) -> &Path {
&self.src &self.src
} }

View File

@ -6,6 +6,7 @@ use std::error::Error;
use {BookConfig, BookItem}; use {BookConfig, BookItem};
use book::BookItems; use book::BookItems;
use parse; use parse;
use theme;
use renderer::Renderer; use renderer::Renderer;
use renderer::HtmlHandlebars; use renderer::HtmlHandlebars;
@ -96,8 +97,8 @@ impl MDBook {
debug!("[fn]: init"); debug!("[fn]: init");
let dest = self.config.dest(); let dest = self.config.get_dest();
let src = self.config.src(); let src = self.config.get_src();
// Hacky way to check if the directory exists... Until PathExt moves to stable // Hacky way to check if the directory exists... Until PathExt moves to stable
match metadata(&dest) { match metadata(&dest) {
@ -169,6 +170,45 @@ impl MDBook {
} }
pub fn copy_theme(&self) -> Result<(), Box<Error>> {
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. /// 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 `book.json` file should be in the root directory of the book.
/// The root directory is the one specified when creating a new `MDBook` /// The root directory is the one specified when creating a new `MDBook`
@ -220,11 +260,19 @@ impl MDBook {
self self
} }
pub fn get_dest(&self) -> &Path {
self.config.get_dest()
}
pub fn set_src(mut self, src: &Path) -> Self { pub fn set_src(mut self, src: &Path) -> Self {
self.config.set_src(&self.root.join(src)); self.config.set_src(&self.root.join(src));
self self
} }
pub fn get_src(&self) -> &Path {
self.config.get_src()
}
pub fn set_title(mut self, title: &str) -> Self { pub fn set_title(mut self, title: &str) -> Self {
self.config.title = title.to_owned(); self.config.title = title.to_owned();
self self
@ -240,7 +288,7 @@ impl MDBook {
fn parse_summary(&mut self) -> Result<(), Box<Error>> { fn parse_summary(&mut self) -> Result<(), Box<Error>> {
// When append becomes stable, use self.content.append() ... // 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 { for item in book_items {
self.content.push(item) self.content.push(item)

View File

@ -31,11 +31,11 @@ impl Renderer for HtmlHandlebars {
let mut handlebars = Handlebars::new(); let mut handlebars = Handlebars::new();
// Load theme // Load theme
let theme = theme::Theme::new(&config.src()); let theme = theme::Theme::new(&config.get_src());
// Register template // Register template
debug!("[*]: Register handlebars 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 // Register helpers
debug!("[*]: Register handlebars helpers"); debug!("[*]: Register handlebars helpers");
@ -47,7 +47,7 @@ impl Renderer for HtmlHandlebars {
// Check if dest directory exists // Check if dest directory exists
debug!("[*]: Check if destination 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"))), 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() { if item.path != PathBuf::new() {
let path = config.src().join(&item.path); let path = config.get_src().join(&item.path);
debug!("[*]: Opening file: {:?}", path); debug!("[*]: Opening file: {:?}", path);
let mut f = try!(File::open(&path)); let mut f = try!(File::open(&path));
@ -86,10 +86,10 @@ impl Renderer for HtmlHandlebars {
debug!("[*]: Render template"); debug!("[*]: Render template");
let rendered = try!(handlebars.render("index", &data)); 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 // Write to file
let mut file = try!(utils::create_file(&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.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())); try!(file.write_all(&rendered.into_bytes()));
@ -97,13 +97,13 @@ impl Renderer for HtmlHandlebars {
if index { if index {
debug!("[*]: index.html"); debug!("[*]: index.html");
try!(fs::copy( try!(fs::copy(
config.dest().join(&item.path.with_extension("html")), config.get_dest().join(&item.path.with_extension("html")),
config.dest().join("index.html") config.get_dest().join("index.html")
)); ));
output!( output!(
"[*] Creating index.html from {:?} ✓", "[*] Creating index.html from {:?} ✓",
config.dest().join(&item.path.with_extension("html")) config.get_dest().join(&item.path.with_extension("html"))
); );
index = false; index = false;
} }
@ -114,17 +114,17 @@ impl Renderer for HtmlHandlebars {
debug!("[*] Copy static files"); debug!("[*] Copy static files");
// JavaScript // 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)); try!(js_file.write_all(&theme.js));
// Css // 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)); try!(css_file.write_all(&theme.css));
// syntax highlighting // 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)); 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)); try!(highlight_js.write_all(&theme.highlight_js));
Ok(()) Ok(())

View File

@ -2,14 +2,20 @@ use std::path::Path;
use std::fs::{File, metadata}; use std::fs::{File, metadata};
use std::io::Read; use std::io::Read;
static INDEX: &'static str = include_str!("index.hbs"); pub static INDEX: &'static [u8] = include_bytes!("index.hbs");
static CSS: &'static [u8] = include_bytes!("book.css"); pub static CSS: &'static [u8] = include_bytes!("book.css");
static JS: &'static [u8] = include_bytes!("book.js"); pub static JS: &'static [u8] = include_bytes!("book.js");
static HIGHLIGHT_JS: &'static [u8] = include_bytes!("highlight.js"); pub static HIGHLIGHT_JS: &'static [u8] = include_bytes!("highlight.js");
static HIGHLIGHT_CSS: &'static [u8] = include_bytes!("highlight.css"); 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 struct Theme {
pub index: String, pub index: Vec<u8>,
pub css: Vec<u8>, pub css: Vec<u8>,
pub js: Vec<u8>, pub js: Vec<u8>,
pub highlight_css: Vec<u8>, pub highlight_css: Vec<u8>,
@ -56,8 +62,8 @@ impl Theme {
// index.hbs // index.hbs
match File::open(&src.join("index.hbs")) { match File::open(&src.join("index.hbs")) {
Ok(mut f) => { Ok(mut f) => {
theme.index = String::new(); // Reset the value, because read_to_string appends... theme.index.clear(); // Reset the value, because read_to_string appends...
f.read_to_string(&mut theme.index).unwrap(); f.read_to_end(&mut theme.index).unwrap();
}, },
_ => {}, _ => {},
} }