diff options
Diffstat (limited to 'vendor/mdbook/src/utils/fs.rs')
-rw-r--r-- | vendor/mdbook/src/utils/fs.rs | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/vendor/mdbook/src/utils/fs.rs b/vendor/mdbook/src/utils/fs.rs new file mode 100644 index 000000000..a933d548a --- /dev/null +++ b/vendor/mdbook/src/utils/fs.rs @@ -0,0 +1,275 @@ +use crate::errors::*; +use std::convert::Into; +use std::fs::{self, File}; +use std::io::Write; +use std::path::{Component, Path, PathBuf}; + +/// Naively replaces any path separator 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::<String>() +} + +/// Write the given data to a file, creating it first if necessary +pub fn write_file<P: AsRef<Path>>(build_dir: &Path, filename: P, content: &[u8]) -> Result<()> { + let path = build_dir.join(filename); + + create_file(&path)?.write_all(content).map_err(Into::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 +/// # use std::path::Path; +/// # use mdbook::utils::fs::path_to_root; +/// 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/mdBook/issues) +/// or a [pull-request](https://github.com/rust-lang/mdBook/pulls) to improve it. +pub fn path_to_root<P: Into<PathBuf>>(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<File> { + 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(Into::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, + avoid_dir: Option<&PathBuf>, + ext_blacklist: &[&str], +) -> Result<()> { + debug!( + "Copying all files from {} to {} (blacklist: {:?}), avoiding {:?}", + from.display(), + to.display(), + ext_blacklist, + avoid_dir + ); + + // 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 + .path() + .metadata() + .with_context(|| format!("Failed to read {:?}", entry.path()))?; + + // 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; + } + + if let Some(avoid) = avoid_dir { + if entry.path() == *avoid { + 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, + avoid_dir, + 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(()) +} + +pub fn get_404_output_file(input_404: &Option<String>) -> String { + input_404 + .as_ref() + .unwrap_or(&"404.md".to_string()) + .replace(".md", ".html") +} + +#[cfg(test)] +mod tests { + use super::copy_files_except_ext; + use std::{fs, io::Result, path::Path}; + + #[cfg(target_os = "windows")] + fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<()> { + std::os::windows::fs::symlink_file(src, dst) + } + + #[cfg(not(target_os = "windows"))] + fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<()> { + std::os::unix::fs::symlink(src, dst) + } + + #[test] + fn copy_files_except_ext_test() { + let tmp = match tempfile::TempDir::new() { + Ok(t) => t, + Err(e) => panic!("Could not create a temp dir: {}", e), + }; + + // Create a couple of files + if let Err(err) = fs::File::create(&tmp.path().join("file.txt")) { + panic!("Could not create file.txt: {}", err); + } + if let Err(err) = fs::File::create(&tmp.path().join("file.md")) { + panic!("Could not create file.md: {}", err); + } + if let Err(err) = fs::File::create(&tmp.path().join("file.png")) { + panic!("Could not create file.png: {}", err); + } + if let Err(err) = fs::create_dir(&tmp.path().join("sub_dir")) { + panic!("Could not create sub_dir: {}", err); + } + if let Err(err) = fs::File::create(&tmp.path().join("sub_dir/file.png")) { + panic!("Could not create sub_dir/file.png: {}", err); + } + if let Err(err) = fs::create_dir(&tmp.path().join("sub_dir_exists")) { + panic!("Could not create sub_dir_exists: {}", err); + } + if let Err(err) = fs::File::create(&tmp.path().join("sub_dir_exists/file.txt")) { + panic!("Could not create sub_dir_exists/file.txt: {}", err); + } + if let Err(err) = symlink( + &tmp.path().join("file.png"), + &tmp.path().join("symlink.png"), + ) { + panic!("Could not symlink file.png: {}", err); + } + + // Create output dir + if let Err(err) = fs::create_dir(&tmp.path().join("output")) { + panic!("Could not create output: {}", err); + } + if let Err(err) = fs::create_dir(&tmp.path().join("output/sub_dir_exists")) { + panic!("Could not create output/sub_dir_exists: {}", err); + } + + if let Err(e) = + copy_files_except_ext(tmp.path(), &tmp.path().join("output"), true, None, &["md"]) + { + panic!("Error while executing the function:\n{:?}", e); + } + + // 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") + } + if !(&tmp.path().join("output/symlink.png")).exists() { + panic!("output/symlink.png should exist") + } + } +} |