use std::sync::Arc;
use parking_lot::Mutex;
use rskit_errors::AppResult;
use crate::Health;
#[async_trait::async_trait]
pub trait Component: Send + Sync {
fn name(&self) -> &str;
async fn start(&self) -> AppResult<()>;
async fn stop(&self) -> AppResult<()>;
fn health(&self) -> Health;
}
pub struct LazyComponent<F> {
name: &'static str,
factory: F,
inner: Mutex<Option<Arc<dyn Component>>>,
}
impl<F: Fn() -> Arc<dyn Component> + Send + Sync> LazyComponent<F> {
#[must_use]
pub fn new(name: &'static str, factory: F) -> Self {
Self {
name,
factory,
inner: Mutex::new(None),
}
}
}
#[async_trait::async_trait]
impl<F: Fn() -> Arc<dyn Component> + Send + Sync> Component for LazyComponent<F> {
fn name(&self) -> &str {
self.name
}
async fn start(&self) -> AppResult<()> {
let component = {
let mut guard = self.inner.lock();
if let Some(component) = guard.as_ref() {
Arc::clone(component)
} else {
let component = (self.factory)();
*guard = Some(Arc::clone(&component));
component
}
};
component.start().await
}
async fn stop(&self) -> AppResult<()> {
let component = self.inner.lock().clone();
if let Some(component) = component {
component.stop().await
} else {
Ok(())
}
}
fn health(&self) -> Health {
let component = self.inner.lock().clone();
if let Some(component) = component {
component.health()
} else {
Health::healthy(self.name)
}
}
}