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
use std::fs::{self, File};
use std::io::{self, Read};
use std::path::Path;
use anyhow::Result;
pub use remove_dir_all::*;
pub fn copy_file_if_different(
src_file: impl AsRef<Path>,
dest_file_or_dir: impl AsRef<Path>,
) -> Result<()> {
let src_file: &Path = src_file.as_ref();
let dest_file_or_dir: &Path = dest_file_or_dir.as_ref();
assert!(src_file.is_file());
let src_fd = fs::File::open(src_file)?;
let (dest_fd, dest_file) = if dest_file_or_dir.exists() {
if dest_file_or_dir.is_dir() {
let dest_file = dest_file_or_dir.join(src_file.file_name().unwrap());
if dest_file.exists() {
(Some(fs::File::open(&dest_file)?), dest_file)
} else {
(None, dest_file)
}
} else {
(
Some(fs::File::open(dest_file_or_dir)?),
dest_file_or_dir.to_owned(),
)
}
} else {
(None, dest_file_or_dir.to_owned())
};
if let Some(dest_fd) = dest_fd {
if !is_file_eq(&src_fd, &dest_fd)? {
drop(dest_fd);
drop(src_fd);
fs::copy(src_file, dest_file)?;
}
} else {
fs::copy(src_file, dest_file)?;
}
Ok(())
}
pub fn is_file_eq(file: &File, other: &File) -> Result<bool> {
let file_meta = file.metadata()?;
let other_meta = other.metadata()?;
if file_meta.file_type() == other_meta.file_type() && file_meta.len() == other_meta.len() {
let mut file_bytes = io::BufReader::new(&*file).bytes();
let mut other_bytes = io::BufReader::new(&*other).bytes();
loop {
match (file_bytes.next(), other_bytes.next()) {
(Some(Ok(b0)), Some(Ok(b1))) => {
if b0 != b1 {
break Ok(false);
}
}
(None, None) => break Ok(true),
(None, Some(_)) | (Some(_), None) => break Ok(false),
(Some(Err(e)), _) | (_, Some(Err(e))) => return Err(e.into()),
}
}
} else {
Ok(false)
}
}