|
1 | 1 | use anyhow::Context;
|
| 2 | +use std::collections::hash_map::DefaultHasher; |
2 | 3 | use std::ffi::OsStr;
|
3 | 4 | use std::fs;
|
4 |
| -use std::path::{Component, Path}; |
| 5 | +use std::hash::{Hash, Hasher}; |
| 6 | +use std::path::{Component, Path, PathBuf}; |
5 | 7 | use std::process::Command;
|
6 | 8 |
|
7 | 9 | #[cfg(windows)]
|
@@ -142,6 +144,46 @@ pub fn robocopy(
|
142 | 144 | Ok(())
|
143 | 145 | }
|
144 | 146 |
|
| 147 | +/// Loads contents of a file and hashes it when it is created. |
| 148 | +/// It then asserts that when it is dropped, the file still has the same contents. |
| 149 | +#[must_use = "EnsureImmutableFile acts like a guard, consider keeping it alive until something can happen with the file"] |
| 150 | +pub struct EnsureImmutableFile { |
| 151 | + path: PathBuf, |
| 152 | + hash: u64, |
| 153 | + name: String, |
| 154 | +} |
| 155 | + |
| 156 | +impl EnsureImmutableFile { |
| 157 | + pub fn new(path: &Path, name: String) -> anyhow::Result<Self> { |
| 158 | + let hash = Self::hash(path)?; |
| 159 | + Ok(Self { |
| 160 | + path: path.to_path_buf(), |
| 161 | + hash, |
| 162 | + name, |
| 163 | + }) |
| 164 | + } |
| 165 | + |
| 166 | + fn hash(path: &Path) -> anyhow::Result<u64> { |
| 167 | + let contents = fs::read(path)?; |
| 168 | + let mut hasher = DefaultHasher::new(); |
| 169 | + contents.hash(&mut hasher); |
| 170 | + Ok(hasher.finish()) |
| 171 | + } |
| 172 | +} |
| 173 | + |
| 174 | +impl Drop for EnsureImmutableFile { |
| 175 | + fn drop(&mut self) { |
| 176 | + let hash = Self::hash(&self.path).expect("Cannot hash file"); |
| 177 | + assert_eq!( |
| 178 | + self.hash, |
| 179 | + hash, |
| 180 | + "{} ({}) has changed during a build", |
| 181 | + self.path.display(), |
| 182 | + self.name |
| 183 | + ); |
| 184 | + } |
| 185 | +} |
| 186 | + |
145 | 187 | #[cfg(test)]
|
146 | 188 | mod tests {
|
147 | 189 | use super::get_file_count_and_size;
|
|
0 commit comments