soph_core/support/
container.rsuse crate::{error::ContainerError, traits::InstanceTrait, BoxFuture, Closure, Instance, Result};
use std::{
any::TypeId,
collections::HashMap,
sync::{Arc, OnceLock},
};
static CONTAINER: OnceLock<Container> = OnceLock::new();
type InstanceClosures = Vec<(
String,
Closure<BoxFuture<'static, Result<(), ContainerError>>>,
Closure<Result<(), ContainerError>>,
)>;
#[derive(Default)]
pub struct Container {
facades: HashMap<TypeId, Instance>,
closures: InstanceClosures,
}
impl Container {
pub async fn register<T: InstanceTrait>(&mut self) -> Result<(), ContainerError> {
let instance =
Arc::new(
T::register(self)
.await
.map_err(|err| ContainerError::Register {
instance: T::name(),
source: Box::new(err),
})?,
);
self.facades.insert(TypeId::of::<T>(), instance);
self.closures
.push((T::name(), Arc::new(T::boot), Arc::new(T::cleanup)));
Ok(())
}
pub async fn boot(self) -> Result<(), ContainerError> {
let container = CONTAINER.get_or_init(|| self);
for (name, boot, _) in &container.closures {
boot().await.map_err(|err| ContainerError::Boot {
instance: name.to_owned(),
source: Box::new(err),
})?
}
Ok(())
}
pub fn cleanup(&self) -> Result<(), ContainerError> {
for (name, _, cleaner) in &self.closures {
cleaner().map_err(|err| ContainerError::Clean {
instance: name.to_owned(),
source: Box::new(err),
})?
}
Ok(())
}
pub fn instances(&self) -> Vec<&String> {
self.closures.iter().map(|(name, _, _)| name).collect()
}
pub fn resolve<T: InstanceTrait>(&self) -> Result<&T, ContainerError> {
let instance = self
.facades
.get(&TypeId::of::<T>())
.and_then(|boxed| boxed.downcast_ref::<T>())
.ok_or(ContainerError::Resolve {
instance: T::name(),
})?;
Ok(instance)
}
}
pub fn app() -> &'static Container {
CONTAINER.get().expect("`Container` not initialized")
}