foundry_common/tempo/
registry.rs1use eyre::{Result, WrapErr};
2use serde::{Serialize, de::DeserializeOwned};
3use std::{
4 fs,
5 io::{ErrorKind, Write},
6 path::Path,
7};
8
9pub(crate) fn read_toml_file<T: DeserializeOwned>(path: &Path, label: &str) -> Result<Option<T>> {
18 let contents = match fs::read_to_string(path) {
19 Ok(contents) => contents,
20 Err(e) if e.kind() == ErrorKind::NotFound => {
21 tracing::trace!(?path, "{label} file not found");
22 return Ok(None);
23 }
24 Err(e) => {
25 return Err(e)
26 .wrap_err_with(|| format!("failed to read {label} file {}", path.display()));
27 }
28 };
29
30 let value = toml::from_str(&contents)
31 .wrap_err_with(|| format!("failed to parse {label} file {}", path.display()))?;
32 Ok(Some(value))
33}
34
35pub(crate) fn write_toml_file_atomic<T: Serialize>(
42 path: &Path,
43 value: &T,
44 header: &str,
45) -> Result<()> {
46 let dir =
47 path.parent().ok_or_else(|| eyre::eyre!("invalid registry path: {}", path.display()))?;
48 fs::create_dir_all(dir)?;
49
50 let body = toml::to_string_pretty(value)?;
51 let contents = if header.trim().is_empty() { body } else { format!("{header}\n\n{body}") };
52
53 let mut tmp = tempfile::NamedTempFile::new_in(dir)?;
54 tmp.write_all(contents.as_bytes())?;
55 tmp.flush()?;
56 tmp.as_file().sync_all()?;
57 tmp.persist(path).map_err(|e| eyre::eyre!("failed to persist {}: {e}", path.display()))?;
58 sync_parent_dir(dir)?;
59
60 Ok(())
61}
62
63#[cfg(unix)]
64fn sync_parent_dir(dir: &Path) -> Result<()> {
65 fs::File::open(dir)?.sync_all()?;
66 Ok(())
67}
68
69#[cfg(not(unix))]
70fn sync_parent_dir(_dir: &Path) -> Result<()> {
71 Ok(())
72}