ayun_core/support/
container.rs

1use crate::{error::ContainerError, traits::InstanceTrait, 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<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 fn register<T: InstanceTrait>(&mut self) -> Result<(), ContainerError> {
24        let instance = Arc::new(T::register(self).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
32            .push((T::name(), Arc::new(T::boot), Arc::new(T::cleanup)));
33
34        Ok(())
35    }
36
37    pub fn instances(&self) -> Vec<&String> {
38        self.closures.iter().map(|(name, _, _)| name).collect()
39    }
40
41    pub fn boot(self) -> Result<(), ContainerError> {
42        let container = CONTAINER.get_or_init(|| self);
43
44        for (name, boot, _) in &container.closures {
45            boot().map_err(|err| ContainerError::Boot {
46                instance: name.to_owned(),
47                source: Box::new(err),
48            })?
49        }
50
51        Ok(())
52    }
53
54    pub fn cleanup(&self) -> Result<(), ContainerError> {
55        for (name, _, cleaner) in &self.closures {
56            cleaner().map_err(|err| ContainerError::Clean {
57                instance: name.to_owned(),
58                source: Box::new(err),
59            })?
60        }
61
62        Ok(())
63    }
64
65    pub fn resolve<T: InstanceTrait>(&self) -> Result<&T, ContainerError> {
66        let instance = self
67            .facades
68            .get(&TypeId::of::<T>())
69            .and_then(|boxed| boxed.downcast_ref::<T>())
70            .ok_or(ContainerError::Resolve {
71                instance: T::name(),
72            })?;
73
74        Ok(instance)
75    }
76}
77
78pub fn app() -> &'static Container {
79    CONTAINER.get().expect("`Container` not initialized")
80}