2019-05-26 02:50:41 +08:00
|
|
|
use crate::errors::*;
|
2019-05-07 02:20:58 +08:00
|
|
|
use std::convert::Into;
|
2016-11-01 16:19:08 +08:00
|
|
|
use std::fs::{self, File};
|
2019-06-20 10:49:18 +08:00
|
|
|
use std::io::Write;
|
2018-03-07 21:02:06 +08:00
|
|
|
use std::path::{Component, Path, PathBuf};
|
2016-03-18 05:41:00 +08:00
|
|
|
|
2018-03-07 21:02:06 +08:00
|
|
|
/// 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::<String>()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Write the given data to a file, creating it first if necessary
|
2018-07-24 01:45:01 +08:00
|
|
|
pub fn write_file<P: AsRef<Path>>(build_dir: &Path, filename: P, content: &[u8]) -> Result<()> {
|
2018-03-07 21:02:06 +08:00
|
|
|
let path = build_dir.join(filename);
|
|
|
|
|
2019-05-07 02:20:58 +08:00
|
|
|
create_file(&path)?.write_all(content).map_err(Into::into)
|
2018-03-07 21:02:06 +08:00
|
|
|
}
|
|
|
|
|
2017-05-19 19:04:37 +08:00
|
|
|
/// Takes a path and returns a path containing just enough `../` to point to
|
|
|
|
/// the root of the given path.
|
2016-03-18 05:41:00 +08:00
|
|
|
///
|
2017-05-19 19:04:37 +08:00
|
|
|
/// This is mostly interesting for a relative path to point back to the
|
|
|
|
/// directory from where the path starts.
|
2016-03-18 05:41:00 +08:00
|
|
|
///
|
2017-06-06 17:58:08 +08:00
|
|
|
/// ```rust
|
|
|
|
/// # 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), "../../");
|
|
|
|
/// # }
|
2016-03-18 05:41:00 +08:00
|
|
|
/// ```
|
|
|
|
///
|
2017-05-19 19:04:37 +08:00
|
|
|
/// **note:** it's not very fool-proof, if you find a situation where
|
|
|
|
/// it doesn't return the correct path.
|
2019-10-29 21:04:16 +08:00
|
|
|
/// 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.
|
2017-06-06 17:58:08 +08:00
|
|
|
pub fn path_to_root<P: Into<PathBuf>>(path: P) -> String {
|
2018-01-23 01:28:37 +08:00
|
|
|
debug!("path_to_root");
|
2016-03-18 05:41:00 +08:00
|
|
|
// Remove filename and add "../" for every directory
|
|
|
|
|
2017-06-06 17:58:08 +08:00
|
|
|
path.into()
|
2016-03-18 05:41:00 +08:00
|
|
|
.parent()
|
|
|
|
.expect("")
|
|
|
|
.components()
|
|
|
|
.fold(String::new(), |mut s, c| {
|
2018-01-23 01:28:37 +08:00
|
|
|
match c {
|
|
|
|
Component::Normal(_) => s.push_str("../"),
|
|
|
|
_ => {
|
|
|
|
debug!("Other path component... {:?}", c);
|
|
|
|
}
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
2018-01-23 01:28:37 +08:00
|
|
|
s
|
|
|
|
})
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
|
|
|
|
2017-05-19 19:04:37 +08:00
|
|
|
/// 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.
|
2017-06-25 00:04:57 +08:00
|
|
|
pub fn create_file(path: &Path) -> Result<File> {
|
2018-01-23 01:28:37 +08:00
|
|
|
debug!("Creating {}", path.display());
|
2016-03-18 05:41:00 +08:00
|
|
|
|
|
|
|
// Construct path
|
|
|
|
if let Some(p) = path.parent() {
|
2018-01-23 01:28:37 +08:00
|
|
|
trace!("Parent directory is: {:?}", p);
|
2016-03-18 05:41:00 +08:00
|
|
|
|
2017-05-19 19:04:37 +08:00
|
|
|
fs::create_dir_all(p)?;
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 02:20:58 +08:00
|
|
|
File::create(path).map_err(Into::into)
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Removes all the content of a directory but not the directory itself
|
2017-06-25 00:04:57 +08:00
|
|
|
pub fn remove_dir_content(dir: &Path) -> Result<()> {
|
2017-05-19 19:04:37 +08:00
|
|
|
for item in fs::read_dir(dir)? {
|
2016-03-18 05:41:00 +08:00
|
|
|
if let Ok(item) = item {
|
|
|
|
let item = item.path();
|
|
|
|
if item.is_dir() {
|
2017-05-19 19:04:37 +08:00
|
|
|
fs::remove_dir_all(item)?;
|
2016-03-18 05:41:00 +08:00
|
|
|
} else {
|
2017-05-19 19:04:37 +08:00
|
|
|
fs::remove_file(item)?;
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2017-05-19 19:04:37 +08:00
|
|
|
/// Copies all files of a directory to another one except the files
|
|
|
|
/// with the extensions given in the `ext_blacklist` array
|
2018-01-23 01:28:37 +08:00
|
|
|
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
|
|
|
|
);
|
|
|
|
|
2016-03-18 05:41:00 +08:00
|
|
|
// Check that from and to are different
|
|
|
|
if from == to {
|
|
|
|
return Ok(());
|
|
|
|
}
|
2018-01-23 01:28:37 +08:00
|
|
|
|
2017-05-19 19:04:37 +08:00
|
|
|
for entry in fs::read_dir(from)? {
|
|
|
|
let entry = entry?;
|
|
|
|
let metadata = entry.metadata()?;
|
2016-03-18 05:41:00 +08:00
|
|
|
|
|
|
|
// 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() {
|
2017-05-19 19:04:37 +08:00
|
|
|
fs::create_dir(&to.join(entry.file_name()))?;
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
|
|
|
|
2018-01-23 01:28:37 +08:00
|
|
|
copy_files_except_ext(
|
|
|
|
&from.join(entry.file_name()),
|
|
|
|
&to.join(entry.file_name()),
|
|
|
|
true,
|
|
|
|
ext_blacklist,
|
|
|
|
)?;
|
2016-03-18 05:41:00 +08:00
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
}
|
2018-01-23 01:28:37 +08:00
|
|
|
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..."),
|
|
|
|
),
|
|
|
|
)?;
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::copy_files_except_ext;
|
|
|
|
use std::fs;
|
|
|
|
|
2020-03-01 02:20:55 +08:00
|
|
|
#[test]
|
|
|
|
fn copy_files_except_ext_test() {
|
2020-04-04 01:12:43 +08:00
|
|
|
let tmp = match tempfile::TempDir::new() {
|
2020-03-01 02:20:55 +08:00
|
|
|
Ok(t) => t,
|
|
|
|
Err(e) => panic!("Could not create a temp dir: {}", e),
|
|
|
|
};
|
2016-03-18 05:41:00 +08:00
|
|
|
|
|
|
|
// Create a couple of files
|
2020-04-04 01:12:43 +08:00
|
|
|
if let Err(err) = fs::File::create(&tmp.path().join("file.txt")) {
|
2019-05-07 02:20:58 +08:00
|
|
|
panic!("Could not create file.txt: {}", err);
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
2020-04-04 01:12:43 +08:00
|
|
|
if let Err(err) = fs::File::create(&tmp.path().join("file.md")) {
|
2019-05-07 02:20:58 +08:00
|
|
|
panic!("Could not create file.md: {}", err);
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
2020-04-04 01:12:43 +08:00
|
|
|
if let Err(err) = fs::File::create(&tmp.path().join("file.png")) {
|
2019-05-07 02:20:58 +08:00
|
|
|
panic!("Could not create file.png: {}", err);
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
2020-04-04 01:12:43 +08:00
|
|
|
if let Err(err) = fs::create_dir(&tmp.path().join("sub_dir")) {
|
2019-05-07 02:20:58 +08:00
|
|
|
panic!("Could not create sub_dir: {}", err);
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
2020-04-04 01:12:43 +08:00
|
|
|
if let Err(err) = fs::File::create(&tmp.path().join("sub_dir/file.png")) {
|
2019-05-07 02:20:58 +08:00
|
|
|
panic!("Could not create sub_dir/file.png: {}", err);
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
2020-04-04 01:12:43 +08:00
|
|
|
if let Err(err) = fs::create_dir(&tmp.path().join("sub_dir_exists")) {
|
2019-05-07 02:20:58 +08:00
|
|
|
panic!("Could not create sub_dir_exists: {}", err);
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
2020-04-04 01:12:43 +08:00
|
|
|
if let Err(err) = fs::File::create(&tmp.path().join("sub_dir_exists/file.txt")) {
|
2019-05-07 02:20:58 +08:00
|
|
|
panic!("Could not create sub_dir_exists/file.txt: {}", err);
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create output dir
|
2020-04-04 01:12:43 +08:00
|
|
|
if let Err(err) = fs::create_dir(&tmp.path().join("output")) {
|
2019-05-07 02:20:58 +08:00
|
|
|
panic!("Could not create output: {}", err);
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
2020-04-04 01:12:43 +08:00
|
|
|
if let Err(err) = fs::create_dir(&tmp.path().join("output/sub_dir_exists")) {
|
2019-05-07 02:20:58 +08:00
|
|
|
panic!("Could not create output/sub_dir_exists: {}", err);
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
|
|
|
|
2019-05-07 02:20:58 +08:00
|
|
|
if let Err(e) =
|
2020-04-04 01:12:43 +08:00
|
|
|
copy_files_except_ext(&tmp.path(), &tmp.path().join("output"), true, &["md"])
|
2019-05-07 02:20:58 +08:00
|
|
|
{
|
|
|
|
panic!("Error while executing the function:\n{:?}", e);
|
2016-03-18 05:41:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the correct files where created
|
2020-04-04 01:12:43 +08:00
|
|
|
if !(&tmp.path().join("output/file.txt")).exists() {
|
2016-03-18 05:41:00 +08:00
|
|
|
panic!("output/file.txt should exist")
|
|
|
|
}
|
2020-04-04 01:12:43 +08:00
|
|
|
if (&tmp.path().join("output/file.md")).exists() {
|
2016-03-18 05:41:00 +08:00
|
|
|
panic!("output/file.md should not exist")
|
|
|
|
}
|
2020-04-04 01:12:43 +08:00
|
|
|
if !(&tmp.path().join("output/file.png")).exists() {
|
2016-03-18 05:41:00 +08:00
|
|
|
panic!("output/file.png should exist")
|
|
|
|
}
|
2020-04-04 01:12:43 +08:00
|
|
|
if !(&tmp.path().join("output/sub_dir/file.png")).exists() {
|
2016-03-18 05:41:00 +08:00
|
|
|
panic!("output/sub_dir/file.png should exist")
|
|
|
|
}
|
2020-04-04 01:12:43 +08:00
|
|
|
if !(&tmp.path().join("output/sub_dir_exists/file.txt")).exists() {
|
2016-03-18 05:41:00 +08:00
|
|
|
panic!("output/sub_dir/file.png should exist")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|