use errors::*; use std::fs::{self, File}; use std::io::{Read, Write}; use std::path::{Component, Path, PathBuf}; /// Takes a path to a file and try to read the file into a String pub fn file_to_string>(path: P) -> Result { let path = path.as_ref(); let mut content = String::new(); File::open(path) .chain_err(|| "Unable to open the file")? .read_to_string(&mut content) .chain_err(|| "Unable to read the file")?; Ok(content) } /// Naively replaces any path seperator with a forward-slash '/' pub fn normalize_path(path: &str) -> String { use std::path::is_separator; path.chars() .map(|ch| if is_separator(ch) { '/' } else { ch }) .collect::() } /// Write the given data to a file, creating it first if necessary pub fn write_file>(build_dir: &Path, filename: P, content: &[u8]) -> Result<()> { let path = build_dir.join(filename); create_file(&path)?.write_all(content).map_err(|e| e.into()) } /// 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. /// /// ```rust /// # extern crate mdbook; /// # /// # use std::path::Path; /// # use mdbook::utils::fs::path_to_root; /// # /// # fn main() { /// let path = Path::new("some/relative/path"); /// assert_eq!(path_to_root(path), "../../"); /// # } /// ``` /// /// **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/rust-lang-nursery/mdBook/issues) /// or a [pull-request](https://github.com/rust-lang-nursery/mdBook/pulls) to improve it. pub fn path_to_root>(path: P) -> String { debug!("path_to_root"); // Remove filename and add "../" for every directory path.into() .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!("Creating {}", path.display()); // Construct path if let Some(p) = path.parent() { trace!("Parent directory is: {:?}", p); fs::create_dir_all(p)?; } File::create(path).map_err(|e| e.into()) } /// Removes all the content of a directory but not the directory itself pub fn remove_dir_content(dir: &Path) -> Result<()> { for item in fs::read_dir(dir)? { if let Ok(item) = item { let item = item.path(); if item.is_dir() { fs::remove_dir_all(item)?; } else { 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<()> { debug!( "Copying all files from {} to {} (blacklist: {:?})", from.display(), to.display(), ext_blacklist ); // Check that from and to are different if from == to { return Ok(()); } for entry in fs::read_dir(from)? { let entry = entry?; let metadata = 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; } // check if output dir already exists if !to.join(entry.file_name()).exists() { fs::create_dir(&to.join(entry.file_name()))?; } 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...") ) ); debug!( "Copying {:?} to {:?}", entry.path(), &to.join( entry .path() .file_name() .expect("a file should have a file name...") ) ); fs::copy( entry.path(), &to.join( entry .path() .file_name() .expect("a file should have a file name..."), ), )?; } } Ok(()) } #[cfg(test)] mod tests { extern crate tempfile; use super::copy_files_except_ext; use std::fs; #[test] fn copy_files_except_ext_test() { let tmp = match tempfile::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") } } }