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
use std::path::{Path, PathBuf};
use std::io;
use std::fs;
use quick_error::ResultExt;
struct SourceDirectory<'a>(&'a Path);
struct ObtainEntryIn<'a>(&'a Path);
struct CreateDirectory<'a>(&'a Path);
quick_error!{
#[derive(Debug)]
pub enum Error {
CreateDirectory(p: PathBuf, err: io::Error) {
description("A directory could not be created")
display("Failed to create directory '{}'", p.display())
context(p: CreateDirectory<'a>, err: io::Error) -> (p.0.to_path_buf(), err)
cause(err)
}
ObtainEntry(p: PathBuf, err: io::Error) {
description("A directory entry could not be obtained")
display("Failed to read directory entry of '{}'", p.display())
context(p: ObtainEntryIn<'a>, err: io::Error) -> (p.0.to_path_buf(), err)
cause(err)
}
ReadDirectory(p: PathBuf, err: io::Error) {
description("A directory could not be read to obtain its entries")
display("Failed to read directory '{}'", p.display())
context(p: SourceDirectory<'a>, err: io::Error) -> (p.0.to_path_buf(), err)
cause(err)
}
DestinationDirectoryExists(p: PathBuf) {
description("Cannot copy directories into an existing destination directory")
display("Destination directory '{}' did already exist", p.display())
}
Copy(src: PathBuf, dest: PathBuf, err: io::Error) {
description("A file could not be copied to its destination")
display("Failed to copy '{}' to '{}'", src.display(), dest.display())
context(c: (&'a PathBuf, &'a PathBuf), err: io::Error) -> (c.0.clone(), c.1.clone(), err)
cause(err)
}
}
}
pub fn destination_directory<P, O>(source_dir: P, destination_dir: O) -> PathBuf
where
P: AsRef<Path>,
O: AsRef<Path>,
{
let source_dir = source_dir
.as_ref()
.canonicalize()
.unwrap_or_else(|_| source_dir.as_ref().to_path_buf());
destination_dir
.as_ref()
.join(source_dir.file_name().unwrap_or_else(|| "ROOT".as_ref()))
}
pub fn copy_directory<P, O>(source_dir: P, destination_dir: O) -> Result<PathBuf, Error>
where
P: AsRef<Path>,
O: AsRef<Path>,
{
let dest = destination_directory(source_dir.as_ref(), destination_dir);
if dest.is_dir() {
return Err(Error::DestinationDirectoryExists(dest));
}
fn visit_dirs(dir: &Path, dest: &Path) -> Result<(), Error> {
for entry in fs::read_dir(dir).context(SourceDirectory(dir))? {
let path = entry.context(ObtainEntryIn(dir))?.path();
if path.is_dir() {
visit_dirs(
&path,
&dest.join(path.file_name().expect("should always have filename here"))
)?;
} else {
fs::create_dir_all(&dest).context(CreateDirectory(&dest))?;
let dest = dest.join(&path.file_name().expect("should have filename here"));
fs::copy(&path, &dest).context((&path, &dest))?;
}
}
Ok(())
}
visit_dirs(source_dir.as_ref(), &dest)?;
Ok(dest)
}