diff --git a/src/book/bookconfig.rs b/src/book/bookconfig.rs deleted file mode 100644 index ac1f0605..00000000 --- a/src/book/bookconfig.rs +++ /dev/null @@ -1,230 +0,0 @@ -extern crate toml; - -use std::process::exit; -use std::fs::File; -use std::io::Read; -use std::path::{Path, PathBuf}; -use std::collections::BTreeMap; -use std::str::FromStr; -use serde_json; - -#[derive(Debug, Clone)] -pub struct BookConfig { - root: PathBuf, - pub dest: PathBuf, - pub src: PathBuf, - pub theme_path: PathBuf, - - pub title: String, - pub author: String, - pub description: String, - - pub indent_spaces: i32, - multilingual: bool, - pub google_analytics: Option, -} - -impl BookConfig { - pub fn new(root: &Path) -> Self { - BookConfig { - root: root.to_owned(), - dest: root.join("book"), - src: root.join("src"), - theme_path: root.join("theme"), - - title: String::new(), - author: String::new(), - description: String::new(), - - indent_spaces: 4, // indentation used for SUMMARY.md - multilingual: false, - google_analytics: None, - } - } - - pub fn read_config(&mut self, root: &Path) -> &mut Self { - - debug!("[fn]: read_config"); - - 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 f.read_to_string(&mut data).is_err() { - error!("[*]: Failed to read {:?}", &path); - exit(2); - } - data - }; - - // Read book.toml or book.json if exists - - if root.join("book.toml").exists() { - - debug!("[*]: Reading config"); - let data = read_file(root.join("book.toml")); - self.parse_from_toml_string(&data); - - } else if root.join("book.json").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."); - } - - self - } - - pub fn parse_from_toml_string(&mut self, data: &str) -> &mut Self { - let config = match toml::from_str(data) { - Ok(x) => x, - Err(e) => { - error!("[*]: Toml parse errors in book.toml: {:?}", e); - exit(2); - }, - }; - - self.parse_from_btreemap(&config); - - self - } - - /// Parses the string to JSON and converts it - /// to BTreeMap. - pub fn parse_from_json_string(&mut self, data: &str) -> &mut Self { - - let c: serde_json::Value = match serde_json::from_str(data) { - Ok(x) => x, - Err(e) => { - error!("[*]: JSON parse errors in book.json: {:?}", e); - exit(2); - }, - }; - - let config = json_object_to_btreemap(c.as_object().unwrap()); - self.parse_from_btreemap(&config); - - self - } - - pub fn parse_from_btreemap(&mut self, config: &BTreeMap) -> &mut Self { - - // 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); - } - - // Google analytics code - if let Some(id) = config.get("google_analytics_id") { - self.google_analytics = Some(id.to_string().replace("\"", "")); - } - - 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 - } - - pub fn set_dest(&mut self, dest: &Path) -> &mut Self { - self.dest = dest.to_owned(); - self - } - - pub fn get_src(&self) -> &Path { - &self.src - } - - pub fn set_src(&mut self, src: &Path) -> &mut Self { - self.src = src.to_owned(); - self - } - - pub fn get_theme_path(&self) -> &Path { - &self.theme_path - } - - pub fn set_theme_path(&mut self, theme_path: &Path) -> &mut Self { - self.theme_path = theme_path.to_owned(); - self - } -} - -pub fn json_object_to_btreemap(json: &serde_json::Map) -> BTreeMap { - let mut config: BTreeMap = BTreeMap::new(); - - for (key, value) in json.iter() { - config.insert(String::from_str(key).unwrap(), json_value_to_toml_value(value.to_owned())); - } - - config -} - -pub fn json_value_to_toml_value(json: serde_json::Value) -> toml::Value { - match json { - serde_json::Value::Null => toml::Value::String("".to_string()), - serde_json::Value::Bool(x) => toml::Value::Boolean(x), - serde_json::Value::Number(ref x) if x.is_i64() => toml::Value::Integer(x.as_i64().unwrap()), - serde_json::Value::Number(ref x) if x.is_u64() => toml::Value::Integer(x.as_i64().unwrap()), - serde_json::Value::Number(x) => toml::Value::Float(x.as_f64().unwrap()), - serde_json::Value::String(x) => toml::Value::String(x), - serde_json::Value::Array(x) => { - toml::Value::Array(x.iter() - .map(|v| json_value_to_toml_value(v.to_owned())) - .collect()) - }, - serde_json::Value::Object(x) => toml::Value::Table(json_object_to_btreemap(&x)), - } -} diff --git a/src/book/mod.rs b/src/book/mod.rs index c3765ff0..d79e7e14 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -497,6 +497,22 @@ impl MDBook { None } + pub fn has_additional_css(&self) -> bool { + if let Some(htmlconfig) = self.config.get_html_config() { + return htmlconfig.has_additional_css(); + } + + false + } + + pub fn get_additional_css(&self) -> &[PathBuf] { + if let Some(htmlconfig) = self.config.get_html_config() { + return htmlconfig.get_additional_css(); + } + + &[] + } + // Construct book fn parse_summary(&mut self) -> Result<(), Box> { // When append becomes stable, use self.content.append() ... diff --git a/src/config/htmlconfig.rs b/src/config/htmlconfig.rs index 030782d1..4beb14c4 100644 --- a/src/config/htmlconfig.rs +++ b/src/config/htmlconfig.rs @@ -7,6 +7,7 @@ pub struct HtmlConfig { destination: PathBuf, theme: Option, google_analytics: Option, + additional_css: Vec, } impl HtmlConfig { @@ -26,6 +27,7 @@ impl HtmlConfig { destination: root.into().join("book"), theme: None, google_analytics: None, + additional_css: Vec::new(), } } @@ -52,6 +54,16 @@ impl HtmlConfig { self.google_analytics = tomlconfig.google_analytics; } + if let Some(stylepaths) = tomlconfig.additional_css { + for path in stylepaths { + if path.is_relative() { + self.additional_css.push(root.join(path)); + } else { + self.additional_css.push(path); + } + } + } + self } @@ -89,4 +101,17 @@ impl HtmlConfig { pub fn get_google_analytics_id(&self) -> Option { self.google_analytics.clone() } + + pub fn set_google_analytics_id(&mut self, id: Option) -> &mut Self { + self.google_analytics = id; + self + } + + pub fn has_additional_css(&self) -> bool { + !self.additional_css.is_empty() + } + + pub fn get_additional_css(&self) -> &[PathBuf] { + &self.additional_css + } } diff --git a/src/config/tomlconfig.rs b/src/config/tomlconfig.rs index ebb43068..2a08fa84 100644 --- a/src/config/tomlconfig.rs +++ b/src/config/tomlconfig.rs @@ -24,6 +24,7 @@ pub struct TomlHtmlConfig { pub destination: Option, pub theme: Option, pub google_analytics: Option, + pub additional_css: Option>, } /// Returns a TomlConfig from a TOML string diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 29fca838..3da67507 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -187,6 +187,19 @@ impl Renderer for HtmlHandlebars { book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff2", theme::FONT_AWESOME_WOFF2)?; book.write_file("_FontAwesome/fonts/FontAwesome.ttf", theme::FONT_AWESOME_TTF)?; + for style in book.get_additional_css() { + let mut data = Vec::new(); + let mut f = File::open(style)?; + f.read_to_end(&mut data)?; + + let name = match style.strip_prefix(book.get_root()) { + Ok(p) => p.to_str().expect("Could not convert to str"), + Err(_) => style.file_name().expect("File has a file name").to_str().expect("Could not convert to str"), + }; + + book.write_file(name, &data)?; + } + // Copy all remaining files utils::fs::copy_files_except_ext( book.get_source(), @@ -215,6 +228,18 @@ fn make_data(book: &MDBook) -> Result data.insert("google_analytics".to_owned(), json!(ga)); } + // Add check to see if there is an additional style + if book.has_additional_css() { + let mut css = Vec::new(); + for style in book.get_additional_css() { + match style.strip_prefix(book.get_root()) { + Ok(p) => css.push(p.to_str().expect("Could not convert to str")), + Err(_) => css.push(style.file_name().expect("File has a file name").to_str().expect("Could not convert to str")), + } + } + data.insert("additional_css".to_owned(), json!(css)); + } + let mut chapters = vec![]; for item in book.iter() { diff --git a/src/theme/index.hbs b/src/theme/index.hbs index ee66e167..a4705884 100644 --- a/src/theme/index.hbs +++ b/src/theme/index.hbs @@ -21,6 +21,11 @@ + + {{#each additional_css}} + + {{/each}} + diff --git a/tests/tomlconfig.rs b/tests/tomlconfig.rs index 73e472a3..91c40016 100644 --- a/tests/tomlconfig.rs +++ b/tests/tomlconfig.rs @@ -99,4 +99,19 @@ fn from_toml_output_html_google_analytics() { let htmlconfig = config.get_html_config().expect("There should be an HtmlConfig"); assert_eq!(htmlconfig.get_google_analytics_id().expect("the google-analytics key was provided"), String::from("123456")); +} + + +// Tests that the `output.html.additional-css` key is correcly parsed in the TOML config +#[test] +fn from_toml_output_html_additional_stylesheet() { + let toml = r#"[output.html] + additional-css = ["custom.css", "two/custom.css"]"#; + + let parsed = TomlConfig::from_toml(&toml).expect("This should parse"); + let config = BookConfig::from_tomlconfig("root", parsed); + + let htmlconfig = config.get_html_config().expect("There should be an HtmlConfig"); + + assert_eq!(htmlconfig.get_additional_css(), &[PathBuf::from("root/custom.css"), PathBuf::from("root/two/custom.css")]); } \ No newline at end of file