1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::path::Path;
use anyhow::{Context, Error};
use globset::{Glob, GlobSet, GlobSetBuilder};
use walkdir::{DirEntry, WalkDir};
#[derive(Debug, Clone)]
pub struct BulkCopy {
include: GlobSet,
blacklist: GlobSet,
max_depth: Option<usize>,
}
impl BulkCopy {
pub fn new<I, S>(include_globs: I) -> Result<Self, Error>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
Ok(BulkCopy {
include: compile_globs(include_globs)?,
blacklist: GlobSet::empty(),
max_depth: None,
})
}
pub fn with_max_depth(self, depth: impl Into<Option<usize>>) -> Self {
BulkCopy {
max_depth: depth.into(),
..self
}
}
pub fn with_blacklist<I, S>(self, globs: I) -> Result<Self, Error>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
Ok(BulkCopy {
blacklist: compile_globs(globs)?,
..self
})
}
pub fn copy<P, Q>(&self, from: P, to: Q) -> Result<(), Error>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let from = from.as_ref();
let to = to.as_ref();
let mut wd = WalkDir::new(from);
if let Some(max_depth) = self.max_depth {
wd = wd.max_depth(max_depth);
}
for entry in wd.into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if !self.include.is_match(path) || self.blacklist.is_match(path) {
continue;
}
self.copy_entry(from, to, &entry)?;
}
Ok(())
}
fn copy_entry(
&self,
from: &Path,
to: &Path,
entry: &DirEntry,
) -> Result<(), Error> {
let path = entry.path();
let stripped = path.strip_prefix(from)?;
let new_name = to.join(stripped);
if let Some(parent) = new_name.parent() {
std::fs::create_dir_all(parent).with_context(|| {
format!(
"Unable to create the \"{}\" directory",
parent.display()
)
})?;
}
log::debug!(
"Copying \"{}\" to \"{}\"",
path.display(),
new_name.display()
);
std::fs::copy(path, &new_name).with_context(|| {
format!(
"Unable to copy \"{}\" to \"{}\"",
path.display(),
new_name.display()
)
})?;
Ok(())
}
}
fn compile_globs<I, S>(globs: I) -> Result<GlobSet, Error>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let mut builder = GlobSetBuilder::new();
for glob in globs {
let glob = Glob::new(glob.as_ref())?;
builder.add(glob);
}
builder.build().map_err(Error::from)
}