Skip to content

Commit a4c48e6

Browse files
committed
Add fs utilities
Add dependency `remove_dir_all` Note: On windows `std::fs::remove_dir_all` doesn't really work (rust-lang/rust#29497).
1 parent 0909ae3 commit a4c48e6

File tree

3 files changed

+90
-0
lines changed

3 files changed

+90
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ bindgen = "0.57"
2828
xmas-elf = "0.8"
2929
bitflags = "1.3"
3030
shlex = "1.0"
31+
remove_dir_all = "0.7"

src/fs.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//! Filesystem utilities
2+
3+
use std::fs::{self, File};
4+
use std::io::{self, Read, Seek};
5+
use std::path::Path;
6+
7+
use anyhow::Result;
8+
9+
pub use remove_dir_all::*;
10+
11+
/// Copy `src_file` to `dest_file_or_dir` if `src_file` is different or the destination
12+
/// file doesn't exist.
13+
pub fn copy_file_if_different(
14+
src_file: impl AsRef<Path>,
15+
dest_file_or_dir: impl AsRef<Path>,
16+
) -> Result<()> {
17+
let src_file: &Path = src_file.as_ref();
18+
let dest_file_or_dir: &Path = dest_file_or_dir.as_ref();
19+
20+
assert!(src_file.is_file());
21+
22+
let mut src_fd = fs::File::open(src_file)?;
23+
24+
let (dest_fd, dest_file) = if dest_file_or_dir.exists() {
25+
if dest_file_or_dir.is_dir() {
26+
let dest_file = dest_file_or_dir.join(src_file.file_name().unwrap());
27+
if dest_file.exists() {
28+
(Some(fs::File::open(&dest_file)?), dest_file)
29+
} else {
30+
(None, dest_file)
31+
}
32+
} else {
33+
(
34+
Some(fs::File::open(dest_file_or_dir)?),
35+
dest_file_or_dir.to_owned(),
36+
)
37+
}
38+
} else {
39+
(None, dest_file_or_dir.to_owned())
40+
};
41+
42+
if let Some(mut dest_fd) = dest_fd {
43+
if !is_file_eq(&mut src_fd, &mut dest_fd)? {
44+
drop(dest_fd);
45+
drop(src_fd);
46+
fs::copy(src_file, dest_file)?;
47+
}
48+
} else {
49+
fs::copy(src_file, dest_file)?;
50+
}
51+
Ok(())
52+
}
53+
54+
/// Whether the file type and contents of `file` are equal to `other`.
55+
pub fn is_file_eq(file: &mut File, other: &mut File) -> Result<bool> {
56+
let file_meta = file.metadata()?;
57+
let other_meta = other.metadata()?;
58+
59+
if file_meta.file_type() == other_meta.file_type() && file_meta.len() == other_meta.len() {
60+
let mut file_bytes = io::BufReader::new(&*file).bytes();
61+
let mut other_bytes = io::BufReader::new(&*other).bytes();
62+
63+
// TODO: check performance
64+
let result = loop {
65+
match (file_bytes.next(), other_bytes.next()) {
66+
(Some(Ok(b0)), Some(Ok(b1))) => {
67+
if b0 != b1 {
68+
break Ok(false);
69+
}
70+
}
71+
(None, None) => break Ok(true),
72+
(None, Some(_)) | (Some(_), None) => break Ok(false),
73+
(Some(Err(e)), _) | (_, Some(Err(e))) => return Err(e.into()),
74+
}
75+
};
76+
drop(file_bytes);
77+
drop(other_bytes);
78+
79+
// rewind files
80+
// TODO: is this needed?
81+
file.seek(io::SeekFrom::Start(0))?;
82+
other.seek(io::SeekFrom::Start(0))?;
83+
84+
result
85+
} else {
86+
Ok(false)
87+
}
88+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ pub mod pio;
88
pub mod python;
99
pub mod symgen;
1010
pub mod utils;
11+
pub mod fs;

0 commit comments

Comments
 (0)