use std::fs;
use std::io;
use std::path::Path;
#[allow(dead_code)] pub fn copy_if_newer(src: &Path, dest: &Path) -> io::Result<bool> {
let should_copy = should_copy_file(src, dest);
if should_copy {
match reflink_copy::reflink(src, dest) {
Ok(()) => Ok(true),
Err(_) => {
fs::copy(src, dest)?;
Ok(true)
}
}
} else {
Ok(false)
}
}
#[allow(dead_code)] fn should_copy_file(src: &Path, dest: &Path) -> bool {
if !dest.exists() {
return true;
}
let src_meta = fs::metadata(src).ok();
let dst_meta = fs::metadata(dest).ok();
match (src_meta, dst_meta) {
(Some(src), Some(dst)) => {
src.modified().ok() > dst.modified().ok() || src.len() != dst.len()
}
_ => true,
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn test_copy_if_newer_new_file() {
let dir = tempdir().unwrap();
let src = dir.path().join("src.txt");
let dest = dir.path().join("dest.txt");
fs::write(&src, "hello").unwrap();
let copied = copy_if_newer(&src, &dest).unwrap();
assert!(copied, "Should copy new file");
assert!(dest.exists(), "Destination should exist");
assert_eq!(fs::read_to_string(&dest).unwrap(), "hello");
}
#[test]
fn test_copy_if_newer_same_content() {
let dir = tempdir().unwrap();
let src = dir.path().join("src.txt");
let dest = dir.path().join("dest.txt");
fs::write(&src, "hello").unwrap();
fs::copy(&src, &dest).unwrap();
std::thread::sleep(std::time::Duration::from_millis(10));
let copied = copy_if_newer(&src, &dest).unwrap();
assert!(!copied, "Should not copy identical file");
}
#[test]
fn test_copy_if_newer_different_size() {
let dir = tempdir().unwrap();
let src = dir.path().join("src.txt");
let dest = dir.path().join("dest.txt");
fs::write(&src, "hello world").unwrap();
fs::write(&dest, "hi").unwrap();
let copied = copy_if_newer(&src, &dest).unwrap();
assert!(copied, "Should copy when sizes differ");
assert_eq!(fs::read_to_string(&dest).unwrap(), "hello world");
}
#[test]
fn test_copy_if_newer_source_newer() {
let dir = tempdir().unwrap();
let src = dir.path().join("src.txt");
let dest = dir.path().join("dest.txt");
fs::write(&dest, "old").unwrap();
std::thread::sleep(std::time::Duration::from_millis(100));
fs::write(&src, "new").unwrap();
let copied = copy_if_newer(&src, &dest).unwrap();
assert!(copied, "Should copy newer source");
assert_eq!(fs::read_to_string(&dest).unwrap(), "new");
}
#[test]
fn test_should_copy_file_nonexistent_dest() {
let dir = tempdir().unwrap();
let src = dir.path().join("src.txt");
let dest = dir.path().join("dest.txt");
fs::write(&src, "hello").unwrap();
assert!(
should_copy_file(&src, &dest),
"Should copy when dest doesn't exist"
);
}
#[test]
fn test_copy_if_newer_creates_distinct_inode() {
use std::os::unix::fs::MetadataExt;
let dir = tempdir().unwrap();
let src = dir.path().join("binary");
let dest = dir.path().join("binary-copy");
fs::write(&src, "ELF-fake-binary-data").unwrap();
let copied = copy_if_newer(&src, &dest).unwrap();
assert!(copied);
let src_ino = fs::metadata(&src).unwrap().ino();
let dest_ino = fs::metadata(&dest).unwrap().ino();
assert_ne!(
src_ino, dest_ino,
"Source and dest must have different inodes for memory isolation"
);
}
#[test]
fn test_copy_if_newer_large_file_byte_identical() {
let dir = tempdir().unwrap();
let src = dir.path().join("large.bin");
let dest = dir.path().join("large-copy.bin");
let pattern: Vec<u8> = (0u8..=255).cycle().take(4096).collect();
fs::write(&src, &pattern).unwrap();
let copied = copy_if_newer(&src, &dest).unwrap();
assert!(copied);
let src_data = fs::read(&src).unwrap();
let dest_data = fs::read(&dest).unwrap();
assert_eq!(src_data.len(), dest_data.len(), "File sizes must match");
assert_eq!(src_data, dest_data, "File contents must be byte-identical");
}
}