use std::sync::Arc;
pub struct Container;
pub trait Resolver<T: ?Sized + Sync + Send> {
fn resolve() -> Arc<T>;
}
pub fn resolve<T: ?Sized + Sync + Send>() -> Arc<T>
where
Container: Resolver<T>,
{
<Container as Resolver<T>>::resolve()
}
#[macro_export]
macro_rules! components {
($($item:tt)*) => {
$crate::__components_internal!($($item)*);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __components_internal {
() => {};
($trait:path: $impl:ty, $($rest:tt)*) => {
const _: () = {
use $crate::resolver::Resolver;
use $crate::resolver::Container;
use $crate::inject::Injectable;
use std::sync::{Arc, LazyLock};
static INSTANCE: LazyLock<Arc<dyn $trait + Send + Sync>> = LazyLock::new(|| {
Arc::new(<$impl as Injectable>::inject()) as Arc<dyn $trait + Send + Sync>
});
impl Resolver<dyn $trait + Send + Sync> for Container {
fn resolve() -> Arc<dyn $trait + Send + Sync> {
INSTANCE.clone()
}
}
};
$crate::__components_internal!($($rest)*);
};
($trait:path: $impl:ty) => {
const _: () = {
use $crate::resolver::Resolver;
use $crate::resolver::Container;
use $crate::inject::Injectable;
use std::sync::{Arc, LazyLock};
static INSTANCE: LazyLock<Arc<dyn $trait + Send + Sync>> = LazyLock::new(|| {
Arc::new(<$impl as Injectable>::inject()) as Arc<dyn $trait + Send + Sync>
});
impl Resolver<dyn $trait + Send + Sync> for Container {
fn resolve() -> Arc<dyn $trait + Send + Sync> {
INSTANCE.clone()
}
}
};
};
($type:ty, $($rest:tt)*) => {
const _: () = {
use $crate::resolver::Resolver;
use $crate::resolver::Container;
use $crate::inject::Injectable;
use std::sync::{Arc, LazyLock};
static VALUE: LazyLock<Arc<$type>> = LazyLock::new(|| Arc::new(<$type as Injectable>::inject()));
impl Resolver<$type> for Container {
fn resolve() -> Arc<$type> {
VALUE.clone()
}
}
};
$crate::__components_internal!($($rest)*);
};
($type:ty) => {
const _: () = {
use $crate::resolver::Resolver;
use $crate::resolver::Container;
use $crate::inject::Injectable;
use std::sync::{Arc, LazyLock};
static VALUE: LazyLock<Arc<$type>> = LazyLock::new(|| Arc::new(<$type as Injectable>::inject()));
impl Resolver<$type> for Container {
fn resolve() -> Arc<$type> {
VALUE.clone()
}
}
};
};
}
#[macro_export]
macro_rules! providers {
($($item:tt)*) => {
$crate::__providers_internal!($($item)*);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __providers_internal {
() => {};
($trait:path: $impl:ty, $($rest:tt)*) => {
const _: () = {
use $crate::resolver::Resolver;
use $crate::resolver::Container;
use $crate::inject::Injectable;
use std::sync::Arc;
impl Resolver<dyn $trait + Send + Sync> for Container {
fn resolve() -> Arc<dyn $trait + Send + Sync> {
Arc::new(<$impl as Injectable>::inject()) as Arc<dyn $trait + Send + Sync>
}
}
};
$crate::__providers_internal!($($rest)*);
};
($trait:path: $impl:ty) => {
const _: () = {
use $crate::resolver::Resolver;
use $crate::resolver::Container;
use $crate::inject::Injectable;
use std::sync::Arc;
impl Resolver<dyn $trait + Send + Sync> for Container {
fn resolve(&self) -> Arc<dyn $trait + Send + Sync> {
Arc::new(<$impl as Injectable>::inject()) as Arc<dyn $trait + Send + Sync>
}
}
};
};
($type:ty, $($rest:tt)*) => {
const _: () = {
use $crate::resolver::Resolver;
use $crate::resolver::Container;
use $crate::inject::Injectable;
use std::sync::Arc;
impl Resolver<$type> for Container {
fn resolve() -> Arc<$type> {
Arc::new(<$type as Injectable>::inject())
}
}
};
$crate::__providers_internal!($($rest)*);
};
($type:ty) => {
const _: () = {
use $crate::resolver::Resolver;
use $crate::resolver::Container;
use $crate::inject::Injectable;
use std::sync::Arc;
impl Resolver<$type> for Container {
fn resolve() -> Arc<$type> {
Arc::new(<$type as Injectable>::inject())
}
}
};
};
}
#[cfg(test)]
mod test {
use noema_macros::Injectable;
use std::sync::Arc;
#[test]
fn components_test() {
use crate::inject::Injectable;
use crate::resolve;
use crate::{components, providers};
struct ServiceA {
pub value: u32,
}
impl Injectable for ServiceA {
fn inject() -> Self {
ServiceA { value: 1 }
}
}
struct ServiceB {
pub value: u32,
}
impl Injectable for ServiceB {
fn inject() -> Self {
ServiceB { value: 2 }
}
}
trait MyTrait: Send + Sync {
fn get_value(&self) -> u32;
}
struct MyTraitImpl;
impl MyTrait for MyTraitImpl {
fn get_value(&self) -> u32 {
42
}
}
impl Injectable for MyTraitImpl {
fn inject() -> Self {
MyTraitImpl {}
}
}
trait AnotherTrait: Send + Sync {
fn get_another_value(&self) -> u32;
}
#[derive(Injectable)]
struct AnotherTraitImpl {
pub my_trait: std::sync::Arc<dyn MyTrait + Send + Sync>,
}
impl AnotherTrait for AnotherTraitImpl {
fn get_another_value(&self) -> u32 {
self.my_trait.get_value() + 1
}
}
components!(
MyTrait: MyTraitImpl,
AnotherTrait: AnotherTraitImpl
);
providers!(ServiceA, ServiceB);
let service_a_1 = resolve::<ServiceA>();
let service_a_2 = resolve::<ServiceA>();
let another_service = resolve::<dyn AnotherTrait + Send + Sync>();
assert!(!Arc::ptr_eq(&service_a_1, &service_a_2));
let my_trait = crate::resolver::resolve::<dyn MyTrait + Send + Sync>();
println!("My another value: {}", another_service.get_another_value());
assert_eq!(my_trait.get_value(), 42);
assert_eq!(another_service.get_another_value(), 43);
}
}