use std::path::Path;
pub(crate) fn read<T: serde::de::DeserializeOwned>(path: &Path, what: &str) -> Option<T> {
let bytes = std::fs::read(path).ok()?;
match ciborium::from_reader(&bytes[..]) {
Ok(value) => Some(value),
Err(e) => {
tracing::warn!("{what} unreadable, starting fresh: {e}");
None
}
}
}
pub(crate) fn write<T: serde::Serialize>(path: &Path, value: &T) -> std::io::Result<()> {
if let Some(dir) = path.parent() {
std::fs::create_dir_all(dir)?;
}
let mut bytes = Vec::new();
ciborium::into_writer(value, &mut bytes).map_err(std::io::Error::other)?;
std::fs::write(path, bytes)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn write_then_read_round_trips_through_a_created_directory() {
let tree = concinnity_testing::TempTree::new();
let path = tree.join("outer/nested/store");
write(&path, &vec![1u32, 2, 3]).expect("write creates the parent dir");
assert_eq!(read::<Vec<u32>>(&path, "test store"), Some(vec![1, 2, 3]));
}
#[test]
fn a_missing_or_corrupt_file_reads_as_none() {
let tree = concinnity_testing::TempTree::new();
let path = tree.join("store");
assert_eq!(read::<Vec<u32>>(&path, "test store"), None, "missing file");
tree.write("store", b"not cbor at all");
assert_eq!(read::<Vec<u32>>(&path, "test store"), None, "corrupt file");
}
}