From 74fff81e4b2c48577d50aa861d5629134be56228 Mon Sep 17 00:00:00 2001 From: Mathieu David Date: Thu, 17 Mar 2016 22:41:00 +0100 Subject: [PATCH] Refactor: Move fs related functions from utils into their own submodule --- src/book/mdbook.rs | 2 +- src/lib.rs | 2 +- src/renderer/html_handlebars/hbs_renderer.rs | 24 +- src/utils/fs.rs | 218 +++++++++++++++++++ src/utils/mod.rs | 218 +------------------ 5 files changed, 233 insertions(+), 231 deletions(-) create mode 100644 src/utils/fs.rs diff --git a/src/book/mdbook.rs b/src/book/mdbook.rs index 53d12d16..6d7945eb 100644 --- a/src/book/mdbook.rs +++ b/src/book/mdbook.rs @@ -198,7 +198,7 @@ impl MDBook { try!(self.init()); // Clean output directory - try!(utils::remove_dir_content(&self.config.get_dest())); + try!(utils::fs::remove_dir_content(&self.config.get_dest())); try!(self.renderer.render(&self)); diff --git a/src/lib.rs b/src/lib.rs index edc2fe9e..33010801 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,7 @@ //! I have regrouped some useful functions in the [utils](utils/index.html) module, like the following function //! //! ```ignore -//! utils::create_path(path: &Path) +//! utils::fs::create_path(path: &Path) //! ``` //! This function creates all the directories in a given path if they do not exist //! diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 798defa0..ddcfcd25 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -100,7 +100,7 @@ impl Renderer for HtmlHandlebars { // Remove path to root from previous file and render content for this one data.remove("path_to_root"); - data.insert("path_to_root".to_owned(), utils::path_to_root(&ch.path).to_json()); + data.insert("path_to_root".to_owned(), utils::fs::path_to_root(&ch.path).to_json()); // Rendere the handlebars template with the data debug!("[*]: Render template"); @@ -108,7 +108,7 @@ impl Renderer for HtmlHandlebars { debug!("[*]: Create file {:?}", &book.get_dest().join(&ch.path).with_extension("html")); // Write to file - let mut file = try!(utils::create_file(&book.get_dest().join(&ch.path).with_extension("html"))); + let mut file = try!(utils::fs::create_file(&book.get_dest().join(&ch.path).with_extension("html"))); output!("[*] Creating {:?} ✓", &book.get_dest().join(&ch.path).with_extension("html")); try!(file.write_all(&rendered.into_bytes())); @@ -153,12 +153,12 @@ impl Renderer for HtmlHandlebars { // Remove path to root from previous file and render content for this one data.remove("path_to_root"); - data.insert("path_to_root".to_owned(), utils::path_to_root(Path::new("print.md")).to_json()); + data.insert("path_to_root".to_owned(), utils::fs::path_to_root(Path::new("print.md")).to_json()); // Rendere the handlebars template with the data debug!("[*]: Render template"); let rendered = try!(handlebars.render("index", &data)); - let mut file = try!(utils::create_file(&book.get_dest().join("print").with_extension("html"))); + let mut file = try!(utils::fs::create_file(&book.get_dest().join("print").with_extension("html"))); try!(file.write_all(&rendered.into_bytes())); output!("[*] Creating print.html ✓"); @@ -220,14 +220,14 @@ impl Renderer for HtmlHandlebars { try!(highlight_js.write_all(&theme.highlight_js)); // Font Awesome local fallback - let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest() + let mut font_awesome = if let Ok(f) = utils::fs::create_file(&book.get_dest() .join("_FontAwesome/css/font-awesome.css")) { f } else { return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create font-awesome.css"))); }; try!(font_awesome.write_all(theme::FONT_AWESOME)); - let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest() + let mut font_awesome = if let Ok(f) = utils::fs::create_file(&book.get_dest() .join("_FontAwesome/fonts/fontawesome-webfon\ t.eot")) { f @@ -235,7 +235,7 @@ impl Renderer for HtmlHandlebars { return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.eot"))); }; try!(font_awesome.write_all(theme::FONT_AWESOME_EOT)); - let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest() + let mut font_awesome = if let Ok(f) = utils::fs::create_file(&book.get_dest() .join("_FontAwesome/fonts/fontawesome-webfon\ t.svg")) { f @@ -243,7 +243,7 @@ impl Renderer for HtmlHandlebars { return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.svg"))); }; try!(font_awesome.write_all(theme::FONT_AWESOME_SVG)); - let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest() + let mut font_awesome = if let Ok(f) = utils::fs::create_file(&book.get_dest() .join("_FontAwesome/fonts/fontawesome-webfon\ t.ttf")) { f @@ -251,7 +251,7 @@ impl Renderer for HtmlHandlebars { return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.ttf"))); }; try!(font_awesome.write_all(theme::FONT_AWESOME_TTF)); - let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest() + let mut font_awesome = if let Ok(f) = utils::fs::create_file(&book.get_dest() .join("_FontAwesome/fonts/fontawesome-webfon\ t.woff")) { f @@ -259,7 +259,7 @@ impl Renderer for HtmlHandlebars { return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.woff"))); }; try!(font_awesome.write_all(theme::FONT_AWESOME_WOFF)); - let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest() + let mut font_awesome = if let Ok(f) = utils::fs::create_file(&book.get_dest() .join("_FontAwesome/fonts/fontawesome-webfon\ t.woff2")) { f @@ -267,7 +267,7 @@ impl Renderer for HtmlHandlebars { return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.woff2"))); }; try!(font_awesome.write_all(theme::FONT_AWESOME_WOFF2)); - let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest() + let mut font_awesome = if let Ok(f) = utils::fs::create_file(&book.get_dest() .join("_FontAwesome/fonts/FontAwesome.ttf")) { f } else { @@ -276,7 +276,7 @@ impl Renderer for HtmlHandlebars { try!(font_awesome.write_all(theme::FONT_AWESOME_TTF)); // Copy all remaining files - try!(utils::copy_files_except_ext(book.get_src(), book.get_dest(), true, &["md"])); + try!(utils::fs::copy_files_except_ext(book.get_src(), book.get_dest(), true, &["md"])); Ok(()) } diff --git a/src/utils/fs.rs b/src/utils/fs.rs new file mode 100644 index 00000000..fd227ded --- /dev/null +++ b/src/utils/fs.rs @@ -0,0 +1,218 @@ +use std::path::{Path, Component}; +use std::error::Error; +use std::io; +use std::fs::{self, metadata, File}; + + +/// Takes a path and returns a path containing just enough `../` to point to the root of the given path. +/// +/// This is mostly interesting for a relative path to point back to the directory from where the +/// path starts. +/// +/// ```ignore +/// let mut path = Path::new("some/relative/path"); +/// +/// println!("{}", path_to_root(&path)); +/// ``` +/// +/// **Outputs** +/// +/// ```text +/// "../../" +/// ``` +/// +/// **note:** it's not very fool-proof, if you find a situation where it doesn't return the correct +/// path. Consider [submitting a new issue](https://github.com/azerupi/mdBook/issues) or a +/// [pull-request](https://github.com/azerupi/mdBook/pulls) to improve it. + +pub fn path_to_root(path: &Path) -> String { + debug!("[fn]: path_to_root"); + // Remove filename and add "../" for every directory + + path.to_path_buf() + .parent() + .expect("") + .components() + .fold(String::new(), |mut s, c| { + match c { + Component::Normal(_) => s.push_str("../"), + _ => { + debug!("[*]: Other path component... {:?}", c); + }, + } + s + }) +} + + + +/// 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. + +pub fn create_file(path: &Path) -> Result> { + debug!("[fn]: create_file"); + + // Construct path + if let Some(p) = path.parent() { + debug!("Parent directory is: {:?}", p); + + try!(fs::create_dir_all(p)); + } + + debug!("[*]: Create file: {:?}", path); + let f = match File::create(path) { + Ok(f) => f, + Err(e) => { + debug!("File::create: {}", e); + return Err(Box::new(io::Error::new(io::ErrorKind::Other, format!("{}", e)))); + }, + }; + + Ok(f) +} + +/// Removes all the content of a directory but not the directory itself + +pub fn remove_dir_content(dir: &Path) -> Result<(), Box> { + for item in try!(fs::read_dir(dir)) { + if let Ok(item) = item { + let item = item.path(); + if item.is_dir() { + try!(fs::remove_dir_all(item)); + } else { + try!(fs::remove_file(item)); + } + } + } + Ok(()) +} + +/// +/// +/// Copies all files of a directory to another one except the files with the extensions given in the +/// `ext_blacklist` array + +pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blacklist: &[&str]) -> Result<(), Box> { + debug!("[fn] copy_files_except_ext"); + // Check that from and to are different + if from == to { + return Ok(()); + } + debug!("[*] Loop"); + for entry in try!(fs::read_dir(from)) { + let entry = try!(entry); + debug!("[*] {:?}", entry.path()); + let metadata = try!(entry.metadata()); + + // If the entry is a dir and the recursive option is enabled, call itself + if metadata.is_dir() && recursive { + if entry.path() == to.to_path_buf() { + continue; + } + debug!("[*] is dir"); + + // check if output dir already exists + if !to.join(entry.file_name()).exists() { + try!(fs::create_dir(&to.join(entry.file_name()))); + } + + try!(copy_files_except_ext(&from.join(entry.file_name()), + &to.join(entry.file_name()), + true, + ext_blacklist)); + } else if metadata.is_file() { + + // Check if it is in the blacklist + if let Some(ext) = entry.path().extension() { + if ext_blacklist.contains(&ext.to_str().unwrap()) { + continue; + } + debug!("[*] creating path for file: {:?}", + &to.join(entry.path().file_name().expect("a file should have a file name..."))); + + output!("[*] copying file: {:?}\n to {:?}", + entry.path(), + &to.join(entry.path().file_name().expect("a file should have a file name..."))); + try!(fs::copy(entry.path(), + &to.join(entry.path().file_name().expect("a file should have a file name...")))); + } + } + } + Ok(()) +} + + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ + +// tests + +#[cfg(test)] +mod tests { + extern crate tempdir; + + use super::copy_files_except_ext; + use std::fs; + + #[test] + fn copy_files_except_ext_test() { + let tmp = match tempdir::TempDir::new("") { + Ok(t) => t, + Err(_) => panic!("Could not create a temp dir"), + }; + + // Create a couple of files + if let Err(_) = fs::File::create(&tmp.path().join("file.txt")) { + panic!("Could not create file.txt") + } + if let Err(_) = fs::File::create(&tmp.path().join("file.md")) { + panic!("Could not create file.md") + } + if let Err(_) = fs::File::create(&tmp.path().join("file.png")) { + panic!("Could not create file.png") + } + if let Err(_) = fs::create_dir(&tmp.path().join("sub_dir")) { + panic!("Could not create sub_dir") + } + if let Err(_) = fs::File::create(&tmp.path().join("sub_dir/file.png")) { + panic!("Could not create sub_dir/file.png") + } + if let Err(_) = fs::create_dir(&tmp.path().join("sub_dir_exists")) { + panic!("Could not create sub_dir_exists") + } + if let Err(_) = fs::File::create(&tmp.path().join("sub_dir_exists/file.txt")) { + panic!("Could not create sub_dir_exists/file.txt") + } + + // Create output dir + if let Err(_) = fs::create_dir(&tmp.path().join("output")) { + panic!("Could not create output") + } + if let Err(_) = fs::create_dir(&tmp.path().join("output/sub_dir_exists")) { + panic!("Could not create output/sub_dir_exists") + } + + match copy_files_except_ext(&tmp.path(), &tmp.path().join("output"), true, &["md"]) { + Err(e) => panic!("Error while executing the function:\n{:?}", e), + Ok(_) => {}, + } + + // Check if the correct files where created + if !(&tmp.path().join("output/file.txt")).exists() { + panic!("output/file.txt should exist") + } + if (&tmp.path().join("output/file.md")).exists() { + panic!("output/file.md should not exist") + } + if !(&tmp.path().join("output/file.png")).exists() { + panic!("output/file.png should exist") + } + if !(&tmp.path().join("output/sub_dir/file.png")).exists() { + panic!("output/sub_dir/file.png should exist") + } + if !(&tmp.path().join("output/sub_dir_exists/file.txt")).exists() { + panic!("output/sub_dir/file.png should exist") + } + + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index c579d692..874896e2 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,148 +1,9 @@ extern crate pulldown_cmark; -use std::path::{Path, Component}; -use std::error::Error; -use std::io; -use std::fs::{self, metadata, File}; +pub mod fs; use self::pulldown_cmark::{Parser, html, Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES}; -/// Takes a path and returns a path containing just enough `../` to point to the root of the given path. -/// -/// This is mostly interesting for a relative path to point back to the directory from where the -/// path starts. -/// -/// ```ignore -/// let mut path = Path::new("some/relative/path"); -/// -/// println!("{}", path_to_root(&path)); -/// ``` -/// -/// **Outputs** -/// -/// ```text -/// "../../" -/// ``` -/// -/// **note:** it's not very fool-proof, if you find a situation where it doesn't return the correct -/// path. Consider [submitting a new issue](https://github.com/azerupi/mdBook/issues) or a -/// [pull-request](https://github.com/azerupi/mdBook/pulls) to improve it. - -pub fn path_to_root(path: &Path) -> String { - debug!("[fn]: path_to_root"); - // Remove filename and add "../" for every directory - - path.to_path_buf() - .parent() - .expect("") - .components() - .fold(String::new(), |mut s, c| { - match c { - Component::Normal(_) => s.push_str("../"), - _ => { - debug!("[*]: Other path component... {:?}", c); - }, - } - s - }) -} - - - -/// 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. - -pub fn create_file(path: &Path) -> Result> { - debug!("[fn]: create_file"); - - // Construct path - if let Some(p) = path.parent() { - debug!("Parent directory is: {:?}", p); - - try!(fs::create_dir_all(p)); - } - - debug!("[*]: Create file: {:?}", path); - let f = match File::create(path) { - Ok(f) => f, - Err(e) => { - debug!("File::create: {}", e); - return Err(Box::new(io::Error::new(io::ErrorKind::Other, format!("{}", e)))); - }, - }; - - Ok(f) -} - -/// Removes all the content of a directory but not the directory itself - -pub fn remove_dir_content(dir: &Path) -> Result<(), Box> { - for item in try!(fs::read_dir(dir)) { - if let Ok(item) = item { - let item = item.path(); - if item.is_dir() { - try!(fs::remove_dir_all(item)); - } else { - try!(fs::remove_file(item)); - } - } - } - Ok(()) -} - -/// -/// -/// Copies all files of a directory to another one except the files with the extensions given in the -/// `ext_blacklist` array - -pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blacklist: &[&str]) -> Result<(), Box> { - debug!("[fn] copy_files_except_ext"); - // Check that from and to are different - if from == to { - return Ok(()); - } - debug!("[*] Loop"); - for entry in try!(fs::read_dir(from)) { - let entry = try!(entry); - debug!("[*] {:?}", entry.path()); - let metadata = try!(entry.metadata()); - - // If the entry is a dir and the recursive option is enabled, call itself - if metadata.is_dir() && recursive { - if entry.path() == to.to_path_buf() { - continue; - } - debug!("[*] is dir"); - - // check if output dir already exists - if !to.join(entry.file_name()).exists() { - try!(fs::create_dir(&to.join(entry.file_name()))); - } - - try!(copy_files_except_ext(&from.join(entry.file_name()), - &to.join(entry.file_name()), - true, - ext_blacklist)); - } else if metadata.is_file() { - - // Check if it is in the blacklist - if let Some(ext) = entry.path().extension() { - if ext_blacklist.contains(&ext.to_str().unwrap()) { - continue; - } - debug!("[*] creating path for file: {:?}", - &to.join(entry.path().file_name().expect("a file should have a file name..."))); - - output!("[*] copying file: {:?}\n to {:?}", - entry.path(), - &to.join(entry.path().file_name().expect("a file should have a file name..."))); - try!(fs::copy(entry.path(), - &to.join(entry.path().file_name().expect("a file should have a file name...")))); - } - } - } - Ok(()) -} /// @@ -160,80 +21,3 @@ pub fn render_markdown(text: &str) -> String { html::push_html(&mut s, p); s } - - - -// ------------------------------------------------------------------------------------------------ -// ------------------------------------------------------------------------------------------------ - -// tests - -#[cfg(test)] -mod tests { - extern crate tempdir; - - use super::copy_files_except_ext; - use std::fs; - - #[test] - fn copy_files_except_ext_test() { - let tmp = match tempdir::TempDir::new("") { - Ok(t) => t, - Err(_) => panic!("Could not create a temp dir"), - }; - - // Create a couple of files - if let Err(_) = fs::File::create(&tmp.path().join("file.txt")) { - panic!("Could not create file.txt") - } - if let Err(_) = fs::File::create(&tmp.path().join("file.md")) { - panic!("Could not create file.md") - } - if let Err(_) = fs::File::create(&tmp.path().join("file.png")) { - panic!("Could not create file.png") - } - if let Err(_) = fs::create_dir(&tmp.path().join("sub_dir")) { - panic!("Could not create sub_dir") - } - if let Err(_) = fs::File::create(&tmp.path().join("sub_dir/file.png")) { - panic!("Could not create sub_dir/file.png") - } - if let Err(_) = fs::create_dir(&tmp.path().join("sub_dir_exists")) { - panic!("Could not create sub_dir_exists") - } - if let Err(_) = fs::File::create(&tmp.path().join("sub_dir_exists/file.txt")) { - panic!("Could not create sub_dir_exists/file.txt") - } - - // Create output dir - if let Err(_) = fs::create_dir(&tmp.path().join("output")) { - panic!("Could not create output") - } - if let Err(_) = fs::create_dir(&tmp.path().join("output/sub_dir_exists")) { - panic!("Could not create output/sub_dir_exists") - } - - match copy_files_except_ext(&tmp.path(), &tmp.path().join("output"), true, &["md"]) { - Err(e) => panic!("Error while executing the function:\n{:?}", e), - Ok(_) => {}, - } - - // Check if the correct files where created - if !(&tmp.path().join("output/file.txt")).exists() { - panic!("output/file.txt should exist") - } - if (&tmp.path().join("output/file.md")).exists() { - panic!("output/file.md should not exist") - } - if !(&tmp.path().join("output/file.png")).exists() { - panic!("output/file.png should exist") - } - if !(&tmp.path().join("output/sub_dir/file.png")).exists() { - panic!("output/sub_dir/file.png should exist") - } - if !(&tmp.path().join("output/sub_dir_exists/file.txt")).exists() { - panic!("output/sub_dir/file.png should exist") - } - - } -}