soph_core/support/
container.rs1use crate::{error::ContainerError, traits::InstanceTrait, BoxFuture, Closure, Instance, Result};
2use std::{
3 any::TypeId,
4 collections::HashMap,
5 sync::{Arc, OnceLock},
6};
7
8static CONTAINER: OnceLock<Container> = OnceLock::new();
9
10type InstanceClosures = Vec<(
11 String,
12 Closure<BoxFuture<'static, Result<(), ContainerError>>>,
13 Closure<Result<(), ContainerError>>,
14)>;
15
16#[derive(Default)]
17pub struct Container {
18 facades: HashMap<TypeId, Instance>,
19 closures: InstanceClosures,
20}
21
22impl Container {
23 pub async fn register<T: InstanceTrait>(&mut self) -> Result<(), ContainerError> {
24 let instance = Arc::new(T::register(self).await.map_err(|err| ContainerError::Register {
25 instance: T::name(),
26 source: Box::new(err),
27 })?);
28
29 self.facades.insert(TypeId::of::<T>(), instance);
30
31 self.closures.push((T::name(), Arc::new(T::boot), Arc::new(T::cleanup)));
32
33 Ok(())
34 }
35
36 pub async fn boot(self) -> Result<(), ContainerError> {
37 let container = CONTAINER.get_or_init(|| self);
38
39 for (name, boot, _) in &container.closures {
40 boot().await.map_err(|err| ContainerError::Boot {
41 instance: name.to_owned(),
42 source: Box::new(err),
43 })?
44 }
45
46 Ok(())
47 }
48
49 pub fn cleanup(&self) -> Result<(), ContainerError> {
50 for (name, _, cleaner) in &self.closures {
51 cleaner().map_err(|err| ContainerError::Clean {
52 instance: name.to_owned(),
53 source: Box::new(err),
54 })?
55 }
56
57 Ok(())
58 }
59
60 pub fn instances(&self) -> Vec<&String> {
61 self.closures.iter().map(|(name, _, _)| name).collect()
62 }
63
64 pub fn resolve<T: InstanceTrait>(&self) -> Result<&T, ContainerError> {
65 let instance = self
66 .facades
67 .get(&TypeId::of::<T>())
68 .and_then(|boxed| boxed.downcast_ref::<T>())
69 .ok_or(ContainerError::Resolve { instance: T::name() })?;
70
71 Ok(instance)
72 }
73}
74
75pub fn app() -> &'static Container {
76 CONTAINER.get().expect("`Container` not initialized")
77}