]> git.proxmox.com Git - rustc.git/blame - vendor/mdbook/src/utils/fs.rs
New upstream version 1.56.0~beta.4+dfsg1
[rustc.git] / vendor / mdbook / src / utils / fs.rs
CommitLineData
dc9dc135
XL
1use crate::errors::*;
2use std::convert::Into;
7cac9316 3use std::fs::{self, File};
416331ca 4use std::io::Write;
83c7162d 5use std::path::{Component, Path, PathBuf};
7cac9316 6
94222f64 7/// Naively replaces any path separator with a forward-slash '/'
83c7162d
XL
8pub fn normalize_path(path: &str) -> String {
9 use std::path::is_separator;
10 path.chars()
11 .map(|ch| if is_separator(ch) { '/' } else { ch })
12 .collect::<String>()
13}
14
15/// Write the given data to a file, creating it first if necessary
9fa01778 16pub fn write_file<P: AsRef<Path>>(build_dir: &Path, filename: P, content: &[u8]) -> Result<()> {
83c7162d
XL
17 let path = build_dir.join(filename);
18
dc9dc135 19 create_file(&path)?.write_all(content).map_err(Into::into)
83c7162d
XL
20}
21
041b39d2
XL
22/// Takes a path and returns a path containing just enough `../` to point to
23/// the root of the given path.
7cac9316 24///
041b39d2
XL
25/// This is mostly interesting for a relative path to point back to the
26/// directory from where the path starts.
7cac9316 27///
ea8adc8c 28/// ```rust
ea8adc8c
XL
29/// # use std::path::Path;
30/// # use mdbook::utils::fs::path_to_root;
ea8adc8c
XL
31/// let path = Path::new("some/relative/path");
32/// assert_eq!(path_to_root(path), "../../");
7cac9316
XL
33/// ```
34///
041b39d2
XL
35/// **note:** it's not very fool-proof, if you find a situation where
36/// it doesn't return the correct path.
e74abb32
XL
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.
ea8adc8c 39pub fn path_to_root<P: Into<PathBuf>>(path: P) -> String {
2c00a5a8 40 debug!("path_to_root");
7cac9316
XL
41 // Remove filename and add "../" for every directory
42
ea8adc8c 43 path.into()
7cac9316
XL
44 .parent()
45 .expect("")
46 .components()
47 .fold(String::new(), |mut s, c| {
48 match c {
49 Component::Normal(_) => s.push_str("../"),
50 _ => {
2c00a5a8
XL
51 debug!("Other path component... {:?}", c);
52 }
7cac9316
XL
53 }
54 s
55 })
56}
57
041b39d2
XL
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.
ea8adc8c 61pub fn create_file(path: &Path) -> Result<File> {
2c00a5a8 62 debug!("Creating {}", path.display());
7cac9316
XL
63
64 // Construct path
65 if let Some(p) = path.parent() {
2c00a5a8 66 trace!("Parent directory is: {:?}", p);
7cac9316 67
041b39d2 68 fs::create_dir_all(p)?;
7cac9316
XL
69 }
70
dc9dc135 71 File::create(path).map_err(Into::into)
7cac9316
XL
72}
73
74/// Removes all the content of a directory but not the directory itself
ea8adc8c 75pub fn remove_dir_content(dir: &Path) -> Result<()> {
041b39d2 76 for item in fs::read_dir(dir)? {
7cac9316
XL
77 if let Ok(item) = item {
78 let item = item.path();
79 if item.is_dir() {
041b39d2 80 fs::remove_dir_all(item)?;
7cac9316 81 } else {
041b39d2 82 fs::remove_file(item)?;
7cac9316
XL
83 }
84 }
85 }
86 Ok(())
87}
88
041b39d2
XL
89/// Copies all files of a directory to another one except the files
90/// with the extensions given in the `ext_blacklist` array
2c00a5a8
XL
91pub fn copy_files_except_ext(
92 from: &Path,
93 to: &Path,
94 recursive: bool,
f9f354fc 95 avoid_dir: Option<&PathBuf>,
2c00a5a8
XL
96 ext_blacklist: &[&str],
97) -> Result<()> {
98 debug!(
f9f354fc 99 "Copying all files from {} to {} (blacklist: {:?}), avoiding {:?}",
2c00a5a8
XL
100 from.display(),
101 to.display(),
f9f354fc
XL
102 ext_blacklist,
103 avoid_dir
2c00a5a8
XL
104 );
105
7cac9316
XL
106 // Check that from and to are different
107 if from == to {
108 return Ok(());
109 }
2c00a5a8 110
041b39d2
XL
111 for entry in fs::read_dir(from)? {
112 let entry = entry?;
5869c6ff
XL
113 let metadata = entry
114 .path()
115 .metadata()
116 .with_context(|| format!("Failed to read {:?}", entry.path()))?;
7cac9316
XL
117
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() {
121 continue;
122 }
7cac9316 123
f9f354fc
XL
124 if let Some(avoid) = avoid_dir {
125 if entry.path() == *avoid {
126 continue;
127 }
128 }
129
7cac9316
XL
130 // check if output dir already exists
131 if !to.join(entry.file_name()).exists() {
041b39d2 132 fs::create_dir(&to.join(entry.file_name()))?;
7cac9316
XL
133 }
134
2c00a5a8
XL
135 copy_files_except_ext(
136 &from.join(entry.file_name()),
137 &to.join(entry.file_name()),
138 true,
f9f354fc 139 avoid_dir,
2c00a5a8
XL
140 ext_blacklist,
141 )?;
7cac9316 142 } else if metadata.is_file() {
7cac9316
XL
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()) {
146 continue;
147 }
148 }
2c00a5a8
XL
149 debug!(
150 "creating path for file: {:?}",
151 &to.join(
152 entry
153 .path()
154 .file_name()
155 .expect("a file should have a file name...")
156 )
157 );
158
159 debug!(
160 "Copying {:?} to {:?}",
161 entry.path(),
162 &to.join(
163 entry
164 .path()
165 .file_name()
166 .expect("a file should have a file name...")
167 )
168 );
169 fs::copy(
170 entry.path(),
171 &to.join(
172 entry
173 .path()
174 .file_name()
175 .expect("a file should have a file name..."),
176 ),
177 )?;
7cac9316
XL
178 }
179 }
180 Ok(())
181}
182
f035d41b
XL
183pub fn get_404_output_file(input_404: &Option<String>) -> String {
184 input_404
185 .as_ref()
186 .unwrap_or(&"404.md".to_string())
187 .replace(".md", ".html")
188}
189
7cac9316
XL
190#[cfg(test)]
191mod tests {
7cac9316 192 use super::copy_files_except_ext;
fc512014
XL
193 use std::{fs, io::Result, path::Path};
194
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)
198 }
199
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)
203 }
7cac9316
XL
204
205 #[test]
206 fn copy_files_except_ext_test() {
83c7162d 207 let tmp = match tempfile::TempDir::new() {
7cac9316 208 Ok(t) => t,
dc9dc135 209 Err(e) => panic!("Could not create a temp dir: {}", e),
7cac9316
XL
210 };
211
212 // Create a couple of files
dc9dc135
XL
213 if let Err(err) = fs::File::create(&tmp.path().join("file.txt")) {
214 panic!("Could not create file.txt: {}", err);
7cac9316 215 }
dc9dc135
XL
216 if let Err(err) = fs::File::create(&tmp.path().join("file.md")) {
217 panic!("Could not create file.md: {}", err);
7cac9316 218 }
dc9dc135
XL
219 if let Err(err) = fs::File::create(&tmp.path().join("file.png")) {
220 panic!("Could not create file.png: {}", err);
7cac9316 221 }
dc9dc135
XL
222 if let Err(err) = fs::create_dir(&tmp.path().join("sub_dir")) {
223 panic!("Could not create sub_dir: {}", err);
7cac9316 224 }
dc9dc135
XL
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);
7cac9316 227 }
dc9dc135
XL
228 if let Err(err) = fs::create_dir(&tmp.path().join("sub_dir_exists")) {
229 panic!("Could not create sub_dir_exists: {}", err);
7cac9316 230 }
dc9dc135
XL
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);
7cac9316 233 }
fc512014
XL
234 if let Err(err) = symlink(
235 &tmp.path().join("file.png"),
236 &tmp.path().join("symlink.png"),
237 ) {
238 panic!("Could not symlink file.png: {}", err);
239 }
7cac9316
XL
240
241 // Create output dir
dc9dc135
XL
242 if let Err(err) = fs::create_dir(&tmp.path().join("output")) {
243 panic!("Could not create output: {}", err);
7cac9316 244 }
dc9dc135
XL
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);
7cac9316
XL
247 }
248
dc9dc135 249 if let Err(e) =
f9f354fc 250 copy_files_except_ext(&tmp.path(), &tmp.path().join("output"), true, None, &["md"])
dc9dc135
XL
251 {
252 panic!("Error while executing the function:\n{:?}", e);
7cac9316
XL
253 }
254
255 // Check if the correct files where created
256 if !(&tmp.path().join("output/file.txt")).exists() {
257 panic!("output/file.txt should exist")
258 }
259 if (&tmp.path().join("output/file.md")).exists() {
260 panic!("output/file.md should not exist")
261 }
262 if !(&tmp.path().join("output/file.png")).exists() {
263 panic!("output/file.png should exist")
264 }
265 if !(&tmp.path().join("output/sub_dir/file.png")).exists() {
266 panic!("output/sub_dir/file.png should exist")
267 }
268 if !(&tmp.path().join("output/sub_dir_exists/file.txt")).exists() {
269 panic!("output/sub_dir/file.png should exist")
270 }
fc512014
XL
271 if !(&tmp.path().join("output/symlink.png")).exists() {
272 panic!("output/symlink.png should exist")
273 }
7cac9316
XL
274 }
275}