use std::sync::Arc;
use arc_swap::ArcSwap;
use tokio::sync::Mutex;
#[async_trait::async_trait]
pub trait Loader {
type Value;
type Error;
async fn load(&mut self) -> Result<Self::Value, Self::Error>;
}
pub struct Reloadable<T, R> {
value: ArcSwap<T>,
loader: Mutex<R>,
}
impl<T, L> Reloadable<T, L> {
pub fn new(loader: L, initial_value: Arc<T>) -> Self {
let loader = Mutex::new(loader);
let value = ArcSwap::new(initial_value);
Self { loader, value }
}
pub fn get(&self) -> Arc<T> {
self.value.load_full()
}
pub fn set(&self, value: Arc<T>) -> Arc<T> {
self.value.swap(value)
}
}
impl<T, L> Reloadable<T, L>
where
L: Loader<Value = T>,
{
pub async fn init_load(mut loader: L) -> Result<(Self, Arc<T>), L::Error> {
let loaded = loader.load().await?;
let loaded = Arc::new(loaded);
let reloadable = Self::new(loader, Arc::clone(&loaded));
Ok((reloadable, loaded))
}
pub async fn reload(&self) -> Result<Arc<T>, L::Error> {
let mut loader = self.loader.lock().await;
let reloaded = loader.load().await?;
let reloaded = Arc::new(reloaded);
drop(self.set(Arc::clone(&reloaded)));
Ok(reloaded)
}
}
impl<T, R> std::fmt::Debug for Reloadable<T, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Reloadable").finish_non_exhaustive()
}
}