use std::fs::{self, File, OpenOptions};
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use shipper_types::PublishEvent;
#[cfg(test)]
mod proptests;
#[cfg(test)]
mod tests;
pub const EVENTS_FILE: &str = "events.jsonl";
pub fn events_path(state_dir: &Path) -> PathBuf {
state_dir.join(EVENTS_FILE)
}
#[derive(Debug, Default)]
pub struct EventLog {
events: Vec<PublishEvent>,
}
impl EventLog {
pub fn new() -> Self {
Self { events: Vec::new() }
}
pub fn record(&mut self, event: PublishEvent) {
self.events.push(event);
}
pub fn write_to_file(&self, path: &Path) -> Result<()> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("failed to create events dir {}", parent.display()))?;
}
let file = OpenOptions::new()
.create(true)
.append(true)
.open(path)
.with_context(|| format!("failed to open events file {}", path.display()))?;
let mut writer = std::io::BufWriter::new(file);
for event in &self.events {
let line = serde_json::to_string(event).context("failed to serialize event to JSON")?;
writeln!(writer, "{}", line).context("failed to write event line")?;
}
writer.flush().context("failed to flush events file")?;
Ok(())
}
pub fn read_from_file(path: &Path) -> Result<Self> {
if !path.exists() {
return Ok(Self::new());
}
let file = File::open(path)
.with_context(|| format!("failed to open events file {}", path.display()))?;
let reader = BufReader::new(file);
let mut events = Vec::new();
for line in reader.lines() {
let line = line.with_context(|| {
format!("failed to read line from events file {}", path.display())
})?;
let event: PublishEvent = serde_json::from_str(&line)
.with_context(|| format!("failed to parse event JSON from line: {}", line))?;
events.push(event);
}
Ok(Self { events })
}
pub fn events_for_package(&self, package: &str) -> Vec<&PublishEvent> {
self.events
.iter()
.filter(|e| e.package == package)
.collect()
}
pub fn all_events(&self) -> &[PublishEvent] {
&self.events
}
pub fn clear(&mut self) {
self.events.clear();
}
pub fn len(&self) -> usize {
self.events.len()
}
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
}