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
use std::{
collections::HashSet,
ffi::{OsStr, OsString},
fs, io,
path::{Path, PathBuf},
};
pub(crate) struct UniqueNameFinder {
names: HashSet<String>,
number_prefix: String,
number_suffix: String,
}
impl UniqueNameFinder {
pub fn new<P, S>(number_prefix: P, number_suffix: S) -> Self
where
P: Into<String>,
S: Into<String>,
{
Self {
names: HashSet::new(),
number_prefix: number_prefix.into(),
number_suffix: number_suffix.into(),
}
}
pub fn find<S: Into<String>>(&mut self, name: S) -> String {
let name = name.into();
if self.names.insert(name.clone()) {
return name;
}
let mut i = 2;
loop {
let name = format!("{}{}{}{}", name, self.number_prefix, i, self.number_suffix);
if self.names.insert(name.clone()) {
return name;
}
i += 1;
}
}
}
pub(crate) fn append_os_file_ext<P, E>(path: P, file_ext: E) -> OsString
where
P: AsRef<OsStr>,
E: AsRef<OsStr>,
{
let mut s = path.as_ref().to_os_string();
s.push(".");
s.push(file_ext);
s
}
pub fn copy_dir<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
let mut stack = vec![from.as_ref().to_owned()];
let output_root = to.as_ref().to_owned();
let input_root = from.as_ref().components().count();
while let Some(working_path) = stack.pop() {
let src: PathBuf = working_path.components().skip(input_root).collect();
let dest = output_root.join(&src);
fs::create_dir_all(&dest)?;
for entry in fs::read_dir(working_path)? {
let path = entry?.path();
if path.is_dir() {
stack.push(path);
} else {
let file_name = path.file_name().expect("invalid file name");
let dest_path = dest.join(file_name);
fs::copy(&path, &dest_path)?;
}
}
}
Ok(())
}
pub fn sanitize_file_name(file_name: &str) -> String {
use sanitize_filename::{sanitize_with_options, Options};
sanitize_with_options(
file_name,
Options {
replacement: "_",
truncate: false,
..Options::default()
},
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unique_name_finder_test() {
let mut finder = UniqueNameFinder::new(" (", ")");
assert_eq!(finder.find("foo"), "foo");
assert_eq!(finder.find("foo"), "foo (2)");
assert_eq!(finder.find("foo (2)"), "foo (2) (2)");
assert_eq!(finder.find("bar (2)"), "bar (2)");
assert_eq!(finder.find("bar"), "bar");
assert_eq!(finder.find("bar"), "bar (3)");
}
}