use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;
use std::sync::atomic::{AtomicU64, Ordering};
use crate::error::{Error, Result};
static TMP_COUNTER: AtomicU64 = AtomicU64::new(0);
pub fn write_atomic(path: &Path, bytes: &[u8]) -> Result<()> {
write_atomic_inner(path, bytes, true)
}
pub fn write_atomic_no_create(path: &Path, bytes: &[u8]) -> Result<()> {
write_atomic_inner(path, bytes, false)
}
fn write_atomic_inner(path: &Path, bytes: &[u8], create_parent: bool) -> Result<()> {
let dir = path.parent().ok_or_else(|| {
Error::IoBare(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("path {} has no parent directory", path.display()),
))
})?;
if create_parent {
std::fs::create_dir_all(dir).map_err(|e| Error::io(dir, e))?;
}
let fname = path
.file_name()
.ok_or_else(|| {
Error::IoBare(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("path {} has no file name", path.display()),
))
})?
.to_string_lossy();
let seq = TMP_COUNTER.fetch_add(1, Ordering::Relaxed);
let tmp = dir.join(format!(".{fname}.tmp.{}.{seq}", std::process::id()));
{
let mut opts = OpenOptions::new();
opts.create_new(true).write(true);
crate::paths::nofollow(&mut opts);
let mut f = opts.open(&tmp).map_err(|e| Error::io(&tmp, e))?;
f.write_all(bytes).map_err(|e| Error::io(&tmp, e))?;
f.sync_all().map_err(|e| Error::io(&tmp, e))?;
}
std::fs::rename(&tmp, path).map_err(|e| Error::io(path, e))?;
if let Ok(dir_file) = File::open(dir) {
let _ = dir_file.sync_all();
}
Ok(())
}
pub fn write_json_atomic<T: serde::Serialize>(path: &Path, value: &T) -> Result<()> {
let bytes = serde_json::to_vec_pretty(value).map_err(|e| Error::json(path, e))?;
write_atomic(path, &bytes)
}
pub fn write_json_atomic_no_create<T: serde::Serialize>(path: &Path, value: &T) -> Result<()> {
let bytes = serde_json::to_vec_pretty(value).map_err(|e| Error::json(path, e))?;
write_atomic_no_create(path, &bytes)
}
pub fn open_events_append(path: &Path) -> Result<File> {
if let Some(p) = path.parent() {
std::fs::create_dir_all(p).map_err(|e| Error::io(p, e))?;
}
let mut opts = OpenOptions::new();
opts.create(true).append(true);
crate::paths::nofollow(&mut opts);
opts.open(path).map_err(|e| Error::io(path, e))
}