1use crate::errors::*;
2use log::{debug, trace};
3use std::convert::Into;
4use std::fs::{self, File};
5use std::io::Write;
6use std::path::{Component, Path, PathBuf};
7
8pub fn normalize_path(path: &str) -> String {
10 use std::path::is_separator;
11 path.chars()
12 .map(|ch| if is_separator(ch) { '/' } else { ch })
13 .collect::<String>()
14}
15
16pub fn write_file<P: AsRef<Path>>(build_dir: &Path, filename: P, content: &[u8]) -> Result<()> {
18 let path = build_dir.join(filename);
19
20 create_file(&path)?.write_all(content).map_err(Into::into)
21}
22
23pub fn path_to_root<P: Into<PathBuf>>(path: P) -> String {
41 debug!("path_to_root");
42 path.into()
45 .parent()
46 .expect("")
47 .components()
48 .fold(String::new(), |mut s, c| {
49 match c {
50 Component::Normal(_) => s.push_str("../"),
51 _ => {
52 debug!("Other path component... {:?}", c);
53 }
54 }
55 s
56 })
57}
58
59pub fn create_file(path: &Path) -> Result<File> {
63 debug!("Creating {}", path.display());
64
65 if let Some(p) = path.parent() {
67 trace!("Parent directory is: {:?}", p);
68
69 fs::create_dir_all(p)?;
70 }
71
72 File::create(path).map_err(Into::into)
73}
74
75pub fn remove_dir_content(dir: &Path) -> Result<()> {
77 for item in fs::read_dir(dir)? {
78 if let Ok(item) = item {
79 let item = item.path();
80 if item.is_dir() {
81 fs::remove_dir_all(item)?;
82 } else {
83 fs::remove_file(item)?;
84 }
85 }
86 }
87 Ok(())
88}
89
90pub fn copy_files_except_ext(
93 from: &Path,
94 to: &Path,
95 recursive: bool,
96 avoid_dir: Option<&PathBuf>,
97 ext_blacklist: &[&str],
98) -> Result<()> {
99 debug!(
100 "Copying all files from {} to {} (blacklist: {:?}), avoiding {:?}",
101 from.display(),
102 to.display(),
103 ext_blacklist,
104 avoid_dir
105 );
106
107 if from == to {
109 return Ok(());
110 }
111
112 for entry in fs::read_dir(from)? {
113 let entry = entry?;
114 let metadata = entry
115 .path()
116 .metadata()
117 .with_context(|| format!("Failed to read {:?}", entry.path()))?;
118
119 if metadata.is_dir() && recursive {
121 if entry.path() == to.to_path_buf() {
122 continue;
123 }
124
125 if let Some(avoid) = avoid_dir {
126 if entry.path() == *avoid {
127 continue;
128 }
129 }
130
131 if !to.join(entry.file_name()).exists() {
133 fs::create_dir(&to.join(entry.file_name()))?;
134 }
135
136 copy_files_except_ext(
137 &from.join(entry.file_name()),
138 &to.join(entry.file_name()),
139 true,
140 avoid_dir,
141 ext_blacklist,
142 )?;
143 } else if metadata.is_file() {
144 if let Some(ext) = entry.path().extension() {
146 if ext_blacklist.contains(&ext.to_str().unwrap()) {
147 continue;
148 }
149 }
150 debug!(
151 "creating path for file: {:?}",
152 &to.join(
153 entry
154 .path()
155 .file_name()
156 .expect("a file should have a file name...")
157 )
158 );
159
160 debug!(
161 "Copying {:?} to {:?}",
162 entry.path(),
163 &to.join(
164 entry
165 .path()
166 .file_name()
167 .expect("a file should have a file name...")
168 )
169 );
170 fs::copy(
171 entry.path(),
172 &to.join(
173 entry
174 .path()
175 .file_name()
176 .expect("a file should have a file name..."),
177 ),
178 )?;
179 }
180 }
181 Ok(())
182}
183
184pub fn get_404_output_file(input_404: &Option<String>) -> String {
185 input_404
186 .as_ref()
187 .unwrap_or(&"404.md".to_string())
188 .replace(".md", ".html")
189}
190
191#[cfg(test)]
192mod tests {
193 use super::copy_files_except_ext;
194 use std::{fs, io::Result, path::Path};
195
196 #[cfg(target_os = "windows")]
197 fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<()> {
198 std::os::windows::fs::symlink_file(src, dst)
199 }
200
201 #[cfg(not(target_os = "windows"))]
202 fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<()> {
203 std::os::unix::fs::symlink(src, dst)
204 }
205
206 #[test]
207 fn copy_files_except_ext_test() {
208 let tmp = match tempfile::TempDir::new() {
209 Ok(t) => t,
210 Err(e) => panic!("Could not create a temp dir: {}", e),
211 };
212
213 if let Err(err) = fs::File::create(&tmp.path().join("file.txt")) {
215 panic!("Could not create file.txt: {}", err);
216 }
217 if let Err(err) = fs::File::create(&tmp.path().join("file.md")) {
218 panic!("Could not create file.md: {}", err);
219 }
220 if let Err(err) = fs::File::create(&tmp.path().join("file.png")) {
221 panic!("Could not create file.png: {}", err);
222 }
223 if let Err(err) = fs::create_dir(&tmp.path().join("sub_dir")) {
224 panic!("Could not create sub_dir: {}", err);
225 }
226 if let Err(err) = fs::File::create(&tmp.path().join("sub_dir/file.png")) {
227 panic!("Could not create sub_dir/file.png: {}", err);
228 }
229 if let Err(err) = fs::create_dir(&tmp.path().join("sub_dir_exists")) {
230 panic!("Could not create sub_dir_exists: {}", err);
231 }
232 if let Err(err) = fs::File::create(&tmp.path().join("sub_dir_exists/file.txt")) {
233 panic!("Could not create sub_dir_exists/file.txt: {}", err);
234 }
235 if let Err(err) = symlink(
236 &tmp.path().join("file.png"),
237 &tmp.path().join("symlink.png"),
238 ) {
239 panic!("Could not symlink file.png: {}", err);
240 }
241
242 if let Err(err) = fs::create_dir(&tmp.path().join("output")) {
244 panic!("Could not create output: {}", err);
245 }
246 if let Err(err) = fs::create_dir(&tmp.path().join("output/sub_dir_exists")) {
247 panic!("Could not create output/sub_dir_exists: {}", err);
248 }
249
250 if let Err(e) =
251 copy_files_except_ext(tmp.path(), &tmp.path().join("output"), true, None, &["md"])
252 {
253 panic!("Error while executing the function:\n{:?}", e);
254 }
255
256 if !(&tmp.path().join("output/file.txt")).exists() {
258 panic!("output/file.txt should exist")
259 }
260 if (&tmp.path().join("output/file.md")).exists() {
261 panic!("output/file.md should not exist")
262 }
263 if !(&tmp.path().join("output/file.png")).exists() {
264 panic!("output/file.png should exist")
265 }
266 if !(&tmp.path().join("output/sub_dir/file.png")).exists() {
267 panic!("output/sub_dir/file.png should exist")
268 }
269 if !(&tmp.path().join("output/sub_dir_exists/file.txt")).exists() {
270 panic!("output/sub_dir/file.png should exist")
271 }
272 if !(&tmp.path().join("output/symlink.png")).exists() {
273 panic!("output/symlink.png should exist")
274 }
275 }
276}