use std::collections::BTreeMap;
use std::fs;
use std::path::{Path, PathBuf};
use time::{Duration, OffsetDateTime};
use argonauts_core::api::StatusSummary;
use crate::error::EngineResult;
pub const MIN_INTERVAL: Duration = Duration::SECOND;
#[derive(Debug, Default)]
pub struct StatusFiles {
written: BTreeMap<PathBuf, OffsetDateTime>,
held: BTreeMap<PathBuf, StatusSummary>,
}
impl StatusFiles {
pub fn write(
&mut self,
path: &Path,
summary: &StatusSummary,
now: OffsetDateTime,
) -> EngineResult<bool> {
if !self.due(path, now) {
self.held.insert(path.to_path_buf(), summary.clone());
return Ok(false);
}
self.write_now(path, summary, now)?;
Ok(true)
}
pub fn flush(&mut self, now: OffsetDateTime) -> EngineResult<usize> {
let due: Vec<PathBuf> = self
.held
.keys()
.filter(|path| self.due(path, now))
.cloned()
.collect();
for path in &due {
if let Some(summary) = self.held.remove(path) {
self.write_now(path, &summary, now)?;
}
}
Ok(due.len())
}
pub fn forget(&mut self, path: &Path) {
self.written.remove(path);
self.held.remove(path);
}
fn due(&self, path: &Path, now: OffsetDateTime) -> bool {
self.written
.get(path)
.is_none_or(|last| now - *last >= MIN_INTERVAL)
}
fn write_now(
&mut self,
path: &Path,
summary: &StatusSummary,
now: OffsetDateTime,
) -> EngineResult<()> {
if let Some(dir) = path.parent() {
fs::create_dir_all(dir)?;
}
let mut staging = path.as_os_str().to_owned();
staging.push(".tmp");
let staging = PathBuf::from(staging);
fs::write(&staging, serde_json::to_vec_pretty(summary)?)?;
fs::rename(&staging, path)?;
self.written.insert(path.to_path_buf(), now);
self.held.remove(path);
Ok(())
}
}