use std::io;
use std::path::PathBuf;
use std::sync::Mutex;
use thiserror::Error;
use uni_sidecar::{SidecarIoError, SystemSidecar};
use crate::DeclaredPlugin;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum PersistenceError {
#[error("persistence I/O: {0}")]
Io(#[from] io::Error),
#[error("persistence serde: {0}")]
Serde(#[from] serde_json::Error),
}
impl From<SidecarIoError> for PersistenceError {
fn from(e: SidecarIoError) -> Self {
PersistenceError::Io(io::Error::other(e.to_string()))
}
}
pub trait Persistence: Send + Sync + std::fmt::Debug {
fn save(&self, plugin: &DeclaredPlugin) -> Result<(), PersistenceError>;
fn delete(&self, qname: &str) -> Result<(), PersistenceError>;
fn load_all(&self) -> Result<Vec<DeclaredPlugin>, PersistenceError>;
}
#[derive(Debug, Default)]
pub struct NullPersistence;
impl Persistence for NullPersistence {
fn save(&self, _plugin: &DeclaredPlugin) -> Result<(), PersistenceError> {
Ok(())
}
fn delete(&self, _qname: &str) -> Result<(), PersistenceError> {
Ok(())
}
fn load_all(&self) -> Result<Vec<DeclaredPlugin>, PersistenceError> {
Ok(Vec::new())
}
}
#[derive(Debug)]
pub struct JsonFilePersistence {
sidecar: SystemSidecar<Vec<DeclaredPlugin>>,
write_guard: Mutex<()>,
}
impl JsonFilePersistence {
#[must_use]
pub fn new(path: PathBuf) -> Self {
Self {
sidecar: SystemSidecar::at_path(path),
write_guard: Mutex::new(()),
}
}
}
impl Persistence for JsonFilePersistence {
fn save(&self, plugin: &DeclaredPlugin) -> Result<(), PersistenceError> {
let _guard = self.write_guard.lock().expect("persistence mutex poisoned");
let mut plugins = self.sidecar.load()?;
if let Some(slot) = plugins.iter_mut().find(|p| p.qname == plugin.qname) {
*slot = plugin.clone();
} else {
plugins.push(plugin.clone());
}
self.sidecar.store(&plugins)?;
Ok(())
}
fn delete(&self, qname: &str) -> Result<(), PersistenceError> {
let _guard = self.write_guard.lock().expect("persistence mutex poisoned");
let mut plugins = self.sidecar.load()?;
plugins.retain(|p| p.qname != qname);
self.sidecar.store(&plugins)?;
Ok(())
}
fn load_all(&self) -> Result<Vec<DeclaredPlugin>, PersistenceError> {
let _guard = self.write_guard.lock().expect("persistence mutex poisoned");
Ok(self.sidecar.load()?)
}
}