caelix-core 0.0.1

Core primitives for the Caelix framework.
Documentation
use std::{
    any::{Any, TypeId},
    collections::HashMap,
    future::Future,
    pin::Pin,
    sync::Arc,
};

pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;

pub trait Injectable: Send + Sync + 'static {
    fn create(container: &Container) -> BoxFuture<'_, Self>
    where
        Self: Sized;

    fn on_module_init(&self) -> BoxFuture<'_, crate::Result<()>> {
        Box::pin(async { Ok(()) })
    }

    fn on_bootstrap(&self) -> BoxFuture<'_, crate::Result<()>> {
        Box::pin(async { Ok(()) })
    }

    fn on_shutdown(&self) -> BoxFuture<'_, crate::Result<()>> {
        Box::pin(async { Ok(()) })
    }
}

#[derive(Clone)]
pub struct Container {
    services: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
}

impl Container {
    pub fn new() -> Self {
        crate::logging::init_logging();

        let mut services: HashMap<TypeId, Arc<dyn Any + Send + Sync>> = HashMap::new();
        services.insert(
            TypeId::of::<crate::Logger>(),
            Arc::new(crate::Logger::new("Application")),
        );
        services.insert(
            TypeId::of::<crate::EventBus>(),
            Arc::new(crate::EventBus::new()),
        );

        Self { services }
    }

    pub fn register_instance<T: Send + Sync + 'static>(&mut self, value: T) {
        self.services.insert(TypeId::of::<T>(), Arc::new(value));
    }

    pub async fn register<T: Injectable>(&mut self) {
        self.try_register::<T>()
            .await
            .unwrap_or_else(|err| panic!("{}", err.message));
    }

    pub async fn try_register<T: Injectable>(&mut self) -> crate::Result<()> {
        let instance = T::create(self).await;
        let instance = Arc::new(instance);
        instance.on_module_init().await.map_err(|err| {
            crate::exception::startup_error(format!(
                "on_module_init failed for {}: {}: {}",
                std::any::type_name::<T>(),
                err.error,
                err.message
            ))
        })?;
        self.services.insert(TypeId::of::<T>(), instance);
        Ok(())
    }

    pub(crate) fn register_erased(&mut self, type_id: TypeId, value: Arc<dyn Any + Send + Sync>) {
        self.services.insert(type_id, value);
    }

    pub(crate) fn resolve_erased(&self, type_id: TypeId) -> Option<Arc<dyn Any + Send + Sync>> {
        self.services.get(&type_id).cloned()
    }

    pub(crate) fn contains_type_id(&self, type_id: TypeId) -> bool {
        self.services.contains_key(&type_id)
    }

    pub fn resolve<T: Send + Sync + 'static>(&self) -> Arc<T> {
        self.services
            .get(&TypeId::of::<T>())
            .unwrap_or_else(|| panic!("no provider registered for {}", std::any::type_name::<T>()))
            .clone()
            .downcast::<T>()
            .unwrap_or_else(|_| panic!("type mismatch resolving {}", std::any::type_name::<T>()))
    }

    pub fn resolve_logger(&self, context: impl Into<String>) -> Arc<crate::Logger> {
        Arc::new(crate::Logger::new(context))
    }
}

impl Default for Container {
    fn default() -> Self {
        Self::new()
    }
}