1use ignore::gitignore::{Gitignore, GitignoreBuilder};
2use log::*;
3use std::fs;
4use std::io;
5use std::path::Path;
6use walkdir::WalkDir;
7
8pub fn copy_directory<P: AsRef<Path>>(source: &P, destination: P) -> io::Result<()> {
9 let mut ignore_builder = GitignoreBuilder::new(source);
10 let ignore_file = source.as_ref().join(".smaugignore");
11
12 if ignore_file.is_file() {
13 ignore_builder.add(ignore_file);
14 }
15
16 let ignore = ignore_builder
17 .build()
18 .expect("Could not parse smaugignore file");
19
20 for entry in WalkDir::new(source) {
21 let entry = entry.expect("Could not find directory");
22 let entry = entry.path();
23 let relative = entry.strip_prefix(source.as_ref()).unwrap();
24 let new_path = destination.as_ref().join(relative);
25
26 if entry.is_file() && !is_ignored(entry, &ignore) {
27 trace!(
28 "Creating directory {}",
29 new_path.parent().and_then(|p| p.to_str()).unwrap()
30 );
31 fs::create_dir_all(new_path.parent().unwrap())?;
32 trace!(
33 "Copying file from {} to {}",
34 entry.to_str().unwrap(),
35 new_path.to_str().unwrap()
36 );
37 fs::copy(entry, new_path)?;
38 }
39 }
40
41 Ok(())
42}
43
44fn is_git_dir(path: &str) -> bool {
45 path.contains("/.git/") || path.contains("\\.git\\")
46}
47
48fn is_ignored(path: &Path, ignore: &Gitignore) -> bool {
49 if matches_ignore(path, ignore) {
50 trace!("Ignoring {:?}", path);
51 return true;
52 }
53
54 false
55}
56
57fn matches_ignore(path: &Path, ignore: &Gitignore) -> bool {
58 ignore
59 .matched_path_or_any_parents(path, path.is_dir())
60 .is_ignore()
61 || is_git_dir(path.to_string_lossy().as_ref())
62}