injection 0.1.1

A lightweight dependency injection container for Rust applications
Documentation
use crate::Component;
use std::any::{Any, TypeId};
use std::pin::Pin;
use tokio::sync::OnceCell;

/// 注入实例类型别名
/// 表示一个静态的、线程安全的任意类型引用
pub type InjectionInstance = &'static (dyn Any + Send + Sync);
/// Bean 初始化 Future 类型别名
/// 表示一个异步操作,最终产生一个注入实例
pub type BeanFuture = Pin<Box<dyn Future<Output = InjectionInstance> + Send>>;
/// 初始化函数类型别名
/// 用于创建和初始化 Bean 实例的工厂函数
pub type InitFn = fn() -> BeanFuture;
/// 注入 Bean
/// 负责管理单个 Bean 实例的初始化和访问
#[derive(Clone)]
pub struct InjectionBean {
    /// Bean 初始化函数,用于创建实例
    pub(crate) initial: InitFn,
    /// Bean 实例的 OnceCell 包装器,确保只初始化一次
    pub(crate) instance: OnceCell<InjectionInstance>,
}

impl InjectionBean {
    /// 创建新的 Bean 实例
    /// 参数 factory 是用于创建 Bean 的工厂函数
    pub(crate) fn new(factory: InitFn) -> Self {
        Self {
            initial: factory,
            instance: OnceCell::new(),
        }
    }
    
    /// 获取 Bean 实例(异步)
    /// 如果 Bean 尚未初始化,会调用工厂函数进行初始化
    /// 使用 OnceCell 确保线程安全的延迟初始化
    pub async fn get(&self) -> InjectionInstance {
        let instance = self
            .instance
            .get_or_init(self.initial)
            .await;
        *instance
    }

    /// 获取 Bean 实例(同步)
    /// 直接返回已初始化的实例引用
    /// 注意:此方法要求 Bean 已经初始化完成,否则会 panic
    pub fn get_inner(&self) -> &InjectionInstance {
        self.instance.get().expect("bean not initialized")
    }
}

/// Bean 定义
/// 包含注册 Bean 到容器所需的所有元数据
pub struct InjectionBeanDefinition {
    /// Bean 的类型 ID,用于类型识别
    pub type_id: TypeId,
    /// Bean 的名称,用于在容器中唯一标识
    pub name: &'static str,
    /// Bean 的初始化函数
    pub init_fn: InitFn,
}

impl InjectionBeanDefinition {
    /// 创建新的 Bean 定义
    /// 泛型参数 T 是 Bean 的具体类型
    /// 参数 name 是 Bean 的名称,init_fn 是初始化函数
    pub const fn new<T: Component>(name: &'static str, init_fn: InitFn) -> Self {
        Self {
            type_id: TypeId::of::<T>(),
            name,
            init_fn,
        }
    }
}

/// 使用 inventory 宏收集所有 Bean 定义
/// 这使得容器可以在启动时自动发现并注册所有 Bean
inventory::collect!(InjectionBeanDefinition);