2 use std
::convert
::Into
;
3 use std
::fs
::{self, File}
;
5 use std
::path
::{Component, Path, PathBuf}
;
7 /// Naively replaces any path seperator with a forward-slash '/'
8 pub fn normalize_path(path
: &str) -> String
{
9 use std
::path
::is_separator
;
11 .map(|ch
| if is_separator(ch
) { '/' }
else { ch }
)
15 /// Write the given data to a file, creating it first if necessary
16 pub fn write_file
<P
: AsRef
<Path
>>(build_dir
: &Path
, filename
: P
, content
: &[u8]) -> Result
<()> {
17 let path
= build_dir
.join(filename
);
19 create_file(&path
)?
.write_all(content
).map_err(Into
::into
)
22 /// Takes a path and returns a path containing just enough `../` to point to
23 /// the root of the given path.
25 /// This is mostly interesting for a relative path to point back to the
26 /// directory from where the path starts.
29 /// # use std::path::Path;
30 /// # use mdbook::utils::fs::path_to_root;
31 /// let path = Path::new("some/relative/path");
32 /// assert_eq!(path_to_root(path), "../../");
35 /// **note:** it's not very fool-proof, if you find a situation where
36 /// it doesn't return the correct path.
37 /// Consider [submitting a new issue](https://github.com/rust-lang/mdBook/issues)
38 /// or a [pull-request](https://github.com/rust-lang/mdBook/pulls) to improve it.
39 pub fn path_to_root
<P
: Into
<PathBuf
>>(path
: P
) -> String
{
40 debug
!("path_to_root");
41 // Remove filename and add "../" for every directory
47 .fold(String
::new(), |mut s
, c
| {
49 Component
::Normal(_
) => s
.push_str("../"),
51 debug
!("Other path component... {:?}", c
);
58 /// This function creates a file and returns it. But before creating the file
59 /// it checks every directory in the path to see if it exists,
60 /// and if it does not it will be created.
61 pub fn create_file(path
: &Path
) -> Result
<File
> {
62 debug
!("Creating {}", path
.display());
65 if let Some(p
) = path
.parent() {
66 trace
!("Parent directory is: {:?}", p
);
68 fs
::create_dir_all(p
)?
;
71 File
::create(path
).map_err(Into
::into
)
74 /// Removes all the content of a directory but not the directory itself
75 pub fn remove_dir_content(dir
: &Path
) -> Result
<()> {
76 for item
in fs
::read_dir(dir
)?
{
77 if let Ok(item
) = item
{
78 let item
= item
.path();
80 fs
::remove_dir_all(item
)?
;
82 fs
::remove_file(item
)?
;
89 /// Copies all files of a directory to another one except the files
90 /// with the extensions given in the `ext_blacklist` array
91 pub fn copy_files_except_ext(
95 avoid_dir
: Option
<&PathBuf
>,
96 ext_blacklist
: &[&str],
99 "Copying all files from {} to {} (blacklist: {:?}), avoiding {:?}",
106 // Check that from and to are different
111 for entry
in fs
::read_dir(from
)?
{
116 .with_context(|| format
!("Failed to read {:?}", entry
.path()))?
;
118 // If the entry is a dir and the recursive option is enabled, call itself
119 if metadata
.is_dir() && recursive
{
120 if entry
.path() == to
.to_path_buf() {
124 if let Some(avoid
) = avoid_dir
{
125 if entry
.path() == *avoid
{
130 // check if output dir already exists
131 if !to
.join(entry
.file_name()).exists() {
132 fs
::create_dir(&to
.join(entry
.file_name()))?
;
135 copy_files_except_ext(
136 &from
.join(entry
.file_name()),
137 &to
.join(entry
.file_name()),
142 } else if metadata
.is_file() {
143 // Check if it is in the blacklist
144 if let Some(ext
) = entry
.path().extension() {
145 if ext_blacklist
.contains(&ext
.to_str().unwrap()) {
150 "creating path for file: {:?}",
155 .expect("a file should have a file name...")
160 "Copying {:?} to {:?}",
166 .expect("a file should have a file name...")
175 .expect("a file should have a file name..."),
183 pub fn get_404_output_file(input_404
: &Option
<String
>) -> String
{
186 .unwrap_or(&"404.md".to_string())
187 .replace(".md", ".html")
192 use super::copy_files_except_ext
;
193 use std
::{fs, io::Result, path::Path}
;
195 #[cfg(target_os = "windows")]
196 fn symlink
<P
: AsRef
<Path
>, Q
: AsRef
<Path
>>(src
: P
, dst
: Q
) -> Result
<()> {
197 std
::os
::windows
::fs
::symlink_file(src
, dst
)
200 #[cfg(not(target_os = "windows"))]
201 fn symlink
<P
: AsRef
<Path
>, Q
: AsRef
<Path
>>(src
: P
, dst
: Q
) -> Result
<()> {
202 std
::os
::unix
::fs
::symlink(src
, dst
)
206 fn copy_files_except_ext_test() {
207 let tmp
= match tempfile
::TempDir
::new() {
209 Err(e
) => panic
!("Could not create a temp dir: {}", e
),
212 // Create a couple of files
213 if let Err(err
) = fs
::File
::create(&tmp
.path().join("file.txt")) {
214 panic
!("Could not create file.txt: {}", err
);
216 if let Err(err
) = fs
::File
::create(&tmp
.path().join("file.md")) {
217 panic
!("Could not create file.md: {}", err
);
219 if let Err(err
) = fs
::File
::create(&tmp
.path().join("file.png")) {
220 panic
!("Could not create file.png: {}", err
);
222 if let Err(err
) = fs
::create_dir(&tmp
.path().join("sub_dir")) {
223 panic
!("Could not create sub_dir: {}", err
);
225 if let Err(err
) = fs
::File
::create(&tmp
.path().join("sub_dir/file.png")) {
226 panic
!("Could not create sub_dir/file.png: {}", err
);
228 if let Err(err
) = fs
::create_dir(&tmp
.path().join("sub_dir_exists")) {
229 panic
!("Could not create sub_dir_exists: {}", err
);
231 if let Err(err
) = fs
::File
::create(&tmp
.path().join("sub_dir_exists/file.txt")) {
232 panic
!("Could not create sub_dir_exists/file.txt: {}", err
);
234 if let Err(err
) = symlink(
235 &tmp
.path().join("file.png"),
236 &tmp
.path().join("symlink.png"),
238 panic
!("Could not symlink file.png: {}", err
);
242 if let Err(err
) = fs
::create_dir(&tmp
.path().join("output")) {
243 panic
!("Could not create output: {}", err
);
245 if let Err(err
) = fs
::create_dir(&tmp
.path().join("output/sub_dir_exists")) {
246 panic
!("Could not create output/sub_dir_exists: {}", err
);
250 copy_files_except_ext(&tmp
.path(), &tmp
.path().join("output"), true, None
, &["md"])
252 panic
!("Error while executing the function:\n{:?}", e
);
255 // Check if the correct files where created
256 if !(&tmp
.path().join("output/file.txt")).exists() {
257 panic
!("output/file.txt should exist")
259 if (&tmp
.path().join("output/file.md")).exists() {
260 panic
!("output/file.md should not exist")
262 if !(&tmp
.path().join("output/file.png")).exists() {
263 panic
!("output/file.png should exist")
265 if !(&tmp
.path().join("output/sub_dir/file.png")).exists() {
266 panic
!("output/sub_dir/file.png should exist")
268 if !(&tmp
.path().join("output/sub_dir_exists/file.txt")).exists() {
269 panic
!("output/sub_dir/file.png should exist")
271 if !(&tmp
.path().join("output/symlink.png")).exists() {
272 panic
!("output/symlink.png should exist")