From 791487bc84aaf77836fc1534b9f6e1e467b93e1a Mon Sep 17 00:00:00 2001 From: Gambhiro Date: Fri, 23 Dec 2016 08:15:32 +0000 Subject: [PATCH] parse either book.toml or book.json --- src/book/bookconfig.rs | 189 ++++++++++++++++++++++++++---------- src/book/bookconfig_test.rs | 63 ++++++++++++ src/book/mod.rs | 2 + 3 files changed, 203 insertions(+), 51 deletions(-) create mode 100644 src/book/bookconfig_test.rs diff --git a/src/book/bookconfig.rs b/src/book/bookconfig.rs index 69f3340f..d0ba686e 100644 --- a/src/book/bookconfig.rs +++ b/src/book/bookconfig.rs @@ -1,8 +1,13 @@ -use serde_json; +extern crate toml; + +use std::process::exit; use std::fs::File; use std::io::Read; use std::path::{Path, PathBuf}; +//use serde::{Serialize, Deserialize}; +use serde_json; + #[derive(Debug, Clone)] pub struct BookConfig { root: PathBuf, @@ -18,7 +23,6 @@ pub struct BookConfig { multilingual: bool, } - impl BookConfig { pub fn new(root: &Path) -> Self { BookConfig { @@ -40,71 +44,154 @@ impl BookConfig { debug!("[fn]: read_config"); - // If the file does not exist, return early - let mut config_file = match File::open(root.join("book.json")) { - Ok(f) => f, - Err(_) => { - debug!("[*]: Failed to open {:?}", root.join("book.json")); - return self; - }, + let read_file = |path: PathBuf| -> String { + let mut data = String::new(); + let mut f: File = match File::open(&path) { + Ok(x) => x, + Err(_) => { + error!("[*]: Failed to open {:?}", &path); + exit(2); + } + }; + if let Err(_) = f.read_to_string(&mut data) { + error!("[*]: Failed to read {:?}", &path); + exit(2); + } + data }; - debug!("[*]: Reading config"); - let mut data = String::new(); + // Read book.toml or book.json if exists - // Just return if an error occured. - // I would like to propagate the error, but I have to return `&self` - if let Err(_) = config_file.read_to_string(&mut data) { - return self; + if Path::new(root.join("book.toml").as_os_str()).exists() { + + debug!("[*]: Reading config"); + let data = read_file(root.join("book.toml")); + self.parse_from_toml_string(&data); + + } else if Path::new(root.join("book.json").as_os_str()).exists() { + + debug!("[*]: Reading config"); + let data = read_file(root.join("book.json")); + self.parse_from_json_string(&data); + + } else { + debug!("[*]: No book.toml or book.json was found, using defaults."); } - // Convert to JSON - if let Ok(config) = serde_json::from_str::(&data) { - // Extract data + self + } - let config = config.as_object().unwrap(); + pub fn parse_from_toml_string(&mut self, data: &String) -> &mut Self { - debug!("[*]: Extracting data from config"); - // Title, author, description - if let Some(a) = config.get("title") { - self.title = a.to_string().replace("\"", "") - } - if let Some(a) = config.get("author") { - self.author = a.to_string().replace("\"", "") - } - if let Some(a) = config.get("description") { - self.description = a.to_string().replace("\"", "") + let mut parser = toml::Parser::new(&data); + + let config = match parser.parse() { + Some(x) => {x}, + None => { + error!("[*]: Toml parse errors in book.toml: {:?}", parser.errors); + exit(2); } + }; - // Destination folder - if let Some(a) = config.get("dest") { - let mut dest = PathBuf::from(&a.to_string().replace("\"", "")); + // TODO this is very similar to how the JSON is parsed. Combine somehow? - // If path is relative make it absolute from the parent directory of src - if dest.is_relative() { - dest = self.get_root().join(&dest); - } - self.set_dest(&dest); + // Title, author, description + if let Some(a) = config.get("title") { + self.title = a.to_string().replace("\"", ""); + } + if let Some(a) = config.get("author") { + self.author = a.to_string().replace("\"", ""); + } + if let Some(a) = config.get("description") { + self.description = a.to_string().replace("\"", ""); + } + + // Destination folder + if let Some(a) = config.get("dest") { + let mut dest = PathBuf::from(&a.to_string().replace("\"", "")); + + // If path is relative make it absolute from the parent directory of src + if dest.is_relative() { + dest = self.get_root().join(&dest); } + self.set_dest(&dest); + } - // Source folder - if let Some(a) = config.get("src") { - let mut src = PathBuf::from(&a.to_string().replace("\"", "")); - if src.is_relative() { - src = self.get_root().join(&src); - } - self.set_src(&src); + // Source folder + if let Some(a) = config.get("src") { + let mut src = PathBuf::from(&a.to_string().replace("\"", "")); + if src.is_relative() { + src = self.get_root().join(&src); } + self.set_src(&src); + } - // Theme path folder - if let Some(a) = config.get("theme_path") { - let mut theme_path = PathBuf::from(&a.to_string().replace("\"", "")); - if theme_path.is_relative() { - theme_path = self.get_root().join(&theme_path); - } - self.set_theme_path(&theme_path); + // Theme path folder + if let Some(a) = config.get("theme_path") { + let mut theme_path = PathBuf::from(&a.to_string().replace("\"", "")); + if theme_path.is_relative() { + theme_path = self.get_root().join(&theme_path); } + self.set_theme_path(&theme_path); + } + self + } + + pub fn parse_from_json_string(&mut self, data: &String) -> &mut Self { + + let config: serde_json::Value = match serde_json::from_str(&data) { + Ok(x) => {x}, + Err(e) => { + error!("[*]: JSON parse errors in book.json: {:?}", e); + exit(2); + } + }; + + // Extract data + + let config = config.as_object().unwrap(); + + debug!("[*]: Extracting data from config"); + + // Title, author, description + if let Some(a) = config.get("title") { + self.title = a.to_string().replace("\"", "") + } + if let Some(a) = config.get("author") { + self.author = a.to_string().replace("\"", "") + } + if let Some(a) = config.get("description") { + self.description = a.to_string().replace("\"", "") + } + + // Destination folder + if let Some(a) = config.get("dest") { + let mut dest = PathBuf::from(&a.to_string().replace("\"", "")); + + // If path is relative make it absolute from the parent directory of src + if dest.is_relative() { + dest = self.get_root().join(&dest); + } + self.set_dest(&dest); + } + + // Source folder + if let Some(a) = config.get("src") { + let mut src = PathBuf::from(&a.to_string().replace("\"", "")); + if src.is_relative() { + src = self.get_root().join(&src); + } + self.set_src(&src); + } + + // Theme path folder + if let Some(a) = config.get("theme_path") { + let mut theme_path = PathBuf::from(&a.to_string().replace("\"", "")); + if theme_path.is_relative() { + theme_path = self.get_root().join(&theme_path); + } + self.set_theme_path(&theme_path); } self diff --git a/src/book/bookconfig_test.rs b/src/book/bookconfig_test.rs new file mode 100644 index 00000000..fc18e9e7 --- /dev/null +++ b/src/book/bookconfig_test.rs @@ -0,0 +1,63 @@ +#[cfg(test)] + +use std::path::Path; +use book::bookconfig::*; + +#[test] +fn it_parses_json_config() { + let text = r#" +{ + "title": "mdBook Documentation", + "description": "Create book from markdown files. Like Gitbook but implemented in Rust", + "author": "Mathieu David" +}"#; + + // TODO don't require path argument, take pwd + let mut config = BookConfig::new(Path::new(".")); + + config.parse_from_json_string(&text.to_string()); + + let expected = r#"BookConfig { + root: ".", + dest: "./book", + src: "./src", + theme_path: "./theme", + title: "mdBook Documentation", + author: "Mathieu David", + description: "Create book from markdown files. Like Gitbook but implemented in Rust", + indent_spaces: 4, + multilingual: false +}"#; + + assert_eq!(format!("{:#?}", config), expected); +} + +#[test] +fn it_parses_toml_config() { + let text = r#" +title = "mdBook Documentation" +description = "Create book from markdown files. Like Gitbook but implemented in Rust" +author = "Mathieu David" +"#; + + // TODO don't require path argument, take pwd + let mut config = BookConfig::new(Path::new(".")); + + config.parse_from_toml_string(&text.to_string()); + + println!("{:#?}", config); + + let expected = r#"BookConfig { + root: ".", + dest: "./book", + src: "./src", + theme_path: "./theme", + title: "mdBook Documentation", + author: "Mathieu David", + description: "Create book from markdown files. Like Gitbook but implemented in Rust", + indent_spaces: 4, + multilingual: false +}"#; + + assert_eq!(format!("{:#?}", config), expected); +} diff --git a/src/book/mod.rs b/src/book/mod.rs index c3594300..25d59ca1 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -1,6 +1,8 @@ pub mod bookitem; pub mod bookconfig; +pub mod bookconfig_test; + pub use self::bookitem::{BookItem, BookItems}; pub use self::bookconfig::BookConfig;