From 6b76c382117f5551b90507cea9121c9cbd500e60 Mon Sep 17 00:00:00 2001 From: Ben Bridle Date: Sat, 1 Feb 2025 17:25:42 +1300 Subject: Only copy modified files When copying a file, if a file already exists at the destination path with the same last-modified date and the same size, the file is not copied. --- src/entry.rs | 8 +++++++- src/operations/cp.rs | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/entry.rs b/src/entry.rs index 72eeb7e..1b6b577 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -1,6 +1,9 @@ -use std::path::{Path, PathBuf}; use crate::*; +use std::path::{Path, PathBuf}; +use std::time::SystemTime; + + #[derive(PartialEq)] pub enum EntryType { File, @@ -17,6 +20,8 @@ pub struct Entry { pub path: PathBuf, /// The file path as originally presented to the [Entry] constructor. pub original_path: PathBuf, + /// Not available on all platforms. + pub last_modified: Option, } impl Entry { @@ -51,6 +56,7 @@ impl Entry { path: canonical_path, original_path: path.to_path_buf(), is_symlink: metadata.is_symlink(), + last_modified: metadata.modified().ok(), }) } diff --git a/src/operations/cp.rs b/src/operations/cp.rs index 33d4d04..6037b67 100644 --- a/src/operations/cp.rs +++ b/src/operations/cp.rs @@ -1,5 +1,8 @@ use crate::*; +use std::time::SystemTime; + + #[must_use] pub fn copy(source_path: impl AsRef, destination_path: impl AsRef) -> WriteResult<()> { let source = get_entry(&source_path)?; @@ -40,8 +43,16 @@ pub fn copy(source_path: impl AsRef, destination_path: impl AsRef) - #[must_use] fn copy_file(source_path: impl AsRef, destination_path: impl AsRef) -> WriteResult<()> { - let copy_result = std::fs::copy(&source_path, &destination_path); - io_result_to_write_result(copy_result, &destination_path.as_ref())?; + // Only copy file if size or last modified time is different to file at destination. + if let Comparison::Different(last_modified) = compare_files(&source_path, &destination_path) { + let copy_result = std::fs::copy(&source_path, &destination_path); + io_result_to_write_result(copy_result, &destination_path.as_ref())?; + if let Some(time) = last_modified { + if let Ok(dest) = std::fs::File::open(&destination_path) { + let _ = dest.set_modified(time); + } + } + } Ok(()) } @@ -53,3 +64,21 @@ fn copy_directory(source_path: impl AsRef, destination_path: impl AsRef), +} + +fn compare_files(source_path: impl AsRef, destination_path: impl AsRef) -> Comparison { + let Ok(source) = std::fs::File::open(&source_path) else { return Comparison::Different(None) }; + let Ok(source_metadata) = source.metadata() else { return Comparison::Different(None) }; + let Ok(source_modified) = source_metadata.modified() else { return Comparison::Different(None) }; + let Ok(dest) = std::fs::File::open(&destination_path) else { return Comparison::Different(Some(source_modified)) }; + let Ok(dest_metadata) = dest.metadata() else { return Comparison::Different(Some(source_modified)) }; + let Ok(dest_modified) = dest_metadata.modified() else { return Comparison::Different(Some(source_modified)) }; + match source_modified == dest_modified && source_metadata.len() == dest_metadata.len() { + true => Comparison::Same, + false => Comparison::Different(Some(source_modified)), + } +} -- cgit v1.2.3-70-g09d2