1use std::fs::OpenOptions;
4use std::io::ErrorKind;
5use std::path::Path;
6
7use anyhow::Context;
8
9pub fn rewrite(dir: &Path, name: &str, change: impl FnOnce(&str) -> String) -> anyhow::Result<()> {
13 std::fs::create_dir_all(dir).with_context(|| format!("creating {}", dir.display()))?;
14 let lock_path = dir.join(format!("{name}.lock"));
15 let lock = OpenOptions::new()
16 .write(true)
17 .create(true)
18 .truncate(false)
19 .open(&lock_path)
20 .with_context(|| format!("opening {}", lock_path.display()))?;
21 lock.lock()
22 .with_context(|| format!("locking {}", lock_path.display()))?;
23 let file = dir.join(name);
24 let text = match std::fs::read_to_string(&file) {
25 Ok(t) => t,
26 Err(e) if e.kind() == ErrorKind::NotFound => String::new(),
27 Err(e) => return Err(e).with_context(|| format!("reading {}", file.display())),
28 };
29 let tmp = dir.join(format!("{name}.tmp"));
30 std::fs::write(&tmp, change(&text)).with_context(|| format!("writing {}", tmp.display()))?;
31 std::fs::rename(&tmp, &file).with_context(|| format!("writing {}", file.display()))?;
32 Ok(())
33}