Merge pull request #377 from Michael-F-Bryan/dry-themes
Make themes module more DRY
This commit is contained in:
commit
ef435825b0
|
@ -65,7 +65,9 @@
|
||||||
//! following function [`utils::fs::create_file(path:
|
//! following function [`utils::fs::create_file(path:
|
||||||
//! &Path)`](utils/fs/fn.create_file.html)
|
//! &Path)`](utils/fs/fn.create_file.html)
|
||||||
//!
|
//!
|
||||||
//! This function creates a file and returns it. But before creating the file it checks every directory in the path to see if it exists, and if it does not it will be created.
|
//! This function creates a file and returns it. But before creating the file
|
||||||
|
//! it checks every directory in the path to see if it exists, and if it does
|
||||||
|
//! not it will be created.
|
||||||
//!
|
//!
|
||||||
//! Make sure to take a look at it.
|
//! Make sure to take a look at it.
|
||||||
|
|
||||||
|
@ -84,6 +86,9 @@ extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate tempdir;
|
||||||
|
|
||||||
mod parse;
|
mod parse;
|
||||||
mod preprocess;
|
mod preprocess;
|
||||||
pub mod book;
|
pub mod book;
|
||||||
|
|
202
src/theme/mod.rs
202
src/theme/mod.rs
|
@ -2,6 +2,8 @@ use std::path::Path;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
|
use errors::*;
|
||||||
|
|
||||||
|
|
||||||
pub static INDEX: &'static [u8] = include_bytes!("index.hbs");
|
pub static INDEX: &'static [u8] = include_bytes!("index.hbs");
|
||||||
pub static CSS: &'static [u8] = include_bytes!("book.css");
|
pub static CSS: &'static [u8] = include_bytes!("book.css");
|
||||||
|
@ -11,7 +13,7 @@ pub static HIGHLIGHT_JS: &'static [u8] = include_bytes!("highlight.js");
|
||||||
pub static TOMORROW_NIGHT_CSS: &'static [u8] = include_bytes!("tomorrow-night.css");
|
pub static TOMORROW_NIGHT_CSS: &'static [u8] = include_bytes!("tomorrow-night.css");
|
||||||
pub static HIGHLIGHT_CSS: &'static [u8] = include_bytes!("highlight.css");
|
pub static HIGHLIGHT_CSS: &'static [u8] = include_bytes!("highlight.css");
|
||||||
pub static AYU_HIGHLIGHT_CSS: &'static [u8] = include_bytes!("ayu-highlight.css");
|
pub static AYU_HIGHLIGHT_CSS: &'static [u8] = include_bytes!("ayu-highlight.css");
|
||||||
pub static JQUERY: &'static [u8] = include_bytes!("jquery-2.1.4.min.js");
|
pub static JQUERY: &'static [u8] = include_bytes!("jquery.js");
|
||||||
pub static CLIPBOARD_JS: &'static [u8] = include_bytes!("clipboard.min.js");
|
pub static CLIPBOARD_JS: &'static [u8] = include_bytes!("clipboard.min.js");
|
||||||
pub static STORE_JS: &'static [u8] = include_bytes!("store.js");
|
pub static STORE_JS: &'static [u8] = include_bytes!("store.js");
|
||||||
pub static FONT_AWESOME: &'static [u8] = include_bytes!("_FontAwesome/css/font-awesome.min.css");
|
pub static FONT_AWESOME: &'static [u8] = include_bytes!("_FontAwesome/css/font-awesome.min.css");
|
||||||
|
@ -22,15 +24,14 @@ pub static FONT_AWESOME_WOFF: &'static [u8] = include_bytes!("_FontAwesome/fonts
|
||||||
pub static FONT_AWESOME_WOFF2: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.woff2");
|
pub static FONT_AWESOME_WOFF2: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.woff2");
|
||||||
pub static FONT_AWESOME_OTF: &'static [u8] = include_bytes!("_FontAwesome/fonts/FontAwesome.otf");
|
pub static FONT_AWESOME_OTF: &'static [u8] = include_bytes!("_FontAwesome/fonts/FontAwesome.otf");
|
||||||
|
|
||||||
|
|
||||||
/// The `Theme` struct should be used instead of the static variables because
|
/// The `Theme` struct should be used instead of the static variables because
|
||||||
/// the `new()` method
|
/// the `new()` method will look if the user has a theme directory in his
|
||||||
/// will look if the user has a theme directory in his source folder and use
|
/// source folder and use the users theme instead of the default.
|
||||||
/// the users theme instead
|
|
||||||
/// of the default.
|
|
||||||
///
|
///
|
||||||
/// You should exceptionnaly use the static variables only if you need the
|
/// You should only ever use the static variables directly if you want to
|
||||||
/// default theme even if the
|
/// override the user's theme with the defaults.
|
||||||
/// user has specified another theme.
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
pub index: Vec<u8>,
|
pub index: Vec<u8>,
|
||||||
pub css: Vec<u8>,
|
pub css: Vec<u8>,
|
||||||
|
@ -46,10 +47,49 @@ pub struct Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
pub fn new(src: &Path) -> Self {
|
pub fn new<P: AsRef<Path>>(theme_dir: P) -> Self {
|
||||||
|
let theme_dir = theme_dir.as_ref();
|
||||||
|
let mut theme = Theme::default();
|
||||||
|
|
||||||
// Default theme
|
// If the theme directory doesn't exist there's no point continuing...
|
||||||
let mut theme = Theme {
|
if !theme_dir.exists() || !theme_dir.is_dir() {
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for individual files, if they exist copy them across
|
||||||
|
{
|
||||||
|
let files = vec![
|
||||||
|
(theme_dir.join("index.hbs"), &mut theme.index),
|
||||||
|
(theme_dir.join("book.js"), &mut theme.js),
|
||||||
|
(theme_dir.join("book.css"), &mut theme.css),
|
||||||
|
(theme_dir.join("favicon.png"), &mut theme.favicon),
|
||||||
|
(theme_dir.join("highlight.js"), &mut theme.highlight_js),
|
||||||
|
(theme_dir.join("clipboard.min.js"), &mut theme.clipboard_js),
|
||||||
|
(theme_dir.join("store.js"), &mut theme.store_js),
|
||||||
|
(theme_dir.join("highlight.css"), &mut theme.highlight_css),
|
||||||
|
(theme_dir.join("tomorrow-night.css"), &mut theme.tomorrow_night_css),
|
||||||
|
(theme_dir.join("ayu-highlight.css"), &mut theme.ayu_highlight_css),
|
||||||
|
(theme_dir.join("jquery.js"), &mut theme.jquery),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (filename, dest) in files {
|
||||||
|
if !filename.exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = load_file_contents(&filename, dest) {
|
||||||
|
warn!("Couldn't load custom file, {}: {}", filename.display(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
theme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Theme {
|
||||||
|
fn default() -> Theme {
|
||||||
|
Theme {
|
||||||
index: INDEX.to_owned(),
|
index: INDEX.to_owned(),
|
||||||
css: CSS.to_owned(),
|
css: CSS.to_owned(),
|
||||||
favicon: FAVICON.to_owned(),
|
favicon: FAVICON.to_owned(),
|
||||||
|
@ -61,75 +101,79 @@ impl Theme {
|
||||||
clipboard_js: CLIPBOARD_JS.to_owned(),
|
clipboard_js: CLIPBOARD_JS.to_owned(),
|
||||||
store_js: STORE_JS.to_owned(),
|
store_js: STORE_JS.to_owned(),
|
||||||
jquery: JQUERY.to_owned(),
|
jquery: JQUERY.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a file exists, if so, the destination buffer will be filled with
|
||||||
|
/// its contents.
|
||||||
|
fn load_file_contents<P: AsRef<Path>>(filename: P, dest: &mut Vec<u8>) -> Result<()> {
|
||||||
|
let filename = filename.as_ref();
|
||||||
|
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
File::open(filename)?.read_to_end(&mut buffer)?;
|
||||||
|
|
||||||
|
// We needed the buffer so we'd only overwrite the existing content if we
|
||||||
|
// could successfully load the file into memory.
|
||||||
|
dest.clear();
|
||||||
|
dest.append(&mut buffer);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use tempdir::TempDir;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn theme_uses_defaults_with_nonexistent_src_dir() {
|
||||||
|
let non_existent = PathBuf::from("/non/existent/directory/");
|
||||||
|
assert!(!non_existent.exists());
|
||||||
|
|
||||||
|
let should_be = Theme::default();
|
||||||
|
let got = Theme::new(&non_existent);
|
||||||
|
|
||||||
|
assert_eq!(got, should_be);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn theme_dir_overrides_defaults() {
|
||||||
|
// Get all the non-Rust files in the theme directory
|
||||||
|
let special_files = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||||
|
.join("src/theme")
|
||||||
|
.read_dir()
|
||||||
|
.unwrap()
|
||||||
|
.filter_map(|f| f.ok())
|
||||||
|
.map(|f| f.path())
|
||||||
|
.filter(|p| p.is_file() && !p.ends_with(".rs"));
|
||||||
|
|
||||||
|
let temp = TempDir::new("mdbook").unwrap();
|
||||||
|
|
||||||
|
// "touch" all of the special files so we have empty copies
|
||||||
|
for special_file in special_files {
|
||||||
|
let filename = temp.path().join(special_file.file_name().unwrap());
|
||||||
|
let _ = File::create(&filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
let got = Theme::new(temp.path());
|
||||||
|
|
||||||
|
let empty = Theme {
|
||||||
|
index: Vec::new(),
|
||||||
|
css: Vec::new(),
|
||||||
|
favicon: Vec::new(),
|
||||||
|
js: Vec::new(),
|
||||||
|
highlight_css: Vec::new(),
|
||||||
|
tomorrow_night_css: Vec::new(),
|
||||||
|
ayu_highlight_css: Vec::new(),
|
||||||
|
highlight_js: Vec::new(),
|
||||||
|
clipboard_js: Vec::new(),
|
||||||
|
store_js: Vec::new(),
|
||||||
|
jquery: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if the given path exists
|
assert_eq!(got, empty);
|
||||||
if !src.exists() || !src.is_dir() {
|
|
||||||
return theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for individual files if they exist
|
|
||||||
|
|
||||||
// index.hbs
|
|
||||||
if let Ok(mut f) = File::open(&src.join("index.hbs")) {
|
|
||||||
theme.index.clear(); // Reset the value, because read_to_string appends...
|
|
||||||
let _ = f.read_to_end(&mut theme.index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// book.js
|
|
||||||
if let Ok(mut f) = File::open(&src.join("book.js")) {
|
|
||||||
theme.js.clear();
|
|
||||||
let _ = f.read_to_end(&mut theme.js);
|
|
||||||
}
|
|
||||||
|
|
||||||
// book.css
|
|
||||||
if let Ok(mut f) = File::open(&src.join("book.css")) {
|
|
||||||
theme.css.clear();
|
|
||||||
let _ = f.read_to_end(&mut theme.css);
|
|
||||||
}
|
|
||||||
|
|
||||||
// favicon.png
|
|
||||||
if let Ok(mut f) = File::open(&src.join("favicon.png")) {
|
|
||||||
theme.favicon.clear();
|
|
||||||
let _ = f.read_to_end(&mut theme.favicon);
|
|
||||||
}
|
|
||||||
|
|
||||||
// highlight.js
|
|
||||||
if let Ok(mut f) = File::open(&src.join("highlight.js")) {
|
|
||||||
theme.highlight_js.clear();
|
|
||||||
let _ = f.read_to_end(&mut theme.highlight_js);
|
|
||||||
}
|
|
||||||
|
|
||||||
// clipboard.js
|
|
||||||
if let Ok(mut f) = File::open(&src.join("clipboard.min.js")) {
|
|
||||||
theme.clipboard_js.clear();
|
|
||||||
let _ = f.read_to_end(&mut theme.clipboard_js);
|
|
||||||
}
|
|
||||||
|
|
||||||
// store.js
|
|
||||||
if let Ok(mut f) = File::open(&src.join("store.js")) {
|
|
||||||
theme.store_js.clear();
|
|
||||||
let _ = f.read_to_end(&mut theme.store_js);
|
|
||||||
}
|
|
||||||
|
|
||||||
// highlight.css
|
|
||||||
if let Ok(mut f) = File::open(&src.join("highlight.css")) {
|
|
||||||
theme.highlight_css.clear();
|
|
||||||
let _ = f.read_to_end(&mut theme.highlight_css);
|
|
||||||
}
|
|
||||||
|
|
||||||
// tomorrow-night.css
|
|
||||||
if let Ok(mut f) = File::open(&src.join("tomorrow-night.css")) {
|
|
||||||
theme.tomorrow_night_css.clear();
|
|
||||||
let _ = f.read_to_end(&mut theme.tomorrow_night_css);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ayu-highlight.css
|
|
||||||
if let Ok(mut f) = File::open(&src.join("ayu-highlight.css")) {
|
|
||||||
theme.ayu_highlight_css.clear();
|
|
||||||
let _ = f.read_to_end(&mut theme.ayu_highlight_css);
|
|
||||||
}
|
|
||||||
|
|
||||||
theme
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue