use crate::{errors::ContainerError, traits::InstanceTrait, Closure, Instance, Result};
use std::{
any::TypeId,
collections::HashMap,
sync::{Arc, OnceLock},
};
static CONTAINER: OnceLock<Container> = OnceLock::new();
#[derive(Default)]
pub struct Container {
facades: HashMap<TypeId, Instance>,
closures: Vec<(String, Closure<Result<(), ContainerError>>)>,
}
impl Container {
pub fn register<T: InstanceTrait>(&mut self) -> Result<(), ContainerError> {
self.facades.insert(
TypeId::of::<T>(),
Arc::new(T::register(self).map_err(|err| ContainerError::Register {
instance: T::name(),
source: Box::new(err),
})?),
);
self.closures.push((T::name(), Arc::new(|| T::boot())));
Ok(())
}
pub fn instances(&self) -> Vec<&String> {
self.closures.iter().map(|(name, _)| name).collect()
}
pub fn boot(self) -> Result<(), ContainerError> {
let container = CONTAINER.get_or_init(|| self);
for (name, closure) in &container.closures {
closure().map_err(|err| ContainerError::Boot {
instance: name.to_owned(),
source: Box::new(err),
})?
}
Ok(())
}
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 name(&self) -> String {
"ayun".to_owned()
}
pub fn version(&self) -> String {
env!("CARGO_PKG_VERSION").to_owned()
}
}
pub fn app() -> &'static Container {
CONTAINER.get().expect("`Container` not initialized")
}