use crate::StorageItem;
use async_trait::async_trait;
use chrono::DateTime;
use chrono::Utc;
use color_eyre::eyre::eyre;
use color_eyre::eyre::Result;
use serde::Deserialize;
use serde::Serialize;
#[async_trait]
pub trait Storage<ITEM: StorageItem + Sized>: Send + Sync + std::fmt::Debug {
async fn ensure_storage_exists(&mut self) -> Result<()>;
async fn create(&self) -> Result<ITEM::ID>;
async fn exists(&self, id: &ITEM::ID) -> Result<bool>;
async fn load(&self, id: &ITEM::ID) -> Result<ITEM>;
async fn save(&self, id: &ITEM::ID, item: &ITEM, lock: &StorageLock) -> Result<()>;
async fn lock(&self, id: &ITEM::ID, who: &str) -> Result<LockResult<ITEM>>;
async fn unlock(&self, id: &ITEM::ID, lock: StorageLock) -> Result<()>;
async fn force_unlock(&self, id: &ITEM::ID) -> Result<()>;
async fn verify_lock(&self, id: &ITEM::ID, lock: &StorageLock) -> Result<bool>;
async fn all_ids(&self) -> Result<Vec<ITEM::ID>>;
async fn display_lock(&self, id: &ITEM::ID) -> Result<String>;
#[cfg(feature = "metadata")]
async fn metadata_highest_seen_id(&self) -> Option<ITEM::ID>;
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct StorageLock {
who: String,
when: DateTime<Utc>,
}
impl StorageLock {
pub fn new(who: &str) -> Self {
Self {
who: who.to_string(),
when: Utc::now(),
}
}
pub fn who(&self) -> &str {
&self.who
}
pub fn when(&self) -> &DateTime<Utc> {
&self.when
}
}
#[derive(Debug)]
pub enum LockResult<ITEM> {
Success { lock: StorageLock, item: ITEM },
AlreadyLocked { who: String },
}
impl<ITEM> LockResult<ITEM> {
pub fn success(self) -> Result<(StorageLock, ITEM)> {
match self {
LockResult::Success { lock, item } => Ok((lock, item)),
LockResult::AlreadyLocked { who } => Err(eyre!("Already locked by {who:?}")),
}
}
}