use std::any::TypeId;
use std::sync::Arc;
pub trait Injectable: Send + Sync + 'static {
#[inline]
fn type_id_of() -> TypeId
where
Self: Sized,
{
TypeId::of::<Self>()
}
#[inline]
fn type_name_of() -> &'static str
where
Self: Sized,
{
std::any::type_name::<Self>()
}
}
impl<T: Send + Sync + 'static> Injectable for T {}
pub trait Provider: Injectable {}
impl<T: Injectable> Provider for T {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Lifetime {
#[default]
Singleton,
Lazy,
Transient,
Scoped,
}
#[derive(Clone)]
pub struct ProviderRegistration {
pub type_id: TypeId,
pub type_name: &'static str,
pub register_fn: Arc<dyn Fn(&crate::Container) + Send + Sync>,
}
impl ProviderRegistration {
#[inline]
pub fn new<T: Injectable>(register_fn: fn(&crate::Container)) -> Self {
Self {
type_id: TypeId::of::<T>(),
type_name: std::any::type_name::<T>(),
register_fn: Arc::new(register_fn),
}
}
pub fn singleton<T: Injectable + Clone>(value: T) -> Self {
Self {
type_id: TypeId::of::<T>(),
type_name: std::any::type_name::<T>(),
register_fn: Arc::new(move |container: &crate::Container| {
container.singleton(value.clone());
}),
}
}
}
impl std::fmt::Debug for ProviderRegistration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ProviderRegistration")
.field("type_id", &self.type_id)
.field("type_name", &self.type_name)
.finish()
}
}
#[macro_export]
macro_rules! provider {
($type:ty, $factory:expr) => {
$crate::ProviderRegistration {
type_id: std::any::TypeId::of::<$type>(),
type_name: std::any::type_name::<$type>(),
register_fn: $crate::Arc::new(|container: &$crate::Container| {
container.singleton($factory);
}),
}
};
(lazy $type:ty, $factory:expr) => {
$crate::ProviderRegistration {
type_id: std::any::TypeId::of::<$type>(),
type_name: std::any::type_name::<$type>(),
register_fn: $crate::Arc::new(|container: &$crate::Container| {
container.lazy($factory);
}),
}
};
(transient $type:ty, $factory:expr) => {
$crate::ProviderRegistration {
type_id: std::any::TypeId::of::<$type>(),
type_name: std::any::type_name::<$type>(),
register_fn: $crate::Arc::new(|container: &$crate::Container| {
container.transient($factory);
}),
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Container;
#[derive(Clone)]
struct TestService {
value: String,
}
#[test]
fn test_singleton_registration_registers_value() {
let registration = ProviderRegistration::singleton(TestService {
value: "provided".into(),
});
assert_eq!(registration.type_id, TypeId::of::<TestService>());
let container = Container::new();
(registration.register_fn)(&container);
let service = container.get::<TestService>().unwrap();
assert_eq!(service.value, "provided");
}
}