diff --git a/book-example/src/cli/init.md b/book-example/src/cli/init.md index 6efa77d7..2656e338 100644 --- a/book-example/src/cli/init.md +++ b/book-example/src/cli/init.md @@ -36,7 +36,7 @@ by appending a path to the command: mdbook init path/to/book ``` -#### --theme +## --theme When you use the `--theme` argument, the default theme will be copied into a directory called `theme` in your source directory so that you can modify it. diff --git a/src/book/bookconfig.rs b/src/book/bookconfig.rs index 73fa35b7..06066f59 100644 --- a/src/book/bookconfig.rs +++ b/src/book/bookconfig.rs @@ -8,6 +8,7 @@ use std::path::{Path, PathBuf}; pub struct BookConfig { pub title: String, pub author: String, + root: PathBuf, dest: PathBuf, src: PathBuf, pub indent_spaces: i32, @@ -16,10 +17,11 @@ pub struct BookConfig { impl BookConfig { - pub fn new() -> Self { + pub fn new(root: &Path) -> Self { BookConfig { title: String::new(), author: String::new(), + root: root.to_owned(), dest: PathBuf::from("book"), src: PathBuf::from("src"), indent_spaces: 4, // indentation used for SUMMARY.md @@ -42,32 +44,50 @@ impl BookConfig { debug!("[*]: Reading config"); let mut data = String::new(); - config_file.read_to_string(&mut data).unwrap(); + + // Just return if an error occured. + // I would like to propagate the error, but I have to return `&self` + match config_file.read_to_string(&mut data) { + Err(_) => return self, + _ => {}, + } // Convert to JSON - let config = Json::from_str(&data).unwrap(); + if let Ok(config) = Json::from_str(&data) { + // Extract data - // Extract data + debug!("[*]: Extracting data from config"); + // Title & author + if let Some(a) = config.find_path(&["title"]) { self.title = a.to_string().replace("\"", "") } + if let Some(a) = config.find_path(&["author"]) { self.author = a.to_string().replace("\"", "") } - debug!("[*]: Extracting data from config"); - // Title & author - if let Some(a) = config.find_path(&["title"]) { self.title = a.to_string().replace("\"", "") } - if let Some(a) = config.find_path(&["author"]) { self.author = a.to_string().replace("\"", "") } + // Destination + if let Some(a) = config.find_path(&["dest"]) { + let dest = PathBuf::from(&a.to_string().replace("\"", "")); - // Destination - if let Some(a) = config.find_path(&["dest"]) { - let dest = PathBuf::from(&a.to_string().replace("\"", "")); - - // If path is relative make it absolute from the parent directory of src - if dest.is_relative() { - let dest = &self.get_src().parent().unwrap().join(&dest); - self.set_dest(dest); + // If path is relative make it absolute from the parent directory of src + match dest.is_relative() { + true => { + let dest = self.get_root().join(&dest).to_owned(); + self.set_dest(&dest); + }, + false => { self.set_dest(&dest); }, + } } } self } + pub fn get_root(&self) -> &Path { + &self.root + } + + pub fn set_root(&mut self, root: &Path) -> &mut Self { + self.root = root.to_owned(); + self + } + pub fn get_dest(&self) -> &Path { &self.dest } diff --git a/src/book/mdbook.rs b/src/book/mdbook.rs index e32d8481..06d273f3 100644 --- a/src/book/mdbook.rs +++ b/src/book/mdbook.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use std::path::Path; use std::fs::{self, File, metadata}; use std::io::Write; use std::error::Error; @@ -12,7 +12,6 @@ use renderer::HtmlHandlebars; pub struct MDBook { config: BookConfig, - pub root: PathBuf, pub content: Vec, renderer: Box, } @@ -39,9 +38,8 @@ impl MDBook { } MDBook { - root: root.to_path_buf(), content: vec![], - config: BookConfig::new() + config: BookConfig::new(root) .set_src(&root.join("src")) .set_dest(&root.join("book")) .to_owned(), @@ -106,7 +104,7 @@ impl MDBook { // 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", dest); - fs::create_dir(&dest).unwrap(); + try!(fs::create_dir(&dest)); }, Ok(_) => { /* If there is no error, the directory / file does exist */ } } @@ -117,7 +115,7 @@ impl MDBook { // 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", src); - fs::create_dir(&src).unwrap(); + try!(fs::create_dir(&src)); }, Ok(_) => { /* If there is no error, the directory / file does exist */ } } @@ -128,7 +126,7 @@ impl MDBook { // 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 SUMMARY.md", src.join("SUMMARY.md")); - Ok(File::create(&src.join("SUMMARY.md")).unwrap()) + Ok(try!(File::create(&src.join("SUMMARY.md")))) }, Ok(_) => { /* If there is no error, the directory / file does exist */ @@ -143,7 +141,7 @@ impl MDBook { try!(writeln!(f, "")); try!(writeln!(f, "- [Chapter 1](./chapter_1.md)")); - let mut chapter_1 = File::create(&src.join("chapter_1.md")).unwrap(); + let mut chapter_1 = try!(File::create(&src.join("chapter_1.md"))); try!(writeln!(chapter_1, "# Chapter 1")); } @@ -181,7 +179,7 @@ impl MDBook { // 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(); + try!(fs::create_dir(&theme_dir)); }, Ok(_) => { /* If there is no error, the directory / file does exist */ } } @@ -226,7 +224,8 @@ impl MDBook { /// of the current working directory by using a relative path instead of an absolute path. pub fn read_config(mut self) -> Self { - self.config.read_config(&self.root); + let root = self.config.get_root().to_owned(); + self.config.read_config(&root); self } @@ -256,7 +255,16 @@ impl MDBook { } pub fn set_dest(mut self, dest: &Path) -> Self { - self.config.set_dest(&self.root.join(dest)); + + // Handle absolute and relative paths + match dest.is_absolute() { + true => { self.config.set_dest(dest); }, + false => { + let dest = self.config.get_root().join(dest).to_owned(); + self.config.set_dest(&dest); + } + } + self } @@ -265,7 +273,16 @@ impl MDBook { } pub fn set_src(mut self, src: &Path) -> Self { - self.config.set_src(&self.root.join(src)); + + // Handle absolute and relative paths + match src.is_absolute() { + true => { self.config.set_src(src); }, + false => { + let src = self.config.get_root().join(src).to_owned(); + self.config.set_src(&src); + } + } + self } diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index ef9e01f5..af9011ff 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -72,7 +72,11 @@ impl Renderer for HtmlHandlebars { // Remove content from previous file and render content for this one data.remove("path"); - data.insert("path".to_string(), item.path.to_str().unwrap().to_json()); + match item.path.to_str() { + Some(p) => { data.insert("path".to_string(), p.to_json()); }, + None => return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))), + } + // Remove content from previous file and render content for this one data.remove("content"); @@ -144,7 +148,10 @@ fn make_data(book: BookItems, config: &BookConfig) -> Result { chapter.insert("path".to_string(), p.to_json()); }, + None => return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))), + } chapters.push(chapter); } diff --git a/src/renderer/html_handlebars/helpers/navigation.rs b/src/renderer/html_handlebars/helpers/navigation.rs index cb8209e4..02e23e4f 100644 --- a/src/renderer/html_handlebars/helpers/navigation.rs +++ b/src/renderer/html_handlebars/helpers/navigation.rs @@ -30,9 +30,13 @@ pub fn previous(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext debug!("[*]: Decode chapters from JSON"); // Decode json format - let decoded: Vec> = json::decode(&chapters.to_string()).unwrap(); + let decoded: Vec> = match json::decode(&chapters.to_string()) { + Ok(data) => data, + Err(_) => return Err(RenderError{ desc: "Could not decode the JSON data"}), + }; let mut previous: Option> = None; + debug!("[*]: Search for current Chapter"); // Search for current chapter and return previous entry for item in decoded { @@ -48,22 +52,32 @@ pub fn previous(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext // Create new BTreeMap to extend the context: 'title' and 'link' let mut previous_chapter = BTreeMap::new(); - debug!("[*]: Inserting title: {}", previous.get("name").unwrap()); - previous_chapter.insert("title".to_string(), previous.get("name").unwrap().to_json()); + // Chapter title + match previous.get("name") { + Some(n) => { + debug!("[*]: Inserting title: {}", n); + previous_chapter.insert("title".to_string(), n.to_json()) + }, + None => { + debug!("[*]: No title found for chapter"); + return Err(RenderError{ desc: "No title found for chapter in JSON data" }) + } + }; - debug!("[*]: Inserting link: {}", - path_to_root.join( - Path::new(previous.get("path").unwrap()) - .with_extension("html") - ).to_str().unwrap()); + // Chapter link - previous_chapter.insert( - "link".to_string(), - path_to_root.join( - Path::new(previous.get("path").unwrap()) - .with_extension("html") - ).to_str().unwrap().to_json() - ); + match previous.get("path") { + Some(p) => { + let path = path_to_root.join(Path::new(p).with_extension("html")); + debug!("[*]: Inserting link: {:?}", path); + + match path.to_str() { + Some(p) => { previous_chapter.insert("link".to_string(), p.to_json()); }, + None => return Err(RenderError{ desc: "Link could not be converted to str" }) + } + }, + None => return Err(RenderError{ desc: "No path found for chapter in JSON data" }) + } debug!("[*]: Inject in context"); // Inject in current context @@ -71,7 +85,13 @@ pub fn previous(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext debug!("[*]: Render template"); // Render template - _h.template().unwrap().render(&updated_context, r, rc).unwrap(); + match _h.template() { + Some(t) => { + try!(t.render(&updated_context, r, rc)); + }, + None => return Err(RenderError{ desc: "Error with the handlebars template" }) + } + } break; @@ -81,8 +101,11 @@ pub fn previous(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext } }, _ => continue, + } + } + Ok(()) } @@ -110,7 +133,10 @@ pub fn next(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> debug!("[*]: Decode chapters from JSON"); // Decode json format - let decoded: Vec> = json::decode(&chapters.to_string()).unwrap(); + let decoded: Vec> = match json::decode(&chapters.to_string()) { + Ok(data) => data, + Err(_) => return Err(RenderError{ desc: "Could not decode the JSON data"}), + }; let mut previous: Option> = None; debug!("[*]: Search for current Chapter"); @@ -122,37 +148,48 @@ pub fn next(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Some(path) if path.len() > 0 => { if let Some(previous) = previous { - if previous.get("path").unwrap() == ¤t { + + let path = match previous.get("path") { + Some(p) => p, + None => return Err(RenderError{ desc: "No path found for chapter in JSON data"}) + }; + + if path == ¤t { debug!("[*]: Found current chapter"); debug!("[*]: Creating BTreeMap to inject in context"); // Create new BTreeMap to extend the context: 'title' and 'link' let mut next_chapter = BTreeMap::new(); - debug!("[*]: Inserting title: {}", item.get("name").unwrap()); - next_chapter.insert("title".to_string(), item.get("name").unwrap().to_json()); + match item.get("name") { + Some(n) => { + debug!("[*]: Inserting title: {}", n); + next_chapter.insert("title".to_string(), n.to_json()); + } + None => return Err(RenderError{ desc: "No title found for chapter in JSON data"}) + } - debug!("[*]: Inserting link: {}", - path_to_root.join( - Path::new(item.get("path").unwrap()) - .with_extension("html") - ).to_str().unwrap()); + let link = path_to_root.join(Path::new(path).with_extension("html")); + debug!("[*]: Inserting link: {:?}", link); - next_chapter.insert( - "link".to_string(), - path_to_root.join( - Path::new(item.get("path").unwrap()) - .with_extension("html") - ).to_str().unwrap().to_json() - ); + match link.to_str() { + Some(l) => { next_chapter.insert("link".to_string(), l.to_json()); }, + None => return Err(RenderError{ desc: "Link could not converted to str"}) + } debug!("[*]: Inject in context"); // Inject in current context let updated_context = c.extend(&next_chapter); debug!("[*]: Render template"); + // Render template - _h.template().unwrap().render(&updated_context, r, rc).unwrap(); + match _h.template() { + Some(t) => { + try!(t.render(&updated_context, r, rc)); + }, + None => return Err(RenderError{ desc: "Error with the handlebars template" }) + } break } @@ -160,7 +197,7 @@ pub fn next(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> previous = Some(item.clone()); }, - + _ => continue, } } diff --git a/src/theme/mod.rs b/src/theme/mod.rs index 34041510..bd6f1795 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -63,7 +63,9 @@ impl Theme { match File::open(&src.join("index.hbs")) { Ok(mut f) => { theme.index.clear(); // Reset the value, because read_to_string appends... - f.read_to_end(&mut theme.index).unwrap(); + match f.read_to_end(&mut theme.index) { + _ => {} + }; }, _ => {}, } @@ -72,7 +74,9 @@ impl Theme { match File::open(&src.join("book.js")) { Ok(mut f) => { theme.js.clear(); - f.read_to_end(&mut theme.js).unwrap(); + match f.read_to_end(&mut theme.js){ + _ => {} + } }, _ => {}, } @@ -81,7 +85,9 @@ impl Theme { match File::open(&src.join("book.css")) { Ok(mut f) => { theme.css.clear(); - f.read_to_end(&mut theme.css).unwrap(); + match f.read_to_end(&mut theme.css) { + _ => {} + } }, _ => {}, } @@ -90,7 +96,9 @@ impl Theme { match File::open(&src.join("highlight.js")) { Ok(mut f) => { theme.highlight_js.clear(); - f.read_to_end(&mut theme.highlight_js).unwrap(); + match f.read_to_end(&mut theme.highlight_js) { + _ => {} + } }, _ => {}, } @@ -99,7 +107,9 @@ impl Theme { match File::open(&src.join("highlight.css")) { Ok(mut f) => { theme.highlight_css.clear(); - f.read_to_end(&mut theme.highlight_css).unwrap(); + match f.read_to_end(&mut theme.highlight_css) { + _ => {} + } }, _ => {}, }