From cb861a7354e65781b85020203a66b8ce3fac5101 Mon Sep 17 00:00:00 2001 From: Gambhiro Date: Wed, 18 Jan 2017 13:13:38 +0000 Subject: [PATCH] clean output folder without removing dotfiles --- src/renderer/html_handlebars/hbs_renderer.rs | 14 ++++--- src/tests/fs_test.rs | 41 +++++++++++++++++--- src/utils/fs.rs | 35 ++++++++++++----- 3 files changed, 70 insertions(+), 20 deletions(-) diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 0b00a0e1..94359c3a 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -6,6 +6,7 @@ use book::toc::{TocItem, TocContent}; use utils; use FILES; +use std::process::exit; use std::path::{Path, PathBuf}; use std::ffi::OsStr; use std::fs::{self, File}; @@ -35,15 +36,16 @@ impl Renderer for HtmlHandlebars { let mut book_project = MDBook::new(&project_root); book_project.read_config(); + + if !book_project.get_src_base().exists() { + println!("Source folder doesn't exist: {:?}", book_project.get_src_base()); + exit(2); + } + book_project.parse_books(); book_project.link_translations(); - // Clean output directory - - // FIXME don't remove dotfiles such as .git/ folder. It's a common - // practice to track gh-pages in a versioned output folder. - - //try!(utils::fs::remove_dir_content(&book_project.get_dest_base())); + try!(utils::fs::clean_output_dir(&book_project.get_dest_base())); // TODO talk to the user try!(self.render(&book_project)); diff --git a/src/tests/fs_test.rs b/src/tests/fs_test.rs index 5b3e3c8c..c4a9dfda 100644 --- a/src/tests/fs_test.rs +++ b/src/tests/fs_test.rs @@ -1,5 +1,6 @@ #[cfg(test)] +use std; use std::fs::{self, File}; use std::io::Read; use std::path::Path; @@ -8,7 +9,9 @@ use utils; #[test] fn it_copies_data_file() { - let dest_path = Path::new("the place was dark").join("and dusty and half-lost").join("book.css"); + let dest_base = std::env::temp_dir().join("in tangles of old alleys"); + let dest_path = dest_base.join("book.css"); + utils::fs::copy_data_file("data/_html-template/css/books.css", &dest_path); let mut file = match File::open(&dest_path) { @@ -26,11 +29,15 @@ fn it_copies_data_file() { } assert!(content.as_str().contains("Open Sans")); + + if dest_base.exists() { + fs::remove_dir_all(dest_base); + } } #[test] fn it_copies_data_by_pattern() { - let dest_base = Path::new("in tangles of old alleys").join("near the quays"); + let dest_base = std::env::temp_dir().join("near the quays"); if let Err(e) = utils::fs::copy_data("data/_html-template/**/*", "data/_html-template/", @@ -43,8 +50,32 @@ fn it_copies_data_by_pattern() { assert!(dest_base.join("css").join("book.css").exists()); assert!(!dest_base.join("_layouts").exists()); - let p = Path::new("in tangles of old alleys"); - if p.exists() { - fs::remove_dir_all(p); + if dest_base.exists() { + fs::remove_dir_all(dest_base); + } +} + +#[test] +fn it_doesnt_delete_toplevel_dotfiles() { + let dest_base = std::env::temp_dir().join("with queer curls of fog"); + + match utils::fs::create_with_str(&dest_base.join(".dotfile"), "that west winds tossed") { + Err(e) => { println!("Error: {:#?}", e); return; }, + Ok(_) => {}, + } + + match utils::fs::create_with_str(&dest_base.join("door.html"), "

I entered, charmed

") { + Err(e) => { println!("Error: {:#?}", e); return; }, + Ok(_) => {}, + } + + utils::fs::clean_output_dir(&dest_base); + + assert!(dest_base.exists()); + assert!(dest_base.join(".dotfile").exists()); + assert!(!dest_base.join("door.html").exists()); + + if dest_base.exists() { + fs::remove_dir_all(dest_base); } } diff --git a/src/utils/fs.rs b/src/utils/fs.rs index 20e455e8..6a0a90e4 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -89,6 +89,7 @@ pub fn copy_data(include_glob: &str, .map(|x| { let mut s: &str = &x.replace(include_base, ""); s = s.trim_left_matches("/"); + s = s.trim_left_matches(r"\"); let p = Path::new(s); let dest_path = dest_base.join(p); @@ -242,21 +243,37 @@ pub fn create_file(path: &Path) -> Result> { Ok(f) } -// TODO why not just delete the folder and re-create it? - -/// Removes all the content of a directory but not the directory itself -pub fn remove_dir_content(dir: &Path) -> Result<(), Box> { +/// A cleaning operation intended to be used on the output directory of a book +/// before producing new output files. It removes the content of the directory, +/// except for dotfiles at the toplevel. +/// +/// This keeps VCS files intact such as `.git/, .gitignore`, and the output +/// folder can be used to track the gh-pages branch of the repository, which is +/// a common practice. +pub fn clean_output_dir(dir: &Path) -> Result<(), Box> { if !dir.exists() { return Ok(()); } + let exclude_pat = Pattern::new(".*").unwrap(); + 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)); + if let Some(a) = item.path().to_str() { + if let Some(d) = dir.as_os_str().to_str() { + let mut s: &str = &a.replace(d, ""); + s = s.trim_left_matches("/"); + s = s.trim_left_matches(r"\"); + + if !exclude_pat.matches(s) { + let p = item.path(); + if p.is_dir() { + try!(fs::remove_dir_all(p)); + } else { + try!(fs::remove_file(p)); + } + } + } } } }