use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use crate::persistence::{self, StoreError};
use super::job::Job;
const STORE_FILE: &str = "jobs.json";
const SCHEMA_VERSION: u32 = 1;
const DEFAULT_LIMIT: usize = 50;
#[derive(Serialize, Deserialize)]
struct Envelope {
schema_version: u32,
jobs: Vec<Job>,
}
pub struct JobHistoryStore {
directory: PathBuf,
limit: usize,
jobs: Vec<Job>,
loaded: bool,
}
impl JobHistoryStore {
pub fn new(directory: &Path, limit: usize) -> Self {
Self {
directory: directory.to_path_buf(),
limit: limit.max(1),
jobs: Vec::new(),
loaded: false,
}
}
pub fn with_default_limit(directory: &Path) -> Self {
Self::new(directory, DEFAULT_LIMIT)
}
pub fn limit(&self) -> usize {
self.limit
}
pub fn set_limit(&mut self, limit: usize) {
self.limit = limit.max(1);
}
pub fn record(&mut self, job: Job) -> Result<(), StoreError> {
self.load_if_needed();
self.jobs.retain(|existing| existing.id != job.id);
self.jobs.push(job);
self.jobs
.sort_by(|a, b| (b.submitted_at, &b.id).cmp(&(a.submitted_at, &a.id)));
self.jobs.truncate(self.limit);
self.save()
}
pub fn list(&mut self) -> &[Job] {
self.load_if_needed();
&self.jobs
}
pub fn get(&mut self, id: &str) -> Option<Job> {
self.load_if_needed();
self.jobs.iter().find(|job| job.id == id).cloned()
}
fn store_file(&self) -> PathBuf {
self.directory.join(STORE_FILE)
}
fn load_if_needed(&mut self) {
if self.loaded {
return;
}
self.loaded = true;
if let Ok(Some(envelope)) = persistence::read_json::<Envelope>(&self.store_file()) {
self.jobs = envelope.jobs;
}
}
fn save(&self) -> Result<(), StoreError> {
let envelope = Envelope {
schema_version: SCHEMA_VERSION,
jobs: self.jobs.clone(),
};
persistence::write_json_atomic(&self.store_file(), &envelope)
}
}