From e18d4cea824e5b5a820b137eea65d57898283d22 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 30 Jul 2023 20:31:40 +0200 Subject: [PATCH] io/fs: do not replace files with unchanged contents Do not update file timestamps when contents have not actually changed. --- src/io/fs.rs | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/io/fs.rs b/src/io/fs.rs index 46e8726..2109961 100644 --- a/src/io/fs.rs +++ b/src/io/fs.rs @@ -1,6 +1,6 @@ use std::{ fs::{self, File}, - io::{BufWriter, Write}, + io::{BufReader, BufWriter, Read, Write}, path::{Path, PathBuf}, }; @@ -41,19 +41,44 @@ where .with_context(|| format!("Failed to write file {}", path.display())) } +pub fn equal(path1: &Path, path2: &Path) -> Result { + let mut file1 = BufReader::new( + fs::File::open(path1) + .with_context(|| format!("Failed to open file {}", path1.display()))?, + ) + .bytes(); + let mut file2 = BufReader::new( + fs::File::open(path2) + .with_context(|| format!("Failed to open file {}", path2.display()))?, + ) + .bytes(); + + Ok(loop { + match (file1.next().transpose()?, file2.next().transpose()?) { + (Some(b1), Some(b2)) if b1 == b2 => continue, + (None, None) => break true, + _ => break false, + }; + }) +} + pub fn create_with_tmpfile(path: &Path, f: F) -> Result where F: FnOnce(&mut BufWriter) -> Result, { let tmp_path = tmpfile_name(path); + let mut cleanup = true; let ret = (|| { let ret = create(&tmp_path, f)?; - rename(&tmp_path, path)?; + if !matches!(equal(path, &tmp_path), Result::Ok(true)) { + rename(&tmp_path, path)?; + cleanup = false; + } Ok(ret) })(); - if ret.is_err() { + if cleanup { let _ = fs::remove_file(&tmp_path); }