use kanban_persistence::PersistenceResult;
use std::path::Path;
use tokio::fs;
pub struct AtomicWriter;
impl AtomicWriter {
pub async fn write_atomic(path: &Path, data: &[u8]) -> PersistenceResult<()> {
let parent = path.parent().unwrap_or_else(|| Path::new("."));
let temp_file = tempfile::NamedTempFile::new_in(parent)?;
let temp_path = temp_file.path().to_path_buf();
tokio::fs::write(&temp_path, data).await?;
fs::rename(&temp_path, path).await?;
tracing::debug!(
"Atomically wrote {} bytes to {}",
data.len(),
path.display()
);
Ok(())
}
pub fn write_atomic_sync(path: &Path, data: &[u8]) -> PersistenceResult<()> {
let parent = path.parent().unwrap_or_else(|| Path::new("."));
let temp_file = tempfile::NamedTempFile::new_in(parent)?;
let temp_path = temp_file.path().to_path_buf();
std::fs::write(&temp_path, data)?;
std::fs::rename(&temp_path, path)?;
Ok(())
}
pub async fn read_all(path: &Path) -> PersistenceResult<Vec<u8>> {
let data = fs::read(path).await?;
tracing::debug!("Read {} bytes from {}", data.len(), path.display());
Ok(data)
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[tokio::test]
async fn test_atomic_write() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test.txt");
let data = b"Hello, World!";
AtomicWriter::write_atomic(&file_path, data).await.unwrap();
let read_data = AtomicWriter::read_all(&file_path).await.unwrap();
assert_eq!(read_data, data);
}
#[tokio::test]
async fn test_atomic_write_overwrites() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test.txt");
AtomicWriter::write_atomic(&file_path, b"First")
.await
.unwrap();
AtomicWriter::write_atomic(&file_path, b"Second")
.await
.unwrap();
let read_data = AtomicWriter::read_all(&file_path).await.unwrap();
assert_eq!(read_data, b"Second");
}
}