use crate::core::{Container, Resolver};
pub use crate::{components, factories};
pub fn service<T: ?Sized + Send + Sync + 'static>() -> std::sync::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::core::{Resolver, Container, Injectable};
use std::sync::{Arc, LazyLock};
static INSTANCE: LazyLock<Arc<dyn $trait + Send + Sync>> = LazyLock::new(|| {
Arc::new(<$impl as Injectable<Container>>::inject(&Container)) 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::core::{Resolver, Container, Injectable};
use std::sync::{Arc, LazyLock};
static INSTANCE: LazyLock<Arc<dyn $trait + Send + Sync>> = LazyLock::new(|| {
Arc::new(<$impl as Injectable<Container>>::inject(&Container)) 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::core::{Resolver, Container, Injectable};
use std::sync::{Arc, LazyLock};
static VALUE: LazyLock<Arc<$type>> = LazyLock::new(|| Arc::new(<$type as Injectable<Container>>::inject(&Container)));
impl Resolver<$type> for Container {
fn resolve() -> Arc<$type> {
VALUE.clone()
}
}
};
$crate::__components_internal!($($rest)*);
};
($type:ty) => {
const _: () = {
use $crate::core::{Resolver, Container, Injectable};
use std::sync::{Arc, LazyLock};
static VALUE: LazyLock<Arc<$type>> = LazyLock::new(|| Arc::new(<$type as Injectable<Container>>::inject(&Container)));
impl Resolver<$type> for Container {
fn resolve() -> Arc<$type> {
VALUE.clone()
}
}
};
};
}
#[macro_export]
macro_rules! factories {
($($item:tt)*) => {
$crate::__factories_internal!($($item)*);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __factories_internal {
() => {};
($trait:path: $impl:ty, $($rest:tt)*) => {
const _: () = {
use $crate::core::{Resolver, Container, 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<Container>>::inject(&Container)) as Arc<dyn $trait + Send + Sync>
}
}
};
$crate::__factories_internal!($($rest)*);
};
($trait:path: $impl:ty) => {
const _: () = {
use $crate::core::{Resolver, Container, 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<Container>::inject(&Container)) as Arc<dyn $trait + Send + Sync>
}
}
};
};
($type:ty, $($rest:tt)*) => {
const _: () = {
use $crate::core::{Resolver, Container, Injectable};
use std::sync::Arc;
impl Resolver<$type> for Container {
fn resolve() -> Arc<$type> {
Arc::new(<$type as Injectable<Container>>::inject(&Container))
}
}
};
$crate::__factories_internal!($($rest)*);
};
($type:ty) => {
const _: () = {
use $crate::core::{Resolver, Container, Injectable};
use std::sync::Arc;
impl Resolver<$type> for Container {
fn resolve() -> Arc<$type> {
Arc::new(<$type as Injectable<Container>>::inject(&Container))
}
}
};
};
}
#[cfg(test)]
mod test {
use noema_macros::Injectable;
use std::sync::Arc;
use crate::services::service;
#[test]
fn components_test() {
use crate::core::{Container, Injectable, Resolver, arc_dyn};
use crate::services::{components, factories};
struct ServiceA {
pub value: u32,
}
impl Injectable<Container> for ServiceA {
fn inject(_: &Container) -> Self {
ServiceA { value: 1 }
}
}
struct ServiceB {
pub value: u32,
}
impl Injectable<Container> for ServiceB {
fn inject(_: &Container) -> 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<Container> for MyTraitImpl {
fn inject(_: &Container) -> Self {
MyTraitImpl {}
}
}
trait AnotherTrait: Send + Sync {
fn get_another_value(&self) -> u32;
}
#[derive(Injectable)]
struct AnotherTraitImpl {
pub my_trait: arc_dyn!(MyTrait),
}
impl AnotherTrait for AnotherTraitImpl {
fn get_another_value(&self) -> u32 {
self.my_trait.get_value() + 1
}
}
components!(
MyTrait: MyTraitImpl,
AnotherTrait: AnotherTraitImpl
);
factories!(ServiceA, ServiceB);
let service_a_1 = service::<ServiceA>();
let service_a_2 = service::<ServiceA>();
let another_service = service::<dyn AnotherTrait + Send + Sync>();
assert!(!Arc::ptr_eq(&service_a_1, &service_a_2));
let my_trait = service::<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);
}
}