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 dir = std::env::temp_dir()
.join(format!("cn_cbor_file_round_trip_{}", std::process::id()))
.join("nested");
let path = dir.join("store");
let _ = std::fs::remove_dir_all(dir.parent().unwrap());
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]));
let _ = std::fs::remove_dir_all(path.parent().unwrap().parent().unwrap());
}
#[test]
fn a_missing_or_corrupt_file_reads_as_none() {
let dir = std::env::temp_dir().join(format!("cn_cbor_file_corrupt_{}", std::process::id()));
let path = dir.join("store");
let _ = std::fs::remove_dir_all(&dir);
assert_eq!(read::<Vec<u32>>(&path, "test store"), None, "missing file");
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(&path, b"not cbor at all").unwrap();
assert_eq!(read::<Vec<u32>>(&path, "test store"), None, "corrupt file");
let _ = std::fs::remove_dir_all(&dir);
}
}