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
use std::fs;
use std::io::{self, Error, ErrorKind};
use std::path::Path;
pub fn flatten<P>(path: P) -> io::Result<()>
where
P: AsRef<Path>,
{
flatten_or_move(&path, &path)
}
fn flatten_or_move<P, Q>(path: P, dest: Q) -> io::Result<()>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let path = path.as_ref();
let dest = dest.as_ref();
if dest.is_file() {
return Err(Error::new(
ErrorKind::InvalidInput,
"Path is not a directory",
));
}
if path.is_file() {
if path.parent().map(|dir| dir == dest).unwrap_or(false) {
return Ok(());
}
let mut to = dest.to_path_buf();
to.push(path.file_name().unwrap());
fs::rename(path, to)?;
} else {
for entry in fs::read_dir(path)? {
flatten_or_move(entry?.path(), dest)?;
}
if path != dest {
fs::remove_dir(path)?;
}
}
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
const TEST_DIR: &str = "./test_dir";
fn make_test_dir() -> io::Result<()> {
fs::create_dir(TEST_DIR)?;
fs::create_dir("./test_dir/test_subdir")?;
fs::write("./test_dir/test_file.txt", "test")?;
fs::write("./test_dir/test_subdir/test_subfile.txt", "test")?;
Ok(())
}
fn remove_test_dir() -> io::Result<()> {
fs::remove_dir_all(TEST_DIR)
}
#[test]
fn test_flatten() {
make_test_dir().unwrap();
flatten(TEST_DIR).unwrap();
let files: Vec<_> = fs::read_dir(TEST_DIR)
.unwrap()
.map(|entry| entry.unwrap().path().file_name().unwrap().to_owned())
.collect();
assert_eq!(files, ["test_file.txt", "test_subfile.txt"]);
remove_test_dir().unwrap();
}
}